Ejemplo n.º 1
0
            public void BuildModel(int modelIndex)
            {
                _srModel = _srFile.Models[modelIndex];
                String modelName = _objectName + "-" + modelIndex.ToString();

                #region Materials
                ProgressStage = "Model " + modelIndex.ToString() + " - Creating Materials";
                Thread.Sleep(500);
                for (int materialIndex = 0; materialIndex < _srModel.MaterialCount; materialIndex++)
                {
                    Material material     = new Material();
                    Color    colorDiffuse = Color.FromArgb((int)unchecked (_srModel.Materials[materialIndex].colour));
                    material.Diffuse         = colorDiffuse;
                    material.TextureFileName = GetTextureName(_srModel, materialIndex);
                    Materials.Add(material);

                    progressLevel += _srModel.IndexCount / _srModel.Groups.Length;
                }
                #endregion

                #region Groups
                for (int groupIndex = 0; groupIndex < _srModel.Groups.Length; groupIndex++)
                {
                    ProgressStage = "Model " + modelIndex.ToString() + " - Creating Group " + groupIndex.ToString();
                    Thread.Sleep(100);

                    Tree   srGroup   = _srModel.Groups[groupIndex];
                    String groupName = String.Format("{0}-{1}-group-{2}", _objectName, modelIndex, groupIndex);
                    if (srGroup != null && srGroup.mesh != null &&
                        srGroup.mesh.indexCount > 0 && srGroup.mesh.polygonCount > 0)
                    {
                        Node         group      = new Node();
                        SRMeshParser meshParser = new SRMeshParser(_objectName, _srFile);
                        meshParser.BuildMesh(modelIndex, groupIndex, 0);
                        foreach (SubMesh subMesh in meshParser.SubMeshes)
                        {
                            // If the mesh parser knew the total submeshes for the model,
                            // then this could be done inside BuildMesh.
                            subMesh.MeshIndex = Meshes.Count;
                            group.SubMeshIndices.Add(SubMeshes.Count);
                            SubMeshes.Add(subMesh);
                        }
                        Meshes.Add(meshParser.Mesh);
                        group.Name = groupName;
                        Groups.Add(group);
                    }
                }
                #endregion

                ModelName = modelName;

                if (_srFile.Asset == CDC.Asset.Unit)
                {
                    Model = new Unit(this);
                }
                else
                {
                    Model = new Physical(this);
                }
            }
Ejemplo n.º 2
0
        public void BuildModel(RenderResource resource, int modelIndex, CDC.Objects.ExportOptions options)
        {
            _srModel = _srFile.Models[modelIndex];
            String modelName = _objectName + "-" + modelIndex.ToString();

            #region Materials

            for (int materialIndex = 0; materialIndex < _srModel.MaterialCount; materialIndex++)
            {
                Material material = new Material();
                material.Visible = _srModel.Materials[materialIndex].visible;
                // Breaks early SR1 builds.
                //material.BlendMode = _srModel.Materials[materialIndex].blendMode;
                //int sortPush = unchecked((sbyte)_srModel.Materials[materialIndex].sortPush);
                //sortPush = 128 - sortPush;
                //material.DepthBias = (1.0f / 100000.0f) * sortPush;
                // Maybe use a hack for warpgates WARPGATE_DrawWarpGateRim indicates tree 3 should have lower priority.
                Color colorDiffuse = Color.FromArgb((int)unchecked (_srModel.Materials[materialIndex].colour));
                material.Diffuse         = colorDiffuse;
                material.TextureFileName = CDC.Objects.Models.SRModel.GetTextureName(_srModel, materialIndex, options);
                Materials.Add(material);
            }

            #endregion

            #region Groups
            for (int groupIndex = 0; groupIndex < _srModel.Groups.Length; groupIndex++)
            {
                Tree   srGroup   = _srModel.Groups[groupIndex];
                String groupName = String.Format("{0}-{1}-group-{2}", _objectName, modelIndex, groupIndex);
                if (srGroup != null && srGroup.mesh != null &&
                    srGroup.mesh.indexCount > 0 && srGroup.mesh.polygonCount > 0)
                {
                    ModelNode    group      = new ModelNode();
                    SRMeshParser meshParser = new SRMeshParser(_objectName, _srFile);
                    meshParser.BuildMesh(resource, modelIndex, groupIndex, 0);
                    foreach (SubMesh subMesh in meshParser.SubMeshes)
                    {
                        // If the mesh parser knew the total submeshes for the model,
                        // then this could be done inside BuildMesh.
                        subMesh.MeshIndex = Meshes.Count;
                        group.SubMeshIndices.Add(SubMeshes.Count);
                        SubMeshes.Add(subMesh);
                    }
                    Meshes.Add(meshParser.Mesh);
                    group.Name = groupName;
                    Groups.Add(group);
                }
            }
            #endregion

            ModelName = modelName;
            Model     = new Model(this);
        }
