示例#1
0
        internal void Read(EndianBinaryReader reader, MeshSection section = null)
        {
            reader.SeekCurrent(4);
            BoundingSphere = reader.ReadBoundingSphere();
            int  indexTableCount   = reader.ReadInt32();
            long indexTablesOffset = 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);

            IndexTables.Capacity = indexTableCount;
            for (int i = 0; i < indexTableCount; i++)
            {
                reader.ReadAtOffset(indexTablesOffset + (i * IndexTable.GetByteSize(section?.Format ?? BinaryFormat.DT)), () =>
                {
                    var indexTable = new IndexTable();
                    indexTable.Read(reader, section);
                    IndexTables.Add(indexTable);
                });
            }

            // 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++)
                    {
                        // So apparently, FT uses -1 instead of 255 for weights that aren't used,
                        // and index tables can use bones more than 85 (85*3=255)
                        // For that reason, weight == 255 check won't and shouldn't be done anymore.

                        Vector4 weight4 = boneWeights[i];
                        Vector4 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() / 32768f,
                                Weight2 = vertexReader.ReadUInt16() / 32768f,
                                Weight3 = vertexReader.ReadUInt16() / 32768f,
                                Weight4 = vertexReader.ReadUInt16() / 32768f,
                                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++)
                    {
                        float   direction = Tangents[i].W < 0.0f ? -1.0f : 1.0f;
                        Vector3 tangent   = Vector3.Normalize(new Vector3(Tangents[i].X, Tangents[i].Y, Tangents[i].Z));

                        Tangents[i] = new Vector4(tangent, direction);
                    }
                }
            }
        }
