Example #1
0
 def _getFileFromZip(self, fname):
     """Return the binary data of an auxiliary file from a zip archive as a string."""
     if not self.zfile:
         raise DaeBrokenRefError('Trying to load an auxiliary file %s but we are not reading from a zip'%fname)
     basepath = posixpath.dirname(self.filename)
     aux_path = posixpath.normpath(posixpath.join(basepath, fname))
     if aux_path not in self.zfile.namelist():
         raise DaeBrokenRefError('Auxiliar file %s not found in archive' % fname)
     return self.zfile.read( aux_path )
Example #2
0
 def _getFileFromDisk(self, fname):
     """Return the binary data of an auxiliary file from the local disk relative to the file path loaded."""
     if self.zfile:
         raise DaeBrokenRefError('Trying to load an auxiliary file %s from disk but we are reading from a zip file'%fname)
     basepath = os.path.dirname(self.filename)
     aux_path = os.path.normpath(os.path.join(basepath, fname))
     if not os.path.exists(aux_path):
         raise DaeBrokenRefError('Auxiliar file %s not found on disk'%fname)
     fdata = open(aux_path, 'rb')
     return fdata.read()
    def load(collada, localscope, morphnode, controllernode):
        baseid = morphnode.get('source')
        if len(baseid) < 2 or baseid[0] != '#' or \
                not baseid[1:] in collada.geometries:
            raise DaeBrokenRefError('Base source of morph %s not found' %
                                    baseid)
        basegeom = collada.geometries[baseid[1:]]

        method = morphnode.get('method')
        if method is None:
            method = 'NORMALIZED'
        if not (method == 'NORMALIZED' or method == 'RELATIVE'):
            raise DaeMalformedError(
                "Morph method must be either NORMALIZED or RELATIVE. Found '%s'"
                % method)

        inputnodes = morphnode.findall(
            '%s/%s' % (collada.tag('targets'), collada.tag('input')))
        if inputnodes is None or len(inputnodes) < 2:
            raise DaeIncompleteError("Not enough inputs in a morph")

        try:
            inputs = [(i.get('semantic'), i.get('source')) for i in inputnodes]
        except ValueError as ex:
            raise DaeMalformedError('Corrupted inputs in morph')

        target_source = None
        weight_source = None
        for i in inputs:
            if len(i[1]) < 2 or i[1][0] != '#' or not i[1][1:] in localscope:
                raise DaeBrokenRefError('Input in morph node %s not found' %
                                        i[1])
            if i[0] == 'MORPH_TARGET':
                target_source = localscope[i[1][1:]]
            elif i[0] == 'MORPH_WEIGHT':
                weight_source = localscope[i[1][1:]]

        if not type(target_source) is source.IDRefSource or \
                not type(weight_source) is source.FloatSource:
            raise DaeIncompleteError("Not enough inputs in targets of morph")

        if len(target_source) != len(weight_source):
            raise DaeMalformedError("Morph inputs must be of same length")

        target_list = []
        for target, weight in zip(target_source, weight_source):
            if len(target) < 1 or not (target in collada.geometries):
                raise DaeBrokenRefError(
                    "Targeted geometry %s in morph not found" % target)
            target_list.append((collada.geometries[target], weight[0]))

        return Morph(basegeom, target_list, controllernode)
Example #4
0
 def __wrapped(fname):
     res = aux_file_loader(fname)
     if res is None:
         raise DaeBrokenRefError(
             'Auxiliar file %s from auxiliary file loader not found' %
             fname)
     return res
Example #5
0
 def load(collada, node):
     url = node.get('url')
     if not url.startswith('#'):
         raise DaeMalformedError('Invalid url in camera instance %s' % url)
     camera = collada.cameras.get(url[1:])
     if not camera:
         raise DaeBrokenRefError('Camera %s not found in library' % url)
     return CameraNode(camera, xmlnode=node)
Example #6
0
 def load(collada, node):
     url = node.get('url')
     if not url.startswith('#'):
         raise DaeMalformedError('Invalid url in light instance %s' % url)
     light = collada.lights.get(url[1:])
     if not light:
         raise DaeBrokenRefError('Light %s not found in library' % url)
     return LightNode(light, xmlnode=node)
