Exemplo n.º 1
0
    def load( collada, localscope, node ):
        indexnode = node.find(tag('p'))
        if indexnode is None: raise DaeIncompleteError('Missing index in polylist')
        vcountnode = node.find(tag('vcount'))
        if vcountnode is None: raise DaeIncompleteError('Missing vcount in polylist')

        try:
            if vcountnode.text is None:
                vcounts = numpy.array([], dtype=numpy.int32)
            else:
                vcounts = numpy.fromstring(vcountnode.text, dtype=numpy.int32, sep=' ')
            vcounts[numpy.isnan(vcounts)] = 0
        except ValueError as ex:
            raise DaeMalformedError('Corrupted vcounts in polylist')

        all_inputs = primitive.Primitive._getInputs(collada, localscope, node.findall(tag('input')))

        try:
            if indexnode.text is None:
                index = numpy.array([], dtype=numpy.int32)
            else:
                index = numpy.fromstring(indexnode.text, dtype=numpy.int32, sep=' ')
            index[numpy.isnan(index)] = 0
        except: raise DaeMalformedError('Corrupted index in polylist')

        polylist = Polylist(all_inputs, node.get('material'), index, vcounts, node)
        return polylist
Exemplo n.º 2
0
    def load(collada, localscope, node):
        sourceid = node.get('id')
        arraynode = node.find(tag('float_array'))
        if arraynode is None:
            raise DaeIncompleteError('No float_array in source node')
        if arraynode.text is None:
            data = numpy.array([], dtype=numpy.float32)
        else:
            try:
                data = numpy.fromstring(arraynode.text,
                                        dtype=numpy.float32,
                                        sep=' ')
            except ValueError:
                raise DaeMalformedError('Corrupted float array')
        data[numpy.isnan(data)] = 0

        paramnodes = node.findall(
            '%s/%s/%s' %
            (tag('technique_common'), tag('accessor'), tag('param')))
        if not paramnodes:
            raise DaeIncompleteError('No accessor info in source node')
        components = [param.get('name') for param in paramnodes]
        if len(components
               ) == 2 and components[0] == 'U' and components[1] == 'V':
            #U,V is used for "generic" arguments - convert to S,T
            components = ['S', 'T']
        if len(components) == 3 and components[0] == 'S' and components[
                1] == 'T' and components[2] == 'P':
            components = ['S', 'T']
            data.shape = (-1, 3)
            #remove 3d texcoord dimension because we don't support it
            #TODO
            data = numpy.array(zip(data[:, 0], data[:, 1]))
            data.shape = (-1)
        return FloatSource(sourceid, data, tuple(components), xmlnode=node)
Exemplo n.º 3
0
    def load( collada, localscope, node ):
        id = node.get("id") or ""
        name = node.get("name") or ""
        meshnode = node.find(tag('mesh'))
        if meshnode is None: raise DaeUnsupportedError('Unknown geometry node')
        sourcebyid = {}
        sources = []
        sourcenodes = node.findall('%s/%s'%(tag('mesh'), tag('source')))
        for sourcenode in sourcenodes:
            ch = source.Source.load(collada, {}, sourcenode)
            sources.append(ch)
            sourcebyid[ch.id] = ch

        verticesnode = meshnode.find(tag('vertices'))
        if verticesnode is None:
            vertexsource = None
        else:
            inputnodes = {}
            for inputnode in verticesnode.findall(tag('input')):
                semantic = inputnode.get('semantic')
                inputsource = inputnode.get('source')
                if not semantic or not inputsource or not inputsource.startswith('#'):
                    raise DaeIncompleteError('Bad input definition inside vertices')
                inputnodes[semantic] = sourcebyid.get(inputsource[1:])
            if (not verticesnode.get('id') or len(inputnodes)==0 or
                not 'POSITION' in inputnodes):
                raise DaeIncompleteError('Bad vertices definition in mesh')
            sourcebyid[verticesnode.get('id')] = inputnodes
            vertexsource = verticesnode.get('id')

        double_sided_node = node.find('.//%s//%s' % (tag('extra'), tag('double_sided')))
        double_sided = False
        if double_sided_node is not None and double_sided_node.text is not None:
            try:
                val = int(double_sided_node.text)
                if val == 1:
                    double_sided = True
            except ValueError: pass

        _primitives = []
        for subnode in meshnode:
            if subnode.tag == tag('polylist'):
                _primitives.append( polylist.Polylist.load( collada, sourcebyid, subnode ) )
            elif subnode.tag == tag('triangles'):
                _primitives.append( triangleset.TriangleSet.load( collada, sourcebyid, subnode ) )
            elif subnode.tag == tag('lines'):
                _primitives.append( lineset.LineSet.load( collada, sourcebyid, subnode ) )
            elif subnode.tag == tag('polygons'):
                _primitives.append( polygons.Polygons.load( collada, sourcebyid, subnode ) )
            elif subnode.tag != tag('source') and subnode.tag != tag('vertices') and subnode.tag != tag('extra'):
                raise DaeUnsupportedError('Unknown geometry tag %s' % subnode.tag)
        geom = Geometry(collada, id, name, sourcebyid, _primitives, xmlnode=node, double_sided=double_sided )
        return geom
