Esempio n. 1
0
    def __init__(self, sources, material, index, xmlnode=None):
        """A TriangleSet should not be created manually. Instead, call the
        :meth:`collada.geometry.Geometry.createTriangleSet` method after
        creating a geometry instance.
        """

        if len(sources) == 0:
            raise DaeIncompleteError("A triangle set needs at least one input for vertex positions")
        if not "VERTEX" in sources:
            raise DaeIncompleteError("Triangle set requires vertex input")

        max_offset = max(
            [
                max([input[0] for input in input_type_array])
                for input_type_array in sources.values()
                if len(input_type_array) > 0
            ]
        )

        self.material = material
        self.index = index
        self.indices = self.index
        self.nindices = max_offset + 1
        self.index.shape = (-1, 3, self.nindices)
        self.ntriangles = len(self.index)
        self.sources = sources

        if len(self.index) > 0:
            self._vertex = sources["VERTEX"][0][4].data
            self._vertex_index = self.index[:, :, sources["VERTEX"][0][0]]
            self.maxvertexindex = numpy.max(self._vertex_index)
            checkSource(sources["VERTEX"][0][4], ("X", "Y", "Z"), self.maxvertexindex)
        else:
            self._vertex = None
            self._vertex_index = None
            self.maxvertexindex = -1

        if "NORMAL" in sources and len(sources["NORMAL"]) > 0 and len(self.index) > 0:
            self._normal = sources["NORMAL"][0][4].data
            self._normal_index = self.index[:, :, sources["NORMAL"][0][0]]
            self.maxnormalindex = numpy.max(self._normal_index)
            checkSource(sources["NORMAL"][0][4], ("X", "Y", "Z"), self.maxnormalindex)
        else:
            self._normal = None
            self._normal_index = None
            self.maxnormalindex = -1

        if "TEXCOORD" in sources and len(sources["TEXCOORD"]) > 0 and len(self.index) > 0:
            self._texcoordset = tuple([texinput[4].data for texinput in sources["TEXCOORD"]])
            self._texcoord_indexset = tuple(
                [self.index[:, :, sources["TEXCOORD"][i][0]] for i in xrange(len(sources["TEXCOORD"]))]
            )
            self.maxtexcoordsetindex = [numpy.max(tex_index) for tex_index in self._texcoord_indexset]
            for i, texinput in enumerate(sources["TEXCOORD"]):
                checkSource(texinput[4], ("S", "T"), self.maxtexcoordsetindex[i])
        else:
            self._texcoordset = tuple()
            self._texcoord_indexset = tuple()
            self.maxtexcoordsetindex = -1

        if "TEXTANGENT" in sources and len(sources["TEXTANGENT"]) > 0 and len(self.index) > 0:
            self._textangentset = tuple([texinput[4].data for texinput in sources["TEXTANGENT"]])
            self._textangent_indexset = tuple(
                [self.index[:, :, sources["TEXTANGENT"][i][0]] for i in xrange(len(sources["TEXTANGENT"]))]
            )
            self.maxtextangentsetindex = [numpy.max(tex_index) for tex_index in self._textangent_indexset]
            for i, texinput in enumerate(sources["TEXTANGENT"]):
                checkSource(texinput[4], ("X", "Y", "Z"), self.maxtextangentsetindex[i])
        else:
            self._textangentset = tuple()
            self._textangent_indexset = tuple()
            self.maxtextangentsetindex = -1

        if "TEXBINORMAL" in sources and len(sources["TEXBINORMAL"]) > 0 and len(self.index) > 0:
            self._texbinormalset = tuple([texinput[4].data for texinput in sources["TEXBINORMAL"]])
            self._texbinormal_indexset = tuple(
                [self.index[:, :, sources["TEXBINORMAL"][i][0]] for i in xrange(len(sources["TEXBINORMAL"]))]
            )
            self.maxtexbinormalsetindex = [numpy.max(tex_index) for tex_index in self._texbinormal_indexset]
            for i, texinput in enumerate(sources["TEXBINORMAL"]):
                checkSource(texinput[4], ("X", "Y", "Z"), self.maxtexbinormalsetindex[i])
        else:
            self._texbinormalset = tuple()
            self._texbinormal_indexset = tuple()
            self.maxtexbinormalsetindex = -1

        if xmlnode is not None:
            self.xmlnode = xmlnode
        else:
            self._recreateXmlNode()
