WIP - support for VRML PROTO's
authorCampbell Barton <ideasman42@gmail.com>
Mon, 22 Dec 2008 15:05:07 +0000 (15:05 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Mon, 22 Dec 2008 15:05:07 +0000 (15:05 +0000)
release/scripts/import_web3d.py

index 06cde898ef2944cf16da78ba649dea69b2e93cf8..a4b367b6df89c5e0b9994c864e353c8bcb2a9ab6 100755 (executable)
@@ -201,6 +201,7 @@ def vrmlFormat(data):
 NODE_NORMAL = 1 # {}
 NODE_ARRAY = 2 # []
 NODE_REFERENCE = 3 # USE foobar
+# NODE_PROTO = 4 #
 
 lines = []
 
@@ -211,6 +212,10 @@ def getNodePreText(i, words):
                
                if i>=len(lines):
                        break
+                       '''
+               elif lines[i].startswith('PROTO'):
+                       return NODE_PROTO, i+1
+                       '''
                elif lines[i]=='{':
                        # words.append(lines[i]) # no need
                        # print "OK"
@@ -239,11 +244,45 @@ def getNodePreText(i, words):
        # print "error value!!!", words
        return 0, -1
 
-def is_nodeline(i, words):
+def is_protoline(i, words, proto_field_defs):
+       
+       node_type = NODE_NORMAL
+       # print "PRPTOTOOOO ---- test", i, lines[i]
+       if not lines[i].startswith('PROTO' ):
+               # print 'not a proto'
+               return node_type, -1
+       
+       words[:] = lines[i].split() # TODO - maybe multiline proto's exist?
+       
+       i+=1
+       if lines[i].startswith('['): # ']\n'
+               while not lines[i].startswith(']'):
+                       proto_field_defs.append(lines[i].split())
+                       i+=1
+               i+=1 # advance past the ']\n'
+       
+       if lines[i].startswith('{'):
+               node_type = NODE_NORMAL
+               i+=1 # advance past the '{\n'
+       
+       elif lines[i].startswith('['): # does this even exist??, add incase
+               node_type = NODE_ARRAY
+               i+=1 # advance past the '{\n'
+       
+       print "IS PROTO", i, node_type
+       return node_type, i
+
+def is_nodeline(i, words, proto_field_defs):
        
        if not lines[i][0].isalpha():
                return 0, 0
        
+       # Is this a prototype??
+       proto_type, new_i = is_protoline(i, words, proto_field_defs)
+       if new_i != -1:
+               return proto_type, new_i
+               
+       
        # Simple "var [" type
        if lines[i+1] == '[':
                if lines[i].count('"') % 2 == 0:
@@ -253,6 +292,7 @@ def is_nodeline(i, words):
        node_type, new_i = getNodePreText(i, words)
        
        if not node_type:
+               print "not node_type", lines[i]
                return 0, 0
        
        # Ok, we have a { after some values
@@ -311,10 +351,10 @@ def is_numline(i):
                return True
        except:
                return False
-       
+
 
 class vrmlNode(object):
-       __slots__ = 'id', 'fields', 'node_type', 'parent', 'children', 'parent', 'array_data', 'reference', 'lineno', 'filename', 'blendObject', 'DEF_NAMESPACE', 'ROUTE_IPO_NAMESPACE', 'FIELD_NAMESPACE', 'x3dNode'
+       __slots__ = 'id', 'fields', 'proto_field_defs', 'proto_fields', 'node_type', 'parent', 'children', 'parent', 'array_data', 'reference', 'lineno', 'filename', 'blendObject', 'DEF_NAMESPACE', 'ROUTE_IPO_NAMESPACE', 'PROTO_NAMESPACE', 'x3dNode'
        def __init__(self, parent, node_type, lineno):
                self.id = None
                self.node_type = node_type
@@ -333,7 +373,12 @@ class vrmlNode(object):
                # Store in the root node because each inline file needs its own root node and its own namespace
                self.DEF_NAMESPACE = None 
                self.ROUTE_IPO_NAMESPACE = None 
+               '''
                self.FIELD_NAMESPACE = None
+               '''
+               
+               
+               self.PROTO_NAMESPACE = None
                
                self.reference = None
                
@@ -343,16 +388,25 @@ class vrmlNode(object):
                        return 
                
                self.fields = [] # fields have no order, in some cases rool level values are not unique so dont use a dict
+               self.proto_field_defs = [] # proto field definition eg: "field SFColor seatColor .6 .6 .1"
+               self.proto_fields = [] # proto field usage "diffuseColor IS seatColor"
                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 getProtoDict(self):
+               if self.PROTO_NAMESPACE != None:
+                       return self.PROTO_NAMESPACE
+               else:
+                       return self.parent.getProtoDict()
        
        def getDefDict(self):
                if self.DEF_NAMESPACE != None:
@@ -368,9 +422,10 @@ class vrmlNode(object):
        
        def setRoot(self, filename):
                self.filename = filename
-               self.FIELD_NAMESPACE =          {}
+               # self.FIELD_NAMESPACE =                {}
                self.DEF_NAMESPACE =            {}
                self.ROUTE_IPO_NAMESPACE =      {}
+               self.PROTO_NAMESPACE =          {}
        
        def isRoot(self):
                if self.filename == None:
@@ -404,15 +459,17 @@ class vrmlNode(object):
                        return self.id[0]
                return None
        
-       def getDefName(self):
+       def getSpecialTypeName(self, typename):
                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
-               
+               try:            return self_real.id[ list(self_real.id).index(typename)+1 ]
+               except: return None
+       
+       
+       def getDefName(self):
+               return self.getSpecialTypeName('DEF')
+       
+       def getProtoName(self):
+               return self.getSpecialTypeName('PROTO')
        
        def getChildrenBySpec(self, node_spec): # spec could be Transform, Shape, Appearance
                self_real = self.getRealNode()
@@ -449,7 +506,21 @@ class vrmlNode(object):
                ancestry.append(self)
                for child in self.getRealNode().children:
                        if child not in ancestry:
-                               child.getSerialized(results, ancestry)
+                               # We dont want to load proto's, they are only references
+                               # We could enforce this elsewhere
+                               
+                               # Only add this in a very special case
+                               # where the parent of this object is not the real parent
+                               # - In this case we have added the proto as a child to a node instancing it.
+                               # This is a bit arbitary, but its how Proto's are done with this importer.
+                               if child.getProtoName() == None:
+                                       child.getSerialized(results, ancestry)
+                               else:
+                                       print "\n\n\n\nTEST!!!!!!!!!!!!!"
+                                       print child.getProtoName(), self.getSpec()
+                                       if child.getProtoName()==self.getSpec():
+                                               print "PROTO!!!!!!!!!!!!!!!!\n"
+                                               child.getSerialized(results, ancestry)
                
                return results
                
@@ -462,7 +533,7 @@ class vrmlNode(object):
                        child.searchNodeTypeID(node_spec, results)
                return results
        
-       def getFieldName(self, field):
+       def getFieldName(self, field, ancestry):
                self_real = self.getRealNode() # incase we're an instance
                
                for f in self_real.fields:
@@ -470,14 +541,51 @@ class vrmlNode(object):
                        if f and f[0] == field:
                                # print '\tfound field', f
                                
-                               return f[1:]
+                               if len(f)>=3 and f[1] == 'IS': # eg: 'diffuseColor IS legColor'
+                                       field_id = f[2]
+                                       
+                                       # print "\n\n\n\n\n\nFOND IS!!!"
+                                       f_proto_lookup = None
+                                       i = len(ancestry)
+                                       while i:
+                                               i -= 1
+                                               node = ancestry[i]
+                                               
+                                               # Get the default value from the proto, this can be overwridden by the proto instace
+                                               # 'field SFColor legColor .8 .4 .7'
+                                               for f_def in node.proto_field_defs:
+                                                       if len(f_def) >= 4:
+                                                               if f_def[0]=='field' and f_def[2]==field_id:
+                                                                       f_proto_lookup = f_def[3:]
+                                               
+                                               # Node instance, Will be 1 up from the proto-node in the ancestry list. but NOT its parent.
+                                               # This is the setting as defined by the instance, including this setting is optional,
+                                               # and will override the default PROTO value 
+                                               # eg: 'legColor 1 0 0'
+                                               for f_def in node.fields:
+                                                       # print "\n\n\n\n\nASSSSSSSS", f_def
+                                                       if len(f_def) >= 2:
+                                                               if f_def[0]==field_id:
+                                                                       f_proto_lookup = f_def[1:]
+                                       # print "\n\n\n\nWOOOOHOOO", field, f_proto_lookup
+                                       
+                                       return f_proto_lookup
+                               else:
+                                       # Not using a proto
+                                       return f[1:]
                # print '\tfield not found', field
+               
+               
+               # See if this is a proto name
+               
+               
+               
                return None
        
-       def getFieldAsInt(self, field, default):
+       def getFieldAsInt(self, field, default, ancestry):
                self_real = self.getRealNode() # incase we're an instance
                
-               f = self_real.getFieldName(field)
+               f = self_real.getFieldName(field, ancestry)
                if f==None:     return default
                if ',' in f: f = f[:f.index(',')] # strip after the comma
                
@@ -491,10 +599,10 @@ class vrmlNode(object):
                        print '\tvalue "%s" could not be used as an int for field "%s"' % (f[0], field)
                        return default
        
-       def getFieldAsFloat(self, field, default):
+       def getFieldAsFloat(self, field, default, ancestry):
                self_real = self.getRealNode() # incase we're an instance
                
-               f = self_real.getFieldName(field)
+               f = self_real.getFieldName(field, ancestry)
                if f==None:     return default
                if ',' in f: f = f[:f.index(',')] # strip after the comma
                
@@ -508,10 +616,10 @@ class vrmlNode(object):
                        print '\tvalue "%s" could not be used as a float for field "%s"' % (f[0], field)
                        return default
        
-       def getFieldAsFloatTuple(self, field, default):
+       def getFieldAsFloatTuple(self, field, default, ancestry):
                self_real = self.getRealNode() # incase we're an instance
                
-               f = self_real.getFieldName(field)
+               f = self_real.getFieldName(field, ancestry)
                if f==None:     return default
                # if ',' in f: f = f[:f.index(',')] # strip after the comma
                
@@ -532,10 +640,10 @@ class vrmlNode(object):
                        print '\tvalue "%s" could not be used as a float tuple for field "%s"' % (f, field)
                        return default
        
-       def getFieldAsBool(self, field, default):
+       def getFieldAsBool(self, field, default, ancestry):
                self_real = self.getRealNode() # incase we're an instance
                
-               f = self_real.getFieldName(field)
+               f = self_real.getFieldName(field, ancestry)
                if f==None:     return default
                if ',' in f: f = f[:f.index(',')] # strip after the comma
                
@@ -551,10 +659,10 @@ class vrmlNode(object):
                        print '\t"%s" could not be used as a bool for field "%s"' % (f[1], field)
                        return default
        
-       def getFieldAsString(self, field, default=None):
+       def getFieldAsString(self, field, default, ancestry):
                self_real = self.getRealNode() # incase we're an instance
                
-               f = self_real.getFieldName(field)
+               f = self_real.getFieldName(field, ancestry)
                if f==None:     return default
                if len(f) < 1:
                        print '\t"%s" wrong length for string conversion for field "%s"' % (f, field)
@@ -576,7 +684,7 @@ class vrmlNode(object):
                        print '\tvalue "%s" could not be used as a string for field "%s"' % (f[0], field)
                        return default
        
-       def getFieldAsArray(self, field, group):
+       def getFieldAsArray(self, field, group, ancestry):
                '''
                For this parser arrays are children
                '''
@@ -593,7 +701,7 @@ class vrmlNode(object):
                        
                        # For x3d, should work ok with vrml too
                        # for x3d arrays are fields, vrml they are nodes, annoying but not tooo bad.
-                       data_split = self.getFieldName(field)
+                       data_split = self.getFieldName(field, ancestry)
                        if not data_split:
                                return []
                        array_data = ' '.join(data_split)
@@ -660,7 +768,7 @@ class vrmlNode(object):
                
                return new_array
        
-       def getFieldAsStringArray(self, field):
+       def getFieldAsStringArray(self, field, ancestry):
                '''
                Get a list of strings
                '''
@@ -698,7 +806,7 @@ class vrmlNode(object):
        def __repr__(self):
                level = self.getLevel()
                ind = '  ' * level
-               
+               print self.id
                if self.node_type==NODE_REFERENCE:
                        brackets = ''
                elif self.node_type==NODE_NORMAL:
@@ -717,7 +825,7 @@ class vrmlNode(object):
                        text += ind + "(reference node)\n"
                        return text
                
-               text += ind + 'FIELDS:\n'
+               text += ind + 'FIELDS:' + str(len(self.fields)) + '\n'
                
                for i,item in enumerate(self.fields):
                        text += ind + 'FIELD:\n'
@@ -743,7 +851,10 @@ class vrmlNode(object):
                # If we were an inline then try load the file
                if self.node_type == NODE_NORMAL and self.getSpec() == 'Inline':
                        
-                       url = self.getFieldAsString('url', None)
+                       
+                       ancestry = [] # Warning! - PROTO's using this wont work at all.
+                       url = self.getFieldAsString('url', None, ancestry)
+                       del ancestry
                        
                        if url != None:
                                urls = []
@@ -786,7 +897,7 @@ class vrmlNode(object):
                                                        lines.insert(0, 'root_node____')
                                                        lines.append('}')
                                                        '''
-                                                       ff = open('/test.txt', 'w')
+                                                       ff = open('/root/test.txt', 'w')
                                                        ff.writelines([l+'\n' for l in lines])
                                                        '''
                                                        
@@ -801,8 +912,10 @@ class vrmlNode(object):
                return new_i
        
        def __parse(self, i):
-               # print 'parsing at', i,
-               # print i, self.id, self.lineno
+               '''
+               print 'parsing at', i,
+               print i, self.id, self.lineno
+               '''
                l = lines[i]
                
                if l=='[':
@@ -811,9 +924,12 @@ class vrmlNode(object):
                        i+=1
                else:
                        words = []
-                       node_type, new_i = is_nodeline(i, words)
+                       proto_field_defs = []
+                       
+                       node_type, new_i = is_nodeline(i, words, proto_field_defs)
                        if not node_type: # fail for parsing new node.
-                               raise "error"
+                               print "Failed to parse new node"
+                               raise ValueError
                        
                        if self.node_type==NODE_REFERENCE:
                                # Only assign the reference and quit
@@ -824,38 +940,55 @@ class vrmlNode(object):
                                return new_i
                        
                        self.id = tuple(words)
+                       self.proto_field_defs[:] = proto_field_defs
                        
                        # fill in DEF/USE
                        key = self.getDefName()
-                       
                        if key != None:
                                self.getDefDict()[ key ] = self
                        
+                       key = self.getProtoName()
+                       proto_dict = self.getProtoDict()
+                       if key != None:
+                               proto_dict[ key ] = self
+                       
+                       else: # If we're a proto instance, add the proto node as our child.
+                               spec = self.getSpec()
+                               try:
+                                       self.children.append( proto_dict[spec] )
+                                       #pass
+                               except:
+                                       pass
+                               
+                               del spec
+                       
+                       del proto_dict, key
+                       
                        i = new_i
                
                # print self.id
                ok = True
                while ok:
                        l = lines[i]
-                       # print '\t', i, l
+                       # print '\tDEBUG:', i, self.node_type, l
                        if l=='':
                                i+=1
                                continue 
                        
                        if l=='}':
-                               if self.node_type != NODE_NORMAL:
-                                       print 'wrong node ending, expected an } ' + str(i)
-                                       raise ""
+                               if self.node_type != NODE_NORMAL: # also ends proto nodes, we may want a type for these too.
+                                       print 'wrong node ending, expected an } ' + str(i) + ' ' + str(self.node_type)
+                                       raise ValueError
                                ### print "returning", i
                                return i+1
                        if l==']':
                                if self.node_type != NODE_ARRAY:
-                                       print 'wrong node ending, expected a ] ' + str(i)
-                                       raise ""
+                                       print 'wrong node ending, expected a ] ' + str(i) + ' ' + str(self.node_type)
+                                       raise ValueError
                                ### print "returning", i
                                return i+1
                                
-                       node_type, new_i = is_nodeline(i, [])
+                       node_type, new_i = is_nodeline(i, [], [])
                        if node_type: # check text\n{
                                ### print '\t\tgroup', i
                                child = vrmlNode(self, node_type, i)
@@ -943,6 +1076,7 @@ class vrmlNode(object):
                                                                        # this IS a key but the previous value was not a key, ot it was a defined field.
                                                                        if (not iskey(field_context[-1])) or ((len(field_context)==3 and field_context[1]=='IS')):
                                                                                field_list.append(field_context)
+                                                                               
                                                                                field_context = [value[j]]
                                                                        else:
                                                                                # The last item was not a value, multiple keys are needed in some cases.
@@ -965,17 +1099,26 @@ class vrmlNode(object):
                                                
                                                if value[0]=='field':
                                                        # field SFFloat creaseAngle 4
+                                                       ''' #oldproto
                                                        self.getFieldDict()[value[2]] = value[3:] # skip the first 3 values
+                                                       '''
+                                                       #self.proto_field_defs.append(value)
+                                                       self.proto_field_defs.append(value)
                                                else:
                                                        # Get referenced field
                                                        if len(value) >= 3 and value[1]=='IS':
+                                                               self.fields.append(value)
+                                                               
+                                                               '''
                                                                try:
                                                                        value = [ value[0] ] + self.getFieldDict()[ value[2] ]
                                                                except:
                                                                        print '\tWarning, field could not be found:', value, 'TODO add support for exposedField'
                                                                        print '\t', self.getFieldDict()
                                                                        self.fields.append(value)
+                                                               '''
                                                        else:
+                                                               print "ADDING '%s'" % value
                                                                self.fields.append(value)
                                i+=1
 
@@ -1014,13 +1157,14 @@ def vrml_parse(path):
        lines.append('}')
        # Use for testing our parsed output, so we can check on line numbers.
        
-       '''
-       ff = open('/test.txt', 'w')
+       
+       ff = open('/root/test.txt', 'w')
        ff.writelines([l+'\n' for l in lines])
-       '''
+       ff.close()
+       
        
        # Now evaluate it
-       node_type, new_i = is_nodeline(0, [])
+       node_type, new_i = is_nodeline(0, [], [])
        if not node_type:
                return None, 'Error: VRML file has no starting Node'
        
@@ -1195,12 +1339,12 @@ def translateScale(sca):
        mat[2][2] = sca[2]
        return mat
 
-def translateTransform(node):
-       cent =          node.getFieldAsFloatTuple('center', None) # (0.0, 0.0, 0.0)
-       rot =           node.getFieldAsFloatTuple('rotation', None) # (0.0, 0.0, 1.0, 0.0)
-       sca =           node.getFieldAsFloatTuple('scale', None) # (1.0, 1.0, 1.0)
-       scaori =        node.getFieldAsFloatTuple('scaleOrientation', None) # (0.0, 0.0, 1.0, 0.0)
-       tx =            node.getFieldAsFloatTuple('translation', None) # (0.0, 0.0, 0.0)
+def translateTransform(node, ancestry):
+       cent =          node.getFieldAsFloatTuple('center', None, ancestry) # (0.0, 0.0, 0.0)
+       rot =           node.getFieldAsFloatTuple('rotation', None, ancestry) # (0.0, 0.0, 1.0, 0.0)
+       sca =           node.getFieldAsFloatTuple('scale', None, ancestry) # (1.0, 1.0, 1.0)
+       scaori =        node.getFieldAsFloatTuple('scaleOrientation', None, ancestry) # (0.0, 0.0, 1.0, 0.0)
+       tx =            node.getFieldAsFloatTuple('translation', None, ancestry) # (0.0, 0.0, 0.0)
        
        if cent:
                cent_mat = TranslationMatrix(Vector(cent)).resize4x4()
@@ -1232,11 +1376,11 @@ def translateTransform(node):
        
        return new_mat
 
-def translateTexTransform(node):
-       cent =          node.getFieldAsFloatTuple('center', None) # (0.0, 0.0)
-       rot =           node.getFieldAsFloat('rotation', None) # 0.0
-       sca =           node.getFieldAsFloatTuple('scale', None) # (1.0, 1.0)
-       tx =            node.getFieldAsFloatTuple('translation', None) # (0.0, 0.0)
+def translateTexTransform(node, ancestry):
+       cent =          node.getFieldAsFloatTuple('center', None, ancestry) # (0.0, 0.0)
+       rot =           node.getFieldAsFloat('rotation', None, ancestry) # 0.0
+       sca =           node.getFieldAsFloatTuple('scale', None, ancestry) # (1.0, 1.0)
+       tx =            node.getFieldAsFloatTuple('translation', None, ancestry) # (0.0, 0.0)
        
        
        if cent:
@@ -1279,17 +1423,17 @@ def getFinalMatrix(node, mtx, ancestry):
                mtx = Matrix()
        
        for node_tx in transform_nodes:
-               mat = translateTransform(node_tx)
+               mat = translateTransform(node_tx, ancestry)
                mtx = mtx * mat
        
        return mtx
 
-def importMesh_IndexedFaceSet(geom, bpyima):
+def importMesh_IndexedFaceSet(geom, bpyima, ancestry):
        # print geom.lineno, geom.id, vrmlNode.DEF_NAMESPACE.keys()
        
-       ccw =                           geom.getFieldAsBool('ccw', True)
-       ifs_colorPerVertex =    geom.getFieldAsBool('colorPerVertex', True) # per vertex or per face
-       ifs_normalPerVertex =   geom.getFieldAsBool('normalPerVertex', True)
+       ccw =                           geom.getFieldAsBool('ccw', True, ancestry)
+       ifs_colorPerVertex =    geom.getFieldAsBool('colorPerVertex', True, ancestry) # per vertex or per face
+       ifs_normalPerVertex =   geom.getFieldAsBool('normalPerVertex', True, ancestry)
        
        # This is odd how point is inside Coordinate
        
@@ -1298,14 +1442,14 @@ def importMesh_IndexedFaceSet(geom, bpyima):
        
        coord = geom.getChildBySpec('Coordinate') # works for x3d and vrml
        
-       if coord:       ifs_points = coord.getFieldAsArray('point', 3)
+       if coord:       ifs_points = coord.getFieldAsArray('point', 3, ancestry)
        else:           coord = []
        
        if not coord:
                print '\tWarnint: IndexedFaceSet has no points'
                return None, ccw
        
-       ifs_faces = geom.getFieldAsArray('coordIndex', 0)
+       ifs_faces = geom.getFieldAsArray('coordIndex', 0, ancestry)
        
        coords_tex = None
        if ifs_faces: # In rare cases this causes problems - no faces but UVs???
@@ -1315,8 +1459,8 @@ def importMesh_IndexedFaceSet(geom, bpyima):
                coords_tex = geom.getChildBySpec('TextureCoordinate')
                
                if coords_tex:
-                       ifs_texpoints = coords_tex.getFieldAsArray('point', 2)
-                       ifs_texfaces = geom.getFieldAsArray('texCoordIndex', 0)
+                       ifs_texpoints = coords_tex.getFieldAsArray('point', 2, ancestry)
+                       ifs_texfaces = geom.getFieldAsArray('texCoordIndex', 0, ancestry)
                        
                        if not ifs_texpoints:
                                # IF we have no coords, then dont bother
@@ -1329,11 +1473,11 @@ def importMesh_IndexedFaceSet(geom, bpyima):
        vcolor_spot = None # spot color when we dont have an array of colors
        if vcolor:
                # float to char
-               ifs_vcol = [[int(c*256) for c in col] for col in vcolor.getFieldAsArray('color', 3)]
-               ifs_color_index = geom.getFieldAsArray('colorIndex', 0)
+               ifs_vcol = [[int(c*256) for c in col] for col in vcolor.getFieldAsArray('color', 3, ancestry)]
+               ifs_color_index = geom.getFieldAsArray('colorIndex', 0, ancestry)
                
                if not ifs_vcol:
-                       vcolor_spot = [int(c*256) for c in vcolor.getFieldAsFloatTuple('color', [])]
+                       vcolor_spot = [int(c*256) for c in vcolor.getFieldAsFloatTuple('color', [], ancestry)]
        
        # Convert faces into somthing blender can use
        edges = []
@@ -1546,18 +1690,18 @@ def importMesh_IndexedFaceSet(geom, bpyima):
        
        return bpymesh, ccw
 
-def importMesh_IndexedLineSet(geom):
+def importMesh_IndexedLineSet(geom, ancestry):
        # VRML not x3d
        #coord = geom.getChildByName('coord') # 'Coordinate'
        coord = geom.getChildBySpec('Coordinate') # works for x3d and vrml
-       if coord:       points = coord.getFieldAsArray('point', 3)
+       if coord:       points = coord.getFieldAsArray('point', 3, ancestry)
        else:           points = []
        
        if not points:
                print '\tWarning: IndexedLineSet had no points'
                return None
        
-       ils_lines = geom.getFieldAsArray('coordIndex', 0)
+       ils_lines = geom.getFieldAsArray('coordIndex', 0, ancestry)
        
        lines = []
        line = []
@@ -1596,11 +1740,11 @@ def importMesh_IndexedLineSet(geom):
        return bpycurve
 
 
-def importMesh_PointSet(geom):
+def importMesh_PointSet(geom, ancestry):
        # VRML not x3d
        #coord = geom.getChildByName('coord') # 'Coordinate'
        coord = geom.getChildBySpec('Coordinate')  # works for x3d and vrml
-       if coord:       points = coord.getFieldAsArray('point', 3)
+       if coord:       points = coord.getFieldAsArray('point', 3, ancestry)
        else:           points = []
        
        # vcolor = geom.getChildByName('color') # blender dosnt have per vertex color
@@ -1614,26 +1758,26 @@ GLOBALS['CIRCLE_DETAIL'] = 12
 
 MATRIX_Z_TO_Y = RotationMatrix(90, 4, 'x')
 
-def importMesh_Sphere(geom):
+def importMesh_Sphere(geom, ancestry):
        # bpymesh = bpy.data.meshes.new()
-       diameter = geom.getFieldAsFloat('radius', 0.5) * 2 # * 2 for the diameter
+       diameter = geom.getFieldAsFloat('radius', 0.5, ancestry) * 2 # * 2 for the diameter
        bpymesh = Mesh.Primitives.UVsphere(GLOBALS['CIRCLE_DETAIL'], GLOBALS['CIRCLE_DETAIL'], diameter)  
        bpymesh.transform(MATRIX_Z_TO_Y)
        return bpymesh
 
-def importMesh_Cylinder(geom):
+def importMesh_Cylinder(geom, ancestry):
        # bpymesh = bpy.data.meshes.new()
-       diameter = geom.getFieldAsFloat('radius', 1.0) * 2 # * 2 for the diameter
-       height = geom.getFieldAsFloat('height', 2)
+       diameter = geom.getFieldAsFloat('radius', 1.0, ancestry) * 2 # * 2 for the diameter
+       height = geom.getFieldAsFloat('height', 2, ancestry)
        bpymesh = Mesh.Primitives.Cylinder(GLOBALS['CIRCLE_DETAIL'], diameter, height) 
        bpymesh.transform(MATRIX_Z_TO_Y)
        
        # Warning - Rely in the order Blender adds verts
        # not nice design but wont change soon.
        
-       bottom = geom.getFieldAsBool('bottom', True)
-       side = geom.getFieldAsBool('side', True)
-       top = geom.getFieldAsBool('top', True)
+       bottom = geom.getFieldAsBool('bottom', True, ancestry)
+       side = geom.getFieldAsBool('side', True, ancestry)
+       top = geom.getFieldAsBool('top', True, ancestry)
        
        if not top: # last vert is top center of tri fan.
                bpymesh.verts.delete([(GLOBALS['CIRCLE_DETAIL']+GLOBALS['CIRCLE_DETAIL'])+1])
@@ -1647,18 +1791,18 @@ def importMesh_Cylinder(geom):
        
        return bpymesh
 
-def importMesh_Cone(geom):
+def importMesh_Cone(geom, ancestry):
        # bpymesh = bpy.data.meshes.new()
-       diameter = geom.getFieldAsFloat('bottomRadius', 1.0) * 2 # * 2 for the diameter
-       height = geom.getFieldAsFloat('height', 2)
+       diameter = geom.getFieldAsFloat('bottomRadius', 1.0, ancestry) * 2 # * 2 for the diameter
+       height = geom.getFieldAsFloat('height', 2, ancestry)
        bpymesh = Mesh.Primitives.Cone(GLOBALS['CIRCLE_DETAIL'], diameter, height) 
        bpymesh.transform(MATRIX_Z_TO_Y)
        
        # Warning - Rely in the order Blender adds verts
        # not nice design but wont change soon.
        
-       bottom = geom.getFieldAsBool('bottom', True)
-       side = geom.getFieldAsBool('side', True)
+       bottom = geom.getFieldAsBool('bottom', True, ancestry)
+       side = geom.getFieldAsBool('side', True, ancestry)
        
        if not bottom: # last vert is on the bottom
                bpymesh.verts.delete([GLOBALS['CIRCLE_DETAIL']+1])
@@ -1667,10 +1811,10 @@ def importMesh_Cone(geom):
        
        return bpymesh
 
-def importMesh_Box(geom):
+def importMesh_Box(geom, ancestry):
        # bpymesh = bpy.data.meshes.new()
        
-       size = geom.getFieldAsFloatTuple('size', (2.0, 2.0, 2.0))
+       size = geom.getFieldAsFloatTuple('size', (2.0, 2.0, 2.0), ancestry)
        bpymesh = Mesh.Primitives.Cube(1.0) 
 
        # Scale the box to the size set
@@ -1714,7 +1858,7 @@ def importShape(node, ancestry):
                        textx = appr.getChildBySpec('TextureTransform')
                        
                        if textx:
-                               texmtx = translateTexTransform(textx)
+                               texmtx = translateTexTransform(textx, ancestry)
                        
 
                        
@@ -1726,28 +1870,28 @@ def importShape(node, ancestry):
                                
                                # all values between 0.0 and 1.0, defaults from VRML docs
                                bpymat = bpy.data.materials.new()
-                               bpymat.amb =            mat.getFieldAsFloat('ambientIntensity', 0.2)
-                               bpymat.rgbCol =         mat.getFieldAsFloatTuple('diffuseColor', [0.8, 0.8, 0.8])
+                               bpymat.amb =            mat.getFieldAsFloat('ambientIntensity', 0.2, ancestry)
+                               bpymat.rgbCol =         mat.getFieldAsFloatTuple('diffuseColor', [0.8, 0.8, 0.8], ancestry)
                                
                                # NOTE - blender dosnt support emmisive color
                                # Store in mirror color and approximate with emit.
-                               emit =                          mat.getFieldAsFloatTuple('emissiveColor', [0.0, 0.0, 0.0])
+                               emit =                          mat.getFieldAsFloatTuple('emissiveColor', [0.0, 0.0, 0.0], ancestry)
                                bpymat.mirCol =         emit
                                bpymat.emit =           (emit[0]+emit[1]+emit[2])/3.0
                                
-                               bpymat.hard =           int(1+(510*mat.getFieldAsFloat('shininess', 0.2))) # 0-1 -> 1-511
-                               bpymat.specCol =        mat.getFieldAsFloatTuple('specularColor', [0.0, 0.0, 0.0])
-                               bpymat.alpha =          1.0 - mat.getFieldAsFloat('transparency', 0.0)
+                               bpymat.hard =           int(1+(510*mat.getFieldAsFloat('shininess', 0.2, ancestry))) # 0-1 -> 1-511
+                               bpymat.specCol =        mat.getFieldAsFloatTuple('specularColor', [0.0, 0.0, 0.0], ancestry)
+                               bpymat.alpha =          1.0 - mat.getFieldAsFloat('transparency', 0.0, ancestry)
                                if bpymat.alpha < 0.999:
                                        bpymat.mode |= Material.Modes.ZTRANSP
                        
                        
                        if ima:
                                
-                               ima_url =                       ima.getFieldAsString('url')
+                               ima_url =                       ima.getFieldAsString('url', None, ancestry)
                                
                                if ima_url==None:
-                                       try:            ima_url = ima.getFieldAsStringArray('url')[0] # in some cases we get a list of images.
+                                       try:            ima_url = ima.getFieldAsStringArray('url', None, ancestry)[0] # in some cases we get a list of images.
                                        except:         ima_url = None
                                
                                if ima_url==None:
@@ -1772,8 +1916,8 @@ def importShape(node, ancestry):
                                                else:
                                                        bpymat.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.COL)
                                                        
