3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
24 #include "util/timetaker.h"
25 #include <string.h> // memcpy, memset
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;
40 VoxelManipulator::VoxelManipulator():
46 VoxelManipulator::~VoxelManipulator()
55 void VoxelManipulator::clear()
57 // Reset area to volume=0
67 void VoxelManipulator::print(std::ostream &o, INodeDefManager *ndef,
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;
75 for(s32 y=m_area.MaxEdge.Y; y>=m_area.MinEdge.Y; y--)
77 if(em.X >= 3 && em.Y >= 3)
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-> ";
85 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
87 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
89 u8 f = m_flags[m_area.index(x,y,z)];
91 if(f & VOXELFLAG_NOT_LOADED)
93 else if(f & VOXELFLAG_INEXISTENT)
98 MapNode n = m_data[m_area.index(x,y,z)];
99 content_t m = n.getContent();
101 if(mode == VOXELPRINT_MATERIAL)
106 else if(mode == VOXELPRINT_WATERPRESSURE)
108 if(ndef->get(m).isLiquid())
114 else if(m == CONTENT_AIR)
123 else if(mode == VOXELPRINT_LIGHT_DAY)
125 if(ndef->get(m).light_source != 0)
127 else if(ndef->get(m).light_propagates == false)
131 u8 light = n.getLight(LIGHTBANK_DAY, ndef);
135 c = 'a' + (light-10);
147 void VoxelManipulator::addArea(VoxelArea area)
149 // Cancel if requested area has zero volume
150 if(area.getExtent() == v3s16(0,0,0))
153 // Cancel if m_area already contains the requested area
154 if(m_area.contains(area))
157 TimeTaker timer("addArea", &addarea_time);
159 // Calculate new area
161 // New area is the requested area if m_area has zero volume
162 if(m_area.getExtent() == v3s16(0,0,0))
166 // Else add requested area to m_area
170 new_area.addArea(area);
173 s32 new_size = new_area.getVolume();
175 /*dstream<<"adding area ";
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;*/
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++)
189 new_flags[i] = VOXELFLAG_NOT_LOADED;
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++)
198 // If loaded, copy data and flags
199 if((m_flags[m_area.index(x,y,z)] & VOXELFLAG_NOT_LOADED) == false)
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)];
206 // Replace area, data and flags
210 MapNode *old_data = m_data;
211 u8 *old_flags = m_flags;
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;*/
224 //dstream<<"addArea done"<<std::endl;
227 void VoxelManipulator::copyFrom(MapNode *src, VoxelArea src_area,
228 v3s16 from_pos, v3s16 to_pos, v3s16 size)
230 for(s16 z=0; z<size.Z; z++)
231 for(s16 y=0; y<size.Y; y++)
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);
240 void VoxelManipulator::copyTo(MapNode *dst, VoxelArea dst_area,
241 v3s16 dst_pos, v3s16 from_pos, v3s16 size)
243 for(s16 z=0; z<size.Z; z++)
244 for(s16 y=0; y<size.Y; y++)
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];
254 //memcpy(&dst[i_dst], &m_data[i_local], size.X*sizeof(MapNode));
260 -----------------------------------------------------
263 void VoxelManipulator::clearFlag(u8 flags)
265 // 0-1ms on moderate area
266 TimeTaker timer("clearFlag", &clearflag_time);
268 //v3s16 s = m_area.getExtent();
270 /*dstream<<"clearFlag clearing area of size "
271 <<""<<s.X<<"x"<<s.Y<<"x"<<s.Z<<""
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++)
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)
286 s32 volume = m_area.getVolume();
287 for(s32 i=0; i<volume; i++)
289 m_flags[i] &= ~flags;
292 /*s32 volume = m_area.getVolume();
293 for(s32 i=0; i<volume; i++)
296 m_flags[i] &= ~flags;
301 dstream<<"clearFlag changed "<<count<<" flags out of "
302 <<volume<<" nodes"<<std::endl;*/
305 void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
306 std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
309 v3s16(0,0,1), // back
311 v3s16(1,0,0), // right
312 v3s16(0,0,-1), // front
313 v3s16(0,-1,0), // bottom
314 v3s16(-1,0,0), // left
317 emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
319 // Loop through 6 neighbors
320 for(u16 i=0; i<6; i++)
322 // Get the position of the neighbor node
323 v3s16 n2pos = p + dirs[i];
325 u32 n2i = m_area.index(n2pos);
327 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
330 MapNode &n2 = m_data[n2i];
333 If the neighbor is dimmer than what was specified
334 as oldlight (the light of the previous node)
336 u8 light2 = n2.getLight(bank, nodemgr);
337 if(light2 < oldlight)
340 And the neighbor is transparent and it has some light
342 if(nodemgr->get(n2).light_propagates && light2 != 0)
345 Set light to 0 and add to queue
348 n2.setLight(bank, 0, nodemgr);
350 unspreadLight(bank, n2pos, light2, light_sources, nodemgr);
353 Remove from light_sources if it is there
354 NOTE: This doesn't happen nearly at all
356 /*if(light_sources.find(n2pos))
358 std::cout<<"Removed from light_sources"<<std::endl;
359 light_sources.remove(n2pos);
364 light_sources.insert(n2pos);
371 Goes recursively through the neighbours of the node.
373 Alters only transparent nodes.
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.
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.
385 values of from_nodes are lighting values.
387 void VoxelManipulator::unspreadLight(enum LightBank bank,
388 std::map<v3s16, u8> & from_nodes,
389 std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
391 if(from_nodes.size() == 0)
394 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
395 j != from_nodes.end(); ++j)
397 unspreadLight(bank, j->first, j->second, light_sources, nodemgr);
404 Goes recursively through the neighbours of the node.
406 Alters only transparent nodes.
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.
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.
418 values of from_nodes are lighting values.
420 void VoxelManipulator::unspreadLight(enum LightBank bank,
421 core::map<v3s16, u8> & from_nodes,
422 core::map<v3s16, bool> & light_sources)
425 v3s16(0,0,1), // back
427 v3s16(1,0,0), // right
428 v3s16(0,0,-1), // front
429 v3s16(0,-1,0), // bottom
430 v3s16(-1,0,0), // left
433 if(from_nodes.size() == 0)
436 core::map<v3s16, u8> unlighted_nodes;
437 core::map<v3s16, u8>::Iterator j;
438 j = from_nodes.getIterator();
440 for(; j.atEnd() == false; j++)
442 v3s16 pos = j.getNode()->getKey();
444 emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
446 //MapNode &n = m_data[m_area.index(pos)];
448 u8 oldlight = j.getNode()->getValue();
450 // Loop through 6 neighbors
451 for(u16 i=0; i<6; i++)
453 // Get the position of the neighbor node
454 v3s16 n2pos = pos + dirs[i];
456 u32 n2i = m_area.index(n2pos);
458 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
461 MapNode &n2 = m_data[n2i];
464 If the neighbor is dimmer than what was specified
465 as oldlight (the light of the previous node)
467 if(n2.getLight(bank, nodemgr) < oldlight)
470 And the neighbor is transparent and it has some light
472 if(nodemgr->get(n2).light_propagates && n2.getLight(bank, nodemgr) != 0)
475 Set light to 0 and add to queue
478 u8 current_light = n2.getLight(bank, nodemgr);
479 n2.setLight(bank, 0);
481 unlighted_nodes.insert(n2pos, current_light);
484 Remove from light_sources if it is there
485 NOTE: This doesn't happen nearly at all
487 /*if(light_sources.find(n2pos))
489 std::cout<<"Removed from light_sources"<<std::endl;
490 light_sources.remove(n2pos);
495 light_sources.insert(n2pos, true);
500 /*dstream<<"unspreadLight(): Changed block "
501 <<blockchangecount<<" times"
502 <<" for "<<from_nodes.size()<<" nodes"
505 if(unlighted_nodes.size() > 0)
506 unspreadLight(bank, unlighted_nodes, light_sources);
510 void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p,
511 INodeDefManager *nodemgr)
513 const v3s16 dirs[6] = {
514 v3s16(0,0,1), // back
516 v3s16(1,0,0), // right
517 v3s16(0,0,-1), // front
518 v3s16(0,-1,0), // bottom
519 v3s16(-1,0,0), // left
522 emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
524 u32 i = m_area.index(p);
526 if(m_flags[i] & VOXELFLAG_INEXISTENT)
529 MapNode &n = m_data[i];
531 u8 oldlight = n.getLight(bank, nodemgr);
532 u8 newlight = diminish_light(oldlight);
534 // Loop through 6 neighbors
535 for(u16 i=0; i<6; i++)
537 // Get the position of the neighbor node
538 v3s16 n2pos = p + dirs[i];
540 u32 n2i = m_area.index(n2pos);
542 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
545 MapNode &n2 = m_data[n2i];
547 u8 light2 = n2.getLight(bank, nodemgr);
550 If the neighbor is brighter than the current node,
551 add to list (it will light up this node on its turn)
553 if(light2 > undiminish_light(oldlight))
555 spreadLight(bank, n2pos, nodemgr);
558 If the neighbor is dimmer than how much light this node
559 would spread on it, add to list
561 if(light2 < newlight)
563 if(nodemgr->get(n2).light_propagates)
565 n2.setLight(bank, newlight, nodemgr);
566 spreadLight(bank, n2pos, nodemgr);
574 Lights neighbors of from_nodes, collects all them and then
577 NOTE: This is faster on small areas but will overflow the
578 stack on large areas. Thus it is not used.
580 void VoxelManipulator::spreadLight(enum LightBank bank,
581 core::map<v3s16, bool> & from_nodes)
583 if(from_nodes.size() == 0)
586 core::map<v3s16, bool> lighted_nodes;
587 core::map<v3s16, bool>::Iterator j;
588 j = from_nodes.getIterator();
590 for(; j.atEnd() == false; j++)
592 v3s16 pos = j.getNode()->getKey();
594 spreadLight(bank, pos);
601 Lights neighbors of from_nodes, collects all them and then
604 void VoxelManipulator::spreadLight(enum LightBank bank,
605 std::set<v3s16> & from_nodes, INodeDefManager *nodemgr)
607 const v3s16 dirs[6] = {
608 v3s16(0,0,1), // back
610 v3s16(1,0,0), // right
611 v3s16(0,0,-1), // front
612 v3s16(0,-1,0), // bottom
613 v3s16(-1,0,0), // left
616 if(from_nodes.size() == 0)
619 std::set<v3s16> lighted_nodes;
621 for(std::set<v3s16>::iterator j = from_nodes.begin();
622 j != from_nodes.end(); ++j)
626 emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
628 u32 i = m_area.index(pos);
630 if(m_flags[i] & VOXELFLAG_INEXISTENT)
633 MapNode &n = m_data[i];
635 u8 oldlight = n.getLight(bank, nodemgr);
636 u8 newlight = diminish_light(oldlight);
638 // Loop through 6 neighbors
639 for(u16 i=0; i<6; i++)
641 // Get the position of the neighbor node
642 v3s16 n2pos = pos + dirs[i];
646 u32 n2i = m_area.index(n2pos);
648 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
651 MapNode &n2 = m_data[n2i];
653 u8 light2 = n2.getLight(bank, nodemgr);
656 If the neighbor is brighter than the current node,
657 add to list (it will light up this node on its turn)
659 if(light2 > undiminish_light(oldlight))
661 lighted_nodes.insert(n2pos);
664 If the neighbor is dimmer than how much light this node
665 would spread on it, add to list
667 if(light2 < newlight)
669 if(nodemgr->get(n2).light_propagates)
671 n2.setLight(bank, newlight, nodemgr);
672 lighted_nodes.insert(n2pos);
676 catch(InvalidPositionException &e)
683 /*dstream<<"spreadLight(): Changed block "
684 <<blockchangecount<<" times"
685 <<" for "<<from_nodes.size()<<" nodes"
688 if(lighted_nodes.size() > 0)
689 spreadLight(bank, lighted_nodes, nodemgr);