From 2a0d1a059e556afaeb7f5b72205b26447e23286f Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sat, 11 Dec 2010 18:11:03 +0200 Subject: commit before some radicallish changes to water behavior --- src/voxel.cpp | 788 +++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 703 insertions(+), 85 deletions(-) (limited to 'src/voxel.cpp') diff --git a/src/voxel.cpp b/src/voxel.cpp index fe176a27a..15624feed 100644 --- a/src/voxel.cpp +++ b/src/voxel.cpp @@ -20,6 +20,23 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "voxel.h" #include "map.h" +// For TimeTaker +#include "main.h" +#include "utility.h" + +/* + Debug stuff +*/ +u32 addarea_time = 0; +u32 emerge_time = 0; +u32 emerge_load_time = 0; +u32 clearflag_time = 0; +//u32 getwaterpressure_time = 0; +//u32 spreadwaterpressure_time = 0; +u32 updateareawaterpressure_time = 0; +u32 flowwater_pre_time = 0; + + VoxelManipulator::VoxelManipulator(): m_data(NULL), m_flags(NULL) @@ -47,7 +64,7 @@ void VoxelManipulator::clear() m_flags = NULL; } -void VoxelManipulator::print(std::ostream &o) +void VoxelManipulator::print(std::ostream &o, VoxelPrintMode mode) { v3s16 em = m_area.getExtent(); v3s16 of = m_area.MinEdge; @@ -78,8 +95,29 @@ void VoxelManipulator::print(std::ostream &o) { c = 'X'; u8 m = m_data[m_area.index(x,y,z)].d; - if(m <= 9) - c = m + '0'; + u8 pr = m_data[m_area.index(x,y,z)].pressure; + if(mode == VOXELPRINT_MATERIAL) + { + if(m <= 9) + c = m + '0'; + } + else if(mode == VOXELPRINT_WATERPRESSURE) + { + if(m == MATERIAL_WATER) + { + c = 'w'; + if(pr <= 9) + c = pr + '0'; + } + else if(m == MATERIAL_AIR) + { + c = ' '; + } + else + { + c = '#'; + } + } } o< highest_y) + highest_y = p.Y; + + recur_count++; + if(recur_count > 30) + throw ProcessingLimitException + ("getWaterPressure recur_count limit reached"); + + v3s16 dirs[6] = { + v3s16(0,1,0), // top + v3s16(-1,0,0), // left + v3s16(1,0,0), // right + v3s16(0,0,-1), // front + v3s16(0,0,1), // back + v3s16(0,-1,0), // bottom + }; + + // Load neighboring nodes + // TODO: A bigger area would be better + emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1))); + + s32 i; + for(i=0; i<6; i++) + { + v3s16 p2 = p + dirs[i]; + u8 f = m_flags[m_area.index(p2)]; + // Ignore inexistent or checked nodes + if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED2)) + continue; + MapNode &n = m_data[m_area.index(p2)]; + // Ignore non-liquid nodes + if(material_liquid(n.d) == false) + continue; + + int pr; + + // If at surface + /*if(n.pressure == 1) + { + pr = 1; + } + // Otherwise recurse more + else*/ + { + pr = getWaterPressure(p2, highest_y, recur_count); + if(pr == -1) + continue; + } + + // If block is at top, pressure here is one higher + if(i == 0) + { + if(pr < 255) + pr++; + } + // If block is at bottom, pressure here is one lower + else if(i == 5) + { + if(pr > 1) + pr--; + } + + // Node is on the pressure route + m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED4; + + // Got pressure + return pr; + } + + // Nothing useful found + return -1; +} + +void VoxelManipulator::spreadWaterPressure(v3s16 p, int pr, + VoxelArea request_area, + core::map &active_nodes, + int recur_count) +{ + recur_count++; + if(recur_count > 10000) + throw ProcessingLimitException + ("spreadWaterPressure recur_count limit reached"); + + m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED3; + m_data[m_area.index(p)].pressure = pr; + + v3s16 dirs[6] = { + v3s16(0,1,0), // top + v3s16(-1,0,0), // left + v3s16(1,0,0), // right + v3s16(0,0,-1), // front + v3s16(0,0,1), // back + v3s16(0,-1,0), // bottom + }; + + // Load neighboring nodes + emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1))); + + s32 i; + for(i=0; i<6; i++) + { + v3s16 p2 = p + dirs[i]; + + u8 f = m_flags[m_area.index(p2)]; + + // Ignore inexistent and checked nodes + if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED3)) + continue; + + MapNode &n = m_data[m_area.index(p2)]; + + /* + If material is air: + add to active_nodes if there is flow-causing pressure. + NOTE: Do not remove anything from there. We cannot know + here if some other neighbor of it causes flow. + */ + if(n.d == MATERIAL_AIR) + { + bool pressure_causes_flow = false; + // If block is at top + if(i == 0) + { + if(pr >= 3) + pressure_causes_flow = true; + } + // If block is at bottom + else if(i == 5) + { + pressure_causes_flow = true; + } + // If block is at side + else + { + if(pr >= 2) + pressure_causes_flow = true; + } + + if(pressure_causes_flow) + { + active_nodes[p2] = 1; + } + + continue; + } + + // Ignore non-liquid nodes + if(material_liquid(n.d) == false) + continue; + + int pr2 = pr; + // If block is at top, pressure there is lower + if(i == 0) + { + if(pr2 > 0) + pr2--; + } + // If block is at bottom, pressure there is higher + else if(i == 5) + { + if(pr2 < 255) + pr2++; + } + + // Ignore if correct pressure is already set and is not on + // request_area + if(n.pressure == pr2 && request_area.contains(p2) == false) + continue; + + spreadWaterPressure(p2, pr2, request_area, active_nodes, recur_count); + } +} + +void VoxelManipulator::updateAreaWaterPressure(VoxelArea a, + core::map &active_nodes, + bool checked3_is_clear) +{ + TimeTaker timer("updateAreaWaterPressure", g_device, + &updateareawaterpressure_time); + + emerge(a); + + bool checked2_clear = false; + + if(checked3_is_clear == false) + { + //clearFlag(VOXELFLAG_CHECKED3); + + clearFlag(VOXELFLAG_CHECKED3 | VOXELFLAG_CHECKED2); + checked2_clear = true; + } + + + for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++) + for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++) + for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++) + { + v3s16 p(x,y,z); + + u8 f = m_flags[m_area.index(p)]; + // Ignore inexistent or checked nodes + if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED3)) + continue; + MapNode &n = m_data[m_area.index(p)]; + // Ignore non-liquid nodes + if(material_liquid(n.d) == false) + continue; + + if(checked2_clear == false) + { + clearFlag(VOXELFLAG_CHECKED2); + checked2_clear = true; + } + + checked2_clear = false; + + s16 highest_y = -32768; + int recur_count = 0; + int pr = -1; + + try + { + // 0-1ms @ recur_count <= 100 + //TimeTaker timer("getWaterPressure", g_device); + pr = getWaterPressure(p, highest_y, recur_count); + } + catch(ProcessingLimitException &e) + { + //dstream<<"getWaterPressure ProcessingLimitException"< 255) + pr = 255; + + /*dstream<<"WARNING: Pressure at (" + < &active_nodes, + int recursion_depth, bool debugprint, + int *counter, int counterlimit) { v3s16 dirs[6] = { v3s16(0,1,0), // top @@ -241,16 +607,43 @@ void VoxelManipulator::flowWater(v3s16 removed_pos) v3s16(0,-1,0), // bottom }; + recursion_depth++; + v3s16 p; + + // Randomize horizontal order + static s32 cs = 0; + if(cs < 3) + cs++; + else + cs = 0; + s16 s1 = (cs & 1) ? 1 : -1; + s16 s2 = (cs & 2) ? 1 : -1; + //dstream<<"s1="<=0; i--) + for(s32 i=5; i>=0; i--) { - p = removed_pos + dirs[i]; + //v3s16 p = removed_pos + dirs[i]; + p = removed_pos + v3s16(s1*dirs[i].X, dirs[i].Y, s2*dirs[i].Z); + u8 f = m_flags[m_area.index(p)]; // Water can't move to inexistent nodes if(f & VOXELFLAG_INEXISTENT) @@ -312,102 +741,291 @@ void VoxelManipulator::flowWater(v3s16 removed_pos) // Water can only move to air if(n.d != MATERIAL_AIR) continue; - flowWater(p); + + // Flow water to node + bool moved = + flowWater(p, active_nodes, recursion_depth, + debugprint, counter, counterlimit); + + if(moved) + { + // Search again from all neighbors + goto find_again; + } } -} -/* - MapVoxelManipulator -*/ + if(counter != NULL) + { + (*counter)++; + if((*counter) % 10 == 0) + dstream<<"flowWater(): moved "<<(*counter)<<" nodes" + < counterlimit) + { + dstream<<"Counter limit reached; returning"< &active_nodes, + int recursion_depth, bool debugprint, + int *counter, int counterlimit) { - v3s16 size = a.getExtent(); + v3s16 dirs[6] = { + v3s16(0,1,0), // top + v3s16(-1,0,0), // left + v3s16(1,0,0), // right + v3s16(0,0,-1), // front + v3s16(0,0,1), // back + v3s16(0,-1,0), // bottom + }; - addArea(a); + recursion_depth++; + + v3s16 p; + + // Randomize horizontal order + static s32 cs = 0; + if(cs < 3) + cs++; + else + cs = 0; + s16 s1 = (cs & 1) ? 1 : -1; + s16 s2 = (cs & 2) ? 1 : -1; + //dstream<<"s1="<= 3) + break; continue; - try{ - MapNode n = m_map->getNode(a.MinEdge + p); - m_data[i] = n; - m_flags[i] = 0; } - catch(InvalidPositionException &e) + // Else block is at some side. Select it if it has enough pressure + if(n.pressure >= 2) { - m_flags[i] = VOXELFLAG_INEXISTENT; + break; } } -} -void MapVoxelManipulator::blitBack - (core::map & modified_blocks) -{ - /* - Initialize block cache - */ - v3s16 blockpos_last; - MapBlock *block = NULL; - bool block_checked_in_modified = false; + // If there is nothing to move, return + if(i==6) + return false; - for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++) - for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++) - for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++) + // Switch nodes at p and removed_pos + u8 m = m_data[m_area.index(p)].d; + u8 f = m_flags[m_area.index(p)]; + m_data[m_area.index(p)].d = m_data[m_area.index(removed_pos)].d; + m_flags[m_area.index(p)] = m_flags[m_area.index(removed_pos)]; + m_data[m_area.index(removed_pos)].d = m; + m_flags[m_area.index(removed_pos)] = f; + + // Mark removed_pos checked + m_flags[m_area.index(removed_pos)] |= VOXELFLAG_CHECKED; + // If block was dropped from surface, increase pressure + if(i == 0 && m_data[m_area.index(removed_pos)].pressure == 1) { - v3s16 p(x,y,z); + m_data[m_area.index(removed_pos)].pressure = 2; + } + + /*if(debugprint) + { + dstream<<"VoxelManipulator::flowWater(): Moved bubble:"<=0; i--) + { + //v3s16 p = removed_pos + dirs[i]; + p = removed_pos + v3s16(s1*dirs[i].X, dirs[i].Y, s2*dirs[i].Z); u8 f = m_flags[m_area.index(p)]; - if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT)) + // Water can't move to inexistent nodes + if(f & VOXELFLAG_INEXISTENT) continue; - MapNode &n = m_data[m_area.index(p)]; + // Water can only move to air + if(n.d != MATERIAL_AIR) + continue; - v3s16 blockpos = getNodeBlockPos(p); + // Flow water to node + bool moved = + flowWater(p, active_nodes, recursion_depth, + debugprint, counter, counterlimit); - try + if(moved) { - // Get block - if(block == NULL || blockpos != blockpos_last){ - block = m_map->getBlockNoCreate(blockpos); - blockpos_last = blockpos; - block_checked_in_modified = false; - } - - // Calculate relative position in block - v3s16 relpos = p - blockpos * MAP_BLOCKSIZE; + // Search again from all neighbors + goto find_again; + } + } - // Don't continue if nothing has changed here - if(block->getNode(relpos) == n) - continue; + if(counter != NULL) + { + (*counter)++; + if((*counter) % 10 == 0) + dstream<<"flowWater(): moved "<<(*counter)<<" nodes" + <setNode(m_area.MinEdge + p, n); - block->setNode(relpos, n); - - /* - Make sure block is in modified_blocks - */ - if(block_checked_in_modified == false) - { - modified_blocks[blockpos] = block; - block_checked_in_modified = true; - } + if(counterlimit != -1 && (*counter) > counterlimit) + { + dstream<<"Counter limit reached; returning"< &active_nodes, + int recursion_depth, bool debugprint, + int counterlimit) +{ + addarea_time = 0; + emerge_time = 0; + emerge_load_time = 0; + clearflag_time = 0; + updateareawaterpressure_time = 0; + flowwater_pre_time = 0; + + TimeTaker timer1("flowWater (active_nodes)", g_device); + + dstream<<"active_nodes.size() = "<::Node + *n = active_nodes.getIterator().getNode(); +#endif + +#if 1 + // Take random one + s32 k = (s32)rand() % (s32)active_nodes.size(); + //s32 k = 0; + core::map::Iterator + i = active_nodes.getIterator().getNode(); + for(s32 j=0; j::Node *n = i.getNode(); +#endif + + v3s16 p = n->getKey(); + active_nodes.remove(p); + flowWater(p, active_nodes, recursion_depth, + debugprint, &counter, counterlimit); + } + } + catch(ProcessingLimitException &e) + { + //dstream<<"getWaterPressure ProcessingLimitException"<