-                                               ima_repS =                      ima.getFieldAsBool('repeatS', True)
-                                               ima_repT =                      ima.getFieldAsBool('repeatT', True)
+                                               ima_repS =                      ima.getFieldAsBool('repeatS', True, ancestry)
+                                               ima_repT =                      ima.getFieldAsBool('repeatT', True, ancestry)
                                                
                                                # To make this work properly we'd need to scale the UV's too, better to ignore th
                                                # texture.repeat =      max(1, ima_repS * 512), max(1, ima_repT * 512)
@@ -1785,19 +1929,19 @@ def importShape(node, ancestry):
                geom_spec = geom.getSpec()
                ccw = True
                if geom_spec == 'IndexedFaceSet':
-                       bpydata, ccw = importMesh_IndexedFaceSet(geom, bpyima)
+                       bpydata, ccw = importMesh_IndexedFaceSet(geom, bpyima, ancestry)
                elif geom_spec == 'IndexedLineSet':
-                       bpydata = importMesh_IndexedLineSet(geom)
+                       bpydata = importMesh_IndexedLineSet(geom, ancestry)
                elif geom_spec == 'PointSet':
-                       bpydata = importMesh_PointSet(geom)
+                       bpydata = importMesh_PointSet(geom, ancestry)
                elif geom_spec == 'Sphere':
