예제 #1
0
    def __init__(self, id, name, effect, xmlnode=None):
        """Creates a material.
        
        :param str id:
          A unique string identifier for the material
        :param str name:
          A name for the material
        :param collada.material.Effect effect:
          The effect instantiated in this material
        :param xmlnode:
          If loaded from xml, the xml node
        
        """

        self.id = id
        """The unique string identifier for the material"""
        self.name = name
        """The name for the material"""
        self.effect = effect
        """The :class:`collada.material.Effect` instantiated in this material"""
        
        if xmlnode != None:
            self.xmlnode = xmlnode
            """ElementTree representation of the surface."""
        else:
            self.xmlnode = E.material(
                E.instance_effect(url="#%s" % self.effect.id)
            , id=str(self.id), name=str(self.name))
예제 #2
0
    def __init__(self, sources, material, polygons, xmlnode=None):
        """A Polygons should not be created manually. Instead, call the
        :meth:`collada.geometry.Geometry.createPolygons` method after
        creating a geometry instance.
        """
        
        max_offset = max([ max([input[0] for input in input_type_array])
                          for input_type_array in sources.itervalues() if len(input_type_array) > 0])
        
        vcounts = numpy.zeros(len(polygons), dtype=numpy.int32)
        for i, poly in enumerate(polygons):
            vcounts[i] = len(poly) / (max_offset + 1)

        indices = numpy.concatenate(polygons)

        super(Polygons, self).__init__(sources, material, indices, vcounts, xmlnode)
        
        if xmlnode is not None: self.xmlnode = xmlnode
        else:
            acclen = len(polygons) 

            self.xmlnode = E.polygons(count=str(acclen), material=self.material)
            
            all_inputs = []
            for semantic_list in self.sources.itervalues():
                all_inputs.extend(semantic_list)
            for offset, semantic, sourceid, set, src in all_inputs:
                inpnode = E.input(offset=str(offset), semantic=semantic, source=sourceid)
                if set is not None:
                    inpnode.set('set', str(set))
                self.xmlnode.append(inpnode)
            
            for poly in polygons:
                self.xmlnode.append(E.p(' '.join(map(str, poly.flatten().tolist()))))
예제 #3
0
    def __init__(self, id, color, xmlnode = None):
        """Create a new ambient light.

        :param str id:
          A unique string identifier for the light
        :param tuple color:
          Either a tuple of size 3 containing the RGB color value
          of the light or a tuple of size 4 containing the RGBA
          color value of the light
        :param xmlnode:
          If loaded from xml, the xml node

        """
        self.id = id
        """The unique string identifier for the light"""
        self.color = color
        """Either a tuple of size 3 containing the RGB color value
          of the light or a tuple of size 4 containing the RGBA
          color value of the light"""
        if xmlnode != None:
            self.xmlnode = xmlnode
            """ElementTree representation of the light."""
        else:
            self.xmlnode = E.light(
                E.technique_common(
                    E.ambient(
                        E.color(' '.join(map(str, self.color ) ))
                    )
                )
            , id=self.id, name=self.id)
예제 #4
0
    def __init__(self, id, color, xmlnode = None):
        """Create a new directional light.

        :param str id:
          A unique string identifier for the light
        :param tuple color:
          Either a tuple of size 3 containing the RGB color value
          of the light or a tuple of size 4 containing the RGBA
          color value of the light
        :param xmlnode:
          If loaded from xml, the xml node

        """
        self.id = id
        """The unique string identifier for the light"""
        self.direction = numpy.array( [0, 0, -1], dtype=numpy.float32 )
        #Not documenting this because it doesn't make sense to set the direction
        # of an unbound light. The direction isn't set until binding in a scene.
        self.color = color
        """Either a tuple of size 3 containing the RGB color value
          of the light or a tuple of size 4 containing the RGBA
          color value of the light"""
        if xmlnode != None:
            self.xmlnode = xmlnode
            """ElementTree representation of the light."""
        else:
            self.xmlnode = E.light(
                E.technique_common(
                    E.directional(
                        E.color(' '.join(map(str, self.color)))
                    )
                )
            , id=self.id, name=self.id)
