static void FillParcel(IntermediateMap map, Parcel parcel, List <Parcel> loadedParcels, int offsetX, int offsetY, int offsetZ, bool hasDonePalette, ParcelJsonConfiguration configuration) { //If no sourcefile, skip, well, sourcefile! and just plop in plots if (!emptySourceFileNames.Contains(parcel.sourceFile)) { ParcelVoxReader parcelReader = new ParcelVoxReader(); VoxReader r = new VoxReader(parcel.sourceFile, parcelReader); r.Read(); parcelReader.insertReadParcel(map, offsetX, offsetY, offsetZ); //Do the palette if (!hasDonePalette) { hasDonePalette = loadPalette(map, parcelReader.palette); } } foreach (Plot plot in parcel.parcelPlots) { Parcel insertParcel = findMatchingParcel(plot, parcel.parcelPlots, loadedParcels, configuration); FillParcel(map, insertParcel, loadedParcels, offsetX + plot.offsetX, offsetY + plot.offsetY, offsetZ + plot.offsetZ, hasDonePalette, configuration); } }
void traverseCompositeTree(IModelTreeComponent component, IntermediateMap map, int offsetX, int offsetY, int offsetZ) { if (component is ModelLeaf) { ModelLeaf model = (ModelLeaf)component; for (int x = 0; x < model.sizeX; x++) { for (int y = 0; y < model.sizeY; y++) { for (int z = 0; z < model.sizeZ; z++) { int colourIndex = model.data[x, y, z]; //Implicit conversion map.setBlockAt(offsetX + x, offsetY + y, offsetZ + z, new Block(colourIndex)); } } } } else if (component is ModelTree) { ModelTree tree = (ModelTree)component; foreach (IModelTreeComponent child in tree.components) { traverseCompositeTree(child, map, offsetX + tree.offsetX, offsetY + tree.offsetY, offsetZ + tree.offsetZ); } } }
public static IntermediateMap CreateMap(ParcelJsonConfiguration configuration) { IntermediateMap emptyMap = new IntermediateMap(configuration.primaryParcel.sizeX, configuration.primaryParcel.sizeY, configuration.primaryParcel.sizeZ, Block.AIR, new Palette(0)); List <Parcel> loadedParcels = createDivisionParcels(configuration.plotDivisions); loadedParcels.AddRange(configuration.insertParcels); FillParcel(emptyMap, configuration.primaryParcel, loadedParcels, 0, 0, 0, false, configuration); return(emptyMap); }
static bool loadPalette(IntermediateMap map, byte[,] paletteData) { try { Palette palette = new Palette(paletteData); map.palette = palette; return(true); } catch (Exception ignored) { Console.WriteLine("Exception loading map palette"); } return(false); }
static void PrintMapProgram(IntermediateMap map, ProgramConfiguration programConfig) { if (programConfig.mirrorMap) { map.mirrorOnY(); Console.WriteLine("Mirrored map"); } string outname = programConfig.outputName; if (outname == null || outname == "") { var random = new Random(); int index = random.Next(int.MaxValue); outname = "DodoTool" + index; } Console.WriteLine("Writting map to file. Might take a couple of seconds..."); IMToVXL.IMToVoxel(map, outname + ".vox", programConfig.modelSizeX, programConfig.modelSizeY, programConfig.modelSizeZ); Console.WriteLine("There we are. There should now be a nice " + outname + ".vox with your map :)"); Console.WriteLine("Press enter to close"); Console.ReadLine(); }
static void ParcelProgram(ProgramConfiguration programConfig) { Console.WriteLine("Running parcel program"); ParcelJsonConfiguration configuration; try { string configFileContent = File.ReadAllText(programConfig.parcelConfiguration + ".json"); configuration = JsonConvert.DeserializeObject <ParcelJsonConfiguration>(configFileContent); } catch (Exception e) { Console.WriteLine("Error parsing the parcel config json. Maybe some of this text will help you find the issue: "); Console.WriteLine(e.Message); return; } Console.WriteLine("Read parcel config"); IntermediateMap map = CreateParcelMap.CreateMap(configuration); Console.WriteLine("Generated parcel map"); PrintMapProgram(map, programConfig); }
static void ConverterProgram(ProgramConfiguration programConfig) { byte[] AoSMapData; Console.WriteLine("Running Ace of Spades to MagicaVoxel map conversion tool"); try { AoSMapData = File.ReadAllBytes(programConfig.aosFile + ".vxl"); } catch (Exception) { Console.WriteLine("Failed to read AoS map file"); return; } Console.WriteLine("Alright, here I go parsing the Ace of Spades map. This might take a few seconds..."); IntermediateMap map = AceOfSpadesToIM.ToIntermediateMap(AoSMapData, programConfig.keepOcean); int colorCount = map.palette.palette.Count; Console.WriteLine("Parsing went well. Map height detected to be " + map.sizeZ + " with " + colorCount + " unique colors"); Console.WriteLine("Condensing colors. This might take a second..."); map.PaletteShrink(Math.Min(programConfig.colourCount, colorCount)); //Use shrink even if at acceptable number of colors to shuffle indexes away from reserved spots Console.WriteLine("Shrunk colors."); PrintMapProgram(map, programConfig); }
public static void IMToVoxel(IntermediateMap map, string outfileName, int modelSizeX, int modelSizeY, int modelSizeZ) { FileStream stream = new FileStream(outfileName, FileMode.Create, FileAccess.Write, FileShare.Write); BinaryWriter writer = new BinaryWriter(stream); //Standard opening writer.Write("VOX ".ToCharArray()); writer.Write(150); //Version writer.Write("MAIN".ToCharArray()); writer.Write(0); //No chunk data in main //List of chunks List <RiffChunk> chunks = new List <RiffChunk>(); Palette palette = map.palette; //Setup for staging nodes ResetNodeID(); IDForGroup = new List <int>(); int masterGRPID = GetNextNodeID(); //Pass to master Translate List <RiffChunk> modelStagings = new List <RiffChunk>(); int modelID = 0; //Seperate the world into regions that become a model each int mapheight = map.sizeZ; for (int x = 0; x < map.sizeX; x += modelSizeX) { Console.Write("|"); for (int y = 0; y < map.sizeY; y += modelSizeY) { for (int z = 0; z < mapheight + modelSizeZ; z += modelSizeZ) //Map may not be that high, but getListOfVoxels will just return nothing for out of bounds values. { //The function will limit sizes to actual map boundaries List <Voxel> voxels = map.getListOfVoxels(x, x + modelSizeX, y, y + modelSizeY, z, z + modelSizeZ); //Find out if chunk even has any voxels if (voxels.Count == 0) { //Don't create the object if there are no voxels continue; } //I've not checked that this is just xyz, assuming it is RiffChunk size = new RiffChunk("SIZE"); size.addData(new IntChunkData(modelSizeX)); size.addData(new IntChunkData(modelSizeY)); size.addData(new IntChunkData(modelSizeZ)); chunks.Add(size); //Get a region of voxels and make them a model RiffChunk xyzi = new RiffChunk("XYZI"); xyzi.addData(new IntChunkData(voxels.Count)); foreach (Voxel voxel in voxels) { try { xyzi.addData(new VoxelChunkData(Convert.ToByte(voxel.x - x), Convert.ToByte(voxel.y - y), Convert.ToByte(voxel.z - z), Convert.ToByte(voxel.colorIndex))); } catch (Exception e) { Console.WriteLine("Byte overflow with voxel " + voxel.x + ", " + voxel.y + ", " + voxel.z + ". With object xyz being " + x + ", " + y + "," + z); Console.WriteLine("Color index of " + voxel.colorIndex); throw e; } } chunks.Add(xyzi); //Create shape and translate node for the model modelStagings.AddRange(CreateStagingChunksForModel(x, y, z, modelID, modelSizeX, modelSizeY, modelSizeZ)); //Create translate and shape noded for model //ModelID isn't written to file, it's just the order the models appear modelID++; } } } //Master group node RiffChunk masterGRP = new RiffChunk("nGRP"); masterGRP.addData(new IntChunkData(masterGRPID)); masterGRP.addData(new DictChunkData()); //empty dict masterGRP.addData(new IntChunkData(modelID)); //Number of models foreach (int nodeID in IDForGroup) { masterGRP.addData(new IntChunkData(nodeID)); } //Master translate node RiffChunk masterTRN = new RiffChunk("nTRN"); masterTRN.addData(new IntChunkData(0)); //Assuming top node has to be number 0 masterTRN.addData(new DictChunkData()); //Empty dict masterTRN.addData(new IntChunkData(masterGRPID)); masterTRN.addData(new IntChunkData(-1)); //Reserved masterTRN.addData(new IntChunkData(-1)); //Layer ID masterTRN.addData(new IntChunkData(1)); //Number of frames is always 1 masterTRN.addData(new DictChunkData()); //Empty dict. No translation of master //Put in all the staging chunks chunks.Add(masterTRN); chunks.Add(masterGRP); chunks.AddRange(modelStagings); //Add in all the layer chunks. This may be unneeded chunks.AddRange(CreateLayrChunks()); //RGBA palette chunk chunks.Add(palette.getPaletteChunk()); //Material chunks. May be unneeded for (int i = 1; i <= 256; i++) { chunks.Add(CreateMatlChunk(i)); } //Whatever the robj chunks are... chunks.AddRange(CreateROBJChunks()); //Write in the main chunk child size field int mainChunkChildDataSize = 0; foreach (RiffChunk chunk in chunks) { mainChunkChildDataSize += chunk.getChunkSize(); } writer.Write(mainChunkChildDataSize); Console.Write("|"); //Write all those chunks foreach (RiffChunk chunk in chunks) { chunk.writeChunk(writer); } Console.Write("|"); //Done writer.Close(); stream.Close(); Console.WriteLine(""); }
//private const int IMSizeZ = 256; //Size Z varies by map, but should be 256 at most. Figure out map height by lowest block and then add that in. public static IntermediateMap ToIntermediateMap(byte[] AoSMapData, bool keepOcean) { //Go through each column int columnStart = 0; int groundHeight = GroundHeight(AoSMapData); List <RGBColor> oceanColors = new List <RGBColor>(); int finalHeight = 256 - groundHeight; if (!keepOcean) { finalHeight--; } IntermediateMap mapResult; mapResult = new IntermediateMap(aosSizeX, aosSizeY, finalHeight, Block.AOSSOLID, new Palette(Palette.SECTORS_EDGE_RESERVED_INDEXES)); Palette palette = mapResult.palette; Block airBlock = new Block(Block.AIR, palette); for (int x = 0; x < aosSizeX; x++) { for (int y = 0; y < aosSizeY; y++) { int spanStart = columnStart; int z = finalHeight - 1; while (true) { //Read span metadata int number_4_byte_chunks = (int)AoSMapData[spanStart]; int top_color_start = (int)AoSMapData[spanStart + 1]; int top_color_end = (int)AoSMapData[spanStart + 2]; //Inclusive int surfaceBlockCount = top_color_end - top_color_start + 1; int i; //Offset into data bytes //Value where z = 0 is the bottom of the map int top_color_switched = finalHeight - top_color_start - 1; //Air run. Start at the latest z and go down to top_color_start for (/*Use z*/; z > top_color_switched; z--) { mapResult.setBlockAt(x, y, z, airBlock); } //Surface/top run for (i = 0; i < surfaceBlockCount; i++) { int blue = AoSMapData[spanStart + 4 + i * 4]; int green = AoSMapData[spanStart + 5 + i * 4]; int red = AoSMapData[spanStart + 6 + i * 4]; RGBColor color = new RGBColor(red, green, blue); if (z < 0) { oceanColors.Add(color); break; } mapResult.setBlockAt(x, y, z, new Block(palette, color)); if (z == 0 && keepOcean) //Ocean height { oceanColors.Add(color); break; } z--; } if (number_4_byte_chunks == 0) //Last span of column { //Update if this column was the highest. //Rest of the column is already solid, leave it //Next column is past first 4 span bytes and then past the number of surface blocks * the four bytes one of those takes columnStart = spanStart + 4 + 4 * surfaceBlockCount; break; } //Don't run over the solid area. It is already there. //Do the ceiling at the bottom of the span int ceilingBlockCount = (number_4_byte_chunks - 1) - surfaceBlockCount; int ceiling_color_end = AoSMapData[spanStart + number_4_byte_chunks * 4 + 3]; //Read next span air start; int ceiling_color_start = ceiling_color_end - ceilingBlockCount; z = finalHeight - ceiling_color_start - 1; //Jump down to top of bottom blocks //Continue from previous set of data bytes for (int j = i; j < i + ceilingBlockCount; j++) { int blue = AoSMapData[spanStart + 4 + j * 4]; int green = AoSMapData[spanStart + 5 + j * 4]; int red = AoSMapData[spanStart + 6 + j * 4]; mapResult.setBlockAt(x, y, z, new Block(palette, new RGBColor(red, green, blue))); z--; } //Set spanstart to next span. Z is already at the end of the previous ceiling spanStart += 4 * number_4_byte_chunks; } } Console.Write("|"); } RGBColor finalOcean = RGBColor.Combine(oceanColors); mapResult.oceanColor = finalOcean; Console.WriteLine(""); return(mapResult); }
public void insertReadParcel(IntermediateMap map, int offsetX, int offsetY, int offsetZ) { assembleTree(); traverseCompositeTree(masterNode, map, offsetX, offsetY, offsetZ); }