-                       bpydata = importMesh_Sphere(geom)
+                       bpydata = importMesh_Sphere(geom, ancestry)
                elif geom_spec == 'Box':
-                       bpydata = importMesh_Box(geom)
+                       bpydata = importMesh_Box(geom, ancestry)
                elif geom_spec == 'Cylinder':
-                       bpydata = importMesh_Cylinder(geom)
+                       bpydata = importMesh_Cylinder(geom, ancestry)
                elif geom_spec == 'Cone':
-                       bpydata = importMesh_Cone(geom)
+                       bpydata = importMesh_Cone(geom, ancestry)
                else:
                        print '\tWarning: unsupported type "%s"' % geom_spec
                        return
@@ -1810,8 +1954,8 @@ def importShape(node, ancestry):
                        bpyob  = node.blendObject = bpy.data.scenes.active.objects.new(bpydata)
                        
                        if type(bpydata) == Types.MeshType:
-                               is_solid =                      geom.getFieldAsBool('solid', True)
-                               creaseAngle =           geom.getFieldAsFloat('creaseAngle', None)
+                               is_solid =                      geom.getFieldAsBool('solid', True, ancestry)
+                               creaseAngle =           geom.getFieldAsFloat('creaseAngle', None, ancestry)
                                
                                if creaseAngle != None:
                                        bpydata.maxSmoothAngle = 1+int(min(79, creaseAngle * RAD_TO_DEG))