예제 #5
0
    def __init__(self, symbol, target, inputs, xmlnode = None):
        """Creates a material node

        :param str symbol:
          The symbol within a geometry this material should be bound to
        :param collada.material.Material target:
          The material object being bound to
        :param list inputs:
          A list of tuples of the form ``(semantic, input_semantic, set)`` mapping
          texcoords or other inputs to material input channels, e.g.
          ``('TEX0', 'TEXCOORD', '0')`` would map the effect parameter ``'TEX0'``
          to the ``'TEXCOORD'`` semantic of the geometry, using texture coordinate
          set ``0``.
        :param xmlnode:
          When loaded, the xmlnode it comes from

        """
        self.symbol = symbol
        """The symbol within a geometry this material should be bound to"""
        self.target = target
        """An object of type :class:`collada.material.Material` representing the material object being bound to"""
        self.inputs = inputs
        """A list of tuples of the form ``(semantic, input_semantic, set)`` mapping
          texcoords or other inputs to material input channels, e.g.
          ``('TEX0', 'TEXCOORD', '0')`` would map the effect parameter ``'TEX0'``
          to the ``'TEXCOORD'`` semantic of the geometry, using texture coordinate
          set ``0``."""
        if xmlnode is not None:
            self.xmlnode = xmlnode
            """ElementTree representation of the material node."""
        else:
            self.xmlnode = E.instance_material(
                *[E.bind_vertex_input(semantic=sem, input_semantic=input_sem, input_set=set)
                  for sem, input_sem, set in self.inputs]
            , **{'symbol': self.symbol, 'target':"#%s"%self.target.id} )
예제 #6
0
파일: light.py 프로젝트: phaeta/pycollada
    def __init__(self, id, color, xmlnode=None):
        """Create a new sun light.

        :Parameters:
          id
            Id for the object
          color
            Light color
          xmlnode
            If load form xml, the xml node

        """
        self.id = id
        """Id in the light library."""
        # TODO: check if this is actually the initial direction
        self.direction = numpy.array([0, 0, 1], dtype=numpy.float32)
        """Incoming direction of the light."""
        self.color = color
        """Light color."""
        if xmlnode != None:
            self.xmlnode = xmlnode
        else:
            self.xmlnode = E.light(
                E.technique_common(E.directional(E.color(" ".join([str(v) for v in self.color])))),
                id=self.id,
                name=self.id,
            )
예제 #7
0
    def __init__(self, geometry, materials=None, xmlnode=None):
        """Creates a geometry node

        :param collada.geometry.Geometry geometry:
          A geometry to instantiate in the scene
        :param list materials:
          A list containing items of type :class:`collada.scene.MaterialNode`.
          Each of these represents a material that the geometry should be
          bound to.
        :param xmlnode:
          When loaded, the xmlnode it comes from

        """
        self.geometry = geometry
        """An object of type :class:`collada.geometry.Geometry` representing the
        geometry to bind in the scene"""
        self.materials = []
        """A list containing items of type :class:`collada.scene.MaterialNode`.
          Each of these represents a material that the geometry is bound to."""
        if materials is not None:
            self.materials = materials
        if xmlnode != None:
            self.xmlnode = xmlnode
            """ElementTree representation of the geometry node."""
        else:
            self.xmlnode = E.instance_geometry(url="#%s" % self.geometry.id)
            if len(self.materials) > 0:
                self.xmlnode.append(E.bind_material(
                    E.technique_common(
                        *[mat.xmlnode for mat in self.materials]
                    )
                ))
