public void SetLevel(BlastCorpsLevel level) { this.level = level; levelModel = new Model3D(level); foreach (Vehicle veh in level.vehicles) { if (veh.type == 0) // player { location = new Vector3((veh.x + 20) * scale, (veh.y + 400) * scale, (veh.z - 400) * scale); pitch = -MathHelper.PiOver6; facing = MathHelper.PiOver2 + MathHelper.PiOver6; SetupViewport(); glControlViewer.Invalidate(); break; } } if (loaded) { glControlViewer.Invalidate(); } }
public void parseModel(BlastCorpsLevel level) { byte[] displayList = level.displayList; Vertex[] vertexBuffer = new Vertex[16]; Stack<uint> dlStack = new Stack<uint>(); uint segAddress; uint segOffset; byte bank; Texture nextTexture = new Texture(); Triangle nextTri = new Triangle(); List<UInt32> displayLists = new List<UInt32>(); displayLists.Add(level.header.offsets[(0x88 - 0x20) / 4]); displayLists.Add(level.header.offsets[(0x90 - 0x20) / 4]); displayLists.Add(level.header.offsets[(0x94 - 0x20) / 4]); displayLists.Add(level.header.offsets[(0x98 - 0x20) / 4]); displayLists.Add(level.header.offsets[(0x9C - 0x20) / 4]); foreach (uint startOffset in displayLists) { bool done = false; for (uint offset = startOffset - level.header.dlOffset; offset < displayList.Length && !done; offset += 8) { UInt32 w0 = BE.U32(displayList, offset); UInt32 w1 = BE.U32(displayList, offset + 4); byte command = displayList[offset]; switch (command) { case F3D_MOVEMEM: break; case F3D_VTX: int count = ((displayList[offset + 1] >> 4) & 0xF) + 1; int index = (displayList[offset + 1]) & 0xF; segAddress = w1; bank = displayList[offset + 4]; segOffset = segAddress & 0x00FFFFFF; LoadVertices(level.vertData, segOffset, index, count, vertexBuffer); break; case F3D_DL: segAddress = w1; bank = displayList[offset + 4]; segOffset = segAddress & 0x00FFFFFF; dlStack.Push(offset); offset = segOffset - 8; // subtract 8 since for loop will increment by 8 break; case F3D_QUAD: break; case F3D_CLRGEOMODE: break; case F3D_SETGEOMODE: break; case F3D_ENDDL: if (dlStack.Count == 0) { done = true; } else { offset = dlStack.Pop(); } break; case F3D_TEXTURE: // reset tile sizes nextTexture = new Texture(); break; case F3D_TRI1: int vertex0 = displayList[offset + 5] / 0x0A; int vertex1 = displayList[offset + 6] / 0x0A; int vertex2 = displayList[offset + 7] / 0x0A; Triangle tri = new Triangle(vertexBuffer[vertex0], vertexBuffer[vertex1], vertexBuffer[vertex2]); tri.texture = nextTexture; triangles.Add(tri); break; case G_SETTILESIZE: if (nextTexture.width < 0) { nextTexture.width = (((displayList[offset + 5] << 8) | (displayList[offset + 6] & 0xF0)) >> 6) + 1; nextTexture.height = (((displayList[offset + 6] & 0x0F) << 8 | displayList[offset + 7]) >> 2) + 1; } if (nextTexture.IsComplete()) { textures.Add(nextTexture); } break; case G_LOADBLOCK: break; case G_SETTILE: break; case G_SETFOGCOLOR: break; case G_SETENVCOLOR: break; case G_SETCOMBINE: break; case G_SETTIMG: segAddress = w1; bank = displayList[offset + 4]; segOffset = segAddress & 0x00FFFFFF; nextTexture.address = segAddress; if (nextTexture.IsComplete()) { textures.Add(nextTexture); } break; } } } }
public Model3D(BlastCorpsLevel level) { parseModel(level); }
public static void ExportDisplayList(BlastCorpsRom rom, BlastCorpsLevel level, string filename, float scale) { Model3D model = new Model3D(level); string mtlFileName = filename + ".mtl"; using (System.IO.StreamWriter file = new System.IO.StreamWriter(filename)) { int vertCount = 1; file.WriteLine(fileHeader()); file.WriteLine("mtllib {0}", Path.GetFileName(mtlFileName)); foreach (Triangle tri in model.triangles) { float uScale = 32.0f * tri.texture.width; float vScale = 32.0f * tri.texture.height; file.WriteLine(); file.WriteLine("usemtl Texture{0:X4}", tri.texture.address); file.WriteLine(toObjVert(tri.vertices[0], scale, uScale, vScale)); file.WriteLine(toObjVert(tri.vertices[1], scale, uScale, vScale)); file.WriteLine(toObjVert(tri.vertices[2], scale, uScale, vScale)); file.WriteLine("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}", vertCount, (vertCount + 1), (vertCount + 2)); vertCount += 3; } } // create textures directory string textureDirName = "textures"; string textureDir = Path.Combine(Path.GetDirectoryName(filename), textureDirName); Directory.CreateDirectory(textureDir); using (System.IO.StreamWriter file = new System.IO.StreamWriter(mtlFileName)) { List<uint> processedTextures = new List<uint>(); file.WriteLine(fileHeader()); foreach (Texture t in model.textures) { if (!processedTextures.Contains(t.address)) { string textureFilename = String.Format("{0:X4}.png", t.address); string textureFile = String.Format(textureDirName + "/" + textureFilename); file.WriteLine("newmtl Texture{0:X4}", t.address); file.WriteLine("Ka 0.0 0.0 0.0"); // ambiant color file.WriteLine("Kd 1.0 1.0 1.0"); // diffuse color file.WriteLine("Ks 0.3 0.3 0.3"); // specular color file.WriteLine("d 1"); // dissolved file.WriteLine("map_Kd {0}", textureFile); file.WriteLine(); BlastCorpsTexture bct = new BlastCorpsTexture(rom.GetRawData(), t.address, 0); bct.decode(); byte[] n64Texture = bct.GetInflated(); N64Format format = N64Format.RGBA; int depth = 16; switch (bct.type) { case 0: // IA8? // TODO: memcpy, no info format = N64Format.IA; depth = 8; break; case 1: // RBGA16? format = N64Format.RGBA; depth = 16; break; case 2: // RGBA32? format = N64Format.RGBA; depth = 32; break; case 3: // IA8? format = N64Format.IA; depth = 8; break; case 4: // IA16? format = N64Format.IA; depth = 16; break; case 5: // RGBA32? format = N64Format.RGBA; depth = 32; break; case 6: // IA8? format = N64Format.IA; depth = 8; break; } Bitmap loadedBitmap; switch (format) { case N64Format.RGBA: loadedBitmap = N64Graphics.RGBA(n64Texture, t.width, t.height, depth); break; case N64Format.IA: default: loadedBitmap = N64Graphics.IA(n64Texture, t.width, t.height, depth); break; } loadedBitmap.RotateFlip(RotateFlipType.RotateNoneFlipY); loadedBitmap.Save(Path.Combine(textureDir, textureFilename)); processedTextures.Add(t.address); } } } }
public static BlastCorpsLevel decodeLevel(byte[] levelData, byte[] displayListData) { BlastCorpsLevel level = new BlastCorpsLevel(); level.decodeHeader(levelData); // 0x00-0x1F level.saveVertices(levelData); // 0xC8-ammo level.decodeAmmoBoxes(levelData); // 0x20 level.decodeCollision24(levelData); // 0x24 level.decodeCommPoints(levelData); // 0x28 level.decodeAnimatedTextures(levelData);// 0x2C level.decodeTerrain(levelData); // 0x30 level.decodeRDUs(levelData); // 0x34 level.decodeTNTCrates(levelData); // 0x38 level.decodeSquareBlocks(levelData); // 0x3C level.decodeBounds40(levelData); // 0x40 level.decodeBounds44(levelData); // 0x44 level.decode48(levelData); // 0x48 TODO: decode these U32s level.decodeLevelBounds(levelData); // 0x4C level.decodeVehicles(levelData); // 0x50 level.decodeMissileCarrier(levelData); // 0x54 level.decode58(levelData); // 0x58 TODO level.decodeBuildings(levelData); // 0x5C level.decode60(levelData); // 0x60 TODO level.decode64(levelData); // 0x64 TODO level.decodeTrainPlatform(levelData); // 0x68 level.decodeCollision6C(levelData); // 0x6C level.decodeCollision70(levelData); // 0x70 level.decode74(levelData); // 0x74 TODO // 0x78-0x9C are located in the display list data level.copyLevelData = levelData; level.displayList = displayListData; return level; }
public void SetLevel(BlastCorpsLevel level) { this.level = level; computeBounds(); Invalidate(); }