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", g_device, &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);
224 void VoxelManipulator::interpolate(VoxelArea area)
226 VoxelArea emerge_area = area;
227 emerge_area.MinEdge -= v3s16(1,1,1);
228 emerge_area.MaxEdge += v3s16(1,1,1);
231 SharedBuffer<u8> buf(area.getVolume());
233 for(s32 z=area.MinEdge.Z; z<=area.MaxEdge.Z; z++)
234 for(s32 y=area.MinEdge.Y; y<=area.MaxEdge.Y; y++)
235 for(s32 x=area.MinEdge.X; x<=area.MaxEdge.X; x++)
249 //const v3s16 *dirs = g_26dirs;
253 u8 m = CONTENT_IGNORE;
255 for(s16 i=0; i<8; i++)
256 //for(s16 i=0; i<26; i++)
258 v3s16 p2 = p + dirs[i];
260 u8 f = m_flags[m_area.index(p2)];
261 assert(!(f & VOXELFLAG_NOT_LOADED));
262 if(f & VOXELFLAG_INEXISTENT)
265 MapNode &n = m_data[m_area.index(p2)];
267 airness += (n.d == CONTENT_AIR) ? 1 : -1;
270 if(m == CONTENT_IGNORE && n.d != CONTENT_AIR)
274 // 1 if air, 0 if not
275 buf[area.index(p)] = airness > -total/2 ? CONTENT_AIR : m;
276 //buf[area.index(p)] = airness > -total ? CONTENT_AIR : m;
277 //buf[area.index(p)] = airness >= -7 ? CONTENT_AIR : m;
280 for(s32 z=area.MinEdge.Z; z<=area.MaxEdge.Z; z++)
281 for(s32 y=area.MinEdge.Y; y<=area.MaxEdge.Y; y++)
282 for(s32 x=area.MinEdge.X; x<=area.MaxEdge.X; x++)
285 m_data[m_area.index(p)].d = buf[area.index(p)];
290 void VoxelManipulator::clearFlag(u8 flags)
292 // 0-1ms on moderate area
293 TimeTaker timer("clearFlag", g_device, &clearflag_time);
295 v3s16 s = m_area.getExtent();
297 /*dstream<<"clearFlag clearing area of size "
298 <<""<<s.X<<"x"<<s.Y<<"x"<<s.Z<<""
303 /*for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
304 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
305 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
307 u8 f = m_flags[m_area.index(x,y,z)];
308 m_flags[m_area.index(x,y,z)] &= ~flags;
309 if(m_flags[m_area.index(x,y,z)] != f)
313 s32 volume = m_area.getVolume();
314 for(s32 i=0; i<volume; i++)
316 m_flags[i] &= ~flags;
319 /*s32 volume = m_area.getVolume();
320 for(s32 i=0; i<volume; i++)
323 m_flags[i] &= ~flags;
328 dstream<<"clearFlag changed "<<count<<" flags out of "
329 <<volume<<" nodes"<<std::endl;*/
332 int VoxelManipulator::getWaterPressure(v3s16 p, s16 &highest_y, int recur_count)
334 m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED2;
339 /*if(recur_count > 1000)
340 throw ProcessingLimitException
341 ("getWaterPressure recur_count limit reached");*/
343 if(recur_count > 10000)
350 v3s16(0,0,1), // back
351 v3s16(0,0,-1), // front
352 v3s16(1,0,0), // right
353 v3s16(-1,0,0), // left
354 v3s16(0,-1,0), // bottom
357 // Load neighboring nodes
358 emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)), 1);
363 v3s16 p2 = p + dirs[i];
364 u8 f = m_flags[m_area.index(p2)];
365 // Ignore inexistent or checked nodes
366 if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED2))
368 MapNode &n = m_data[m_area.index(p2)];
369 // Ignore non-liquid nodes
370 if(content_liquid(n.d) == false)
375 // If at ocean surface
376 if(n.pressure == 1 && n.d == CONTENT_OCEAN)
377 //if(n.pressure == 1) // Causes glitches but is fast
381 // Otherwise recurse more
384 pr = getWaterPressure(p2, highest_y, recur_count);
389 // If block is at top, pressure here is one higher
395 // If block is at bottom, pressure here is one lower
402 // Node is on the pressure route
403 m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED4;
409 // Nothing useful found
413 void VoxelManipulator::spreadWaterPressure(v3s16 p, int pr,
414 VoxelArea request_area,
415 core::map<v3s16, u8> &active_nodes,
418 //if(recur_count > 10000)
419 /*throw ProcessingLimitException
420 ("spreadWaterPressure recur_count limit reached");*/
425 /*dstream<<"spreadWaterPressure: p=("
426 <<p.X<<","<<p.Y<<","<<p.Z<<")"
427 <<", oldpr="<<(int)m_data[m_area.index(p)].pressure
429 <<", recur_count="<<recur_count
431 request_area.print(dstream);
432 dstream<<std::endl;*/
434 m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED3;
435 m_data[m_area.index(p)].pressure = pr;
439 v3s16(-1,0,0), // left
440 v3s16(1,0,0), // right
441 v3s16(0,0,-1), // front
442 v3s16(0,0,1), // back
443 v3s16(0,-1,0), // bottom
446 // Load neighboring nodes
447 emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)), 2);
452 v3s16 p2 = p + dirs[i];
454 u8 f = m_flags[m_area.index(p2)];
456 // Ignore inexistent and checked nodes
457 if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED3))
460 MapNode &n = m_data[m_area.index(p2)];
464 add to active_nodes if there is flow-causing pressure.
465 NOTE: Do not remove anything from there. We cannot know
466 here if some other neighbor of it causes flow.
468 if(liquid_replaces_content(n.d))
470 bool pressure_causes_flow = false;
471 // If empty block is at top
474 if(m_disable_water_climb)
477 //if(pr >= PRESERVE_WATER_VOLUME ? 3 : 2)
479 pressure_causes_flow = true;
481 // If block is at bottom
484 pressure_causes_flow = true;
486 // If block is at side
489 //if(pr >= PRESERVE_WATER_VOLUME ? 2 : 1)
491 pressure_causes_flow = true;
494 if(pressure_causes_flow)
496 active_nodes[p2] = 1;
502 // Ignore non-liquid nodes
503 if(content_liquid(n.d) == false)
507 // If block is at top, pressure there is lower
513 // If block is at bottom, pressure there is higher
520 /*if(m_disable_water_climb)
526 // Ignore if correct pressure is already set and is not on
528 // Thus, request_area can be used for updating as much
529 // pressure info in some area as possible to possibly
530 // make some calls to getWaterPressure unnecessary.
531 if(n.pressure == pr2 && request_area.contains(p2) == false)
534 spreadWaterPressure(p2, pr2, request_area, active_nodes, recur_count);
538 void VoxelManipulator::updateAreaWaterPressure(VoxelArea a,
539 core::map<v3s16, u8> &active_nodes,
540 bool checked3_is_clear)
542 TimeTaker timer("updateAreaWaterPressure", g_device,
543 &updateareawaterpressure_time);
547 bool checked2_clear = false;
549 if(checked3_is_clear == false)
551 //clearFlag(VOXELFLAG_CHECKED3);
553 clearFlag(VOXELFLAG_CHECKED3 | VOXELFLAG_CHECKED2);
554 checked2_clear = true;
558 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
559 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
560 for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
564 u8 f = m_flags[m_area.index(p)];
565 // Ignore inexistent or checked nodes
566 if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED3))
568 MapNode &n = m_data[m_area.index(p)];
569 // Ignore non-liquid nodes
570 if(content_liquid(n.d) == false)
573 if(checked2_clear == false)
575 clearFlag(VOXELFLAG_CHECKED2);
576 checked2_clear = true;
579 checked2_clear = false;
581 s16 highest_y = -32768;
587 // 0-1ms @ recur_count <= 100
588 //TimeTaker timer("getWaterPressure", g_device);
589 pr = getWaterPressure(p, highest_y, recur_count);
591 catch(ProcessingLimitException &e)
593 //dstream<<"getWaterPressure ProcessingLimitException"<<std::endl;
598 assert(highest_y != -32768);
600 pr = highest_y - p.Y + 1;
604 /*dstream<<"WARNING: Pressure at ("
605 <<p.X<<","<<p.Y<<","<<p.Z<<")"
607 //<<" and highest_y == -32768"
609 assert(highest_y != -32768);
616 //TimeTaker timer("spreadWaterPressure", g_device);
617 spreadWaterPressure(p, pr, a, active_nodes, 0);
619 catch(ProcessingLimitException &e)
621 //dstream<<"getWaterPressure ProcessingLimitException"<<std::endl;
626 bool VoxelManipulator::flowWater(v3s16 removed_pos,
627 core::map<v3s16, u8> &active_nodes,
628 int recursion_depth, bool debugprint,
633 v3s16(0,0,-1), // front
634 v3s16(0,0,1), // back
635 v3s16(-1,0,0), // left
636 v3s16(1,0,0), // right
637 v3s16(0,-1,0), // bottom
643 bool from_ocean = false;
645 // Randomize horizontal order
651 s16 s1 = (cs & 1) ? 1 : -1;
652 s16 s2 = (cs & 2) ? 1 : -1;
653 //dstream<<"s1="<<s1<<", s2="<<s2<<std::endl;
656 TimeTaker timer1("flowWater pre", g_device, &flowwater_pre_time);
658 // Load neighboring nodes
659 emerge(VoxelArea(removed_pos - v3s16(1,1,1), removed_pos + v3s16(1,1,1)), 4);
661 // Ignore incorrect removed_pos
663 u8 f = m_flags[m_area.index(removed_pos)];
664 // Ignore inexistent or checked node
665 if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED))
667 MapNode &n = m_data[m_area.index(removed_pos)];
668 // Ignore nodes to which the water can't go
669 if(liquid_replaces_content(n.d) == false)
676 // Don't raise water from bottom
677 if(m_disable_water_climb && i == 5)
680 p = removed_pos + v3s16(s1*dirs[i].X, dirs[i].Y, s2*dirs[i].Z);
682 u8 f = m_flags[m_area.index(p)];
683 // Inexistent or checked nodes can't move
684 if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED))
686 MapNode &n = m_data[m_area.index(p)];
687 // Only liquid nodes can move
688 if(content_liquid(n.d) == false)
690 // If block is at top, select it always
695 // If block is at bottom, select it if it has enough pressure
698 //if(n.pressure >= PRESERVE_WATER_VOLUME ? 3 : 2)
703 // Else block is at some side. Select it if it has enough pressure
704 //if(n.pressure >= PRESERVE_WATER_VOLUME ? 2 : 1)
711 // If there is nothing to move, return
716 Move water and bubble
719 u8 m = m_data[m_area.index(p)].d;
720 u8 f = m_flags[m_area.index(p)];
722 if(m == CONTENT_OCEAN)
725 // Move air bubble if not taking water from ocean
726 if(from_ocean == false)
728 m_data[m_area.index(p)].d = m_data[m_area.index(removed_pos)].d;
729 m_flags[m_area.index(p)] = m_flags[m_area.index(removed_pos)];
733 This has to be done to copy the brightness of a light source
734 correctly. Otherwise unspreadLight will fuck up when water
735 has replaced a light source.
737 u8 light = m_data[m_area.index(removed_pos)].getLightBanksWithSource();
739 m_data[m_area.index(removed_pos)].d = m;
740 m_flags[m_area.index(removed_pos)] = f;
742 m_data[m_area.index(removed_pos)].setLightBanks(light);
744 /*// NOTE: HACK: This has to be set to LIGHT_MAX so that
745 // unspreadLight will clear all light that came from this node.
746 // Otherwise there will be weird bugs
747 m_data[m_area.index(removed_pos)].setLight(LIGHT_MAX);*/
749 // Mark removed_pos checked
750 m_flags[m_area.index(removed_pos)] |= VOXELFLAG_CHECKED;
752 // If block was dropped from surface, increase pressure
753 if(i == 0 && m_data[m_area.index(removed_pos)].pressure == 1)
755 m_data[m_area.index(removed_pos)].pressure = 2;
759 NOTE: This does not work as-is
760 if(m == CONTENT_OCEAN)
762 // If block was raised to surface, increase pressure of
764 if(i == 5 && m_data[m_area.index(p)].pressure == 1)
766 m_data[m_area.index(p)].pressure = 2;
772 dstream<<"VoxelManipulator::flowWater(): Moved bubble:"<<std::endl;
773 print(dstream, VOXELPRINT_WATERPRESSURE);
778 a.addPoint(p - v3s16(1,1,1));
779 a.addPoint(p + v3s16(1,1,1));
780 a.addPoint(removed_pos - v3s16(1,1,1));
781 a.addPoint(removed_pos + v3s16(1,1,1));
782 updateAreaWaterPressure(a, active_nodes);
786 dstream<<"VoxelManipulator::flowWater(): Pressure updated:"<<std::endl;
787 print(dstream, VOXELPRINT_WATERPRESSURE);
793 dstream<<"VoxelManipulator::flowWater(): step done:"<<std::endl;
794 print(dstream, VOXELPRINT_WATERPRESSURE);
800 //if(PRESERVE_WATER_VOLUME)
801 if(from_ocean == false)
803 // Flow water to the newly created empty position
804 /*flowWater(p, active_nodes, recursion_depth,
805 debugprint, counter, counterlimit);*/
806 flowWater(p, active_nodes, recursion_depth,
807 debugprint, stoptime);
810 if(stoptime != 0 && g_device != NULL)
812 u32 timenow = g_device->getTimer()->getRealTime();
813 if(timenow >= stoptime ||
814 (stoptime < 0x80000000 && timenow > 0x80000000))
816 dstream<<"flowWater: stoptime reached"<<std::endl;
817 throw ProcessingLimitException("flowWater stoptime reached");
823 // Try flowing water to empty positions around removed_pos.
824 // They are checked in reverse order compared to the previous loop.
825 for(s32 i=5; i>=0; i--)
827 // Don't try to flow to top
828 if(m_disable_water_climb && i == 0)
831 //v3s16 p = removed_pos + dirs[i];
832 p = removed_pos + v3s16(s1*dirs[i].X, dirs[i].Y, s2*dirs[i].Z);
834 u8 f = m_flags[m_area.index(p)];
835 // Water can't move to inexistent nodes
836 if(f & VOXELFLAG_INEXISTENT)
838 MapNode &n = m_data[m_area.index(p)];
839 // Water can only move to air
840 if(liquid_replaces_content(n.d) == false)
843 // Flow water to node
845 flowWater(p, active_nodes, recursion_depth,
846 debugprint, stoptime);
847 /*flowWater(p, active_nodes, recursion_depth,
848 debugprint, counter, counterlimit);*/
852 // Search again from all neighbors
860 void VoxelManipulator::flowWater(
861 core::map<v3s16, u8> &active_nodes,
862 int recursion_depth, bool debugprint,
867 emerge_load_time = 0;
869 updateareawaterpressure_time = 0;
870 flowwater_pre_time = 0;
872 if(active_nodes.size() == 0)
874 dstream<<"flowWater: no active nodes"<<std::endl;
878 //TimeTaker timer1("flowWater (active_nodes)", g_device);
880 //dstream<<"active_nodes.size() = "<<active_nodes.size()<<std::endl;
886 stoptime = g_device->getTimer()->getRealTime() + timelimit;
889 // Count of handled active nodes
890 u32 handled_count = 0;
896 Take random one at first
898 This is randomized only at the first time so that all
899 subsequent nodes will be taken at roughly the same position
902 if(active_nodes.size() != 0)
903 k = (s32)rand() % (s32)active_nodes.size();
905 // Flow water to active nodes
907 //for(s32 h=0; h<1; h++)
909 if(active_nodes.size() == 0)
915 clearFlag(VOXELFLAG_CHECKED);
917 //dstream<<"Selecting a new active_node"<<std::endl;
921 core::map<v3s16, u8>::Node
922 *n = active_nodes.getIterator().getNode();
927 core::map<v3s16, u8>::Iterator
928 i = active_nodes.getIterator().getNode();
929 for(s32 j=0; j<k; j++)
933 core::map<v3s16, u8>::Node *n = i.getNode();
935 // Decrement index if less than 0.
936 // This keeps us in existing indices always.
941 v3s16 p = n->getKey();
942 active_nodes.remove(p);
943 flowWater(p, active_nodes, recursion_depth,
944 debugprint, stoptime);
948 catch(ProcessingLimitException &e)
950 //dstream<<"getWaterPressure ProcessingLimitException"<<std::endl;
953 /*v3s16 e = m_area.getExtent();
954 s32 v = m_area.getVolume();
955 dstream<<"flowWater (active): "
956 <<"area ended up as "
957 <<e.X<<"x"<<e.Y<<"x"<<e.Z<<" = "<<v
958 <<", handled a_node count: "<<handled_count
959 <<", active_nodes.size() = "<<active_nodes.size()
961 dstream<<"addarea_time: "<<addarea_time
962 <<", emerge_time: "<<emerge_time
963 <<", emerge_load_time: "<<emerge_load_time
964 <<", clearflag_time: "<<clearflag_time
965 <<", flowwater_pre_time: "<<flowwater_pre_time
966 <<", updateareawaterpressure_time: "<<updateareawaterpressure_time