[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