Esempio n. 2
0
    def __init__(self, sources, material, index, xmlnode=None):
        """A TriangleSet should not be created manually. Instead, call the
        :meth:`collada.geometry.Geometry.createTriangleSet` method after
        creating a geometry instance.
        """

        if len(sources) == 0:
            raise DaeIncompleteError(
                'A triangle set needs at least one input for vertex positions')
        if not 'VERTEX' in sources:
            raise DaeIncompleteError('Triangle set requires vertex input')

        max_offset = max([
            max([input[0] for input in input_type_array])
            for input_type_array in sources.values()
            if len(input_type_array) > 0
        ])

        self.material = material
        self.index = index
        self.indices = self.index
        self.nindices = max_offset + 1
        self.index.shape = (-1, 3, self.nindices)
        self.ntriangles = len(self.index)
        self.sources = sources

        if len(self.index) > 0:
            self._vertex = sources['VERTEX'][0][4].data
            self._vertex_index = self.index[:, :, sources['VERTEX'][0][0]]
            self.maxvertexindex = numpy.max(self._vertex_index)
            checkSource(sources['VERTEX'][0][4], ('X', 'Y', 'Z'),
                        self.maxvertexindex)
        else:
            self._vertex = None
            self._vertex_index = None
            self.maxvertexindex = -1

        if 'NORMAL' in sources and len(sources['NORMAL']) > 0 and len(
                self.index) > 0:
            self._normal = sources['NORMAL'][0][4].data
            self._normal_index = self.index[:, :, sources['NORMAL'][0][0]]
            self.maxnormalindex = numpy.max(self._normal_index)
            checkSource(sources['NORMAL'][0][4], ('X', 'Y', 'Z'),
                        self.maxnormalindex)
        else:
            self._normal = None
            self._normal_index = None
            self.maxnormalindex = -1

        if 'TEXCOORD' in sources and len(sources['TEXCOORD']) > 0 and len(
                self.index) > 0:
            self._texcoordset = tuple(
                [texinput[4].data for texinput in sources['TEXCOORD']])
            self._texcoord_indexset = tuple([
                self.index[:, :, sources['TEXCOORD'][i][0]]
                for i in xrange(len(sources['TEXCOORD']))
            ])
            self.maxtexcoordsetindex = [
                numpy.max(tex_index) for tex_index in self._texcoord_indexset
            ]
            for i, texinput in enumerate(sources['TEXCOORD']):
                checkSource(texinput[4], ('S', 'T'),
                            self.maxtexcoordsetindex[i])
        else:
            self._texcoordset = tuple()
            self._texcoord_indexset = tuple()
            self.maxtexcoordsetindex = -1

        if 'TEXTANGENT' in sources and len(sources['TEXTANGENT']) > 0 and len(
                self.index) > 0:
            self._textangentset = tuple(
                [texinput[4].data for texinput in sources['TEXTANGENT']])
            self._textangent_indexset = tuple([
                self.index[:, :, sources['TEXTANGENT'][i][0]]
                for i in xrange(len(sources['TEXTANGENT']))
            ])
            self.maxtextangentsetindex = [
                numpy.max(tex_index) for tex_index in self._textangent_indexset
            ]
            for i, texinput in enumerate(sources['TEXTANGENT']):
                checkSource(texinput[4], ('X', 'Y', 'Z'),
                            self.maxtextangentsetindex[i])
        else:
            self._textangentset = tuple()
            self._textangent_indexset = tuple()
            self.maxtextangentsetindex = -1

        if 'TEXBINORMAL' in sources and len(
                sources['TEXBINORMAL']) > 0 and len(self.index) > 0:
            self._texbinormalset = tuple(
                [texinput[4].data for texinput in sources['TEXBINORMAL']])
            self._texbinormal_indexset = tuple([
                self.index[:, :, sources['TEXBINORMAL'][i][0]]
                for i in xrange(len(sources['TEXBINORMAL']))
            ])
            self.maxtexbinormalsetindex = [
                numpy.max(tex_index)
                for tex_index in self._texbinormal_indexset
            ]
            for i, texinput in enumerate(sources['TEXBINORMAL']):
                checkSource(texinput[4], ('X', 'Y', 'Z'),
                            self.maxtexbinormalsetindex[i])
        else:
            self._texbinormalset = tuple()
            self._texbinormal_indexset = tuple()
            self.maxtexbinormalsetindex = -1

        if xmlnode is not None: self.xmlnode = xmlnode
        else:
            self._recreateXmlNode()
