3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
32 u32 emerge_load_time = 0;
33 u32 clearflag_time = 0;
34 //u32 getwaterpressure_time = 0;
35 //u32 spreadwaterpressure_time = 0;
36 u32 updateareawaterpressure_time = 0;
37 u32 flowwater_pre_time = 0;
40 VoxelManipulator::VoxelManipulator():
44 m_disable_water_climb = false;
47 VoxelManipulator::~VoxelManipulator()
56 void VoxelManipulator::clear()
58 // Reset area to volume=0
68 void VoxelManipulator::print(std::ostream &o, VoxelPrintMode mode)
70 v3s16 em = m_area.getExtent();
71 v3s16 of = m_area.MinEdge;
72 o<<"size: "<<em.X<<"x"<<em.Y<<"x"<<em.Z
73 <<" offset: ("<<of.X<<","<<of.Y<<","<<of.Z<<")"<<std::endl;
75 for(s32 y=m_area.MaxEdge.Y; y>=m_area.MinEdge.Y; y--)
77 if(em.X >= 3 && em.Y >= 3)
79 if (y==m_area.MinEdge.Y+2) o<<"^ ";
80 else if(y==m_area.MinEdge.Y+1) o<<"| ";
81 else if(y==m_area.MinEdge.Y+0) o<<"y x-> ";
85 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
87 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
89 u8 f = m_flags[m_area.index(x,y,z)];
91 if(f & VOXELFLAG_NOT_LOADED)
93 else if(f & VOXELFLAG_INEXISTENT)
98 u8 m = m_data[m_area.index(x,y,z)].d;
99 u8 pr = m_data[m_area.index(x,y,z)].pressure;
100 if(mode == VOXELPRINT_MATERIAL)
105 else if(mode == VOXELPRINT_WATERPRESSURE)
107 if(m == CONTENT_WATER)
113 else if(liquid_replaces_content(m))
131 void VoxelManipulator::addArea(VoxelArea area)
133 // Cancel if requested area has zero volume
134 if(area.getExtent() == v3s16(0,0,0))
137 // Cancel if m_area already contains the requested area
138 if(m_area.contains(area))
141 TimeTaker timer("addArea", &addarea_time);
143 // Calculate new area
145 // New area is the requested area if m_area has zero volume
146 if(m_area.getExtent() == v3s16(0,0,0))
150 // Else add requested area to m_area
154 new_area.addArea(area);
157 s32 new_size = new_area.getVolume();
159 /*dstream<<"adding area ";
161 dstream<<", old area ";
162 m_area.print(dstream);
163 dstream<<", new area ";
164 new_area.print(dstream);
165 dstream<<", new_size="<<new_size;
166 dstream<<std::endl;*/
168 // Allocate and clear new data
169 MapNode *new_data = new MapNode[new_size];
170 u8 *new_flags = new u8[new_size];
171 for(s32 i=0; i<new_size; i++)
173 new_flags[i] = VOXELFLAG_NOT_LOADED;
178 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
179 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
180 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
182 // If loaded, copy data and flags
183 if((m_flags[m_area.index(x,y,z)] & VOXELFLAG_NOT_LOADED) == false)
185 new_data[new_area.index(x,y,z)] = m_data[m_area.index(x,y,z)];
186 new_flags[new_area.index(x,y,z)] = m_flags[m_area.index(x,y,z)];
190 // Replace area, data and flags
194 MapNode *old_data = m_data;
195 u8 *old_flags = m_flags;
197 /*dstream<<"old_data="<<(int)old_data<<", new_data="<<(int)new_data
198 <<", old_flags="<<(int)m_flags<<", new_flags="<<(int)new_flags<<std::endl;*/
208 //dstream<<"addArea done"<<std::endl;
211 void VoxelManipulator::copyFrom(MapNode *src, VoxelArea src_area,
212 v3s16 from_pos, v3s16 to_pos, v3s16 size)
214 for(s16 z=0; z<size.Z; z++)
215 for(s16 y=0; y<size.Y; y++)
217 s32 i_src = src_area.index(from_pos.X, from_pos.Y+y, from_pos.Z+z);
218 s32 i_local = m_area.index(to_pos.X, to_pos.Y+y, to_pos.Z+z);
219 memcpy(&m_data[i_local], &src[i_src], size.X*sizeof(MapNode));
220 memset(&m_flags[i_local], 0, size.X);
225 void VoxelManipulator::clearFlag(u8 flags)
227 // 0-1ms on moderate area
228 TimeTaker timer("clearFlag", &clearflag_time);
230 v3s16 s = m_area.getExtent();
232 /*dstream<<"clearFlag clearing area of size "
233 <<""<<s.X<<"x"<<s.Y<<"x"<<s.Z<<""
238 /*for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
239 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
240 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
242 u8 f = m_flags[m_area.index(x,y,z)];
243 m_flags[m_area.index(x,y,z)] &= ~flags;
244 if(m_flags[m_area.index(x,y,z)] != f)
248 s32 volume = m_area.getVolume();
249 for(s32 i=0; i<volume; i++)
251 m_flags[i] &= ~flags;
254 /*s32 volume = m_area.getVolume();
255 for(s32 i=0; i<volume; i++)
258 m_flags[i] &= ~flags;
263 dstream<<"clearFlag changed "<<count<<" flags out of "
264 <<volume<<" nodes"<<std::endl;*/
267 int VoxelManipulator::getWaterPressure(v3s16 p, s16 &highest_y, int recur_count)
269 m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED2;
274 /*if(recur_count > 1000)
275 throw ProcessingLimitException
276 ("getWaterPressure recur_count limit reached");*/
278 if(recur_count > 10000)
285 v3s16(0,0,1), // back
286 v3s16(0,0,-1), // front
287 v3s16(1,0,0), // right
288 v3s16(-1,0,0), // left
289 v3s16(0,-1,0), // bottom
292 // Load neighboring nodes
293 emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)), 1);
298 v3s16 p2 = p + dirs[i];
299 u8 f = m_flags[m_area.index(p2)];
300 // Ignore inexistent or checked nodes
301 if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED2))
303 MapNode &n = m_data[m_area.index(p2)];
304 // Ignore non-liquid nodes
305 if(content_liquid(n.d) == false)
310 // If at ocean surface
311 if(n.pressure == 1 && n.d == CONTENT_WATERSOURCE)
312 //if(n.pressure == 1) // Causes glitches but is fast
316 // Otherwise recurse more
319 pr = getWaterPressure(p2, highest_y, recur_count);
324 // If block is at top, pressure here is one higher
330 // If block is at bottom, pressure here is one lower
337 // Node is on the pressure route
338 m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED4;
344 // Nothing useful found
348 void VoxelManipulator::spreadWaterPressure(v3s16 p, int pr,
349 VoxelArea request_area,
350 core::map<v3s16, u8> &active_nodes,
353 //if(recur_count > 10000)
354 /*throw ProcessingLimitException
355 ("spreadWaterPressure recur_count limit reached");*/
360 /*dstream<<"spreadWaterPressure: p=("
361 <<p.X<<","<<p.Y<<","<<p.Z<<")"
362 <<", oldpr="<<(int)m_data[m_area.index(p)].pressure
364 <<", recur_count="<<recur_count
366 request_area.print(dstream);
367 dstream<<std::endl;*/
369 m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED3;
370 m_data[m_area.index(p)].pressure = pr;
374 v3s16(-1,0,0), // left
375 v3s16(1,0,0), // right
376 v3s16(0,0,-1), // front
377 v3s16(0,0,1), // back
378 v3s16(0,-1,0), // bottom
381 // Load neighboring nodes
382 emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)), 2);
387 v3s16 p2 = p + dirs[i];
389 u8 f = m_flags[m_area.index(p2)];
391 // Ignore inexistent and checked nodes
392 if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED3))
395 MapNode &n = m_data[m_area.index(p2)];
399 add to active_nodes if there is flow-causing pressure.
400 NOTE: Do not remove anything from there. We cannot know
401 here if some other neighbor of it causes flow.
403 if(liquid_replaces_content(n.d))
405 bool pressure_causes_flow = false;
406 // If empty block is at top
409 if(m_disable_water_climb)
412 //if(pr >= PRESERVE_WATER_VOLUME ? 3 : 2)
414 pressure_causes_flow = true;
416 // If block is at bottom
419 pressure_causes_flow = true;
421 // If block is at side
424 //if(pr >= PRESERVE_WATER_VOLUME ? 2 : 1)
426 pressure_causes_flow = true;
429 if(pressure_causes_flow)
431 active_nodes[p2] = 1;
437 // Ignore non-liquid nodes
438 if(content_liquid(n.d) == false)
442 // If block is at top, pressure there is lower
448 // If block is at bottom, pressure there is higher
455 /*if(m_disable_water_climb)
461 // Ignore if correct pressure is already set and is not on
463 // Thus, request_area can be used for updating as much
464 // pressure info in some area as possible to possibly
465 // make some calls to getWaterPressure unnecessary.
466 if(n.pressure == pr2 && request_area.contains(p2) == false)
469 spreadWaterPressure(p2, pr2, request_area, active_nodes, recur_count);
473 void VoxelManipulator::updateAreaWaterPressure(VoxelArea a,
474 core::map<v3s16, u8> &active_nodes,
475 bool checked3_is_clear)
477 TimeTaker timer("updateAreaWaterPressure", &updateareawaterpressure_time);
481 bool checked2_clear = false;
483 if(checked3_is_clear == false)
485 //clearFlag(VOXELFLAG_CHECKED3);
487 clearFlag(VOXELFLAG_CHECKED3 | VOXELFLAG_CHECKED2);
488 checked2_clear = true;
492 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
493 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
494 for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
498 u8 f = m_flags[m_area.index(p)];
499 // Ignore inexistent or checked nodes
500 if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED3))
502 MapNode &n = m_data[m_area.index(p)];
503 // Ignore non-liquid nodes
504 if(content_liquid(n.d) == false)
507 if(checked2_clear == false)
509 clearFlag(VOXELFLAG_CHECKED2);
510 checked2_clear = true;
513 checked2_clear = false;
515 s16 highest_y = -32768;
521 // 0-1ms @ recur_count <= 100
522 //TimeTaker timer("getWaterPressure", g_irrlicht);
523 pr = getWaterPressure(p, highest_y, recur_count);
525 catch(ProcessingLimitException &e)
527 //dstream<<"getWaterPressure ProcessingLimitException"<<std::endl;
532 assert(highest_y != -32768);
534 pr = highest_y - p.Y + 1;
538 /*dstream<<"WARNING: Pressure at ("
539 <<p.X<<","<<p.Y<<","<<p.Z<<")"
541 //<<" and highest_y == -32768"
543 assert(highest_y != -32768);
550 //TimeTaker timer("spreadWaterPressure", g_irrlicht);
551 spreadWaterPressure(p, pr, a, active_nodes, 0);
553 catch(ProcessingLimitException &e)
555 //dstream<<"getWaterPressure ProcessingLimitException"<<std::endl;
560 bool VoxelManipulator::flowWater(v3s16 removed_pos,
561 core::map<v3s16, u8> &active_nodes,
562 int recursion_depth, bool debugprint,
567 v3s16(0,0,-1), // front
568 v3s16(0,0,1), // back
569 v3s16(-1,0,0), // left
570 v3s16(1,0,0), // right
571 v3s16(0,-1,0), // bottom
577 bool from_ocean = false;
579 // Randomize horizontal order
585 s16 s1 = (cs & 1) ? 1 : -1;
586 s16 s2 = (cs & 2) ? 1 : -1;
587 //dstream<<"s1="<<s1<<", s2="<<s2<<std::endl;
590 TimeTaker timer1("flowWater pre", &flowwater_pre_time);
592 // Load neighboring nodes
593 emerge(VoxelArea(removed_pos - v3s16(1,1,1), removed_pos + v3s16(1,1,1)), 4);
595 // Ignore incorrect removed_pos
597 u8 f = m_flags[m_area.index(removed_pos)];
598 // Ignore inexistent or checked node
599 if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED))
601 MapNode &n = m_data[m_area.index(removed_pos)];
602 // Ignore nodes to which the water can't go
603 if(liquid_replaces_content(n.d) == false)
610 // Don't raise water from bottom
611 if(m_disable_water_climb && i == 5)
614 p = removed_pos + v3s16(s1*dirs[i].X, dirs[i].Y, s2*dirs[i].Z);
616 u8 f = m_flags[m_area.index(p)];
617 // Inexistent or checked nodes can't move
618 if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED))
620 MapNode &n = m_data[m_area.index(p)];
621 // Only liquid nodes can move
622 if(content_liquid(n.d) == false)
624 // If block is at top, select it always
629 // If block is at bottom, select it if it has enough pressure
632 //if(n.pressure >= PRESERVE_WATER_VOLUME ? 3 : 2)
637 // Else block is at some side. Select it if it has enough pressure
638 //if(n.pressure >= PRESERVE_WATER_VOLUME ? 2 : 1)
645 // If there is nothing to move, return
650 Move water and bubble
653 u8 m = m_data[m_area.index(p)].d;
654 u8 f = m_flags[m_area.index(p)];
656 if(m == CONTENT_WATERSOURCE)
659 // Move air bubble if not taking water from ocean
660 if(from_ocean == false)
662 m_data[m_area.index(p)].d = m_data[m_area.index(removed_pos)].d;
663 m_flags[m_area.index(p)] = m_flags[m_area.index(removed_pos)];
667 This has to be done to copy the brightness of a light source
668 correctly. Otherwise unspreadLight will fuck up when water
669 has replaced a light source.
671 u8 light = m_data[m_area.index(removed_pos)].getLightBanksWithSource();
673 m_data[m_area.index(removed_pos)].d = m;
674 m_flags[m_area.index(removed_pos)] = f;
676 m_data[m_area.index(removed_pos)].setLightBanks(light);
678 // Mark removed_pos checked
679 m_flags[m_area.index(removed_pos)] |= VOXELFLAG_CHECKED;
681 // If block was dropped from surface, increase pressure
682 if(i == 0 && m_data[m_area.index(removed_pos)].pressure == 1)
684 m_data[m_area.index(removed_pos)].pressure = 2;
688 NOTE: This does not work as-is
689 if(m == CONTENT_WATERSOURCE)
691 // If block was raised to surface, increase pressure of
693 if(i == 5 && m_data[m_area.index(p)].pressure == 1)
695 m_data[m_area.index(p)].pressure = 2;
701 dstream<<"VoxelManipulator::flowWater(): Moved bubble:"<<std::endl;
702 print(dstream, VOXELPRINT_WATERPRESSURE);
707 a.addPoint(p - v3s16(1,1,1));
708 a.addPoint(p + v3s16(1,1,1));
709 a.addPoint(removed_pos - v3s16(1,1,1));
710 a.addPoint(removed_pos + v3s16(1,1,1));
711 updateAreaWaterPressure(a, active_nodes);
715 dstream<<"VoxelManipulator::flowWater(): Pressure updated:"<<std::endl;
716 print(dstream, VOXELPRINT_WATERPRESSURE);
722 dstream<<"VoxelManipulator::flowWater(): step done:"<<std::endl;
723 print(dstream, VOXELPRINT_WATERPRESSURE);
729 //if(PRESERVE_WATER_VOLUME)
730 if(from_ocean == false)
732 // Flow water to the newly created empty position
733 /*flowWater(p, active_nodes, recursion_depth,
734 debugprint, counter, counterlimit);*/
735 flowWater(p, active_nodes, recursion_depth,
736 debugprint, stoptime);
741 u32 timenow = getTimeMs();
742 // Well, it is a bit hard to guess because we don't know the
744 bool overflow = timenow < stoptime - 100000;
745 if(timenow >= stoptime || overflow)
747 dstream<<"flowWater: stoptime reached"<<std::endl;
748 throw ProcessingLimitException("flowWater stoptime reached");
754 // Try flowing water to empty positions around removed_pos.
755 // They are checked in reverse order compared to the previous loop.
756 for(s32 i=5; i>=0; i--)
758 // Don't try to flow to top
759 if(m_disable_water_climb && i == 0)
762 //v3s16 p = removed_pos + dirs[i];
763 p = removed_pos + v3s16(s1*dirs[i].X, dirs[i].Y, s2*dirs[i].Z);
765 u8 f = m_flags[m_area.index(p)];
766 // Water can't move to inexistent nodes
767 if(f & VOXELFLAG_INEXISTENT)
769 MapNode &n = m_data[m_area.index(p)];
770 // Water can only move to air
771 if(liquid_replaces_content(n.d) == false)
774 // Flow water to node
776 flowWater(p, active_nodes, recursion_depth,
777 debugprint, stoptime);
778 /*flowWater(p, active_nodes, recursion_depth,
779 debugprint, counter, counterlimit);*/
783 // Search again from all neighbors
791 void VoxelManipulator::flowWater(
792 core::map<v3s16, u8> &active_nodes,
793 int recursion_depth, bool debugprint,
798 emerge_load_time = 0;
800 updateareawaterpressure_time = 0;
801 flowwater_pre_time = 0;
803 if(active_nodes.size() == 0)
805 dstream<<"flowWater: no active nodes"<<std::endl;
809 //TimeTaker timer1("flowWater (active_nodes)", g_irrlicht);
811 //dstream<<"active_nodes.size() = "<<active_nodes.size()<<std::endl;
815 stoptime = getTimeMs() + timelimit;
817 // Count of handled active nodes
818 u32 handled_count = 0;
824 Take random one at first
826 This is randomized only at the first time so that all
827 subsequent nodes will be taken at roughly the same position
830 if(active_nodes.size() != 0)
831 k = (s32)myrand() % (s32)active_nodes.size();
833 // Flow water to active nodes
835 //for(s32 h=0; h<1; h++)
837 if(active_nodes.size() == 0)
843 clearFlag(VOXELFLAG_CHECKED);
845 //dstream<<"Selecting a new active_node"<<std::endl;
849 core::map<v3s16, u8>::Node
850 *n = active_nodes.getIterator().getNode();
855 core::map<v3s16, u8>::Iterator
856 i = active_nodes.getIterator().getNode();
857 for(s32 j=0; j<k; j++)
861 core::map<v3s16, u8>::Node *n = i.getNode();
863 // Decrement index if less than 0.
864 // This keeps us in existing indices always.
869 v3s16 p = n->getKey();
870 active_nodes.remove(p);
871 flowWater(p, active_nodes, recursion_depth,
872 debugprint, stoptime);
876 catch(ProcessingLimitException &e)
878 //dstream<<"getWaterPressure ProcessingLimitException"<<std::endl;
881 /*v3s16 e = m_area.getExtent();
882 s32 v = m_area.getVolume();
883 dstream<<"flowWater (active): "
884 <<"area ended up as "
885 <<e.X<<"x"<<e.Y<<"x"<<e.Z<<" = "<<v
886 <<", handled a_node count: "<<handled_count
887 <<", active_nodes.size() = "<<active_nodes.size()
889 dstream<<"addarea_time: "<<addarea_time
890 <<", emerge_time: "<<emerge_time
891 <<", emerge_load_time: "<<emerge_load_time
892 <<", clearflag_time: "<<clearflag_time
893 <<", flowwater_pre_time: "<<flowwater_pre_time
894 <<", updateareawaterpressure_time: "<<updateareawaterpressure_time