예제 #8
0
    def save(self):
        """Saves the geometry node back to :attr:`xmlnode`"""
        self.xmlnode.set('url', "#%s" % self.geometry.id)
        
        for m in self.materials:
            m.save()
        
        matparent = self.xmlnode.find('%s/%s'%( tag('bind_material'), tag('technique_common') ) )
        if matparent is None and len(self.materials)==0:
            return
        elif matparent is None:
            matparent = E.technique_common()
            self.xmlnode.append(E.bind_material(matparent))
        elif len(self.materials) == 0 and matparent is not None:
            bindnode = self.xmlnode.find('%s' % tag('bind_material'))
            self.xmlnode.remove(bindnode)
            return

        for m in self.materials:
            if m.xmlnode not in matparent:
                matparent.append(m.xmlnode)
        xmlnodes = [m.xmlnode for m in self.materials]
        for n in matparent:
            if n not in xmlnodes:
                matparent.remove(n)
예제 #9
0
    def __init__(self, id, surface, minfilter=None, magfilter=None, xmlnode=None):
        """Create a Sampler2D object.
        
        :param str id:
          A string identifier for the sampler within the local scope of the material
        :param collada.material.Surface surface:
          Surface instance that this object samples from
        :param str minfilter:
          Minification filter string id, see collada spec for details
        :param str magfilter:
          Maximization filter string id, see collada spec for details
        :param xmlnode:
          If loaded from xml, the xml node

        """
        self.id = id
        """The string identifier for the sampler within the local scope of the material"""
        self.surface = surface
        """Surface instance that this object samples from"""
        self.minfilter = minfilter
        """Minification filter string id, see collada spec for details"""
        self.magfilter = magfilter
        """Maximization filter string id, see collada spec for details"""
        if xmlnode != None:
            self.xmlnode = xmlnode
            """ElementTree representation of the sampler."""
        else:
            sampler_node = E.sampler2D(E.source(self.surface.id))
            if minfilter:
                sampler_node.append(E.minfilter(self.minfilter))
            if magfilter:
                sampler_node.append(E.magfilter(self.magfilter))
                
            self.xmlnode = E.newparam(sampler_node, sid=self.id)
예제 #10
0
    def __init__(self, id, img, format=None, xmlnode=None):
        """Creates a surface.
        
        :param str id:
          A string identifier for the surface within the local scope of the material
        :param collada.material.CImage img:
          The image object
        :param str format:
          The format of the image
        :param xmlnode:
          If loaded from xml, the xml node
        
        """

        self.id = id
        """The string identifier for the surface within the local scope of the material"""
        self.image = img
        """:class:`collada.material.CImage` object from the image library."""
        self.format = format if format is not None else "A8R8G8B8"
        """Format string."""
        if xmlnode != None:
            self.xmlnode = xmlnode
            """ElementTree representation of the surface."""
        else:
            self.xmlnode = E.newparam(
                E.surface(
                    E.init_from(self.image.id),
                    E.format(self.format)
                , type="2D")
            , sid=self.id)
예제 #11
0
    def __init__(self, id, path, collada = None, xmlnode = None):
        """Create an image object.
        
        :param str id:
          A unique string identifier for the image
        :param str path:
          Path relative to the collada document where the image is located
        :param collada.Collada collada:
          The collada object this image belongs to
        :param xmlnode:
          If loaded from xml, the node this data comes from

        """
        self.id = id
        """The unique string identifier for the image"""
        self.path = path
        """Path relative to the collada document where the image is located"""
        
        self.collada = collada
        self._data = None
        self._pilimage = None
        self._uintarray = None
        self._floatarray = None
        if xmlnode != None:
            self.xmlnode = xmlnode
            """ElementTree representation of the image."""
        else:
            self.xmlnode = E.image(
                E.init_from(path)
            , id=self.id, name=self.id)