Exemplo n.º 4
0
    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)
Exemplo n.º 5
0
 def load( collada, localscope, node ):
     sourceid = node.get('id')
     arraynode = node.find(tag('Name_array'))
     if arraynode is None: raise DaeIncompleteError('No Name_array in source node')
     if arraynode.text is None:
         values = []
     else:
         try: values = [v for v in arraynode.text.split()]
         except ValueError: raise DaeMalformedError('Corrupted Name array')
     data = numpy.array( values, dtype=numpy.string_ )
     paramnodes = node.findall('%s/%s/%s'%(tag('technique_common'), tag('accessor'), tag('param')))
     if not paramnodes: raise DaeIncompleteError('No accessor info in source node')
     components = [ param.get('name') for param in paramnodes ]
     return NameSource( sourceid, data, tuple(components), xmlnode=node )
Exemplo n.º 6
0
 def _loadShadingParam( collada, localscope, node ):
     """Load from the node a definition for a material property."""
     children = node.getchildren()
     if not children: raise DaeIncompleteError('Incorrect effect shading parameter '+node.tag)
     vnode = children[0]
     if vnode.tag == tag('color'):
         try:
             value = tuple([ float(v) for v in vnode.text.split() ])
         except ValueError as ex:
             raise DaeMalformedError('Corrupted color definition in effect '+id)
         except IndexError as ex:
             raise DaeMalformedError('Corrupted color definition in effect '+id)
     elif vnode.tag == tag('float'):
         try: value = float(vnode.text)
         except ValueError as ex:
             raise DaeMalformedError('Corrupted float definition in effect '+id)
     elif vnode.tag == tag('texture'):
         value = Map.load(collada, localscope, vnode)
     elif vnode.tag == tag('param'):
         refid = vnode.get('ref')
         if refid is not None and refid in localscope:
             value = localscope[refid]
         else:
             return None
     else:
         raise DaeUnsupportedError('Unknown shading param definition ' + \
                 vnode.tag)
     return value
Exemplo n.º 7
0
 def load(collada, localscope, node):
     pnode = node.find('%s/%s' % (tag('technique_common'), tag('point')))
     colornode = pnode.find(tag('color'))
     if colornode is None:
         raise DaeIncompleteError('Missing color for point light')
     try:
         color = tuple([float(v) for v in colornode.text.split()])
     except ValueError as ex:
         raise DaeMalformedError(
             'Corrupted color values in light definition')
     constant_att = linear_att = quad_att = zfar = None
     qattnode = pnode.find(tag('quadratic_attenuation'))
     cattnode = pnode.find(tag('constant_attenuation'))
     lattnode = pnode.find(tag('linear_attenuation'))
     zfarnode = pnode.find(tag('zfar'))
     try:
         if cattnode is not None:
             constant_att = float(cattnode.text)
         if lattnode is not None:
             linear_att = float(lattnode.text)
         if qattnode is not None:
             quad_att = float(qattnode.text)
         if zfarnode is not None:
             zfar = float(zfarnode.text)
     except ValueError as ex:
         raise DaeMalformedError('Corrupted values in light definition')
     return PointLight(node.get('id'),
                       color,
                       constant_att,
                       linear_att,
                       quad_att,
                       zfar,
                       xmlnode=node)
