static Mathbox.Mesh TryGetMeshAt(UInt16 address) { // don't bother with these addresses if (address == 0x40DC || address == 0x4344) { return(null); } if (!ROM.TryRead(address, out UInt16 vertex_base)) { return(null); } if (vertex_base < 0x2000 || vertex_base >= 0x8000) { return(null); } // at this point we should try and parse an object // the parsing could fail, in which case the catch block will catch us try { Mesh mesh = new Mathbox.Mesh(address, vertex_base); return(mesh.IsValid ? mesh : null); } catch { return(null); } }
static internal bool Parse(Mathbox.Mesh mesh, UInt16 address) { // scan forever until exit condition reached for (; ;) { // ensure no duplicate surfaces foreach (Surface p in mesh.mSurfaces) { if (p.Address == address) { return(true); // everything OK, we just can't add any more polys } } // try and create a new surface object if (!Surface.TryCreateSurface(ref address, out Surface surface)) { return(false); } /// is this the end of the surface list? if (surface.VertexOffsetTable == 0x8000) { return(true); // end of surface list reached } // should the surface be drawn? if (!surface.Flags.IsCullInstruction || surface.Flags.CullInstruction == CULL.AlwaysBranch) { // yes // scan the vertex index list int pIndices = surface.VertexOffsetTable; // read the first offset if (!ROM.TryRead(pIndices++, out UInt16 offset)) { return(false); } // first vertex specifies the (optional) normal vector if ((offset & 0x4000) == 0) { // normal vector exists Vector3D normal = mesh.Vector(offset); normal.Normalize(); surface.Normal = normal; } // remaining vertices are the points that make up the surface while (offset < 0x8000) { if (!ROM.TryRead(pIndices++, out offset)) { return(false); } surface.mPoints.Add(mesh.Point(offset)); } // make sure the surface type matches the number of vertices switch (surface.mPoints.Count) { case 0: return(false); case 1: // always set to "DOT" surface.Flags.Type = TYPE.Dot; break; case 2: // must be dot or vector if (surface.Type == TYPE.Polygon) { surface.Flags.Type = TYPE.Vector; } break; default: if (surface.Type == TYPE.Vector) { surface.mPoints.Add(surface.mPoints[0]); // close the vector list } break; } // shade certain objects if (surface.Type == TYPE.Polygon) { switch (mesh.Address) { case 0x3892: // eyeball case 0x38A4: // eyeball case 0x38B6: // eyeball case 0x38C8: // eyeball case 0x38DA: // eyeball if (surface.ColorIndex > 0 && surface.ColorIndex <= 7) { surface.Flags.IsShaded = true; // make shaded } break; //case 0x5767: // transporter //case 0x577B: // transporter //case 0x5791: // transporter case 0x5B56: // tanker case 0x5B68: // spike case 0x5B6E: // spike case 0x5B72: // spike case 0x5B8C: // spike case 0x5BB5: // spike case 0x5BE7: // spike case 0x5C22: // spike case 0x5F08: // colored "big ball" case 0x692F: // hand case 0x730A: // cube case 0x7318: // cube case 0x7326: // cube case 0x7334: // cube case 0x7342: // cube case 0x7350: // cube case 0x735E: // cube //case 0x755D: // dodecahedron case 0x77D0: // ring case 0x7DF4: // viewer killer surface.Flags.IsShaded = true; // make shaded break; } } // fix objects switch (mesh.Address) { case 0x2958: // robot visor case 0x29EE: // robot visor case 0x2A84: // robot visor case 0x2B1A: // robot visor case 0x2C00: // robot visor if (surface.ColorIndex == 0x38 || surface.ColorIndex == 0x39) { for (int n = 0; n < surface.mPoints.Count; n++) { surface.mPoints[n] = surface.mPoints[n] + new Vector3D(-0.1, 0, 0); } } break; case 0x51E0: // bird eye case 0x5234: // bird eye case 0x5288: // bird eye if (surface.ColorIndex == 0x38 || surface.ColorIndex == 0x39) { for (int n = 0; n < surface.mPoints.Count; n++) { surface.mPoints[n] = new Point3D( surface.mPoints[n].X * 15f / 18f, surface.mPoints[n].Y - 6f, surface.mPoints[n].Z + 3f); } } break; } // this is a good surface // check for duplicates bool keep = true; foreach (Surface s in mesh.mSurfaces) { if (surface.Equals(s)) { keep = false; break; } } // keep surface if it is unique if (keep) { mesh.mSurfaces.Add(surface); } } // manage branching if (surface.Flags.IsCullInstruction) { // find address of branch UInt16 branch_addr = address; if (!ROM.TryRead(address++, out UInt16 offset)) { return(false); } branch_addr += offset; // what type of branch is this switch (surface.Flags.CullInstruction) { case CULL.AlwaysBranch: address = branch_addr; // take the branch break; case CULL.BranchIfVisible: // branch if surface visible case CULL.BranchIfHidden: // branch if surface hidden case CULL.BranchAlways: // branch never (never draw) // parse branch if (!Parse(mesh, branch_addr)) { return(false); // something went wrong } break; // now continue parsing the original branch } } } }