示例#1
0
        public static PrimitiveType GLPrimitiveType(GXPrimitiveType gxprim)
        {
            switch (gxprim)
            {
            case GXPrimitiveType.Lines:
                return(PrimitiveType.Lines);

            case GXPrimitiveType.LineStrip:
                return(PrimitiveType.LineStrip);

            case GXPrimitiveType.Points:
                return(PrimitiveType.Points);

            case GXPrimitiveType.Quads:
                return(PrimitiveType.Quads);

            case GXPrimitiveType.TriangleFan:
                return(PrimitiveType.TriangleFan);

            case GXPrimitiveType.Triangles:
                return(PrimitiveType.Triangles);

            case GXPrimitiveType.TriangleStrip:
                return(PrimitiveType.TriangleStrip);

            default:
                return(PrimitiveType.Points);
            }
        }
示例#2
0
        public List <MeshVertexIndex> ConvertTopologyToTriangles(GXPrimitiveType fromType, List <MeshVertexIndex> indexes)
        {
            List <MeshVertexIndex> sortedIndexes = new List <MeshVertexIndex>();

            if (fromType == GXPrimitiveType.TriangleStrip)
            {
                for (int v = 2; v < indexes.Count; v++)
                {
                    bool isEven = v % 2 != 0;
                    MeshVertexIndex[] newTri = new MeshVertexIndex[3];

                    newTri[0] = indexes[v - 2];
                    newTri[1] = isEven ? indexes[v] : indexes[v - 1];
                    newTri[2] = isEven ? indexes[v - 1] : indexes[v];

                    // Check against degenerate triangles (a triangle which shares indexes)
                    if (newTri[0] != newTri[1] && newTri[1] != newTri[2] && newTri[2] != newTri[0])
                    {
                        sortedIndexes.AddRange(newTri);
                    }
                    else
                    {
                        System.Console.WriteLine("Degenerate triangle detected, skipping TriangleStrip conversion to triangle.");
                    }
                }
            }
            else if (fromType == GXPrimitiveType.TriangleFan)
            {
                for (int v = 1; v < indexes.Count - 1; v++)
                {
                    // Triangle is always, v, v+1, and index[0]?
                    MeshVertexIndex[] newTri = new MeshVertexIndex[3];
                    newTri[0] = indexes[v];
                    newTri[1] = indexes[v + 1];
                    newTri[2] = indexes[0];

                    // Check against degenerate triangles (a triangle which shares indexes)
                    if (newTri[0] != newTri[1] && newTri[1] != newTri[2] && newTri[2] != newTri[0])
                    {
                        sortedIndexes.AddRange(newTri);
                    }
                    else
                    {
                        System.Console.WriteLine("Degenerate triangle detected, skipping TriangleFan conversion to triangle.");
                    }
                }
            }
            else if (fromType == GXPrimitiveType.Triangles)
            {
                // The good news is, Triangles just go straight though!
                sortedIndexes.AddRange(indexes);
            }
            else
            {
                System.Console.WriteLine("Unsupported GXPrimitiveType: {0} in conversion to Triangle List.", fromType);
            }

            return(sortedIndexes);
        }
示例#3
0
        private List <GXVertex> ReadMdlPrimitives(EndianBinaryReader reader, ShapePacket curPak)
        {
            // OK. As far as we can tell, mdl suckerpunches our normal
            // understanding of the GC's GX, so we have to deal with a
            // hardcoded implementation.

            List <GXVertex> outList = new List <GXVertex>();

            GXPrimitiveType curPrim = (GXPrimitiveType)reader.ReadByte();

            while (curPrim != GXPrimitiveType.None)
            {
                List <GXVertex> tempVerts = new List <GXVertex>();
                ushort          vertCount = reader.ReadUInt16();

                for (int i = 0; i < vertCount; i++)
                {
                    List <int> tempList = new List <int>();

                    byte mtxPos1 = (byte)(reader.ReadByte() / 3);
                    byte mtxPos2 = (byte)(reader.ReadByte() / 3);
                    byte mtxPos3 = (byte)(reader.ReadByte() / 3);



                    if (mtxPos1 != mtxPos2)
                    {
                    }

                    ushort posIndex    = reader.ReadUInt16();
                    ushort normalIndex = reader.ReadUInt16();
                    ushort tex0Index   = reader.ReadUInt16();

                    if (ActiveAttributes.Contains(GXAttribute.PositionMatrixIndex))
                    {
                        tempList.Add((int)mtxPos1);
                    }
                    if (ActiveAttributes.Contains(GXAttribute.Position))
                    {
                        tempList.Add(posIndex);
                    }
                    if (ActiveAttributes.Contains(GXAttribute.Normal))
                    {
                        tempList.Add(normalIndex);
                    }
                    if (ActiveAttributes.Contains(GXAttribute.Tex0))
                    {
                        tempList.Add(tex0Index);
                    }

                    tempVerts.Add(new GXVertex(tempList.ToArray()));
                }

                outList.AddRange(ConvertTopologyToTriangles(curPrim, tempVerts));
                curPrim = (GXPrimitiveType)reader.ReadByte();
            }

            return(outList);
        }
