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
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)
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
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)
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 )
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
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)
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)
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)
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)
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)
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
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)
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)
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)
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
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)
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)
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
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)
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)
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)
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)
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, 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))
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()
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()
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)