@@ -1852,17 +1996,17 @@ def importShape(node, ancestry):
                        bpyob.setMatrix( getFinalMatrix(node, None, ancestry) )
 
 
-def importLamp_PointLight(node):
+def importLamp_PointLight(node, ancestry):
        vrmlname = node.getDefName()
        if not vrmlname: vrmlname = 'PointLight'
        
-       # ambientIntensity = node.getFieldAsFloat('ambientIntensity', 0.0) # TODO
-       # attenuation = node.getFieldAsFloatTuple('attenuation', (1.0, 0.0, 0.0)) # TODO
-       color = node.getFieldAsFloatTuple('color', (1.0, 1.0, 1.0))
-       intensity = node.getFieldAsFloat('intensity', 1.0) # max is documented to be 1.0 but some files have higher.
-       location = node.getFieldAsFloatTuple('location', (0.0, 0.0, 0.0))
-       # is_on = node.getFieldAsBool('on', True) # TODO
-       radius = node.getFieldAsFloat('radius', 100.0)
+       # ambientIntensity = node.getFieldAsFloat('ambientIntensity', 0.0, ancestry) # TODO
+       # attenuation = node.getFieldAsFloatTuple('attenuation', (1.0, 0.0, 0.0), ancestry) # TODO
+       color = node.getFieldAsFloatTuple('color', (1.0, 1.0, 1.0), ancestry)
+       intensity = node.getFieldAsFloat('intensity', 1.0, ancestry) # max is documented to be 1.0 but some files have higher.
+       location = node.getFieldAsFloatTuple('location', (0.0, 0.0, 0.0), ancestry)
+       # is_on = node.getFieldAsBool('on', True, ancestry) # TODO
+       radius = node.getFieldAsFloat('radius', 100.0, ancestry)
        
        bpylamp = bpy.data.lamps.new()
        bpylamp.setType('Lamp')
