/// <summary> /// Stub for now, creates n materials, which are named 0 to (n-1) /// </summary> /// <param name="coll"></param> /// <param name="tag_data"></param> /// <param name="pos"></param> /// <returns></returns> public long ParseMaterials(CollisionModel coll, BinaryReader reader, int count) { coll.Materials = new List<CollisionModel.Material>(); for (uint i = 0; i < count; ++i) { var material = new CollisionModel.Material(); material.Name = new StringID(0x140 + i); coll.Materials.Add(material); } return reader.BaseStream.Position + MATERIAL_TAGBLOCK_SIZE * count; }
/// <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); }
/// <summary> /// This file parser will parse Halo 1 CE 'model_collision_geometry' tags. /// The addresses of the tagblocks inside the tag are likely to be garbage /// values. The Halo 1 CE development tool 'guerilla' does not use the /// reflexive address value and expects chunks to occur in the order that /// the reflexives occur in the parent struct. /// /// The Halo1 CE collision tag is used due to high compatibility and /// availability of 'Tool' - a program which can compile collision tags. /// /// The parser expects the following format: /// h1ce coll tag format: ///main struct ///all materials sequential ///all regions sequential ///all permutations sequential ///all path finding spheres sequential ///all nodes sequential ///bsp 0 /// bsp0 3dnodes sequential /// ... /// bsp0 vertices sequential ///bsp 1 /// ... ///... /// </summary> /// <param name="fpath"></param> /// <returns></returns> public bool ParseFromFile(string fpath) { FileStream fs = null; try { fs = new FileStream(fpath, FileMode.Open, FileAccess.Read); } catch (FileNotFoundException) { Console.WriteLine("The system cannot find the file specified."); return false; } var reader = new BinaryReader(fs); var coll = new CollisionModel(); // h1 ce tags will have a 64 byte header. The main struct is immediately after. var len = reader.BaseStream.Length; reader.BaseStream.Position = MAIN_STRUCT_OFFSET; var size = ParseMain(coll, reader); if (len != size) { Console.WriteLine("length expected was not actual.\nexpected: " + len + ", actual: " + size); return false; } //builder succeeded Definition = coll; return true; }
public long ParseMain(CollisionModel coll, BinaryReader reader) { // start of the main struct in the reader var originalPos = reader.BaseStream.Position; //location of the count of materials reader.BaseStream.Position += MAIN_MATERIAL_OFFSET; var n_materials = BitConverter.ToInt32(reader.ReadBytes(4).Reverse().ToArray(), 0); //change pos to the offset where the 'material' tagblocks begin reader.BaseStream.Position = originalPos + MAIN_REGION_OFFSET; var n_regions = BitConverter.ToInt32(reader.ReadBytes(4).Reverse().ToArray(), 0); reader.BaseStream.Position = originalPos + MAIN_PATHF_SPHERES_OFFSET; var n_pathf_spheres = BitConverter.ToInt32(reader.ReadBytes(4).Reverse().ToArray(), 0); reader.BaseStream.Position = originalPos + MAIN_NODES_OFFSET; var n_nodes = BitConverter.ToInt32(reader.ReadBytes(4).Reverse().ToArray(), 0); var afterReadPos = originalPos + MAIN_STRUCT_SIZE; //get the position after all of the materials reader.BaseStream.Position = afterReadPos; afterReadPos = ParseMaterials(coll, reader, n_materials); //Get the position after all of the sequentially stored regions and sequentially stored permutations reader.BaseStream.Position = afterReadPos; afterReadPos = ParseRegions(coll, reader, n_regions); //set to beginning of sequential list of path finding spheres. reader.BaseStream.Position = afterReadPos; afterReadPos = ParsePathFindingSpheres(coll, reader, n_pathf_spheres); reader.BaseStream.Position = afterReadPos; afterReadPos = ParseNodes(coll, reader, n_nodes); return afterReadPos; }
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 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 ParsePathFindingSpheres(CollisionModel coll, BinaryReader reader, int count) { var originalPos = reader.BaseStream.Position; coll.PathfindingSpheres = new List<CollisionModel.PathfindingSphere>(); for (uint i = 0; i < count; ++i) { var pfSphere = new CollisionModel.PathfindingSphere(); reader.BaseStream.Position = originalPos + (i * PATHF_SPHERE_SIZE); var node_idx = BitConverter.ToInt16(reader.ReadBytes(2).Reverse().ToArray(), 0); reader.BaseStream.Position += 14; //14 bytes between node index and sphere location,radius var center_x = BitConverter.ToSingle(reader.ReadBytes(4).Reverse().ToArray(), 0); var center_y = BitConverter.ToSingle(reader.ReadBytes(4).Reverse().ToArray(), 0); var center_z = BitConverter.ToSingle(reader.ReadBytes(4).Reverse().ToArray(), 0); var radius = BitConverter.ToSingle(reader.ReadBytes(4).Reverse().ToArray(), 0); pfSphere.CenterX = center_x; pfSphere.CenterY = center_y; pfSphere.CenterZ = center_z; pfSphere.Radius = radius; pfSphere.Node = node_idx; coll.PathfindingSpheres.Add(pfSphere); } return originalPos + (count * PATHF_SPHERE_SIZE); }