Ejemplo n.º 3
0
        public void MergeSubMeshes()
        {
            var submesh = new ObjSubMesh(this);

            submesh.PositionFaces = SubMeshes.SelectMany(i => i.PositionFaces).ToList();
            submesh.NormalFaces   = SubMeshes.SelectMany(i => i.NormalFaces).ToList();
            submesh.TexCoordFaces = SubMeshes.SelectMany(i => i.TexCoordFaces).ToList();

            SubMeshes.Clear();
            SubMeshes.Add(submesh);
        }
Ejemplo n.º 4
0
        internal void Read(EndianBinaryReader reader, MeshSection section = null)
        {
            uint signature = reader.ReadUInt32();

            reader.SeekCurrent(4);

            int  subMeshCount, materialCount;
            long subMeshesOffset, materialsOffset;

            // X stores submesh/material count before the bounding sphere
            if (section?.Format == BinaryFormat.X)
            {
                subMeshCount    = reader.ReadInt32();
                materialCount   = reader.ReadInt32();
                BoundingSphere  = reader.ReadBoundingSphere();
                subMeshesOffset = reader.ReadOffset();
                materialsOffset = reader.ReadOffset();
            }

            else
            {
                BoundingSphere  = reader.ReadBoundingSphere();
                subMeshCount    = reader.ReadInt32();
                subMeshesOffset = reader.ReadOffset();
                materialCount   = reader.ReadInt32();
                materialsOffset = reader.ReadOffset();
            }

            SubMeshes.Capacity = subMeshCount;
            for (int i = 0; i < subMeshCount; i++)
            {
                reader.ReadAtOffset(subMeshesOffset + i * SubMesh.GetByteSize(section?.Format ?? BinaryFormat.DT), () =>
                {
                    var submesh = new SubMesh();
                    submesh.Read(reader, section);
                    SubMeshes.Add(submesh);
                });
            }

            Materials.Capacity = materialCount;
            for (int i = 0; i < materialCount; i++)
            {
                reader.ReadAtOffset(materialsOffset + i * Material.BYTE_SIZE, () =>
                {
                    var material = new Material();
                    material.Read(reader);
                    Materials.Add(material);
                });
            }
        }
Ejemplo n.º 5
0
        public ushort CreateFreshSubMesh(ushort vertsToReserve, ushort trisToReserve)
        {
            // create custom List<> implementation with NativeArray that supports growth without assignment!
            SubMesh subMesh = new SubMesh((ushort)Verts.Count, (ushort)Tris.Count, vertsToReserve, trisToReserve);

            lastVert += vertsToReserve;
            lastTri  += trisToReserve;
            SubMeshes.Add(subMesh);
            for (int i = 0; i < vertsToReserve; i++)
            {
                Verts.Add(Vector3.zero); // this is multiple assignment! BAD!
                Normals.Add(Vector3.up);
                TextureUVs.Add(Vector2.zero);
                SubmaterialUVs.Add(Vector2.zero);
            }
            for (int i = 0; i < trisToReserve; i++)
            {
                Tris.Add(0); // more evil multiple assignment!
            }
            return((ushort)(SubMeshes.Count + SubMeshBaseIdx - 1));
        }