@@ -1874,15 +2018,15 @@ def importLamp_PointLight(node):
        
        return bpylamp, mtx
 
-def importLamp_DirectionalLight(node):
+def importLamp_DirectionalLight(node, ancestry):
        vrmlname = node.getDefName()
        if not vrmlname: vrmlname = 'DirectLight'
        
        # ambientIntensity = node.getFieldAsFloat('ambientIntensity', 0.0) # TODO
-       color = node.getFieldAsFloatTuple('color', (1.0, 1.0, 1.0))
-       direction = node.getFieldAsFloatTuple('direction', (0.0, 0.0, -1.0))
-       intensity = node.getFieldAsFloat('intensity', 1.0) # max is documented to be 1.0 but some files have higher.
-       # is_on = node.getFieldAsBool('on', True) # TODO
+       color = node.getFieldAsFloatTuple('color', (1.0, 1.0, 1.0), ancestry)
+       direction = node.getFieldAsFloatTuple('direction', (0.0, 0.0, -1.0), ancestry)
+       intensity = node.getFieldAsFloat('intensity', 1.0, ancestry) # max is documented to be 1.0 but some files have higher.
+       # is_on = node.getFieldAsBool('on', True, ancestry) # TODO
        
        bpylamp = bpy.data.lamps.new(vrmlname)
        bpylamp.setType('Sun')