示例#4
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="type"></param>
        /// <param name="vertices"></param>
        /// <param name="parent"></param>
        /// <returns></returns>
        private List <IOVertex> ConvertGXDLtoTriangleList(GXPrimitiveType type, List <SBHsdVertex> vertices, SBHsdBone parent)
        {
            var list = new List <IOVertex>();

            foreach (var v in vertices)
            {
                var vertex = new IOVertex()
                {
                    Position = new System.Numerics.Vector3(v.POS.X, v.POS.Y, v.POS.Z),
                    Normal   = new System.Numerics.Vector3(v.NRM.X, v.NRM.Y, v.NRM.Z),
                };
                vertex.SetUV(v.UV0.X, v.UV0.Y, 0);

                for (int i = 0; i < 4; i++)
                {
                    if (v.Weight[i] > 0)
                    {
                        vertex.Envelope.Weights.Add(new IOBoneWeight()
                        {
                            BoneName = "JOBJ_" + (int)v.Bone[i],
                            Weight   = v.Weight[i]
                        });
                    }
                }

                if (parent != null)
                {
                    vertex.Transform(TktoNumeric(parent.WorldTransform));

                    if (vertex.Envelope.Weights.Count == 0)
                    {
                        vertex.Envelope.Weights.Add(new IOBoneWeight()
                        {
                            BoneName = parent.Name,
                            Weight   = 1
                        });
                    }
                }
                if (v.Weight.X == 1)
                {
                    vertex.Transform(TktoNumeric(Skeleton.Bones[(int)v.Bone.X].WorldTransform));
                }
                list.Add(vertex);
            }

            if (type == GXPrimitiveType.TriangleStrip)
            {
                TriangleConvert.StripToList(list, out list);
            }

            if (type == GXPrimitiveType.Quads)
            {
                TriangleConvert.QuadToList(list, out list);
            }

            return(list);
        }
示例#5
0
        public bool Read(BinaryReaderExt Reader, GX_Attribute[] Attributes)
        {
            PrimitiveType = (GXPrimitiveType)Reader.ReadByte();

            if (PrimitiveType == 0)
            {
                return(false);
            }

            var count = Reader.ReadInt16();

            Indices = new GX_IndexGroup[count];
            for (int j = 0; j < count; j++)
            {
                GX_IndexGroup g = new GX_IndexGroup();
                g.Indices = new ushort[Attributes.Length];
                int i = 0;
                Indices[j] = g;
                foreach (var att in Attributes)
                {
                    if (att.AttributeName == GXAttribName.GX_VA_NULL)
                    {
                        continue;
                    }
                    switch (att.AttributeType)
                    {
                    case GXAttribType.GX_DIRECT:
                        if (att.AttributeName == GXAttribName.GX_VA_CLR0)
                        {
                            g.Clr0 = ReadDirectGXColor(Reader, (int)att.CompType);
                        }
                        else if (att.AttributeName == GXAttribName.GX_VA_CLR1)
                        {
                            g.Clr1 = ReadDirectGXColor(Reader, (int)att.CompType);
                        }
                        else
                        {
                            g.Indices[i] = Reader.ReadByte();
                        }
                        break;

                    case GXAttribType.GX_INDEX8:
                        g.Indices[i] = Reader.ReadByte();
                        break;

                    case GXAttribType.GX_INDEX16:
                        g.Indices[i] = Reader.ReadUInt16();
                        break;
                    }
                    i++;
                }
            }
            return(true);
        }
示例#6
0
        public bool Read(HSDReader Reader, HSD_AttributeGroup Attributes)
        {
            PrimitiveType = (GXPrimitiveType)Reader.ReadByte();
            if (PrimitiveType == 0)
            {
                return(false);
            }
            Count   = Reader.ReadUInt16();
            Indices = new GXIndexGroup[Count];
            for (int j = 0; j < Count; j++)
            {
                GXIndexGroup g = new GXIndexGroup();
                g.Indices = new ushort[Attributes.Attributes.Count];
                int i = 0;
                Indices[j] = g;
                foreach (GXVertexBuffer att in Attributes.Attributes)
                {
                    switch (att.AttributeType)
                    {
                    case GXAttribType.GX_DIRECT:
                        if (att.Name == GXAttribName.GX_VA_CLR0)
                        {
                            g.Clr0 = ReadGXClr(Reader, (int)att.CompType);
                        }
                        else if (att.Name == GXAttribName.GX_VA_CLR1)
                        {
                            g.Clr1 = ReadGXClr(Reader, (int)att.CompType);
                        }
                        else
                        {
                            g.Indices[i] = Reader.ReadByte();
                        }
                        break;

                    case GXAttribType.GX_INDEX8:
                        g.Indices[i] = Reader.ReadByte();
                        break;

                    case GXAttribType.GX_INDEX16:
                        g.Indices[i] = Reader.ReadUInt16();
                        break;
                    }
                    i++;
                }
            }
            return(true);
        }
