def _process_frame( self, buffer, seek_to = True ): """Processes a frame block. The format specifies that there are 'self.num_animated_components' float values. It does not specify how many lines (which would be nicer). """ def process_bone( buffer ): """Processes a single bone statement """ # split on whitespace values = buffer.split( None ) # convert to float # do this to avoid issues converting None to float # when we add padding values = map( float, values ) return values # find the 'hierarchy {' line if seek_to: parse_to( buffer, 'frame' ) # iterate through our specified number of joints values = [] while True: line = buffer.next() if line.startswith( '}' ): break values += list( process_bone( line ) ) self.values = numpy.array( values, dtype = 'float' )
def _process_hierarchy( self, buffer, num_joints, seek_to ): """Processes the hierarchy block. Will simply iterate over 'self.num_joints' lines. This should be valid as any invalid lines or comments will be removed by our pre-parser 'process_md5_buffer'. Joints follow the format: "boneName" parentIndex flags start_index // parentName ( tX tY tZ qX qY qZ ) Data to the right of the comment indicator is ignored. Returns a joint_layout named tuple. """ def process_joint( buffer ): """Processes a single joint statement """ # split on whitespace values = buffer.split( None ) # extract values # "boneName" parentIndex flags startIndex name, parent_index, flags, start_index = values # remove quotes from name name = name[ 1:-1 ] # convert to appropriate type parent_index = int( parent_index ) flags = int( flags ) start_index = int( start_index ) return ( name, parent_index, flags, start_index ) # find the 'hierarchy {' line if seek_to: parse_to( buffer, 'hierarchy' ) # # TODO: convert this to a single N * 3 array and then extract using slices # self.names = [] self.parent_indices = numpy.empty( num_joints, dtype = 'int' ) self.flags = numpy.empty( num_joints, dtype = 'int' ) self.start_indices = numpy.empty( num_joints, dtype = 'int' ) # iterate through our specified number of joints for index in range( num_joints ): name, parent_index, flags, start_index = process_joint( buffer.next() ) self.names.append( name ) self.parent_indices[ index ] = parent_index self.flags[ index ] = flags self.start_indices[ index ] = start_index
def _process_buffer( self, buffer ): """Processes the MD5 Mesh file from the specified buffer. """ # Processes the MD5 Anim header. line = parse_to( buffer, 'MD5Version' ) values = line.split( None ) self.md5_version = int( values[ 1 ] ) if self.md5_version != MD5.md5_version: raise ValueError( "MD5 version is incorrect, expected '%i', found '%i'" % ( MD5.version, self.md5_version ) ) # we ignore command line # this is only present in Doom 3 MD5's line = parse_to( buffer, 'numFrames' ) values = line.split( None ) num_frames = int( values[ 1 ] ) line = parse_to( buffer, 'numJoints' ) values = line.split( None ) num_joints = int( values[ 1 ] ) line = parse_to( buffer, 'frameRate' ) values = line.split( None ) self.frame_rate = int( values[ 1 ] ) # this value is basically useless # it is the number of floats that are provided per frame # as we read line-by-line, it's useless line = parse_to( buffer, 'numAnimatedComponents' ) values = line.split( None ) num_animated_components = int( values[ 1 ] ) # process hierarchy self.hierarchy = MD5_Hierarchy( buffer, num_joints ) # process bounds self.bounds = MD5_Bounds( buffer, num_frames ) # process the base frame self.base_frame = MD5_BaseFrame( buffer, num_joints ) # process frames # each frame is processed individually self.frames = [ MD5_Frame( buffer ) for index in range( num_frames ) ] # validate our frame data for frame in self.frames: if frame.num_animated_components != num_animated_components: raise ValueError("Number of animated components doesn't match")
def _process_mesh( self, buffer, seek_to = True ): """Processes a single 'mesh' block. If 'seek_to' is True, the first step will be to seek to the next mesh block Otherwise it will begin parsing the mesh block where it is (looking for shader). """ # find the 'mesh {' line if seek_to: parse_to( buffer, 'mesh' ) self._process_shader( buffer ) self._process_vertices( buffer ) self._process_triangles( buffer ) self._process_weights( buffer )
def _process_base_frame( self, buffer, num_joints, seek_to = True ): """Processes the baseframe block. Will simply iterate over 'self.num_joints' lines. This should be valid as any invalid lines or comments will be removed by our pre-parser 'process_md5_buffer'. Joints follow the format: ( xPos yPos zPos ) ( xOrient yOrient zOrient ) Returns a tuple that contains a vertex and a quaternion. The vertex is a 3 value tuple. The quaternion is a 4 tuple value (x,y,z,w) The W component is calculated based on the x,y,z values. For example: ( (0.0, 0.0, 0.0), (1.0, 1.0, 1.0, 1.0) ) """ def process_bone( buffer ): """Processes a single bone statement """ # split on whitespace values = buffer.split( None ) # extract values # ( xPos yPos zPos ) ( xOrient yOrient zOrient ) nil, pos_x, pos_y, pos_z, nil, nil, quat_x, quat_y, quat_z, nil = values # convert to appropriate type pos_x, pos_y, pos_z = float( pos_x ), float( pos_y ), float( pos_z ) quat_x, quat_y, quat_z = float( quat_x ), float( quat_y ), float( quat_z ) # calculate quaternion W value quat_w = compute_quaternion_w( quat_x, quat_y, quat_z ) return ( (pos_x, pos_y, pos_z), (quat_x, quat_y, quat_z, quat_w) ) # find the 'baseframe {' line if seek_to: parse_to( buffer, 'baseframe' ) self.positions = numpy.empty( (num_joints, 3 ), dtype = 'float' ) self.orientations = numpy.empty( (num_joints, 4 ), dtype = 'float' ) # iterate through our specified number of joints for position, orientation in zip( self.positions, self.orientations ): position[:], orientation[:] = process_bone( buffer.next() )
def _process_weights( self, buffer ): """Processes the 'numweights' and 'weight' statements of a mesh block. """ def process_weight( buffer ): values = buffer.split( None ) # weight weightIndex jointIndex weightValue ( xPos yPos zPos ) # we ignore the weightIndex as it is implied by order nil, nil, joint, bias, nil, pos_x, pos_y, pos_z, nil = values joint = int( joint ) bias = float( bias ) pos_x, pos_y, pos_z = float( pos_x ), float( pos_y ), float( pos_z ) return ( joint, bias, ( pos_x, pos_y, pos_z ) ) line = parse_to( buffer, 'numweights' ) values = line.split( None, 1 ) num_weights = int( values[ 1 ] ) self.joints = numpy.empty( num_weights, dtype = 'int' ) self.biases = numpy.empty( num_weights, dtype = 'float' ) self.positions = numpy.empty( (num_weights, 3), dtype = 'float' ) for index in range( num_weights ): joint, bias, position = process_weight( buffer.next() ) self.joints[ index ] = joint self.biases[ index ] = bias self.positions[ index ] = position
def _process_vertices( self, buffer ): """Processes the 'numverts' and 'vert' statements of a mesh block. """ def process_vert( buffer ): values = buffer.split( None ) # vert vertIndex ( texU texV ) weightIndex weightElem # we ignore the vertIndex as it is implied by order nil, nil, nil, tu, tv, nil, start_weight, weight_count = values tu, tv = float( tu ), float( tv ) start_weight = int( start_weight ) weight_count = int( weight_count ) return ( tu, tv, start_weight, weight_count ) line = parse_to( buffer, 'numverts' ) values = line.split( None, 1 ) num_verts = int( values[ 1 ] ) self.tcs = numpy.empty( (num_verts, 2), dtype = 'float' ) self.start_weights = numpy.empty( num_verts, dtype = 'int' ) self.weight_counts = numpy.empty( num_verts, dtype = 'int' ) # process the vertices for index in range( num_verts ): tu, tv, start_weight, weight_count = process_vert( buffer.next() ) self.tcs[ index ] = [ tu, tv ] self.start_weights[ index ] = start_weight self.weight_counts[ index ] = weight_count
def _process_shader( self, buffer ): """Extracts the 'shader' parameter from a mesh block. """ line = parse_to( buffer, 'shader' ) values = line.split( None, 1 ) # remove quotes self.shader = values[ 1 ][ 1:-1 ]
def _process_bounds( self, buffer, num_frames, seek_to ): """Processes the bounds block. Will simply iterate over 'self.num_frames' lines. This should be valid as any invalid lines or comments will be removed by our pre-parser 'process_md5_buffer'. Joints follow the format: ( minX minY minZ ) ( maxX maxY maxZ ) Returns a tuple that contains a 2 vertices. Each vertex is a 3 value tuple. For example: ( (0.0, 0.0, 0.0), (1.0, 1.0, 1.0) ) """ def process_bounds( buffer ): """Processes a single bounds statement """ # split on whitespace values = buffer.split( None ) # extract values # ( minX minY minZ ) ( maxX maxY maxZ ) nil, x1, y1, z1, nil, nil, x2, y2, z2, nil = values # convert to appropriate type x1, y1, z1 = float( x1 ), float( y1 ), float( z1 ) x2, y2, z2 = float( x2 ), float( y2 ), float( z2 ) return ( (x1, y1, z1), (x2, y2, z2) ) # find the 'bounds {' line if seek_to: parse_to( buffer, 'bounds' ) # iterate through our specified number of joints self.bounds = numpy.array( [ process_bounds( buffer.next() ) for num in range( num_frames ) ], dtype = 'float' )
def _process_buffer( self, buffer ): """Processes the MD5 Mesh file from the specified buffer. """ # Processes the MD5 Mesh header. line = parse_to( buffer, 'MD5Version' ) values = line.split( None, 1 ) self.md5_version = int( values[ 1 ] ) if self.md5_version != MD5.md5_version: raise ValueError( "MD5 version is incorrect: expected '%i', found '%i'" % ( MD5.version, self.md5_version ) ) # we ignore command line # this is only present in Doom 3 MD5's line = parse_to( buffer, 'numJoints' ) values = line.split( None, 1 ) num_joints = int( values[ 1 ] ) line = parse_to( buffer, 'numMeshes' ) values = line.split( None, 1 ) num_meshes = int( values[ 1 ] ) # process joints self.joints = MD5_Joints( buffer, num_joints ) # process meshes # each mesh is processed individually self.meshes = [ MD5_SubMesh( buffer ) for num in range( num_meshes ) ]
def _process_triangles( self, buffer ): """Processes the 'numvtris' and 'tri' statements of a mesh block. """ def process_tri( buffer ): values = buffer.split( None ) # tri triIndex vertIndex1 vertIndex2 vertIndex3 # we ignore the triIndex as it is implied by order nil, nil, v1, v2, v3 = values v1, v2, v3 = int( v1 ), int( v2 ), int( v3 ) return ( v1, v2, v3 ) line = parse_to( buffer, 'numtris' ) values = line.split( None, 1 ) num_tris = int( values[ 1 ] ) self.tris = numpy.array( [ process_tri( buffer.next() ) for num in range( num_tris ) ], dtype = 'float' )
def _process_frame(self, buffer, seek_to=True): """Processes a frame block. The format specifies that there are 'self.num_animated_components' float values. It does not specify how many lines (which would be nicer). It is not trivial to perform this. So we will simply iterate until the end of a block marked with a line starting with '}'. This should be valid as any invalid lines or comments will be removed by our pre-parser 'process_md5_buffer'. Joints follow the format: xPos yPos zPos xOrient yOrient zOrient Each value is optional. Returns a tuple that contains a vertex and a quaternion. The vertex is a 3 value tuple. The quaternion is a 4 tuple value (x,y,z,w) Be aware, that ANY of these values can be None. Once a single None value is found, all proceeding values will be None. A None value indicates that it should be inherited from the baseframe values. For example: ( (0.0, 0.0, 0.0), (1.0, 1.0, 1.0, 1.0) ) ( (0.0, 0.0, None), (None, None, None, None) ) """ def process_bone(buffer): """Processes a single bone statement """ # split on whitespace values = buffer.split(None) # convert to float # do this to avoid issues converting None to float # when we add padding values = map(float, values) # extract values # xPos yPos zPos xOrient yOrient zOrient # because the values are optional, we need to use # our tuple extraction routine values = utils.extract_tuple(values, 6, None) pos_x, pos_y, pos_z, quat_x, quat_y, quat_z = values quat_w = None if quat_x and quat_y and quat_z: quat_w = compute_quaternion_w(quat_x, quat_y, quat_z) return ((pos_x, pos_y, pos_z), (quat_x, quat_y, quat_z, quat_w)) # find the 'hierarchy {' line if seek_to: parse_to(buffer, "frame") # iterate through our specified number of joints positions = [] orientations = [] while True: line = buffer.next() if line.startswith("}"): break position, orientation = process_bone(line) positions.append(position) orientations.append(orientation) # don't set a dtype as some may be None self.positions = numpy.array(positions) self.orientations = numpy.array(orientations)
def _process_joints( self, buffer, num_joints, seek_to = True ): """Processes the joint block. Will simply iterate over 'self.num_joints' lines. This should be valid as any invalid lines or comments will be removed by our pre-parser 'process_md5_buffer'. Joints follow the format: "boneName" parentIndex ( xPos yPos zPos ) ( xOrient yOrient zOrient ) parentIndex >= -1 -1 indicates the current joint is the root joint. orientation is a quaternion. The W component is derrived from the x,y,z components. Returns a joint_layout named tuple. Parent is the parentIndex value. Position is in the format [x, y, z]. Quaternion is in the format [x, y, z, w]. """ def process_joint( buffer ): """Processes a single joint statement """ # split on whitespace values = buffer.split( None ) # extract values # "boneName" parentIndex ( xPos yPos zPos ) ( xOrient yOrient zOrient ) name, parent, nil, pos_x, pos_y, pos_z, nil, nil, quat_x, quat_y, quat_z, nil = values # remove quotes from name name = name[ 1:-1 ] # convert to appropriate type parent = int( parent ) pos_x = float( pos_x ) pos_y = float( pos_y ) pos_z = float( pos_z ) quat_x = float( quat_x ) quat_y = float( quat_y ) quat_z = float( quat_z ) quat_w = compute_quaternion_w( quat_x, quat_y, quat_z ) return ( name, parent, (pos_x, pos_y, pos_z), (quat_x, quat_y, quat_z, quat_w) ) # find the 'joints {' line if seek_to: parse_to( buffer, 'joints' ) self.names = [] self.parents = numpy.empty( (num_joints), dtype = 'int' ) self.positions = numpy.empty( (num_joints, 3), dtype = 'float' ) self.orientations = numpy.empty( (num_joints, 4), dtype = 'float' ) # iterate through our specified number of joints for index in range( num_joints ): name, parent, position, orientation = process_joint( buffer.next() ) self.names.append( name ) self.parents[ index ] = parent self.positions[ index ] = position self.orientations[ index ] = orientation