예제 #12
0
 def getPropNode(prop, value):
     propnode = E(prop)
     if type(value) is Map:
         propnode.append(copy.deepcopy(value.xmlnode))
     elif type(value) is float:
         propnode.append(E.float(str(value)))
     else:
         propnode.append(E.color(' '.join(map(str, value) )))
     return propnode
예제 #13
0
    def __init__(self, id, color, constant_att=None, linear_att=None, quad_att=None, zfar=None, xmlnode = None):
        """Create a new sun light.

        :param str id:
          A unique string identifier for the light
        :param tuple color:
          Either a tuple of size 3 containing the RGB color value
          of the light or a tuple of size 4 containing the RGBA
          color value of the light
        :param float constant_att:
          Constant attenuation factor
        :param float linear_att:
          Linear attenuation factor
        :param float quad_att:
          Quadratic attenuation factor
        :param float zfar:
          Distance to the far clipping plane
        :param xmlnode:
          If loaded from xml, the xml node

        """
        self.id = id
        """The unique string identifier for the light"""
        self.position = numpy.array( [0, 0, 0], dtype=numpy.float32 )
        #Not documenting this because it doesn't make sense to set the position
        # of an unbound light. The position isn't set until binding in a scene.
        self.color = color
        """Either a tuple of size 3 containing the RGB color value
          of the light or a tuple of size 4 containing the RGBA
          color value of the light"""
        self.constant_att = constant_att
        """Constant attenuation factor."""
        self.linear_att = linear_att
        """Linear attenuation factor."""
        self.quad_att = quad_att
        """Quadratic attenuation factor."""
        self.zfar = zfar
        """Distance to the far clipping plane"""
        
        if xmlnode != None:
            self.xmlnode = xmlnode
            """ElementTree representation of the light."""
        else:
            pnode = E.point(
                E.color(' '.join(map(str, self.color ) ))
            )
            if self.constant_att is not None:
                pnode.append(E.constant_attenuation(str(self.constant_att)))
            if self.linear_att is not None:
                pnode.append(E.linear_attenuation(str(self.linear_att)))
            if self.quad_att is not None:
                pnode.append(E.quadratic_attenuation(str(self.quad_att)))
            if self.zfar is not None:
                pnode.append(E.zfar(str(self.zvar)))
            
            self.xmlnode = E.light(
                E.technique_common(pnode)
            , id=self.id, name=self.id)
예제 #14
0
 def __init__(self, x, y, z, angle, xmlnode=None):
     """Creates a rotation transformation
     
     :param float x:
       x coordinate
     :param float y:
       y coordinate
     :param float z:
       z coordinate
     :param float angle:
       angle of rotation, in radians
     :param xmlnode:
        When loaded, the xmlnode it comes from
     
     """
     self.x = x
     """x coordinate"""
     self.y = y
     """y coordinate"""
     self.z = z
     """z coordinate"""
     self.angle = angle
     """angle of rotation, in radians"""
     self.matrix = makeRotationMatrix(x, y, z, angle*numpy.pi/180.0)
     """The resulting transformation matrix. This will be a numpy.array of size 4x4."""
     self.xmlnode = xmlnode
     """ElementTree representation of the transform."""
     if xmlnode is None:
         self.xmlnode = E.rotate(' '.join([str(x),str(y),str(z),str(angle)]))
예제 #15
0
 def __init__(self, id, nodes, xmlnode=None, collada=None):
     """Create a scene
     
     :param str id:
       A unique string identifier for the scene
     :param list nodes:
       A list of type :class:`collada.scene.Node` representing the nodes in the scene
     :param xmlnode:
       When loaded, the xmlnode it comes from
     :param collada:
       The collada instance this is part of
     
     """
     self.id = id
     """The unique string identifier for the scene"""
     self.nodes = nodes
     """A list of type :class:`collada.scene.Node` representing the nodes in the scene"""
     self.collada = collada
     """The collada instance this is part of"""
     if xmlnode != None:
         self.xmlnode = xmlnode
         """ElementTree representation of the scene node."""
     else:
         self.xmlnode = E.visual_scene(id=self.id)
         for node in nodes:
             self.xmlnode.append( node.xmlnode )