Esempio n. 3
0
    def __init__(self,
                 sourcebyid,
                 bind_shape_matrix,
                 joint_source,
                 joint_matrix_source,
                 weight_source,
                 weight_joint_source,
                 vcounts,
                 vertex_weight_index,
                 offsets,
                 geometry,
                 controller_node=None,
                 skin_node=None):
        """Create a skin.

        :Parameters:
          sourceById
            A dict mapping id's to a collada source
          bind_shape_matrix
            A numpy array of floats (pre-shape)
          joint_source
            The string id for the joint source
          joint_matrix_source
            The string id for the joint matrix source
          weight_source
            The string id for the weight source
          weight_joint_source
            The string id for the joint source of weights
          vcounts
            A list with the number of influences on each vertex
          vertex_weight_index
            An array with the indexes as they come from <v> array
          offsets
            A list with the offsets in the weight index array for each source
            in (joint, weight)
          geometry
            The source geometry this should be applied to (geometry.Geometry)
          controller_node
            XML node of the <controller> tag which is the parent of this
          skin_node
            XML node of the <skin> tag if this is from there

        """
        self.sourcebyid = sourcebyid
        self.bind_shape_matrix = bind_shape_matrix
        self.joint_source = joint_source
        self.joint_matrix_source = joint_matrix_source
        self.weight_source = weight_source
        self.weight_joint_source = weight_joint_source
        self.vcounts = vcounts
        self.vertex_weight_index = vertex_weight_index
        self.offsets = offsets
        self.geometry = geometry
        self.controller_node = controller_node
        self.skin_node = skin_node
        self.xmlnode = controller_node

        if not type(self.geometry) is Geometry:
            raise DaeMalformedError('Invalid reference geometry in skin')

        self.id = controller_node.get('id')
        if self.id is None:
            raise DaeMalformedError('Controller node requires an ID')

        self.nindices = max(self.offsets) + 1

        if len(bind_shape_matrix) != 16:
            raise DaeMalformedError('Corrupted bind shape matrix in skin')
        self.bind_shape_matrix.shape = (4, 4)

        if not (joint_source in sourcebyid
                and joint_matrix_source in sourcebyid):
            raise DaeBrokenRefError("Input in joints not found")
        if not (type(sourcebyid[joint_source]) is source.NameSource
                or type(sourcebyid[joint_source]) is source.IDRefSource):
            raise DaeIncompleteError(
                "Could not find joint name input for skin")
        if not type(sourcebyid[joint_matrix_source]) is source.FloatSource:
            raise DaeIncompleteError(
                "Could not find joint matrix source for skin")
        joint_names = [j for j in sourcebyid[joint_source]]
        joint_matrices = sourcebyid[joint_matrix_source].data
        joint_matrices.shape = (-1, 4, 4)
        if len(joint_names) != len(joint_matrices):
            raise DaeMalformedError(
                "Skin joint and matrix inputs must be same length")
        self.joint_matrices = {}
        for n, m in zip(joint_names, joint_matrices):
            self.joint_matrices[n] = m

        if not (weight_source in sourcebyid
                and weight_joint_source in sourcebyid):
            raise DaeBrokenRefError("Weights input in joints not found")
        if not type(sourcebyid[weight_source]) is source.FloatSource:
            raise DaeIncompleteError("Could not find weight inputs for skin")
        if not (type(sourcebyid[weight_joint_source]) is source.NameSource or
                type(sourcebyid[weight_joint_source]) is source.IDRefSource):
            raise DaeIncompleteError(
                "Could not find weight joint source input for skin")
        self.weights = sourcebyid[weight_source]
        self.weight_joints = sourcebyid[weight_joint_source]

        try:
            newshape = []
            at = 0
            for ct in self.vcounts:
                this_set = self.vertex_weight_index[self.nindices *
                                                    at:self.nindices *
                                                    (at + ct)]
                this_set.shape = (ct, self.nindices)
                newshape.append(numpy.array(this_set))
                at += ct
            self.index = newshape
        except:
            raise DaeMalformedError(
                'Corrupted vcounts or index in skin weights')

        try:
            self.joint_index = [
                influence[:, self.offsets[0]] for influence in self.index
            ]
            self.weight_index = [
                influence[:, self.offsets[1]] for influence in self.index
            ]
        except:
            raise DaeMalformedError('Corrupted joint or weight index in skin')

        self.max_joint_index = numpy.max([
            numpy.max(joint) if len(joint) > 0 else 0
            for joint in self.joint_index
        ])
        self.max_weight_index = numpy.max([
            numpy.max(weight) if len(weight) > 0 else 0
            for weight in self.weight_index
        ])
        checkSource(self.weight_joints, ('JOINT', ), self.max_joint_index)
        checkSource(self.weights, ('WEIGHT', ), self.max_weight_index)