Exemplo n.º 8
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)
Exemplo n.º 9
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)
Exemplo n.º 10
0
 def load(collada, localscope, node):
     tecnode = node.find('%s/%s' % (tag('optics'), tag('technique_common')))
     if tecnode is None or len(tecnode) == 0:
         raise DaeIncompleteError('Missing common technique in camera')
     camnode = tecnode[0]
     if camnode.tag == tag('perspective'):
         return PerspectiveCamera.load(collada, localscope, node)
     elif camnode.tag == tag('orthographic'):
         return OrthographicCamera.load(collada, localscope, node)
     else:
         raise DaeUnsupportedError('Unrecognized camera type: %s' %
                                   camnode.tag)
Exemplo n.º 11
0
 def load(collada, localscope, node):
     colornode = node.find(
         '%s/%s/%s' %
         (tag('technique_common'), tag('directional'), tag('color')))
     if colornode is None:
         raise DaeIncompleteError('Missing color for directional light')
     try:
         color = tuple([float(v) for v in colornode.text.split()])
     except ValueError as ex:
         raise DaeMalformedError(
             'Corrupted color values in light definition')
     return DirectionalLight(node.get('id'), color, xmlnode=node)
Exemplo n.º 12
0
    def load( collada, localscope, node ):
        indexnodes = node.findall(tag('p'))
        if indexnodes is None: raise DaeIncompleteError('Missing indices in polygons')

        polygon_indices = []
        for indexnode in indexnodes:
            index = numpy.fromstring(indexnode.text, dtype=numpy.int32, sep=' ')
            index[numpy.isnan(index)] = 0
            polygon_indices.append(index)

        all_inputs = primitive.Primitive._getInputs(collada, localscope, node.findall(tag('input')))

        polygons = Polygons(all_inputs, node.get('material'), polygon_indices, node)
        return polygons
Exemplo n.º 13
0
    def load(collada, localscope, node):
        sourceid = node.get('id')
        arraynode = node.find(tag('float_array'))
        if not arraynode is None:
            return FloatSource.load(collada, localscope, node)

        arraynode = node.find(tag('IDREF_array'))
        if not arraynode is None:
            return IDRefSource.load(collada, localscope, node)

        arraynode = node.find(tag('Name_array'))
        if not arraynode is None:
            return NameSource.load(collada, localscope, node)

        if arraynode is None: raise DaeIncompleteError('No array found in source %s' % sourceid)
Exemplo n.º 14
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)
Exemplo n.º 15
0
 def load(collada, localscope, node):
     tecnode = node.find(tag('technique_common'))
     if tecnode is None or len(tecnode) == 0:
         raise DaeIncompleteError('Missing common technique in light')
     lightnode = tecnode[0]
     if lightnode.tag == tag('directional'):
         return DirectionalLight.load(collada, localscope, node)
     elif lightnode.tag == tag('point'):
         return PointLight.load(collada, localscope, node)
     elif lightnode.tag == tag('ambient'):
         return AmbientLight.load(collada, localscope, node)
     elif lightnode.tag == tag('spot'):
         return SpotLight.load(collada, localscope, node)
     else:
         raise DaeUnsupportedError('Unrecognized light type: %s' %
                                   lightnode.tag)
