public void ConvertIndexBuffer(RenderGeometryApiResourceDefinition resourceDefinition, Stream inputStream, Stream outputStream, int indexBufferIndex)
        {
            var indexBuffer = resourceDefinition.IndexBuffers[indexBufferIndex].Definition;

            var indexCount = indexBuffer.Data.Size / 2;

            var inIndexStream = new IndexBufferStream(
                inputStream,
                EndianFormat.BigEndian);

            var outIndexStream = new IndexBufferStream(
                outputStream,
                EndianFormat.LittleEndian);

            StreamUtil.Align(outputStream, 4);
            indexBuffer.Data.Address = new CacheAddress(CacheAddressType.Resource, (int)outputStream.Position);

            for (var j = 0; j < indexCount; j++)
            {
                outIndexStream.WriteIndex(inIndexStream.ReadIndex());
            }

            resourceDefinition.IndexBuffers[indexBufferIndex].DefinitionAddress = 0;
            resourceDefinition.IndexBuffers[indexBufferIndex].RuntimeAddress    = 0;
        }
예제 #2
0
        private void BuildResourceData(TagSerializer serializer, Stream resourceDataStream)
        {
            var definition = new RenderGeometryApiResourceDefinition
            {
                VertexBuffers = new TagBlock <D3DStructure <VertexBufferDefinition> >(),
                IndexBuffers  = new TagBlock <D3DStructure <IndexBufferDefinition> >()
            };

            definition.IndexBuffers.AddressType  = CacheAddressType.Definition;
            definition.VertexBuffers.AddressType = CacheAddressType.Definition;
            foreach (var mesh in Meshes)
            {
                // Serialize the mesh's vertex buffer
                var vertexBufferStart  = (int)resourceDataStream.Position;
                var vertexCount        = SerializeVertexBuffer(mesh, resourceDataStream);
                var vertexBufferEnd    = (int)resourceDataStream.Position;
                var vertexBufferStream = new MemoryStream();

                resourceDataStream.Position = vertexBufferStart;
                resourceDataStream.CopyTo(vertexBufferStream, vertexBufferEnd - vertexBufferStart);

                // Add a definition for it
                mesh.Mesh.VertexBufferIndices[0] = (short)definition.VertexBuffers.Count;
                definition.VertexBuffers.Add(new D3DStructure <VertexBufferDefinition>
                {
                    Definition = new VertexBufferDefinition
                    {
                        Count      = vertexCount,
                        Format     = mesh.VertexFormat,
                        VertexSize = VertexSizes[mesh.VertexFormat],
                        Data       = new TagData(vertexBufferStream.ToArray()),
                    },
                });

                // Serialize the mesh's index buffer
                var indexBufferStart = vertexBufferEnd;
                SerializeIndexBuffer(mesh, resourceDataStream);
                var indexBufferEnd = (int)resourceDataStream.Position;

                var indexBufferStream = new MemoryStream();

                resourceDataStream.Position = indexBufferStart;
                resourceDataStream.CopyTo(indexBufferStream, indexBufferEnd - indexBufferStart);

                // Add a definition for it
                mesh.Mesh.IndexBufferIndices[0] = (short)definition.IndexBuffers.Count;
                definition.IndexBuffers.Add(new D3DStructure <IndexBufferDefinition>
                {
                    Definition = new IndexBufferDefinition
                    {
                        Format = (IndexBufferFormat)Enum.Parse(typeof(IndexBufferFormat), mesh.Mesh.IndexBufferType.ToString()),
                        Data   = new TagData(indexBufferStream.ToArray()),
                    },
                });
            }
            SerializeDefinitionData(definition);
            resourceDataStream.Position = 0;
        }
예제 #3
0
 /// <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(CacheVersion version, Mesh mesh, RenderGeometryApiResourceDefinition definition)
 {
     _version      = version;
     Mesh          = mesh;
     Definition    = definition;
     VertexStreams = new VertexBufferDefinition[StreamCount];
     IndexBuffers  = new IndexBufferDefinition[IndexBufferCount];
     BindVertexStreams();
     BindIndexBuffers();
 }
예제 #4
0
        private static RenderGeometryApiResourceDefinition GetSingleMeshResourceDefinition(RenderGeometry renderGeometry, int meshindex)
        {
            RenderGeometryApiResourceDefinition result = new RenderGeometryApiResourceDefinition
            {
                IndexBuffers  = new TagBlock <D3DStructure <IndexBufferDefinition> >(),
                VertexBuffers = new TagBlock <D3DStructure <VertexBufferDefinition> >()
            };

            // valid for gen3, InteropLocations should also point to the definition.
            result.IndexBuffers.AddressType  = CacheAddressType.Definition;
            result.VertexBuffers.AddressType = CacheAddressType.Definition;

            var mesh = renderGeometry.Meshes[meshindex];

            for (int i = 0; i < mesh.ResourceVertexBuffers.Length; i++)
            {
                var vertexBuffer = mesh.ResourceVertexBuffers[i];
                if (vertexBuffer != null)
                {
                    var d3dPointer = new D3DStructure <VertexBufferDefinition>();
                    d3dPointer.Definition = vertexBuffer;
                    result.VertexBuffers.Add(d3dPointer);
                    mesh.VertexBufferIndices[i] = (short)(result.VertexBuffers.Elements.Count - 1);
                }
                else
                {
                    mesh.VertexBufferIndices[i] = -1;
                }
            }

            for (int i = 0; i < mesh.ResourceIndexBuffers.Length; i++)
            {
                var indexBuffer = mesh.ResourceIndexBuffers[i];
                if (indexBuffer != null)
                {
                    var d3dPointer = new D3DStructure <IndexBufferDefinition>();
                    d3dPointer.Definition = indexBuffer;
                    result.IndexBuffers.Add(d3dPointer);
                    mesh.IndexBufferIndices[i] = (short)(result.IndexBuffers.Elements.Count - 1);
                }
                else
                {
                    mesh.IndexBufferIndices[i] = -1;
                }
            }

            // if the mesh is unindexed the index in the index buffer should be 0, but the buffer is empty. Copying what h3\ho does.
            if (mesh.Flags.HasFlag(MeshFlags.MeshIsUnindexed))
            {
                mesh.IndexBufferIndices[0] = 0;
                mesh.IndexBufferIndices[1] = 0;
            }

            return(result);
        }
예제 #5
0
        private void BuildResourceData(TagSerializer serializer, Stream resourceDataStream)
        {
            var definition = new RenderGeometryApiResourceDefinition
            {
                VertexBuffers = new List <TagStructureReference <VertexBufferDefinition> >(),
                IndexBuffers  = new List <TagStructureReference <IndexBufferDefinition> >()
            };

            foreach (var mesh in Meshes)
            {
                // Serialize the mesh's vertex buffer
                var vertexBufferStart = (int)resourceDataStream.Position;
                var vertexCount       = SerializeVertexBuffer(mesh, resourceDataStream);
                var vertexBufferEnd   = (int)resourceDataStream.Position;

                // Add a definition for it
                mesh.Mesh.VertexBufferIndices[0] = (ushort)definition.VertexBuffers.Count;
                definition.VertexBuffers.Add(new TagStructureReference <VertexBufferDefinition>
                {
                    Definition = new VertexBufferDefinition
                    {
                        Count      = vertexCount,
                        Format     = mesh.VertexFormat,
                        VertexSize = VertexSizes[mesh.VertexFormat],
                        Data       = new TagData(vertexBufferEnd - vertexBufferStart, new CacheResourceAddress(CacheResourceAddressType.Resource, vertexBufferStart)),
                    },
                });

                // Serialize the mesh's index buffer
                var indexBufferStart = vertexBufferEnd;
                SerializeIndexBuffer(mesh, resourceDataStream);
                var indexBufferEnd = (int)resourceDataStream.Position;

                // Add a definition for it
                mesh.Mesh.IndexBufferIndices[0] = (ushort)definition.IndexBuffers.Count;
                definition.IndexBuffers.Add(new TagStructureReference <IndexBufferDefinition>
                {
                    Definition = new IndexBufferDefinition
                    {
                        Format = (IndexBufferFormat)Enum.Parse(typeof(IndexBufferFormat), mesh.Mesh.IndexBufferType.ToString()),
                        Data   = new TagData(indexBufferEnd - indexBufferStart, new CacheResourceAddress(CacheResourceAddressType.Resource, indexBufferStart)),
                    },
                });
            }
            SerializeDefinitionData(serializer, definition);
            resourceDataStream.Position = 0;
        }
