void SetMesh() { if (Filter.mesh) { Destroy(Filter.mesh); } List <Material> materials = new List <Material>(); List <MeshTopology> topos = new List <MeshTopology>(); materials.Add(new Material(MainCamera.MainShader)); // this is for outlines topos.Add(MeshTopology.Lines); //Renderer.materials = new Material[] { new Material(MainCamera.MainShader), new Material(MainCamera.MainShader) }; Utils.MeshBuilder mb = new Utils.MeshBuilder(); for (int i = 0; i < CurrentLevel.Sectors.Count; i++) { LevelSector sec = CurrentLevel.Sectors[i]; if (drawAfter >= 0 && i < drawAfter) { continue; } int planemesh = mb.CurrentMesh + 1; mb.CurrentMesh = 0; //if (sec.Walls.Count > 1) Debug.LogFormat("sector {0} has walls", i); for (int j = 0; j < sec.Walls.Count; j++) { LevelWall wall = sec.Walls[j]; //if (j != 0) continue; if (wall.V1.X < 0 || wall.V1.Y < 0 || wall.V2.X < 0 || wall.V2.Y < 0) { continue; } Color color = new Color(1, 0, 0, 1); if (wall.Back != null) { color.r = 0; color.g = 1f; } //if (i == 0) color.g = color.b = 0; //else if (i == 10) color.g = color.r = 0; // sector 0 = 0 // sector 10 = +8 (-8) floor height float TopFZ1 = wall.Front.Sector.Floor.ZatPoint(wall.V1.X, wall.V1.Y); if (wall.Back != null) { TopFZ1 = Mathf.Max(TopFZ1, wall.Back.Sector.Floor.ZatPoint(wall.V1.X, wall.V1.Y)); // top point at v1 } float BottomFZ1 = wall.Front.Sector.Floor.ZatPoint(wall.V1.X, wall.V1.Y); if (wall.Back != null) { BottomFZ1 = Mathf.Min(BottomFZ1, wall.Back.Sector.Floor.ZatPoint(wall.V1.X, wall.V1.Y)); } float TopFZ2 = wall.Front.Sector.Floor.ZatPoint(wall.V2.X, wall.V2.Y); if (wall.Back != null) { TopFZ2 = Mathf.Max(TopFZ2, wall.Back.Sector.Floor.ZatPoint(wall.V2.X, wall.V2.Y)); // top point at v2 } float BottomFZ2 = wall.Front.Sector.Floor.ZatPoint(wall.V2.X, wall.V2.Y); if (wall.Back != null) { BottomFZ2 = Mathf.Min(BottomFZ2, wall.Back.Sector.Floor.ZatPoint(wall.V2.X, wall.V2.Y)); } float TopCZ1 = wall.Front.Sector.Ceiling.ZatPoint(wall.V1.X, wall.V1.Y); if (wall.Back != null) { TopCZ1 = Mathf.Max(TopCZ1, wall.Back.Sector.Ceiling.ZatPoint(wall.V1.X, wall.V1.Y)); // top point at v1 } float BottomCZ1 = wall.Front.Sector.Ceiling.ZatPoint(wall.V1.X, wall.V1.Y); if (wall.Back != null) { BottomCZ1 = Mathf.Min(BottomCZ1, wall.Back.Sector.Ceiling.ZatPoint(wall.V1.X, wall.V1.Y)); } float TopCZ2 = wall.Front.Sector.Ceiling.ZatPoint(wall.V2.X, wall.V2.Y); if (wall.Back != null) { TopCZ2 = Mathf.Max(TopCZ2, wall.Back.Sector.Ceiling.ZatPoint(wall.V2.X, wall.V2.Y)); // top point at v2 } float BottomCZ2 = wall.Front.Sector.Ceiling.ZatPoint(wall.V2.X, wall.V2.Y); if (wall.Back != null) { BottomCZ2 = Mathf.Min(BottomCZ2, wall.Back.Sector.Ceiling.ZatPoint(wall.V2.X, wall.V2.Y)); } if (wall.Back != null) { mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V1.X, wall.V1.Y, TopFZ1); mb.NextVertex(); mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V2.X, wall.V2.Y, TopFZ2); mb.NextVertex(); mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V1.X, wall.V1.Y, TopCZ1); mb.NextVertex(); mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V2.X, wall.V2.Y, TopCZ2); mb.NextVertex(); mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V1.X, wall.V1.Y, BottomFZ1); mb.NextVertex(); mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V2.X, wall.V2.Y, BottomFZ2); mb.NextVertex(); mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V1.X, wall.V1.Y, BottomCZ1); mb.NextVertex(); mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V2.X, wall.V2.Y, BottomCZ2); mb.NextVertex(); } else { mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V1.X, wall.V1.Y, TopFZ1); mb.NextVertex(); mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V2.X, wall.V2.Y, TopFZ2); mb.NextVertex(); mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V1.X, wall.V1.Y, TopCZ1); mb.NextVertex(); mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V2.X, wall.V2.Y, TopCZ2); mb.NextVertex(); } // vertical lines if (wall.Back != null) { mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V1.X, wall.V1.Y, TopFZ1); mb.NextVertex(); mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V1.X, wall.V1.Y, BottomFZ1); mb.NextVertex(); mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V2.X, wall.V2.Y, TopFZ2); mb.NextVertex(); mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V2.X, wall.V2.Y, BottomFZ2); mb.NextVertex(); mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V1.X, wall.V1.Y, TopCZ1); mb.NextVertex(); mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V1.X, wall.V1.Y, BottomCZ1); mb.NextVertex(); mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V2.X, wall.V2.Y, TopCZ2); mb.NextVertex(); mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V2.X, wall.V2.Y, BottomCZ2); mb.NextVertex(); } else { mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V1.X, wall.V1.Y, TopFZ1); mb.NextVertex(); mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V1.X, wall.V1.Y, BottomCZ1); mb.NextVertex(); mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V2.X, wall.V2.Y, TopFZ2); mb.NextVertex(); mb.CurrentColor = color; mb.CurrentPosition = new Vector3(wall.V2.X, wall.V2.Y, BottomCZ2); mb.NextVertex(); } } // draw floor Material texfloor = new Material(MainCamera.MainShader); RadixBitmap bitmapfloor = TextureManager.GetTextureById(sec.Floor.Texture); Vector2 texmul = new Vector2(bitmapfloor.Width, bitmapfloor.Height); if (bitmapfloor != null) { texfloor.mainTexture = bitmapfloor.Texture; } Material texceiling = new Material(MainCamera.MainShader); RadixBitmap bitmapceiling = TextureManager.GetTextureById(sec.Ceiling.Texture); Vector2 texmul2 = new Vector2(bitmapceiling.Width, bitmapceiling.Height); if (bitmapceiling != null) { texceiling.mainTexture = bitmapceiling.Texture; } materials.Add(texfloor); materials.Add(texceiling); topos.Add(MeshTopology.Triangles); topos.Add(MeshTopology.Triangles); for (int p = 0; p < 2; p++) { mb.CurrentMesh = planemesh + p; LevelSector.Plane plane = p == 0 ? sec.Floor : sec.Ceiling; for (int j = 0; j < sec.Triangles.Count; j++) { LevelSector.Triangle tri = sec.Triangles[j]; Color color = new Color(0.5f, 0.5f, 0.5f, 1); if (tri.Points.Count != 3) { // now, we know that this mesh is NOT actual triangle list, it consists of points around the sector. Vector2 center = tri.Center; for (int k = 0; k < tri.Points.Count; k++) { // add center point mb.CurrentColor = color; mb.CurrentPosition = new Vector3(center.x, center.y, plane.ZatPoint(center.x, center.y)); mb.CurrentUV1 = new Vector2(center.x / texmul.x, center.y / texmul.y); mb.NextVertex(); // add current point int cIndex = k % tri.Points.Count; mb.CurrentColor = color; mb.CurrentPosition = new Vector3(tri.Points[cIndex].x, tri.Points[cIndex].y, plane.ZatPoint(tri.Points[cIndex].x, tri.Points[cIndex].y)); mb.CurrentUV1 = new Vector2(tri.Points[cIndex].x / texmul.x, tri.Points[cIndex].y / texmul.y); mb.NextVertex(); // add next point int nIndex = (k + 1) % tri.Points.Count; mb.CurrentColor = color; mb.CurrentPosition = new Vector3(tri.Points[nIndex].x, tri.Points[nIndex].y, plane.ZatPoint(tri.Points[nIndex].x, tri.Points[nIndex].y)); mb.CurrentUV1 = new Vector2(tri.Points[nIndex].x / texmul.x, tri.Points[nIndex].y / texmul.y); mb.NextVertex(); } } else // it's a legit triangle, don't overcomplicate { for (int k = 0; k < 3; k++) { mb.CurrentColor = color; mb.CurrentPosition = new Vector3(tri.Points[k].x, tri.Points[k].y, plane.ZatPoint(tri.Points[k].x, tri.Points[k].y)); mb.CurrentUV1 = new Vector2(tri.Points[k].x / texmul.x, tri.Points[k].y / texmul.y); mb.NextVertex(); } } } } if (drawAfter >= 0 && i >= drawAfter && sec.Walls.Count > 0) { Debug.LogFormat("stopped rendering at {0}", i); break; } } Filter.mesh = mb.ToMesh(topos.ToArray()); Renderer.materials = materials.ToArray(); }
public Level(string filename) { // 0..0x48 = header // 0x49.. = data using (MemoryStream ms = ResourceManager.OpenRead(filename)) using (BinaryReader br = new BinaryReader(ms)) { ms.Position = 0; uint level_magic = br.ReadUInt32(); if (level_magic != 0xFFFFFEE7) { throw new LevelException("Invalid level header"); } ms.Position = 0x1D; int level_numwalls = br.ReadInt32(); int level_numsectors = br.ReadInt32(); Debug.LogFormat("numwalls = {0}, numsectors = {1}, level = {2}", level_numwalls, level_numsectors, filename); Dictionary <int, int> sectorRemap = new Dictionary <int, int>(); Dictionary <LevelSector, LevelSector> sectorRemapRef = new Dictionary <LevelSector, LevelSector>(); ms.Position = 0x49; for (int i = 0; i < level_numsectors; i++) { // sector size = 0x8E long curPos = ms.Position; long nextPos = ms.Position + 0x8E; LevelSector sec = new LevelSector(); ms.Position = curPos + 2; sec.Tag = Utils.GetNullTermString(br.ReadBytes(0x1A)); // for now ignore everything else int sec_texfloor = br.ReadInt16(); int sec_texceiling = br.ReadInt16(); sec.Floor.Z = br.ReadInt16(); sec.Ceiling.Z = br.ReadInt16(); sec.Floor.Texture = sec_texfloor; sec.Ceiling.Texture = sec_texceiling; // 00: ?? ?? // 02: name (0x1A long), also known as tag (used in scripting?) // 1C: ## ## (floor texture) // 1E: ## ## (ceiling texture) // // what the f**k is so important in sector structure that it takes 0x6E bytes to store? ms.Position = curPos + 0x25; byte sec_flags = br.ReadByte(); string flagsstr = ""; for (int j = 0; j < 8; j++) { if ((sec_flags & (byte)(1 << j)) != 0) { if (flagsstr != "") { flagsstr += ", "; } if (((1 << j) & 4) != 0) { flagsstr += string.Format("floorslope"); } else if (((1 << j) & 8) != 0) { flagsstr += string.Format("ceilingslope"); } else { flagsstr += string.Format("unknown:{0:X2}", (byte)(1 << j)); } } } if (flagsstr != "") { flagsstr = " (" + flagsstr + ")"; } Debug.LogFormat("sector {0}, flags {1}{2}", sec.Tag, sec_flags, flagsstr); if ((sec_flags & 4) != 0) // floor slope { // ReadSlope(ms, br, curPos + 0x26, sec.Floor); } if ((sec_flags & 8) != 0) // ceiling slope { // ReadSlope(ms, br, curPos + 0x36, sec.Ceiling); } // check if such sector already exists sectorRemap[i] = i; sectorRemapRef[sec] = sec; for (int j = 0; j < Sectors.Count; j++) { LevelSector other = Sectors[j]; if (other.Floor.Z == sec.Floor.Z && other.Ceiling.Z == sec.Ceiling.Z) { sectorRemap[i] = j; sectorRemapRef[sec] = other; break; } } Sectors.Add(sec); ms.Position = nextPos; } for (int i = 0; i < level_numwalls; i++) { // wall size = 0x56 long curPos = ms.Position; long nextPos = ms.Position + 0x56; LevelWall wall = new LevelWall(); ms.Position = curPos + 0x0A; wall.V1 = GetVertex(br.ReadInt32(), br.ReadInt32(), wall); wall.V2 = GetVertex(br.ReadInt32(), br.ReadInt32(), wall); ms.Position = curPos + 0x1A; short wall_ofront = br.ReadInt16(); short wall_oback = br.ReadInt16(); short wall_front = wall_ofront; short wall_back = wall_oback; if (wall_front < 0) { wall.Front = null; } else { wall.Front = new LevelWallSide(); wall.Front.Wall = wall; wall.Front.V1 = wall.V1; wall.Front.V2 = wall.V2; wall.Front.Sector = Sectors[wall_front]; wall.Front.Sector.Walls.Add(wall); } if (wall_back < 0) { wall.Back = null; } else { wall.Back = new LevelWallSide(); wall.Back.Wall = wall; wall.Back.V1 = wall.V2; wall.Back.V2 = wall.V1; wall.Back.Sector = Sectors[wall_back]; wall.Back.Sector.Walls.Add(wall); } ms.Position = curPos + 0x4E; int wall_texfront = br.ReadInt16(); // this is offset into wall textures, to be changed to a pointer later int wall_texback = br.ReadInt16(); ms.Position = curPos + 0x48; wall.Flags = (LevelWallFlags)br.ReadUInt32(); // for now ignore everything else Walls.Add(wall); //Debug.LogFormat("Wall Front = {0}, Back = {1}, TextureFront = {2}, TextureBack = {3}, Flags = {4}\nV1 = {5},{6} V2 = {7},{8}", wall_front, wall_back, wall_texfront, wall_texback, wall.Flags.ToString(), wall.V1.X, wall.V1.Y, wall.V2.X, wall.V2.Y); ms.Position = nextPos; } // handle unclosed sectors // if sector is not closed, we have to find a wall that references a similar sector (using sectorRemap) and remap. for (int i = 0; i < Sectors.Count; i++) { LevelSector sec = Sectors[i]; if (sec.Walls.Count <= 0) { continue; } bool isclosed = true; int closedloops = 0; HashSet <LevelWall> checkedwalls = new HashSet <LevelWall>(); while (true) { LevelWall firstwall = null; for (int j = 0; j < sec.Walls.Count; j++) { if (checkedwalls.Contains(sec.Walls[j])) { continue; } firstwall = sec.Walls[j]; break; } if (firstwall == null) { break; } // take next line loop (polygon) LevelWall curwall = firstwall; do { checkedwalls.Add(curwall); LevelWall nextwall = null; for (int j = 0; j < sec.Walls.Count; j++) { LevelWall checkwall = sec.Walls[j]; if (checkwall.GetV1(sec) == curwall.GetV2(sec)) { nextwall = checkwall; break; } } if (nextwall == null) // sector is not closed. try to find a remapped wall here { for (int j = 0; j < Walls.Count; j++) { LevelWall rwall = Walls[j]; if (checkedwalls.Contains(rwall) || sec.Walls.Contains(rwall)) { continue; } LevelSector remappedFront = rwall.Front != null ? sectorRemapRef[rwall.Front.Sector] : null; LevelSector remappedBack = rwall.Back != null ? sectorRemapRef[rwall.Back.Sector] : null; LevelSector remappedSelf = sectorRemapRef[sec]; if ((remappedFront == remappedSelf && rwall.V1 == curwall.GetV2(sec)) || (remappedBack == remappedSelf && rwall.V2 == curwall.GetV2(sec))) { // add this wall to sector walls if (remappedFront == remappedSelf) { rwall.Front.Sector.Walls.Remove(rwall); rwall.Front.Sector = sec; sec.Walls.Add(rwall); } else { rwall.Back.Sector.Walls.Remove(rwall); rwall.Back.Sector = sec; sec.Walls.Add(rwall); } nextwall = rwall; } } } curwall = nextwall; }while (curwall != firstwall && curwall != null); if (curwall == null) { //Debug.LogFormat("sector {0} is not closed", i); isclosed = false; } else { closedloops++; } } if (!isclosed) { //Debug.LogFormat("but has {0} closed loops", closedloops); } } // based on lines and sectors, build GL nodes like structure for (int i = 0; i < Sectors.Count; i++) { LevelSector sec = Sectors[i]; if (sec.Walls.Count <= 0) { continue; } //if (i != 56) // continue; //BayazitDecomposer //List<List<Vector2>> polygons = TriangulatorHelper.GetLineLoops(sec); try { List <List <Vector2> > polygons = TriangulatorHelper.MakeTriangles(TriangulatorHelper.GetLineLoops(sec)); //Triangulation triangulation = Triangulation.Create(sec); // these polygons should be convex /*for (int j = 0; j < triangulation.Vertices.Count; j += 3) * { * LevelSector.Triangle tri = new LevelSector.Triangle(); * tri.Points.Add(new Vector2(triangulation.Vertices[j].x, triangulation.Vertices[j].y)); * tri.Points.Add(new Vector2(triangulation.Vertices[j+1].x, triangulation.Vertices[j+1].y)); * tri.Points.Add(new Vector2(triangulation.Vertices[j+2].x, triangulation.Vertices[j+2].y)); * sec.Triangles.Add(tri); * }*/ for (int j = 0; j < polygons.Count; j++) { LevelSector.Triangle tri = new LevelSector.Triangle(); tri.Points.AddRange(polygons[j]); sec.Triangles.Add(tri); } } catch (NotSupportedException e) { /* ... */ sec.Triangles.Clear(); } } } }