Mapgen: Performance improvement and fixes for updateLiquid (#4065)

- Adds only ~100 nodes per chunk to trans_liquid with similar processing time
- Adds liquid nodes themselves instead of potential solid nodes below them
- CONTENT_IGNORE nodes are interpreted as if they continue their neighborhood
- This allows liquid columns to span multiple chunks without being interrupted
- NOTE: Expects an one-node border in generation chunk without liquid changes
This commit is contained in:
MillersMan 2016-06-05 01:30:36 +02:00 committed by kwolekr
parent 109c7e3349
commit 7841f1c509
2 changed files with 75 additions and 12 deletions

@ -213,27 +213,86 @@ void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax)
//printf("updateHeightmap: %dus\n", t.stop());
}
inline bool Mapgen::isLiquidHorizontallyFlowable(u32 vi, v3s16 em)
{
u32 vi_neg_x = vi;
vm->m_area.add_x(em, vi_neg_x, -1);
if (vm->m_data[vi_neg_x].getContent() != CONTENT_IGNORE) {
const ContentFeatures &c_nx = ndef->get(vm->m_data[vi_neg_x]);
if (c_nx.floodable && !c_nx.isLiquid())
return true;
}
u32 vi_pos_x = vi;
vm->m_area.add_x(em, vi_pos_x, +1);
if (vm->m_data[vi_pos_x].getContent() != CONTENT_IGNORE) {
const ContentFeatures &c_px = ndef->get(vm->m_data[vi_pos_x]);
if (c_px.floodable && !c_px.isLiquid())
return true;
}
u32 vi_neg_z = vi;
vm->m_area.add_z(em, vi_neg_z, -1);
if (vm->m_data[vi_neg_z].getContent() != CONTENT_IGNORE) {
const ContentFeatures &c_nz = ndef->get(vm->m_data[vi_neg_z]);
if (c_nz.floodable && !c_nz.isLiquid())
return true;
}
u32 vi_pos_z = vi;
vm->m_area.add_z(em, vi_pos_z, +1);
if (vm->m_data[vi_pos_z].getContent() != CONTENT_IGNORE) {
const ContentFeatures &c_pz = ndef->get(vm->m_data[vi_pos_z]);
if (c_pz.floodable && !c_pz.isLiquid())
return true;
}
return false;
}
void Mapgen::updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax)
{
bool isliquid, wasliquid;
bool isignored, isliquid, wasignored, wasliquid, waschecked, waspushed;
v3s16 em = vm->m_area.getExtent();
for (s16 z = nmin.Z; z <= nmax.Z; z++) {
for (s16 x = nmin.X; x <= nmax.X; x++) {
wasliquid = true;
for (s16 z = nmin.Z + 1; z <= nmax.Z - 1; z++)
for (s16 x = nmin.X + 1; x <= nmax.X - 1; x++) {
wasignored = true;
wasliquid = false;
waschecked = false;
waspushed = false;
u32 i = vm->m_area.index(x, nmax.Y, z);
for (s16 y = nmax.Y; y >= nmin.Y; y--) {
isliquid = ndef->get(vm->m_data[i]).isLiquid();
u32 vi = vm->m_area.index(x, nmax.Y, z);
for (s16 y = nmax.Y; y >= nmin.Y; y--) {
isignored = vm->m_data[vi].getContent() == CONTENT_IGNORE;
isliquid = ndef->get(vm->m_data[vi]).isLiquid();
// there was a change between liquid and nonliquid, add to queue.
if (isliquid != wasliquid)
if (isignored || wasignored || isliquid == wasliquid) {
// Neither topmost node of liquid column nor topmost node below column
waschecked = false;
waspushed = false;
} else if (isliquid) {
// This is the topmost node in the column
bool ispushed = false;
if (isLiquidHorizontallyFlowable(vi, em)) {
trans_liquid->push_back(v3s16(x, y, z));
wasliquid = isliquid;
vm->m_area.add_y(em, i, -1);
ispushed = true;
}
// Remember waschecked and waspushed to avoid repeated
// checks/pushes in case the column consists of only this node
waschecked = true;
waspushed = ispushed;
} else {
// This is the topmost node below a liquid column
u32 vi_above = vi;
vm->m_area.add_y(em, vi_above, 1);
if (!waspushed && (ndef->get(vm->m_data[vi]).floodable ||
(!waschecked && isLiquidHorizontallyFlowable(vi_above, em)))) {
// Push back the lowest node in the column which is one
// node above this one
trans_liquid->push_back(v3s16(x, y + 1, z));
}
}
wasliquid = isliquid;
wasignored = isignored;
vm->m_area.add_y(em, vi, -1);
}
}
}

@ -199,6 +199,10 @@ public:
virtual int getSpawnLevelAtPoint(v2s16 p) { return 0; }
private:
// isLiquidHorizontallyFlowable() is a helper function for updateLiquid()
// that checks whether there are floodable nodes without liquid beneath
// the node at index vi.
inline bool isLiquidHorizontallyFlowable(u32 vi, v3s16 em);
DISABLE_CLASS_COPY(Mapgen);
};