Ejemplo n.º 6
0
        public void BuildModel(RenderResource resource)
        {
            Material materialA = new Material();

            materialA.Visible = true;
            Color colorDiffuseA = Color.FromArgb(unchecked ((int)0xFF0000FF));

            materialA.Diffuse         = colorDiffuseA;
            materialA.TextureFileName = "";
            Materials.Add(materialA);
            Material materialB = new Material();

            materialB.Visible = true;
            Color colorDiffuseB = Color.FromArgb(unchecked ((int)0xFF00FF00));

            materialB.Diffuse         = colorDiffuseB;
            materialB.TextureFileName = "";
            Materials.Add(materialB);

            ModelNode group = new ModelNode();

            group.Name = "group";

            MeshParser meshParser = new MeshParser(ModelName);

            meshParser.BuildMesh(resource);
            foreach (SubMesh subMesh in meshParser.SubMeshes)
            {
                // If the mesh parser knew the total submeshes for the model,
                // then this could be done inside BuildMesh.
                subMesh.MeshIndex = Meshes.Count;
                group.SubMeshIndices.Add(SubMeshes.Count);
                SubMeshes.Add(subMesh);
            }

            Meshes.Add(meshParser.Mesh);
            Groups.Add(group);
            Model = new Model(this);
        }
Ejemplo n.º 7
0
            public void Push(SkinnedMeshRenderer renderer)
            {
                var mesh = renderer.sharedMesh;

                if (mesh == null)
                {
                    Debug.LogWarningFormat("{0} has no mesh", renderer.name);
                    return;
                }

                Renderers.Add(renderer);

                var indexOffset     = Positions.Count;
                var boneIndexOffset = Bones.Count;

                Positions.AddRange(mesh.vertices);
                Normals.AddRange(mesh.normals);
                UV.AddRange(mesh.uv);
                Tangents.AddRange(mesh.tangents);

                if (mesh.vertexCount == mesh.boneWeights.Length)
                {
                    BoneWeights.AddRange(mesh.boneWeights.Select(x => AddBoneIndexOffset(x, boneIndexOffset)).ToArray());
                }
                else
                {
                    BoneWeights.AddRange(Enumerable.Range(0, mesh.vertexCount).Select(x => new BoneWeight()).ToArray());
                }

                BindPoses.AddRange(mesh.bindposes);
                Bones.AddRange(renderer.bones);

                for (int i = 0; i < mesh.subMeshCount; ++i)
                {
                    var indices = mesh.GetIndices(i).Select(x => x + indexOffset);
                    var mat     = renderer.sharedMaterials[i];
                    var sameMaterialSubMeshIndex = SubMeshes.FindIndex(x => ReferenceEquals(x.Material, mat));
                    if (sameMaterialSubMeshIndex >= 0)
                    {
                        SubMeshes[sameMaterialSubMeshIndex].Indices.AddRange(indices);
                    }
                    else
                    {
                        SubMeshes.Add(new SubMesh
                        {
                            Indices  = indices.ToList(),
                            Material = mat,
                        });
                    }
                }

                for (int i = 0; i < mesh.blendShapeCount; ++i)
                {
                    var positions = (Vector3[])mesh.vertices.Clone();
                    var normals   = (Vector3[])mesh.normals.Clone();
                    var tangents  = mesh.tangents.Select(x => (Vector3)x).ToArray();

                    mesh.GetBlendShapeFrameVertices(i, 0, positions, normals, tangents);
                    BlendShapes.Add(new BlendShape
                    {
                        VertexOffset = indexOffset,
                        FrameWeight  = mesh.GetBlendShapeFrameWeight(i, 0),
                        Name         = mesh.GetBlendShapeName(i),
                        Positions    = positions,
                        Normals      = normals,
                        Tangents     = tangents,
                    });
                }
            }
