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; }
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; }
/// <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(); }
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); }
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; }
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); }
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())); }
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); }
private void SerializeDefinitionData(RenderGeometryApiResourceDefinition definition) { _model.Geometry.Resource = CacheContext.ResourceCache.CreateRenderGeometryApiResource(definition); }
public abstract TagResourceReference CreateRenderGeometryApiResource(RenderGeometryApiResourceDefinition renderGeometryDefinition);
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; }
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(); } }
public override TagResourceReference CreateRenderGeometryApiResource(RenderGeometryApiResourceDefinition renderGeometryDefinition) { throw new NotImplementedException(); }
/// <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()); }
// // 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; } } } }
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*/ } }