예제 #16
0
 def __init__(self, x, y, z, xmlnode=None):
     """Creates a translation transformation
     
     :param float x:
       x coordinate
     :param float y:
       y coordinate
     :param float z:
       z coordinate
     :param xmlnode:
        When loaded, the xmlnode it comes from
     
     """
     self.x = x
     """x coordinate"""
     self.y = y
     """y coordinate"""
     self.z = z
     """z coordinate"""
     self.matrix = numpy.identity(4, dtype=numpy.float32)
     """The resulting transformation matrix. This will be a numpy.array of size 4x4."""
     self.matrix[:3,3] = [ x, y, z ]
     self.xmlnode = xmlnode
     """ElementTree representation of the transform."""
     if xmlnode is None:
         self.xmlnode = E.translate(' '.join([str(x),str(y),str(z)]))
예제 #17
0
 def _recreateXmlNode(self):
     perspective_node = E.perspective()
     if self.xfov is not None:
         perspective_node.append(E.xfov(str(self.xfov)))
     if self.yfov is not None:
         perspective_node.append(E.yfov(str(self.yfov)))
     if self.aspect_ratio is not None:
         perspective_node.append(E.aspect_ratio(str(self.aspect_ratio)))
     perspective_node.append(E.znear(str(self.znear)))
     perspective_node.append(E.zfar(str(self.zfar)))
     self.xmlnode = E.camera(
         E.optics(
             E.technique_common(perspective_node)
         )
     , id=self.id, name=self.id)
예제 #18
0
 def _recreateXmlNode(self):
     orthographic_node = E.orthographic()
     if self.xmag is not None:
         orthographic_node.append(E.xmag(str(self.xmag)))
     if self.ymag is not None:
         orthographic_node.append(E.ymag(str(self.ymag)))
     if self.aspect_ratio is not None:
         orthographic_node.append(E.aspect_ratio(str(self.aspect_ratio)))
     orthographic_node.append(E.znear(str(self.znear)))
     orthographic_node.append(E.zfar(str(self.zfar)))
     self.xmlnode = E.camera(
         E.optics(
             E.technique_common(orthographic_node)
         )
     , id=self.id, name=self.id)
예제 #19
0
    def __init__(self, id, data, components, xmlnode=None):
        """Create a name source instance.

        :param str id:
          A unique string identifier for the source
        :param numpy.array data:
          Numpy array (unshaped) with the source values
        :param tuple components:
          Tuple of strings describing the semantic of the data,
          e.g. ``('JOINT')`` would cause :attr:`data` to be
          reshaped as ``(-1, 1)``
        :param xmlnode:
          When loaded, the xmlnode it comes from.

        """
          
        self.id = id
        """The unique string identifier for the source"""
        self.data = data
        """Numpy array with the source values. This will be shaped as ``(-1,N)`` where ``N = len(self.components)``"""
        self.data.shape = (-1, len(components) )
        self.components = components
        """Tuple of strings describing the semantic of the data, e.g. ``('JOINT')``"""
        if xmlnode != None:
            self.xmlnode = xmlnode
            """ElementTree representation of the source."""
        else:
            self.data.shape = (-1,)
            txtdata = ' '.join(map(str, self.data.tolist() ))
            rawlen = len( self.data )
            self.data.shape = (-1, len(self.components) )
            acclen = len( self.data )
            stridelen = len(self.components)
            sourcename = "%s-array"%self.id
            
            self.xmlnode = E.source(
                E.Name_array(txtdata, count=str(rawlen), id=sourcename),
                E.technique_common(
                    E.accessor(
                        *[E.param(type='Name', name=c) for c in self.components]
                    , **{'count':str(acclen), 'stride':str(stridelen), 'source':sourcename})
                )
            , id=self.id )
