Exemple #1
0
        private RenderGeometryCompression CompressVertexBuffer(VertexBufferDefinition vertexBuffer)
        {
            Debug.Assert(vertexBuffer.Format == VertexBufferFormat.Rigid);

            var compression = new RenderGeometryCompression();

            var rigidVertices = new List <RigidVertex>();

            using (var stream = new MemoryStream(vertexBuffer.Data.Data))
            {
                var vertexStream = VertexStreamFactory.Create(DestCache.Version, stream);
                for (int i = 0; i < vertexBuffer.Count; i++)
                {
                    var vertex = vertexStream.ReadRigidVertex();
                    rigidVertices.Add(vertex);
                }
            }

            var positions = rigidVertices.Select(v => v.Position);
            var texCoords = rigidVertices.Select(v => v.Texcoord);

            if (positions != null && positions.Count() > 0)
            {
                compression.X.Lower = Math.Min(compression.X.Lower, positions.Min(v => v.I));
                compression.Y.Lower = Math.Min(compression.Y.Lower, positions.Min(v => v.J));
                compression.Z.Lower = Math.Min(compression.Z.Lower, positions.Min(v => v.K));
                compression.X.Upper = Math.Max(compression.X.Upper, positions.Max(v => v.I));
                compression.Y.Upper = Math.Max(compression.Y.Upper, positions.Max(v => v.J));
                compression.Z.Upper = Math.Max(compression.Z.Upper, positions.Max(v => v.K));
            }
            if (texCoords != null && texCoords.Count() > 0)
            {
                compression.U.Lower = Math.Min(compression.U.Lower, texCoords.Min(v => v.I));
                compression.V.Lower = Math.Min(compression.V.Lower, texCoords.Min(v => v.J));
                compression.U.Upper = Math.Max(compression.U.Upper, texCoords.Max(v => v.I));
                compression.V.Upper = Math.Max(compression.V.Upper, texCoords.Max(v => v.J));
            }

            var compressor = new VertexCompressor(compression);

            using (var outStream = new MemoryStream())
            {
                var outVertexStream = VertexStreamFactory.Create(DestCache.Version, outStream);
                foreach (var vertex in rigidVertices)
                {
                    vertex.Position = compressor.CompressPosition(vertex.Position);
                    vertex.Texcoord = compressor.CompressUv(vertex.Texcoord);
                    outVertexStream.WriteRigidVertex(vertex);
                }

                vertexBuffer.Data.Data = outStream.ToArray();
            }

            return(compression);
        }
Exemple #2
0
        private static List <BMFVertexLighting> ConvertLightingVertices(VertexBufferDefinition vertexBuffer, CacheVersion version, int vertexCount)
        {
            var vertices = new List <BMFVertexLighting>();

            using (var vertexDataStream = new MemoryStream(vertexBuffer.Data.Data))
            {
                var vertexStream = VertexStreamFactory.Create(version, vertexDataStream);

                for (int j = 0; j < vertexCount; j++)
                {
                    var vertex = new BMFVertexLighting();

                    switch (vertexBuffer.Format)
                    {
                    case VertexBufferFormat.AmbientPrt:
                        var ambientPrt = vertexStream.ReadAmbientPrtData();
                        vertex.SHOrder            = 0;
                        vertex.PRTCoefficients[0] = ambientPrt.SHCoefficient;
                        break;

                    case VertexBufferFormat.LinearPrt:
                        var linearPrt = vertexStream.ReadLinearPrtData();
                        vertex.SHOrder            = 1;
                        vertex.PRTCoefficients[0] = linearPrt.SHCoefficients.I;
                        vertex.PRTCoefficients[1] = linearPrt.SHCoefficients.J;
                        vertex.PRTCoefficients[2] = linearPrt.SHCoefficients.K;
                        vertex.PRTCoefficients[3] = linearPrt.SHCoefficients.W;
                        break;

                    case VertexBufferFormat.QuadraticPrt:
                        var quadPrt = vertexStream.ReadQuadraticPrtData();
                        vertex.SHOrder            = 2;
                        vertex.PRTCoefficients[0] = quadPrt.SHCoefficients1.I;
                        vertex.PRTCoefficients[1] = quadPrt.SHCoefficients1.J;
                        vertex.PRTCoefficients[2] = quadPrt.SHCoefficients1.K;
                        vertex.PRTCoefficients[3] = quadPrt.SHCoefficients2.I;
                        vertex.PRTCoefficients[4] = quadPrt.SHCoefficients2.J;
                        vertex.PRTCoefficients[5] = quadPrt.SHCoefficients2.K;
                        vertex.PRTCoefficients[6] = quadPrt.SHCoefficients3.I;
                        vertex.PRTCoefficients[7] = quadPrt.SHCoefficients3.J;
                        vertex.PRTCoefficients[8] = quadPrt.SHCoefficients3.K;
                        break;

                    default:
                        throw new InvalidOperationException("Unsupported vertex buffer type: " + vertexBuffer.Format);
                    }
                    vertices.Add(vertex);
                }

                return(vertices);
            }
        }