Ejemplo n.º 8
0
        public void Push(MeshRenderer renderer)
        {
            var meshFilter = renderer.GetComponent <MeshFilter>();

            if (meshFilter == null)
            {
                Debug.LogWarningFormat("{0} has no mesh filter", renderer.name);
                return;
            }
            var mesh = meshFilter.sharedMesh;

            if (mesh == null)
            {
                Debug.LogWarningFormat("{0} has no mesh", renderer.name);
                return;
            }
            Result.SourceMeshRenderers.Add(renderer);
            Result.MeshMap.Sources.Add(mesh);

            var indexOffset     = Positions.Count;
            var boneIndexOffset = Bones.Count;

            Positions.AddRange(mesh.vertices
                               .Select(x => renderer.transform.TransformPoint(x))
                               );
            Normals.AddRange(mesh.normals
                             .Select(x => renderer.transform.TransformVector(x))
                             );
            UV.AddRange(mesh.uv);
            Tangents.AddRange(mesh.tangents
                              .Select(t =>
            {
                var v = renderer.transform.TransformVector(t.x, t.y, t.z);
                return(new Vector4(v.x, v.y, v.z, t.w));
            })
                              );

            var self = renderer.transform;
            var bone = self.parent;

            if (bone == null)
            {
                Debug.LogWarningFormat("{0} is root gameobject.", self.name);
                return;
            }
            var bindpose = bone.worldToLocalMatrix;

            BoneWeights.AddRange(Enumerable.Range(0, mesh.vertices.Length)
                                 .Select(x => new BoneWeight()
            {
                boneIndex0 = Bones.Count,
                weight0    = 1,
            })
                                 );

            BindPoses.Add(bindpose);
            Bones.Add(bone);

            for (int i = 0; i < mesh.subMeshCount && i < renderer.sharedMaterials.Length; ++i)
            {
                var indices = mesh.GetIndices(i).Select(x => x + indexOffset);
                var mat     = renderer.sharedMaterials[i];
                var sameMaterialSubMeshIndex = SubMeshes.FindIndex(x => ReferenceEquals(x.Material, mat));
                if (sameMaterialSubMeshIndex >= 0)
                {
                    SubMeshes[sameMaterialSubMeshIndex].Indices.AddRange(indices);
                }
                else
                {
                    SubMeshes.Add(new SubMesh
                    {
                        Indices  = indices.ToList(),
                        Material = mat,
                    });
                }
            }
        }
Ejemplo n.º 9
0
        public void BuildMesh(RenderResource resource)
        {
            float v = 1.2f;
            float h = 1.0f;

            BasicVertex[] vertices =
            {
                new BasicVertex {
                    X = 0, Y = v, Z = 0
                },
                new BasicVertex {
                    X = -h, Y = 0, Z = h
                },
                new BasicVertex {
                    X = -h, Y = 0, Z = -h
                },
                new BasicVertex {
                    X = h, Y = 0, Z = -h
                },
                new BasicVertex {
                    X = h, Y = 0, Z = h
                },
                new BasicVertex {
                    X = 0, Y = -v, Z = 0
                }
            };

            _vertexList.AddRange(vertices);

            int[] indices =
            {
                0, 1, 2,
                5, 3, 2,
                0, 3, 4,
                5, 1, 4,
                5, 2, 1,
                0, 2, 3,
                5, 4, 3,
                0, 4, 1
            };

            _indexList.AddRange(indices);

            Technique = "DefaultRender";

            Mesh = new MeshPCT(resource, this);

            SubMesh subMeshA = new SubMesh
            {
                Name               = MeshName + "-0",
                MaterialIndex      = 0,
                indexCount         = 12,
                startIndexLocation = 0,
                baseVertexLocation = 0
            };

            SubMeshes.Add(subMeshA);

            SubMesh subMeshB = new SubMesh
            {
                Name               = MeshName + "-1",
                MaterialIndex      = 1,
                indexCount         = 12,
                startIndexLocation = 12,
                baseVertexLocation = 0
            };

            SubMeshes.Add(subMeshB);
        }