Exemplo n.º 16
0
    def load( collada, localscope, node ):
        indexnode = node.find(tag('p'))
        if indexnode is None: raise DaeIncompleteError('Missing index in line set')

        source_array = primitive.Primitive._getInputs(collada, localscope, node.findall(tag('input')))

        try:
            if indexnode.text is None or indexnode.text.isspace():
                index = numpy.array([],  dtype=numpy.int32)
            else:
                index = numpy.fromstring(indexnode.text, dtype=numpy.int32, sep=' ')
            index[numpy.isnan(index)] = 0
        except: raise DaeMalformedError('Corrupted index in line set')

        lineset = LineSet(source_array, node.get('material'), index, node)
        lineset.xmlnode = node
        return lineset
Exemplo n.º 17
0
    def load(collada, localscope, node):
        orthonode = node.find(
            '%s/%s/%s' %
            (collada.tag('optics'), collada.tag('technique_common'),
             collada.tag('orthographic')))

        if orthonode is None:
            raise DaeIncompleteError(
                'Missing orthographic for camera definition')

        xmag = orthonode.find(collada.tag('xmag'))
        ymag = orthonode.find(collada.tag('ymag'))
        aspect_ratio = orthonode.find(collada.tag('aspect_ratio'))
        znearnode = orthonode.find(collada.tag('znear'))
        zfarnode = orthonode.find(collada.tag('zfar'))
        id = node.get('id', '')

        try:
            if xmag is not None:
                xmag = float(xmag.text)
            if ymag is not None:
                ymag = float(ymag.text)
            if aspect_ratio is not None:
                aspect_ratio = float(aspect_ratio.text)
            znear = float(znearnode.text)
            zfar = float(zfarnode.text)
        except (TypeError, ValueError) as ex:
            raise DaeMalformedError(
                'Corrupted float values in camera definition')

        #There are some exporters that incorrectly output all three of these.
        # Worse, they actually got the calculation of aspect_ratio wrong!
        # So instead of failing to load, let's just add one more hack because of terrible exporters
        if xmag is not None and ymag is not None and aspect_ratio is not None:
            aspect_ratio = None

        return OrthographicCamera(id,
                                  znear,
                                  zfar,
                                  xmag=xmag,
                                  ymag=ymag,
                                  aspect_ratio=aspect_ratio,
                                  xmlnode=node)
Exemplo n.º 18
0
    def load(collada, localscope, node):
        persnode = node.find(
            '%s/%s/%s' %
            (tag('optics'), tag('technique_common'), tag('perspective')))

        if persnode is None:
            raise DaeIncompleteError(
                'Missing perspective for camera definition')

        xfov = persnode.find(tag('xfov'))
        yfov = persnode.find(tag('yfov'))
        aspect_ratio = persnode.find(tag('aspect_ratio'))
        znearnode = persnode.find(tag('znear'))
        zfarnode = persnode.find(tag('zfar'))
        id = node.get('id', '')

        try:
            if xfov is not None:
                xfov = float(xfov.text)
            if yfov is not None:
                yfov = float(yfov.text)
            if aspect_ratio is not None:
                aspect_ratio = float(aspect_ratio.text)
            znear = float(znearnode.text)
            zfar = float(zfarnode.text)
        except (TypeError, ValueError) as ex:
            raise DaeMalformedError(
                'Corrupted float values in camera definition')

        #There are some exporters that incorrectly output all three of these.
        # Worse, they actually got the caculation of aspect_ratio wrong!
        # So instead of failing to load, let's just add one more hack because of terrible exporters
        if xfov is not None and yfov is not None and aspect_ratio is not None:
            aspect_ratio = None

        return PerspectiveCamera(id,
                                 znear,
                                 zfar,
                                 xfov=xfov,
                                 yfov=yfov,
                                 aspect_ratio=aspect_ratio,
                                 xmlnode=node)