示例#7
0
        private List <IOVertex> ConvertGXDLtoTriangleList(GXPrimitiveType type, List <SBHsdVertex> vertices, SBHsdBone parent)
        {
            var list = new List <IOVertex>();

            foreach (var v in vertices)
            {
                var vertex = new IOVertex()
                {
                    Position    = GXtoGL.GLVector3(v.POS),
                    Normal      = GXtoGL.GLVector3(v.NRM),
                    UV0         = GXtoGL.GLVector2(v.UV0),
                    BoneIndices = v.Bone,
                    BoneWeights = v.Weight,
                };
                if (parent != null)
                {
                    vertex.Position = Vector3.TransformPosition(vertex.Position, parent.WorldTransform);
                    vertex.Normal   = Vector3.TransformNormal(vertex.Normal, parent.WorldTransform);

                    if (vertex.BoneWeights.X == 0)
                    {
                        vertex.BoneWeights.X = 1;
                        vertex.BoneIndices.X = Skeleton.IndexOfBone(parent);
                    }
                }
                if (v.Weight.X == 1)
                {
                    vertex.Position = Vector3.TransformPosition(vertex.Position, Skeleton.Bones[(int)v.Bone.X].WorldTransform);
                    vertex.Normal   = Vector3.TransformNormal(vertex.Normal, Skeleton.Bones[(int)v.Bone.X].WorldTransform);
                }
                list.Add(vertex);
            }

            if (type == GXPrimitiveType.TriangleStrip)
            {
                TriangleConvert.StripToList(list, out list);
            }

            if (type == GXPrimitiveType.Quads)
            {
                TriangleConvert.QuadToList(list, out list);
            }

            return(list);
        }
示例#8
0
        private List <GXVertex> ConvertTopologyToTriangles(GXPrimitiveType originalType, List <GXVertex> vertices)
        {
            List <GXVertex> tris = new List <GXVertex>();

            switch (originalType)
            {
            case GXPrimitiveType.Triangles:
                return(tris);

            case GXPrimitiveType.TriangleStrip:
                for (int v = 2; v < vertices.Count; v++)
                {
                    bool even = v % 2 != 0;
                    var  tri  = new GXVertex[3];
                    tri[0] = vertices[v - 2];
                    tri[1] = even ? vertices[v] : vertices[v - 1];
                    tri[2] = even ? vertices[v - 1] : vertices[v];
                    if (tri[0] != tri[1] && tri[1] != tri[2] && tri[2] != tri[0])
                    {
                        tris.AddRange(tri);
                    }
                }
                break;

            case GXPrimitiveType.TriangleFan:
                for (int v = 1; v < vertices.Count; v++)
                {
                    var tri = new GXVertex[3];
                    tri[0] = vertices[v];
                    tri[1] = vertices[v + 1];
                    tri[2] = vertices[0];

                    if (tri[0] != tri[1] && tri[1] != tri[2] && tri[2] != tri[0])
                    {
                        tris.AddRange(tri);
                    }
                }
                break;
            }

            return(tris);
        }
示例#9
0
        private List <GXVertex> ReadPrimitives(EndianBinaryReader reader)
        {
            List <GXVertex> verts = new List <GXVertex>();

            GXPrimitiveType curPrim = (GXPrimitiveType)reader.ReadByte();

            while (curPrim != GXPrimitiveType.None)
            {
                List <GXVertex> temp = new List <GXVertex>();

                ushort vertCount = reader.ReadUInt16();

                for (int i = 0; i < vertCount; i++)
                {
                    temp.Add(new GXVertex(reader, ActiveAttributes.Count));
                }

                verts.AddRange(ConvertTopologyToTriangles(curPrim, temp));

                curPrim = (GXPrimitiveType)reader.ReadByte();
            }

            return(verts);
        }
示例#10
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();
        }
