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);
        }
예제 #2
0
        /// <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());
        }