Ejemplo n.º 10
0
        internal void Read(EndianBinaryReader reader, ObjectSection section = null)
        {
            reader.SeekCurrent(4);   // Unused flags

            BoundingSphere = reader.ReadBoundingSphere();

            int  subMeshCount    = reader.ReadInt32();
            long subMeshesOffset = reader.ReadOffset();

            var vertexFormat     = ( VertexFormatAttributes )reader.ReadUInt32();
            int vertexSize       = reader.ReadInt32();
            int vertexCount      = reader.ReadInt32();
            var attributeOffsets = reader.ReadOffsets(20);

            Flags = ( MeshFlags )reader.ReadInt32();

            uint attributeFlags = reader.ReadUInt32();

            reader.SkipNulls(6 * sizeof(uint));

            Name = reader.ReadString(StringBinaryFormat.FixedLength, 64);

            reader.ReadAtOffset(subMeshesOffset, () =>
            {
                SubMeshes.Capacity = subMeshCount;

                for (int i = 0; i < subMeshCount; i++)
                {
                    var subMesh = new SubMesh();
                    subMesh.Read(reader, section);
                    SubMeshes.Add(subMesh);
                }
            });

            // Modern Format
            if ((vertexFormat & VertexFormatAttributes.UsesModernStorage) != 0)
            {
                ReadVertexAttributesModern();
            }

            else
            {
                ReadVertexAttributesClassic();
            }

            void ReadVertexAttributesClassic()
            {
                Vector4[] boneWeights = null;
                Vector4[] boneIndices = null;

                for (int i = 0; i < attributeOffsets.Length; i++)
                {
                    var attribute = ( VertexFormatAttributes )(1 << i);

                    reader.ReadAtOffsetIf((vertexFormat & attribute) != 0, attributeOffsets[i], () =>
                    {
                        switch (attribute)
                        {
                        case VertexFormatAttributes.Position:
                            Positions = reader.ReadVector3s(vertexCount);
                            break;

                        case VertexFormatAttributes.Normal:
                            Normals = reader.ReadVector3s(vertexCount);
                            break;

                        case VertexFormatAttributes.Tangent:
                            Tangents = reader.ReadVector4s(vertexCount);
                            break;

                        case VertexFormatAttributes.TexCoord0:
                            TexCoords0 = reader.ReadVector2s(vertexCount);
                            break;

                        case VertexFormatAttributes.TexCoord1:
                            TexCoords1 = reader.ReadVector2s(vertexCount);
                            break;

                        case VertexFormatAttributes.TexCoord2:
                            TexCoords2 = reader.ReadVector2s(vertexCount);
                            break;

                        case VertexFormatAttributes.TexCoord3:
                            TexCoords3 = reader.ReadVector2s(vertexCount);
                            break;

                        case VertexFormatAttributes.Color0:
                            Colors0 = reader.ReadColors(vertexCount);
                            break;

                        case VertexFormatAttributes.Color1:
                            Colors1 = reader.ReadColors(vertexCount);
                            break;

                        case VertexFormatAttributes.BoneWeight:
                            boneWeights = reader.ReadVector4s(vertexCount);
                            break;

                        case VertexFormatAttributes.BoneIndex:
                            boneIndices = reader.ReadVector4s(vertexCount);
                            break;

                        default:
                            Console.WriteLine("Unhandled vertex format element: {0}", attribute);
                            break;
                        }
                    });
                }

                if (boneWeights == null || boneIndices == null)
                {
                    return;
                }

                BoneWeights = new BoneWeight[vertexCount];

                for (int i = 0; i < vertexCount; i++)
                {
                    var weight4 = boneWeights[i];
                    var index4  = Vector4.Divide(boneIndices[i], 3);

                    var boneWeight = new BoneWeight
                    {
                        Weight1 = weight4.X,
                        Weight2 = weight4.Y,
                        Weight3 = weight4.Z,
                        Weight4 = weight4.W,
                        Index1  = ( int )index4.X,
                        Index2  = ( int )index4.Y,
                        Index3  = ( int )index4.Z,
                        Index4  = ( int )index4.W
                    };

                    boneWeight.Validate();

                    BoneWeights[i] = boneWeight;
                }
            }

            void ReadVertexAttributesModern()
            {
                Positions  = new Vector3[vertexCount];
                Normals    = new Vector3[vertexCount];
                Tangents   = new Vector4[vertexCount];
                TexCoords0 = new Vector2[vertexCount];
                TexCoords1 = new Vector2[vertexCount];

                if (attributeFlags == 10)
                {
                    TexCoords2 = new Vector2[vertexCount];
                    TexCoords3 = new Vector2[vertexCount];
                }
                else if (attributeFlags == 6)
                {
                    TexCoords2 = new Vector2[vertexCount];
                }

                Colors0 = new Color[vertexCount];

                if (attributeFlags == 4)
                {
                    BoneWeights = new BoneWeight[vertexCount];
                }

                bool hasTangents = false;

                EndianBinaryReader vertexReader;
                long baseOffset;

                if (section != null)
                {
                    vertexReader = section.VertexData.Reader;
                    baseOffset   = section.VertexData.DataOffset;
                }
                else
                {
                    vertexReader = reader;
                    baseOffset   = reader.BaseOffset;
                }

                long current = reader.Position;

                for (int i = 0; i < vertexCount; i++)
                {
                    vertexReader.SeekBegin(baseOffset + attributeOffsets[13] + vertexSize * i);

                    Positions[i] = vertexReader.ReadVector3();
                    Normals[i]   = vertexReader.ReadVector3(VectorBinaryFormat.Int16);
                    vertexReader.SeekCurrent(2);
                    Tangents[i]   = vertexReader.ReadVector4(VectorBinaryFormat.Int16);
                    TexCoords0[i] = vertexReader.ReadVector2(VectorBinaryFormat.Half);
                    TexCoords1[i] = vertexReader.ReadVector2(VectorBinaryFormat.Half);

                    if (attributeFlags == 10)
                    {
                        TexCoords2[i] = vertexReader.ReadVector2(VectorBinaryFormat.Half);
                        TexCoords3[i] = vertexReader.ReadVector2(VectorBinaryFormat.Half);
                    }
                    else if (attributeFlags == 6)
                    {
                        TexCoords2[i] = vertexReader.ReadVector2(VectorBinaryFormat.Half);
                    }

                    Colors0[i] = vertexReader.ReadColor(VectorBinaryFormat.Half);

                    if (attributeFlags == 4)
                    {
                        var boneWeight = new BoneWeight
                        {
                            Weight1 = vertexReader.ReadUInt16() / 32767f,
                            Weight2 = vertexReader.ReadUInt16() / 32767f,
                            Weight3 = vertexReader.ReadUInt16() / 32767f,
                            Weight4 = vertexReader.ReadUInt16() / 32767f,
                            Index1  = vertexReader.ReadByte() / 3,
                            Index2  = vertexReader.ReadByte() / 3,
                            Index3  = vertexReader.ReadByte() / 3,
                            Index4  = vertexReader.ReadByte() / 3
                        };

                        boneWeight.Validate();

                        BoneWeights[i] = boneWeight;
                    }

                    // Normalize normal because precision
                    Normals[i] = Vector3.Normalize(Normals[i]);

                    // Checks to get rid of useless data after reading
                    if (Tangents[i] != Vector4.Zero)
                    {
                        hasTangents = true;
                    }
                }

                if (!hasTangents)
                {
                    Tangents = null;
                }

                reader.SeekBegin(current);
            }

            if (Tangents == null)
            {
                return;
            }

            for (int i = 0; i < Tangents.Length; i++)
            {
                int direction = Math.Sign(Tangents[i].W);
                var tangent   = Vector3.Normalize(new Vector3(Tangents[i].X, Tangents[i].Y, Tangents[i].Z));

                Tangents[i] = new Vector4(tangent, direction);
            }
        }
