/// <summary> /// Opens a vertex stream on one of the mesh's vertex buffers. /// </summary> /// <param name="definition">The vertex buffer definition.</param> /// <param name="baseStream">The stream open on the mesh's resource data to use as a base stream.</param> /// <returns>The vertex stream if successful, or <c>null</c> otherwise.</returns> public IVertexStream OpenVertexStream(VertexBufferDefinition definition, Stream baseStream) { if (definition.Data.Address.Type != ResourceAddressType.Resource) { return(null); // Don't bother supporting non-resource addresses } baseStream.Position = definition.Data.Address.Offset; return(VertexStreamFactory.Create(_version, baseStream)); }
private int SerializeVertexBuffer(MeshData mesh, Stream outStream) { var vertexStream = VertexStreamFactory.Create(_version, outStream); if (mesh.RigidVertices != null) { foreach (var v in mesh.RigidVertices) { vertexStream.WriteRigidVertex(v); } return(mesh.RigidVertices.Length); } if (mesh.SkinnedVertices != null) { foreach (var v in mesh.SkinnedVertices) { vertexStream.WriteSkinnedVertex(v); } return(mesh.SkinnedVertices.Length); } return(0); }
public void ConvertVertexBuffer(RenderGeometryApiResourceDefinition resourceDefinition, Stream inputStream, Stream outputStream, int vertexBufferIndex, int previousVertexBufferCount) { var vertexBuffer = resourceDefinition.VertexBuffers[vertexBufferIndex].Definition; var count = vertexBuffer.Count; var startPos = (int)outputStream.Position; vertexBuffer.Data.Address = new CacheAddress(CacheAddressType.Resource, startPos); var inVertexStream = VertexStreamFactory.Create(BlamCache.Version, inputStream); var outVertexStream = VertexStreamFactory.Create(CacheContext.Version, outputStream); OriginalBufferOffsets.Add(inputStream.Position); switch (vertexBuffer.Format) { case VertexBufferFormat.World: ConvertVertices(count, inVertexStream.ReadWorldVertex, (v, i) => { //v.Tangent = new RealQuaternion(-Math.Abs(v.Tangent.I), -Math.Abs(v.Tangent.J), Math.Abs(v.Tangent.K), Math.Abs(v.Tangent.W)); // great results for H3 armors outVertexStream.WriteWorldVertex(v); }); break; case VertexBufferFormat.Rigid: ConvertVertices(count, inVertexStream.ReadRigidVertex, (v, i) => { //v.Tangent = new RealQuaternion(-Math.Abs(v.Tangent.I), -Math.Abs(v.Tangent.J), Math.Abs(v.Tangent.K), Math.Abs(v.Tangent.W)); // great results for H3 armors outVertexStream.WriteRigidVertex(v); }); break; case VertexBufferFormat.Skinned: ConvertVertices(count, inVertexStream.ReadSkinnedVertex, (v, i) => { //v.Tangent = new RealQuaternion(-Math.Abs(v.Tangent.I), -Math.Abs(v.Tangent.J), Math.Abs(v.Tangent.K), Math.Abs(v.Tangent.W)); // great results for H3 armors outVertexStream.WriteSkinnedVertex(v); }); break; case VertexBufferFormat.StaticPerPixel: ConvertVertices(count, inVertexStream.ReadStaticPerPixelData, (v, i) => outVertexStream.WriteStaticPerPixelData(v)); break; case VertexBufferFormat.StaticPerVertex: ConvertVertices(count, inVertexStream.ReadStaticPerVertexData, (v, i) => { v.Texcoord1 = ConvertNormal(v.Texcoord1); v.Texcoord2 = ConvertNormal(v.Texcoord2); v.Texcoord3 = ConvertNormal(v.Texcoord3); v.Texcoord4 = ConvertNormal(v.Texcoord4); v.Texcoord5 = ConvertNormal(v.Texcoord5); outVertexStream.WriteStaticPerVertexData(v); }); break; case VertexBufferFormat.AmbientPrt: ConvertVertices(vertexBuffer.Count = previousVertexBufferCount, inVertexStream.ReadAmbientPrtData, (v, i) => outVertexStream.WriteAmbientPrtData(v)); break; case VertexBufferFormat.LinearPrt: ConvertVertices(count, inVertexStream.ReadLinearPrtData, (v, i) => { v.BlendWeight = ConvertNormal(v.BlendWeight); outVertexStream.WriteLinearPrtData(v); }); break; case VertexBufferFormat.QuadraticPrt: ConvertVertices(count, inVertexStream.ReadQuadraticPrtData, (v, i) => outVertexStream.WriteQuadraticPrtData(v)); break; case VertexBufferFormat.StaticPerVertexColor: ConvertVertices(count, inVertexStream.ReadStaticPerVertexColorData, (v, i) => outVertexStream.WriteStaticPerVertexColorData(v)); break; case VertexBufferFormat.Decorator: ConvertVertices(count, inVertexStream.ReadDecoratorVertex, (v, i) => outVertexStream.WriteDecoratorVertex(v)); break; case VertexBufferFormat.World2: vertexBuffer.Format = VertexBufferFormat.World; goto case VertexBufferFormat.World; case VertexBufferFormat.Unknown1A: var waterData = WaterData[CurrentWaterBuffer]; // Reformat Vertex Buffer vertexBuffer.Format = VertexBufferFormat.World; vertexBuffer.VertexSize = 0x34; vertexBuffer.Count = waterData.IndexBufferLength; // Create list of indices for later use. Unknown1BIndices = new List <ushort>(); for (int k = 0; k < waterData.PartData.Count(); k++) { Tuple <int, int, bool> currentPartData = waterData.PartData[k]; // Not water, add garbage data if (currentPartData.Item3 == false) { for (int j = 0; j < currentPartData.Item2; j++) { WriteUnusedWorldWaterData(outputStream); } } else { ConvertVertices(currentPartData.Item2 / 3, inVertexStream.ReadUnknown1A, (v, i) => { // Store current stream position var tempStreamPosition = inputStream.Position; // Open previous world buffer (H3) var worldVertexBufferBasePosition = OriginalBufferOffsets[OriginalBufferOffsets.Count() - 3]; inputStream.Position = worldVertexBufferBasePosition; for (int j = 0; j < 3; j++) { inputStream.Position = 0x20 * v.Vertices[j] + worldVertexBufferBasePosition; WorldVertex w = inVertexStream.ReadWorldVertex(); Unknown1BIndices.Add(v.Indices[j]); // The last 2 floats in WorldWater are unknown. outVertexStream.WriteWorldWaterVertex(w); } // Restore position for reading the next vertex correctly inputStream.Position = tempStreamPosition; }); } } break; case VertexBufferFormat.Unknown1B: var waterDataB = WaterData[CurrentWaterBuffer]; // Adjust vertex size to match HO. Set count of vertices vertexBuffer.VertexSize = 0x18; var originalCount = vertexBuffer.Count; vertexBuffer.Count = waterDataB.IndexBufferLength; var basePosition = inputStream.Position; var unknown1BPosition = 0; for (int k = 0; k < waterDataB.PartData.Count(); k++) { Tuple <int, int, bool> currentPartData = waterDataB.PartData[k]; // Not water, add garbage data if (currentPartData.Item3 == false) { for (int j = 0; j < currentPartData.Item2; j++) { WriteUnusedUnknown1BData(outputStream); } } else { for (int j = unknown1BPosition; j < Unknown1BIndices.Count() && j - unknown1BPosition < currentPartData.Item2; j++) { inputStream.Position = basePosition + 0x24 * Unknown1BIndices[j]; ConvertVertices(1, inVertexStream.ReadUnknown1B, (v, i) => outVertexStream.WriteUnknown1B(v)); unknown1BPosition++; } } } // Get to the end of Unknown1B in H3 data inputStream.Position = basePosition + originalCount * 0x24; CurrentWaterBuffer++; break; case VertexBufferFormat.ParticleModel: ConvertVertices(count, inVertexStream.ReadParticleModelVertex, (v, i) => outVertexStream.WriteParticleModelVertex(v)); break; case VertexBufferFormat.TinyPosition: ConvertVertices(count, inVertexStream.ReadTinyPositionVertex, (v, i) => { v.Position = ConvertPositionShort(v.Position); v.Variant = (ushort)((v.Variant >> 8) & 0xFF); v.Normal = ConvertNormal(v.Normal); outVertexStream.WriteTinyPositionVertex(v); }); break; default: throw new NotSupportedException(vertexBuffer.Format.ToString()); } vertexBuffer.Data.Size = (int)outputStream.Position - startPos; vertexBuffer.VertexSize = (short)(vertexBuffer.Data.Size / vertexBuffer.Count); resourceDefinition.VertexBuffers[vertexBufferIndex].DefinitionAddress = 0; resourceDefinition.VertexBuffers[vertexBufferIndex].RuntimeAddress = 0; }
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()); }
/// <summary> /// Opens a vertex stream on one of the mesh's vertex buffers. /// </summary> /// <param name="definition">The vertex buffer definition.</param> /// <returns>The vertex stream if successful, or <c>null</c> otherwise.</returns> public IVertexStream OpenVertexStream(VertexBufferDefinition definition) { var stream = new MemoryStream(definition.Data.Data); return(VertexStreamFactory.Create(_version, stream)); }
public static void ConvertVertexBuffer(CacheVersion inVersion, CacheVersion outVersion, VertexBufferDefinition vertexBuffer) { using (var outputStream = new MemoryStream()) using (var inputStream = new MemoryStream(vertexBuffer.Data.Data)) { var inVertexStream = VertexStreamFactory.Create(inVersion, inputStream); var outVertexStream = VertexStreamFactory.Create(outVersion, outputStream); var count = vertexBuffer.Count; switch (vertexBuffer.Format) { case VertexBufferFormat.World: ConvertVertices(count, inVertexStream.ReadWorldVertex, (v, i) => { v.Normal = ConvertVectorSpace(v.Normal); v.Tangent = ConvertVectorSpace(v.Tangent); v.Binormal = ConvertVectorSpace(v.Binormal); outVertexStream.WriteWorldVertex(v); }); break; case VertexBufferFormat.Rigid: ConvertVertices(count, inVertexStream.ReadRigidVertex, (v, i) => { v.Normal = ConvertVectorSpace(v.Normal); v.Tangent = ConvertVectorSpace(v.Tangent); v.Binormal = ConvertVectorSpace(v.Binormal); outVertexStream.WriteRigidVertex(v); }); break; case VertexBufferFormat.Skinned: ConvertVertices(count, inVertexStream.ReadSkinnedVertex, (v, i) => { v.Normal = ConvertVectorSpace(v.Normal); v.Tangent = ConvertVectorSpace(v.Tangent); v.Binormal = ConvertVectorSpace(v.Binormal); outVertexStream.WriteSkinnedVertex(v); }); break; case VertexBufferFormat.StaticPerPixel: ConvertVertices(count, inVertexStream.ReadStaticPerPixelData, (v, i) => outVertexStream.WriteStaticPerPixelData(v)); break; case VertexBufferFormat.StaticPerVertex: ConvertVertices(count, inVertexStream.ReadStaticPerVertexData, (v, i) => { v.Color1 = ConvertColorSpace(v.Color1); v.Color2 = ConvertColorSpace(v.Color2); v.Color3 = ConvertColorSpace(v.Color3); v.Color4 = ConvertColorSpace(v.Color4); v.Color5 = ConvertColorSpace(v.Color5); outVertexStream.WriteStaticPerVertexData(v); }); break; // assume count has been fixed up before this case VertexBufferFormat.AmbientPrt: ConvertVertices(count, inVertexStream.ReadAmbientPrtData, (v, i) => outVertexStream.WriteAmbientPrtData(v)); break; case VertexBufferFormat.LinearPrt: ConvertVertices(count, inVertexStream.ReadLinearPrtData, (v, i) => { v.SHCoefficients = ConvertNormal(v.SHCoefficients); outVertexStream.WriteLinearPrtData(v); }); break; case VertexBufferFormat.QuadraticPrt: ConvertVertices(count, inVertexStream.ReadQuadraticPrtData, (v, i) => outVertexStream.WriteQuadraticPrtData(v)); break; case VertexBufferFormat.StaticPerVertexColor: ConvertVertices(count, inVertexStream.ReadStaticPerVertexColorData, (v, i) => outVertexStream.WriteStaticPerVertexColorData(v)); break; case VertexBufferFormat.Decorator: ConvertVertices(count, inVertexStream.ReadDecoratorVertex, (v, i) => { v.Normal = ConvertVectorSpace(v.Normal); outVertexStream.WriteDecoratorVertex(v); }); break; case VertexBufferFormat.World2: vertexBuffer.Format = VertexBufferFormat.World; goto case VertexBufferFormat.World; /* * case VertexBufferFormat.Unknown1A: * * var waterData = WaterData[CurrentWaterBuffer]; * * // Reformat Vertex Buffer * vertexBuffer.Format = VertexBufferFormat.World; * vertexBuffer.VertexSize = 0x34; * vertexBuffer.Count = waterData.IndexBufferLength; * * // Create list of indices for later use. * Unknown1BIndices = new List<ushort>(); * * for (int k = 0; k < waterData.PartData.Count(); k++) * { * Tuple<int, int, bool> currentPartData = waterData.PartData[k]; * * // Not water, add garbage data * if (currentPartData.Item3 == false) * { * for (int j = 0; j < currentPartData.Item2; j++) * WriteUnusedWorldWaterData(outputStream); * } * else * { * ConvertVertices(currentPartData.Item2 / 3, inVertexStream.ReadUnknown1A, (v, i) => * { * // Store current stream position * var tempStreamPosition = inputStream.Position; * * // Open previous world buffer (H3) * var worldVertexBufferBasePosition = OriginalBufferOffsets[OriginalBufferOffsets.Count() - 3]; * inputStream.Position = worldVertexBufferBasePosition; * * for (int j = 0; j < 3; j++) * { * inputStream.Position = 0x20 * v.Vertices[j] + worldVertexBufferBasePosition; * * WorldVertex w = inVertexStream.ReadWorldVertex(); * * Unknown1BIndices.Add(v.Indices[j]); * * // The last 2 floats in WorldWater are unknown. * * outVertexStream.WriteWorldWaterVertex(w); * } * * // Restore position for reading the next vertex correctly * inputStream.Position = tempStreamPosition; * }); * } * } * break; * * case VertexBufferFormat.Unknown1B: * * var waterDataB = WaterData[CurrentWaterBuffer]; * * // Adjust vertex size to match HO. Set count of vertices * * vertexBuffer.VertexSize = 0x18; * * var originalCount = vertexBuffer.Count; * vertexBuffer.Count = waterDataB.IndexBufferLength; * * var basePosition = inputStream.Position; * var unknown1BPosition = 0; * * for (int k = 0; k < waterDataB.PartData.Count(); k++) * { * Tuple<int, int, bool> currentPartData = waterDataB.PartData[k]; * * // Not water, add garbage data * if (currentPartData.Item3 == false) * { * for (int j = 0; j < currentPartData.Item2; j++) * WriteUnusedUnknown1BData(outputStream); * } * else * { * for (int j = unknown1BPosition; j < Unknown1BIndices.Count() && j - unknown1BPosition < currentPartData.Item2; j++) * { * inputStream.Position = basePosition + 0x24 * Unknown1BIndices[j]; * ConvertVertices(1, inVertexStream.ReadUnknown1B, (v, i) => outVertexStream.WriteUnknown1B(v)); * unknown1BPosition++; * } * } * } * * // Get to the end of Unknown1B in H3 data * inputStream.Position = basePosition + originalCount * 0x24; * * CurrentWaterBuffer++; * * break; */ case VertexBufferFormat.ParticleModel: ConvertVertices(count, inVertexStream.ReadParticleModelVertex, (v, i) => { v.Normal = ConvertVectorSpace(v.Normal); outVertexStream.WriteParticleModelVertex(v); }); break; case VertexBufferFormat.TinyPosition: ConvertVertices(count, inVertexStream.ReadTinyPositionVertex, (v, i) => { v.Position = ConvertPositionShort(v.Position); v.Variant = (ushort)((v.Variant >> 8) & 0xFF); v.Normal = ConvertNormal(v.Normal); outVertexStream.WriteTinyPositionVertex(v); }); break; default: throw new NotSupportedException(vertexBuffer.Format.ToString()); } // set proper size of vertices vertexBuffer.VertexSize = (short)outVertexStream.GetVertexSize(vertexBuffer.Format); // set data vertexBuffer.Data.Data = outputStream.ToArray(); } }