Example #7
0
 def load(collada, node):
     inputs = []
     for inputnode in node.findall( tag('bind_vertex_input') ):
         inputs.append( ( inputnode.get('semantic'), inputnode.get('input_semantic'), inputnode.get('input_set') ) )
     targetid = node.get('target')
     if not targetid.startswith('#'): raise DaeMalformedError('Incorrect target id in material '+targetid)
     target = collada.materials.get(targetid[1:])
     if not target: raise DaeBrokenRefError('Material %s not found'%targetid)
     return MaterialNode(node.get('symbol'), target, inputs, xmlnode = node)
Example #8
0
    def save(self):
        """Saves the collada document back to :attr:`xmlnode`"""
        libraries = [(self.geometries, 'library_geometries'),
                     (self.controllers, 'library_controllers'),
                     (self.lights, 'library_lights'),
                     (self.cameras, 'library_cameras'),
                     (self.images, 'library_images'),
                     (self.effects, 'library_effects'),
                     (self.materials, 'library_materials'),
                     (self.nodes, 'library_nodes'),
                     (self.scenes, 'library_visual_scenes')]

        self.assetInfo.save()
        assetnode = self.xmlnode.getroot().find(self.tag('asset'))
        if assetnode is not None:
            self.xmlnode.getroot().remove(assetnode)
        self.xmlnode.getroot().insert(0, self.assetInfo.xmlnode)

        library_loc = 0
        for i, node in enumerate(self.xmlnode.getroot()):
            if node.tag == self.tag('asset'):
                library_loc = i + 1

        for arr, name in libraries:
            node = self.xmlnode.find(self.tag(name))
            if node is None:
                if len(arr) == 0:
                    continue
                self.xmlnode.getroot().insert(library_loc, E(name))
                node = self.xmlnode.find(self.tag(name))
            elif node is not None and len(arr) == 0:
                self.xmlnode.getroot().remove(node)
                continue

            for o in arr:
                o.save()
                if o.xmlnode not in node:
                    node.append(o.xmlnode)
            xmlnodes = [o.xmlnode for o in arr]
            for n in node:
                if n not in xmlnodes:
                    node.remove(n)

        scenenode = self.xmlnode.find(self.tag('scene'))
        scenenode.clear()
        if self.scene is not None:
            sceneid = self.scene.id
            if sceneid not in self.scenes:
                raise DaeBrokenRefError('Default scene %s not found' % sceneid)
            scenenode.append(E.instance_visual_scene(url="#%s" % sceneid))

        if self.validator is not None:
            if not self.validator.validate(self.xmlnode):
                raise DaeSaveValidationError(
                    "Validation error when saving: " + self.validator.
                    COLLADA_SCHEMA_1_4_1_INSTANCE.error_log.last_error.message)
Example #9
0
 def load( collada, node ):
     url = node.get('url')
     if not url.startswith('#'): raise DaeMalformedError('Invalid url in geometry instance %s' % url)
     geometry = collada.geometries.get(url[1:])
     if not geometry: raise DaeBrokenRefError('Geometry %s not found in library'%url)
     matnodes = node.findall('%s/%s/%s'%( tag('bind_material'), tag('technique_common'), tag('instance_material') ) )
     materials = []
     for matnode in matnodes:
         materials.append( MaterialNode.load(collada, matnode) )
     return GeometryNode( geometry, materials, xmlnode=node)
Example #10
0
 def load( collada, node ):
     url = node.get('url')
     if not url.startswith('#'): raise DaeMalformedError('Invalid url in controller instance %s' % url)
     controller = collada.controllers.get(url[1:])
     if not controller: raise DaeBrokenRefError('Controller %s not found in library'%url)
     matnodes = node.findall('%s/%s/%s'%( tag('bind_material'), tag('technique_common'), tag('instance_material') ) )
     materials = []
     for matnode in matnodes:
         materials.append( MaterialNode.load(collada, matnode) )
     return ControllerNode( controller, materials, xmlnode=node)