Ejemplo n.º 11
0
        internal void Read(EndianBinaryReader reader, ObjectSection section = null)
        {
            reader.SeekCurrent(4);
            BoundingSphere = reader.ReadBoundingSphere();
            int  subMeshCount    = reader.ReadInt32();
            long subMeshesOffset = reader.ReadOffset();
            var  attributes      = ( VertexFormatAttribute )reader.ReadUInt32();
            int  stride          = reader.ReadInt32();
            int  vertexCount     = reader.ReadInt32();
            var  elemItems       = reader.ReadUInt32s(section?.Format == BinaryFormat.X ? 49 : 28);

            Name = reader.ReadString(StringBinaryFormat.FixedLength, 64);

            SubMeshes.Capacity = subMeshCount;
            for (int i = 0; i < subMeshCount; i++)
            {
                reader.ReadAtOffset(subMeshesOffset + i * SubMesh.GetByteSize(section?.Format ?? BinaryFormat.DT),
                                    () =>
                {
                    var subMesh = new SubMesh();
                    subMesh.Read(reader, section);
                    SubMeshes.Add(subMesh);
                });
            }

            // Modern Format
            if (section != null)
            {
                ReadVertexAttributesModern();
            }
            else
            {
                ReadVertexAttributesClassic();
            }

            void ReadVertexAttributesClassic()
            {
                Vector4[] boneWeights = null;
                Vector4[] boneIndices = null;

                for (int i = 0; i < 28; i++)
                {
                    var attribute = ( VertexFormatAttribute )(1 << i);

                    reader.ReadAtOffsetIf((attributes & attribute) != 0, elemItems[i], () =>
                    {
                        switch (attribute)
                        {
                        case VertexFormatAttribute.Vertex:
                            Vertices = reader.ReadVector3s(vertexCount);
                            break;

                        case VertexFormatAttribute.Normal:
                            Normals = reader.ReadVector3s(vertexCount);
                            break;

                        case VertexFormatAttribute.Tangent:
                            Tangents = reader.ReadVector4s(vertexCount);
                            break;

                        case VertexFormatAttribute.UVChannel1:
                            UVChannel1 = reader.ReadVector2s(vertexCount);
                            break;

                        case VertexFormatAttribute.UVChannel2:
                            UVChannel2 = reader.ReadVector2s(vertexCount);
                            break;

                        case VertexFormatAttribute.Color:
                            Colors = reader.ReadColors(vertexCount);
                            break;

                        case VertexFormatAttribute.BoneWeight:
                            boneWeights = reader.ReadVector4s(vertexCount);
                            break;

                        case VertexFormatAttribute.BoneIndex:
                            boneIndices = reader.ReadVector4s(vertexCount);
                            break;

                        default:
                            Console.WriteLine("Unhandled vertex format element: {0}", attribute);
                            break;
                        }
                    });
                }

                if (boneWeights != null && boneIndices != null)
                {
                    BoneWeights = new BoneWeight[vertexCount];
                    for (int i = 0; i < vertexCount; i++)
                    {
                        var weight4 = boneWeights[i];
                        var index4  = Vector4.Divide(boneIndices[i], 3);

                        var boneWeight = new BoneWeight
                        {
                            Weight1 = weight4.X,
                            Weight2 = weight4.Y,
                            Weight3 = weight4.Z,
                            Weight4 = weight4.W,
                            Index1  = ( int )index4.X,
                            Index2  = ( int )index4.Y,
                            Index3  = ( int )index4.Z,
                            Index4  = ( int )index4.W
                        };
                        boneWeight.Validate();

                        BoneWeights[i] = boneWeight;
                    }
                }
            }

            void ReadVertexAttributesModern()
            {
                uint dataOffset     = elemItems[section.Format == BinaryFormat.X ? 27 : 13];
                uint attributeFlags = elemItems[section.Format == BinaryFormat.X ? 42 : 21];

                if (attributeFlags == 2 || attributeFlags == 4)
                {
                    Vertices   = new Vector3[vertexCount];
                    Normals    = new Vector3[vertexCount];
                    Tangents   = new Vector4[vertexCount];
                    UVChannel1 = new Vector2[vertexCount];
                    UVChannel2 = new Vector2[vertexCount];
                    Colors     = new Color[vertexCount];

                    if (attributeFlags == 4)
                    {
                        BoneWeights = new BoneWeight[vertexCount];
                    }

                    bool hasTangents   = false;
                    bool hasUVChannel2 = false;
                    bool hasColors     = false;

                    var vertexReader = section.VertexData.Reader;
                    for (int i = 0; i < vertexCount; i++)
                    {
                        vertexReader.SeekBegin(section.VertexData.DataOffset + dataOffset + stride * i);
                        Vertices[i] = vertexReader.ReadVector3();
                        Normals[i]  = vertexReader.ReadVector3(VectorBinaryFormat.Int16);
                        vertexReader.SeekCurrent(2);
                        Tangents[i]   = vertexReader.ReadVector4(VectorBinaryFormat.Int16);
                        UVChannel1[i] = vertexReader.ReadVector2(VectorBinaryFormat.Half);
                        UVChannel2[i] = vertexReader.ReadVector2(VectorBinaryFormat.Half);
                        Colors[i]     = vertexReader.ReadColor(VectorBinaryFormat.Half);

                        if (attributeFlags == 4)
                        {
                            var boneWeight = new BoneWeight
                            {
                                Weight1 = vertexReader.ReadUInt16() / 32767f,
                                Weight2 = vertexReader.ReadUInt16() / 32767f,
                                Weight3 = vertexReader.ReadUInt16() / 32767f,
                                Weight4 = vertexReader.ReadUInt16() / 32767f,
                                Index1  = vertexReader.ReadByte() / 3,
                                Index2  = vertexReader.ReadByte() / 3,
                                Index3  = vertexReader.ReadByte() / 3,
                                Index4  = vertexReader.ReadByte() / 3
                            };
                            boneWeight.Validate();

                            BoneWeights[i] = boneWeight;
                        }

                        // Normalize normal because precision
                        Normals[i] = Vector3.Normalize(Normals[i]);

                        // Checks to get rid of useless data after reading
                        if (Tangents[i] != Vector4.Zero)
                        {
                            hasTangents = true;
                        }
                        if (UVChannel1[i] != UVChannel2[i])
                        {
                            hasUVChannel2 = true;
                        }
                        if (!Colors[i].Equals(Color.White))
                        {
                            hasColors = true;
                        }
                    }

                    if (!hasTangents)
                    {
                        Tangents = null;
                    }
                    if (!hasUVChannel2)
                    {
                        UVChannel2 = null;
                    }
                    if (!hasColors)
                    {
                        Colors = null;
                    }
                }

                if (Tangents != null)
                {
                    for (int i = 0; i < Tangents.Length; i++)
                    {
                        int direction = Math.Sign(Tangents[i].W);
                        var tangent   =
                            Vector3.Normalize(new Vector3(Tangents[i].X, Tangents[i].Y, Tangents[i].Z));

                        Tangents[i] = new Vector4(tangent, direction);
                    }
                }
            }
        }
        /// <summary>
        /// Returns the submesh for this face based on material info.
        /// </summary>
        /// <param name='f'>The face to find a submesh for.</param>
        public KeyValuePair <IntermediateMaterial, List <int> > FindOrCreateSubMesh(Face f)
        {
            ExternalReference externalRef = null;

            if (Header.Parent != null)
            {
                externalRef = Header.Parent as ExternalReference;
            }

            // Fetch palettes
            MaterialPalette mp = null;

            if (f.MaterialIndex != -1)
            {
                if (externalRef != null)
                {
                    externalRef.Header.MaterialPalettes.TryGetValue(f.MaterialIndex, out mp);
                }

                if (mp == null)
                {
                    Header.MaterialPalettes.TryGetValue(f.MaterialIndex, out mp);
                }

                if (mp == null)
                {
                    Log.WriteError("Could not find material palette: " + f.MaterialIndex);
                }
            }

            TexturePalette mainTex = null;

            if (f.TexturePattern != -1)
            {
                if (externalRef != null)
                {
                    externalRef.Header.TexturePalettes.TryGetValue(f.TexturePattern, out mainTex);
                }

                if (mainTex == null)
                {
                    Header.TexturePalettes.TryGetValue(f.TexturePattern, out mainTex);
                }

                if (mainTex == null)
                {
                    Log.WriteError("Could not find texture pattern: " + f.TexturePattern);
                }
            }

            TexturePalette detailTex = null;

            if (f.DetailTexturePattern != -1)
            {
                if (externalRef != null)
                {
                    externalRef.Header.TexturePalettes.TryGetValue(f.DetailTexturePattern, out detailTex);
                }

                if (mainTex == null)
                {
                    Header.TexturePalettes.TryGetValue(f.DetailTexturePattern, out detailTex);
                }

                if (mainTex == null)
                {
                    Log.WriteError("Could not find detail texture pattern: " + f.DetailTexturePattern);
                }
            }

            // Check locally
            foreach (KeyValuePair <IntermediateMaterial, List <int> > mesh in SubMeshes)
            {
                if (mesh.Key.Equals(mp, mainTex, detailTex, f.Transparency, f.LightMode))
                {
                    return(mesh);
                }
            }

            // Create a new submesh
            IntermediateMaterial im = Header.MaterialBank.FindOrCreateMaterial(f);
            KeyValuePair <IntermediateMaterial, List <int> > newMesh = new KeyValuePair <IntermediateMaterial, List <int> >(im, new List <int>());

            SubMeshes.Add(newMesh);
            return(newMesh);
        }