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 checkSource( source, components, maxindex): """Check if a source objects complies with the needed `components` and has the needed length :param collada.source.Source source: A source instance to check :param tuple components: A tuple describing the needed channels, e.g. ``('X','Y','Z')`` :param int maxindex: The maximum index that refers to this source """ if len(source.data) <= maxindex: raise DaeMalformedError( "Indexes (maxindex=%d) for source '%s' (len=%d) go beyond the limits of the source" % (maxindex, source.id, len(source.data)) ) #some files will write sources with no named parameters #by spec, these params should just be skipped, but we need to #adapt to the failed output of others... if len(source.components) == len(components): source.components = components if source.components != components: raise DaeMalformedError('Wrong format in source %s'%source.id) return source
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 ): 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, 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('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, node): url = node.get('url') if not url.startswith('#'): raise DaeMalformedError('Invalid url in camera instance %s' % url) camera = collada.cameras.get(url[1:]) if not camera: raise DaeBrokenRefError('Camera %s not found in library' % url) return CameraNode(camera, xmlnode=node)
def load(collada, node): url = node.get('url') if not url.startswith('#'): raise DaeMalformedError('Invalid url in light instance %s' % url) light = collada.lights.get(url[1:]) if not light: raise DaeBrokenRefError('Light %s not found in library' % url) return LightNode(light, xmlnode=node)
def _getInputs(collada, localscope, inputnodes): try: inputs = [(int(i.get('offset')), i.get('semantic'), i.get('source'), i.get('set')) for i in inputnodes] except ValueError as ex: raise DaeMalformedError('Corrupted offsets in primitive') return Primitive._getInputsFromList(collada, localscope, inputs)
def load(collada, node): inputs = [] for inputnode in node.findall( tag('bind_vertex_input') ): inputs.append( ( inputnode.get('semantic'), inputnode.get('input_semantic'), inputnode.get('input_set') ) ) targetid = node.get('target') if not targetid.startswith('#'): raise DaeMalformedError('Incorrect target id in material '+targetid) target = collada.materials.get(targetid[1:]) if not target: raise DaeBrokenRefError('Material %s not found'%targetid) return MaterialNode(node.get('symbol'), target, inputs, xmlnode = node)
def load( collada, node ): url = node.get('url') if not url.startswith('#'): raise DaeMalformedError('Invalid url in geometry instance %s' % url) geometry = collada.geometries.get(url[1:]) if not geometry: raise DaeBrokenRefError('Geometry %s not found in library'%url) matnodes = node.findall('%s/%s/%s'%( tag('bind_material'), tag('technique_common'), tag('instance_material') ) ) materials = [] for matnode in matnodes: materials.append( MaterialNode.load(collada, matnode) ) return GeometryNode( geometry, materials, xmlnode=node)
def load( collada, node ): url = node.get('url') if not url.startswith('#'): raise DaeMalformedError('Invalid url in controller instance %s' % url) controller = collada.controllers.get(url[1:]) if not controller: raise DaeBrokenRefError('Controller %s not found in library'%url) matnodes = node.findall('%s/%s/%s'%( tag('bind_material'), tag('technique_common'), tag('instance_material') ) ) materials = [] for matnode in matnodes: materials.append( MaterialNode.load(collada, matnode) ) return ControllerNode( controller, materials, xmlnode=node)
def load( collada, node, localscope ): url = node.get('url') if not url.startswith('#'): raise DaeMalformedError('Invalid url in node instance %s' % url) referred_node = localscope.get(url[1:]) if not referred_node: referred_node = collada.nodes.get(url[1:]) if not referred_node: raise DaeInstanceNotLoadedError('Node %s not found in library'%url) return NodeNode(referred_node, xmlnode=node)
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): 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 _loadDefaultScene(self): """Loads the default scene from <scene> tag in the root node.""" node = self.xmlnode.find('%s/%s' % (tag('scene'), tag('instance_visual_scene'))) try: if node != None: sceneid = node.get('url') if not sceneid.startswith('#'): raise DaeMalformedError('Malformed default scene reference to %s: '%sceneid) self.scene = self.scenes.get(sceneid[1:]) if not self.scene: raise DaeBrokenRefError('Default scene %s not found' % sceneid) except DaeError as ex: self.handleError(ex)
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 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 ): 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 __init__(self, matrix, xmlnode=None): """Creates a matrix transformation :param numpy.array matrix: This should be an unshaped numpy array of floats of length 16 :param xmlnode: When loaded, the xmlnode it comes from """ self.matrix = matrix """The resulting transformation matrix. This will be a numpy.array of size 4x4.""" if len(self.matrix) != 16: raise DaeMalformedError('Corrupted matrix transformation node') self.matrix.shape = (4, 4) self.xmlnode = xmlnode """ElementTree representation of the transform.""" if xmlnode is None: self.xmlnode = E.matrix(' '.join(map(str, self.matrix.flat)))
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): 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 _checkValidParams(self): if self.xmag is not None and self.ymag is None \ and self.aspect_ratio is None: pass elif self.xmag is None and self.ymag is not None \ and self.aspect_ratio is None: pass elif self.xmag is not None and self.ymag is None \ and self.aspect_ratio is not None: pass elif self.xmag is None and self.ymag is not None \ and self.aspect_ratio is not None: pass elif self.xmag is not None and self.ymag is not None \ and self.aspect_ratio is None: pass else: raise DaeMalformedError( "Received invalid combination of xmag (%s), ymag (%s), and aspect_ratio (%s)" % (str(self.xmag), str(self.ymag), str(self.aspect_ratio)))
def __init__(self, eye, interest, upvector, xmlnode=None): """Creates a lookat transformation :param numpy.array eye: An unshaped numpy array of floats of length 3 containing the position of the eye :param numpy.array interest: An unshaped numpy array of floats of length 3 containing the point of interest :param numpy.array upvector: An unshaped numpy array of floats of length 3 containing the up-axis direction :param xmlnode: When loaded, the xmlnode it comes from """ self.eye = eye """A numpy array of length 3 containing the position of the eye""" self.interest = interest """A numpy array of length 3 containing the point of interest""" self.upvector = upvector """A numpy array of length 3 containing the up-axis direction""" if len(eye) != 3 or len(interest) != 3 or len(upvector) != 3: raise DaeMalformedError('Corrupted lookat transformation node') self.matrix = numpy.identity(4, dtype=numpy.float32) """The resulting transformation matrix. This will be a numpy.array of size 4x4.""" front = toUnitVec(numpy.subtract(eye, interest)) side = numpy.multiply(-1, toUnitVec(numpy.cross(front, upvector))) self.matrix[0, 0:3] = side self.matrix[1, 0:3] = upvector self.matrix[2, 0:3] = front self.matrix[3, 0:3] = eye self.xmlnode = xmlnode """ElementTree representation of the transform.""" if xmlnode is None: self.xmlnode = E.lookat(' '.join( map( str, numpy.concatenate( (self.eye, self.interest, self.upvector)))))
def __init__(self, source_geometry, target_list, xmlnode=None): """Create a morph instance :Parameters: source_geometry The source geometry (Geometry) targets A list of tuples where each tuple (g,w) contains a Geometry (g) and a float weight value (w) xmlnode When loaded, the xmlnode it comes from """ self.id = xmlnode.get('id') if self.id is None: raise DaeMalformedError('Controller node requires an ID') self.source_geometry = source_geometry """The source geometry (Geometry)""" self.target_list = target_list """A list of tuples where each tuple (g,w) contains a Geometry (g) and a float weight value (w)""" self.xmlnode = xmlnode
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 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, 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()