AString cCaveTunnel::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const { AString SVG; SVG.reserve(m_Points.size() * 20 + 200); AppendPrintf(SVG, "<path style=\"fill:none;stroke:#%06x;stroke-width:1px;\"\nd=\"", a_Color); char Prefix = 'M'; // The first point needs "M" prefix, all the others need "L" for (cCaveDefPoints::const_iterator itr = m_Points.begin(); itr != m_Points.end(); ++itr) { AppendPrintf(SVG, "%c %d, %d ", Prefix, a_OffsetX + itr->m_BlockX, a_OffsetZ + itr->m_BlockZ); Prefix = 'L'; } SVG.append("\"/>\n"); return SVG; }
void cCaveTunnel::CalcBoundingBox(void) { m_MinBlockX = m_MaxBlockX = m_Points.front().m_BlockX; m_MinBlockY = m_MaxBlockY = m_Points.front().m_BlockY; m_MinBlockZ = m_MaxBlockZ = m_Points.front().m_BlockZ; for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr) { m_MinBlockX = std::min(m_MinBlockX, itr->m_BlockX - itr->m_Radius); m_MaxBlockX = std::max(m_MaxBlockX, itr->m_BlockX + itr->m_Radius); m_MinBlockY = std::min(m_MinBlockY, itr->m_BlockY - itr->m_Radius); m_MaxBlockY = std::max(m_MaxBlockY, itr->m_BlockY + itr->m_Radius); m_MinBlockZ = std::min(m_MinBlockZ, itr->m_BlockZ - itr->m_Radius); m_MaxBlockZ = std::max(m_MaxBlockZ, itr->m_BlockZ + itr->m_Radius); } // for itr - m_Points[] }
bool cCaveTunnel::RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints & a_Dst) { if (a_Src.size() < 2) { // There are no midpoints, nothing to smooth return true; } // Smoothing: for each line segment, add points on its 1 / 4 lengths bool res = false; size_t Num = a_Src.size() - 2; // this many intermediary points a_Dst.clear(); a_Dst.reserve(Num * 2 + 2); cCaveDefPoints::const_iterator itr = a_Src.begin() + 1; const cCaveDefPoint & Source = a_Src.front(); a_Dst.push_back(Source); int PrevX = Source.m_BlockX; int PrevY = Source.m_BlockY; int PrevZ = Source.m_BlockZ; int PrevR = Source.m_Radius; for (size_t i = 0; i <= Num; ++i, ++itr) { int dx = itr->m_BlockX - PrevX; int dy = itr->m_BlockY - PrevY; int dz = itr->m_BlockZ - PrevZ; if (abs(dx) + abs(dz) + abs(dy) < 6) { // Too short a segment to smooth-subdivide into quarters PrevX = itr->m_BlockX; PrevY = itr->m_BlockY; PrevZ = itr->m_BlockZ; PrevR = itr->m_Radius; continue; } int dr = itr->m_Radius - PrevR; int Rad1 = std::max(PrevR + 1 * dr / 4, 1); int Rad2 = std::max(PrevR + 3 * dr / 4, 1); a_Dst.push_back(cCaveDefPoint(PrevX + 1 * dx / 4, PrevY + 1 * dy / 4, PrevZ + 1 * dz / 4, Rad1)); a_Dst.push_back(cCaveDefPoint(PrevX + 3 * dx / 4, PrevY + 3 * dy / 4, PrevZ + 3 * dz / 4, Rad2)); PrevX = itr->m_BlockX; PrevY = itr->m_BlockY; PrevZ = itr->m_BlockZ; PrevR = itr->m_Radius; res = true; } a_Dst.push_back(a_Src.back()); return res && (a_Src.size() < a_Dst.size()); }
void cCaveTunnel::Randomize(cNoise & a_Noise) { // Repeat 4 times: for (int i = 0; i < 4; i++) { // For each already present point, insert a point in between it and its predecessor, shifted randomly. cCaveDefPoint & Point = m_Points.front(); int PrevX = Point.m_BlockX; int PrevY = Point.m_BlockY; int PrevZ = Point.m_BlockZ; int PrevR = Point.m_Radius; cCaveDefPoints Pts; Pts.reserve(m_Points.size() * 2 + 1); Pts.push_back(Point); for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr) { int Random = a_Noise.IntNoise3DInt(PrevX, PrevY, PrevZ + i) / 11; int len = (PrevX - itr->m_BlockX) * (PrevX - itr->m_BlockX); len += (PrevY - itr->m_BlockY) * (PrevY - itr->m_BlockY); len += (PrevZ - itr->m_BlockZ) * (PrevZ - itr->m_BlockZ); len = 3 * static_cast<int>(sqrt(static_cast<double>(len))) / 4; int Rad = std::min(MAX_RADIUS, std::max(MIN_RADIUS, (PrevR + itr->m_Radius) / 2 + (Random % 3) - 1)); Random /= 4; int x = (itr->m_BlockX + PrevX) / 2 + (Random % (len + 1) - len / 2); Random /= 256; int y = (itr->m_BlockY + PrevY) / 2 + (Random % (len / 2 + 1) - len / 4); Random /= 256; int z = (itr->m_BlockZ + PrevZ) / 2 + (Random % (len + 1) - len / 2); Pts.push_back(cCaveDefPoint(x, y, z, Rad)); Pts.push_back(*itr); PrevX = itr->m_BlockX; PrevY = itr->m_BlockY; PrevZ = itr->m_BlockZ; PrevR = itr->m_Radius; } std::swap(Pts, m_Points); } }
void cCaveTunnel::ProcessChunk( int a_ChunkX, int a_ChunkZ, cChunkDef::BlockTypes & a_BlockTypes, cChunkDesc::BlockNibbleBytes & a_BlockMetas, cChunkDef::HeightMap & a_HeightMap ) { int BaseX = a_ChunkX * cChunkDef::Width; int BaseZ = a_ChunkZ * cChunkDef::Width; if ( (BaseX > m_MaxBlockX) || (BaseX + cChunkDef::Width < m_MinBlockX) || (BaseZ > m_MaxBlockZ) || (BaseZ + cChunkDef::Width < m_MinBlockZ) ) { // Tunnel does not intersect the chunk at all, bail out return; } int BlockStartX = a_ChunkX * cChunkDef::Width; int BlockStartZ = a_ChunkZ * cChunkDef::Width; int BlockEndX = BlockStartX + cChunkDef::Width; int BlockEndZ = BlockStartZ + cChunkDef::Width; for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr) { if ( (itr->m_BlockX + itr->m_Radius < BlockStartX) || (itr->m_BlockX - itr->m_Radius > BlockEndX) || (itr->m_BlockZ + itr->m_Radius < BlockStartZ) || (itr->m_BlockZ - itr->m_Radius > BlockEndZ) ) { // Cannot intersect, bail out early continue; } // Carve out a sphere around the xyz point, m_Radius in diameter; skip 3 / 7 off the top and bottom: int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc int DifY = itr->m_BlockY; int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc int Bottom = std::max(itr->m_BlockY - 3 * itr->m_Radius / 7, 1); int Top = std::min(itr->m_BlockY + 3 * itr->m_Radius / 7, static_cast<int>(cChunkDef::Height)); int SqRad = itr->m_Radius * itr->m_Radius; for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++) { for (int y = Bottom; y <= Top; y++) { int SqDist = (DifX - x) * (DifX - x) + (DifY - y) * (DifY - y) + (DifZ - z) * (DifZ - z); if (4 * SqDist <= SqRad) { if (cBlockInfo::CanBeTerraformed(cChunkDef::GetBlock(a_BlockTypes, x, y, z))) { cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR); } } else if (SqDist <= SqRad * 2) { if (cChunkDef::GetBlock(a_BlockTypes, x, y, z) == E_BLOCK_SAND) { int Index = cChunkDef::MakeIndexNoCheck(x, y, z); if (a_BlockMetas[Index] == 1) { a_BlockMetas[Index] = 0; cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_RED_SANDSTONE); } else { cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_SANDSTONE); } } } } // for y } // for x, z } // for itr - m_Points[] /* #ifdef _DEBUG // For debugging purposes, outline the shape of the cave using glowstone, after carving the entire cave: for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr) { int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc if ( (DifX >= 0) && (DifX < cChunkDef::Width) && (itr->m_BlockY > 0) && (itr->m_BlockY < cChunkDef::Height) && (DifZ >= 0) && (DifZ < cChunkDef::Width) ) { cChunkDef::SetBlock(a_BlockTypes, DifX, itr->m_BlockY, DifZ, E_BLOCK_GLOWSTONE); } } // for itr - m_Points[] #endif // _DEBUG //*/ }
void cCaveTunnel::ProcessChunk( int a_ChunkX, int a_ChunkZ, cChunkDef::BlockTypes & a_BlockTypes, cChunkDef::HeightMap & a_HeightMap ) { int BaseX = a_ChunkX * cChunkDef::Width; int BaseZ = a_ChunkZ * cChunkDef::Width; if ( (BaseX > m_MaxBlockX) || (BaseX + cChunkDef::Width < m_MinBlockX) || (BaseX > m_MaxBlockX) || (BaseX + cChunkDef::Width < m_MinBlockX) ) { // Tunnel does not intersect the chunk at all, bail out return; } int BlockStartX = a_ChunkX * cChunkDef::Width; int BlockStartZ = a_ChunkZ * cChunkDef::Width; int BlockEndX = BlockStartX + cChunkDef::Width; int BlockEndZ = BlockStartZ + cChunkDef::Width; for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr) { if ( (itr->m_BlockX + itr->m_Radius < BlockStartX) || (itr->m_BlockX - itr->m_Radius > BlockEndX) || (itr->m_BlockZ + itr->m_Radius < BlockStartZ) || (itr->m_BlockZ - itr->m_Radius > BlockEndZ) ) { // Cannot intersect, bail out early continue; } // Carve out a sphere around the xyz point, m_Radius in diameter; skip 3/7 off the top and bottom: int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc int DifY = itr->m_BlockY; int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc int Bottom = std::max(itr->m_BlockY - 3 * itr->m_Radius / 7, 1); int Top = std::min(itr->m_BlockY + 3 * itr->m_Radius / 7, (int)(cChunkDef::Height)); int SqRad = itr->m_Radius * itr->m_Radius; for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++) { for (int y = Bottom; y <= Top; y++) { int SqDist = (DifX - x) * (DifX - x) + (DifY - y) * (DifY - y) + (DifZ - z) * (DifZ - z); if (4 * SqDist <= SqRad) { switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z)) { // Only carve out these specific block types case E_BLOCK_DIRT: case E_BLOCK_GRASS: case E_BLOCK_STONE: case E_BLOCK_COBBLESTONE: case E_BLOCK_GRAVEL: case E_BLOCK_SAND: case E_BLOCK_SANDSTONE: case E_BLOCK_NETHERRACK: case E_BLOCK_COAL_ORE: case E_BLOCK_IRON_ORE: case E_BLOCK_GOLD_ORE: case E_BLOCK_DIAMOND_ORE: case E_BLOCK_REDSTONE_ORE: case E_BLOCK_REDSTONE_ORE_GLOWING: { cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR); break; } default: break; } } } // for y } // for x, z } // for itr - m_Points[] /* #ifdef _DEBUG // For debugging purposes, outline the shape of the cave using glowstone, *after* carving the entire cave: for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr) { int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc if ( (DifX >= 0) && (DifX < cChunkDef::Width) && (itr->m_BlockY > 0) && (itr->m_BlockY < cChunkDef::Height) && (DifZ >= 0) && (DifZ < cChunkDef::Width) ) { cChunkDef::SetBlock(a_BlockTypes, DifX, itr->m_BlockY, DifZ, E_BLOCK_GLOWSTONE); } } // for itr - m_Points[] #endif // _DEBUG //*/ }