示例#11
0
        private static void LoadSHP1SectionFromFile(MeshVertexAttributeHolder vertexData, Mesh j3dMesh, EndianBinaryReader reader, long chunkStart)
        {
            short batchCount           = reader.ReadInt16();
            short padding              = reader.ReadInt16();
            int   batchOffset          = reader.ReadInt32();
            int   unknownTableOffset   = reader.ReadInt32(); // Another one of those 0->(n-1) counters. I think all sections have it? Might be part of the way they used inheritance to write files.
            int   alwaysZero           = reader.ReadInt32(); Trace.Assert(alwaysZero == 0);
            int   attributeOffset      = reader.ReadInt32();
            int   matrixTableOffset    = reader.ReadInt32();
            int   primitiveDataOffset  = reader.ReadInt32();
            int   matrixDataOffset     = reader.ReadInt32();
            int   packetLocationOffset = reader.ReadInt32();

            // Batches can have different attributes (ie: some have pos, some have normal, some have texcoords, etc.) they're split by batches,
            // where everything in the batch uses the same set of vertex attributes. Each batch then has several packets, which are a collection
            // of primitives.
            for (int b = 0; b < batchCount; b++)
            {
                MeshBatch meshBatch = new MeshBatch();
                j3dMesh.SubMeshes.Add(meshBatch);
                int overallVertexCount = 0;
                meshBatch.PrimitveType = OpenTK.Graphics.OpenGL.PrimitiveType.TriangleStrip; // HackHack, this varies per primitive.
                // We need to look on each primitive and convert them to trianglestrips, most are TS some are TF's.

                // We re-use the list struct here to dynamically add paired pos/col/tex as we load them
                // then we convert them into arrays for the MeshBatch afterwards.
                MeshVertexAttributeHolder meshVertexData = new MeshVertexAttributeHolder();

                // chunkStart + batchOffset gets you the position where the batches are listed
                // 0x28 * b gives you the right batch - a batch is 0x28 in length
                reader.BaseStream.Position = chunkStart + batchOffset + (0x28 * b);
                long batchStart = reader.BaseStream.Position;

                byte matrixType = reader.ReadByte();
                Trace.Assert(reader.ReadByte() == 0xFF); // Padding
                ushort packetCount          = reader.ReadUInt16();
                ushort batchAttributeOffset = reader.ReadUInt16();
                ushort firstMatrixIndex     = reader.ReadUInt16();
                ushort firstPacketIndex     = reader.ReadUInt16();
                ushort unknownpadding       = reader.ReadUInt16(); Trace.Assert(unknownpadding == 0xFFFF);

                float   boundingSphereDiameter = reader.ReadSingle();
                Vector3 boundingBoxMin         = new Vector3();
                boundingBoxMin.X = reader.ReadSingle();
                boundingBoxMin.Y = reader.ReadSingle();
                boundingBoxMin.Z = reader.ReadSingle();

                Vector3 boundingBoxMax = new Vector3();
                boundingBoxMax.X = reader.ReadSingle();
                boundingBoxMax.Y = reader.ReadSingle();
                boundingBoxMax.Z = reader.ReadSingle();


                // We need to figure out how many primitive attributes there are in the SHP1 section. This can differ from the number of
                // attributes in the VTX1 section, as the SHP1 can also include things like PositionMatrixIndex, so the count can be different.
                // This also varies *per batch* as not all batches will have the things like PositionMatrixIndex.
                reader.BaseStream.Position = chunkStart + attributeOffset + batchAttributeOffset;
                var batchAttributes = new List <ShapeAttribute>();
                do
                {
                    ShapeAttribute attribute = new ShapeAttribute();
                    attribute.ArrayType = (VertexArrayType)reader.ReadInt32();
                    attribute.DataType  = (VertexDataType)reader.ReadInt32();

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

                    batchAttributes.Add(attribute);
                } while (true);


                for (ushort p = 0; p < packetCount; p++)
                {
                    // Packet Location
                    reader.BaseStream.Position  = chunkStart + packetLocationOffset;
                    reader.BaseStream.Position += (firstPacketIndex + p) * 0x8; // A Packet Location is 0x8 long, so we skip ahead to the right one.

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

                    // Read the matrix data for this packet
                    reader.BaseStream.Position = chunkStart + matrixDataOffset + (firstMatrixIndex + p) * 0x08;
                    ushort matrixUnknown0   = reader.ReadUInt16();
                    ushort matrixCount      = reader.ReadUInt16();
                    uint   matrixFirstIndex = reader.ReadUInt32();

                    // Skip ahead to the actual data.
                    reader.BaseStream.Position = chunkStart + matrixTableOffset + (matrixFirstIndex * 0x2);
                    List <ushort> matrixTable = new List <ushort>();
                    for (int m = 0; m < matrixCount; m++)
                    {
                        matrixTable.Add(reader.ReadUInt16());
                    }

                    // Jump the read head to the location of the primitives for this packet.
                    reader.BaseStream.Position = chunkStart + primitiveDataOffset + packetOffset;
                    int numVertexesAtPacketStart = meshVertexData.PositionMatrixIndexes.Count;

                    uint numPrimitiveBytesRead = 0;
                    while (numPrimitiveBytesRead < packetSize)
                    {
                        // Jump to the primitives
                        // Primitives
                        GXPrimitiveType type = (GXPrimitiveType)reader.ReadByte();
                        // Game pads the chunks out with zeros, so this is the signal for an early break;
                        if (type == 0 || numPrimitiveBytesRead >= packetSize)
                        {
                            break;
                        }

                        ushort vertexCount = reader.ReadUInt16();

                        meshBatch.PrimitveType = type == GXPrimitiveType.TriangleStrip ? OpenTK.Graphics.OpenGL.PrimitiveType.TriangleStrip : OpenTK.Graphics.OpenGL.PrimitiveType.TriangleFan;
                        //if (type != GXPrimitiveType.TriangleStrip)
                        //{
                        //    WLog.Warning(LogCategory.ModelLoading, null, "Unsupported GXPrimitiveType {0}", type);
                        //}

                        numPrimitiveBytesRead += 0x3; // Advance us by 3 for the Primitive header.

                        for (int v = 0; v < vertexCount; v++)
                        {
                            meshVertexData.Indexes.Add(overallVertexCount);
                            overallVertexCount++;

                            // Iterate through the attribute types. I think the actual vertices are stored in interleaved format,
                            // ie: there's say 13 vertexes but those 13 vertexes will have a pos/color/tex index listed after it
                            // depending on the overall attributes of the file.
                            for (int attrib = 0; attrib < batchAttributes.Count; attrib++)
                            {
                                // Jump to primitive location
                                //reader.BaseStream.Position = chunkStart + primitiveDataOffset + numPrimitiveBytesRead + packetOffset;

                                // Now that we know how big the vertex type is stored in (either a Signed8 or a Signed16) we can read that much data
                                // and then we can use that index and index into
                                int  val          = 0;
                                uint numBytesRead = 0;
                                switch (batchAttributes[attrib].DataType)
                                {
                                case VertexDataType.Signed8:
                                    val          = reader.ReadByte();
                                    numBytesRead = 1;
                                    break;

                                case VertexDataType.Signed16:
                                    val          = reader.ReadInt16();
                                    numBytesRead = 2;
                                    break;

                                default:
                                    WLog.Warning(LogCategory.ModelLoading, null, "Unknown Batch Index Type: {0}", batchAttributes[attrib].DataType);
                                    break;
                                }

                                // Now that we know what the index is, we can retrieve it from the appropriate array
                                // and stick it into our vertex. The J3D format removes all duplicate vertex attributes
                                // so we need to re-duplicate them here so that we can feed them to a PC GPU in a normal fashion.
                                switch (batchAttributes[attrib].ArrayType)
                                {
                                case VertexArrayType.Position:
                                    meshVertexData.Position.Add(vertexData.Position[val]);
                                    break;

                                case VertexArrayType.PositionMatrixIndex:
                                    meshVertexData.PositionMatrixIndexes.Add(val);
                                    break;

                                case VertexArrayType.Normal:
                                    meshVertexData.Normal.Add(vertexData.Normal[val]);
                                    break;

                                case VertexArrayType.Color0:
                                    meshVertexData.Color0.Add(vertexData.Color0[val]);
                                    break;

                                case VertexArrayType.Color1:
                                    meshVertexData.Color1.Add(vertexData.Color1[val]);
                                    break;

                                case VertexArrayType.Tex0:
                                    meshVertexData.Tex0.Add(vertexData.Tex0[val]);
                                    break;

                                case VertexArrayType.Tex1:
                                    meshVertexData.Tex1.Add(vertexData.Tex1[val]);
                                    break;

                                case VertexArrayType.Tex2:
                                    meshVertexData.Tex2.Add(vertexData.Tex2[val]);
                                    break;

                                case VertexArrayType.Tex3:
                                    meshVertexData.Tex3.Add(vertexData.Tex3[val]);
                                    break;

                                case VertexArrayType.Tex4:
                                    meshVertexData.Tex4.Add(vertexData.Tex4[val]);
                                    break;

                                case VertexArrayType.Tex5:
                                    meshVertexData.Tex5.Add(vertexData.Tex5[val]);
                                    break;

                                case VertexArrayType.Tex6:
                                    meshVertexData.Tex6.Add(vertexData.Tex6[val]);
                                    break;

                                case VertexArrayType.Tex7:
                                    meshVertexData.Tex7.Add(vertexData.Tex7[val]);
                                    break;

                                default:
                                    WLog.Warning(LogCategory.ModelLoading, null, "Unsupported attribType {0}", batchAttributes[attrib].ArrayType);
                                    break;
                                }

                                numPrimitiveBytesRead += numBytesRead;
                            }

                            // Gonna try a weird hack, where if the mesh doesn't have PMI values, we're going to use just use the packet index into the matrixtable
                            // so that all meshes always have PMI values, to abstract out the ones that don't seem to (but still have matrixtable) junk. It's a guess
                            // here.
                            if (batchAttributes.Find(x => x.ArrayType == VertexArrayType.PositionMatrixIndex) == null)
                            {
                                meshVertexData.PositionMatrixIndexes.Add(p);
                            }
                        }

                        // After we write a primitive, write a special null-terminator which signifies the GPU to do a primitive restart for the next tri-strip.
                        meshVertexData.Indexes.Add(0xFFFF);
                    }

                    // The Matrix Table is per-packet, so we need to reach into the the matrix table after processing each packet
                    // and transform the indexes. Yuck. Yay.
                    for (int j = numVertexesAtPacketStart; j < meshVertexData.PositionMatrixIndexes.Count; j++)
                    {
                        // Yes you divide this by 3. No, no one knows why. $20 to the person who figures out why.
                        meshBatch.drawIndexes.Add(matrixTable[meshVertexData.PositionMatrixIndexes[j] / 3]);
                    }
                }

                meshBatch.Vertices             = meshVertexData.Position.ToArray();
                meshBatch.Color0               = meshVertexData.Color0.ToArray();
                meshBatch.Color1               = meshVertexData.Color1.ToArray();
                meshBatch.TexCoord0            = meshVertexData.Tex0.ToArray();
                meshBatch.TexCoord1            = meshVertexData.Tex0.ToArray();
                meshBatch.TexCoord2            = meshVertexData.Tex0.ToArray();
                meshBatch.TexCoord3            = meshVertexData.Tex0.ToArray();
                meshBatch.TexCoord4            = meshVertexData.Tex0.ToArray();
                meshBatch.TexCoord5            = meshVertexData.Tex0.ToArray();
                meshBatch.TexCoord6            = meshVertexData.Tex0.ToArray();
                meshBatch.TexCoord7            = meshVertexData.Tex0.ToArray();
                meshBatch.Indexes              = meshVertexData.Indexes.ToArray();
                meshBatch.PositionMatrixIndexs = meshVertexData.PositionMatrixIndexes; // This should be obsolete as they should be transformed already.
            }
        }
