]> git.lizzy.rs Git - dragonfireclient.git/blob - src/voxel.cpp
framework for modifying textures
[dragonfireclient.git] / src / voxel.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
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.
9
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.
14
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.
18 */
19
20 #include "voxel.h"
21 #include "map.h"
22
23 // For TimeTaker
24 #include "main.h"
25 #include "utility.h"
26
27 /*
28         Debug stuff
29 */
30 u32 addarea_time = 0;
31 u32 emerge_time = 0;
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;
38
39
40 VoxelManipulator::VoxelManipulator():
41         m_data(NULL),
42         m_flags(NULL)
43 {
44         m_disable_water_climb = false;
45 }
46
47 VoxelManipulator::~VoxelManipulator()
48 {
49         clear();
50         if(m_data)
51                 delete[] m_data;
52         if(m_flags)
53                 delete[] m_flags;
54 }
55
56 void VoxelManipulator::clear()
57 {
58         // Reset area to volume=0
59         m_area = VoxelArea();
60         if(m_data)
61                 delete[] m_data;
62         m_data = NULL;
63         if(m_flags)
64                 delete[] m_flags;
65         m_flags = NULL;
66 }
67
68 void VoxelManipulator::print(std::ostream &o, VoxelPrintMode mode)
69 {
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;
74         
75         for(s32 y=m_area.MaxEdge.Y; y>=m_area.MinEdge.Y; y--)
76         {
77                 if(em.X >= 3 && em.Y >= 3)
78                 {
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-> ";
82                         else                           o<<"      ";
83                 }
84
85                 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
86                 {
87                         for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
88                         {
89                                 u8 f = m_flags[m_area.index(x,y,z)];
90                                 char c;
91                                 if(f & VOXELFLAG_NOT_LOADED)
92                                         c = 'N';
93                                 else if(f & VOXELFLAG_INEXISTENT)
94                                         c = 'I';
95                                 else
96                                 {
97                                         c = 'X';
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)
101                                         {
102                                                 if(m <= 9)
103                                                         c = m + '0';
104                                         }
105                                         else if(mode == VOXELPRINT_WATERPRESSURE)
106                                         {
107                                                 if(m == CONTENT_WATER)
108                                                 {
109                                                         c = 'w';
110                                                         if(pr <= 9)
111                                                                 c = pr + '0';
112                                                 }
113                                                 else if(liquid_replaces_content(m))
114                                                 {
115                                                         c = ' ';
116                                                 }
117                                                 else
118                                                 {
119                                                         c = '#';
120                                                 }
121                                         }
122                                 }
123                                 o<<c;
124                         }
125                         o<<' ';
126                 }
127                 o<<std::endl;
128         }
129 }
130
131 void VoxelManipulator::addArea(VoxelArea area)
132 {
133         // Cancel if requested area has zero volume
134         if(area.getExtent() == v3s16(0,0,0))
135                 return;
136         
137         // Cancel if m_area already contains the requested area
138         if(m_area.contains(area))
139                 return;
140         
141         TimeTaker timer("addArea", g_irrlicht, &addarea_time);
142
143         // Calculate new area
144         VoxelArea new_area;
145         // New area is the requested area if m_area has zero volume
146         if(m_area.getExtent() == v3s16(0,0,0))
147         {
148                 new_area = area;
149         }
150         // Else add requested area to m_area
151         else
152         {
153                 new_area = m_area;
154                 new_area.addArea(area);
155         }
156
157         s32 new_size = new_area.getVolume();
158
159         /*dstream<<"adding area ";
160         area.print(dstream);
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;*/
167
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++)
172         {
173                 new_flags[i] = VOXELFLAG_NOT_LOADED;
174         }
175         
176         // Copy old data
177         
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++)
181         {
182                 // If loaded, copy data and flags
183                 if((m_flags[m_area.index(x,y,z)] & VOXELFLAG_NOT_LOADED) == false)
184                 {
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)];
187                 }
188         }
189
190         // Replace area, data and flags
191         
192         m_area = new_area;
193         
194         MapNode *old_data = m_data;
195         u8 *old_flags = m_flags;
196
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;*/
199
200         m_data = new_data;
201         m_flags = new_flags;
202         
203         if(old_data)
204                 delete[] old_data;
205         if(old_flags)
206                 delete[] old_flags;
207
208         //dstream<<"addArea done"<<std::endl;
209 }
210
211 void VoxelManipulator::copyFrom(MapNode *src, VoxelArea src_area,
212                 v3s16 from_pos, v3s16 to_pos, v3s16 size)
213 {
214         for(s16 z=0; z<size.Z; z++)
215         for(s16 y=0; y<size.Y; y++)
216         {
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);
221         }
222 }
223
224 void VoxelManipulator::interpolate(VoxelArea area)
225 {
226         VoxelArea emerge_area = area;
227         emerge_area.MinEdge -= v3s16(1,1,1);
228         emerge_area.MaxEdge += v3s16(1,1,1);
229         emerge(emerge_area);
230
231         SharedBuffer<u8> buf(area.getVolume());
232
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++)
236         {
237                 v3s16 p(x,y,z);
238
239                 v3s16 dirs[] = {
240                         v3s16(1,1,0),
241                         v3s16(1,0,1),
242                         v3s16(1,-1,0),
243                         v3s16(1,0,-1),
244                         v3s16(-1,1,0),
245                         v3s16(-1,0,1),
246                         v3s16(-1,-1,0),
247                         v3s16(-1,0,-1),
248                 };
249                 //const v3s16 *dirs = g_26dirs;
250                 
251                 s16 total = 0;
252                 s16 airness = 0;
253                 u8 m = CONTENT_IGNORE;
254
255                 for(s16 i=0; i<8; i++)
256                 //for(s16 i=0; i<26; i++)
257                 {
258                         v3s16 p2 = p + dirs[i];
259
260                         u8 f = m_flags[m_area.index(p2)];
261                         assert(!(f & VOXELFLAG_NOT_LOADED));
262                         if(f & VOXELFLAG_INEXISTENT)
263                                 continue;
264
265                         MapNode &n = m_data[m_area.index(p2)];
266
267                         airness += (n.d == CONTENT_AIR) ? 1 : -1;
268                         total++;
269
270                         if(m == CONTENT_IGNORE && n.d != CONTENT_AIR)
271                                 m = n.d;
272                 }
273
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;
278         }
279
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++)
283         {
284                 v3s16 p(x,y,z);
285                 m_data[m_area.index(p)].d = buf[area.index(p)];
286         }
287 }
288
289
290 void VoxelManipulator::clearFlag(u8 flags)
291 {
292         // 0-1ms on moderate area
293         TimeTaker timer("clearFlag", g_irrlicht, &clearflag_time);
294
295         v3s16 s = m_area.getExtent();
296
297         /*dstream<<"clearFlag clearing area of size "
298                         <<""<<s.X<<"x"<<s.Y<<"x"<<s.Z<<""
299                         <<std::endl;*/
300
301         //s32 count = 0;
302
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++)
306         {
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)
310                         count++;
311         }*/
312
313         s32 volume = m_area.getVolume();
314         for(s32 i=0; i<volume; i++)
315         {
316                 m_flags[i] &= ~flags;
317         }
318
319         /*s32 volume = m_area.getVolume();
320         for(s32 i=0; i<volume; i++)
321         {
322                 u8 f = m_flags[i];
323                 m_flags[i] &= ~flags;
324                 if(m_flags[i] != f)
325                         count++;
326         }
327
328         dstream<<"clearFlag changed "<<count<<" flags out of "
329                         <<volume<<" nodes"<<std::endl;*/
330 }
331
332 int VoxelManipulator::getWaterPressure(v3s16 p, s16 &highest_y, int recur_count)
333 {
334         m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED2;
335
336         if(p.Y > highest_y)
337                 highest_y = p.Y;
338         
339         /*if(recur_count > 1000)
340                 throw ProcessingLimitException
341                                 ("getWaterPressure recur_count limit reached");*/
342         
343         if(recur_count > 10000)
344                 return -1;
345         
346         recur_count++;
347
348         v3s16 dirs[6] = {
349                 v3s16(0,1,0), // top
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
355         };
356
357         // Load neighboring nodes
358         emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)), 1);
359
360         s32 i;
361         for(i=0; i<6; i++)
362         {
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))
367                         continue;
368                 MapNode &n = m_data[m_area.index(p2)];
369                 // Ignore non-liquid nodes
370                 if(content_liquid(n.d) == false)
371                         continue;
372
373                 int pr;
374
375                 // If at ocean surface
376                 if(n.pressure == 1 && n.d == CONTENT_OCEAN)
377                 //if(n.pressure == 1) // Causes glitches but is fast
378                 {
379                         pr = 1;
380                 }
381                 // Otherwise recurse more
382                 else
383                 {
384                         pr = getWaterPressure(p2, highest_y, recur_count);
385                         if(pr == -1)
386                                 continue;
387                 }
388
389                 // If block is at top, pressure here is one higher
390                 if(i == 0)
391                 {
392                         if(pr < 255)
393                                 pr++;
394                 }
395                 // If block is at bottom, pressure here is one lower
396                 else if(i == 5)
397                 {
398                         if(pr > 1)
399                                 pr--;
400                 }
401                 
402                 // Node is on the pressure route
403                 m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED4;
404
405                 // Got pressure
406                 return pr;
407         }
408         
409         // Nothing useful found
410         return -1;
411 }
412
413 void VoxelManipulator::spreadWaterPressure(v3s16 p, int pr,
414                 VoxelArea request_area,
415                 core::map<v3s16, u8> &active_nodes,
416                 int recur_count)
417 {
418         //if(recur_count > 10000)
419                 /*throw ProcessingLimitException
420                                 ("spreadWaterPressure recur_count limit reached");*/
421         if(recur_count > 10)
422                 return;
423         recur_count++;
424         
425         /*dstream<<"spreadWaterPressure: p=("
426                         <<p.X<<","<<p.Y<<","<<p.Z<<")"
427                         <<", oldpr="<<(int)m_data[m_area.index(p)].pressure
428                         <<", pr="<<pr
429                         <<", recur_count="<<recur_count
430                         <<", request_area=";
431         request_area.print(dstream);
432         dstream<<std::endl;*/
433
434         m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED3;
435         m_data[m_area.index(p)].pressure = pr;
436
437         v3s16 dirs[6] = {
438                 v3s16(0,1,0), // top
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
444         };
445
446         // Load neighboring nodes
447         emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)), 2);
448
449         s32 i;
450         for(i=0; i<6; i++)
451         {
452                 v3s16 p2 = p + dirs[i];
453                 
454                 u8 f = m_flags[m_area.index(p2)];
455
456                 // Ignore inexistent and checked nodes
457                 if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED3))
458                         continue;
459
460                 MapNode &n = m_data[m_area.index(p2)];
461                 
462                 /*
463                         If material is air:
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.
467                 */
468                 if(liquid_replaces_content(n.d))
469                 {
470                         bool pressure_causes_flow = false;
471                         // If empty block is at top
472                         if(i == 0)
473                         {
474                                 if(m_disable_water_climb)
475                                         continue;
476                                 
477                                 //if(pr >= PRESERVE_WATER_VOLUME ? 3 : 2)
478                                 if(pr >= 3)
479                                         pressure_causes_flow = true;
480                         }
481                         // If block is at bottom
482                         else if(i == 5)
483                         {
484                                 pressure_causes_flow = true;
485                         }
486                         // If block is at side
487                         else
488                         {
489                                 //if(pr >= PRESERVE_WATER_VOLUME ? 2 : 1)
490                                 if(pr >= 2)
491                                         pressure_causes_flow = true;
492                         }
493                         
494                         if(pressure_causes_flow)
495                         {
496                                 active_nodes[p2] = 1;
497                         }
498
499                         continue;
500                 }
501
502                 // Ignore non-liquid nodes
503                 if(content_liquid(n.d) == false)
504                         continue;
505
506                 int pr2 = pr;
507                 // If block is at top, pressure there is lower
508                 if(i == 0)
509                 {
510                         if(pr2 > 0)
511                                 pr2--;
512                 }
513                 // If block is at bottom, pressure there is higher
514                 else if(i == 5)
515                 {
516                         if(pr2 < 255)
517                                 pr2++;
518                 }
519
520                 /*if(m_disable_water_climb)
521                 {
522                         if(pr2 > 3)
523                                 pr2 = 3;
524                 }*/
525                 
526                 // Ignore if correct pressure is already set and is not on
527                 // request_area.
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)
532                         continue;
533
534                 spreadWaterPressure(p2, pr2, request_area, active_nodes, recur_count);
535         }
536 }
537
538 void VoxelManipulator::updateAreaWaterPressure(VoxelArea a,
539                 core::map<v3s16, u8> &active_nodes,
540                 bool checked3_is_clear)
541 {
542         TimeTaker timer("updateAreaWaterPressure", g_irrlicht,
543                         &updateareawaterpressure_time);
544
545         emerge(a, 3);
546         
547         bool checked2_clear = false;
548         
549         if(checked3_is_clear == false)
550         {
551                 //clearFlag(VOXELFLAG_CHECKED3);
552
553                 clearFlag(VOXELFLAG_CHECKED3 | VOXELFLAG_CHECKED2);
554                 checked2_clear = true;
555         }
556         
557
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++)
561         {
562                 v3s16 p(x,y,z);
563
564                 u8 f = m_flags[m_area.index(p)];
565                 // Ignore inexistent or checked nodes
566                 if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED3))
567                         continue;
568                 MapNode &n = m_data[m_area.index(p)];
569                 // Ignore non-liquid nodes
570                 if(content_liquid(n.d) == false)
571                         continue;
572                 
573                 if(checked2_clear == false)
574                 {
575                         clearFlag(VOXELFLAG_CHECKED2);
576                         checked2_clear = true;
577                 }
578
579                 checked2_clear = false;
580
581                 s16 highest_y = -32768;
582                 int recur_count = 0;
583                 int pr = -1;
584
585                 try
586                 {
587                         // 0-1ms @ recur_count <= 100
588                         //TimeTaker timer("getWaterPressure", g_irrlicht);
589                         pr = getWaterPressure(p, highest_y, recur_count);
590                 }
591                 catch(ProcessingLimitException &e)
592                 {
593                         //dstream<<"getWaterPressure ProcessingLimitException"<<std::endl;
594                 }
595
596                 if(pr == -1)
597                 {
598                         assert(highest_y != -32768);
599
600                         pr = highest_y - p.Y + 1;
601                         if(pr > 255)
602                                 pr = 255;
603
604                         /*dstream<<"WARNING: Pressure at ("
605                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"
606                                         <<" = "<<pr
607                                         //<<" and highest_y == -32768"
608                                         <<std::endl;
609                         assert(highest_y != -32768);
610                         continue;*/
611                 }
612                 
613                 try
614                 {
615                         // 0ms
616                         //TimeTaker timer("spreadWaterPressure", g_irrlicht);
617                         spreadWaterPressure(p, pr, a, active_nodes, 0);
618                 }
619                 catch(ProcessingLimitException &e)
620                 {
621                         //dstream<<"getWaterPressure ProcessingLimitException"<<std::endl;
622                 }
623         }
624 }
625
626 bool VoxelManipulator::flowWater(v3s16 removed_pos,
627                 core::map<v3s16, u8> &active_nodes,
628                 int recursion_depth, bool debugprint,
629                 u32 stoptime)
630 {
631         v3s16 dirs[6] = {
632                 v3s16(0,1,0), // top
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
638         };
639
640         recursion_depth++;
641
642         v3s16 p;
643         bool from_ocean = false;
644         
645         // Randomize horizontal order
646         static s32 cs = 0;
647         if(cs < 3)
648                 cs++;
649         else
650                 cs = 0;
651         s16 s1 = (cs & 1) ? 1 : -1;
652         s16 s2 = (cs & 2) ? 1 : -1;
653         //dstream<<"s1="<<s1<<", s2="<<s2<<std::endl;
654
655         {
656         TimeTaker timer1("flowWater pre", g_irrlicht, &flowwater_pre_time);
657         
658         // Load neighboring nodes
659         emerge(VoxelArea(removed_pos - v3s16(1,1,1), removed_pos + v3s16(1,1,1)), 4);
660         
661         // Ignore incorrect removed_pos
662         {
663                 u8 f = m_flags[m_area.index(removed_pos)];
664                 // Ignore inexistent or checked node
665                 if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED))
666                         return false;
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)
670                         return false;
671         }
672         
673         s32 i;
674         for(i=0; i<6; i++)
675         {
676                 // Don't raise water from bottom
677                 if(m_disable_water_climb && i == 5)
678                         continue;
679
680                 p = removed_pos + v3s16(s1*dirs[i].X, dirs[i].Y, s2*dirs[i].Z);
681
682                 u8 f = m_flags[m_area.index(p)];
683                 // Inexistent or checked nodes can't move
684                 if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED))
685                         continue;
686                 MapNode &n = m_data[m_area.index(p)];
687                 // Only liquid nodes can move
688                 if(content_liquid(n.d) == false)
689                         continue;
690                 // If block is at top, select it always
691                 if(i == 0)
692                 {
693                         break;
694                 }
695                 // If block is at bottom, select it if it has enough pressure
696                 if(i == 5)
697                 {
698                         //if(n.pressure >= PRESERVE_WATER_VOLUME ? 3 : 2)
699                         if(n.pressure >= 3)
700                                 break;
701                         continue;
702                 }
703                 // Else block is at some side. Select it if it has enough pressure
704                 //if(n.pressure >= PRESERVE_WATER_VOLUME ? 2 : 1)
705                 if(n.pressure >= 2)
706                 {
707                         break;
708                 }
709         }
710
711         // If there is nothing to move, return
712         if(i==6)
713                 return false;
714
715         /*
716                 Move water and bubble
717         */
718
719         u8 m = m_data[m_area.index(p)].d;
720         u8 f = m_flags[m_area.index(p)];
721
722         if(m == CONTENT_OCEAN)
723                 from_ocean = true;
724
725         // Move air bubble if not taking water from ocean
726         if(from_ocean == false)
727         {
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)];
730         }
731         
732         /*
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.
736         */
737         u8 light = m_data[m_area.index(removed_pos)].getLightBanksWithSource();
738
739         m_data[m_area.index(removed_pos)].d = m;
740         m_flags[m_area.index(removed_pos)] = f;
741
742         m_data[m_area.index(removed_pos)].setLightBanks(light);
743         
744         // Mark removed_pos checked
745         m_flags[m_area.index(removed_pos)] |= VOXELFLAG_CHECKED;
746
747         // If block was dropped from surface, increase pressure
748         if(i == 0 && m_data[m_area.index(removed_pos)].pressure == 1)
749         {
750                 m_data[m_area.index(removed_pos)].pressure = 2;
751         }
752         
753         /*
754         NOTE: This does not work as-is
755         if(m == CONTENT_OCEAN)
756         {
757                 // If block was raised to surface, increase pressure of
758                 // source node
759                 if(i == 5 && m_data[m_area.index(p)].pressure == 1)
760                 {
761                         m_data[m_area.index(p)].pressure = 2;
762                 }
763         }*/
764         
765         /*if(debugprint)
766         {
767                 dstream<<"VoxelManipulator::flowWater(): Moved bubble:"<<std::endl;
768                 print(dstream, VOXELPRINT_WATERPRESSURE);
769         }*/
770
771         // Update pressure
772         VoxelArea a;
773         a.addPoint(p - v3s16(1,1,1));
774         a.addPoint(p + v3s16(1,1,1));
775         a.addPoint(removed_pos - v3s16(1,1,1));
776         a.addPoint(removed_pos + v3s16(1,1,1));
777         updateAreaWaterPressure(a, active_nodes);
778         
779         /*if(debugprint)
780         {
781                 dstream<<"VoxelManipulator::flowWater(): Pressure updated:"<<std::endl;
782                 print(dstream, VOXELPRINT_WATERPRESSURE);
783                 //std::cin.get();
784         }*/
785
786         if(debugprint)
787         {
788                 dstream<<"VoxelManipulator::flowWater(): step done:"<<std::endl;
789                 print(dstream, VOXELPRINT_WATERPRESSURE);
790                 //std::cin.get();
791         }
792         
793         }//timer1
794         
795         //if(PRESERVE_WATER_VOLUME)
796         if(from_ocean == false)
797         {
798                 // Flow water to the newly created empty position
799                 /*flowWater(p, active_nodes, recursion_depth,
800                                 debugprint, counter, counterlimit);*/
801                 flowWater(p, active_nodes, recursion_depth,
802                                 debugprint, stoptime);
803         }
804         
805         if(stoptime != 0 && g_irrlicht != NULL)
806         {
807                 u32 timenow = g_irrlicht->getTime();
808                 if(timenow >= stoptime ||
809                                 (stoptime < 0x80000000 && timenow > 0x80000000))
810                 {
811                         dstream<<"flowWater: stoptime reached"<<std::endl;
812                         throw ProcessingLimitException("flowWater stoptime reached");
813                 }
814         }
815         
816 find_again:
817         
818         // Try flowing water to empty positions around removed_pos.
819         // They are checked in reverse order compared to the previous loop.
820         for(s32 i=5; i>=0; i--)
821         {
822                 // Don't try to flow to top
823                 if(m_disable_water_climb && i == 0)
824                         continue;
825
826                 //v3s16 p = removed_pos + dirs[i];
827                 p = removed_pos + v3s16(s1*dirs[i].X, dirs[i].Y, s2*dirs[i].Z);
828
829                 u8 f = m_flags[m_area.index(p)];
830                 // Water can't move to inexistent nodes
831                 if(f & VOXELFLAG_INEXISTENT)
832                         continue;
833                 MapNode &n = m_data[m_area.index(p)];
834                 // Water can only move to air
835                 if(liquid_replaces_content(n.d) == false)
836                         continue;
837                         
838                 // Flow water to node
839                 bool moved =
840                 flowWater(p, active_nodes, recursion_depth,
841                                 debugprint, stoptime);
842                 /*flowWater(p, active_nodes, recursion_depth,
843                                 debugprint, counter, counterlimit);*/
844                 
845                 if(moved)
846                 {
847                         // Search again from all neighbors
848                         goto find_again;
849                 }
850         }
851
852         return true;
853 }
854
855 void VoxelManipulator::flowWater(
856                 core::map<v3s16, u8> &active_nodes,
857                 int recursion_depth, bool debugprint,
858                 u32 timelimit)
859 {
860         addarea_time = 0;
861         emerge_time = 0;
862         emerge_load_time = 0;
863         clearflag_time = 0;
864         updateareawaterpressure_time = 0;
865         flowwater_pre_time = 0;
866
867         if(active_nodes.size() == 0)
868         {
869                 dstream<<"flowWater: no active nodes"<<std::endl;
870                 return;
871         }
872
873         //TimeTaker timer1("flowWater (active_nodes)", g_irrlicht);
874
875         //dstream<<"active_nodes.size() = "<<active_nodes.size()<<std::endl;
876
877
878         u32 stoptime = 0;
879         if(g_irrlicht != NULL)
880         {
881                 stoptime = g_irrlicht->getTime() + timelimit;
882         }
883
884         // Count of handled active nodes
885         u32 handled_count = 0;
886
887         try
888         {
889
890         /*
891                 Take random one at first
892
893                 This is randomized only at the first time so that all
894                 subsequent nodes will be taken at roughly the same position
895         */
896         s32 k = 0;
897         if(active_nodes.size() != 0)
898                 k = (s32)rand() % (s32)active_nodes.size();
899
900         // Flow water to active nodes
901         for(;;)
902         //for(s32 h=0; h<1; h++)
903         {
904                 if(active_nodes.size() == 0)
905                         break;
906
907                 handled_count++;
908                 
909                 // Clear check flags
910                 clearFlag(VOXELFLAG_CHECKED);
911                 
912                 //dstream<<"Selecting a new active_node"<<std::endl;
913
914 #if 0
915                 // Take first one
916                 core::map<v3s16, u8>::Node
917                                 *n = active_nodes.getIterator().getNode();
918 #endif
919
920 #if 1
921                 
922                 core::map<v3s16, u8>::Iterator
923                                 i = active_nodes.getIterator().getNode();
924                 for(s32 j=0; j<k; j++)
925                 {
926                         i++;
927                 }
928                 core::map<v3s16, u8>::Node *n = i.getNode();
929
930                 // Decrement index if less than 0.
931                 // This keeps us in existing indices always.
932                 if(k > 0)
933                         k--;
934 #endif
935
936                 v3s16 p = n->getKey();
937                 active_nodes.remove(p);
938                 flowWater(p, active_nodes, recursion_depth,
939                                 debugprint, stoptime);
940         }
941
942         }
943         catch(ProcessingLimitException &e)
944         {
945                 //dstream<<"getWaterPressure ProcessingLimitException"<<std::endl;
946         }
947         
948         /*v3s16 e = m_area.getExtent();
949         s32 v = m_area.getVolume();
950         dstream<<"flowWater (active): "
951                         <<"area ended up as "
952                         <<e.X<<"x"<<e.Y<<"x"<<e.Z<<" = "<<v
953                         <<", handled a_node count: "<<handled_count
954                         <<", active_nodes.size() = "<<active_nodes.size()
955                         <<std::endl;
956         dstream<<"addarea_time: "<<addarea_time
957                         <<", emerge_time: "<<emerge_time
958                         <<", emerge_load_time: "<<emerge_load_time
959                         <<", clearflag_time: "<<clearflag_time
960                         <<", flowwater_pre_time: "<<flowwater_pre_time
961                         <<", updateareawaterpressure_time: "<<updateareawaterpressure_time
962                         <<std::endl;*/
963 }
964
965
966 //END