예제 #20
0
 def _recreateXmlNode(self):
     self.index.shape = (-1)
     acclen = len(self.index)
     txtindices = ' '.join(map(str, self.index.tolist()))
     self.index.shape = (-1, 3, self.nindices)
     
     self.xmlnode = E.triangles(count=str(self.ntriangles))
     if self.material is not None:
         self.xmlnode.set('material', self.material)
     
     all_inputs = []
     for semantic_list in self.sources.itervalues():
         all_inputs.extend(semantic_list)
     for offset, semantic, sourceid, set, src in all_inputs:
         inpnode = E.input(offset=str(offset), semantic=semantic, source=sourceid)
         if set is not None:
             inpnode.set('set', str(set))
         self.xmlnode.append(inpnode)
     
     self.xmlnode.append(E.p(txtindices))
예제 #21
0
 def _recreateXmlNode(self):
     self.xmlnode = E.asset()
     for contributor in self.contributors:
         self.xmlnode.append(contributor.xmlnode)
     self.xmlnode.append(E.created(self.created.isoformat()))
     if self.keywords is not None:
         self.xmlnode.append(E.keywords(self.keywords))
     self.xmlnode.append(E.modified(self.modified.isoformat()))
     if self.revision is not None:
         self.xmlnode.append(E.revision(self.revision))
     if self.subject is not None:
         self.xmlnode.append(E.subject(self.subject))
     if self.title is not None:
         self.xmlnode.append(E.title(self.title))
     if self.unitmeter is not None and self.unitname is not None:
         self.xmlnode.append(E.unit(name=self.unitname, meter=str(self.unitmeter)))
     self.xmlnode.append(E.up_axis(self.upaxis))
예제 #22
0
 def __init__(self, xmlnode):
     """Create an extra node which stores arbitrary xml
     
     :param xmlnode:
       Should be an ElementTree instance of tag type <extra>
     
     """
     if xmlnode != None:
         self.xmlnode = xmlnode
         """ElementTree representation of the extra node."""
     else:
         self.xmlnode = E.extra()
예제 #23
0
 def save(self):
     """Saves the surface data back to :attr:`xmlnode`"""
     surfacenode = self.xmlnode.find( tag('surface') )
     initnode = surfacenode.find( tag('init_from') )
     if self.format:
         formatnode = surfacenode.find( tag('format') )
         if formatnode is None:
             surfacenode.append(E.format(self.format))
         else:
             formatnode.text = self.format
     initnode.text = self.image.id
     self.xmlnode.set('sid', self.id)
예제 #24
0
파일: light.py 프로젝트: phaeta/pycollada
    def __init__(self, id, color, xmlnode=None):
        """Create a new ambient light.

        :Parameters:
          id
            Id for the object
          color
            Light color
          xmlnode
            If load form xml, the xml node

        """
        self.id = id
        """Id in the light library."""
        self.color = color
        """Light color."""
        if xmlnode != None:
            self.xmlnode = xmlnode
        else:
            self.xmlnode = E.light(
                E.technique_common(E.ambient(E.color(" ".join([str(v) for v in self.color])))), id=self.id, name=self.id
            )
예제 #25
0
    def save(self):
        """Saves the material node back to :attr:`xmlnode`"""
        self.xmlnode.set('symbol', self.symbol)
        self.xmlnode.set('target', "#%s"%self.target.id)

        inputs_in = []
        for i in self.xmlnode.findall( tag('bind_vertex_input') ):
            input_tuple = ( i.get('semantic'), i.get('input_semantic'), i.get('input_set') )
            if input_tuple not in self.inputs:
                self.xmlnode.remove(i)
            else:
                inputs_in.append(input_tuple)
        for i in self.inputs:
            if i not in inputs_in:
                self.xmlnode.append(E.bind_vertex_input(semantic=i[0], input_semantic=i[1], input_set=i[2]))
