/// <summary> /// Initializes a new instance of the <see cref="MeshReader"/> class. /// </summary> /// <param name="version">The engine version to target.</param> /// <param name="mesh">The mesh.</param> public MeshReader(CacheVersion version, Mesh mesh) { _version = version; Mesh = mesh; VertexStreams = new VertexBufferDefinition[StreamCount]; IndexBuffers = new IndexBufferDefinition[IndexBufferCount]; BindVertexStreams(); BindIndexBuffers(); }
/// <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)); }
/// <summary> /// Initializes a new instance of the <see cref="MeshReader"/> class. /// </summary> /// <param name="version">The engine version to target.</param> /// <param name="mesh">The mesh.</param> /// <param name="definition">The mesh's definition data.</param> public MeshReader(DefinitionSet version, Mesh mesh, RenderGeometryResourceDefinition definition) { _version = version; Mesh = mesh; Definition = definition; VertexStreams = new VertexBufferDefinition[StreamCount]; IndexBuffers = new IndexBufferDefinition[IndexBufferCount]; BindVertexStreams(); BindIndexBuffers(); }
/// <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(); } }