Exemplo n.º 19
0
    def load(collada, localscope, node):
        indexnodes = node.findall(collada.tag('p'))
        if not indexnodes:
            raise DaeIncompleteError('Missing index in triangle set')

        source_array = primitive.Primitive._getInputs(
            collada, localscope, node.findall(collada.tag('input')))

        def parse_p(indexnode):
            if indexnode.text is None or indexnode.text.isspace():
                index = numpy.array([], dtype=numpy.int32)
            else:
                index = numpy.fromstring(indexnode.text,
                                         dtype=numpy.int32,
                                         sep=' ')
            index[numpy.isnan(index)] = 0
            return index

        indexlist = []
        tag_bare = node.tag.split('}')[-1]

        extendfunc = _indexExtendFunctions[tag_bare]

        max_offset = max(input[0]
                         for input_type_array in source_array.values()
                         for input in input_type_array)

        try:
            for indexnode in indexnodes:
                index = parse_p(indexnode)
                if extendfunc is None:
                    break

                extendfunc(indexlist, index.reshape((-1, max_offset + 1)))
            else:
                index = numpy.concatenate(indexlist)
        except:
            raise DaeMalformedError('Corrupted index in triangleset')

        triset = TriangleSet(source_array, node.get('material'), index, node)
        triset.xmlnode = node
        return triset
Exemplo n.º 20
0
 def load(collada, localscope, node):
     pnode = node.find(
         '%s/%s' % (collada.tag('technique_common'), collada.tag('spot')))
     colornode = pnode.find(collada.tag('color'))
     if colornode is None:
         raise DaeIncompleteError('Missing color for spot light')
     try:
         color = tuple([float(v) for v in colornode.text.split()])
     except ValueError as ex:
         raise DaeMalformedError(
             'Corrupted color values in spot light definition')
     constant_att = linear_att = quad_att = falloff_ang = falloff_exp = None
     cattnode = pnode.find(collada.tag('constant_attenuation'))
     lattnode = pnode.find(collada.tag('linear_attenuation'))
     qattnode = pnode.find(collada.tag('quadratic_attenuation'))
     fangnode = pnode.find(collada.tag('falloff_angle'))
     fexpnode = pnode.find(collada.tag('falloff_exponent'))
     try:
         if cattnode is not None:
             constant_att = float(cattnode.text)
         if lattnode is not None:
             linear_att = float(lattnode.text)
         if qattnode is not None:
             quad_att = float(qattnode.text)
         if fangnode is not None:
             falloff_ang = float(fangnode.text)
         if fexpnode is not None:
             falloff_exp = float(fexpnode.text)
     except ValueError as ex:
         raise DaeMalformedError(
             'Corrupted values in spot light definition')
     return SpotLight(node.get('id'),
                      color,
                      constant_att,
                      linear_att,
                      quad_att,
                      falloff_ang,
                      falloff_exp,
                      xmlnode=node)