Esempio n. 4
0
    def __init__(self, sources, material, index, xmlnode=None):
        """A LineSet should not be created manually. Instead, call the
        :meth:`collada.geometry.Geometry.createLineSet` method after
        creating a geometry instance.
        """

        if len(sources) == 0:
            raise DaeIncompleteError(
                'A line set needs at least one input for vertex positions')
        if not 'VERTEX' in sources:
            raise DaeIncompleteError('Line set requires vertex input')

        #find max offset
        max_offset = max([
            max([input[0] for input in input_type_array])
            for input_type_array in sources.values()
            if len(input_type_array) > 0
        ])

        self.sources = sources
        self.material = material
        self.index = index
        self.indices = self.index
        self.nindices = max_offset + 1
        self.index.shape = (-1, 2, self.nindices)
        self.nlines = len(self.index)

        if len(self.index) > 0:
            self._vertex = sources['VERTEX'][0][4].data
            self._vertex_index = self.index[:, :, sources['VERTEX'][0][0]]
            self.maxvertexindex = numpy.max(self._vertex_index)
            checkSource(sources['VERTEX'][0][4], ('X', 'Y', 'Z'),
                        self.maxvertexindex)
        else:
            self._vertex = None
            self._vertex_index = None
            self.maxvertexindex = -1

        if 'NORMAL' in sources and len(sources['NORMAL']) > 0 \
                and len(self.index) > 0:
            self._normal = sources['NORMAL'][0][4].data
            self._normal_index = self.index[:, :, sources['NORMAL'][0][0]]
            self.maxnormalindex = numpy.max(self._normal_index)
            checkSource(sources['NORMAL'][0][4], ('X', 'Y', 'Z'),
                        self.maxnormalindex)
        else:
            self._normal = None
            self._normal_index = None
            self.maxnormalindex = -1

        if 'TEXCOORD' in sources and len(sources['TEXCOORD']) > 0 \
                and len(self.index) > 0:
            self._texcoordset = tuple(
                [texinput[4].data for texinput in sources['TEXCOORD']])
            self._texcoord_indexset = tuple([
                self.index[:, :, sources['TEXCOORD'][i][0]]
                for i in xrange(len(sources['TEXCOORD']))
            ])
            self.maxtexcoordsetindex = [
                numpy.max(tex_index) for tex_index in self._texcoord_indexset
            ]
            for i, texinput in enumerate(sources['TEXCOORD']):
                checkSource(texinput[4], ('S', 'T'),
                            self.maxtexcoordsetindex[i])
        else:
            self._texcoordset = tuple()
            self._texcoord_indexset = tuple()
            self.maxtexcoordsetindex = -1

        if xmlnode is not None:
            self.xmlnode = xmlnode
            """ElementTree representation of the line set."""
        else:
            self.index.shape = (-1)
            acclen = len(self.index)
            txtindices = ' '.join(map(str, self.index.tolist()))
            self.index.shape = (-1, 2, self.nindices)

            self.xmlnode = E.lines(count=str(self.nlines),
                                   material=self.material)

            all_inputs = []
            for semantic_list in self.sources.values():
                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))
