public RenderGeometry Convert(Stream cacheStream, RenderGeometry geometry, Dictionary <ResourceLocation, Stream> resourceStreams, PortTagCommand.PortingFlags portingFlags) { if (BlamCache.ResourceGestalt == null || BlamCache.ResourceLayoutTable == null) { BlamCache.LoadResourceTags(); } // // Convert byte[] of UnknownBlock // foreach (var block in geometry.Unknown2) { var data = block.Unknown3; if (data != null || data.Length != 0) { var result = new byte[data.Length]; using (var inputReader = new EndianReader(new MemoryStream(data), EndianFormat.BigEndian)) using (var outputWriter = new EndianWriter(new MemoryStream(result), EndianFormat.LittleEndian)) { while (!inputReader.EOF) { outputWriter.Write(inputReader.ReadUInt32()); } block.Unknown3 = result; } } } // // Convert UnknownSection.Unknown byte[] endian // for (int i = 0; i < geometry.UnknownSections.Count; i++) { byte[] dataref = geometry.UnknownSections[i].Unknown; if (dataref.Length == 0) { continue; } using (var outStream = new MemoryStream()) using (var outReader = new BinaryReader(outStream)) using (var outWriter = new EndianWriter(outStream, EndianFormat.LittleEndian)) using (var stream = new MemoryStream(dataref)) using (var reader = new EndianReader(stream, EndianFormat.BigEndian)) { var dataContext = new DataSerializationContext(reader, outWriter); var header = CacheContext.Deserializer.Deserialize <ScenarioLightmapBspDataSection.Header>(dataContext); var section = new ScenarioLightmapBspDataSection { Headers = new List <ScenarioLightmapBspDataSection.Header> { header }, VertexLists = new ScenarioLightmapBspDataSection.VertexList { Vertex = new List <ScenarioLightmapBspDataSection.VertexList.Datum>() } }; CacheContext.Serializer.Serialize(dataContext, header); while (reader.BaseStream.Position < dataref.Length) // read the rest of dataref { if (section.Headers.Count == 2) // remove "wrongfully" added ones { section.Headers.RemoveAt(1); } section.Headers.Add(CacheContext.Deserializer.Deserialize <ScenarioLightmapBspDataSection.Header>(dataContext)); // if some values match header1, continue if (section.Headers[0].Position == section.Headers[1].Position) { header = section.Headers[1]; CacheContext.Serializer.Serialize(dataContext, header); while (reader.BaseStream.Position < dataref.Length) { section.VertexLists.Vertex.Add(new ScenarioLightmapBspDataSection.VertexList.Datum { Value = reader.ReadByte() }); outWriter.Write(section.VertexLists.Vertex[section.VertexLists.Vertex.Count - 1].Value); } } else // if read data doesn't match, go back and just read 4 bytes { reader.BaseStream.Position = reader.BaseStream.Position - 0x2C; // if read data doesn't match, go back and serialize section.VertexLists.Vertex.Add(new ScenarioLightmapBspDataSection.VertexList.Datum { Value = reader.ReadByte() }); outWriter.Write(section.VertexLists.Vertex[section.VertexLists.Vertex.Count - 1].Value); section.VertexLists.Vertex.Add(new ScenarioLightmapBspDataSection.VertexList.Datum { Value = reader.ReadByte() }); outWriter.Write(section.VertexLists.Vertex[section.VertexLists.Vertex.Count - 1].Value); section.VertexLists.Vertex.Add(new ScenarioLightmapBspDataSection.VertexList.Datum { Value = reader.ReadByte() }); outWriter.Write(section.VertexLists.Vertex[section.VertexLists.Vertex.Count - 1].Value); section.VertexLists.Vertex.Add(new ScenarioLightmapBspDataSection.VertexList.Datum { Value = reader.ReadByte() }); outWriter.Write(section.VertexLists.Vertex[section.VertexLists.Vertex.Count - 1].Value); } } // Write back to tag outStream.Position = 0; geometry.UnknownSections[i].Unknown = outStream.ToArray(); } } // // Set up ElDorado resource reference // geometry.Resource = new PageableResource { Page = new RawPage { Index = -1 }, Resource = new TagResourceGen3 { ResourceType = TagResourceTypeGen3.RenderGeometry, DefinitionData = new byte[0x30], DefinitionAddress = new CacheAddress(CacheAddressType.Definition, 0), ResourceFixups = new List <TagResourceGen3.ResourceFixup>(), ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(), Unknown2 = 1 } }; // // Port Blam resource definition // var resourceEntry = BlamCache.ResourceGestalt.TagResources[geometry.ZoneAssetHandle & ushort.MaxValue]; geometry.Resource.Resource.DefinitionAddress = resourceEntry.DefinitionAddress; geometry.Resource.Resource.DefinitionData = BlamCache.ResourceGestalt.FixupInformation.Skip(resourceEntry.FixupInformationOffset).Take(resourceEntry.FixupInformationLength).ToArray(); RenderGeometryApiResourceDefinition resourceDefinition = null; if (geometry.Resource.Resource.DefinitionData.Length < 0x30) { resourceDefinition = new RenderGeometryApiResourceDefinition { VertexBuffers = new List <TagStructureReference <VertexBufferDefinition> >(), IndexBuffers = new List <TagStructureReference <IndexBufferDefinition> >() }; } else { using (var definitionStream = new MemoryStream(geometry.Resource.Resource.DefinitionData, true)) using (var definitionReader = new EndianReader(definitionStream, EndianFormat.BigEndian)) using (var definitionWriter = new EndianWriter(definitionStream, EndianFormat.BigEndian)) { foreach (var fixup in resourceEntry.ResourceFixups) { definitionStream.Position = fixup.BlockOffset; definitionWriter.Write(fixup.Address.Value); geometry.Resource.Resource.ResourceFixups.Add(fixup); } foreach (var definitionFixup in resourceEntry.ResourceDefinitionFixups) { var newDefinitionFixup = new TagResourceGen3.ResourceDefinitionFixup { Address = definitionFixup.Address, ResourceStructureTypeIndex = definitionFixup.ResourceStructureTypeIndex }; geometry.Resource.Resource.ResourceDefinitionFixups.Add(newDefinitionFixup); } var dataContext = new DataSerializationContext(definitionReader, definitionWriter, CacheAddressType.Definition); definitionStream.Position = geometry.Resource.Resource.DefinitionAddress.Offset; resourceDefinition = BlamCache.Deserializer.Deserialize <RenderGeometryApiResourceDefinition>(dataContext); } } // // Load Blam resource data // var resourceData = BlamCache.GetRawFromID(geometry.ZoneAssetHandle); var generateParticles = false; if (resourceData == null) { if (geometry.Meshes.Count == 1 && geometry.Meshes[0].Type == VertexType.ParticleModel) { generateParticles = true; resourceData = new byte[0]; } else { geometry.Resource.Resource.ResourceType = TagResourceTypeGen3.None; return(geometry); } } // // Convert Blam data to ElDorado data // using (var dataStream = new MemoryStream()) using (var blamResourceStream = new MemoryStream(resourceData)) { for (int i = 0; i < geometry.Meshes.Count(); i++) { var mesh = geometry.Meshes[i]; if (mesh.VertexBufferIndices[6] != 0xFFFF && mesh.VertexBufferIndices[7] != 0xFFFF) { ushort temp = mesh.VertexBufferIndices[6]; mesh.VertexBufferIndices[6] = mesh.VertexBufferIndices[7]; mesh.VertexBufferIndices[7] = temp; // Get total amount of indices int indexCount = 0; foreach (var subpart in mesh.SubParts) { indexCount += subpart.IndexCount; } WaterConversionData waterData = new WaterConversionData() { IndexBufferLength = indexCount, }; for (int j = 0; j < mesh.Parts.Count(); j++) { var part = mesh.Parts[j]; waterData.PartData.Add(new Tuple <int, int, bool>(part.FirstIndex, part.IndexCount, part.FlagsNew.HasFlag(Mesh.Part.PartFlagsNew.CanBeRenderedInDrawBundles))); } waterData.Sort(); WaterData.Add(waterData); } } if (generateParticles) { var outVertexStream = VertexStreamFactory.Create(CacheContext.Version, dataStream); StreamUtil.Align(dataStream, 4); resourceDefinition.VertexBuffers.Add(new TagStructureReference <VertexBufferDefinition> { Definition = new VertexBufferDefinition { Format = VertexBufferFormat.ParticleModel, Data = new TagData { Size = 32, Unused4 = 0, Unused8 = 0, Address = new CacheAddress(CacheAddressType.Resource, (int)dataStream.Position), Unused10 = 0 } } }); var vertexBuffer = resourceDefinition.VertexBuffers.Last().Definition; for (var j = 0; j < 3; j++) { outVertexStream.WriteParticleModelVertex(new ParticleModelVertex { Position = new RealVector3d(), Texcoord = new RealVector2d(), Normal = new RealVector3d() }); } geometry.Meshes[0].VertexBufferIndices[0] = (ushort)resourceDefinition.VertexBuffers.IndexOf(resourceDefinition.VertexBuffers.Last()); geometry.Meshes[0].IndexBufferIndices[0] = CreateIndexBuffer(resourceDefinition, dataStream, 3); } else { for (int i = 0, prevVertCount = -1; i < resourceDefinition.VertexBuffers.Count; i++, prevVertCount = resourceDefinition.VertexBuffers[i - 1].Definition.Count) { blamResourceStream.Position = resourceDefinition.VertexBuffers[i].Definition.Data.Address.Offset; // resourceEntry.ResourceFixups[i].Offset; ConvertVertexBuffer(resourceDefinition, blamResourceStream, dataStream, i, prevVertCount); } for (var i = 0; i < resourceDefinition.IndexBuffers.Count; i++) { blamResourceStream.Position = resourceDefinition.IndexBuffers[i].Definition.Data.Address.Offset; // resourceEntry.ResourceFixups[resourceDefinition.VertexBuffers.Count * 2 + i].Offset; ConvertIndexBuffer(resourceDefinition, blamResourceStream, dataStream, i); } foreach (var mesh in geometry.Meshes) { if (!mesh.Flags.HasFlag(MeshFlags.MeshIsUnindexed)) { continue; } var indexCount = 0; foreach (var part in mesh.Parts) { indexCount += part.IndexCount; } mesh.IndexBufferIndices[0] = CreateIndexBuffer(resourceDefinition, dataStream, indexCount); } } // // Swap order of water vertex buffers // for (var i = 0; i < resourceDefinition.VertexBuffers.Count; i++) { var vertexBuffer = resourceDefinition.VertexBuffers[i]; if (vertexBuffer.Definition.Format == VertexBufferFormat.Unknown1B) { TagStructureReference <VertexBufferDefinition> temp = vertexBuffer; resourceDefinition.VertexBuffers[i] = resourceDefinition.VertexBuffers[i - 1]; resourceDefinition.VertexBuffers[i - 1] = temp; } } // // Finalize the new ElDorado geometry resource // var cache = CacheContext.GetResourceCache(ResourceLocation.Resources); if (!resourceStreams.ContainsKey(ResourceLocation.Resources)) { resourceStreams[ResourceLocation.Resources] = portingFlags.HasFlag(PortTagCommand.PortingFlags.Memory) ? new MemoryStream() : (Stream)CacheContext.OpenResourceCacheReadWrite(ResourceLocation.Resources); if (portingFlags.HasFlag(PortTagCommand.PortingFlags.Memory)) { using (var resourceStream = CacheContext.OpenResourceCacheRead(ResourceLocation.Resources)) resourceStream.CopyTo(resourceStreams[ResourceLocation.Resources]); } } geometry.Resource.ChangeLocation(ResourceLocation.Resources); geometry.Resource.Page.Index = cache.Add(resourceStreams[ResourceLocation.Resources], dataStream.ToArray(), out uint compressedSize); geometry.Resource.Page.CompressedBlockSize = compressedSize; geometry.Resource.Page.UncompressedBlockSize = (uint)dataStream.Length; geometry.Resource.DisableChecksum(); var resourceContext = new ResourceSerializationContext(CacheContext, geometry.Resource); CacheContext.Serializer.Serialize(resourceContext, resourceDefinition); } return(geometry); }
/// <summary> /// Converts RenderGeometry class in place and returns a new RenderGeometryApiResourceDefinition /// </summary> public RenderGeometryApiResourceDefinition Convert(RenderGeometry geometry, RenderGeometryApiResourceDefinition resourceDefinition) { // // Convert byte[] of UnknownBlock // foreach (var block in geometry.Unknown2) { var data = block.Unknown3; if (data != null || data.Length != 0) { var result = new byte[data.Length]; using (var inputReader = new EndianReader(new MemoryStream(data), SourceCache.Endianness)) using (var outputWriter = new EndianWriter(new MemoryStream(result), HOCache.Endianness)) { while (!inputReader.EOF) { outputWriter.Write(inputReader.ReadUInt32()); } block.Unknown3 = result; } } } // // Convert mopps in cluster visibility // foreach (var clusterVisibility in geometry.MeshClusterVisibility) { clusterVisibility.MoppData = HavokConverter.ConvertHkpMoppData(SourceCache.Version, HOCache.Version, clusterVisibility.MoppData); } // // Port resource definition // var wasNull = false; if (resourceDefinition == null) { wasNull = true; Console.Error.WriteLine("Render geometry does not have a valid resource definition, continuing anyway."); resourceDefinition = new RenderGeometryApiResourceDefinition { VertexBuffers = new TagBlock <D3DStructure <VertexBufferDefinition> >(CacheAddressType.Definition), IndexBuffers = new TagBlock <D3DStructure <IndexBufferDefinition> >(CacheAddressType.Definition) }; } geometry.SetResourceBuffers(resourceDefinition); // do conversion (PARTICLE INDEX BUFFERS, WATER CONVERSION TO DO) AMBIENT PRT TOO var generateParticles = false; // temp fix when pmdf geo is null if (wasNull) { if (geometry.Meshes.Count == 1 && geometry.Meshes[0].Type == VertexType.ParticleModel) { generateParticles = true; } else { geometry.Resource = HOCache.ResourceCache.CreateRenderGeometryApiResource(resourceDefinition); geometry.Resource.HaloOnlinePageableResource.Resource.ResourceType = TagResourceTypeGen3.None; return(resourceDefinition); } } // // Convert Blam data to ElDorado data // if (generateParticles) { var mesh = geometry.Meshes[0]; mesh.Flags |= MeshFlags.MeshIsUnindexed; mesh.PrtType = PrtSHType.None; var newVertexBuffer = new VertexBufferDefinition { Format = VertexBufferFormat.ParticleModel, VertexSize = (short)VertexStreamFactory.Create(HOCache.Version, null).GetVertexSize(VertexBufferFormat.ParticleModel), Data = new TagData { Data = new byte[32], AddressType = CacheAddressType.Data } }; mesh.ResourceVertexBuffers[0] = newVertexBuffer; } else { foreach (var mesh in geometry.Meshes) { foreach (var vertexBuffer in mesh.ResourceVertexBuffers) { if (vertexBuffer == null) { continue; } // Gen3 order 0 coefficients are stored in ints but should be read as bytes, 1 per vertex in the original buffer if (vertexBuffer.Format == VertexBufferFormat.AmbientPrt) { vertexBuffer.Count = mesh.ResourceVertexBuffers[0].Count; } // skip conversion of water vertices, done right after the loop if (vertexBuffer.Format == VertexBufferFormat.Unknown1A || vertexBuffer.Format == VertexBufferFormat.Unknown1B) { continue; } VertexBufferConverter.ConvertVertexBuffer(SourceCache.Version, HOCache.Version, vertexBuffer); } // convert water vertex buffers if (mesh.ResourceVertexBuffers[6] != null && mesh.ResourceVertexBuffers[7] != null) { // Get total amount of indices and prepare for water conversion int indexCount = 0; foreach (var subpart in mesh.SubParts) { indexCount += subpart.IndexCount; } WaterConversionData waterData = new WaterConversionData(); for (int j = 0; j < mesh.Parts.Count(); j++) { var part = mesh.Parts[j]; if (part.FlagsNew.HasFlag(Mesh.Part.PartFlagsNew.IsWaterPart)) { waterData.PartData.Add(new Tuple <int, int>(part.FirstIndexOld, part.IndexCountOld)); } } if (waterData.PartData.Count > 1) { waterData.Sort(); } // read all world vertices, unknown1A and unknown1B into lists. List <WorldVertex> worldVertices = new List <WorldVertex>(); List <Unknown1B> h3WaterParameters = new List <Unknown1B>(); List <Unknown1A> h3WaterIndices = new List <Unknown1A>(); using (var stream = new MemoryStream(mesh.ResourceVertexBuffers[0].Data.Data)) { var vertexStream = VertexStreamFactory.Create(HOCache.Version, stream); for (int v = 0; v < mesh.ResourceVertexBuffers[0].Count; v++) { worldVertices.Add(vertexStream.ReadWorldVertex()); } } using (var stream = new MemoryStream(mesh.ResourceVertexBuffers[6].Data.Data)) { var vertexStream = VertexStreamFactory.Create(SourceCache.Version, stream); for (int v = 0; v < mesh.ResourceVertexBuffers[6].Count; v++) { h3WaterIndices.Add(vertexStream.ReadUnknown1A()); } } using (var stream = new MemoryStream(mesh.ResourceVertexBuffers[7].Data.Data)) { var vertexStream = VertexStreamFactory.Create(SourceCache.Version, stream); for (int v = 0; v < mesh.ResourceVertexBuffers[7].Count; v++) { h3WaterParameters.Add(vertexStream.ReadUnknown1B()); } } // create vertex buffer for Unknown1A -> World VertexBufferDefinition waterVertices = new VertexBufferDefinition { Count = indexCount, Format = VertexBufferFormat.World, Data = new TagData(), VertexSize = 0x38 // this size is actually wrong but I replicate the errors in HO data, size should be 0x34 }; // create vertex buffer for Unknown1B VertexBufferDefinition waterParameters = new VertexBufferDefinition { Count = indexCount, Format = VertexBufferFormat.Unknown1B, Data = new TagData(), VertexSize = 0x24 // wrong size, this is 0x18 on file, padded with zeroes. }; using (var outputWorldWaterStream = new MemoryStream()) using (var outputWaterParametersStream = new MemoryStream()) { var outWorldVertexStream = VertexStreamFactory.Create(HOCache.Version, outputWorldWaterStream); var outWaterParameterVertexStream = VertexStreamFactory.Create(HOCache.Version, outputWaterParametersStream); // fill vertex buffer to the right size HO expects, then write the vertex data at the actual proper position VertexBufferConverter.DebugFill(outputWorldWaterStream, waterVertices.VertexSize * waterVertices.Count); VertexBufferConverter.Fill(outputWaterParametersStream, waterParameters.VertexSize * waterParameters.Count); var unknown1ABaseIndex = 0; // unknown1A are not separated into parts, if a mesh has multiple parts we need to get the right unknown1As for (int k = 0; k < waterData.PartData.Count(); k++) { Tuple <int, int> currentPartData = waterData.PartData[k]; //seek to the right location in the buffer outputWorldWaterStream.Position = 0x34 * currentPartData.Item1; outputWaterParametersStream.Position = 0x18 * currentPartData.Item1; for (int v = 0; v < currentPartData.Item2; v += 3) { var unknown1A = h3WaterIndices[(v / 3) + unknown1ABaseIndex]; for (int j = 0; j < 3; j++) { var worldVertex = worldVertices[unknown1A.Vertices[j]]; var unknown1B = h3WaterParameters[unknown1A.Indices[j]]; // conversion should happen here outWorldVertexStream.WriteWorldWaterVertex(worldVertex); outWaterParameterVertexStream.WriteUnknown1B(unknown1B); } } unknown1ABaseIndex += currentPartData.Item2 / 3; // tells next part we read those indices already } waterVertices.Data.Data = outputWorldWaterStream.ToArray(); waterParameters.Data.Data = outputWaterParametersStream.ToArray(); } mesh.ResourceVertexBuffers[6] = waterVertices; mesh.ResourceVertexBuffers[7] = waterParameters; } foreach (var indexBuffer in mesh.ResourceIndexBuffers) { if (indexBuffer == null) { continue; } IndexBufferConverter.ConvertIndexBuffer(SourceCache.Version, HOCache.Version, indexBuffer); } // create index buffers for decorators, gen3 didn't have them if (mesh.Flags.HasFlag(MeshFlags.MeshIsUnindexed) && mesh.Type == VertexType.Decorator) { mesh.Flags &= ~MeshFlags.MeshIsUnindexed; var indexCount = 0; foreach (var part in mesh.Parts) { indexCount += part.IndexCountOld; } mesh.ResourceIndexBuffers[0] = IndexBufferConverter.CreateIndexBuffer(indexCount); } } } foreach (var perPixel in geometry.InstancedGeometryPerPixelLighting) { if (perPixel.VertexBuffer != null) { VertexBufferConverter.ConvertVertexBuffer(SourceCache.Version, HOCache.Version, perPixel.VertexBuffer); } } return(geometry.GetResourceDefinition()); }