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); }
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); } }
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); }
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); }
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); }
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); }
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); }