Esempio n. 5
0
    def __init__(self, sources, material, index, vcounts, xmlnode=None):
        """A Polylist should not be created manually. Instead, call the
        :meth:`collada.geometry.Geometry.createPolylist` method after
        creating a geometry instance.
        """

        if len(sources) == 0: raise DaeIncompleteError('A polylist set needs at least one input for vertex positions')
        if not 'VERTEX' in sources: raise DaeIncompleteError('Polylist requires vertex input')

        #find max offset
        max_offset = max([ max([input[0] for input in input_type_array])
                          for input_type_array in sources.values() if len(input_type_array) > 0])

        self.material = material
        self.index = index
        self.indices = self.index
        self.nindices = max_offset + 1
        self.vcounts = vcounts
        self.sources = sources
        self.index.shape = (-1, self.nindices)
        self.npolygons = len(self.vcounts)
        self.nvertices = numpy.sum(self.vcounts) if len(self.index) > 0 else 0
        self.polyends = numpy.cumsum(self.vcounts)
        self.polystarts = self.polyends - self.vcounts
        self.polyindex = numpy.dstack((self.polystarts, self.polyends))[0]

        if len(self.index) > 0:
            self._vertex = sources['VERTEX'][0][4].data
            self._vertex_index = self.index[:,sources['VERTEX'][0][0]]
            self.maxvertexindex = numpy.max( self._vertex_index )
            checkSource(sources['VERTEX'][0][4], ('X', 'Y', 'Z'), self.maxvertexindex)
        else:
            self._vertex = None
            self._vertex_index = None
            self.maxvertexindex = -1

        if 'NORMAL' in sources and len(sources['NORMAL']) > 0 and len(self.index) > 0:
            self._normal = sources['NORMAL'][0][4].data
            self._normal_index = self.index[:,sources['NORMAL'][0][0]]
            self.maxnormalindex = numpy.max( self._normal_index )
            checkSource(sources['NORMAL'][0][4], ('X', 'Y', 'Z'), self.maxnormalindex)
        else:
            self._normal = None
            self._normal_index = None
            self.maxnormalindex = -1

        if 'TEXCOORD' in sources and len(sources['TEXCOORD']) > 0 \
                and len(self.index) > 0:
            self._texcoordset = tuple([texinput[4].data
                for texinput in sources['TEXCOORD']])
            self._texcoord_indexset = tuple([ self.index[:,sources['TEXCOORD'][i][0]]
                for i in xrange(len(sources['TEXCOORD'])) ])
            self.maxtexcoordsetindex = [numpy.max(each)
                for each in self._texcoord_indexset]
            for i, texinput in enumerate(sources['TEXCOORD']):
                checkSource(texinput[4], ('S', 'T'), self.maxtexcoordsetindex[i])
        else:
            self._texcoordset = tuple()
            self._texcoord_indexset = tuple()
            self.maxtexcoordsetindex = -1

        if xmlnode is not None:
            self.xmlnode = xmlnode
            """ElementTree representation of the line set."""
        else:
            txtindices = ' '.join(map(str, self.indices.flatten().tolist()))
            acclen = len(self.indices)

            self.xmlnode = E.polylist(count=str(self.npolygons),
                    material=self.material)

            all_inputs = []
            for semantic_list in self.sources.values():
                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)

            vcountnode = E.vcount(' '.join(map(str, self.vcounts)))
            self.xmlnode.append(vcountnode)
            self.xmlnode.append(E.p(txtindices))