Example #11
0
 def _loadDefaultScene(self):
     """Loads the default scene from <scene> tag in the root node."""
     node = self.xmlnode.find('%s/%s' % (tag('scene'), tag('instance_visual_scene')))
     try:
         if node != None:
             sceneid = node.get('url')
             if not sceneid.startswith('#'):
                 raise DaeMalformedError('Malformed default scene reference to %s: '%sceneid)
             self.scene = self.scenes.get(sceneid[1:])
             if not self.scene:
                 raise DaeBrokenRefError('Default scene %s not found' % sceneid)
     except DaeError as ex:
         self.handleError(ex)
Example #12
0
    def load( collada, localscope, node ):
        matid = node.get('id')
        matname = node.get('name')

        effnode = node.find( tag('instance_effect'))
        if effnode is None: raise DaeIncompleteError('No effect inside material')
        effectid = effnode.get('url')

        if not effectid.startswith('#'):
            raise DaeMalformedError('Corrupted effect reference in material %s' % effectid)

        effect = collada.effects.get(effectid[1:])
        if not effect:
            raise DaeBrokenRefError('Effect not found: '+effectid)

        return Material(matid, matname, effect, xmlnode=node)
Example #13
0
 def load( collada, localscope, node ):
     surfacenode = node.find( tag('surface') )
     if surfacenode is None: raise DaeIncompleteError('No surface found in newparam')
     if surfacenode.get('type') != '2D': raise DaeMalformedError('Hard to imagine a non-2D surface, isn\'t it?')
     initnode = surfacenode.find( tag('init_from') )
     if initnode is None: raise DaeIncompleteError('No init image found in surface')
     formatnode = surfacenode.find( tag('format') )
     if formatnode is None: format = None
     else: format = formatnode.text
     imgid = initnode.text
     id = node.get('sid')
     if imgid in localscope:
         img = localscope[imgid]
     else:
         img = collada.images.get(imgid)
     if img is None: raise DaeBrokenRefError("Missing image '%s' in surface '%s'" % (imgid, id))
     return Surface(id, img, format, xmlnode=node)
Example #14
0
    def load( collada, localscope, node ):
        samplernode = node.find( tag('sampler2D') )
        if samplernode is None: raise DaeIncompleteError('No sampler found in newparam')
        sourcenode = samplernode.find( tag('source') )
        if sourcenode is None: raise DaeIncompleteError('No source found in sampler')
        minnode = samplernode.find( tag('minfilter') )
        if minnode is None: minfilter = None
        else: minfilter = minnode.text
        magnode = samplernode.find( tag('magfilter') )
        if magnode is None: magfilter = None
        else: magfilter = magnode.text

        surfaceid = sourcenode.text
        id = node.get('sid')
        surface = localscope.get(surfaceid)
        if surface is None or type(surface) != Surface: raise DaeBrokenRefError('Missing surface ' + surfaceid)
        return Sampler2D(id, surface, minfilter, magfilter, xmlnode=node)
Example #15
0
    def load(collada, node):
        id = node.get('id')
        nodes = []
        tried_loading = []
        succeeded = False
        localscope = {}
        for nodenode in node.findall(collada.tag('node')):
            try:
                N = loadNode(collada, nodenode, localscope)
            except DaeInstanceNotLoadedError as ex:
                tried_loading.append((nodenode, ex))
            except DaeError as ex:
                collada.handleError(ex)
            else:
                if N is not None:
                    nodes.append(N)
                    if N.id and N.id not in localscope:
                        localscope[N.id] = N
                    succeeded = True
        while len(tried_loading) > 0 and succeeded:
            succeeded = False
            next_tried = []
            for nodenode, ex in tried_loading:
                try:
                    N = loadNode(collada, nodenode, localscope)
                except DaeInstanceNotLoadedError as ex:
                    next_tried.append((nodenode, ex))
                except DaeError as ex:
                    collada.handleError(ex)
                else:
                    if N is not None:
                        nodes.append(N)
                        succeeded = True
            tried_loading = next_tried
        if len(tried_loading) > 0:
            for nodenode, ex in tried_loading:
                raise DaeBrokenRefError(ex.msg)

        return Scene(id, nodes, xmlnode=node, collada=collada)