Exemplo n.º 21
0
    def load(collada, localscope, node):
        localscope = {} # we have our own scope, shadow it
        params = []
        id = node.get('id')
        profilenode = node.find( tag('profile_COMMON') )
        if profilenode is None:
            raise DaeUnsupportedError('Found effect with profile other than profile_COMMON')

        #<image> can be local to a material instead of global in <library_images>
        for imgnode in profilenode.findall( tag('image') ):
            local_image = CImage.load(collada, localscope, imgnode)
            localscope[local_image.id] = local_image

            global_image_id = local_image.id
            uniquenum = 2
            while global_image_id in collada.images:
                global_image_id = local_image.id + "-" + uniquenum
                uniquenum += 1
            collada.images.append(local_image)

        Effect.getEffectParameters(collada, profilenode, localscope, params)
        
        tecnode = profilenode.find( tag('technique') )
        
        Effect.getEffectParameters(collada, tecnode, localscope, params)
        
        shadnode = None
        for shad in Effect.shaders:
            shadnode = tecnode.find(tag(shad))
            shadingtype = shad
            if not shadnode is None:
                break
        if shadnode is None: raise DaeIncompleteError('No material properties found in effect')
        props = {}
        for key in Effect.supported:
            pnode = shadnode.find( tag(key) )
            if pnode is None: props[key] = None
            else:
                try: props[key] = Effect._loadShadingParam(collada, localscope, pnode)
                except DaeMissingSampler2D as ex:
                    if ex.samplerid in collada.images:
                        #Whoever exported this collada file didn't include the proper references so we will create them
                        surf = Surface(ex.samplerid + '-surface', collada.images[ex.samplerid], 'A8R8G8B8')
                        sampler = Sampler2D(ex.samplerid, surf, None, None);
                        params.append(surf)
                        params.append(sampler)
                        localscope[surf.id] = surf
                        localscope[sampler.id] = sampler
                        try:
                            props[key] = Effect._loadShadingParam(
                                    collada, localscope, pnode)
                        except DaeUnsupportedError as ex:
                            props[key] = None
                            collada.handleError(ex)
                except DaeUnsupportedError as ex:
                    props[key] = None
                    collada.handleError(ex) # Give the chance to ignore error and load the rest

                if key == 'transparent' and key in props and props[key] is not None:
                    opaque_mode = pnode.get('opaque')
                    if opaque_mode is not None and opaque_mode == OPAQUE_MODE.RGB_ZERO:
                        props['opaque_mode'] = OPAQUE_MODE.RGB_ZERO
        props['xmlnode'] = node

        bumpnode = node.find('.//%s//%s' % (tag('extra'), tag('texture')))
        if bumpnode is not None:
            bumpmap =  Map.load(collada, localscope, bumpnode)
        else:
            bumpmap = None

        double_sided_node = node.find('.//%s//%s' % (tag('extra'), tag('double_sided')))
        double_sided = False
        if double_sided_node is not None and double_sided_node.text is not None:
            try:
                val = int(double_sided_node.text)
                if val == 1:
                    double_sided = True
            except ValueError:
                pass
        return Effect(id, params, shadingtype, bumpmap, double_sided, **props)
Exemplo n.º 22
0
 def load( collada, localspace, node ):
     id = node.get('id')
     initnode = node.find( tag('init_from') )
     if initnode is None: raise DaeIncompleteError('Image has no file path')
     path = initnode.text
     return CImage(id, path, collada, xmlnode = node)