Exemple #3
0
        public static GeometryReference ConvertGeometry(GeometryReference geometry, OpenTagCache srcInfo, ResourceDataManager srcResources, OpenTagCache destInfo, ResourceDataManager destResources)
        {
            if (geometry == null || geometry.Resource == null || geometry.Resource.Index < 0)
            {
                return(geometry);
            }

            // The format changed starting with version 1.235640, so if both versions are on the same side then they can be converted normally
            var srcCompare  = Definition.Compare(srcInfo.Version, DefinitionSet.HaloOnline235640);
            var destCompare = Definition.Compare(destInfo.Version, DefinitionSet.HaloOnline235640);

            if ((srcCompare < 0 && destCompare < 0) || (srcCompare >= 0 && destCompare >= 0))
            {
                geometry.Resource = ConvertResource(geometry.Resource, srcInfo, srcResources, destInfo, destResources);
                return(geometry);
            }

            Console.WriteLine("- Rebuilding geometry resource {0} in {1}...", geometry.Resource.Index, geometry.Resource.GetLocation());
            using (MemoryStream inStream = new MemoryStream(), outStream = new MemoryStream())
            {
                // First extract the model data
                srcResources.Extract(geometry.Resource, inStream);

                // Now open source and destination vertex streams
                inStream.Position = 0;
                var inVertexStream  = VertexStreamFactory.Create(srcInfo.Version, inStream);
                var outVertexStream = VertexStreamFactory.Create(destInfo.Version, outStream);

                // Deserialize the definition data
                var resourceContext = new ResourceSerializationContext(geometry.Resource);
                var definition      = srcInfo.Deserializer.Deserialize <RenderGeometryResourceDefinition>(resourceContext);

                // Convert each vertex buffer
                foreach (var buffer in definition.VertexBuffers)
                {
                    ConvertVertexBuffer(buffer.Definition, inStream, inVertexStream, outStream, outVertexStream);
                }

                // Copy each index buffer over
                foreach (var buffer in definition.IndexBuffers)
                {
                    if (buffer.Definition.Data.Size == 0)
                    {
                        continue;
                    }
                    inStream.Position = buffer.Definition.Data.Address.Offset;
                    buffer.Definition.Data.Address = new ResourceAddress(ResourceAddressType.Resource, (int)outStream.Position);
                    var bufferData = new byte[buffer.Definition.Data.Size];
                    inStream.Read(bufferData, 0, bufferData.Length);
                    outStream.Write(bufferData, 0, bufferData.Length);
                    StreamUtil.Align(outStream, 4);
                }

                // Update the definition data
                destInfo.Serializer.Serialize(resourceContext, definition);

                // Now inject the new resource data
                var newLocation = FixResourceLocation(geometry.Resource.GetLocation(), srcInfo.Version, destInfo.Version);
                outStream.Position = 0;
                destResources.Add(geometry.Resource, newLocation, outStream);
            }
            return(geometry);
        }
