/// <summary> /// Parses all H1CE Collision Node tagblocks stored sequentially. /// The purpose of 'Node' is similar to 'Region' in Halo Online. /// </summary> /// <param name="coll"></param> /// <param name="reader"></param> /// <param name="count"></param> /// <returns></returns> public long ParseNodes(CollisionModel coll, BinaryReader reader, int count) { var originalPos = reader.BaseStream.Position; coll.Nodes = new List <CollisionModel.Node>(); //destroy the old list of regions, it may not be fine-grained enough coll.Regions = new List <CollisionModel.Region>(); var new_region_count = 0; var current_bsp_offset = originalPos + (count * NODE_SIZE); for (var i = 0; i < count; ++i) { var node = new CollisionModel.Node(); node.Name = new StringId(0x140 + (uint)i); //offset of the parent node in the h1 ce node tagblock reader.BaseStream.Position = originalPos + (i * NODE_SIZE) + 32; short region_idx = BitConverter.ToInt16(reader.ReadBytes(2).Reverse().ToArray(), 0); short parent_node_idx = BitConverter.ToInt16(reader.ReadBytes(2).Reverse().ToArray(), 0); short next_sibling_idx = BitConverter.ToInt16(reader.ReadBytes(2).Reverse().ToArray(), 0); short first_child_idx = BitConverter.ToInt16(reader.ReadBytes(2).Reverse().ToArray(), 0); node.ParentNode = parent_node_idx; node.NextSiblingNode = next_sibling_idx; node.FirstChildNode = first_child_idx; coll.Nodes.Add(node); //there exists a region when the region index of the h1 ce collision node tagblock is not null if (region_idx >= 0) { var region = new CollisionModel.Region(); coll.Regions.Add(region); region.Name = new StringId(0x140 + (uint)new_region_count); reader.BaseStream.Position = originalPos + (i * NODE_SIZE) + 52; //bsp tagblock count //each bsp is placed into a separate permutation. In h1 ce // a node referencing a region with n permutations has n bsps region.Permutations = new List <CollisionModel.Region.Permutation>(); CollisionModel.Region.Permutation permutation = new CollisionModel.Region.Permutation(); region.Permutations.Add(permutation); permutation.Bsps = new List <BSP>(); uint n_bsps = (uint)BitConverter.ToInt32(reader.ReadBytes(4).Reverse().ToArray(), 0); for (uint j = 0; j < n_bsps; ++j) { reader.BaseStream.Position = current_bsp_offset; current_bsp_offset = ParseBSP(permutation, reader); } new_region_count++; } } return(current_bsp_offset); }
public long ParseBSP(CollisionModel.Region.Permutation permutation, BinaryReader reader) { long originalPos = reader.BaseStream.Position; var bsp = new BSP(); permutation.Bsps.Add(bsp); reader.BaseStream.Position = originalPos + BSP_BSP3DNODES_OFFSET; int n_3dnodes = BitConverter.ToInt32(reader.ReadBytes(4).Reverse().ToArray(), 0); reader.BaseStream.Position = originalPos + BSP_PLANES_OFFSET; int n_planes = BitConverter.ToInt32(reader.ReadBytes(4).Reverse().ToArray(), 0); reader.BaseStream.Position = originalPos + BSP_LEAVES_OFFSET; int n_leaves = BitConverter.ToInt32(reader.ReadBytes(4).Reverse().ToArray(), 0); reader.BaseStream.Position = originalPos + BSP_BSP2DREFERENCES_OFFSET; int n_2dreferences = BitConverter.ToInt32(reader.ReadBytes(4).Reverse().ToArray(), 0); reader.BaseStream.Position = originalPos + BSP_BSP2DNODES_OFFSET; int n_2dnodes = BitConverter.ToInt32(reader.ReadBytes(4).Reverse().ToArray(), 0); reader.BaseStream.Position = originalPos + BSP_SURFACES_OFFSET; int n_surfaces = BitConverter.ToInt32(reader.ReadBytes(4).Reverse().ToArray(), 0); reader.BaseStream.Position = originalPos + BSP_EDGES_OFFSET; int n_edges = BitConverter.ToInt32(reader.ReadBytes(4).Reverse().ToArray(), 0); reader.BaseStream.Position = originalPos + BSP_VERTICES_OFFSET; int n_vertices = BitConverter.ToInt32(reader.ReadBytes(4).Reverse().ToArray(), 0); reader.BaseStream.Position = originalPos + BSP_SIZE; reader.BaseStream.Position = ParseBSP3DNodes(bsp, reader, n_3dnodes); reader.BaseStream.Position = ParsePlanes(bsp, reader, n_planes); reader.BaseStream.Position = ParseLeaves(bsp, reader, n_leaves); reader.BaseStream.Position = ParseBSP2DReferences(bsp, reader, n_2dreferences); reader.BaseStream.Position = ParseBSP2DNodes(bsp, reader, n_2dnodes); reader.BaseStream.Position = ParseSurfaces(bsp, reader, n_surfaces); reader.BaseStream.Position = ParseEdges(bsp, reader, n_edges); return(ParseVertices(bsp, reader, n_vertices)); }
/// <summary> /// Parses regions into Halo Online collision 'Region' tagblocks. /// Names are not preserved. /// </summary> /// <param name="coll"></param> /// <param name="reader"></param> /// <param name="count"></param> /// <returns></returns> public long ParseRegions(CollisionModel coll, BinaryReader reader, int count) { var originalPos = reader.BaseStream.Position; //The number of permutations that have been parsed in total for all regions var n_parsed_permutations = 0; //Permutations for all regions follow sequentially after all regions var permutations_base_addr = reader.BaseStream.Position + count * REGION_TAGBLOCK_SIZE; coll.Regions = new List <CollisionModel.Region>(); for (uint i = 0; i < count; ++i) { //configure the current region CollisionModel.Region region = new CollisionModel.Region(); region.Name = new StringId(0x140 + i); //set up stream for reading number of permutations in current region reader.BaseStream.Position = originalPos + (long)(i * REGION_TAGBLOCK_SIZE) + REGION_PERMUTATION_OFFSET; uint n_permutations = (uint)BitConverter.ToInt32(reader.ReadBytes(4).Reverse().ToArray(), 0); region.Permutations = new List <CollisionModel.Region.Permutation>(); for (uint j = 0; j < n_permutations; ++j) { var permutation = new CollisionModel.Region.Permutation(); permutation.Name = new StringId(0x140 + j); permutation.Bsps = new List <BSP>(); region.Permutations.Add(permutation); } coll.Regions.Add(region); n_parsed_permutations += (int)n_permutations; } return(permutations_base_addr + (n_parsed_permutations * PERMUTATION_SIZE)); }
private CollisionModel GenerateCollisionModel(CachedTag modelTag, int geometryIndex, bool iscluster) { var collisionModel = new CollisionModel(); collisionModel.Regions = new List <CollisionModel.Region>(); var permutation = new CollisionModel.Region.Permutation() { Name = DestCache.StringTable.GetStringId("default"), BspPhysics = new List <CollisionBspPhysicsDefinition>(), BspMoppCodes = new List <TagTool.Havok.TagHkpMoppCode>(), Bsps = new List <CollisionModel.Region.Permutation.Bsp>() }; collisionModel.Regions = new List <CollisionModel.Region>() { new CollisionModel.Region() { Name = DestCache.StringTable.GetStringId("default"), Permutations = new List <CollisionModel.Region.Permutation>() { permutation } } }; // copy over and fixup bsp physics blocks var newCollisionGeometry = new BspCollisionGeometry.CollisionGeometry(); //instanced geometry if (!iscluster) { //bsp physics var instancedGeometryInstance = StructureBsp.InstancedGeometryInstances[geometryIndex]; var instancedGeometryDef = StructureBspResources.InstancedGeometry[instancedGeometryInstance.MeshIndex]; foreach (var bspPhysics in instancedGeometryInstance.BspPhysics) { permutation.BspPhysics.Add(ConvertData(bspPhysics)); } //mopps foreach (var mopp in instancedGeometryDef.CollisionMoppCodes) { permutation.BspMoppCodes.Add(ConvertData(mopp)); } //collision geometry newCollisionGeometry = instancedGeometryDef.CollisionInfo.DeepClone(); } else { var cluster = StructureBsp.Clusters[geometryIndex]; // bsp physics & mopps foreach (var mopp in cluster.CollisionMoppCodes) { permutation.BspMoppCodes.Add(ConvertData(mopp)); var bspPhysics = new CollisionBspPhysicsDefinition { GeometryShape = new CollisionGeometryShape() { // need to double check this AABB_Min = new RealQuaternion(cluster.BoundsX.Lower, cluster.BoundsY.Lower, cluster.BoundsZ.Lower, 0), AABB_Max = new RealQuaternion(cluster.BoundsX.Upper, cluster.BoundsY.Upper, cluster.BoundsZ.Upper, 0), }, MoppBvTreeShape = new Havok.HkpBvMoppTreeShape() }; permutation.BspPhysics.Add(bspPhysics); } // collision geometry if (StructureBspResources.CollisionBsps.Count > 0) { newCollisionGeometry = StructureBspResources.CollisionBsps[0].DeepClone(); } //TODO: cull unnecessary parts of bsp collision } // fixup bsp fixups for collision model foreach (var bspPhysics in permutation.BspPhysics) { bspPhysics.GeometryShape.Model = modelTag; bspPhysics.GeometryShape.BspIndex = -1; bspPhysics.GeometryShape.CollisionGeometryShapeKey = 0xffff; bspPhysics.GeometryShape.CollisionGeometryShapeType = 0; } // fixup surfaces materials block // build a mapping of surface material indices to collision materials foreach (var surface in newCollisionGeometry.Surfaces) { if (surface.MaterialIndex == -1) { continue; } short modelMaterialIndex; if (!CollisionMaterialMapping.TryGetValue(surface.MaterialIndex, out modelMaterialIndex)) { CollisionMaterialMapping.Add(surface.MaterialIndex, modelMaterialIndex); } surface.MaterialIndex = modelMaterialIndex; } // add the collision geometry permutation.Bsps.Add(new CollisionModel.Region.Permutation.Bsp() { NodeIndex = 0, Geometry = newCollisionGeometry }); return(collisionModel); }