@@ -1896,20 +2040,20 @@ def importLamp_DirectionalLight(node):
 
 # looks like default values for beamWidth and cutOffAngle were swapped in VRML docs.
 
-def importLamp_SpotLight(node):
+def importLamp_SpotLight(node, ancestry):
        vrmlname = node.getDefName()
        if not vrmlname: vrmlname = 'SpotLight'
        
-       # ambientIntensity = geom.getFieldAsFloat('ambientIntensity', 0.0) # TODO
-       # attenuation = geom.getFieldAsFloatTuple('attenuation', (1.0, 0.0, 0.0)) # TODO
-       beamWidth = node.getFieldAsFloat('beamWidth', 1.570796) * RAD_TO_DEG # max is documented to be 1.0 but some files have higher.
-       color = node.getFieldAsFloatTuple('color', (1.0, 1.0, 1.0))
-       cutOffAngle = node.getFieldAsFloat('cutOffAngle', 0.785398) * RAD_TO_DEG # max is documented to be 1.0 but some files have higher.
-       direction = node.getFieldAsFloatTuple('direction', (0.0, 0.0, -1.0))
-       intensity = node.getFieldAsFloat('intensity', 1.0) # max is documented to be 1.0 but some files have higher.
-       location = node.getFieldAsFloatTuple('location', (0.0, 0.0, 0.0))
-       # is_on = node.getFieldAsBool('on', True) # TODO
-       radius = node.getFieldAsFloat('radius', 100.0)
+       # ambientIntensity = geom.getFieldAsFloat('ambientIntensity', 0.0, ancestry) # TODO
+       # attenuation = geom.getFieldAsFloatTuple('attenuation', (1.0, 0.0, 0.0), ancestry) # TODO
+       beamWidth = node.getFieldAsFloat('beamWidth', 1.570796, ancestry) * RAD_TO_DEG # max is documented to be 1.0 but some files have higher.
+       color = node.getFieldAsFloatTuple('color', (1.0, 1.0, 1.0), ancestry)
+       cutOffAngle = node.getFieldAsFloat('cutOffAngle', 0.785398, ancestry) * RAD_TO_DEG # max is documented to be 1.0 but some files have higher.
+       direction = node.getFieldAsFloatTuple('direction', (0.0, 0.0, -1.0), ancestry)
+       intensity = node.getFieldAsFloat('intensity', 1.0, ancestry) # max is documented to be 1.0 but some files have higher.
+       location = node.getFieldAsFloatTuple('location', (0.0, 0.0, 0.0), ancestry)
+       # is_on = node.getFieldAsBool('on', True, ancestry) # TODO
+       radius = node.getFieldAsFloat('radius', 100.0, ancestry)
        
        bpylamp = bpy.data.lamps.new(vrmlname)
        bpylamp.setType('Spot')