Exemple #4
0
        public override bool Execute(List <string> args)
        {
            if (args.Count != 2)
            {
                return(false);
            }

            var destDir = new DirectoryInfo(args[1]);

            if (!destDir.Exists)
            {
                WriteLine($"Destination cache directory does not exist: {destDir.FullName}");
                return(false);
            }

            var destTagsFile = new FileInfo(Combine(destDir.FullName, "tags.dat"));

            if (!destTagsFile.Exists)
            {
                WriteLine($"Destination tag cache file does not exist: {destTagsFile.FullName}");
                return(false);
            }

            var destStringIDsFile = new FileInfo(Combine(destDir.FullName, "string_ids.dat"));

            if (!destStringIDsFile.Exists)
            {
                WriteLine($"Destination string id cache file does not exist: {destStringIDsFile.FullName}");
                return(false);
            }

            var destResourcesFile = new FileInfo(Combine(destDir.FullName, "resources.dat"));

            if (!destResourcesFile.Exists)
            {
                WriteLine($"Destination resource cache file does not exist: {destResourcesFile.FullName}");
                return(false);
            }

            var destTexturesFile = new FileInfo(Combine(destDir.FullName, "textures.dat"));

            if (!destTexturesFile.Exists)
            {
                WriteLine($"Destination texture cache file does not exist: {destTexturesFile.FullName}");
                return(false);
            }

            var destTexturesBFile = new FileInfo(Combine(destDir.FullName, "textures_b.dat"));

            if (!destTexturesBFile.Exists)
            {
                WriteLine($"Destination texture cache file does not exist: {destTexturesBFile.FullName}");
                return(false);
            }

            var destAudioFile = new FileInfo(Combine(destDir.FullName, "audio.dat"));

            if (!destAudioFile.Exists)
            {
                WriteLine($"Destination audio cache file does not exist: {destAudioFile.FullName}");
                return(false);
            }

            TagCache destTagCache;

            using (var stream = destTagsFile.OpenRead())
                destTagCache = new TagCache(stream);

            DefinitionSet guessedVersion;
            var           destVersion = Detect(destTagCache, out guessedVersion);

            if (destVersion == Unknown)
            {
                WriteLine($"Unrecognized target version! (guessed {GetVersionString(guessedVersion)})");
                return(true);
            }

            WriteLine($"Destination cache version: {GetVersionString(destVersion)}");

            StringIDCache destStringIDCache;

            using (var stream = destStringIDsFile.OpenRead())
                destStringIDCache = new StringIDCache(stream, Create(destVersion));

            var destResources = new ResourceDataManager();

            destResources.LoadCachesFromDirectory(destDir.FullName);

            var srcResources = new ResourceDataManager();

            srcResources.LoadCachesFromDirectory(Info.CacheFile.DirectoryName);

            var destSerializer   = new TagSerializer(destVersion);
            var destDeserializer = new TagDeserializer(destVersion);

            var destInfo = new OpenTagCache
            {
                Cache         = destTagCache,
                CacheFile     = destTagsFile,
                StringIDs     = destStringIDCache,
                StringIDsFile = destStringIDsFile,
                Version       = destVersion,
                Serializer    = destSerializer,
                Deserializer  = destDeserializer
            };

            var destTag = ParseTagIndex(destInfo, args[0]);

            if (destTag == null || !destTag.IsInGroup(new Tag("mode")))
            {
                WriteLine("Destination tag must be of group 'mode'.");
                return(false);
            }

            RenderModel destDefinition;

            using (var destStream = destInfo.OpenCacheRead())
            {
                var context = new TagSerializationContext(destStream, destInfo.Cache, destInfo.StringIDs, destTag);
                destDefinition = destInfo.Deserializer.Deserialize <RenderModel>(context);
            }

            using (MemoryStream inStream = new MemoryStream(), outStream = new MemoryStream())
            {
                // First extract the model data
                srcResources.Extract(Definition.Geometry.Resource, inStream);

                // Now open source and destination vertex streams
                inStream.Position = 0;
                var inVertexStream  = VertexStreamFactory.Create(Info.Version, inStream);
                var outVertexStream = VertexStreamFactory.Create(destInfo.Version, outStream);

                // Deserialize the definition data
                var resourceContext = new ResourceSerializationContext(Definition.Geometry.Resource);
                var definition      = Info.Deserializer.Deserialize <RenderGeometryResourceDefinition>(resourceContext);

                // Convert each vertex buffer
                foreach (var buffer in definition.VertexBuffers)
                {
                    TagConverter.ConvertVertexBuffer(buffer.Definition, inStream, inVertexStream, outStream, outVertexStream);
                }

                // Copy each index buffer over
                foreach (var buffer in definition.IndexBuffers)
                {
                    if (buffer.Definition.Data.Size == 0)
                    {
                        continue;
                    }
                    inStream.Position = buffer.Definition.Data.Address.Offset;
                    buffer.Definition.Data.Address = new ResourceAddress(ResourceAddressType.Resource, (int)outStream.Position);
                    var bufferData = new byte[buffer.Definition.Data.Size];
                    inStream.Read(bufferData, 0, bufferData.Length);
                    outStream.Write(bufferData, 0, bufferData.Length);
                    StreamUtil.Align(outStream, 4);
                }

                destInfo.Serializer.Serialize(resourceContext, definition);

                outStream.Position = 0;
                destResources.Replace(destDefinition.Geometry.Resource, outStream);
            }

            return(true);
        }
