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