예제 #6
0
        private void SerializeDefinitionData(TagSerializer serializer, RenderGeometryApiResourceDefinition definition)
        {
            _model.Geometry.Resource = new PageableResource
            {
                Page     = new RawPage(),
                Resource = new TagResourceGen3
                {
                    ResourceType             = TagResourceTypeGen3.RenderGeometry,
                    ResourceFixups           = new List <TagResourceGen3.ResourceFixup>(),
                    ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(),
                    Unknown2 = 1
                }
            };
            var context = new ResourceSerializationContext(CacheContext, _model.Geometry.Resource);

            serializer.Serialize(context, definition);
        }
예제 #7
0
        public ModelExtractor(HaloOnlineCacheContext cacheContext, RenderModel renderModel)
        {
            Scene = new Scene();

            CacheContext = cacheContext;
            RenderModel  = renderModel;

            MeshMapping = new Dictionary <int, int>();
            BoneNodes   = new List <Node>();

            // Deserialize the render_model resource
            var resourceContext = new ResourceSerializationContext(CacheContext, RenderModel.Geometry.Resource);

            RenderModelResourceDefinition = CacheContext.Deserializer.Deserialize <RenderGeometryApiResourceDefinition>(resourceContext);
            RenderModelResourceStream     = new MemoryStream();
            CacheContext.ExtractResource(RenderModel.Geometry.Resource, RenderModelResourceStream);
        }
        public ushort CreateIndexBuffer(RenderGeometryApiResourceDefinition resourceDefinition, Stream outputStream, List <ushort> buffer)
        {
            resourceDefinition.IndexBuffers.Add(new TagStructureReference <IndexBufferDefinition>
            {
                Definition = new IndexBufferDefinition
                {
                    Format = IndexBufferFormat.TriangleStrip,
                    Data   = new TagData
                    {
                        Size     = buffer.Count() * 2,
                        Unused4  = 0,
                        Unused8  = 0,
                        Address  = new CacheAddress(),
                        Unused10 = 0
                    }
                }
            });

            var indexBuffer = resourceDefinition.IndexBuffers.Last().Definition;

            var indexCount = indexBuffer.Data.Size / 2;

            var outIndexStream = new EndianWriter(
                outputStream,
                EndianFormat.LittleEndian);

            StreamUtil.Align(outputStream, 4);
            indexBuffer.Data.Address = new CacheAddress(CacheAddressType.Resource, (int)outputStream.Position);

            for (var j = 0; j < indexCount; j++)
            {
                outIndexStream.Write((short)buffer[j]);
            }

            return((ushort)resourceDefinition.IndexBuffers.IndexOf(resourceDefinition.IndexBuffers.Last()));
        }