Exemplo n.º 23
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)
Exemplo n.º 24
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)
Exemplo n.º 25
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))
Exemplo n.º 26
0
    def __init__(self,
                 filename=None,
                 ignore=None,
                 aux_file_loader=None,
                 zip_filename=None,
                 validate_output=False):
        """Load collada data from filename or file like object.

        :param filename:
          String containing path to filename to open or file-like object.
          Uncompressed .dae files are supported, as well as zip file archives.
          If this is set to ``None``, a new collada instance is created.
        :param list ignore:
          A list of :class:`common.DaeError` types that should be ignored
          when loading the collada document. Instances of these types will
          be added to :attr:`errors` after loading but won't be raised.
          Only used if `filename` is not ``None``.
        :param function aux_file_loader:
          Referenced files (e.g. texture images) are loaded from disk when
          reading from the local filesystem and from the zip archive when
          loading from a zip file. If these files are coming from another source
          (e.g. database) and/or you're loading with StringIO, set this to
          a function that given a filename, returns the binary data in the file.
          If `filename` is ``None``, you must set this parameter if you want to
          load auxiliary files.
        :param str zip_filename:
          If the file being loaded is a zip archive, you can set this parameter
          to indicate the file within the archive that should be loaded. If not
          set, a file that ends with .dae will be searched.
        :param bool validate_output:
          If set to True, the XML written when calling :meth:`save` will be
          validated against the COLLADA 1.4.1 schema. If validation fails, the
          :class:`common.DaeSaveValidationError` exception will be thrown.
        """

        self.errors = []
        """List of :class:`common.common.DaeError` objects representing errors encountered while loading collada file"""
        self.assetInfo = None
        """Instance of :class:`collada.asset.Asset` containing asset information"""

        self._geometries = IndexedList([], ('id', ))
        self._controllers = IndexedList([], ('id', ))
        self._animations = IndexedList([], ('id', ))
        self._lights = IndexedList([], ('id', ))
        self._cameras = IndexedList([], ('id', ))
        self._images = IndexedList([], ('id', ))
        self._effects = IndexedList([], ('id', ))
        self._materials = IndexedList([], ('id', ))
        self._nodes = IndexedList([], ('id', ))
        self._scenes = IndexedList([], ('id', ))

        self.scene = None
        """The default scene. This is either an instance of :class:`collada.scene.Scene` or `None`."""

        # a function which will apply the namespace
        self.tag = tag

        if validate_output and schema:
            self.validator = schema.ColladaValidator()
        else:
            self.validator = None

        self.maskedErrors = []
        if ignore is not None:
            self.ignoreErrors(*ignore)

        if filename is None:
            self.filename = None
            self.zfile = None
            self.getFileData = self._nullGetFile
            if aux_file_loader is not None:
                self.getFileData = self._wrappedFileLoader(aux_file_loader)

            self.xmlnode = ElementTree.ElementTree(
                E.COLLADA(E.library_cameras(),
                          E.library_controllers(),
                          E.library_effects(),
                          E.library_geometries(),
                          E.library_images(),
                          E.library_lights(),
                          E.library_materials(),
                          E.library_nodes(),
                          E.library_visual_scenes(),
                          E.scene(),
                          version='1.4.1'))
            """ElementTree representation of the collada document"""

            self.assetInfo = asset.Asset()
            return

        if isinstance(filename, basestring):
            fdata = open(filename, 'rb')
            self.filename = filename
            self.getFileData = self._getFileFromDisk
        else:
            fdata = filename  # assume it is a file like object
            self.filename = None
            self.getFileData = self._nullGetFile
        strdata = fdata.read()

        try:
            self.zfile = zipfile.ZipFile(BytesIO(strdata), 'r')
        except:
            self.zfile = None

        if self.zfile:
            self.filename = ''
            daefiles = []
            if zip_filename is not None:
                self.filename = zip_filename
            else:
                for name in self.zfile.namelist():
                    if name.upper().endswith('.DAE'):
                        daefiles.append(name)
                for name in daefiles:
                    if not self.filename:
                        self.filename = name
                    elif "MACOSX" in self.filename:
                        self.filename = name
            if not self.filename or self.filename not in self.zfile.namelist():
                raise DaeIncompleteError(
                    'COLLADA file not found inside zip compressed file')
            data = self.zfile.read(self.filename)
            self.getFileData = self._getFileFromZip
        else:
            data = strdata

        if aux_file_loader is not None:
            self.getFileData = self._wrappedFileLoader(aux_file_loader)

        etree_parser = ElementTree.XMLParser()
        try:
            self.xmlnode = ElementTree.ElementTree(element=None,
                                                   file=BytesIO(data))
        except ElementTree.ParseError as e:
            raise DaeMalformedError("XML Parsing Error: %s" % e)

        # if we can't get the current namespace
        # the tagger from above will use a hardcoded default
        try:
            # get the root node, same for both etree and lxml
            xml_root = self.xmlnode.getroot()
            if hasattr(xml_root, 'nsmap'):
                # lxml has an nsmap
                # use the first value in the namespace map
                namespace = next(iter(xml_root.nsmap.values()))
            elif hasattr(xml_root, 'tag'):
                # for xml.etree we need to extract ns from root tag
                namespace = xml_root.tag.split('}')[0].lstrip('{')
            # create a tagging function using the extracted namespace
            self.tag = tagger(namespace)
        except BaseException:
            # failed to extract a namespace, using default
            traceback.print_exc()

        # functions which will load various things into collada object
        self._loadAssetInfo()
        self._loadImages()
        self._loadEffects()
        self._loadMaterials()
        self._loadAnimations()
        self._loadGeometry()
        self._loadControllers()
        self._loadLights()
        self._loadCameras()
        self._loadNodes()
        self._loadScenes()
        self._loadDefaultScene()
Exemplo n.º 27
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()
Exemplo n.º 28
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,
                 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)