Esempio n. 6
0
    def __init__(self, sourcebyid, bind_shape_matrix, joint_source, joint_matrix_source,
                 weight_source, weight_joint_source, vcounts, vertex_weight_index,
                 offsets, geometry, controller_node=None, skin_node=None):
        """Create a skin.

        :Parameters:
          sourceById
            A dict mapping id's to a collada source
          bind_shape_matrix
            A numpy array of floats (pre-shape)
          joint_source
            The string id for the joint source
          joint_matrix_source
            The string id for the joint matrix source
          weight_source
            The string id for the weight source
          weight_joint_source
            The string id for the joint source of weights
          vcounts
            A list with the number of influences on each vertex
          vertex_weight_index
            An array with the indexes as they come from <v> array
          offsets
            A list with the offsets in the weight index array for each source
            in (joint, weight)
          geometry
            The source geometry this should be applied to (geometry.Geometry)
          controller_node
            XML node of the <controller> tag which is the parent of this
          skin_node
            XML node of the <skin> tag if this is from there

        """
        self.sourcebyid = sourcebyid
        self.bind_shape_matrix = bind_shape_matrix
        self.joint_source = joint_source
        self.joint_matrix_source = joint_matrix_source
        self.weight_source = weight_source
        self.weight_joint_source = weight_joint_source
        self.vcounts = vcounts
        self.vertex_weight_index = vertex_weight_index
        self.offsets = offsets
        self.geometry = geometry
        self.controller_node = controller_node
        self.skin_node = skin_node
        self.xmlnode = controller_node

        if not type(self.geometry) is Geometry:
            raise DaeMalformedError('Invalid reference geometry in skin')

        self.id = controller_node.get('id')
        if self.id is None:
            raise DaeMalformedError('Controller node requires an ID')

        self.nindices = max(self.offsets) + 1

        if len(bind_shape_matrix) != 16:
            raise DaeMalformedError('Corrupted bind shape matrix in skin')
        self.bind_shape_matrix.shape = (4,4)

        if not(joint_source in sourcebyid and joint_matrix_source in sourcebyid):
            raise DaeBrokenRefError("Input in joints not found")
        if not(type(sourcebyid[joint_source]) is source.NameSource or type(sourcebyid[joint_source]) is source.IDRefSource):
            raise DaeIncompleteError("Could not find joint name input for skin")
        if not type(sourcebyid[joint_matrix_source]) is source.FloatSource:
            raise DaeIncompleteError("Could not find joint matrix source for skin")
        joint_names = [j for j in sourcebyid[joint_source]]
        joint_matrices = sourcebyid[joint_matrix_source].data
        joint_matrices.shape = (-1,4,4)
        if len(joint_names) != len(joint_matrices):
            raise DaeMalformedError("Skin joint and matrix inputs must be same length")
        self.joint_matrices = {}
        for n,m in zip(joint_names, joint_matrices):
            self.joint_matrices[n] = m

        if not(weight_source in sourcebyid and weight_joint_source in sourcebyid):
            raise DaeBrokenRefError("Weights input in joints not found")
        if not type(sourcebyid[weight_source]) is source.FloatSource:
            raise DaeIncompleteError("Could not find weight inputs for skin")
        if not(type(sourcebyid[weight_joint_source]) is source.NameSource or type(sourcebyid[weight_joint_source]) is source.IDRefSource):
            raise DaeIncompleteError("Could not find weight joint source input for skin")
        self.weights = sourcebyid[weight_source]
        self.weight_joints = sourcebyid[weight_joint_source]

        try:
            newshape = []
            at = 0
            for ct in self.vcounts:
                this_set = self.vertex_weight_index[self.nindices*at:self.nindices*(at+ct)]
                this_set.shape = (ct, self.nindices)
                newshape.append(numpy.array(this_set))
                at+=ct
            self.index = newshape
        except:
            raise DaeMalformedError('Corrupted vcounts or index in skin weights')

        try:
            self.joint_index = [influence[:, self.offsets[0]] for influence in self.index]
            self.weight_index = [influence[:, self.offsets[1]] for influence in self.index]
        except:
            raise DaeMalformedError('Corrupted joint or weight index in skin')

        self.max_joint_index = numpy.max( [numpy.max(joint) if len(joint) > 0 else 0 for joint in self.joint_index] )
        self.max_weight_index = numpy.max( [numpy.max(weight) if len(weight) > 0 else 0 for weight in self.weight_index] )
        checkSource(self.weight_joints, ('JOINT',), self.max_joint_index)
        checkSource(self.weights, ('WEIGHT',), self.max_weight_index)
    def __init__(self,
                 sourcebyid,
                 bind_shape_matrix,
                 joint_source,
                 joint_matrix_source,
                 weight_source,
                 weight_joint_source,
                 vcounts,
                 vertex_weight_index,
                 offsets,
                 geometry,
                 controller_node=None,
                 skin_node=None,
                 controllerID=None):
        """Create a skin.

        :Parameters:
          sourceById
            A dict mapping id's to a collada source
          bind_shape_matrix
            A numpy array of floats (pre-shape)
          joint_source
            The string id for the joint source
          joint_matrix_source
            The string id for the joint matrix source
          weight_source
            The string id for the weight source
          weight_joint_source
            The string id for the joint source of weights
          vcounts
            A list with the number of influences on each vertex
          vertex_weight_index
            An array with the indexes as they come from <v> array
          offsets
            A list with the offsets in the weight index array for each source
            in (joint, weight)
          geometry
            The source geometry this should be applied to (geometry.Geometry)
          controller_node
            XML node of the <controller> tag which is the parent of this
          skin_node
            XML node of the <skin> tag if this is from there
          controllerID
            The string id for current controller (optional)
            if controller_node is not provided, feed controllerID from here directly

        """
        self.sourceById = sourcebyid
        self.bind_shape_matrix = bind_shape_matrix
        self.joint_source = joint_source
        self.joint_matrix_source = joint_matrix_source
        self.weight_source = weight_source
        self.weight_joint_source = weight_joint_source
        self.vcounts = vcounts
        self.vertex_weight_index = vertex_weight_index
        self.offsets = offsets
        self.geometry = geometry
        self.controller_node = controller_node
        self.skin_node = skin_node

        if not type(self.geometry) is Geometry:
            raise DaeMalformedError('Invalid reference geometry in skin')

        if controller_node is not None:
            self.id = controller_node.get('id')
        else:
            if controllerID:
                self.id = controllerID
        if self.id is None:
            raise DaeMalformedError('Controller node requires an ID')

        self.nindices = max(self.offsets) + 1

        if len(bind_shape_matrix) != 16:
            raise DaeMalformedError('Corrupted bind shape matrix in skin')
        self.bind_shape_matrix.shape = (4, 4)

        if not (joint_source in sourcebyid
                and joint_matrix_source in sourcebyid):
            raise DaeBrokenRefError("Input in joints not found")
        if not (type(sourcebyid[joint_source]) is source.NameSource
                or type(sourcebyid[joint_source]) is source.IDRefSource):
            raise DaeIncompleteError(
                "Could not find joint name input for skin")
        if not type(sourcebyid[joint_matrix_source]) is source.FloatSource:
            raise DaeIncompleteError(
                "Could not find joint matrix source for skin")
        joint_names = [j for j in sourcebyid[joint_source]]
        joint_matrices = sourcebyid[joint_matrix_source].data
        joint_matrices.shape = (-1, 4, 4)
        if len(joint_names) != len(joint_matrices):
            raise DaeMalformedError(
                "Skin joint and matrix inputs must be same length")
        self.joint_matrices = {}
        for n, m in zip(joint_names, joint_matrices):
            self.joint_matrices[n] = m

        if not (weight_source in sourcebyid
                and weight_joint_source in sourcebyid):
            raise DaeBrokenRefError("Weights input in joints not found")
        if not type(sourcebyid[weight_source]) is source.FloatSource:
            raise DaeIncompleteError("Could not find weight inputs for skin")
        if not (type(sourcebyid[weight_joint_source]) is source.NameSource or
                type(sourcebyid[weight_joint_source]) is source.IDRefSource):
            raise DaeIncompleteError(
                "Could not find weight joint source input for skin")
        self.weights = sourcebyid[weight_source]
        self.weight_joints = sourcebyid[weight_joint_source]

        try:
            newshape = []
            at = 0
            for ct in self.vcounts:
                this_set = self.vertex_weight_index[self.nindices *
                                                    at:self.nindices *
                                                    (at + ct)]
                this_set.shape = (ct, self.nindices)
                newshape.append(numpy.array(this_set))
                at += ct
            self.index = newshape
        except:
            raise DaeMalformedError(
                'Corrupted vcounts or index in skin weights')

        try:
            self.joint_index = [
                influence[:, self.offsets[0]] for influence in self.index
            ]
            self.weight_index = [
                influence[:, self.offsets[1]] for influence in self.index
            ]
        except:
            raise DaeMalformedError('Corrupted joint or weight index in skin')

        self.max_joint_index = numpy.max([
            numpy.max(joint) if len(joint) > 0 else 0
            for joint in self.joint_index
        ])
        self.max_weight_index = numpy.max([
            numpy.max(weight) if len(weight) > 0 else 0
            for weight in self.weight_index
        ])
        checkSource(self.weight_joints, ('JOINT', ), self.max_joint_index)
        checkSource(self.weights, ('WEIGHT', ), self.max_weight_index)

        # original lib missing xml reconstruct clause
        if controller_node is not None:
            self.xmlnode = controller_node
        else:
            sourcenodes = []
            for srcid, src in self.sourceById.items():
                sourcenodes.append(src.xmlnode)
            controllernode = E.skin(*sourcenodes)
            if len(geometry.id) > 0:
                controllernode.set("source", '#' + geometry.id)

            #construct bind_shape_matrix xml node
            node = E.bind_shape_matrix()
            bind_shape_matrix.shape = (-1, )
            node.text = ' '.join(map(str, bind_shape_matrix.tolist()))
            controllernode.append(node)

            #construct <joints> node
            node = E.joints()
            node.append(
                E.input(semantic="JOINT", source="#%s" % self.joint_source))
            node.append(
                E.input(semantic="INV_BIND_MATRIX",
                        source="#%s" % self.joint_matrix_source))
            controllernode.append(node)

            #construct <vertex_weights> node
            node = E.vertex_weights()
            #todo: offset is hardcoded here.
            node.append(
                E.input(semantic="JOINT",
                        offset="0",
                        source="#%s" % self.weight_joint_source))
            node.append(
                E.input(semantic="WEIGHT",
                        offset="1",
                        source="#%s" % self.weight_source))
            nodevcount = E.vcount()
            nodevcount.text = ' '.join(map(str, self.vcounts))
            node.append(nodevcount)
            nodev = E.v()
            nodev.text = ' '.join(map(str, self.vertex_weight_index))
            node.set("count", str(len(self.vcounts)))
            node.append(nodev)
            controllernode.append(node)

            self.xmlnode = E.controller(controllernode)
            if len(self.id) > 0: self.xmlnode.set("id", self.id)