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