Example #1
0
        /// <summary>
        /// Replaces skinning indices of 65535 (ushort.max) with the proper indices from previous groups.
        /// </summary>
        private void FixGroupSkinningIndices()
        {
            foreach (Shape s in Shapes)
            {
                for (int i = 0; i < s.MatrixGroups.Count; i++)
                {
                    MatrixGroup cur_group = s.MatrixGroups[i];

                    for (int j = 0; j < cur_group.MatrixDataTable.MatrixTable.Count; j++)
                    {
                        ushort cur_index = cur_group.MatrixDataTable.MatrixTable[j];

                        if (cur_index == ushort.MaxValue)
                        {
                            for (int k = i - 1; k > -1; k--)
                            {
                                ushort last_index = s.MatrixGroups[k].MatrixDataTable.MatrixTable[j];

                                if (last_index != ushort.MaxValue)
                                {
                                    cur_group.MatrixDataTable.MatrixTable[j] = last_index;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
Example #2
0
        public void ReadSHP1FromStream(EndianBinaryReader reader, long tagStart)
        {
            #region Load data from SHP1 header
            short shapeCount = reader.ReadInt16();
            Trace.Assert(reader.ReadUInt16() == 0xFFFF); // Padding
            int shapeOffset = reader.ReadInt32();

            // Another index remap table.
            int remapTableOffset = reader.ReadInt32();

            Trace.Assert(reader.ReadInt32() == 0);
            int attributeOffset = reader.ReadInt32();

            // Offset to the Matrix Table which holds a list of ushorts used for ??
            int matrixTableOffset = reader.ReadInt32();

            // Offset to the array of primitive's data.
            int primitiveDataOffset  = reader.ReadInt32();
            int matrixDataOffset     = reader.ReadInt32();
            int packetLocationOffset = reader.ReadInt32();
            #endregion

            for (int s = 0; s < shapeCount; s++)
            {
                // Shapes can have different attributes for each shape. (ie: Some have only Position, while others have Pos & TexCoord, etc.) Each
                // shape (which has a consistent number of attributes) it is split into individual packets, which are a collection of geometric primitives.
                // Each packet can have individual unique skinning data.

                reader.BaseStream.Position = tagStart + shapeOffset + (0x28 * s); /* 0x28 is the size of one Shape entry*/

                Shape shape = new Shape();

                #region Load data from shape struct
                shape.MatrixType = reader.ReadByte();
                Trace.Assert(reader.ReadByte() == 0xFF); // Padding

                // Number of Packets (of data) contained in this Shape
                ushort grp_count = reader.ReadUInt16();

                // Offset from the start of the Attribute List to the attributes this particular batch uses.
                ushort batchAttributeOffset = reader.ReadUInt16();

                ushort firstMatrixIndex = reader.ReadUInt16();
                ushort firstGrpIndex    = reader.ReadUInt16();
                Trace.Assert(reader.ReadUInt16() == 0xFFFF); // Padding

                float   boundingSphereDiameter = reader.ReadSingle();
                Vector3 bboxMin = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
                Vector3 bboxMax = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());

                shape.BoundingSphereDiameter = boundingSphereDiameter;
                shape.BoundingBox            = new FAABox(bboxMin, bboxMax);
                #endregion

                Shapes.Add(shape);

                // Determine which Attributes this particular shape uses.
                reader.BaseStream.Position = tagStart + attributeOffset + batchAttributeOffset;

                List <ShapeVertexAttribute> attributes = new List <ShapeVertexAttribute>();

                while (true)
                {
                    ShapeVertexAttribute attribute = new ShapeVertexAttribute((VertexArrayType)reader.ReadInt32(), (VertexDataType)reader.ReadInt32());

                    if (attribute.ArrayType == VertexArrayType.NullAttr)
                    {
                        break;
                    }

                    attributes.Add(attribute);

                    // We'll enable the attributes for the shape here, but we'll skip PositionMatrixIndex.
                    // We're going to use our own attributes for skinning - SkinIndices and SkinWeights.
                    if (attribute.ArrayType != VertexArrayType.PositionMatrixIndex)
                    {
                        shape.VertexDescription.EnableAttribute(ArrayTypeToShader(attribute.ArrayType));
                    }
                }

                for (ushort p = 0; p < grp_count; p++)
                {
                    MatrixGroup grp = new MatrixGroup(new List <MeshVertexIndex>(), new SkinDataTable(0));

                    // The packets are all stored linearly and then they point to the specific size and offset of the data for this particular packet.
                    reader.BaseStream.Position = tagStart + packetLocationOffset + ((firstGrpIndex + p) * 0x8); /* 0x8 is the size of one Packet entry */

                    int packetSize   = reader.ReadInt32();
                    int packetOffset = reader.ReadInt32();

                    // Read Matrix Data for Packet
                    reader.BaseStream.Position = tagStart + matrixDataOffset + (firstMatrixIndex + p) * 0x08; /* 0x8 is the size of one Matrix Data */
                    ushort matrixUnknown0   = reader.ReadUInt16();
                    ushort matrixCount      = reader.ReadUInt16();
                    uint   matrixFirstIndex = reader.ReadUInt32();

                    SkinDataTable matrixData = new SkinDataTable(matrixUnknown0);
                    grp.MatrixDataTable = matrixData;

                    // Read Matrix Table data. The Matrix Table is skinning information for the packet which indexes into the DRW1 section for more info.
                    reader.BaseStream.Position = tagStart + matrixTableOffset + (matrixFirstIndex * 0x2); /* 0x2 is the size of one Matrix Table entry */
                    for (int m = 0; m < matrixCount; m++)
                    {
                        matrixData.MatrixTable.Add(reader.ReadUInt16());
                    }

                    // Read the Primitive Data
                    reader.BaseStream.Position = tagStart + primitiveDataOffset + packetOffset;

                    uint numPrimitiveBytesRead = 0;
                    while (numPrimitiveBytesRead < packetSize)
                    {
                        // The game pads the chunk out with zeros, so if there's a primitive with type zero (invalid) then we early out of the loop.
                        GXPrimitiveType type = (GXPrimitiveType)reader.ReadByte();
                        if (type == 0 || numPrimitiveBytesRead >= packetSize)
                        {
                            break;
                        }

                        // The number of vertices this primitive has indexes for
                        ushort vertexCount = reader.ReadUInt16();
                        numPrimitiveBytesRead += 0x3; // 2 bytes for vertex count, one byte for GXPrimitiveType.

                        List <MeshVertexIndex> primitiveVertices = new List <MeshVertexIndex>();

                        for (int v = 0; v < vertexCount; v++)
                        {
                            MeshVertexIndex newVert = new MeshVertexIndex();
                            primitiveVertices.Add(newVert);

                            // Each vertex has an index for each ShapeAttribute specified by the Shape that we belong to. So we'll loop through
                            // each index and load it appropriately (as vertices can have different data sizes).
                            foreach (ShapeVertexAttribute curAttribute in attributes)
                            {
                                int  index        = 0;
                                uint numBytesRead = 0;

                                switch (curAttribute.DataType)
                                {
                                case VertexDataType.Unsigned8:
                                case VertexDataType.Signed8:
                                    index        = reader.ReadByte();
                                    numBytesRead = 1;
                                    break;

                                case VertexDataType.Unsigned16:
                                case VertexDataType.Signed16:
                                    index        = reader.ReadUInt16();
                                    numBytesRead = 2;
                                    break;

                                case VertexDataType.Float32:
                                case VertexDataType.None:
                                default:
                                    System.Console.WriteLine("Unknown Data Type {0} for ShapeAttribute!", curAttribute.DataType);
                                    break;
                                }

                                // We now have the index into the datatype this array points to. We can now inspect the array type of the
                                // attribute to get the value out of the correct source array.
                                switch (curAttribute.ArrayType)
                                {
                                case VertexArrayType.Position: newVert.Position = index; break;

                                case VertexArrayType.PositionMatrixIndex: newVert.PosMtxIndex = index / 3; break;

                                case VertexArrayType.Normal: newVert.Normal = index; break;

                                case VertexArrayType.Color0: newVert.Color0 = index; break;

                                case VertexArrayType.Color1: newVert.Color1 = index; break;

                                case VertexArrayType.Tex0:  newVert.Tex0 = index; break;

                                case VertexArrayType.Tex1:  newVert.Tex1 = index; break;

                                case VertexArrayType.Tex2:  newVert.Tex2 = index; break;

                                case VertexArrayType.Tex3:  newVert.Tex3 = index; break;

                                case VertexArrayType.Tex4:  newVert.Tex4 = index; break;

                                case VertexArrayType.Tex5:  newVert.Tex5 = index; break;

                                case VertexArrayType.Tex6:  newVert.Tex6 = index; break;

                                case VertexArrayType.Tex7:  newVert.Tex7 = index; break;

                                default:
                                    System.Console.WriteLine("Unsupported ArrayType {0} for ShapeAttribute!", curAttribute.ArrayType);
                                    break;
                                }

                                numPrimitiveBytesRead += numBytesRead;
                            }
                        }

                        // All vertices have now been loaded into the primitiveIndexes array. We can now convert them if needed
                        // to triangle lists, instead of triangle fans, strips, etc.
                        grp.Indices.AddRange(ConvertTopologyToTriangles(type, primitiveVertices));
                    }

                    shape.MatrixGroups.Add(grp);
                }
            }

            FixGroupSkinningIndices();
        }