@@ -1935,14 +2079,14 @@ def importLamp_SpotLight(node):
 
 def importLamp(node, spec, ancestry):
        if spec=='PointLight':
-               bpylamp,mtx = importLamp_PointLight(node)
+               bpylamp,mtx = importLamp_PointLight(node, ancestry)
        elif spec=='DirectionalLight':
-               bpylamp,mtx = importLamp_DirectionalLight(node)
+               bpylamp,mtx = importLamp_DirectionalLight(node, ancestry)
        elif spec=='SpotLight':
-               bpylamp,mtx = importLamp_SpotLight(node)
+               bpylamp,mtx = importLamp_SpotLight(node, ancestry)
        else:
                print "Error, not a lamp"
-               raise ""
+               raise ValueError
        
        bpyob = node.blendObject = bpy.data.scenes.active.objects.new(bpylamp)
        bpyob.setMatrix( getFinalMatrix(node, mtx, ancestry) )
@@ -1952,11 +2096,11 @@ def importViewpoint(node, ancestry):
        name = node.getDefName()
        if not name: name = 'Viewpoint'
        
-       fieldOfView = node.getFieldAsFloat('fieldOfView', 0.785398) * RAD_TO_DEG # max is documented to be 1.0 but some files have higher.
-       # jump = node.getFieldAsBool('jump', True)
-       orientation = node.getFieldAsFloatTuple('orientation', (0.0, 0.0, 1.0, 0.0))
-       position = node.getFieldAsFloatTuple('position', (0.0, 0.0, 0.0))
-       description = node.getFieldAsString('description', '')
+       fieldOfView = node.getFieldAsFloat('fieldOfView', 0.785398, ancestry) * RAD_TO_DEG # max is documented to be 1.0 but some files have higher.
+       # jump = node.getFieldAsBool('jump', True, ancestry)
+       orientation = node.getFieldAsFloatTuple('orientation', (0.0, 0.0, 1.0, 0.0), ancestry)
+       position = node.getFieldAsFloatTuple('position', (0.0, 0.0, 0.0), ancestry)
+       description = node.getFieldAsString('description', '', ancestry)
        
        bpycam = bpy.data.cameras.new(name)
        
@@ -1980,59 +2124,72 @@ def importTransform(node, ancestry):
 #def importTimeSensor(node):
 
 
-def translatePositionInterpolator(node, ipo):
-       key = node.getFieldAsArray('key', 0)
-       keyValue = node.getFieldAsArray('keyValue', 3)
-       
-       loc_x = ipo.addCurve('LocX')
-       loc_y = ipo.addCurve('LocY')
-       loc_z = ipo.addCurve('LocZ')
+def translatePositionInterpolator(node, ipo, ancestry):
+       key = node.getFieldAsArray('key', 0, ancestry)
+       keyValue = node.getFieldAsArray('keyValue', 3, ancestry)
        
+       try:
+               loc_x = ipo.addCurve('LocX')
+               loc_y = ipo.addCurve('LocY')
+               loc_z = ipo.addCurve('LocZ')
+       except ValueError:
+               return
+               
        loc_x.interpolation = loc_y.interpolation = loc_z.interpolation = Blender.IpoCurve.InterpTypes.LINEAR
        
        for i, time in enumerate(key):
-               x,y,z = keyValue[i]
+               try:            x,y,z = keyValue[i]
+               except: continue
                
                loc_x.append((time,x))
                loc_y.append((time,y))
                loc_z.append((time,z))
 
-def translateOrientationInterpolator(node, ipo):
-       key = node.getFieldAsArray('key', 0)
-       keyValue = node.getFieldAsArray('keyValue', 4)
+def translateOrientationInterpolator(node, ipo, ancestry):
+       key = node.getFieldAsArray('key', 0, ancestry)
+       keyValue = node.getFieldAsArray('keyValue', 4, ancestry)
        
-       rot_x = ipo.addCurve('RotX')
-       rot_y = ipo.addCurve('RotY')
-       rot_z = ipo.addCurve('RotZ')
+       try:
+               rot_x = ipo.addCurve('RotX')
+               rot_y = ipo.addCurve('RotY')
+               rot_z = ipo.addCurve('RotZ')
+       except ValueError:
+               return
        
        rot_x.interpolation = rot_y.interpolation = rot_z.interpolation = Blender.IpoCurve.InterpTypes.LINEAR
        
        for i, time in enumerate(key):
+               try:            x,y,z,w = keyValue[i]
+               except: continue
                
-               mtx = translateRotation(keyValue[i])
+               mtx = translateRotation((x,y,z,w))
                eul = mtx.toEuler()
                rot_x.append((time,eul.x/10.0))
                rot_y.append((time,eul.y/10.0))
                rot_z.append((time,eul.z/10.0))
 
 # Untested!
-def translateScalarInterpolator(node, ipo):
-       key = node.getFieldAsArray('key', 0)
-       keyValue = node.getFieldAsArray('keyValue', 4)
+def translateScalarInterpolator(node, ipo, ancestry):
+       key = node.getFieldAsArray('key', 0, ancestry)
+       keyValue = node.getFieldAsArray('keyValue', 4, ancestry)
        