Example #16
0
 def _loadNodes(self):
     libnodes = self.xmlnode.findall(self.tag('library_nodes'))
     if libnodes is not None:
         for libnode in libnodes:
             if libnode is not None:
                 tried_loading = []
                 succeeded = False
                 for node in libnode.findall(self.tag('node')):
                     try:
                         N = scene.loadNode(self, node, {})
                     except scene.DaeInstanceNotLoadedError as ex:
                         tried_loading.append((node, ex))
                     except DaeError as ex:
                         self.handleError(ex)
                     else:
                         if N is not None:
                             self.nodes.append(N)
                             succeeded = True
                 while len(tried_loading) > 0 and succeeded:
                     succeeded = False
                     next_tried = []
                     for node, ex in tried_loading:
                         try:
                             N = scene.loadNode(self, node, {})
                         except scene.DaeInstanceNotLoadedError as ex:
                             next_tried.append((node, ex))
                         except DaeError as ex:
                             self.handleError(ex)
                         else:
                             if N is not None:
                                 self.nodes.append(N)
                                 succeeded = True
                     tried_loading = next_tried
                 if len(tried_loading) > 0:
                     for node, ex in tried_loading:
                         raise DaeBrokenRefError(ex.msg)
Example #17
0
    def load(collada, localscope, skinnode, controllernode):
        if len(localscope) < 3:
            raise DaeMalformedError('Not enough sources in skin')

        geometry_source = skinnode.get('source')
        if geometry_source is None or len(geometry_source) < 2 \
                or geometry_source[0] != '#':
            raise DaeBrokenRefError('Invalid source attribute of skin node')
        if not geometry_source[1:] in collada.geometries:
            raise DaeBrokenRefError('Source geometry for skin node not found')
        geometry = collada.geometries[geometry_source[1:]]

        bind_shape_mat = skinnode.find(collada.tag('bind_shape_matrix'))
        if bind_shape_mat is None:
            bind_shape_mat = numpy.identity(4, dtype=numpy.float32)
            bind_shape_mat.shape = (-1, )
        else:
            try:
                values = [float(v) for v in bind_shape_mat.text.split()]
            except ValueError:
                raise DaeMalformedError('Corrupted bind shape matrix in skin')
            bind_shape_mat = numpy.array(values, dtype=numpy.float32)

        inputnodes = skinnode.findall(
            '%s/%s' % (collada.tag('joints'), collada.tag('input')))
        if inputnodes is None or len(inputnodes) < 2:
            raise DaeIncompleteError("Not enough inputs in skin joints")

        try:
            inputs = [(i.get('semantic'), i.get('source')) for i in inputnodes]
        except ValueError as ex:
            raise DaeMalformedError('Corrupted inputs in skin')

        joint_source = None
        matrix_source = None
        for i in inputs:
            if len(i[1]) < 2 or i[1][0] != '#':
                raise DaeBrokenRefError('Input in skin node %s not found' %
                                        i[1])
            if i[0] == 'JOINT':
                joint_source = i[1][1:]
            elif i[0] == 'INV_BIND_MATRIX':
                matrix_source = i[1][1:]

        weightsnode = skinnode.find(collada.tag('vertex_weights'))
        if weightsnode is None:
            raise DaeIncompleteError("No vertex_weights found in skin")
        indexnode = weightsnode.find(collada.tag('v'))
        if indexnode is None:
            raise DaeIncompleteError('Missing indices in skin vertex weights')
        vcountnode = weightsnode.find(collada.tag('vcount'))
        if vcountnode is None:
            raise DaeIncompleteError('Missing vcount in skin vertex weights')
        inputnodes = weightsnode.findall(collada.tag('input'))

        try:
            index = numpy.array([float(v) for v in indexnode.text.split()],
                                dtype=numpy.int32)
            vcounts = numpy.array([int(v) for v in vcountnode.text.split()],
                                  dtype=numpy.int32)
            inputs = [(i.get('semantic'), i.get('source'),
                       int(i.get('offset'))) for i in inputnodes]
        except ValueError as ex:
            raise DaeMalformedError(
                'Corrupted index or offsets in skin vertex weights')

        weight_joint_source = None
        weight_source = None
        offsets = [0, 0]
        for i in inputs:
            if len(i[1]) < 2 or i[1][0] != '#':
                raise DaeBrokenRefError('Input in skin node %s not found' %
                                        i[1])
            if i[0] == 'JOINT':
                weight_joint_source = i[1][1:]
                offsets[0] = i[2]
            elif i[0] == 'WEIGHT':
                weight_source = i[1][1:]
                offsets[1] = i[2]

        if joint_source is None or weight_source is None:
            raise DaeMalformedError(
                'Not enough inputs for vertex weights in skin')

        return Skin(localscope, bind_shape_mat, joint_source, matrix_source,
                    weight_source, weight_joint_source, vcounts, index,
                    offsets, geometry, controllernode, skinnode)