示例#2
0
        private static SubMesh ConvertSubMeshFromAiNode(Ai.Node aiNode, Ai.Scene aiScene, Matrix4x4 parentTransformation, Dictionary <string, int> boneMap, List <Bone> bones, Dictionary <string, int> materialMap, List <Material> materials, string texturesDirectory, TextureSet textureSet)
        {
            if (!aiNode.HasMeshes)
            {
                return(null);
            }

            // Select meshes that have triangles
            var aiMeshes = aiNode.MeshIndices.Select(x => aiScene.Meshes[x]).Where(x =>
                                                                                   x.PrimitiveType == Ai.PrimitiveType.Triangle && x.Faces.Any(y => y.IndexCount == 3)).ToList();

            if (aiMeshes.Count == 0)
            {
                return(null);
            }

            var transformation = parentTransformation * GetMatrix4x4FromAiMatrix4x4(aiNode.Transform);
            int vertexCount    = aiMeshes.Sum(x => x.VertexCount);

            var subMesh = new SubMesh
            {
                Name     = aiNode.Name,
                Vertices = new Vector3[vertexCount],
            };

            int vertexOffset = 0;

            foreach (var aiMesh in aiMeshes)
            {
                for (int i = 0; i < aiMesh.Vertices.Count; i++)
                {
                    subMesh.Vertices[vertexOffset + i] = Vector3.Transform(new Vector3(aiMesh.Vertices[i].X, aiMesh.Vertices[i].Y, aiMesh.Vertices[i].Z), transformation);
                }

                if (aiMesh.HasNormals)
                {
                    if (subMesh.Normals == null)
                    {
                        subMesh.Normals = new Vector3[vertexCount];
                    }

                    for (int i = 0; i < aiMesh.Normals.Count; i++)
                    {
                        subMesh.Normals[vertexOffset + i] = Vector3.Normalize(Vector3.TransformNormal(new Vector3(aiMesh.Normals[i].X, aiMesh.Normals[i].Y, aiMesh.Normals[i].Z), transformation));
                    }
                }

                if (aiMesh.HasTangentBasis)
                {
                    if (subMesh.Tangents == null)
                    {
                        subMesh.Tangents = new Vector4[vertexCount];
                    }

                    for (int i = 0; i < aiMesh.Tangents.Count; i++)
                    {
                        Vector3 tangent   = Vector3.TransformNormal(new Vector3(aiMesh.Tangents[i].X, aiMesh.Tangents[i].Y, aiMesh.Tangents[i].Z), transformation);
                        Vector3 bitangent = Vector3.TransformNormal(new Vector3(aiMesh.BiTangents[i].X, aiMesh.BiTangents[i].Y, aiMesh.BiTangents[i].Z), transformation);
                        float   direction = Vector3.Dot(bitangent, Vector3.Cross(subMesh.Normals[vertexOffset + i], tangent)) > 0 ? 1.0f : -1.0f;

                        subMesh.Tangents[vertexOffset + i] = new Vector4(tangent, direction);
                    }
                }

                if (aiMesh.HasTextureCoords(0))
                {
                    if (subMesh.UVChannel1 == null)
                    {
                        subMesh.UVChannel1 = new Vector2[vertexCount];
                    }

                    for (int i = 0; i < aiMesh.TextureCoordinateChannels[0].Count; i++)
                    {
                        subMesh.UVChannel1[vertexOffset + i] = ClampTextureCoordinates(new Vector2(aiMesh.TextureCoordinateChannels[0][i].X, 1f - aiMesh.TextureCoordinateChannels[0][i].Y));
                    }
                }

                if (aiMesh.HasTextureCoords(1))
                {
                    if (subMesh.UVChannel2 == null)
                    {
                        subMesh.UVChannel2 = new Vector2[vertexCount];
                    }

                    for (int i = 0; i < aiMesh.TextureCoordinateChannels[1].Count; i++)
                    {
                        subMesh.UVChannel2[vertexOffset + i] = ClampTextureCoordinates(new Vector2(aiMesh.TextureCoordinateChannels[1][i].X, 1f - aiMesh.TextureCoordinateChannels[1][i].Y));
                    }
                }

                if (aiMesh.HasVertexColors(0))
                {
                    if (subMesh.Colors == null)
                    {
                        subMesh.Colors = Enumerable.Repeat(Color.White, vertexCount).ToArray();
                    }

                    for (int i = 0; i < aiMesh.VertexColorChannels[0].Count; i++)
                    {
                        subMesh.Colors[vertexOffset + i] = new Color(aiMesh.VertexColorChannels[0][i].R, aiMesh.VertexColorChannels[0][i].G, aiMesh.VertexColorChannels[0][i].B, aiMesh.VertexColorChannels[0][i].A);
                    }
                }

                var indexTable = new IndexTable();

                if (aiMesh.HasBones)
                {
                    if (subMesh.BoneWeights == null)
                    {
                        subMesh.BoneWeights = Enumerable.Repeat(BoneWeight.Empty, vertexCount).ToArray();
                    }

                    indexTable.BoneIndices = new ushort[aiMesh.Bones.Count];
                    for (int i = 0; i < aiMesh.Bones.Count; i++)
                    {
                        var aiBone = aiMesh.Bones[i];

                        if (!boneMap.TryGetValue(aiBone.Name, out int boneIndex))
                        {
                            boneIndex            = bones.Count;
                            boneMap[aiBone.Name] = boneIndex;
                            bones.Add(ConvertBoneFromAiBone(aiBone, aiScene, boneIndex));
                        }

                        indexTable.BoneIndices[i] = ( ushort )boneIndex;

                        foreach (var aiWeight in aiBone.VertexWeights)
                        {
                            subMesh.BoneWeights[vertexOffset + aiWeight.VertexID].AddWeight(i, aiWeight.Weight);
                        }
                    }
                }

                indexTable.Indices = aiMesh.Faces.Where(x => x.IndexCount == 3).SelectMany(x => x.Indices).Select(x => ( ushort )(vertexOffset + x)).ToArray();

                ushort[] triangleStrip = TriangleStripUtilities.GenerateStrips(indexTable.Indices);
                if (triangleStrip != null)
                {
                    indexTable.PrimitiveType = IndexTablePrimitiveType.TriangleStrip;
                    indexTable.Indices       = triangleStrip;
                }

                var aiMaterial = aiScene.Materials[aiMesh.MaterialIndex];
                if (!materialMap.TryGetValue(aiMaterial.Name, out int materialIndex))
                {
                    materialIndex = materials.Count;
                    materialMap[aiMaterial.Name] = materialIndex;
                    materials.Add(ConvertMaterialFromAiMaterial(aiMaterial, texturesDirectory, textureSet));
                }

                indexTable.MaterialIndex = materialIndex;

                var axisAlignedBoundingBox = new AxisAlignedBoundingBox(subMesh.Vertices.Skip(vertexOffset).Take(aiMesh.Vertices.Count));

                indexTable.BoundingSphere = axisAlignedBoundingBox.ToBoundingSphere();
                indexTable.BoundingBox    = axisAlignedBoundingBox.ToBoundingBox();

                subMesh.IndexTables.Add(indexTable);

                vertexOffset += aiMesh.VertexCount;
            }

            subMesh.BoundingSphere = new AxisAlignedBoundingBox(subMesh.Vertices).ToBoundingSphere();

            return(subMesh);
        }