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