Exemple #1
0
    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' )
Exemple #2
0
    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
Exemple #3
0
    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")
Exemple #4
0
    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 )
Exemple #5
0
    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() )
Exemple #6
0
    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
Exemple #7
0
    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
Exemple #8
0
 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 ]
Exemple #9
0
    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'
            )
Exemple #10
0
    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 )
            ]
Exemple #11
0
    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'
            )
Exemple #12
0
    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)
Exemple #13
0
    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