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 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 void SetLevel(BlastCorpsLevel level) { this.level = level; computeBounds(); 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); }
static void Main(string[] args) { if (args.Length < 1) { System.Console.WriteLine("Usage: BlastCorpsConsole <DIR with .raw levels> [DIR to output <level>.txt]"); return; } string tmpDir = Path.GetTempPath(); string inputDir = args[0]; string outputDir = null; if (args.Length > 1) { outputDir = args[1]; System.Console.WriteLine("Outputing data to {0}", outputDir); } tmpDir = Path.Combine(tmpDir, "BlastCorps"); System.Console.WriteLine("Saving generated levels to: " + tmpDir); Directory.CreateDirectory(tmpDir); foreach (BlastCorpsLevelMeta levelMeta in BlastCorpsRom.levelMeta) { string inputFile = Path.Combine(inputDir, levelMeta.filename + ".raw"); string outputFile = Path.Combine(tmpDir, levelMeta.filename + ".raw"); byte[] inData = File.ReadAllBytes(inputFile); BlastCorpsLevel level = BlastCorpsLevel.decodeLevel(inData, inData); if (outputDir != null) { string txtFile = Path.Combine(outputDir, levelMeta.filename + ".txt"); using (StreamWriter writer = new StreamWriter(txtFile)) { level.Write(writer, levelMeta); } } byte[] outData = level.ToBytes(); FileStream outStream = File.OpenWrite(outputFile); outStream.Write(outData, 0, outData.Length); outStream.Close(); bool passed = true; if (levelMeta.id == 14) { StatusPrint(ConsoleColor.Yellow, "level14 fails square hole off", "0175A5"); } if (inData.Length != outData.Length) { StatusPrint(ConsoleColor.Red, "Fail", levelMeta.filename + ".raw, lengths differ: " + inData.Length + " -> " + outData.Length); passed = false; } for (int i = 0; i < Math.Min(inData.Length, outData.Length); i++) { if (inData[i] != outData[i]) { StatusPrint(ConsoleColor.Red, "Fail", levelMeta.filename + ".raw, mismatch at " + i.ToString("X6")); passed = false; break; } } if (passed) { StatusPrint(ConsoleColor.Green, "Pass", levelMeta.filename + ".raw"); } } }