示例#12
0
 public Primitive(GXPrimitiveType type)
 {
     PrimitiveType = type;
     Vertices      = new List <Vertex>();
 }
示例#13
0
        /// <summary>
        /// Creates a primitive group for the vertex buffer
        /// </summary>
        /// <param name="type"></param>
        /// <param name="Vertices"></param>
        /// <param name="Attributes"></param>
        /// <returns></returns>
        private GX_PrimitiveGroup Compress(GXPrimitiveType type, GX_Vertex[] Vertices, GXAttribName[] Attributes)
        {
            GX_PrimitiveGroup g = new GX_PrimitiveGroup();

            g.PrimitiveType = type;
            g.Indices       = new GX_IndexGroup[Vertices.Length];
            int IndexGroupIndex = 0;

            foreach (GX_Vertex v in Vertices)
            {
                GX_IndexGroup ig = new GX_IndexGroup();
                ig.Indices = new ushort[Attributes.Length];

                int i = 0;
                foreach (var b in Attributes)
                {
                    switch (b)
                    {
                    case GXAttribName.GX_VA_CLR0:
                        ig.Clr0 = v.CLR0.ToBytes();
                        break;

                    case GXAttribName.GX_VA_CLR1:
                        ig.Clr1 = v.CLR1.ToBytes();
                        break;

                    case GXAttribName.GX_VA_PNMTXIDX:
                        ig.Indices[i] = v.PNMTXIDX;
                        break;

                    case GXAttribName.GX_VA_TEX0MTXIDX:
                        ig.Indices[i] = v.TEX0MTXIDX;
                        break;

                    case GXAttribName.GX_VA_TEX1MTXIDX:
                        ig.Indices[i] = v.TEX1MTXIDX;
                        break;

                    case GXAttribName.GX_VA_NULL: break;

                    case GXAttribName.GX_VA_POS: ig.Indices[i] = GetIndex(b, new float[] { v.POS.X, v.POS.Y, v.POS.Z }); break;

                    case GXAttribName.GX_VA_NRM: ig.Indices[i] = GetIndex(b, new float[] { v.NRM.X, v.NRM.Y, v.NRM.Z }); break;

                    case GXAttribName.GX_VA_NBT: ig.Indices[i] = GetIndex(b, new float[] { v.NRM.X, v.NRM.Y, v.NRM.Z, v.BITAN.X, v.BITAN.Y, v.BITAN.Z, v.TAN.X, v.TAN.Y, v.TAN.Z }); break;

                    case GXAttribName.GX_VA_TEX0: ig.Indices[i] = GetIndex(b, new float[] { v.TEX0.X, v.TEX0.Y }); break;

                    case GXAttribName.GX_VA_TEX1: ig.Indices[i] = GetIndex(b, new float[] { v.TEX1.X, v.TEX1.Y }); break;

                    case GXAttribName.GX_VA_TEX2: ig.Indices[i] = GetIndex(b, new float[] { v.TEX2.X, v.TEX2.Y }); break;

                    case GXAttribName.GX_VA_TEX3: ig.Indices[i] = GetIndex(b, new float[] { v.TEX3.X, v.TEX3.Y }); break;

                    case GXAttribName.GX_VA_TEX4: ig.Indices[i] = GetIndex(b, new float[] { v.TEX4.X, v.TEX4.Y }); break;

                    case GXAttribName.GX_VA_TEX5: ig.Indices[i] = GetIndex(b, new float[] { v.TEX5.X, v.TEX5.Y }); break;

                    case GXAttribName.GX_VA_TEX6: ig.Indices[i] = GetIndex(b, new float[] { v.TEX6.X, v.TEX6.Y }); break;

                    case GXAttribName.GX_VA_TEX7: ig.Indices[i] = GetIndex(b, new float[] { v.TEX7.X, v.TEX7.Y }); break;

                    //case GXAttribName.GX_VA_CLR0: ig.Indices[i] = GetIndex(b, v.CLR0); break;
                    default:
                        throw new Exception("Error Building " + b);
                    }
                    i++;
                }
                g.Indices[IndexGroupIndex++] = ig;
            }

            return(g);
        }