예제 #9
0
        public override object Execute(List <string> args)
        {
            if (args.Count < 2)
            {
                return(false);
            }

            //
            // Verify Blam tag instance
            //

            var unitName = args[0].ToLower();

            if (unitName != "spartan" && unitName != "elite")
            {
                Console.WriteLine("ERROR: Only 'spartan' and 'elite' armor variants are allowed.");
                return(false);
            }

            args.RemoveAt(0);

            var blamTagName = unitName == "spartan" ?
                              @"objects\characters\masterchief\mp_masterchief\mp_masterchief" :
                              @"objects\characters\elite\mp_elite\mp_elite";

            Console.Write($"Verifying {blamTagName}.render_model...");

            CacheFile.IndexItem blamTag = null;

            foreach (var tag in BlamCache.IndexItems)
            {
                if ((tag.GroupTag == "mode") && (tag.Name == blamTagName))
                {
                    blamTag = tag;
                    break;
                }
            }

            if (blamTag == null)
            {
                Console.WriteLine($"ERROR: Blam tag does not exist: {blamTagName}.render_model");
                return(true);
            }

            Console.WriteLine("done.");

            //
            // Load the Blam tag definition
            //

            var variantName = args[0];

            args.RemoveAt(0);

            CachedTagInstance edModeTag = null;
            var isScenery   = false;
            var regionNames = new List <string>();

            while (args.Count != 0)
            {
                switch (args[0].ToLower())
                {
                case "scenery":
                    isScenery = true;
                    args.RemoveAt(0);
                    break;

                case "replace:":
                    edModeTag = CacheContext.GetTag(args[1]);
                    args.RemoveAt(1);
                    args.RemoveAt(0);
                    break;

                case "regions:":
                    regionNames.AddRange(args.Skip(1));
                    args.Clear();
                    break;

                default:
                    throw new InvalidDataException($"{args}");
                }
            }

            var blamContext      = new CacheSerializationContext(ref BlamCache, blamTag);
            var edModeDefinition = BlamCache.Deserializer.Deserialize <RenderModel>(blamContext);

            var materials = edModeDefinition.Materials.Select(i => new RenderMaterial
            {
                BreakableSurfaceIndex = i.BreakableSurfaceIndex,
                Properties            = i.Properties,
                RenderMethod          = i.RenderMethod,
                Skins    = i.Skins,
                Unknown  = i.Unknown,
                Unknown2 = i.Unknown2,
                Unknown3 = i.Unknown3,
                Unknown4 = i.Unknown4
            }).ToList();

            edModeDefinition = (RenderModel)ConvertData(null, edModeDefinition, false);

            var variantRegions = new List <RenderModel.Region>();

            foreach (var region in edModeDefinition.Regions)
            {
                if (regionNames.Count != 0 && !regionNames.Contains(CacheContext.GetString(region.Name)))
                {
                    continue;
                }

                var variantRegion = new RenderModel.Region
                {
                    Name         = region.Name,
                    Permutations = new List <RenderModel.Region.Permutation>()
                };

                foreach (var permutation in region.Permutations)
                {
                    if (variantName == CacheContext.GetString(permutation.Name))
                    {
                        variantRegion.Permutations.Add(permutation);
                    }
                }

                variantRegions.Add(variantRegion);
            }

            var variantMeshes        = new List <int>();
            var variantMaterials     = new List <int>();
            var variantVertexBuffers = new List <int>();
            var variantIndexBuffers  = new List <int>();

            foreach (var region in variantRegions)
            {
                foreach (var permutation in region.Permutations)
                {
                    for (var i = permutation.MeshIndex; i < (short)(permutation.MeshIndex + permutation.MeshCount); i++)
                    {
                        var mesh = edModeDefinition.Geometry.Meshes[i];

                        foreach (var part in mesh.Parts)
                        {
                            if (part.MaterialIndex != -1 && !variantMaterials.Contains(part.MaterialIndex))
                            {
                                variantMaterials.Add(part.MaterialIndex);
                            }
                        }

                        foreach (var vertexBuffer in mesh.VertexBufferIndices)
                        {
                            if (vertexBuffer != ushort.MaxValue && !variantVertexBuffers.Contains(vertexBuffer))
                            {
                                variantVertexBuffers.Add(vertexBuffer);
                            }
                        }

                        foreach (var indexBuffer in mesh.IndexBufferIndices)
                        {
                            if (indexBuffer != ushort.MaxValue && !variantIndexBuffers.Contains(indexBuffer))
                            {
                                variantIndexBuffers.Add(indexBuffer);
                            }
                        }

                        if (!variantMeshes.Contains(i))
                        {
                            variantMeshes.Add(i);
                        }
                    }
                }
            }

            variantMeshes.Sort();
            variantMaterials.Sort();
            variantVertexBuffers.Sort();
            variantIndexBuffers.Sort();

            foreach (var meshIndex in variantMeshes)
            {
                var mesh = edModeDefinition.Geometry.Meshes[meshIndex];

                foreach (var part in mesh.Parts)
                {
                    if (part.MaterialIndex != -1)
                    {
                        part.MaterialIndex = (short)variantMaterials.IndexOf(part.MaterialIndex);
                    }
                }
            }

            foreach (var region in variantRegions)
            {
                foreach (var permutation in region.Permutations)
                {
                    if (permutation.MeshIndex != -1)
                    {
                        permutation.MeshIndex = (short)variantMeshes.IndexOf(permutation.MeshIndex);
                    }
                }
            }

            foreach (var meshIndex in variantMeshes)
            {
                var mesh = edModeDefinition.Geometry.Meshes[meshIndex];

                for (var i = 0; i < mesh.VertexBufferIndices.Length; i++)
                {
                    if (!variantVertexBuffers.Contains(mesh.VertexBufferIndices[i]))
                    {
                        mesh.VertexBufferIndices[i] = ushort.MaxValue;
                    }
                    else
                    {
                        mesh.VertexBufferIndices[i] = (ushort)variantVertexBuffers.IndexOf(mesh.VertexBufferIndices[i]);
                    }
                }

                for (var i = 0; i < mesh.IndexBufferIndices.Length; i++)
                {
                    if (!variantIndexBuffers.Contains(mesh.IndexBufferIndices[i]))
                    {
                        mesh.IndexBufferIndices[i] = ushort.MaxValue;
                    }
                    else
                    {
                        mesh.IndexBufferIndices[i] = (ushort)variantIndexBuffers.IndexOf(mesh.IndexBufferIndices[i]);
                    }
                }
            }

            edModeDefinition.Regions         = variantRegions;
            edModeDefinition.Geometry.Meshes = edModeDefinition.Geometry.Meshes.Where(i => variantMeshes.Contains(edModeDefinition.Geometry.Meshes.IndexOf(i))).ToList();

            //
            // Port Blam render_model materials
            //

            materials = materials.Where(i => variantMaterials.Contains(materials.IndexOf(i))).ToList();

            using (var stream = CacheContext.OpenTagCacheReadWrite())
            {
                for (var i = 0; i < materials.Count; i++)
                {
                    var material = materials[i];

                    if (material.RenderMethod.Index == -1)
                    {
                        continue;
                    }

                    var blamRenderMethod    = materials[i].RenderMethod;
                    var blamRenderMethodTag = BlamCache.IndexItems.GetItemByID(blamRenderMethod.Index);

                    var renderMethodExists = false;

                    foreach (var instance in CacheContext.TagCache.Index.FindAllInGroup("rm  "))
                    {
                        if (instance?.Name == blamRenderMethodTag.Name)
                        {
                            renderMethodExists    = true;
                            material.RenderMethod = instance;
                            break;
                        }
                    }

                    if (!renderMethodExists)
                    {
                        material.RenderMethod = CacheContext.GetTag <Shader>(@"shaders\invalid");
                    }
                }
            }

            edModeDefinition.Materials = materials;

            //
            // Load Blam resource data
            //

            var resourceData = BlamCache.GetRawFromID(edModeDefinition.Geometry.ZoneAssetHandle);

            if (resourceData == null)
            {
                Console.WriteLine("Blam render_geometry resource contains no data.");
                return(true);
            }

            //
            // Load Blam resource definition
            //

            Console.Write("Loading Blam render_geometry resource definition...");

            var definitionEntry = BlamCache.ResourceGestalt.TagResources[edModeDefinition.Geometry.ZoneAssetHandle.Index];

            var resourceDefinition = new RenderGeometryApiResourceDefinition
            {
                VertexBuffers = new List <TagStructureReference <VertexBufferDefinition> >(),
                IndexBuffers  = new List <TagStructureReference <IndexBufferDefinition> >()
            };

            using (var definitionStream = new MemoryStream(BlamCache.ResourceGestalt.FixupInformation))
                using (var definitionReader = new EndianReader(definitionStream, EndianFormat.BigEndian))
                {
                    var dataContext = new DataSerializationContext(definitionReader, null, CacheResourceAddressType.Definition);

                    definitionReader.SeekTo(definitionEntry.FixupInformationOffset + (definitionEntry.FixupInformationLength - 24));

                    var vertexBufferCount = definitionReader.ReadInt32();
                    definitionReader.Skip(8);
                    var indexBufferCount = definitionReader.ReadInt32();

                    definitionReader.SeekTo(definitionEntry.FixupInformationOffset);

                    for (var i = 0; i < vertexBufferCount; i++)
                    {
                        resourceDefinition.VertexBuffers.Add(new TagStructureReference <VertexBufferDefinition>
                        {
                            Definition = new VertexBufferDefinition
                            {
                                Count      = definitionReader.ReadInt32(),
                                Format     = (VertexBufferFormat)definitionReader.ReadInt16(),
                                VertexSize = definitionReader.ReadInt16(),
                                Data       = new TagData
                                {
                                    Size     = definitionReader.ReadInt32(),
                                    Unused4  = definitionReader.ReadInt32(),
                                    Unused8  = definitionReader.ReadInt32(),
                                    Address  = new CacheResourceAddress(CacheResourceAddressType.Memory, definitionReader.ReadInt32()),
                                    Unused10 = definitionReader.ReadInt32()
                                }
                            }
                        });
                    }

                    definitionReader.Skip(vertexBufferCount * 12);

                    for (var i = 0; i < indexBufferCount; i++)
                    {
                        resourceDefinition.IndexBuffers.Add(new TagStructureReference <IndexBufferDefinition>
                        {
                            Definition = new IndexBufferDefinition
                            {
                                Format = (IndexBufferFormat)definitionReader.ReadInt32(),
                                Data   = new TagData
                                {
                                    Size     = definitionReader.ReadInt32(),
                                    Unused4  = definitionReader.ReadInt32(),
                                    Unused8  = definitionReader.ReadInt32(),
                                    Address  = new CacheResourceAddress(CacheResourceAddressType.Memory, definitionReader.ReadInt32()),
                                    Unused10 = definitionReader.ReadInt32()
                                }
                            }
                        });
                    }
                }

            Console.WriteLine("done.");

            //
            // Convert Blam resource data
            //

            using (var edResourceStream = new MemoryStream())
            {
                //
                // Convert Blam render_geometry_api_resource_definition
                //

                using (var blamResourceStream = new MemoryStream(resourceData))
                {
                    //
                    // Convert Blam vertex buffers
                    //

                    Console.Write("Converting vertex buffers...");

                    var previousVertexBufferCount = -1;

                    for (var i = 0; i < resourceDefinition.VertexBuffers.Count; i++)
                    {
                        if (!variantVertexBuffers.Contains(i))
                        {
                            continue;
                        }

                        blamResourceStream.Position = definitionEntry.ResourceFixups[i].Offset;
                        if (i > 0)
                        {
                            previousVertexBufferCount = resourceDefinition.VertexBuffers[i - 1].Definition.Count;
                        }
                        GeometryConverter.ConvertVertexBuffer(resourceDefinition, blamResourceStream, edResourceStream, i, previousVertexBufferCount);
                    }

                    Console.WriteLine("done.");

                    //
                    // Convert Blam index buffers
                    //

                    Console.Write("Converting index buffers...");

                    for (var i = 0; i < resourceDefinition.IndexBuffers.Count; i++)
                    {
                        if (!variantIndexBuffers.Contains(i))
                        {
                            continue;
                        }

                        blamResourceStream.Position = definitionEntry.ResourceFixups[resourceDefinition.VertexBuffers.Count * 2 + i].Offset;
                        GeometryConverter.ConvertIndexBuffer(resourceDefinition, blamResourceStream, edResourceStream, i);
                    }

                    Console.WriteLine("done.");
                }

                resourceDefinition.VertexBuffers = resourceDefinition.VertexBuffers.Where(i => variantVertexBuffers.Contains(resourceDefinition.VertexBuffers.IndexOf(i))).ToList();
                resourceDefinition.IndexBuffers  = resourceDefinition.IndexBuffers.Where(i => variantIndexBuffers.Contains(resourceDefinition.IndexBuffers.IndexOf(i))).ToList();

                //
                // Finalize the new ElDorado geometry resource
                //

                Console.Write("Writing resource data...");

                edModeDefinition.Geometry.Resource = new PageableResource
                {
                    Page     = new RawPage(),
                    Resource = new TagResourceGen3
                    {
                        ResourceType             = TagResourceTypeGen3.RenderGeometry,
                        ResourceFixups           = new List <TagResourceGen3.ResourceFixup>(),
                        ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(),
                        Unknown2 = 1
                    }
                };

                edResourceStream.Position = 0;

                var resourceContext = new ResourceSerializationContext(CacheContext, edModeDefinition.Geometry.Resource);
                CacheContext.Serializer.Serialize(resourceContext, resourceDefinition);
                edModeDefinition.Geometry.Resource.ChangeLocation(ResourceLocation.ResourcesB);
                CacheContext.AddResource(edModeDefinition.Geometry.Resource, edResourceStream);

                Console.WriteLine("done.");
            }

            edModeDefinition.Name = CacheContext.GetStringId(variantName);

            if (edModeTag == null)
            {
                for (var i = 0; i < CacheContext.TagCache.Index.Count; i++)
                {
                    if (CacheContext.TagCache.Index[i] == null)
                    {
                        CacheContext.TagCache.Index[i] = edModeTag = new CachedTagInstance(i, TagGroup.Instances[new Tag("mode")]);
                        break;
                    }
                }

                if (edModeTag == null)
                {
                    edModeTag = CacheContext.TagCache.AllocateTag(TagGroup.Instances[new Tag("mode")]);
                }
            }

            //
            // Create a new armor model tag
            //

            Model             edHlmtDefinition = null;
            CachedTagInstance edHlmtTag        = null;

            if (isScenery)
            {
                Console.Write($"Verifying {blamTagName}.model...");

                CacheFile.IndexItem blamHlmtTag = null;

                foreach (var tag in BlamCache.IndexItems)
                {
                    if ((tag.GroupTag == "hlmt") && (tag.Name == blamTagName))
                    {
                        blamHlmtTag = tag;
                        break;
                    }
                }

                if (blamHlmtTag == null)
                {
                    Console.WriteLine($"ERROR: Blam tag does not exist: {blamTagName}.model");
                    return(true);
                }

                Console.WriteLine("done.");

                blamContext      = new CacheSerializationContext(ref BlamCache, blamHlmtTag);
                edHlmtDefinition = (Model)ConvertData(null, BlamCache.Deserializer.Deserialize <Model>(blamContext), false);

                edHlmtDefinition.RenderModel        = edModeTag;
                edHlmtDefinition.ReduceToL1SuperLow = 36.38004f;
                edHlmtDefinition.ReduceToL2Low      = 27.28503f;
                edHlmtDefinition.Variants           = new List <Model.Variant>();
                edHlmtDefinition.Materials          = new List <Model.Material>();
                edHlmtDefinition.NewDamageInfo      = new List <Model.GlobalDamageInfoBlock>();
                edHlmtDefinition.Targets            = new List <Model.Target>();

                var collisionRegions = new List <Model.CollisionRegion>();

                foreach (var collisionRegion in edHlmtDefinition.CollisionRegions)
                {
                    var found = false;

                    foreach (var variantRegion in variantRegions)
                    {
                        if (collisionRegion.Name == variantRegion.Name)
                        {
                            found = true;
                            break;
                        }
                    }

                    if (!found)
                    {
                        continue;
                    }

                    found = false;

                    foreach (var permutation in collisionRegion.Permutations)
                    {
                        if (permutation.Name == CacheContext.GetStringId(variantName))
                        {
                            found = true;
                            break;
                        }
                    }

                    if (found)
                    {
                        collisionRegions.Add(collisionRegion);
                    }
                }

                foreach (var collisionRegion in collisionRegions)
                {
                    Model.CollisionRegion.Permutation permutation = null;

                    foreach (var collisionPermutation in collisionRegion.Permutations)
                    {
                        if (collisionPermutation.Name == CacheContext.GetStringId(variantName))
                        {
                            permutation = collisionPermutation;
                            break;
                        }
                    }

                    if (permutation == null)
                    {
                        throw new KeyNotFoundException();
                    }

                    collisionRegion.Permutations = new List <Model.CollisionRegion.Permutation> {
                        permutation
                    };
                }

                edHlmtDefinition.CollisionRegions = collisionRegions;

                for (var i = 0; i < CacheContext.TagCache.Index.Count; i++)
                {
                    if (CacheContext.TagCache.Index[i] == null)
                    {
                        CacheContext.TagCache.Index[i] = edHlmtTag = new CachedTagInstance(i, TagGroup.Instances[new Tag("hlmt")]);
                        break;
                    }
                }

                if (edHlmtTag == null)
                {
                    edHlmtTag = CacheContext.TagCache.AllocateTag(TagGroup.Instances[new Tag("hlmt")]);
                }
            }

            //
            // Create a new armor scenery tag
            //

            Scenery           edScenDefinition = null;
            CachedTagInstance edScenTag        = null;

            if (isScenery)
            {
                edScenDefinition = new Scenery
                {
                    ObjectType = new GameObjectType
                    {
                        Halo2       = GameObjectTypeHalo2.Scenery,
                        Halo3Retail = GameObjectTypeHalo3Retail.Scenery,
                        Halo3ODST   = GameObjectTypeHalo3ODST.Scenery,
                        HaloOnline  = GameObjectTypeHaloOnline.Scenery
                    },
                    BoundingRadius    = 0.44f,
                    BoundingOffset    = new RealPoint3d(-0.02f, 0.0f, 0.0f),
                    AccelerationScale = 1.2f,
                    SweetenerSize     = GameObject.SweetenerSizeValue.Medium,
                    Model             = edHlmtTag,
                    ChangeColors      = new List <GameObject.ChangeColor>
                    {
                        new GameObject.ChangeColor(),
                        new GameObject.ChangeColor(),
                        new GameObject.ChangeColor(),
                        new GameObject.ChangeColor(),
                        new GameObject.ChangeColor()
                    },
                    NodeMaps = new List <GameObject.NodeMap>()
                };

                for (sbyte i = 0; i < 51; i++)
                {
                    edScenDefinition.NodeMaps.Add(new GameObject.NodeMap {
                        TargetNode = i
                    });
                }

                for (var i = 0; i < CacheContext.TagCache.Index.Count; i++)
                {
                    if (CacheContext.TagCache.Index[i] == null)
                    {
                        CacheContext.TagCache.Index[i] = edScenTag = new CachedTagInstance(i, TagGroup.Instances[new Tag("scen")]);
                        break;
                    }
                }

                if (edScenTag == null)
                {
                    edScenTag = CacheContext.TagCache.AllocateTag(TagGroup.Instances[new Tag("scen")]);
                }
            }

            //
            // Serialize new ElDorado tag definitions
            //

            using (var cacheStream = CacheContext.OpenTagCacheReadWrite())
            {
                CacheContext.Serialize(cacheStream, edModeTag, edModeDefinition);
                edModeTag.Name = isScenery ?
                                 (unitName == "spartan" ?
                                  $@"objects\characters\masterchief\mp_masterchief\armor\{variantName}" :
                                  $@"objects\characters\elite\mp_elite\armor\{variantName}") :
                                 (unitName == "spartan" ?
                                  @"objects\characters\masterchief\mp_masterchief\mp_masterchief" :
                                  @"objects\characters\elite\mp_elite\mp_elite");

                if (isScenery)
                {
                    CacheContext.Serialize(cacheStream, edHlmtTag, edHlmtDefinition);
                    CacheContext.Serialize(cacheStream, edScenTag, edScenDefinition);
                    edScenTag.Name = unitName == "spartan" ?
                                     $@"objects\characters\masterchief\mp_masterchief\armor\{variantName}" :
                                     $@"objects\characters\elite\mp_elite\armor\{variantName}";
                }
            }

            return(true);
        }