Exemple #5
0
        private RenderGeometry ConvertGeometry(RenderGeometry geometry, GameCacheContextHaloOnline srcCacheContext, GameCacheContextHaloOnline destCacheContext)
        {
            if (geometry == null || geometry.Resource.HaloOnlinePageableResource == null || geometry.Resource.HaloOnlinePageableResource.Page.Index < 0 || !geometry.Resource.HaloOnlinePageableResource.GetLocation(out var location))
            {
                return(geometry);
            }

            // The format changed starting with version 1.235640, so if both versions are on the same side then they can be converted normally
            var srcCompare  = CacheVersionDetection.Compare(srcCacheContext.Version, CacheVersion.HaloOnline235640);
            var destCompare = CacheVersionDetection.Compare(destCacheContext.Version, CacheVersion.HaloOnline235640);

            if ((srcCompare < 0 && destCompare < 0) || (srcCompare >= 0 && destCompare >= 0))
            {
                geometry.Resource.HaloOnlinePageableResource = ConvertResource(geometry.Resource.HaloOnlinePageableResource, srcCacheContext, destCacheContext);
                return(geometry);
            }

            Console.WriteLine("- Rebuilding geometry resource {0} in {1}...", geometry.Resource.HaloOnlinePageableResource.Page.Index, location);
            using (MemoryStream inStream = new MemoryStream(), outStream = new MemoryStream())
            {
                // First extract the model data
                srcCacheContext.ExtractResource(geometry.Resource.HaloOnlinePageableResource, inStream);

                // Now open source and destination vertex streams
                inStream.Position = 0;
                var inVertexStream  = VertexStreamFactory.Create(srcCacheContext.Version, inStream);
                var outVertexStream = VertexStreamFactory.Create(destCacheContext.Version, outStream);

                // Deserialize the definition data
                var resourceContext = new ResourceSerializationContext(CacheContext, geometry.Resource.HaloOnlinePageableResource);
                var definition      = srcCacheContext.Deserializer.Deserialize <RenderGeometryApiResourceDefinition>(resourceContext);

                // Convert each vertex buffer
                foreach (var buffer in definition.VertexBuffers)
                {
                    ConvertVertexBuffer(srcCacheContext, destCacheContext, buffer.Definition, inStream, inVertexStream, outStream, outVertexStream);
                }

                // Copy each index buffer over
                foreach (var buffer in definition.IndexBuffers)
                {
                    if (buffer.Definition.Data.Size == 0)
                    {
                        continue;
                    }
                    inStream.Position = buffer.Definition.Data.Address.Offset;
                    buffer.Definition.Data.Address = new CacheAddress(CacheAddressType.Data, (int)outStream.Position);
                    var bufferData = new byte[buffer.Definition.Data.Size];
                    inStream.Read(bufferData, 0, bufferData.Length);
                    outStream.Write(bufferData, 0, bufferData.Length);
                    StreamUtil.Align(outStream, 4);
                }

                // Update the definition data
                destCacheContext.Serializer.Serialize(resourceContext, definition);

                // Now inject the new resource data
                var newLocation = FixResourceLocation(location, srcCacheContext.Version, destCacheContext.Version);

                outStream.Position = 0;
                geometry.Resource.HaloOnlinePageableResource.ChangeLocation(newLocation);
                destCacheContext.AddResource(geometry.Resource.HaloOnlinePageableResource, outStream);
            }

            return(geometry);
        }