示例#14
0
        public void ReadSHP1FromStream(EndianBinaryReader reader, long tagStart, MeshVertexHolder compressedVertexData)
        {
            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();

            reader.BaseStream.Position = tagStart + remapTableOffset;
            ShapeRemapTable            = new List <short>();
            for (int i = 0; i < ShapeCount; i++)
            {
                ShapeRemapTable.Add(reader.ReadInt16());
            }

            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*/;
                long  shapeStart = reader.BaseStream.Position;
                Shape shape      = new Shape();
                shape.MatrixType = reader.ReadByte();
                Trace.Assert(reader.ReadByte() == 0xFF); // Padding

                // Number of Packets (of data) contained in this Shape
                ushort packetCount = 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 firstPacketIndex = 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());

                // Determine which Attributes this particular shape uses.
                reader.BaseStream.Position = tagStart + attributeOffset + batchAttributeOffset;
                List <ShapeVertexAttribute> attributes = new List <ShapeVertexAttribute>();
                do
                {
                    ShapeVertexAttribute attribute = new ShapeVertexAttribute((VertexArrayType)reader.ReadInt32(), (VertexDataType)reader.ReadInt32());
                    if (attribute.ArrayType == VertexArrayType.NullAttr)
                    {
                        break;
                    }

                    attributes.Add(attribute);
                } while (true);

                shape.BoundingSphereDiameter = boundingSphereDiameter;
                shape.BoundingBox            = new FAABox(bboxMin, bboxMax);
                shape.Attributes             = attributes;
                Shapes.Add(shape);

                int numVertexRead = 0;
                for (ushort p = 0; p < packetCount; p++)
                {
                    // 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 + ((firstPacketIndex + 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();

                    Shape.SkinDataTable matrixData = new Shape.SkinDataTable(matrixUnknown0);
                    shape.MatrixDataTable.Add(matrixData);
                    matrixData.FirstRelevantVertexIndex = shape.VertexData.Position.Count;

                    // 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; 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.
                        var triangleList = ConvertTopologyToTriangles(type, primitiveVertices);
                        for (int i = 0; i < triangleList.Count; i++)
                        {
                            shape.Indexes.Add(numVertexRead);
                            numVertexRead++;

                            var tri = triangleList[i];
                            if (tri.Position >= 0)
                            {
                                shape.VertexData.Position.Add(compressedVertexData.Position[tri.Position]);
                            }
                            if (tri.Normal >= 0)
                            {
                                shape.VertexData.Normal.Add(compressedVertexData.Normal[tri.Normal]);
                            }
                            if (tri.Binormal >= 0)
                            {
                                shape.VertexData.Binormal.Add(compressedVertexData.Binormal[tri.Binormal]);
                            }
                            if (tri.Color0 >= 0)
                            {
                                shape.VertexData.Color0.Add(compressedVertexData.Color0[tri.Color0]);
                            }
                            if (tri.Color1 >= 0)
                            {
                                shape.VertexData.Color1.Add(compressedVertexData.Color1[tri.Color1]);
                            }
                            if (tri.Tex0 >= 0)
                            {
                                shape.VertexData.Tex0.Add(compressedVertexData.Tex0[tri.Tex0]);
                            }
                            if (tri.Tex1 >= 0)
                            {
                                shape.VertexData.Tex1.Add(compressedVertexData.Tex1[tri.Tex1]);
                            }
                            if (tri.Tex2 >= 0)
                            {
                                shape.VertexData.Tex2.Add(compressedVertexData.Tex2[tri.Tex2]);
                            }
                            if (tri.Tex3 >= 0)
                            {
                                shape.VertexData.Tex3.Add(compressedVertexData.Tex3[tri.Tex3]);
                            }
                            if (tri.Tex4 >= 0)
                            {
                                shape.VertexData.Tex4.Add(compressedVertexData.Tex4[tri.Tex4]);
                            }
                            if (tri.Tex5 >= 0)
                            {
                                shape.VertexData.Tex5.Add(compressedVertexData.Tex5[tri.Tex5]);
                            }
                            if (tri.Tex6 >= 0)
                            {
                                shape.VertexData.Tex6.Add(compressedVertexData.Tex6[tri.Tex6]);
                            }
                            if (tri.Tex7 >= 0)
                            {
                                shape.VertexData.Tex7.Add(compressedVertexData.Tex7[tri.Tex7]);
                            }

                            // We pre-divide the index here just to make life simpler. For some reason the index is multiplied by three
                            // and it's not entirely clear why. Might be related to doing skinning on the GPU and offsetting the correct
                            // number of floats in a matrix?
                            if (tri.PosMtxIndex >= 0)
                            {
                                shape.VertexData.PositionMatrixIndexes.Add(tri.PosMtxIndex / 3);
                            }
                            else
                            {
                                shape.VertexData.PositionMatrixIndexes.Add(0);
                            }
                        }
                    }

                    // Set the last relevant vertex for this packet.
                    matrixData.LastRelevantVertexIndex = shape.VertexData.Position.Count;
                }

                shape.UploadBuffersToGPU(false);
            }
        }