예제 #10
0
 private void SerializeDefinitionData(RenderGeometryApiResourceDefinition definition)
 {
     _model.Geometry.Resource = CacheContext.ResourceCache.CreateRenderGeometryApiResource(definition);
 }
예제 #11
0
 public abstract TagResourceReference CreateRenderGeometryApiResource(RenderGeometryApiResourceDefinition renderGeometryDefinition);
예제 #12
0
        private bool ExtractObj(string variantName, FileInfo modelFile, RenderModel renderModel, RenderGeometryApiResourceDefinition resourceDefinition, Stream resourceStream)
        {
            var meshes           = new Dictionary <string, Mesh>();
            var vertexCompressor = new VertexCompressor(renderModel.Geometry.Compression[0]);

            foreach (var region in renderModel.Regions)
            {
                var regionName = CacheContext.GetString(region.Name);

                foreach (var permutation in region.Permutations)
                {
                    var permutationName = CacheContext.GetString(permutation.Name);

                    if (variantName != "*" && variantName != permutationName)
                    {
                        continue;
                    }

                    for (var i = 0; i < permutation.MeshCount; i++)
                    {
                        var name = $"{regionName}_{permutationName}_{i}";
                        meshes[name] = renderModel.Geometry.Meshes[permutation.MeshIndex + i];
                    }
                }
            }

            if (meshes.Count == 0)
            {
                Console.WriteLine($"ERROR: No meshes found under variant '{variantName}'!");
                return(false);
            }

            Console.Write("Extracting {0} mesh(es)...", meshes.Count);

            using (var objFile = new StreamWriter(modelFile.Create()))
            {
                var objExtractor = new ObjExtractor(objFile);

                foreach (var entry in meshes)
                {
                    var meshReader = new MeshReader(CacheContext.Version, entry.Value, resourceDefinition);
                    objExtractor.ExtractMesh(meshReader, vertexCompressor, resourceStream, entry.Key);
                }

                objExtractor.Finish();
            }

            Console.WriteLine("done!");

            return(true);
        }
 public override TagResourceReference CreateRenderGeometryApiResource(RenderGeometryApiResourceDefinition renderGeometryDefinition)
 {
     return(CreateResource(renderGeometryDefinition, ResourceLocation.Resources, TagResourceTypeGen3.RenderGeometry));
 }
        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);
        }
        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;
        }