예제 #26
0
 def __init__(self, camera, xmlnode=None):
     """Create a camera instance
     
     :param collada.camera.Camera camera:
       The camera being instantiated
     :param xmlnode:
       When loaded, the xmlnode it comes from
     
     """
     self.camera = camera
     """An object of type :class:`collada.camera.Camera` representing the instantiated camera"""
     if xmlnode != None:
         self.xmlnode = xmlnode
         """ElementTree representation of the camera node."""
     else:
         self.xmlnode = E.instance_camera(url="#%s"%camera.id)
예제 #27
0
 def __init__(self, light, xmlnode=None):
     """Create a light instance
     
     :param collada.light.Light light:
       The light being instantiated
     :param xmlnode:
       When loaded, the xmlnode it comes from
     
     """
     self.light = light
     """An object of type :class:`collada.light.Light` representing the instantiated light"""
     if xmlnode != None:
         self.xmlnode = xmlnode
         """ElementTree representation of the light node."""
     else:
         self.xmlnode = E.instance_light(url="#%s"%light.id)
예제 #28
0
 def __init__(self, matrix, xmlnode=None):
     """Creates a matrix transformation
     
     :param numpy.array matrix:
       This should be an unshaped numpy array of floats of length 16
     :param xmlnode:
        When loaded, the xmlnode it comes from
     
     """
     self.matrix = matrix
     """The resulting transformation matrix. This will be a numpy.array of size 4x4."""
     if len(self.matrix) != 16: raise DaeMalformedError('Corrupted matrix transformation node')
     self.matrix.shape = (4, 4)
     self.xmlnode = xmlnode
     """ElementTree representation of the transform."""
     if xmlnode is None:
         self.xmlnode = E.matrix(' '.join(map(str, self.matrix.flat)))
예제 #29
0
    def __init__(self, node, xmlnode=None):
        """Creates a node node

        :param collada.scene.Node node:
          A node to instantiate in the scene
        :param xmlnode:
          When loaded, the xmlnode it comes from

        """
        self.node = node
        """An object of type :class:`collada.scene.Node` representing the node to bind in the scene"""

        if xmlnode != None:
            self.xmlnode = xmlnode
            """ElementTree representation of the node node."""
        else:
            self.xmlnode = E.instance_node(url="#%s" % self.node.id)
예제 #30
0
    def __init__(self, id, children=None, transforms=None, xmlnode=None):
        """Create a node in the scene graph.
        
        :param str id:
          A unique string identifier for the node
        :param list children:
          A list of child nodes of this node. This can contain any
          object that inherits from :class:`collada.scene.SceneNode`
        :param list transforms:
          A list of transformations effecting the node. This can
          contain any object that inherits from :class:`collada.scene.Transform`
        :param xmlnode:
          When loaded, the xmlnode it comes from

        """
        self.id = id
        """The unique string identifier for the node"""
        self.children = []
        """A list of child nodes of this node. This can contain any
          object that inherits from :class:`collada.scene.SceneNode`"""
        if children is not None:
            self.children = children
        self.transforms = []
        if transforms is not None:
            self.transforms = transforms
        """A list of transformations effecting the node. This can
          contain any object that inherits from :class:`collada.scene.Transform`"""
        self.matrix = numpy.identity(4, dtype=numpy.float32)
        """A numpy.array of size 4x4 containing a transformation matrix that
        combines all the transformations in :attr:`transforms`. This will only
        be updated after calling :meth:`save`."""

        for t in self.transforms:
            self.matrix = numpy.dot(self.matrix, t.matrix)

        if xmlnode is not None:
            self.xmlnode = xmlnode
            """ElementTree representation of the transform."""
        else:
            self.xmlnode = E.node(id=self.id, name=self.id)
            for t in self.transforms:
                self.xmlnode.append(t.xmlnode)
            for c in self.children:
                self.xmlnode.append(c.xmlnode)