void DrawTerrainColumn(int index, Vector3 corner) { // local tile map (we are rendering tile A) // TileF TileD TileC // TileE TileA TileB // TileG TileH // get this tile Tile TileA = Memory[Row.This + ((index + 0) & 15)]; // check if object must be rendered with this tile if (TileA.HoldsObject) { Row.ObjectCount++; } // check if tile is empty if (TileA.IsEmpty) { return; // nothing to draw } // get base tile color int colorIndex = TileA.ColorIndex; // locate tile corners // d +-------+ c // / /| // / / | x2 = x1 + Tile.WIDTH_X // y1-- +-------+ | y2 = y1 + Tile.DEFAULT_HEIGHT_Y // |a b| g+ --z2 z2 = z1 + Tile.DEPTH_Z // | | / // |e f|/ // y2-- +-------+ --z1 // | | // x1 x2 float x1 = corner.X; float x2 = x1 + Tile.WIDTH_X; float z1 = corner.Z; float z2 = corner.Z + Tile.DEPTH_Z; // determine tile height offset Tile TileB = Memory[Row.This + ((index + 1) & 15)]; Tile TileC = Memory[Row.Next + ((index + 1) & 15)]; Tile TileD = Memory[Row.Next + ((index + 0) & 15)]; TILE_HEIGHT height = GetTileHeight(TileA, TileB, TileC, TileD); // draw left or right side of cube if (x1 > 0) { // left side of the cube TILE_HEIGHT side; if (index == 1) { // leftmost tile side.b = side.c = -Parent.ViewPosition.Y + Tile.DEFAULT_HEIGHT_Y; } else { Tile TileE = Memory[Row.This + ((index - 1) & 15)]; Tile TileF = Memory[Row.Next + ((index - 1) & 15)]; side = GetTileHeight(TileE, TileA, TileD, TileF); } // draw if tile to the left is lower than current tile if (height.a < side.b || height.d < side.c) { Vertices[0] = Vector3.Transform(new Vector3(x1, height.a, z1), Parent.D3DTS_WORLD); Vertices[1] = Vector3.Transform(new Vector3(x1, height.d, z2), Parent.D3DTS_WORLD); Vertices[2] = Vector3.Transform(new Vector3(x1, side.c, z2), Parent.D3DTS_WORLD); Vertices[3] = Vector3.Transform(new Vector3(x1, side.b, z1), Parent.D3DTS_WORLD); DisplayListManager.AddPrimitive(renderMode, Vertices, 4, Parent.GetColor(colorIndex - 1)); } } else if (x2 < 0) { // right side of the cube TILE_HEIGHT side; if (index == 15) { // rightmost tile side.a = side.d = -Parent.ViewPosition.Y + Tile.DEFAULT_HEIGHT_Y; } else { side = GetTileHeight(TileB, TileB, TileC, TileC); } // draw if tile to the right is lower than current tile if (height.b < side.a || height.c < side.d) { Vertices[0] = Vector3.Transform(new Vector3(x2, height.b, z1), Parent.D3DTS_WORLD); Vertices[1] = Vector3.Transform(new Vector3(x2, side.a, z1), Parent.D3DTS_WORLD); Vertices[2] = Vector3.Transform(new Vector3(x2, side.d, z2), Parent.D3DTS_WORLD); Vertices[3] = Vector3.Transform(new Vector3(x2, height.c, z2), Parent.D3DTS_WORLD); DisplayListManager.AddPrimitive(renderMode, Vertices, 4, Parent.GetColor(colorIndex - 1)); } } // if tile is flat if (TileA.IsFlat) { // Draw the front of the cube if: // - This is the last row to render // OR // - Tile in front is lower than current tile (or empty) TILE_HEIGHT side; if (Row.Count == 0) { side.c = side.d = -Parent.ViewPosition.Y + Tile.DEFAULT_HEIGHT_Y; } else { Tile TileG = Memory[Row.Prev + ((index + 0) & 15)]; Tile TileH = Memory[Row.Prev + ((index + 10) & 15)]; side = GetTileHeight(TileG, TileH, TileB, TileA); } if (height.a < side.d || height.b < side.c) { Vertices[0] = Vector3.Transform(new Vector3(x1, height.a, z1), Parent.D3DTS_WORLD); Vertices[1] = Vector3.Transform(new Vector3(x1, side.d, z1), Parent.D3DTS_WORLD); Vertices[2] = Vector3.Transform(new Vector3(x2, side.c, z1), Parent.D3DTS_WORLD); Vertices[3] = Vector3.Transform(new Vector3(x2, height.b, z1), Parent.D3DTS_WORLD); DisplayListManager.AddPrimitive(renderMode, Vertices, 4, Parent.GetColor(colorIndex - 2)); } } // draw the top of the cube Vertices[0] = Vector3.Transform(new Vector3(x1, height.a, z1), Parent.D3DTS_WORLD); Vertices[1] = Vector3.Transform(new Vector3(x2, height.b, z1), Parent.D3DTS_WORLD); Vertices[2] = Vector3.Transform(new Vector3(x2, height.c, z2), Parent.D3DTS_WORLD); Vertices[3] = Vector3.Transform(new Vector3(x1, height.d, z2), Parent.D3DTS_WORLD); DisplayListManager.AddPrimitive(renderMode, Vertices, 4, Parent.GetColor(colorIndex)); // special case for rendering solid sloped tiles // this is done for maximum compatibility with real machine if (!TileA.IsFlat && renderMode == Mathbox.RenderMode.Polygon) { DisplayListManager.AddPrimitive(Mathbox.RenderMode.Vector, Vertices, 4, Parent.GetColor(colorIndex)); } // draw the bottom of the cube Vertices[0] = Vector3.Transform(new Vector3(x1, -Parent.ViewPosition.Y + Tile.DEFAULT_HEIGHT_Y, z1), Parent.D3DTS_WORLD); Vertices[1] = Vector3.Transform(new Vector3(x1, -Parent.ViewPosition.Y + Tile.DEFAULT_HEIGHT_Y, z2), Parent.D3DTS_WORLD); Vertices[2] = Vector3.Transform(new Vector3(x2, -Parent.ViewPosition.Y + Tile.DEFAULT_HEIGHT_Y, z2), Parent.D3DTS_WORLD); Vertices[3] = Vector3.Transform(new Vector3(x2, -Parent.ViewPosition.Y + Tile.DEFAULT_HEIGHT_Y, z1), Parent.D3DTS_WORLD); DisplayListManager.AddPrimitive(renderMode, Vertices, 4, Parent.GetColor(colorIndex)); }
/// <summary> /// Parses the list of objects to render /// </summary> /// <param name="address">base address of object list</param> public void ParseObjectList(UInt16 address) { // address+0 Object position.X // address+1 Object position.Y // address+2 Object position.Z // address+3 Object Instruction // address+4 Address of view matrix // address+5 Address of rotation matrix (only if specified by instruction) // address+6 Address of surface list (only if specified by instruction) // address +5 or + 7 first child object #if false Debug.WriteLine($"{Memory[address+0].HexString()} {Memory[address + 1].HexString()} {Memory[address + 2].HexString()} {Memory[address + 3].HexString()} {Memory[address + 4].HexString()} {Memory[address + 5].HexString()} {Memory[address + 6].HexString()}"); #endif for (; ;) { // check for end of list if (address == 0 || address >= 0x8000) { return; } // Control word encoding // 0x8000 = stop flag // 0x4000 = don't load camera matrix // 0x1000 = ? // 0x0800 = don't load base address or rotation matrix // 0x0400 = x/y/z value is relative offset, not absolute position Mathbox.ObjectInstruction opcode = Memory[address + 3]; // load camera matrix if (!opcode.SkipObjectRotation) { Parent.LoadViewMatrix(Memory[address + 4]); } // Get new primitive list address and rotation matrix if they exist int childAddr = address + 5; if (!opcode.UsePreviousObjectPointsAndFaces) { PrimitiveListAddress = Memory[address + 6]; if (PrimitiveListAddress >= 0x8000) { return; } Parent.LoadRotationMatrix(Memory[address + 5]); childAddr += 2; } // Don't render invalid objects if (PrimitiveListAddress >= 0x8000) { return; } // Determine position of object Vector3 pt = Parent.GetVectorAt(address); if (opcode.ObjectPositionIsRelative) { // relative position Parent.WorldPosition += Vector3.Transform(pt, Parent.ViewRotation); } else { // absolute position pt -= Parent.ViewPosition; Parent.WorldPosition = Vector3.Transform(pt, Parent.ViewRotation); } Parent.SetWorldMatrix(ref Parent.WorldPosition, ref Parent.WorldRotation); #if WIDESCREEN_STARS if (PrimitiveListAddress >= 0x4AE8 && PrimitiveListAddress <= 0x4B44) { for (int n = 0; n < Stars.Length; n++) { Vertices[n] = Vector3.Transform(Stars[n], Parent.D3DTS_WORLD); } DisplayListManager.AddPrimitive(Mathbox.RenderMode.Dot, Vertices, Stars.Length, Parent.GetColor(7)); return; } #endif // parese the surfaces in this object ParsePrimitiveList(PrimitiveListAddress); // parse all child objects for (; ;) { UInt16 child = Memory[childAddr++]; if (child == 0 || child >= 0x8000) { return; } if (child == 0x0002) { address += 8; break; } ParseObjectList(child); } } }