]> git.lizzy.rs Git - minetest.git/blob - src/voxel.cpp
Do not allow the m_transforming_liquid queue to increase until all RAM is consumed
[minetest.git] / src / voxel.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 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 Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser 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 #include "gettime.h"
23 #include "nodedef.h"
24 #include "util/timetaker.h"
25 #include <string.h>  // memcpy, memset
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 }
45
46 VoxelManipulator::~VoxelManipulator()
47 {
48         clear();
49         if(m_data)
50                 delete[] m_data;
51         if(m_flags)
52                 delete[] m_flags;
53 }
54
55 void VoxelManipulator::clear()
56 {
57         // Reset area to volume=0
58         m_area = VoxelArea();
59         if(m_data)
60                 delete[] m_data;
61         m_data = NULL;
62         if(m_flags)
63                 delete[] m_flags;
64         m_flags = NULL;
65 }
66
67 void VoxelManipulator::print(std::ostream &o, INodeDefManager *ndef,
68                 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_NO_DATA)
92                                         c = 'N';
93                                 else
94                                 {
95                                         c = 'X';
96                                         MapNode n = m_data[m_area.index(x,y,z)];
97                                         content_t m = n.getContent();
98                                         u8 pr = n.param2;
99                                         if(mode == VOXELPRINT_MATERIAL)
100                                         {
101                                                 if(m <= 9)
102                                                         c = m + '0';
103                                         }
104                                         else if(mode == VOXELPRINT_WATERPRESSURE)
105                                         {
106                                                 if(ndef->get(m).isLiquid())
107                                                 {
108                                                         c = 'w';
109                                                         if(pr <= 9)
110                                                                 c = pr + '0';
111                                                 }
112                                                 else if(m == CONTENT_AIR)
113                                                 {
114                                                         c = ' ';
115                                                 }
116                                                 else
117                                                 {
118                                                         c = '#';
119                                                 }
120                                         }
121                                         else if(mode == VOXELPRINT_LIGHT_DAY)
122                                         {
123                                                 if(ndef->get(m).light_source != 0)
124                                                         c = 'S';
125                                                 else if(ndef->get(m).light_propagates == false)
126                                                         c = 'X';
127                                                 else
128                                                 {
129                                                         u8 light = n.getLight(LIGHTBANK_DAY, ndef);
130                                                         if(light < 10)
131                                                                 c = '0' + light;
132                                                         else
133                                                                 c = 'a' + (light-10);
134                                                 }
135                                         }
136                                 }
137                                 o<<c;
138                         }
139                         o<<' ';
140                 }
141                 o<<std::endl;
142         }
143 }
144
145 void VoxelManipulator::addArea(const VoxelArea &area)
146 {
147         // Cancel if requested area has zero volume
148         if(area.getExtent() == v3s16(0,0,0))
149                 return;
150
151         // Cancel if m_area already contains the requested area
152         if(m_area.contains(area))
153                 return;
154
155         TimeTaker timer("addArea", &addarea_time);
156
157         // Calculate new area
158         VoxelArea new_area;
159         // New area is the requested area if m_area has zero volume
160         if(m_area.getExtent() == v3s16(0,0,0))
161         {
162                 new_area = area;
163         }
164         // Else add requested area to m_area
165         else
166         {
167                 new_area = m_area;
168                 new_area.addArea(area);
169         }
170
171         s32 new_size = new_area.getVolume();
172
173         /*dstream<<"adding area ";
174         area.print(dstream);
175         dstream<<", old area ";
176         m_area.print(dstream);
177         dstream<<", new area ";
178         new_area.print(dstream);
179         dstream<<", new_size="<<new_size;
180         dstream<<std::endl;*/
181
182         // Allocate and clear new data
183         MapNode *new_data = new MapNode[new_size];
184         assert(new_data);
185         u8 *new_flags = new u8[new_size];
186         assert(new_flags);
187         memset(new_flags, VOXELFLAG_NO_DATA, new_size);
188
189         // Copy old data
190         s32 old_x_width = m_area.MaxEdge.X - m_area.MinEdge.X + 1;
191         for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
192         for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
193         {
194                 unsigned int old_index = m_area.index(m_area.MinEdge.X,y,z);
195                 unsigned int new_index = new_area.index(m_area.MinEdge.X,y,z);
196
197                 memcpy(&new_data[new_index], &m_data[old_index],
198                                 old_x_width * sizeof(MapNode));
199                 memcpy(&new_flags[new_index], &m_flags[old_index],
200                                 old_x_width * sizeof(u8));
201         }
202
203         // Replace area, data and flags
204
205         m_area = new_area;
206
207         MapNode *old_data = m_data;
208         u8 *old_flags = m_flags;
209
210         /*dstream<<"old_data="<<(int)old_data<<", new_data="<<(int)new_data
211         <<", old_flags="<<(int)m_flags<<", new_flags="<<(int)new_flags<<std::endl;*/
212
213         m_data = new_data;
214         m_flags = new_flags;
215
216         if(old_data)
217                 delete[] old_data;
218         if(old_flags)
219                 delete[] old_flags;
220
221         //dstream<<"addArea done"<<std::endl;
222 }
223
224 void VoxelManipulator::copyFrom(MapNode *src, const VoxelArea& src_area,
225                 v3s16 from_pos, v3s16 to_pos, v3s16 size)
226 {
227         /* The reason for this optimised code is that we're a member function
228          * and the data type/layout of m_data is know to us: it's stored as
229          * [z*h*w + y*h + x]. Therefore we can take the calls to m_area index
230          * (which performs the preceding mapping/indexing of m_data) out of the
231          * inner loop and calculate the next index as we're iterating to gain
232          * performance.
233          *
234          * src_step and dest_step is the amount required to be added to our index
235          * every time y increments. Because the destination area may be larger
236          * than the source area we need one additional variable (otherwise we could
237          * just continue adding dest_step as is done for the source data): dest_mod.
238          * dest_mod is the difference in size between a "row" in the source data
239          * and a "row" in the destination data (I am using the term row loosely
240          * and for illustrative purposes). E.g.
241          *
242          * src       <-------------------->|'''''' dest mod ''''''''
243          * dest      <--------------------------------------------->
244          *
245          * dest_mod (it's essentially a modulus) is added to the destination index
246          * after every full iteration of the y span.
247          *
248          * This method falls under the category "linear array and incrementing
249          * index".
250          */
251
252         s32 src_step = src_area.getExtent().X;
253         s32 dest_step = m_area.getExtent().X;
254         s32 dest_mod = m_area.index(to_pos.X, to_pos.Y, to_pos.Z + 1)
255                         - m_area.index(to_pos.X, to_pos.Y, to_pos.Z)
256                         - dest_step * size.Y;
257
258         s32 i_src = src_area.index(from_pos.X, from_pos.Y, from_pos.Z);
259         s32 i_local = m_area.index(to_pos.X, to_pos.Y, to_pos.Z);
260
261         for (s16 z = 0; z < size.Z; z++) {
262                 for (s16 y = 0; y < size.Y; y++) {
263                         memcpy(&m_data[i_local], &src[i_src], size.X * sizeof(*m_data));
264                         memset(&m_flags[i_local], 0, size.X);
265                         i_src += src_step;
266                         i_local += dest_step;
267                 }
268                 i_local += dest_mod;
269         }
270 }
271
272 void VoxelManipulator::copyTo(MapNode *dst, const VoxelArea& dst_area,
273                 v3s16 dst_pos, v3s16 from_pos, v3s16 size)
274 {
275         for(s16 z=0; z<size.Z; z++)
276         for(s16 y=0; y<size.Y; y++)
277         {
278                 s32 i_dst = dst_area.index(dst_pos.X, dst_pos.Y+y, dst_pos.Z+z);
279                 s32 i_local = m_area.index(from_pos.X, from_pos.Y+y, from_pos.Z+z);
280                 for (s16 x = 0; x < size.X; x++) {
281                         if (m_data[i_local].getContent() != CONTENT_IGNORE)
282                                 dst[i_dst] = m_data[i_local];
283                         i_dst++;
284                         i_local++;
285                 }
286         }
287 }
288
289 /*
290         Algorithms
291         -----------------------------------------------------
292 */
293
294 void VoxelManipulator::clearFlag(u8 flags)
295 {
296         // 0-1ms on moderate area
297         TimeTaker timer("clearFlag", &clearflag_time);
298
299         //v3s16 s = m_area.getExtent();
300
301         /*dstream<<"clearFlag clearing area of size "
302                         <<""<<s.X<<"x"<<s.Y<<"x"<<s.Z<<""
303                         <<std::endl;*/
304
305         //s32 count = 0;
306
307         /*for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
308         for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
309         for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
310         {
311                 u8 f = m_flags[m_area.index(x,y,z)];
312                 m_flags[m_area.index(x,y,z)] &= ~flags;
313                 if(m_flags[m_area.index(x,y,z)] != f)
314                         count++;
315         }*/
316
317         s32 volume = m_area.getVolume();
318         for(s32 i=0; i<volume; i++)
319         {
320                 m_flags[i] &= ~flags;
321         }
322
323         /*s32 volume = m_area.getVolume();
324         for(s32 i=0; i<volume; i++)
325         {
326                 u8 f = m_flags[i];
327                 m_flags[i] &= ~flags;
328                 if(m_flags[i] != f)
329                         count++;
330         }
331
332         dstream<<"clearFlag changed "<<count<<" flags out of "
333                         <<volume<<" nodes"<<std::endl;*/
334 }
335
336 void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
337                 std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
338 {
339         v3s16 dirs[6] = {
340                 v3s16(0,0,1), // back
341                 v3s16(0,1,0), // top
342                 v3s16(1,0,0), // right
343                 v3s16(0,0,-1), // front
344                 v3s16(0,-1,0), // bottom
345                 v3s16(-1,0,0), // left
346         };
347
348         VoxelArea voxel_area(p - v3s16(1,1,1), p + v3s16(1,1,1));
349         addArea(voxel_area);
350
351         // Loop through 6 neighbors
352         for(u16 i=0; i<6; i++)
353         {
354                 // Get the position of the neighbor node
355                 v3s16 n2pos = p + dirs[i];
356
357                 u32 n2i = m_area.index(n2pos);
358
359                 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
360                         continue;
361
362                 MapNode &n2 = m_data[n2i];
363
364                 /*
365                         If the neighbor is dimmer than what was specified
366                         as oldlight (the light of the previous node)
367                 */
368                 u8 light2 = n2.getLight(bank, nodemgr);
369                 if(light2 < oldlight)
370                 {
371                         /*
372                                 And the neighbor is transparent and it has some light
373                         */
374                         if(nodemgr->get(n2).light_propagates && light2 != 0)
375                         {
376                                 /*
377                                         Set light to 0 and add to queue
378                                 */
379
380                                 n2.setLight(bank, 0, nodemgr);
381
382                                 unspreadLight(bank, n2pos, light2, light_sources, nodemgr);
383
384                                 /*
385                                         Remove from light_sources if it is there
386                                         NOTE: This doesn't happen nearly at all
387                                 */
388                                 /*if(light_sources.find(n2pos))
389                                 {
390                                         std::cout<<"Removed from light_sources"<<std::endl;
391                                         light_sources.remove(n2pos);
392                                 }*/
393                         }
394                 }
395                 else{
396                         light_sources.insert(n2pos);
397                 }
398         }
399 }
400
401 #if 1
402 /*
403         Goes recursively through the neighbours of the node.
404
405         Alters only transparent nodes.
406
407         If the lighting of the neighbour is lower than the lighting of
408         the node was (before changing it to 0 at the step before), the
409         lighting of the neighbour is set to 0 and then the same stuff
410         repeats for the neighbour.
411
412         The ending nodes of the routine are stored in light_sources.
413         This is useful when a light is removed. In such case, this
414         routine can be called for the light node and then again for
415         light_sources to re-light the area without the removed light.
416
417         values of from_nodes are lighting values.
418 */
419 void VoxelManipulator::unspreadLight(enum LightBank bank,
420                 std::map<v3s16, u8> & from_nodes,
421                 std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
422 {
423         if(from_nodes.empty())
424                 return;
425
426         for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
427                 j != from_nodes.end(); ++j)
428         {
429                 unspreadLight(bank, j->first, j->second, light_sources, nodemgr);
430         }
431 }
432 #endif
433
434 #if 0
435 /*
436         Goes recursively through the neighbours of the node.
437
438         Alters only transparent nodes.
439
440         If the lighting of the neighbour is lower than the lighting of
441         the node was (before changing it to 0 at the step before), the
442         lighting of the neighbour is set to 0 and then the same stuff
443         repeats for the neighbour.
444
445         The ending nodes of the routine are stored in light_sources.
446         This is useful when a light is removed. In such case, this
447         routine can be called for the light node and then again for
448         light_sources to re-light the area without the removed light.
449
450         values of from_nodes are lighting values.
451 */
452 void VoxelManipulator::unspreadLight(enum LightBank bank,
453                 core::map<v3s16, u8> & from_nodes,
454                 core::map<v3s16, bool> & light_sources)
455 {
456         v3s16 dirs[6] = {
457                 v3s16(0,0,1), // back
458                 v3s16(0,1,0), // top
459                 v3s16(1,0,0), // right
460                 v3s16(0,0,-1), // front
461                 v3s16(0,-1,0), // bottom
462                 v3s16(-1,0,0), // left
463         };
464
465         if(from_nodes.size() == 0)
466                 return;
467
468         core::map<v3s16, u8> unlighted_nodes;
469         core::map<v3s16, u8>::Iterator j;
470         j = from_nodes.getIterator();
471
472         for(; j.atEnd() == false; j++)
473         {
474                 v3s16 pos = j.getNode()->getKey();
475
476                 addArea(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
477
478                 //MapNode &n = m_data[m_area.index(pos)];
479
480                 u8 oldlight = j.getNode()->getValue();
481
482                 // Loop through 6 neighbors
483                 for(u16 i=0; i<6; i++)
484                 {
485                         // Get the position of the neighbor node
486                         v3s16 n2pos = pos + dirs[i];
487
488                         u32 n2i = m_area.index(n2pos);
489
490                         if(m_flags[n2i] & VOXELFLAG_NO_DATA)
491                                 continue;
492
493                         MapNode &n2 = m_data[n2i];
494
495                         /*
496                                 If the neighbor is dimmer than what was specified
497                                 as oldlight (the light of the previous node)
498                         */
499                         if(n2.getLight(bank, nodemgr) < oldlight)
500                         {
501                                 /*
502                                         And the neighbor is transparent and it has some light
503                                 */
504                                 if(nodemgr->get(n2).light_propagates && n2.getLight(bank, nodemgr) != 0)
505                                 {
506                                         /*
507                                                 Set light to 0 and add to queue
508                                         */
509
510                                         u8 current_light = n2.getLight(bank, nodemgr);
511                                         n2.setLight(bank, 0);
512
513                                         unlighted_nodes.insert(n2pos, current_light);
514
515                                         /*
516                                                 Remove from light_sources if it is there
517                                                 NOTE: This doesn't happen nearly at all
518                                         */
519                                         /*if(light_sources.find(n2pos))
520                                         {
521                                                 std::cout<<"Removed from light_sources"<<std::endl;
522                                                 light_sources.remove(n2pos);
523                                         }*/
524                                 }
525                         }
526                         else{
527                                 light_sources.insert(n2pos, true);
528                         }
529                 }
530         }
531
532         /*dstream<<"unspreadLight(): Changed block "
533                         <<blockchangecount<<" times"
534                         <<" for "<<from_nodes.size()<<" nodes"
535                         <<std::endl;*/
536
537         if(unlighted_nodes.size() > 0)
538                 unspreadLight(bank, unlighted_nodes, light_sources);
539 }
540 #endif
541
542 void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p,
543                 INodeDefManager *nodemgr)
544 {
545         const v3s16 dirs[6] = {
546                 v3s16(0,0,1), // back
547                 v3s16(0,1,0), // top
548                 v3s16(1,0,0), // right
549                 v3s16(0,0,-1), // front
550                 v3s16(0,-1,0), // bottom
551                 v3s16(-1,0,0), // left
552         };
553
554         VoxelArea voxel_area(p - v3s16(1,1,1), p + v3s16(1,1,1));
555         addArea(voxel_area);
556
557         u32 i = m_area.index(p);
558
559         if(m_flags[i] & VOXELFLAG_NO_DATA)
560                 return;
561
562         MapNode &n = m_data[i];
563
564         u8 oldlight = n.getLight(bank, nodemgr);
565         u8 newlight = diminish_light(oldlight);
566
567         // Loop through 6 neighbors
568         for(u16 i=0; i<6; i++)
569         {
570                 // Get the position of the neighbor node
571                 v3s16 n2pos = p + dirs[i];
572
573                 u32 n2i = m_area.index(n2pos);
574
575                 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
576                         continue;
577
578                 MapNode &n2 = m_data[n2i];
579
580                 u8 light2 = n2.getLight(bank, nodemgr);
581
582                 /*
583                         If the neighbor is brighter than the current node,
584                         add to list (it will light up this node on its turn)
585                 */
586                 if(light2 > undiminish_light(oldlight))
587                 {
588                         spreadLight(bank, n2pos, nodemgr);
589                 }
590                 /*
591                         If the neighbor is dimmer than how much light this node
592                         would spread on it, add to list
593                 */
594                 if(light2 < newlight)
595                 {
596                         if(nodemgr->get(n2).light_propagates)
597                         {
598                                 n2.setLight(bank, newlight, nodemgr);
599                                 spreadLight(bank, n2pos, nodemgr);
600                         }
601                 }
602         }
603 }
604
605 #if 0
606 /*
607         Lights neighbors of from_nodes, collects all them and then
608         goes on recursively.
609
610         NOTE: This is faster on small areas but will overflow the
611               stack on large areas. Thus it is not used.
612 */
613 void VoxelManipulator::spreadLight(enum LightBank bank,
614                 core::map<v3s16, bool> & from_nodes)
615 {
616         if(from_nodes.size() == 0)
617                 return;
618
619         core::map<v3s16, bool> lighted_nodes;
620         core::map<v3s16, bool>::Iterator j;
621         j = from_nodes.getIterator();
622
623         for(; j.atEnd() == false; j++)
624         {
625                 v3s16 pos = j.getNode()->getKey();
626
627                 spreadLight(bank, pos);
628         }
629 }
630 #endif
631
632 #if 1
633 /*
634         Lights neighbors of from_nodes, collects all them and then
635         goes on recursively.
636 */
637 void VoxelManipulator::spreadLight(enum LightBank bank,
638                 std::set<v3s16> & from_nodes, INodeDefManager *nodemgr)
639 {
640         const v3s16 dirs[6] = {
641                 v3s16(0,0,1), // back
642                 v3s16(0,1,0), // top
643                 v3s16(1,0,0), // right
644                 v3s16(0,0,-1), // front
645                 v3s16(0,-1,0), // bottom
646                 v3s16(-1,0,0), // left
647         };
648
649         if(from_nodes.empty())
650                 return;
651
652         std::set<v3s16> lighted_nodes;
653
654         for(std::set<v3s16>::iterator j = from_nodes.begin();
655                 j != from_nodes.end(); ++j)
656         {
657                 v3s16 pos = *j;
658
659                 VoxelArea voxel_area(pos - v3s16(1,1,1), pos + v3s16(1,1,1));
660                 addArea(voxel_area);
661
662                 u32 i = m_area.index(pos);
663
664                 if(m_flags[i] & VOXELFLAG_NO_DATA)
665                         continue;
666
667                 MapNode &n = m_data[i];
668
669                 u8 oldlight = n.getLight(bank, nodemgr);
670                 u8 newlight = diminish_light(oldlight);
671
672                 // Loop through 6 neighbors
673                 for(u16 i=0; i<6; i++)
674                 {
675                         // Get the position of the neighbor node
676                         v3s16 n2pos = pos + dirs[i];
677
678                         try
679                         {
680                                 u32 n2i = m_area.index(n2pos);
681
682                                 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
683                                         continue;
684
685                                 MapNode &n2 = m_data[n2i];
686
687                                 u8 light2 = n2.getLight(bank, nodemgr);
688
689                                 /*
690                                         If the neighbor is brighter than the current node,
691                                         add to list (it will light up this node on its turn)
692                                 */
693                                 if(light2 > undiminish_light(oldlight))
694                                 {
695                                         lighted_nodes.insert(n2pos);
696                                 }
697                                 /*
698                                         If the neighbor is dimmer than how much light this node
699                                         would spread on it, add to list
700                                 */
701                                 if(light2 < newlight)
702                                 {
703                                         if(nodemgr->get(n2).light_propagates)
704                                         {
705                                                 n2.setLight(bank, newlight, nodemgr);
706                                                 lighted_nodes.insert(n2pos);
707                                         }
708                                 }
709                         }
710                         catch(InvalidPositionException &e)
711                         {
712                                 continue;
713                         }
714                 }
715         }
716
717         /*dstream<<"spreadLight(): Changed block "
718                         <<blockchangecount<<" times"
719                         <<" for "<<from_nodes.size()<<" nodes"
720                         <<std::endl;*/
721
722         if(!lighted_nodes.empty())
723                 spreadLight(bank, lighted_nodes, nodemgr);
724 }
725 #endif
726
727 //END