示例#15
0
        /// <summary>
        /// Creates a primitive group for the vertex buffer
        /// </summary>
        /// <param name="type"></param>
        /// <param name="Vertices"></param>
        /// <param name="Attributes"></param>
        /// <returns></returns>
        public GXPrimitiveGroup Compress(GXPrimitiveType type, GXVertex[] Vertices, HSD_AttributeGroup Attributes)
        {
            GXPrimitiveGroup g = new GXPrimitiveGroup();

            g.PrimitiveType = type;
            g.Indices       = new GXIndexGroup[Vertices.Length];
            int IndexGroupIndex = 0;

            foreach (GXVertex v in Vertices)
            {
                GXIndexGroup ig = new GXIndexGroup();
                ig.Indices = new ushort[Attributes.Attributes.Count];

                int i = 0;
                foreach (GXVertexBuffer b in Attributes.Attributes)
                {
                    switch (b.AttributeType)
                    {
                    case GXAttribType.GX_DIRECT:
                        if (b.Name == GXAttribName.GX_VA_CLR0)
                        {
                            ig.Clr0 = new byte[] { (byte)(v.Clr0.R * 0xFF), (byte)(v.Clr0.G * 0xFF), (byte)(v.Clr0.B * 0xFF), (byte)(v.Clr0.A * 0xFF) }
                        }
                        ;
                        else
                        if (b.Name == GXAttribName.GX_VA_CLR1)
                        {
                            ig.Clr1 = new byte[] { (byte)(v.Clr1.R * 0xFF), (byte)(v.Clr1.G * 0xFF), (byte)(v.Clr1.B * 0xFF), (byte)(v.Clr1.A * 0xFF) }
                        }
                        ;
                        else
                        if (b.Name == GXAttribName.GX_VA_PNMTXIDX)
                        {
                            ig.Indices[i] = v.PMXID;
                        }
                        if (b.Name == GXAttribName.GX_VA_TEX0MTXIDX)
                        {
                            ig.Indices[i] = v.TEX0MTXIDX;
                        }
                        break;

                    default:
                        switch (b.Name)
                        {
                        case GXAttribName.GX_VA_POS: ig.Indices[i] = GetIndex(b, v.Pos); break;

                        case GXAttribName.GX_VA_NRM: ig.Indices[i] = GetIndex(b, v.Nrm); break;

                        case GXAttribName.GX_VA_TEX0: ig.Indices[i] = GetIndex(b, v.TEX0); break;

                        case GXAttribName.GX_VA_TEX1: ig.Indices[i] = GetIndex(b, v.TEX1); break;

                        case GXAttribName.GX_VA_CLR0: ig.Indices[i] = GetIndex(b, v.Clr0); break;

                        default:
                            throw new Exception("Error Building " + b.Name);
                        }

                        break;
                    }
                    i++;
                }
                g.Indices[IndexGroupIndex++] = ig;
            }

            return(g);
        }