예제 #16
0
        private void ExtractObj(FileInfo modelFile, RenderModel renderModel, Model.Variant modelVariant, RenderGeometryApiResourceDefinition resourceDefinition, Stream resourceStream)
        {
            using (var objFile = new StreamWriter(modelFile.Create()))
            {
                var objExtractor = new ObjExtractor(objFile);

                // Create a (de)compressor from the first compression block
                var vertexCompressor = new VertexCompressor(renderModel.Geometry.Compression[0]);

                if (modelVariant != null)
                {
                    // Extract each region in the variant
                    foreach (var region in modelVariant.Regions)
                    {
                        // Get the corresonding region in the render model tag
                        if (region.RenderModelRegionIndex >= renderModel.Regions.Count)
                        {
                            continue;
                        }

                        var renderModelRegion = renderModel.Regions[region.RenderModelRegionIndex];

                        // Get the corresponding permutation in the render model tag
                        // (Just extract the first permutation for now)
                        if (region.Permutations.Count == 0)
                        {
                            continue;
                        }

                        var permutation = region.Permutations[0];

                        if (permutation.RenderModelPermutationIndex < 0 ||
                            permutation.RenderModelPermutationIndex >= renderModelRegion.Permutations.Count)
                        {
                            continue;
                        }

                        var renderModelPermutation = renderModelRegion.Permutations[permutation.RenderModelPermutationIndex];

                        // Extract each mesh in the permutation
                        var meshIndex       = renderModelPermutation.MeshIndex;
                        var meshCount       = renderModelPermutation.MeshCount;
                        var regionName      = CacheContext.GetString(region.Name) ?? region.Name.ToString();
                        var permutationName = CacheContext.GetString(permutation.Name) ?? permutation.Name.ToString();

                        Console.WriteLine("Extracting {0} mesh(es) for {1}:{2}...", meshCount, regionName, permutationName);

                        for (var i = 0; i < meshCount; i++)
                        {
                            // Create a MeshReader for the mesh and pass it to the obj extractor
                            var meshReader = new MeshReader(CacheContext.Version, renderModel.Geometry.Meshes[meshIndex + i], resourceDefinition);
                            objExtractor.ExtractMesh(meshReader, vertexCompressor, resourceStream);
                        }
                    }
                }
                else
                {
                    // No variant - just extract every mesh
                    Console.WriteLine("Extracting {0} mesh(es)...", renderModel.Geometry.Meshes.Count);

                    foreach (var mesh in renderModel.Geometry.Meshes)
                    {
                        // Create a MeshReader for the mesh and pass it to the obj extractor
                        var meshReader = new MeshReader(CacheContext.Version, mesh, resourceDefinition);
                        objExtractor.ExtractMesh(meshReader, vertexCompressor, resourceStream);
                    }
                }

                objExtractor.Finish();
            }
        }
예제 #17
0
 public override TagResourceReference CreateRenderGeometryApiResource(RenderGeometryApiResourceDefinition renderGeometryDefinition)
 {
     throw new NotImplementedException();
 }
예제 #18
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());
        }
예제 #19
0
        //
        // Methods
        //

        /// <summary>
        /// Set the runtime VertexBufferResources and IndexBufferResources fields given the resource definition
        /// </summary>
        /// <param name="resourceDefinition"></param>
        public void SetResourceBuffers(RenderGeometryApiResourceDefinition resourceDefinition)
        {
            bool[] convertedVertexBuffers = new bool[resourceDefinition.VertexBuffers.Count];
            bool[] convertedIndexBuffers  = new bool[resourceDefinition.IndexBuffers.Count];

            foreach (var mesh in Meshes)
            {
                mesh.ResourceVertexBuffers = new VertexBufferDefinition[8];
                mesh.ResourceIndexBuffers  = new IndexBufferDefinition[2];

                for (int i = 0; i < mesh.VertexBufferIndices.Length; i++)
                {
                    var vertexBufferIndex = mesh.VertexBufferIndices[i];
                    if (vertexBufferIndex != -1)
                    {
                        if (vertexBufferIndex < resourceDefinition.VertexBuffers.Count)
                        {
                            if (convertedVertexBuffers[vertexBufferIndex] == false)
                            {
                                convertedVertexBuffers[vertexBufferIndex] = true;
                                mesh.ResourceVertexBuffers[i]             = resourceDefinition.VertexBuffers[vertexBufferIndex].Definition;
                            }
                            else
                            {
                                throw new System.Exception("Sharing vertex buffers is not supported");
                            }
                        }

                        else
                        {
                            mesh.ResourceVertexBuffers[i] = null; // happens on sbsp
                        }
                    }
                }

                for (int i = 0; i < mesh.IndexBufferIndices.Length; i++)
                {
                    var indexBufferIndex = mesh.IndexBufferIndices[i];
                    if (indexBufferIndex != -1)
                    {
                        if (indexBufferIndex < resourceDefinition.IndexBuffers.Count)
                        {
                            if (convertedIndexBuffers[indexBufferIndex] == false)
                            {
                                mesh.ResourceIndexBuffers[i]            = resourceDefinition.IndexBuffers[indexBufferIndex].Definition;
                                convertedIndexBuffers[indexBufferIndex] = true;
                            }
                            else
                            {
                                mesh.IndexBufferIndices[i] = -1;
                                System.Console.WriteLine("Sharing index buffers not supported, ignoring it.");
                            }
                        }
                        else
                        {
                            mesh.ResourceIndexBuffers[i] = null; // this happens when loading particle model from gen3, the index buffers are empty but indices are set to 0
                        }
                    }
                }
            }

            for (int i = 0; i < InstancedGeometryPerPixelLighting.Count; i++)
            {
                var vertexBufferIndex = InstancedGeometryPerPixelLighting[i].VertexBufferIndex;
                if (vertexBufferIndex != -1)
                {
                    if (vertexBufferIndex < resourceDefinition.VertexBuffers.Count)
                    {
                        InstancedGeometryPerPixelLighting[i].VertexBuffer = resourceDefinition.VertexBuffers[vertexBufferIndex].Definition;
                    }
                    else
                    {
                        InstancedGeometryPerPixelLighting[i].VertexBuffer = null;
                    }
                }
            }
        }
