// HeightMap.cpp // Implements the cHeightMap class representing a cCallback descendant that draws a B & W map of heights for the world #include "Globals.h" #include "HeightMap.h" static const unsigned char g_BMPHeader[] = { 0x42, 0x4D, 0x36, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } ; cHeightMap::cHeightMap(void) : m_CurrentRegionX(0), m_CurrentRegionZ(0), m_IsCurrentRegionValid(false) { } void cHeightMap::Finish(void) { if (m_IsCurrentRegionValid) { StartNewRegion(0, 0); } } bool cHeightMap::OnNewChunk(int a_ChunkX, int a_ChunkZ) { int RegionX = (a_ChunkX < 0) ? (a_ChunkX - 31) / 32 : a_ChunkX / 32; int RegionZ = (a_ChunkZ < 0) ? (a_ChunkZ - 31) / 32 : a_ChunkZ / 32; if ((RegionX != m_CurrentRegionX) || (RegionZ != m_CurrentRegionZ)) { if (m_IsCurrentRegionValid) { StartNewRegion(RegionX, RegionZ); } m_CurrentRegionX = RegionX; m_CurrentRegionZ = RegionZ; } m_IsCurrentRegionValid = true; m_CurrentChunkX = a_ChunkX; m_CurrentChunkZ = a_ChunkZ; m_CurrentChunkOffX = m_CurrentChunkX - m_CurrentRegionX * 32; m_CurrentChunkOffZ = m_CurrentChunkZ - m_CurrentRegionZ * 32; memset(m_BlockTypes, 0, sizeof(m_BlockTypes)); return false; } bool cHeightMap::OnHeightMap(const int * a_HeightMapBE) { ASSERT(m_CurrentChunkOffX >= 0); ASSERT(m_CurrentChunkOffX < 32); ASSERT(m_CurrentChunkOffZ >= 0); ASSERT(m_CurrentChunkOffZ < 32); int * BaseHeight = m_Height + m_CurrentChunkOffZ * 16 * 512 + m_CurrentChunkOffX * 16; for (int z = 0; z < 16; z++) { int * Row = BaseHeight + z * 512; for (int x = 0; x < 16; x++) { Row[x] = ntohl(a_HeightMapBE[z * 16 + x]); } } // for z return false; // Still want blockdata to remove trees from the heightmap } bool cHeightMap::OnSection( unsigned char a_Y, const BLOCKTYPE * a_BlockTypes, const NIBBLETYPE * a_BlockAdditional, const NIBBLETYPE * a_BlockMeta, const NIBBLETYPE * a_BlockLight, const NIBBLETYPE * a_BlockSkyLight ) { // Copy the section data into the appropriate place in the internal buffer memcpy(m_BlockTypes + a_Y * 16 * 16 * 16, a_BlockTypes, 16 * 16 * 16); return false; } bool cHeightMap::OnSectionsFinished(void) { // Remove trees from the heightmap: for (int z = 0; z < 16; z++) { for (int x = 0; x < 16; x++) { for (int y = m_Height[512 * (16 * m_CurrentChunkOffZ + z) + 16 * m_CurrentChunkOffX + x]; y >= 0; y--) { if (IsGround(m_BlockTypes[256 * y + 16 * z + x])) { m_Height[512 * (16 * m_CurrentChunkOffZ + z) + 16 * m_CurrentChunkOffX + x] = y; break; // for y } } // for y } // for x } // for z return true; } void cHeightMap::StartNewRegion(int a_RegionX, int a_RegionZ) { AString FileName; Printf(FileName, "Height.%d.%d.bmp", m_CurrentRegionX, m_CurrentRegionZ); cFile f; if (!f.Open(FileName, cFile::fmWrite)) { LOG("Cannot open file \"%s\" for writing the height map. Data for this region lost.", FileName.c_str()); } else { f.Write(g_BMPHeader, sizeof(g_BMPHeader)); for (int z = 0; z < 512; z++) { int RowData[512]; int * HeightRow = m_Height + z * 512; for (int x = 0; x < 512; x++) { RowData[x] = std::max(std::min(HeightRow[x], 255), 0) * 0x010101; } f.Write(RowData, sizeof(RowData)); } // for z } memset(m_Height, 0, sizeof(m_Height)); m_CurrentRegionX = a_RegionX; m_CurrentRegionZ = a_RegionZ; } bool cHeightMap::IsGround(BLOCKTYPE a_BlockType) { // Name all blocks that are NOT ground, return false for them: switch (a_BlockType) { case E_BLOCK_AIR: case E_BLOCK_BED: case E_BLOCK_BEETROOTS: case E_BLOCK_BREWING_STAND: case E_BLOCK_BROWN_MUSHROOM: case E_BLOCK_CACTUS: case E_BLOCK_CAKE: case E_BLOCK_CARROTS: case E_BLOCK_CAULDRON: case E_BLOCK_CHEST: case E_BLOCK_CHORUS_FLOWER: case E_BLOCK_CHORUS_PLANT: case E_BLOCK_COBBLESTONE_WALL: case E_BLOCK_COBWEB: case E_BLOCK_COCOA_POD: case E_BLOCK_CROPS: case E_BLOCK_DEAD_BUSH: case E_BLOCK_DETECTOR_RAIL: case E_BLOCK_DIRT: case E_BLOCK_DRAGON_EGG: case E_BLOCK_END_GATEWAY: case E_BLOCK_END_PORTAL: case E_BLOCK_END_ROD: case E_BLOCK_ENDER_CHEST: case E_BLOCK_FENCE: case E_BLOCK_FENCE_GATE: case E_BLOCK_FIRE: case E_BLOCK_FLOWER_POT: case E_BLOCK_HEAD: case E_BLOCK_IRON_BARS: case E_BLOCK_LADDER: case E_BLOCK_LAVA: case E_BLOCK_LEAVES: case E_BLOCK_LEVER: case E_BLOCK_LILY_PAD: case E_BLOCK_LOG: // NOTE: This block is actually solid, but we don't want it because it's the thing that trees are made of, and we're getting rid of trees case E_BLOCK_MELON: case E_BLOCK_MELON_STEM: case E_BLOCK_NETHER_BRICK_FENCE: case E_BLOCK_NETHER_PORTAL: case E_BLOCK_POWERED_RAIL: case E_BLOCK_PUMPKIN: case E_BLOCK_PUMPKIN_STEM: case E_BLOCK_RAIL: case E_BLOCK_RED_ROSE: case E_BLOCK_RED_MUSHROOM: case E_BLOCK_REDSTONE_REPEATER_OFF: case E_BLOCK_REDSTONE_REPEATER_ON: case E_BLOCK_REDSTONE_TORCH_OFF: case E_BLOCK_REDSTONE_TORCH_ON: case E_BLOCK_REDSTONE_WIRE: case E_BLOCK_REEDS: case E_BLOCK_SAPLING: case E_BLOCK_SIGN_POST: case E_BLOCK_SNOW: case E_BLOCK_STATIONARY_LAVA: case E_BLOCK_STATIONARY_WATER: case E_BLOCK_STONE_BUTTON: case E_BLOCK_STONE_PRESSURE_PLATE: case E_BLOCK_STRUCTURE_VOID: case E_BLOCK_TALL_GRASS: case E_BLOCK_TORCH: case E_BLOCK_TRIPWIRE: case E_BLOCK_TRIPWIRE_HOOK: case E_BLOCK_VINES: case E_BLOCK_WALLSIGN: case E_BLOCK_WATER: case E_BLOCK_WOODEN_BUTTON: case E_BLOCK_WOODEN_PRESSURE_PLATE: case E_BLOCK_YELLOW_FLOWER: { return false; } } return true; } //////////////////////////////////////////////////////////////////////////////// // cHeightMapFactory: cHeightMapFactory::~cHeightMapFactory() { // Force all threads to save their last regions: for (cCallbacks::iterator itr = m_Callbacks.begin(), end = m_Callbacks.end(); itr != end; ++itr) { ((cHeightMap *)(*itr))->Finish(); } // TODO: Join all the files into one giant image file }