PyCOLLADA Internals ===================== Examinine pycollada to see how to extract info needed for Chroma. XML parsing into IndexLists ------------------------------ Collada object holds IndexLists `_geometries` `_nodes` that behave like both lists and dicts based on `id`:: In [106]: dae._geometries['near-radslab-box-10xb3e60a0'] Out[106]: In [118]: len(dae.geometries) // properties provide dict and list access to these IndexedLists Out[118]: 249 In [125]: len(dae.nodes) Out[125]: 249 In [126]: len(dae.materials) Out[126]: 36 In [119]: dae.geometries['near-radslab-box-10xb3e60a0'] Out[119]: In [120]: dae.geometries[-1] Out[120]: In [121]: dae.geometries[-2] Out[121]: In [122]: dae.geometries[0] Out[122]: In [123]: dae.geometries[1] Out[123]: collada._loadNodes -------------------- collada/__init__.py:: 397 def _loadNodes(self): 398 libnodes = self.xmlnode.findall(tag('library_nodes')) 399 if libnodes is not None: 400 for libnode in libnodes: 401 if libnode is not None: 402 tried_loading = [] 403 succeeded = False 404 for node in libnode.findall(tag('node')): 405 try: 406 N = scene.loadNode(self, node, {}) 407 except scene.DaeInstanceNotLoadedError as ex: 408 tried_loading.append((node, ex)) 409 except DaeError as ex: 410 self.handleError(ex) 411 else: 412 if N is not None: 413 self.nodes.append(N) 414 succeeded = True scene.loadNode ~~~~~~~~~~~~~~~~~ Node classes for each XML tag collada/scene.py:: 829 def loadNode( collada, node, localscope ): 830 """Generic scene node loading from a xml `node` and a `collada` object. 831 832 Knowing the supported nodes, create the appropiate class for the given node 833 and return it. 834 835 """ 836 if node.tag == tag('node'): return Node.load(collada, node, localscope) 837 elif node.tag == tag('translate'): return TranslateTransform.load(collada, node) 838 elif node.tag == tag('rotate'): return RotateTransform.load(collada, node) 839 elif node.tag == tag('scale'): return ScaleTransform.load(collada, node) 840 elif node.tag == tag('matrix'): return MatrixTransform.load(collada, node) 841 elif node.tag == tag('lookat'): return LookAtTransform.load(collada, node) 842 elif node.tag == tag('instance_geometry'): return GeometryNode.load(collada, node) 843 elif node.tag == tag('instance_camera'): return CameraNode.load(collada, node) 844 elif node.tag == tag('instance_light'): return LightNode.load(collada, node) 845 elif node.tag == tag('instance_controller'): return ControllerNode.load(collada, node) 846 elif node.tag == tag('instance_node'): return NodeNode.load(collada, node, localscope) 847 elif node.tag == tag('extra'): 848 return ExtraNode.load(collada, node) 849 elif node.tag == tag('asset'): 850 return None 851 else: raise DaeUnsupportedError('Unknown scene node %s' % str(node.tag)) Node.load ~~~~~~~~~~~~~ #. transform subnodes are appended to transforms which is used to construct the parent `Node` other subnodes become the children #. source XML id is reused, this *id* will not be unique after instance-ing reuse in scene graph formation collada/scene.py:: 307 class Node(SceneNode): 308 """Represents a node object, which is a point on the scene graph, as defined in the collada tag. 309 310 Contains the list of transformations effecting the node as well as any children. 311 """ ... 402 @staticmethod 403 def load( collada, node, localscope ): 404 id = node.get('id') 405 children = [] 406 transforms = [] 407 408 for subnode in node: 409 try: 410 n = loadNode(collada, subnode, localscope) 411 if isinstance(n, Transform): 412 transforms.append(n) 413 elif n is not None: 414 children.append(n) 415 except DaeError as ex: 416 collada.handleError(ex) 417 418 return Node(id, children, transforms, xmlnode=node) 419 420 def __str__(self): 421 return '' % (len(self.transforms), len(self.children)) Dump source xml:: In [131]: from collada.xmlutil import etree as ElementTree In [136]: print ElementTree.tostring(dae.nodes[-1].xmlnode) -0.543174 0.83962 0 -16520 -0.83962 -0.543174 0 -802110 0 0 1 -2110 0.0 0.0 0.0 1.0 Binding objects to a list of transforms ---------------------------------------- :: 029 class Geometry(DaeObject): 030 """A class containing the data coming from a COLLADA tag""" 031 ... 304 def bind(self, matrix, materialnodebysymbol): 305 """Binds this geometry to a transform matrix and material mapping. 306 The geometry's points get transformed by the given matrix and its 307 inputs get mapped to the given materials. 308 309 :param numpy.array matrix: 310 A 4x4 numpy float matrix 311 :param dict materialnodebysymbol: 312 A dictionary with the material symbols inside the primitive 313 assigned to :class:`collada.scene.MaterialNode` defined in the 314 scene 315 316 :rtype: :class:`collada.geometry.BoundGeometry` 317 318 """ 319 return BoundGeometry(self, matrix, materialnodebysymbol) 320 321 def __str__(self): 322 return '' % (self.id, len(self.primitives)) 323 324 def __repr__(self): 325 return str(self) 326 327 328 class BoundGeometry( object ): 329 """A geometry bound to a transform matrix and material mapping. 330 This gets created when a geometry is instantiated in a scene. 331 Do not create this manually.""" 332 333 def __init__(self, geom, matrix, materialnodebysymbol): 334 self.matrix = matrix 335 """The matrix bound to""" 336 self.materialnodebysymbol = materialnodebysymbol 337 """Dictionary with the material symbols inside the primitive 338 assigned to :class:`collada.scene.MaterialNode` defined in the 339 scene""" 340 self._primitives = geom.primitives 341 self.original = geom 342 """The original :class:`collada.geometry.Geometry` object this 343 is bound to""" 344 345 def __len__(self): 346 """Returns the number of primitives in the bound geometry""" 347 return len(self._primitives) 348 349 def primitives(self): 350 """Returns an iterator that iterates through the primitives in 351 the bound geometry. Each value returned will be of base type 352 :class:`collada.primitive.BoundPrimitive`""" 353 for p in self._primitives: 354 boundp = p.bind( self.matrix, self.materialnodebysymbol ) 355 yield boundp 356 357 def __str__(self): 358 return '' % (self.original.id, len(self)) SceneNodes ~~~~~~~~~~~~ :: 43 class SceneNode(DaeObject): 44 """Abstract base class for all nodes within a scene.""" 45 46 def objects(self, tipo, matrix=None): 47 """Iterate through all objects under this node that match `tipo`. 48 The objects will be bound and transformed via the scene transformations. 49 50 :param str tipo: 51 A string for the desired object type. This can be one of 'geometry', 52 'camera', 'light', or 'controller'. 53 :param numpy.matrix matrix: 54 An optional transformation matrix 55 56 :rtype: generator that yields the type specified 57 58 """ 59 pass :: simon:collada blyth$ grep SceneNode *.py scene.py:class SceneNode(DaeObject): scene.py:class Node(SceneNode): scene.py:class GeometryNode(SceneNode): scene.py:class ControllerNode(SceneNode): scene.py:class MaterialNode(SceneNode): scene.py:class CameraNode(SceneNode): scene.py:class LightNode(SceneNode): scene.py:class ExtraNode(SceneNode): :: 479 class GeometryNode(SceneNode): 480 """Represents a geometry instance in a scene, as defined in the collada tag.""" 481 ... 515 def objects(self, tipo, matrix=None): 516 """Yields a :class:`collada.geometry.BoundGeometry` if ``tipo=='geometry'``""" 517 if tipo == 'geometry': 518 if matrix is None: matrix = numpy.identity(4, dtype=numpy.float32) 519 materialnodesbysymbol = {} 520 for mat in self.materials: 521 materialnodesbysymbol[mat.symbol] = mat 522 yield self.geometry.bind(matrix, materialnodesbysymbol)