[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [17096] trunk/blender/release/scripts/ import_web3d.py: x3d and vrml importer for loading thes files as static scenes.
Campbell Barton
ideasman42 at gmail.com
Fri Oct 17 21:06:25 CEST 2008
Revision: 17096
http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=17096
Author: campbellbarton
Date: 2008-10-17 21:06:24 +0200 (Fri, 17 Oct 2008)
Log Message:
-----------
x3d and vrml importer for loading thes files as static scenes. x3d needs a full python install. VRML97 files can be imported without python.
for details on what is supported see
http://wiki.blender.org/index.php/Scripts/Manual/Import/X3D_VRML97#Compatibility
Added Paths:
-----------
trunk/blender/release/scripts/import_web3d.py
Added: trunk/blender/release/scripts/import_web3d.py
===================================================================
--- trunk/blender/release/scripts/import_web3d.py (rev 0)
+++ trunk/blender/release/scripts/import_web3d.py 2008-10-17 19:06:24 UTC (rev 17096)
@@ -0,0 +1,1905 @@
+#!BPY
+"""
+Name: 'X3D & VRML97 (.x3d / wrl)...'
+Blender: 248
+Group: 'Import'
+Tooltip: 'Load a VRML97 File'
+"""
+
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# (C) Copyright 2008 Paravizion
+# Written by Campbell Barton aka Ideasman42
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+# --------------------------------------------------------------------------
+
+__author__ = "Campbell Barton"
+__url__ = ['www.blender.org', 'blenderartists.org', 'http://wiki.blender.org/index.php/Scripts/Manual/Import/X3D_VRML97']
+__version__ = "0.1"
+
+__bpydoc__ = """\
+This script is an importer for the X3D and VRML97 file formats.
+"""
+
+def vrmlFormat(data):
+ '''
+ Keep this as a valid vrml file, but format in a way we can pradict.
+ '''
+ # Strip all commends - # not in strings - warning multiline strings are ignored.
+ def strip_comment(l):
+ #l = ' '.join(l.split())
+ l = l.strip()
+
+ if l.startswith('#'):
+ return ''
+
+ i = l.find('#')
+
+ if i==-1:
+ return l
+
+ # Most cases accounted for! if we have a comment at the end of the line do this...
+ j = l.find('"')
+
+ if j == -1: # simple no strings
+ return l[:i].strip()
+
+ q = False
+ for i,c in enumerate(l):
+ if c == '"':
+ q = not q # invert
+
+ elif c == '#':
+ if q==False:
+ return l[:i-1]
+
+ return l
+
+ data = '\n'.join([strip_comment(l) for l in data.split('\n') ]) # remove all whitespace
+
+
+ # Bad, dont take strings into account
+ '''
+ data = data.replace('#', '\n#')
+ data = '\n'.join([ll for l in data.split('\n') for ll in (l.strip(),) if not ll.startswith('#')]) # remove all whitespace
+ '''
+ data = data.replace('{', '\n{\n')
+ data = data.replace('}', '\n}\n')
+ data = data.replace('[', '\n[\n')
+ data = data.replace(']', '\n]\n')
+ data = data.replace(',', ' , ') # make sure comma's seperate
+
+ # More annoying obscure cases where USE or DEF are placed on a newline
+ # data = data.replace('\nDEF ', ' DEF ')
+ # data = data.replace('\nUSE ', ' USE ')
+
+ data = '\n'.join([' '.join(l.split()) for l in data.split('\n')]) # remove all whitespace
+
+ # Better to parse the file accounting for multiline arrays
+ '''
+ data = data.replace(',\n', ' , ') # remove line endings with commas
+ data = data.replace(']', '\n]\n') # very very annoying - but some comma's are at the end of the list, must run this again.
+ '''
+
+ return [l for l in data.split('\n') if l]
+
+
+# This should work without a blender at all
+try:
+ from Blender.sys import exists
+except:
+ from os.path import exists
+
+def baseName(path):
+ return path.split('/')[-1].split('\\')[-1]
+
+def dirName(path):
+ return path[:-len(baseName(path))]
+
+# notes
+# transform are relative
+# order dosnt matter for loc/size/rot
+# right handed rotation
+# angles are in radians
+# rotation first defines axis then ammount in deg
+
+
+
+# =============================== VRML Spesific
+
+NODE_NORMAL = 1 # {}
+NODE_ARRAY = 2 # []
+NODE_REFERENCE = 3 # USE foobar
+
+lines = []
+
+def getNodePreText(i, words):
+ # print lines[i]
+ use_node = False
+ while len(words) < 5:
+
+ if i>=len(lines):
+ break
+ elif lines[i]=='{':
+ # words.append(lines[i]) # no need
+ # print "OK"
+ return NODE_NORMAL, i+1
+ elif lines[i].count('"') % 2 != 0: # odd number of quotes? - part of a string.
+ # print 'ISSTRING'
+ break
+ else:
+ new_words = lines[i].split()
+ if 'USE' in new_words:
+ use_node = True
+
+ words.extend(new_words)
+ i += 1
+
+ # Check for USE node - no {
+ # USE #id - should always be on the same line.
+ if use_node:
+ # print 'LINE', i, words[:words.index('USE')+2]
+ words[:] = words[:words.index('USE')+2]
+ if lines[i] == '{' and lines[i+1] == '}':
+ # USE sometimes has {} after it anyway
+ i+=2
+ return NODE_REFERENCE, i
+
+ # print "error value!!!", words
+ return 0, -1
+
+def is_nodeline(i, words):
+
+ if not lines[i][0].isalpha():
+ return 0, 0
+
+ # Simple "var [" type
+ if lines[i+1] == '[':
+ if lines[i].count('"') % 2 == 0:
+ words[:] = lines[i].split()
+ return NODE_ARRAY, i+2
+
+ node_type, new_i = getNodePreText(i, words)
+
+ if not node_type:
+ return 0, 0
+
+ # Ok, we have a { after some values
+ # Check the values are not fields
+ for i, val in enumerate(words):
+ if i != 0 and words[i-1] in ('DEF', 'USE'):
+ # ignore anything after DEF, it is a ID and can contain any chars.
+ pass
+ elif val[0].isalpha() and val not in ('TRUE', 'FALSE'):
+ pass
+ else:
+ # There is a number in one of the values, therefor we are not a node.
+ return 0, 0
+
+ #if node_type==NODE_REFERENCE:
+ # print words, "REF_!!!!!!!"
+ return node_type, new_i
+
+def is_numline(i):
+ '''
+ Does this line start with a number?
+ '''
+ l = lines[i]
+ line_end = len(l)-1
+ line_end_new = l.find(' ') # comma's always have a space before them
+
+ if line_end_new != -1:
+ line_end = line_end_new
+
+ try:
+ float(l[:line_end]) # works for a float or int
+ return True
+ except:
+ return False
+
+class vrmlNode(object):
+ __slots__ = 'id', 'fields', 'node_type', 'parent', 'children', 'parent', 'array_data', 'reference', 'lineno', 'filename', 'blendObject', 'DEF_NAMESPACE', 'FIELD_NAMESPACE', 'x3dNode'
+ def __init__(self, parent, node_type, lineno):
+ self.id = None
+ self.node_type = node_type
+ self.parent = parent
+ self.blendObject = None
+ self.x3dNode = None # for x3d import only
+ if parent:
+ parent.children.append(self)
+
+ self.lineno = lineno
+
+ # This is only set from the root nodes.
+ # Having a filename also denotes a root node
+ self.filename = None
+
+ # Store in the root node because each inline file needs its own root node and its own namespace
+ self.DEF_NAMESPACE = None
+ self.FIELD_NAMESPACE = None
+
+ self.reference = None
+
+ if node_type==NODE_REFERENCE:
+ # For references, only the parent and ID are needed
+ # the reference its self is assigned on parsing
+ return
+
+ self.fields = [] # fields have no order, in some cases rool level values are not unique so dont use a dict
+ self.children = []
+ self.array_data = [] # use for arrays of data - should only be for NODE_ARRAY types
+
+
+ # Only available from the root node
+ def getFieldDict(self):
+ if self.FIELD_NAMESPACE != None:
+ return self.FIELD_NAMESPACE
+ else:
+ return self.parent.getFieldDict()
+
+ def getDefDict(self):
+ if self.DEF_NAMESPACE != None:
+ return self.DEF_NAMESPACE
+ else:
+ return self.parent.getDefDict()
+
+ def setRoot(self, filename):
+ self.filename = filename
+ self.FIELD_NAMESPACE = {}
+ self.DEF_NAMESPACE= {}
+
+ def getFilename(self):
+ if self.filename:
+ return self.filename
+ elif self.parent:
+ return self.parent.getFilename()
+ else:
+ return None
+
+ def getRealNode(self):
+ if self.reference:
+ return self.reference
+ else:
+ return self
+
+ def getSpec(self):
+ self_real = self.getRealNode()
+ try:
+ return self_real.id[-1] # its possible this node has no spec
+ except:
+ return None
+
+ def getDefName(self):
+ self_real = self.getRealNode()
+
+ if 'DEF' in self_real.id:
+ # print self_real.id
+ return self_real.id[ list(self_real.id).index('DEF')+1 ]
+ else:
+ return None
+
+
+ def getChildrenBySpec(self, node_spec): # spec could be Transform, Shape, Appearance
+ self_real = self.getRealNode()
+ # using getSpec functions allows us to use the spec of USE children that dont have their spec in their ID
+ if type(node_spec) == str:
+ return [child for child in self_real.children if child.getSpec()==node_spec]
+ else:
+ # Check inside a list of optional types
+ return [child for child in self_real.children if child.getSpec() in node_spec]
+
+ def getChildBySpec(self, node_spec): # spec could be Transform, Shape, Appearance
+ # Use in cases where there is only ever 1 child of this type
+ ls = self.getChildrenBySpec(node_spec)
+ if ls: return ls[0]
+ else: return None
+
+ def getChildrenByName(self, node_name): # type could be geometry, children, appearance
+ self_real = self.getRealNode()
+ return [child for child in self_real.children if child.id if child.id[0]==node_name]
+
+ def getChildByName(self, node_name):
+ self_real = self.getRealNode()
+ for child in self_real.children:
+ if child.id and child.id[0]==node_name: # and child.id[-1]==node_spec:
+ return child
+
+ def getSerialized(self, results, ancestry):
+ ''' Return this node and all its children in a flat list '''
+ ancestry = ancestry[:] # always use a copy
+
+ # self_real = self.getRealNode()
+
+ results.append((self, tuple(ancestry)))
+ ancestry.append(self)
+ for child in self.getRealNode().children:
+ if child not in ancestry:
+ child.getSerialized(results, ancestry)
+
+ return results
+
+ def searchNodeTypeID(self, node_spec, results):
+ self_real = self.getRealNode()
+ # print self.lineno, self.id
+ if self_real.id and self_real.id[-1]==node_spec: # use last element, could also be only element
+ results.append(self_real)
+ for child in self_real.children:
+ child.searchNodeTypeID(node_spec, results)
+ return results
+
+ def getFieldName(self, field):
+ self_real = self.getRealNode() # incase we're an instance
+
+ for f in self_real.fields:
+ # print f
+ if f and f[0] == field:
+ # print '\tfound field', f
+
+ return f[1:]
+ # print '\tfield not found', field
+ return None
+
+ def getFieldAsInt(self, field, default):
+ self_real = self.getRealNode() # incase we're an instance
+
+ f = self_real.getFieldName(field)
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list