Exemple #6
0
        private ScenarioStructureBsp ConvertScenarioStructureBsp(ScenarioStructureBsp sbsp, CachedTagInstance instance, Dictionary <ResourceLocation, Stream> resourceStreams)
        {
            sbsp.CollisionBspResource = ConvertStructureBspTagResources(sbsp, resourceStreams);
            sbsp.PathfindingResource  = ConvertStructureBspCacheFileTagResources(sbsp, resourceStreams);

            sbsp.Unknown86 = 1;

            //
            // Set compatibility flag for H3 mopps
            //

            if (BlamCache.Version == CacheVersion.Halo3Retail)
            {
                sbsp.CompatibilityFlags = ScenarioStructureBsp.StructureBspCompatibilityValue.UseMoppIndexPatch;
            }
            else
            {
                sbsp.CompatibilityFlags = ScenarioStructureBsp.StructureBspCompatibilityValue.None;
            }

            //
            // Fix cluster tag ref and decorator grids
            //

            var resource = sbsp.Geometry.Resource;

            if (resource != null && resource.Page.Index >= 0 && resource.GetLocation(out var location))
            {
                var resourceContext = new ResourceSerializationContext(CacheContext, sbsp.Geometry.Resource);
                var definition      = CacheContext.Deserializer.Deserialize <RenderGeometryApiResourceDefinition>(resourceContext);

                using (var edResourceStream = new MemoryStream())
                    using (var edResourceReader = new EndianReader(edResourceStream, EndianFormat.LittleEndian))
                    {
                        var pageable = sbsp.Geometry.Resource;

                        if (pageable == null)
                        {
                            throw new ArgumentNullException("sbsp.Geometry.Resource");
                        }

                        if (!edResourceStream.CanWrite)
                        {
                            throw new ArgumentException("The output stream is not open for writing", "outStream");
                        }

                        pageable.GetLocation(out var resourceLocation);

                        var cache = CacheContext.GetResourceCache(resourceLocation);

                        if (!resourceStreams.ContainsKey(resourceLocation))
                        {
                            resourceStreams[resourceLocation] = FlagIsSet(PortingFlags.Memory) ?
                                                                new MemoryStream() :
                                                                (Stream)CacheContext.OpenResourceCacheReadWrite(resourceLocation);

                            if (FlagIsSet(PortingFlags.Memory))
                            {
                                using (var resourceStream = CacheContext.OpenResourceCacheRead(resourceLocation))
                                    resourceStream.CopyTo(resourceStreams[resourceLocation]);
                            }
                        }

                        cache.Decompress(resourceStreams[resourceLocation], pageable.Page.Index, pageable.Page.CompressedBlockSize, edResourceStream);

                        var inVertexStream = VertexStreamFactory.Create(CacheVersion.HaloOnline106708, edResourceStream);

                        foreach (var cluster in sbsp.Clusters)
                        {
                            List <ScenarioStructureBsp.Cluster.DecoratorGrid> newDecoratorGrids = new List <ScenarioStructureBsp.Cluster.DecoratorGrid>();

                            foreach (var grid in cluster.DecoratorGrids)
                            {
                                grid.DecoratorGeometryIndex_HO = grid.DecoratorGeometryIndex_H3;
                                grid.DecoratorIndex_HO         = grid.DecoratorIndex_H3;

                                if (grid.Amount == 0)
                                {
                                    newDecoratorGrids.Add(grid);
                                }
                                else
                                {
                                    List <TinyPositionVertex> vertices = new List <TinyPositionVertex>();

                                    // Get the buffer the right grid
                                    var vertexBuffer = definition.VertexBuffers[grid.DecoratorGeometryIndex_HO].Definition;
                                    // Get the offset from the grid
                                    edResourceStream.Position = vertexBuffer.Data.Address.Offset + grid.DecoratorGeometryOffset;
                                    // Read all vertices and add to the list
                                    for (int i = 0; i < grid.Amount; i++)
                                    {
                                        vertices.Add(inVertexStream.ReadTinyPositionVertex());
                                    }

                                    // Get the new grids
                                    List <ScenarioStructureBsp.Cluster.DecoratorGrid> newGrids = ConvertDecoratorGrid(vertices, grid);

                                    // Add all to list
                                    foreach (var newGrid in newGrids)
                                    {
                                        newDecoratorGrids.Add(newGrid);
                                    }
                                }
                            }

                            cluster.DecoratorGrids = newDecoratorGrids;
                        }
                    }
            }

            //
            // Temporary Fixes:
            //

            // Without this 005_intro crash on cortana sbsp
            sbsp.Geometry2.UnknownSections = new List <RenderGeometry.UnknownSection>();

            return(sbsp);
        }
        private ScenarioStructureBsp ConvertScenarioStructureBsp(ScenarioStructureBsp sbsp, CachedTag instance, Dictionary <ResourceLocation, Stream> resourceStreams)
        {
            var converter = new RenderGeometryConverter(CacheContext, BlamCache);   // should be made static

            var blamDecoratorResourceDefinition = BlamCache.ResourceCache.GetRenderGeometryApiResourceDefinition(sbsp.DecoratorGeometry.Resource);
            var blamGeometryResourceDefinition  = BlamCache.ResourceCache.GetRenderGeometryApiResourceDefinition(sbsp.Geometry.Resource);

            var decoratorGeometry = converter.Convert(sbsp.DecoratorGeometry, blamDecoratorResourceDefinition);
            var geometry          = converter.Convert(sbsp.Geometry, blamGeometryResourceDefinition);

            foreach (var cluster in sbsp.Clusters)
            {
                List <ScenarioStructureBsp.Cluster.DecoratorGrid> newDecoratorGrids = new List <ScenarioStructureBsp.Cluster.DecoratorGrid>();

                foreach (var grid in cluster.DecoratorGrids)
                {
                    var buffer = blamDecoratorResourceDefinition.VertexBuffers[grid.Gen3Info.VertexBufferIndex].Definition;
                    var offset = grid.VertexBufferOffset;

                    grid.Vertices = new List <TinyPositionVertex>();
                    using (var stream = new MemoryStream(buffer.Data.Data))
                    {
                        var vertexStream = VertexStreamFactory.Create(BlamCache.Version, stream);
                        stream.Position = offset;

                        for (int i = 0; i < grid.Amount; i++)
                        {
                            grid.Vertices.Add(vertexStream.ReadTinyPositionVertex());
                        }
                    }

                    if (grid.Amount == 0)
                    {
                        newDecoratorGrids.Add(grid);
                    }
                    else
                    {
                        // Get the new grids
                        var newGrids = ConvertDecoratorGrid(grid.Vertices, grid);

                        // Add all to list
                        foreach (var newGrid in newGrids)
                        {
                            newDecoratorGrids.Add(newGrid);
                        }
                    }
                }
                cluster.DecoratorGrids = newDecoratorGrids;
            }

            // convert all the decorator vertex buffers
            foreach (var d3dBuffer in blamDecoratorResourceDefinition.VertexBuffers)
            {
                VertexBufferConverter.ConvertVertexBuffer(BlamCache.Version, CacheContext.Version, d3dBuffer.Definition);
                decoratorGeometry.VertexBuffers.Add(d3dBuffer);
            }

            sbsp.DecoratorGeometry.Resource = CacheContext.ResourceCache.CreateRenderGeometryApiResource(decoratorGeometry);
            sbsp.Geometry.Resource          = CacheContext.ResourceCache.CreateRenderGeometryApiResource(geometry);

            sbsp.CollisionBspResource = ConvertStructureBspTagResources(sbsp);
            sbsp.PathfindingResource  = ConvertStructureBspCacheFileTagResources(sbsp);

            sbsp.Unknown86 = 1;

            //
            // Set compatibility flag for H3 mopps for the engine to perform some fixups just in time
            //

            if (BlamCache.Version == CacheVersion.Halo3Retail || BlamCache.Version == CacheVersion.Halo3Beta)
            {
                sbsp.CompatibilityFlags |= ScenarioStructureBsp.StructureBspCompatibilityValue.UseMoppIndexPatch;
            }

            //
            // Temporary Fixes:
            //

            // Without this 005_intro crash on cortana sbsp
            sbsp.Geometry.MeshClusterVisibility = new List <RenderGeometry.MoppClusterVisiblity>();

            return(sbsp);
        }