예제 #20
0
        private void ExtractAmf(FileInfo modelFile, RenderModel renderModel, Model.Variant modelVariant, RenderGeometryApiResourceDefinition resourceDefinition, Stream resourceStream)
        {
            using (var amfStream = modelFile.Create())
                using (var amfWriter = new EndianWriter(amfStream))
                {
                    var dupeDic = new Dictionary <int, int>();

                    var indxAddressList = new List <int>();
                    var indxValueList   = new List <int>();

                    var meshAddressList = new List <int>();
                    var meshValueList   = new List <int>();

                    var regions      = new List <RenderModel.Region>();
                    var permutations = new List <RenderModel.Region.Permutation>();

                    foreach (var variantRegion in modelVariant.Regions)
                    {
                        if (variantRegion.RenderModelRegionIndex == -1)
                        {
                            continue;
                        }

                        var region = renderModel.Regions[variantRegion.RenderModelRegionIndex];
                        var variantPermutations = region.Permutations.Where(i => variantRegion.Permutations.Find(j => j.Name == i.Name) != null).ToList();

                        if (variantPermutations.Count == 0)
                        {
                            continue;
                        }

                        if (!regions.Contains(region))
                        {
                            regions.Add(region);
                        }

                        foreach (var variantPermutation in variantPermutations)
                        {
                            if (!permutations.Contains(variantPermutation))
                            {
                                permutations.Add(variantPermutation);
                            }
                        }
                    }

                    var headerAddressList = new List <int>();
                    var headerValueList   = new List <int>();

                    amfWriter.Write("AMF!".ToCharArray());
                    amfWriter.Write(2.0f); //format version
                    amfWriter.Write((CacheContext.GetString(renderModel.Name) + "\0").ToCharArray());

                    amfWriter.Write(renderModel.Nodes.Count);
                    headerAddressList.Add((int)amfWriter.BaseStream.Position);
                    amfWriter.Write(0);

                    amfWriter.Write(renderModel.MarkerGroups.Count);
                    headerAddressList.Add((int)amfWriter.BaseStream.Position);
                    amfWriter.Write(0);

                    amfWriter.Write(regions.Count);
                    headerAddressList.Add((int)amfWriter.BaseStream.Position);
                    amfWriter.Write(0);

                    amfWriter.Write(renderModel.Materials.Count);
                    headerAddressList.Add((int)amfWriter.BaseStream.Position);
                    amfWriter.Write(0);

                    headerValueList.Add((int)amfWriter.BaseStream.Position);

                    foreach (var node in renderModel.Nodes)
                    {
                        amfWriter.Write((CacheContext.GetString(node.Name) + "\0").ToCharArray());
                        amfWriter.Write(node.ParentNode);
                        amfWriter.Write(node.FirstChildNode);
                        amfWriter.Write(node.NextSiblingNode);
                        amfWriter.Write(node.DefaultTranslation.X * 100);
                        amfWriter.Write(node.DefaultTranslation.Y * 100);
                        amfWriter.Write(node.DefaultTranslation.Z * 100);
                        amfWriter.Write(node.DefaultRotation.I);
                        amfWriter.Write(node.DefaultRotation.J);
                        amfWriter.Write(node.DefaultRotation.K);
                        amfWriter.Write(node.DefaultRotation.W);
                    }

                    var markerAddressList = new List <int>();
                    var markerValueList   = new List <int>();

                    headerValueList.Add((int)amfWriter.BaseStream.Position);

                    foreach (var group in renderModel.MarkerGroups)
                    {
                        amfWriter.Write((CacheContext.GetString(group.Name) + "\0").ToCharArray());
                        amfWriter.Write(group.Markers.Count);
                        markerAddressList.Add((int)amfWriter.BaseStream.Position);
                        amfWriter.Write(0);
                    }

                    foreach (var group in renderModel.MarkerGroups)
                    {
                        markerValueList.Add((int)amfWriter.BaseStream.Position);
                        foreach (var marker in group.Markers)
                        {
                            amfWriter.Write((byte)marker.RegionIndex);
                            amfWriter.Write((byte)marker.PermutationIndex);
                            amfWriter.Write((short)marker.NodeIndex);
                            amfWriter.Write(marker.Translation.X * 100);
                            amfWriter.Write(marker.Translation.Y * 100);
                            amfWriter.Write(marker.Translation.Z * 100);
                            amfWriter.Write(marker.Rotation.I);
                            amfWriter.Write(marker.Rotation.J);
                            amfWriter.Write(marker.Rotation.K);
                            amfWriter.Write(marker.Rotation.W);
                        }
                    }

                    var permAddressList = new List <int>();
                    var permValueList   = new List <int>();

                    headerValueList.Add((int)amfWriter.BaseStream.Position);

                    foreach (var variantRegion in modelVariant.Regions)
                    {
                        if (variantRegion.RenderModelRegionIndex == -1)
                        {
                            continue;
                        }

                        var region = renderModel.Regions[variantRegion.RenderModelRegionIndex];
                        var variantPermutations = region.Permutations.Where(i => variantRegion.Permutations.Find(j => j.Name == i.Name) != null).ToList();

                        if (variantPermutations.Count == 0)
                        {
                            continue;
                        }

                        amfWriter.Write((CacheContext.GetString(region.Name) + "\0").ToCharArray());
                        amfWriter.Write(variantPermutations.Count);
                        permAddressList.Add((int)amfWriter.BaseStream.Position);
                        amfWriter.Write(0);
                    }

                    var vertAddressList = new List <int>();
                    var vertValueList   = new List <int>();

                    /*
                     * foreach (var variantRegion in modelVariant.Regions)
                     * {
                     *  if (variantRegion.RenderModelRegionIndex == -1)
                     *      continue;
                     *
                     *  var region = renderModel.Regions[variantRegion.RenderModelRegionIndex];
                     *  var variantPermutations = region.Permutations.Where(i => variantRegion.Permutations.Find(j => j.Name == i.Name) != null).ToList();
                     *
                     *  if (variantPermutations.Count == 0)
                     *      continue;
                     *
                     *  permValueList.Add((int)amfWriter.BaseStream.Position);
                     *
                     *  foreach (var permutation in variantPermutations)
                     *  {
                     *      if (permutation.MeshIndex == -1)
                     *          continue;
                     *
                     *      var mesh = renderModel.Geometry.Meshes[permutation.MeshIndex];
                     *
                     *      amfWriter.Write((CacheContext.GetString(permutation.Name) + "\0").ToCharArray());
                     *      amfWriter.Write((byte)(mesh.RigidNodeIndex == -1 ? 1 : 0));
                     *      amfWriter.Write((byte)mesh.RigidNodeIndex);
                     *
                     *      var vertexCount = 0;
                     *      var indexCount = 0;
                     *      foreach (var part in mesh.Parts)
                     *      {
                     *          vertexCount += part.VertexCount;
                     *          indexCount += part.IndexCount;
                     *      }
                     *
                     *      amfWriter.Write(vertexCount);
                     *      vertAddressList.Add((int)amfWriter.BaseStream.Position);
                     *      amfWriter.Write(0);
                     *
                     *      amfWriter.Write(indexCount);
                     *      indxAddressList.Add((int)amfWriter.BaseStream.Position);
                     *      amfWriter.Write(0);
                     *
                     *      amfWriter.Write(mesh.SubParts.Count);
                     *      meshAddressList.Add((int)amfWriter.BaseStream.Position);
                     *      amfWriter.Write(0);
                     *
                     *      amfWriter.Write(float.NaN);
                     *  }
                     * }
                     *
                     * foreach (var variantRegion in modelVariant.Regions)
                     * {
                     *  if (variantRegion.RenderModelRegionIndex == -1)
                     *      continue;
                     *
                     *  var region = renderModel.Regions[variantRegion.RenderModelRegionIndex];
                     *  var variantPermutations = region.Permutations.Where(i => variantRegion.Permutations.Find(j => j.Name == i.Name) != null).ToList();
                     *
                     *  if (variantPermutations.Count == 0)
                     *      continue;
                     *
                     *  foreach (var permutation in variantPermutations)
                     *  {
                     *      if (permutation.MeshIndex == -1)
                     *          continue;
                     *
                     *      var mesh = renderModel.Geometry.Meshes[permutation.MeshIndex];
                     *
                     *      if (dupeDic.TryGetValue(mesh.VertexBuffers[0], out int address))
                     *      {
                     *          vertValueList.Add(address);
                     *          continue;
                     *      }
                     *      else
                     *          dupeDic.Add(mesh.VertexBuffers[0], (int)amfWriter.BaseStream.Position);
                     *
                     *      var hasNodes = mesh.RigidNodeIndex == -1;
                     *
                     *      vertValueList.Add((int)amfWriter.BaseStream.Position);
                     *
                     *      foreach (Vertex vert in part.Vertices)
                     *      {
                     *          vert.TryGetValue("position", 0, out v);
                     *          amfWriter.Write(v.Data.x * 100);
                     *          amfWriter.Write(v.Data.y * 100);
                     *          amfWriter.Write(v.Data.z * 100);
                     *
                     *          vert.TryGetValue("normal", 0, out v);
                     *          amfWriter.Write(v.Data.i);
                     *          amfWriter.Write(v.Data.j);
                     *          amfWriter.Write(v.Data.k);
                     *
                     *          vert.TryGetValue("texcoords", 0, out v);
                     *          amfWriter.Write(v.Data.x);
                     *          amfWriter.Write(v.Data.y);
                     *
                     *          if (hasNodes)
                     *          {
                     *              VertexValue i, w;
                     *              vert.TryGetValue("blendindices", 0, out i);
                     *              vert.TryGetValue("blendweight", 0, out w);
                     *              int count = 0;
                     *              if (w.Data.a > 0)
                     *              {
                     *                  amfWriter.Write((byte)i.Data.a);
                     *                  count++;
                     *              }
                     *              if (w.Data.b > 0)
                     *              {
                     *                  amfWriter.Write((byte)i.Data.b);
                     *                  count++;
                     *              }
                     *              if (w.Data.c > 0)
                     *              {
                     *                  amfWriter.Write((byte)i.Data.c);
                     *                  count++;
                     *              }
                     *              if (w.Data.d > 0)
                     *              {
                     *                  amfWriter.Write((byte)i.Data.d);
                     *                  count++;
                     *              }
                     *
                     *              if (count == 0)
                     *              {
                     *                  amfWriter.Write((byte)0);
                     *                  amfWriter.Write((byte)255);
                     *                  amfWriter.Write(0);
                     *                  continue;
                     *                  //throw new Exception("no weights on a weighted node. report ");
                     *              }
                     *
                     *              if (count != 4) amfWriter.Write((byte)255);
                     *
                     *              if (w.Data.a > 0) amfWriter.Write(w.Data.a);
                     *              if (w.Data.b > 0) amfWriter.Write(w.Data.b);
                     *              if (w.Data.c > 0) amfWriter.Write(w.Data.c);
                     *              if (w.Data.d > 0) amfWriter.Write(w.Data.d);
                     *          }
                     *      }
                     *  }
                     * }
                     *
                     * foreach (var perm in permutations)
                     * {
                     *  var part = renderModel.ModelSections[perm.PieceIndex];
                     *
                     *  int address;
                     *  if (dupeDic.TryGetValue(part.VertsIndex, out address))
                     *  {
                     *      vertValueList.Add(address);
                     *      continue;
                     *  }
                     *  else
                     *      dupeDic.Add(part.VertsIndex, (int)amfWriter.BaseStream.Position);
                     *
                     *  VertexValue v;
                     *  bool hasNodes = part.Vertices[0].TryGetValue("blendindices", 0, out v) && part.NodeIndex == 255;
                     *
                     *  vertValueList.Add((int)amfWriter.BaseStream.Position);
                     *
                     *  foreach (Vertex vert in part.Vertices)
                     *  {
                     *      vert.TryGetValue("position", 0, out v);
                     *      amfWriter.Write(v.Data.x * 100);
                     *      amfWriter.Write(v.Data.y * 100);
                     *      amfWriter.Write(v.Data.z * 100);
                     *
                     *      vert.TryGetValue("normal", 0, out v);
                     *      amfWriter.Write(v.Data.i);
                     *      amfWriter.Write(v.Data.j);
                     *      amfWriter.Write(v.Data.k);
                     *
                     *      vert.TryGetValue("texcoords", 0, out v);
                     *      amfWriter.Write(v.Data.x);
                     *      amfWriter.Write(v.Data.y);
                     *
                     *      if (hasNodes)
                     *      {
                     *          VertexValue i, w;
                     *          vert.TryGetValue("blendindices", 0, out i);
                     *          vert.TryGetValue("blendweight", 0, out w);
                     *          int count = 0;
                     *          if (w.Data.a > 0)
                     *          {
                     *              amfWriter.Write((byte)i.Data.a);
                     *              count++;
                     *          }
                     *          if (w.Data.b > 0)
                     *          {
                     *              amfWriter.Write((byte)i.Data.b);
                     *              count++;
                     *          }
                     *          if (w.Data.c > 0)
                     *          {
                     *              amfWriter.Write((byte)i.Data.c);
                     *              count++;
                     *          }
                     *          if (w.Data.d > 0)
                     *          {
                     *              amfWriter.Write((byte)i.Data.d);
                     *              count++;
                     *          }
                     *
                     *          if (count == 0)
                     *          {
                     *              amfWriter.Write((byte)0);
                     *              amfWriter.Write((byte)255);
                     *              amfWriter.Write(0);
                     *              continue;
                     *              //throw new Exception("no weights on a weighted node. report ");
                     *          }
                     *
                     *          if (count != 4) amfWriter.Write((byte)255);
                     *
                     *          if (w.Data.a > 0) amfWriter.Write(w.Data.a);
                     *          if (w.Data.b > 0) amfWriter.Write(w.Data.b);
                     *          if (w.Data.c > 0) amfWriter.Write(w.Data.c);
                     *          if (w.Data.d > 0) amfWriter.Write(w.Data.d);
                     *      }
                     *  }
                     * }
                     #endregion
                     *
                     * dupeDic.Clear();
                     *
                     #region Indices
                     * foreach (var perm in permutations)
                     * {
                     *  var part = renderModel.ModelSections[perm.PieceIndex];
                     *
                     *  int address;
                     *  if (dupeDic.TryGetValue(part.FacesIndex, out address))
                     *  {
                     *      indxValueList.Add(address);
                     *      continue;
                     *  }
                     *  else
                     *      dupeDic.Add(part.FacesIndex, (int)amfWriter.BaseStream.Position);
                     *
                     *  indxValueList.Add((int)amfWriter.BaseStream.Position);
                     *
                     *  foreach (var submesh in part.Submeshes)
                     *  {
                     *      var indices = GetTriangleList(part.Indices, submesh.FaceIndex, submesh.FaceCount, renderModel.IndexInfoList[part.FacesIndex].FaceFormat);
                     *      foreach (var index in indices)
                     *      {
                     *          if (part.Vertices.Length > 0xFFFF) amfWriter.Write(index);
                     *          else amfWriter.Write((ushort)index);
                     *      }
                     *  }
                     *
                     * }
                     #endregion
                     #region Submeshes
                     * foreach (var perm in permutations)
                     * {
                     *  var part = renderModel.ModelSections[perm.PieceIndex];
                     *  meshValueList.Add((int)amfWriter.BaseStream.Position);
                     *  int tCount = 0;
                     *  foreach (var mesh in part.Submeshes)
                     *  {
                     *
                     *      int sCount = GetTriangleList(part.Indices, mesh.FaceIndex, mesh.FaceCount, renderModel.IndexInfoList[part.FacesIndex].FaceFormat).Count / 3;
                     *
                     *      amfWriter.Write((short)mesh.ShaderIndex);
                     *      amfWriter.Write(tCount);
                     *      amfWriter.Write(sCount);
                     *
                     *      tCount += sCount;
                     *  }
                     * }
                     #endregion
                     #region Shaders
                     * headerValueList.Add((int)amfWriter.BaseStream.Position);
                     * foreach (var shaderBlock in renderModel.Shaders)
                     * {
                     *  //skip null shaders
                     *  if (shaderBlock.tagID == -1)
                     *  {
                     *      amfWriter.Write("null\0".ToCharArray());
                     *      for (int i = 0; i < 8; i++)
                     *          amfWriter.Write("null\0".ToCharArray());
                     *
                     *      for (int i = 0; i < 4; i++)
                     *          amfWriter.Write(0);
                     *
                     *      amfWriter.Write(Convert.ToByte(false));
                     *      amfWriter.Write(Convert.ToByte(false));
                     *
                     *      continue;
                     *  }
                     *
                     *  var rmshTag = Cache.IndexItems.GetItemByID(shaderBlock.tagID);
                     *  var rmsh = DefinitionsManager.rmsh(Cache, rmshTag);
                     *  string shaderName = rmshTag.Filename.Substring(rmshTag.Filename.LastIndexOf("\\") + 1) + "\0";
                     *  string[] paths = new string[8] { "null\0", "null\0", "null\0", "null\0", "null\0", "null\0", "null\0", "null\0" };
                     *  float[] uTiles = new float[8] { 1, 1, 1, 1, 1, 1, 1, 1 };
                     *  float[] vTiles = new float[8] { 1, 1, 1, 1, 1, 1, 1, 1 };
                     *  int[] tints = new int[4] { -1, -1, -1, -1 };
                     *  bool isTransparent = false;
                     *  bool ccOnly = false;
                     *
                     *  //Halo4 f****d this up
                     *  if (Cache.Version >= DefinitionSet.Halo3Beta && Cache.Version <= DefinitionSet.HaloReachRetail)
                     *  {
                     *      var rmt2Tag = Cache.IndexItems.GetItemByID(rmsh.Properties[0].TemplateTagID);
                     *      var rmt2 = DefinitionsManager.rmt2(Cache, rmt2Tag);
                     *
                     *      for (int i = 0; i < rmt2.UsageBlocks.Count; i++)
                     *      {
                     *          var s = rmt2.UsageBlocks[i].Usage;
                     *          var bitmTag = Cache.IndexItems.GetItemByID(rmsh.Properties[0].ShaderMaps[i].BitmapTagID);
                     *
                     *          switch (s)
                     *          {
                     *              case "base_map":
                     *                  paths[0] = (bitmTag != null) ? bitmTag.Filename + "\0" : "null\0";
                     *                  break;
                     *              case "detail_map":
                     *              case "detail_map_overlay":
                     *                  paths[1] = (bitmTag != null) ? bitmTag.Filename + "\0" : "null\0";
                     *                  break;
                     *              case "change_color_map":
                     *                  paths[2] = (bitmTag != null) ? bitmTag.Filename + "\0" : "null\0";
                     *                  break;
                     *              case "bump_map":
                     *                  paths[3] = (bitmTag != null) ? bitmTag.Filename + "\0" : "null\0";
                     *                  break;
                     *              case "bump_detail_map":
                     *                  paths[4] = (bitmTag != null) ? bitmTag.Filename + "\0" : "null\0";
                     *                  break;
                     *              case "self_illum_map":
                     *                  paths[5] = (bitmTag != null) ? bitmTag.Filename + "\0" : "null\0";
                     *                  break;
                     *              case "specular_map":
                     *                  paths[6] = (bitmTag != null) ? bitmTag.Filename + "\0" : "null\0";
                     *                  break;
                     *          }
                     *      }
                     *
                     *      for (int i = 0; i < rmt2.ArgumentBlocks.Count; i++)
                     *      {
                     *          var s = rmt2.ArgumentBlocks[i].Argument;
                     *
                     *          switch (s)
                     *          {
                     *              //case "env_tint_color":
                     *              //case "fresnel_color":
                     *              case "albedo_color":
                     *                  tints[0] = i;
                     *                  break;
                     *
                     *              case "self_illum_color":
                     *                  tints[1] = i;
                     *                  break;
                     *
                     *              case "specular_tint":
                     *                  tints[2] = i;
                     *                  break;
                     *          }
                     *      }
                     *
                     *      short[] tiles = new short[8] { -1, -1, -1, -1, -1, -1, -1, -1 };
                     *
                     *      foreach (var map in rmsh.Properties[0].ShaderMaps)
                     *      {
                     *          var bitmTag = Cache.IndexItems.GetItemByID(map.BitmapTagID);
                     *
                     *          for (int i = 0; i < 8; i++)
                     *          {
                     *              if (bitmTag.Filename + "\0" != paths[i]) continue;
                     *
                     *              tiles[i] = (short)map.TilingIndex;
                     *          }
                     *      }
                     *
                     *      for (int i = 0; i < 8; i++)
                     *      {
                     *          try
                     *          {
                     *              uTiles[i] = rmsh.Properties[0].Tilings[tiles[i]].UTiling;
                     *              vTiles[i] = rmsh.Properties[0].Tilings[tiles[i]].VTiling;
                     *          }
                     *          catch { }
                     *      }
                     *  }
                     *  else
                     *      try
                     *      {
                     *          paths[0] = Cache.IndexItems.GetItemByID(rmsh.Properties[0].ShaderMaps[0].BitmapTagID).Filename + "\0";
                     *          uTiles[0] = rmsh.Properties[0].Tilings[rmsh.Properties[0].ShaderMaps[0].TilingIndex].UTiling;
                     *          vTiles[0] = rmsh.Properties[0].Tilings[rmsh.Properties[0].ShaderMaps[0].TilingIndex].VTiling;
                     *      }
                     *      catch { }
                     *
                     *  if (rmshTag.ClassCode != "rmsh" && rmshTag.ClassCode != "mat")
                     *  {
                     *      isTransparent = true;
                     *      if (paths[0] == "null\0" && paths[2] != "null\0")
                     *          ccOnly = true;
                     *  }
                     *
                     *  amfWriter.Write(shaderName.ToCharArray());
                     *  for (int i = 0; i < 8; i++)
                     *  {
                     *      amfWriter.Write(paths[i].ToCharArray());
                     *      if (paths[i] != "null\0")
                     *      {
                     *          amfWriter.Write(uTiles[i]);
                     *          amfWriter.Write(vTiles[i]);
                     *      }
                     *  }
                     *
                     *  for (int i = 0; i < 4; i++)
                     *  {
                     *      if (tints[i] == -1)
                     *      {
                     *          amfWriter.Write(0);
                     *          continue;
                     *      }
                     *
                     *      amfWriter.Write((byte)(255f * rmsh.Properties[0].Tilings[tints[i]].UTiling));
                     *      amfWriter.Write((byte)(255f * rmsh.Properties[0].Tilings[tints[i]].VTiling));
                     *      amfWriter.Write((byte)(255f * rmsh.Properties[0].Tilings[tints[i]].Unknown0));
                     *      amfWriter.Write((byte)(255f * rmsh.Properties[0].Tilings[tints[i]].Unknown1));
                     *  }
                     *
                     *  amfWriter.Write(Convert.ToByte(isTransparent));
                     *  amfWriter.Write(Convert.ToByte(ccOnly));
                     * }
                     #endregion
                     #region Write Addresses
                     * for (int i = 0; i < headerAddressList.Count; i++)
                     * {
                     *  amfWriter.BaseStream.Position = headerAddressList[i];
                     *  amfWriter.Write(headerValueList[i]);
                     * }
                     *
                     * for (int i = 0; i < markerAddressList.Count; i++)
                     * {
                     *  amfWriter.BaseStream.Position = markerAddressList[i];
                     *  amfWriter.Write(markerValueList[i]);
                     * }
                     *
                     * for (int i = 0; i < permAddressList.Count; i++)
                     * {
                     *  amfWriter.BaseStream.Position = permAddressList[i];
                     *  amfWriter.Write(permValueList[i]);
                     * }
                     *
                     * for (int i = 0; i < vertAddressList.Count; i++)
                     * {
                     *  amfWriter.BaseStream.Position = vertAddressList[i];
                     *  amfWriter.Write(vertValueList[i]);
                     * }
                     *
                     * for (int i = 0; i < indxAddressList.Count; i++)
                     * {
                     *  amfWriter.BaseStream.Position = indxAddressList[i];
                     *  amfWriter.Write(indxValueList[i]);
                     * }
                     *
                     * for (int i = 0; i < meshAddressList.Count; i++)
                     * {
                     *  amfWriter.BaseStream.Position = meshAddressList[i];
                     *  amfWriter.Write(meshValueList[i]);
                     * }
                     #endregion*/
                }
        }