1
0

Initial WormNestCaves commit. Won't generate caves, only the schematic for caves' centers.

git-svn-id: http://mc-server.googlecode.com/svn/trunk@696 0a769ca7-a7f5-676a-18bf-c427514a06d6
This commit is contained in:
madmaxoft@gmail.com 2012-07-27 16:47:55 +00:00
parent a68b9bd986
commit b521456a01
3 changed files with 622 additions and 58 deletions

View File

@ -3,9 +3,31 @@
// Implements the various cave structure generators:
// - cStructGenWormNestCaves
// - cStructGenDualRidgeCaves
// - cStructGenMarbleCaves
// - cStructGenNetherCaves
/*
WormNestCave generator:
Caves are generated in "nests" - groups of tunnels generated from a single point.
For each chunk, all the nests that could intersect it are generated.
For each nest, first the schematic structure is generated (tunnel from ... to ..., branch, tunnel2 from ... to ...)
Then each tunnel is randomized by inserting points in between its ends.
Finally each tunnel is smoothed and Bresenham-3D-ed so that it is a collection of spheres with their centers next to each other.
When the tunnels are ready, they are simply carved into the chunk, one by one.
To optimize, each tunnel keeps track of its bounding box, so that it can be skipped for chunks that don't intersect it.
MarbleCaves generator:
For each voxel a 3D noise function is evaluated, if the value crosses a boundary, the voxel is dug out, otherwise it is kept.
Problem with this is the amount of CPU work needed for each chunk.
Also the overall shape of the generated holes is unsatisfactory - there are whole "sheets" of holes in the ground.
DualRidgeCaves generator:
Instead of evaluating a single noise function, two different noise functions are multiplied. This produces
regular tunnels instead of sheets. However due to the sheer amount of CPU work needed, the nosie functions need to be
reduced in complexity in order for this generator to be useful, so the caves' shapes are "bubbly" at best.
*/
#include "Globals.h"
#include "Caves.h"
@ -13,14 +35,23 @@
/// How many nests in each direction are generated for a given chunk. Must be an even number
#define NEIGHBORHOOD_SIZE 8
struct cDefPoint
{
int m_BlockX;
int m_BlockY;
int m_BlockZ;
int m_Radius;
cDefPoint(int a_BlockX, int a_BlockZ, int a_Radius) :
cDefPoint(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Radius) :
m_BlockX(a_BlockX),
m_BlockY(a_BlockY),
m_BlockZ(a_BlockZ),
m_Radius(a_Radius)
{
@ -33,29 +64,37 @@ typedef std::vector<cDefPoint> cDefPoints;
/// A single non-branching tunnel
/// A single non-branching tunnel of a WormNestCave
class cCaveTunnel
{
cDefPoints m_Points;
// The bounding box, including the radii around defpoints:
int m_MinBlockX, m_MaxBlockX;
int m_MinBlockY, m_MaxBlockY;
int m_MinBlockZ, m_MaxBlockZ;
/// Generates the shaping defpoints for the ravine, based on the ravine block coords and noise
void GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise);
void Randomize(cNoise & a_Noise);
/// Refines (adds and smooths) defpoints from a_Src into a_Dst
void RefineDefPoints(const cDefPoints & a_Src, cDefPoints & a_Dst);
/// Refines (adds and smooths) defpoints from a_Src into a_Dst; returns false if no refinement possible (segments too short)
bool RefineDefPoints(const cDefPoints & a_Src, cDefPoints & a_Dst);
/// Does one round of smoothing, two passes of RefineDefPoints()
/// Does rounds of smoothing, two passes of RefineDefPoints(), as long as they return true
void Smooth(void);
/// Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block
void FinishLinear(void);
public:
// Coords for which the ravine was generated (not necessarily the center)
int m_BlockX;
int m_BlockZ;
/// Calculates the bounding box of the points present
void CalcBoundingBox(void);
cCaveTunnel(int a_BlockStartX, int a_BlockStartZ, int a_Size, cNoise & a_Noise);
public:
cDefPoints m_Points;
cCaveTunnel(
int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ,
int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ,
cNoise & a_Noise
);
/// Carves the tunnel into the chunk specified
void ProcessChunk(
@ -63,6 +102,10 @@ public:
cChunkDef::BlockTypes & a_BlockTypes,
cChunkDef::HeightMap & a_HeightMap
);
#ifdef _DEBUG
AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const;
#endif // _DEBUG
} ;
typedef std::vector<cCaveTunnel *> cCaveTunnels;
@ -75,7 +118,11 @@ typedef std::vector<cCaveTunnel *> cCaveTunnels;
class cStructGenWormNestCaves::cCaveSystem
{
public:
cCaveSystem(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise);
// The generating block position; is read directly in cStructGenWormNestCaves::GetCavesForChunk()
int m_BlockX;
int m_BlockZ;
cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise);
~cCaveSystem();
/// Carves the cave system into the chunk specified
@ -85,12 +132,21 @@ public:
cChunkDef::HeightMap & a_HeightMap
);
#ifdef _DEBUG
AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const;
#endif // _DEBUG
protected:
int m_BlockX;
int m_BlockZ;
int m_Size;
cCaveTunnels m_Tunnels;
void Clear(void);
/// Generates a_Segment successive tunnels, with possible branches. Generates the same output for the same [x, y, z, a_Segments]
void GenerateTunnelsFromPoint(
int a_OriginX, int a_OriginY, int a_OriginZ,
cNoise & a_Noise, int a_Segments
);
} ;
@ -100,27 +156,389 @@ protected:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cCaveTunnel:
cCaveTunnel::cCaveTunnel(
int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ,
int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ,
cNoise & a_Noise
)
{
m_Points.push_back(cDefPoint(a_BlockStartX, a_BlockStartY, a_BlockStartZ, 5));
m_Points.push_back(cDefPoint(a_BlockEndX, a_BlockEndY, a_BlockEndZ, 5));
if ((a_BlockStartY <= 0) && (a_BlockEndY <= 0))
{
// Don't bother detailing this cave, it's under the world anyway
return;
}
Randomize(a_Noise);
Smooth();
// We know that the linear finishing won't affect the bounding box, so let's calculate it now, as we have less data:
CalcBoundingBox();
FinishLinear();
}
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.
int PrevX = m_Points.front().m_BlockX;
int PrevY = m_Points.front().m_BlockY;
int PrevZ = m_Points.front().m_BlockZ;
cDefPoints Pts;
Pts.reserve(m_Points.size() * 2);
Pts.push_back(m_Points.front());
for (cDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr)
{
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 * (int)sqrt((double)len) / 4;
int x = (itr->m_BlockX + PrevX) / 2 + ((a_Noise.IntNoise3DInt(PrevX, PrevY, PrevZ + i) / 17) % (len + 1) - len / 2);
int y = (itr->m_BlockY + PrevY) / 2 + ((a_Noise.IntNoise3DInt(PrevY, PrevZ, PrevX + i) / 17) % (len / 2 + 1) - len / 4);
int z = (itr->m_BlockZ + PrevZ) / 2 + ((a_Noise.IntNoise3DInt(PrevZ, PrevX, PrevY + i) / 17) % (len + 1) - len / 2);
Pts.push_back(cDefPoint(x, y, z, 5));
Pts.push_back(*itr);
PrevX = itr->m_BlockX;
PrevY = itr->m_BlockY;
PrevZ = itr->m_BlockZ;
}
std::swap(Pts, m_Points);
}
}
bool cCaveTunnel::RefineDefPoints(const cDefPoints & a_Src, cDefPoints & a_Dst)
{
// Smoothing: for each line segment, add points on its 1/4 lengths
bool res = false;
int Num = a_Src.size() - 2; // this many intermediary points
a_Dst.clear();
a_Dst.reserve(Num * 2 + 2);
cDefPoints::const_iterator itr = a_Src.begin() + 1;
a_Dst.push_back(a_Src.front());
int PrevX = a_Src.front().m_BlockX;
int PrevY = a_Src.front().m_BlockY;
int PrevZ = a_Src.front().m_BlockZ;
int PrevR = a_Src.front().m_Radius;
for (int 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(cDefPoint(PrevX + 1 * dx / 4, PrevY + 1 * dy / 4, PrevZ + 1 * dz / 4, Rad1));
a_Dst.push_back(cDefPoint(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::Smooth(void)
{
cDefPoints Pts;
while (true)
{
if (!RefineDefPoints(m_Points, Pts))
{
std::swap(Pts, m_Points);
return;
}
if (!RefineDefPoints(Pts, m_Points))
{
return;
}
}
}
void cCaveTunnel::FinishLinear(void)
{
// For each segment, use Bresenham's 3D line algorithm to draw a "line" of defpoints
cDefPoints Pts;
std::swap(Pts, m_Points);
m_Points.reserve(Pts.size() * 3);
int PrevX = Pts.front().m_BlockX;
int PrevY = Pts.front().m_BlockY;
int PrevZ = Pts.front().m_BlockZ;
for (cDefPoints::const_iterator itr = Pts.begin() + 1, end = Pts.end(); itr != end; ++itr)
{
int x1 = itr->m_BlockX;
int y1 = itr->m_BlockY;
int z1 = itr->m_BlockZ;
int dx = abs(x1 - PrevX);
int dy = abs(y1 - PrevY);
int dz = abs(z1 - PrevZ);
int sx = (PrevX < x1) ? 1 : -1;
int sy = (PrevY < y1) ? 1 : -1;
int sz = (PrevZ < z1) ? 1 : -1;
int err = dx - dz;
int R = itr->m_Radius;
if (dx >= std::max(dy, dz)) // x dominant
{
int yd = dy - dx / 2;
int zd = dz - dx / 2;
while (true)
{
m_Points.push_back(cDefPoint(PrevX, PrevY, PrevZ, R));
if (PrevX == x1)
{
break;
}
if (yd >= 0) // move along y
{
PrevY += sy;
yd -= dx;
}
if (zd >= 0) // move along z
{
PrevZ += sz;
zd -= dx;
}
// move along x
PrevX += sx;
yd += dy;
zd += dz;
}
}
else if (dy >= std::max(dx, dz)) // y dominant
{
int xd = dx - dy / 2;
int zd = dz - dy / 2;
while (true)
{
m_Points.push_back(cDefPoint(PrevX, PrevY, PrevZ, R));
if (PrevY == y1)
{
break;
}
if (xd >= 0) // move along x
{
PrevX += sx;
xd -= dy;
}
if (zd >= 0) // move along z
{
PrevZ += sz;
zd -= dy;
}
// move along y
PrevY += sy;
xd += dx;
zd += dz;
}
}
else
{
// z dominant
ASSERT(dz >= std::max(dx, dy));
int xd = dx - dz / 2;
int yd = dy - dz / 2;
while (true)
{
m_Points.push_back(cDefPoint(PrevX, PrevY, PrevZ, R));
if (PrevZ == z1)
{
break;
}
if (xd >= 0) // move along x
{
PrevX += sx;
xd -= dz;
}
if (yd >= 0) // move along y
{
PrevY += sy;
yd -= dz;
}
// move along z
PrevZ += sz;
xd += dx;
yd += dy;
}
} // if (which dimension is dominant)
} // for itr
}
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 (cDefPoints::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[]
}
void cCaveTunnel::ProcessChunk(
int a_ChunkX, int a_ChunkZ,
cChunkDef::BlockTypes & a_BlockTypes,
cChunkDef::HeightMap & a_HeightMap
)
{
// TODO
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 (cDefPoints::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:
int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc
int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc
for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++)
{
// TODO
} // for x, z
#ifdef _DEBUG
// For debugging purposes, outline the shape of the cave:
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);
}
#endif // _DEBUG
} // for itr - m_Points[]
}
#ifdef _DEBUG
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 (cDefPoints::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;
}
#endif // _DEBUG
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cStructGenWormNestCaves::cCaveSystem:
cStructGenWormNestCaves::cCaveSystem::cCaveSystem(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise) :
cStructGenWormNestCaves::cCaveSystem::cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise) :
m_BlockX(a_BlockX),
m_BlockZ(a_BlockZ)
m_BlockZ(a_BlockZ),
m_Size(a_Size)
{
// TODO: Generate a cave system
int Num = 1 + a_Noise.IntNoise2DInt(a_BlockX, a_BlockZ) % 3;
for (int i = 0; i < Num; i++)
{
int OriginX = a_BlockX + (a_Noise.IntNoise3DInt(13 * a_BlockX, 17 * a_BlockZ, 11 * i) / 19) % a_MaxOffset;
int OriginZ = a_BlockZ + (a_Noise.IntNoise3DInt(17 * a_BlockX, 13 * a_BlockZ, 11 * i) / 23) % a_MaxOffset;
int OriginY = 20 + (a_Noise.IntNoise3DInt(19 * a_BlockX, 13 * a_BlockZ, 11 * i) / 17) % 20;
// Generate three branches from the origin point:
// The tunnels generated depend on X, Y, Z and Branches,
// for the same set of numbers it generates the same offsets!
// That's why we add a +1 to X in the third line
GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 3);
GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 2);
GenerateTunnelsFromPoint(OriginX + 1, OriginY, OriginZ, a_Noise, 3);
}
}
@ -137,15 +555,6 @@ cStructGenWormNestCaves::cCaveSystem::~cCaveSystem()
void cStructGenWormNestCaves::cCaveSystem::Clear(void)
{
// TODO
}
void cStructGenWormNestCaves::cCaveSystem::ProcessChunk(
int a_ChunkX, int a_ChunkZ,
cChunkDef::BlockTypes & a_BlockTypes,
@ -162,6 +571,89 @@ void cStructGenWormNestCaves::cCaveSystem::ProcessChunk(
#ifdef _DEBUG
AString cStructGenWormNestCaves::cCaveSystem::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const
{
AString SVG;
SVG.reserve(512 * 1024);
for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr)
{
SVG.append((*itr)->ExportAsSVG(a_Color, a_OffsetX, a_OffsetZ));
} // for itr - m_Tunnels[]
// Base point highlight:
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
a_OffsetX + m_BlockX - 5, a_OffsetZ + m_BlockZ, a_OffsetX + m_BlockX + 5, a_OffsetZ + m_BlockZ
);
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ - 5, a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ + 5
);
// A gray line from the base point to the first point of the ravine, for identification:
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#cfcfcf;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ,
a_OffsetX + m_Tunnels.front()->m_Points.front().m_BlockX,
a_OffsetZ + m_Tunnels.front()->m_Points.front().m_BlockZ
);
// Offset guides:
if (a_OffsetX > 0)
{
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M %d,0 L %d,1024\"/>\n",
a_OffsetX, a_OffsetX
);
}
if (a_OffsetZ > 0)
{
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M 0,%d L 1024,%d\"/>\n",
a_OffsetZ, a_OffsetZ
);
}
return SVG;
}
#endif // _DEBUG
void cStructGenWormNestCaves::cCaveSystem::Clear(void)
{
for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr)
{
delete *itr;
}
m_Tunnels.clear();
}
void cStructGenWormNestCaves::cCaveSystem::GenerateTunnelsFromPoint(
int a_OriginX, int a_OriginY, int a_OriginZ,
cNoise & a_Noise, int a_NumSegments
)
{
int DoubleSize = m_Size * 2;
for (int i = a_NumSegments - 1; i >= 0; --i)
{
int EndX = a_OriginX + (((a_Noise.IntNoise3DInt(a_OriginX, a_OriginY, a_OriginZ + 11 * a_NumSegments) / 7) % DoubleSize) - m_Size) / 2;
int EndY = a_OriginY + (((a_Noise.IntNoise3DInt(a_OriginY, 13 * a_NumSegments, a_OriginZ + a_OriginX) / 7) % DoubleSize) - m_Size) / 4;
int EndZ = a_OriginZ + (((a_Noise.IntNoise3DInt(a_OriginZ + 17 * a_NumSegments, a_OriginX, a_OriginY) / 7) % DoubleSize) - m_Size) / 2;
m_Tunnels.push_back(new cCaveTunnel(a_OriginX, a_OriginY, a_OriginZ, EndX, EndY, EndZ, a_Noise));
GenerateTunnelsFromPoint(EndX, EndY, EndZ, a_Noise, i);
a_OriginX = EndX;
a_OriginY = EndY;
a_OriginZ = EndZ;
} // for i - a_NumSegments
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cStructGenWormNestCaves:
@ -176,7 +668,7 @@ cStructGenWormNestCaves::~cStructGenWormNestCaves()
void cStructGenWormNestCaves::ClearCache(void)
{
for (cCaves::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr)
for (cCaveSystems::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr)
{
delete *itr;
} // for itr - m_Cache[]
@ -196,9 +688,9 @@ void cStructGenWormNestCaves::GenStructures(
cBlockEntityList & a_BlockEntities // Block entities may be added or deleted
)
{
cCaves Caves;
cCaveSystems Caves;
GetCavesForChunk(a_ChunkX, a_ChunkZ, Caves);
for (cCaves::const_iterator itr = Caves.begin(); itr != Caves.end(); ++itr)
for (cCaveSystems::const_iterator itr = Caves.begin(); itr != Caves.end(); ++itr)
{
(*itr)->ProcessChunk(a_ChunkX, a_ChunkZ, a_BlockTypes, a_HeightMap);
} // for itr - Caves[]
@ -208,16 +700,10 @@ void cStructGenWormNestCaves::GenStructures(
void cStructGenWormNestCaves::GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenWormNestCaves::cCaves & a_Caves)
void cStructGenWormNestCaves::GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenWormNestCaves::cCaveSystems & a_Caves)
{
// TODO: Implement proper caching
// - don't destroy caves that are reusable
// - don't create caves that are already in the cache
ClearCache();
int BaseX = a_ChunkX * cChunkDef::Width / m_Size;
int BaseZ = a_ChunkZ * cChunkDef::Width / m_Size;
int BaseX = a_ChunkX * cChunkDef::Width / m_Grid;
int BaseZ = a_ChunkZ * cChunkDef::Width / m_Grid;
if (BaseX < 0)
{
--BaseX;
@ -226,18 +712,92 @@ void cStructGenWormNestCaves::GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cStru
{
--BaseZ;
}
BaseX -= 4;
BaseZ -= 4;
for (int x = 0; x < 8; x++)
BaseX -= NEIGHBORHOOD_SIZE / 2;
BaseZ -= NEIGHBORHOOD_SIZE / 2;
// Walk the cache, move each cave system that we want into a_Caves:
int StartX = BaseX * m_Grid;
int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_Grid;
int StartZ = BaseZ * m_Grid;
int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_Grid;
for (cCaveSystems::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;)
{
int RealX = (BaseX + x) * m_Size;
for (int z = 0; z < 8; z++)
if (
((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) &&
((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ)
)
{
int RealZ = (BaseZ + z) * m_Size;
m_Cache.push_back(new cCaveSystem(RealX, RealZ, m_Size, m_Noise));
// want
a_Caves.push_back(*itr);
itr = m_Cache.erase(itr);
}
else
{
// don't want
++itr;
}
} // for itr - m_Cache[]
for (int x = 0; x < NEIGHBORHOOD_SIZE; x++)
{
int RealX = (BaseX + x) * m_Grid;
for (int z = 0; z < NEIGHBORHOOD_SIZE; z++)
{
int RealZ = (BaseZ + z) * m_Grid;
bool Found = false;
for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr)
{
if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ))
{
Found = true;
break;
}
}
if (!Found)
{
a_Caves.push_back(new cCaveSystem(RealX, RealZ, m_MaxOffset, m_Size, m_Noise));
}
}
}
a_Caves = m_Cache;
// Copy a_Caves into m_Cache to the beginning:
cCaveSystems CavesCopy(a_Caves);
m_Cache.splice(m_Cache.begin(), CavesCopy, CavesCopy.begin(), CavesCopy.end());
// Trim the cache if it's too long:
if (m_Cache.size() > 100)
{
cCaveSystems::const_iterator itr = m_Cache.begin();
std::advance(itr, 100);
for (cCaveSystems::const_iterator end = m_Cache.end(); itr != end; ++itr)
{
delete *itr;
}
itr = m_Cache.begin();
std::advance(itr, 100);
m_Cache.erase(itr, m_Cache.end());
}
/*
// Uncomment this block for debugging the caves' shapes in 2D using an SVG export
#ifdef _DEBUG
AString SVG;
SVG.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1024\" height = \"1024\">\n");
SVG.reserve(2 * 1024 * 1024);
for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr)
{
int Color = 0x10 * abs((*itr)->m_BlockX / m_Grid);
Color |= 0x1000 * abs((*itr)->m_BlockZ / m_Grid);
SVG.append((*itr)->ExportAsSVG(Color, 512, 512));
}
SVG.append("</svg>\n");
AString fnam;
Printf(fnam, "wnc\\%03d_%03d.svg", a_ChunkX, a_ChunkZ);
cFile File(fnam, cFile::fmWrite);
File.Write(SVG.c_str(), SVG.size());
#endif // _DEBUG
//*/
}

View File

@ -78,9 +78,11 @@ class cStructGenWormNestCaves :
public cStructureGen
{
public:
cStructGenWormNestCaves(int a_Seed, int a_Size = 128) :
cStructGenWormNestCaves(int a_Seed, int a_Size = 64, int a_Grid = 96, int a_MaxOffset = 128) :
m_Noise(a_Seed),
m_Size(128)
m_Size(a_Size),
m_Grid(a_Grid),
m_MaxOffset(a_MaxOffset)
{
}
@ -88,17 +90,19 @@ public:
protected:
class cCaveSystem; // fwd: Caves.cpp
typedef std::list<cCaveSystem *> cCaves;
typedef std::list<cCaveSystem *> cCaveSystems;
cNoise m_Noise;
int m_Size; // relative size, in blocks, of the nests produced. Also used for spacing.
cCaves m_Cache;
cNoise m_Noise;
int m_Size; // relative size of the cave systems' caves. Average number of blocks of each initial tunnel
int m_MaxOffset; // maximum offset of the cave nest origin from the grid cell the nest belongs to
int m_Grid; // average spacing of the nests
cCaveSystems m_Cache;
/// Clears everything from the cache
void ClearCache(void);
/// Returns all caves that *may* intersect the given chunk. All the caves are valid until the next call to this function.
void GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cCaves & a_Caves);
void GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cCaveSystems & a_Caves);
// cStructGen override:
virtual void GenStructures(

View File

@ -326,13 +326,13 @@ void cChunkGenerator::InitStructureGens(cIniFile & a_IniFile)
{
m_StructureGens.push_back(new cStructGenRavines(m_Seed, 128));
}
/*
//*
// TODO: Not implemented yet; need a name
else if (NoCaseCompare(*itr, "caves") == 0)
else if (NoCaseCompare(*itr, "wormnestcaves") == 0)
{
m_StructureGens.push_back(new cStructGenWormNestCaves(m_Seed));
}
*/
//*/
else
{
LOGWARNING("Unknown structure generator: \"%s\". Ignoring.", itr->c_str());