Exemple #8
0
        private static List <BMFVertex> ConvertGeometryVertices(VertexCompressor vertexCompressor, VertexBufferDefinition vertexBuffer, CacheVersion version, int vertexCount, int rigidNodeIndex)
        {
            var vertices = new List <BMFVertex>();

            using (var vertexDataStream = new MemoryStream(vertexBuffer.Data.Data))
            {
                var vertexStream = VertexStreamFactory.Create(version, vertexDataStream);

                for (int j = 0; j < vertexCount; j++)
                {
                    var          vertex = new BMFVertex();
                    RealVector2d texcoordTemp;

                    switch (vertexBuffer.Format)
                    {
                    case VertexBufferFormat.Rigid:
                        var rigid = vertexStream.ReadRigidVertex();

                        vertex.Position = vertexCompressor.DecompressPosition(rigid.Position).IJK;
                        texcoordTemp    = vertexCompressor.DecompressUv(rigid.Texcoord);
                        vertex.Texcoord = new RealVector3d(texcoordTemp.I, texcoordTemp.J, 0.0f);
                        vertex.Normal   = rigid.Normal;
                        vertex.Tangent  = rigid.Tangent.IJK;
                        vertex.Binormal = rigid.Binormal;

                        vertex.Weights[0] = new BMFVertexWeight {
                            NodeIndex = rigidNodeIndex, Weight = 1.0f
                        };

                        for (int i = 1; i < 4; i++)
                        {
                            vertex.Weights[i] = new BMFVertexWeight();
                        }

                        break;

                    case VertexBufferFormat.Skinned:
                        var skinned = vertexStream.ReadSkinnedVertex();

                        vertex.Position = vertexCompressor.DecompressPosition(skinned.Position).IJK;
                        texcoordTemp    = vertexCompressor.DecompressUv(skinned.Texcoord);
                        vertex.Texcoord = new RealVector3d(texcoordTemp.I, texcoordTemp.J, 0.0f);
                        vertex.Normal   = skinned.Normal;
                        vertex.Tangent  = skinned.Tangent.IJK;
                        vertex.Binormal = skinned.Binormal;

                        for (int i = 0; i < 4; i++)
                        {
                            vertex.Weights[i] = new BMFVertexWeight {
                                NodeIndex = skinned.BlendIndices[i], Weight = skinned.BlendWeights[i]
                            };
                        }
                        break;

                    default:
                        throw new InvalidOperationException("Unsupported vertex buffer type: " + vertexBuffer.Format);
                    }
                    vertices.Add(vertex);
                }
            }
            return(vertices);
        }