Example #18
0
    def _getInputsFromList(collada, localscope, inputs):
        #first let's save any of the source that are references to a dict
        to_append = []
        for input in inputs:
            offset, semantic, source, set = input
            if semantic == 'VERTEX':
                vertex_source = localscope.get(source[1:])
                if isinstance(vertex_source, dict):
                    for inputsemantic, inputsource in vertex_source.items():
                        if inputsemantic == 'POSITION':
                            to_append.append([offset, 'VERTEX', '#' + inputsource.id, set])
                        else:
                            to_append.append([offset, inputsemantic, '#' + inputsource.id, set])

        #remove all the dicts
        inputs[:] = [input for input in inputs
                if not isinstance(localscope.get(input[2][1:]), dict)]

        #append the dereferenced dicts
        for a in to_append:
            inputs.append(a)

        vertex_inputs = []
        normal_inputs = []
        texcoord_inputs = []
        textangent_inputs = []
        texbinormal_inputs = []
        color_inputs = []
        tangent_inputs = []
        binormal_inputs = []

        all_inputs = {}

        for input in inputs:
            offset, semantic, source, set = input
            if len(source) < 2 or source[0] != '#':
                raise DaeMalformedError('Incorrect source id "%s" in input' % source)
            if source[1:] not in localscope:
                raise DaeBrokenRefError('Source input id "%s" not found' % source)
            input = (input[0], input[1], input[2], input[3], localscope[source[1:]])
            if semantic == 'VERTEX':
                vertex_inputs.append(input)
            elif semantic == 'NORMAL':
                normal_inputs.append(input)
            elif semantic == 'TEXCOORD':
                texcoord_inputs.append(input)
            elif semantic == 'TEXTANGENT':
                textangent_inputs.append(input)
            elif semantic == 'TEXBINORMAL':
                texbinormal_inputs.append(input)
            elif semantic == 'COLOR':
                color_inputs.append(input)
            elif semantic == 'TANGENT':
                tangent_inputs.append(input)
            elif semantic == 'BINORMAL':
                binormal_inputs.append(input)
            else:
                try:
                    raise DaeUnsupportedError('Unknown input semantic: %s' % semantic)
                except DaeUnsupportedError as ex:
                    collada.handleError(ex)
                unknown_input = all_inputs.get(semantic, [])
                unknown_input.append(input)
                all_inputs[semantic] = unknown_input

        all_inputs['VERTEX'] = vertex_inputs
        all_inputs['NORMAL'] = normal_inputs
        all_inputs['TEXCOORD'] = texcoord_inputs
        all_inputs['TEXBINORMAL'] = texbinormal_inputs
        all_inputs['TEXTANGENT'] = textangent_inputs
        all_inputs['COLOR'] = color_inputs
        all_inputs['TANGENT'] = tangent_inputs
        all_inputs['BINORMAL'] = binormal_inputs

        return all_inputs
Example #19
0
 def _nullGetFile(self, fname):
     raise DaeBrokenRefError(
         'Trying to load auxiliary file but collada was not loaded from disk, zip, or with custom handler'
     )
Example #20
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)