-       sca_x = ipo.addCurve('SizeX')
-       sca_y = ipo.addCurve('SizeY')
-       sca_z = ipo.addCurve('SizeZ')
+       try:
+               sca_x = ipo.addCurve('ScaleX')
+               sca_y = ipo.addCurve('ScaleY')
+               sca_z = ipo.addCurve('ScaleZ')
+       except ValueError:
+               return
        
        sca_x.interpolation = sca_y.interpolation = sca_z.interpolation = Blender.IpoCurve.InterpTypes.LINEAR
        
        for i, time in enumerate(key):
-               x,y,z = keyValue[i]
+               try:            x,y,z = keyValue[i]
+               except: continue
                sca_x.append((time,x/10.0))
                sca_y.append((time,y/10.0))
                sca_z.append((time,z/10.0))
 
-def translateTimeSensor(node, ipo):
+def translateTimeSensor(node, ipo, ancestry):
        '''
        Apply a time sensor to an IPO, VRML has many combinations of loop/start/stop/cycle times
        to give different results, for now just do the basics
@@ -2041,15 +2198,15 @@ def translateTimeSensor(node, ipo):
        time_cu = ipo.addCurve('Time')
        time_cu.interpolation = Blender.IpoCurve.InterpTypes.LINEAR
        
-       cycleInterval = node.getFieldAsFloat('cycleInterval', None)
+       cycleInterval = node.getFieldAsFloat('cycleInterval', None, ancestry)
        
-       startTime = node.getFieldAsFloat('startTime', 0.0)
-       stopTime = node.getFieldAsFloat('stopTime', 250.0)
+       startTime = node.getFieldAsFloat('startTime', 0.0, ancestry)
+       stopTime = node.getFieldAsFloat('stopTime', 250.0, ancestry)
                
        if cycleInterval != None:
                stopTime = startTime+cycleInterval
        
-       loop = node.getFieldAsBool('loop', False)
+       loop = node.getFieldAsBool('loop', False, ancestry)
        
        time_cu.append((1+startTime, 0.0))
        time_cu.append((1+stopTime, 1.0/10.0))# anoying, the UI uses /10
@@ -2059,11 +2216,14 @@ def translateTimeSensor(node, ipo):
                time_cu.extend = Blender.IpoCurve.ExtendTypes.CYCLIC # or - EXTRAP, CYCLIC_EXTRAP, CONST, 
 
 
-def importRoute(node):
+def importRoute(node, ancestry):
        '''
        Animation route only at the moment
        '''
        
+       if not hasattr(node, 'fields'):
+               return
+       
        routeIpoDict = node.getRouteIpoDict()
        
        def getIpo(id):
@@ -2100,22 +2260,22 @@ ROUTE champFly001.bindTime TO vpTs.set_startTime
                                if to_type == 'set_position':
                                        ipo = getIpo(to_id)
                                        set_data_from_node = defDict[from_id]
-                                       translatePositionInterpolator(set_data_from_node, ipo)
+                                       translatePositionInterpolator(set_data_from_node, ipo, ancestry)
                                
-                               if to_type == 'set_orientation':
+                               if to_type in ('set_orientation', 'rotation'):
                                        ipo = getIpo(to_id)
                                        set_data_from_node = defDict[from_id]
-                                       translateOrientationInterpolator(set_data_from_node, ipo)
+                                       translateOrientationInterpolator(set_data_from_node, ipo, ancestry)
                                
                                if to_type == 'set_scale':
                                        ipo = getIpo(to_id)
                                        set_data_from_node = defDict[from_id]
-                                       translateScalarInterpolator(set_data_from_node, ipo)
+                                       translateScalarInterpolator(set_data_from_node, ipo, ancestry)
                                
-                       elif from_type == 'bindTime':
+                       elif from_type =='bindTime':
                                ipo = getIpo(from_id)
                                time_node = defDict[to_id]
-                               translateTimeSensor(time_node, ipo)
+                               translateTimeSensor(time_node, ipo, ancestry)
                        
                
 
@@ -2147,6 +2307,12 @@ def load_web3d(path, PREF_FLAT=False, PREF_CIRCLE_DIV=16, HELPER_FUNC = None):
                #       continue
                
                spec = node.getSpec()
+               '''
+               prefix = node.getPrefix()
+               if prefix=='PROTO':
+                       pass
+               el
+               '''
                if spec=='Shape':
                        importShape(node, ancestry)
                elif spec in ('PointLight', 'DirectionalLight', 'SpotLight'):
@@ -2174,7 +2340,7 @@ def load_web3d(path, PREF_FLAT=False, PREF_CIRCLE_DIV=16, HELPER_FUNC = None):
 
        # After we import all nodes, route events - anim paths
        for node, ancestry in all_nodes:
-               importRoute(node)
+               importRoute(node, ancestry)
        
        for node, ancestry in all_nodes:
                if node.isRoot():
@@ -2289,24 +2455,31 @@ if __name__ == '__main__':
 
 # load_web3d('/fe/x3d/www.web3d.org/x3d/content/examples/Basic/StudentProjects/PlayRoom.x3d') # invalid UVs
 
-'''
-import os
-# files = os.popen('find /fe/wrl -iname "*.wrl"').readlines()
-# files = os.popen('find /fe/x3d -iname "*.x3d"').readlines()
-files = os.popen('find   /fe/x3d/X3dExamplesSavage   -iname "*.x3d"').readlines()
 
-files.sort()
-tot = len(files)
-for i, f in enumerate(files):
-       if i < 12803 or i > 1000000:
-               continue
-       #if i != 12686:
-       #       continue
+
+def test():
+       import os
        
-       f = f.strip()
-       print f, i, tot
-       sce = bpy.data.scenes.new(f.split('/')[-1])
-       bpy.data.scenes.active = sce
-       # Window.
-       load_web3d(f, PREF_FLAT=True)
-'''
\ No newline at end of file
+       files = os.popen('find /fe/wrl -iname "*.wrl"').readlines()
+       # files = os.popen('find /fe/x3d -iname "*.x3d"').readlines()
+       # files = os.popen('find   /fe/x3d/X3dExamplesSavage   -iname "*.x3d"').readlines()
+
+       files.sort()
+       tot = len(files)
+       for i, f in enumerate(files):
+               #if i < 126 or i > 1000000:
+               #       continue
+               
+               if i < 181 or i > 1000000:
+                       continue
+               
+               #if i != 12686:
+               #       continue
+               
+               f = f.strip()
+               print f, i, tot
+               sce = bpy.data.scenes.new(str(i) + '_' + f.split('/')[-1])
+               bpy.data.scenes.active = sce
+               # Window.
+               load_web3d(f, PREF_FLAT=True)
+       
\ No newline at end of file