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"
31 u32 emerge_load_time = 0;
32 u32 clearflag_time = 0;
33 //u32 getwaterpressure_time = 0;
34 //u32 spreadwaterpressure_time = 0;
35 u32 updateareawaterpressure_time = 0;
36 u32 flowwater_pre_time = 0;
39 VoxelManipulator::VoxelManipulator():
45 VoxelManipulator::~VoxelManipulator()
54 void VoxelManipulator::clear()
56 // Reset area to volume=0
66 void VoxelManipulator::print(std::ostream &o, INodeDefManager *ndef,
69 v3s16 em = m_area.getExtent();
70 v3s16 of = m_area.MinEdge;
71 o<<"size: "<<em.X<<"x"<<em.Y<<"x"<<em.Z
72 <<" offset: ("<<of.X<<","<<of.Y<<","<<of.Z<<")"<<std::endl;
74 for(s32 y=m_area.MaxEdge.Y; y>=m_area.MinEdge.Y; y--)
76 if(em.X >= 3 && em.Y >= 3)
78 if (y==m_area.MinEdge.Y+2) o<<"^ ";
79 else if(y==m_area.MinEdge.Y+1) o<<"| ";
80 else if(y==m_area.MinEdge.Y+0) o<<"y x-> ";
84 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
86 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
88 u8 f = m_flags[m_area.index(x,y,z)];
90 if(f & VOXELFLAG_NOT_LOADED)
92 else if(f & VOXELFLAG_INEXISTENT)
97 MapNode n = m_data[m_area.index(x,y,z)];
98 content_t m = n.getContent();
100 if(mode == VOXELPRINT_MATERIAL)
105 else if(mode == VOXELPRINT_WATERPRESSURE)
107 if(ndef->get(m).isLiquid())
113 else if(m == CONTENT_AIR)
122 else if(mode == VOXELPRINT_LIGHT_DAY)
124 if(ndef->get(m).light_source != 0)
126 else if(ndef->get(m).light_propagates == false)
130 u8 light = n.getLight(LIGHTBANK_DAY, ndef);
134 c = 'a' + (light-10);
146 void VoxelManipulator::addArea(VoxelArea area)
148 // Cancel if requested area has zero volume
149 if(area.getExtent() == v3s16(0,0,0))
152 // Cancel if m_area already contains the requested area
153 if(m_area.contains(area))
156 TimeTaker timer("addArea", &addarea_time);
158 // Calculate new area
160 // New area is the requested area if m_area has zero volume
161 if(m_area.getExtent() == v3s16(0,0,0))
165 // Else add requested area to m_area
169 new_area.addArea(area);
172 s32 new_size = new_area.getVolume();
174 /*dstream<<"adding area ";
176 dstream<<", old area ";
177 m_area.print(dstream);
178 dstream<<", new area ";
179 new_area.print(dstream);
180 dstream<<", new_size="<<new_size;
181 dstream<<std::endl;*/
183 // Allocate and clear new data
184 MapNode *new_data = new MapNode[new_size];
185 u8 *new_flags = new u8[new_size];
186 for(s32 i=0; i<new_size; i++)
188 new_flags[i] = VOXELFLAG_NOT_LOADED;
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++)
197 // If loaded, copy data and flags
198 if((m_flags[m_area.index(x,y,z)] & VOXELFLAG_NOT_LOADED) == false)
200 new_data[new_area.index(x,y,z)] = m_data[m_area.index(x,y,z)];
201 new_flags[new_area.index(x,y,z)] = m_flags[m_area.index(x,y,z)];
205 // Replace area, data and flags
209 MapNode *old_data = m_data;
210 u8 *old_flags = m_flags;
212 /*dstream<<"old_data="<<(int)old_data<<", new_data="<<(int)new_data
213 <<", old_flags="<<(int)m_flags<<", new_flags="<<(int)new_flags<<std::endl;*/
223 //dstream<<"addArea done"<<std::endl;
226 void VoxelManipulator::copyFrom(MapNode *src, VoxelArea src_area,
227 v3s16 from_pos, v3s16 to_pos, v3s16 size)
229 for(s16 z=0; z<size.Z; z++)
230 for(s16 y=0; y<size.Y; y++)
232 s32 i_src = src_area.index(from_pos.X, from_pos.Y+y, from_pos.Z+z);
233 s32 i_local = m_area.index(to_pos.X, to_pos.Y+y, to_pos.Z+z);
234 memcpy(&m_data[i_local], &src[i_src], size.X*sizeof(MapNode));
235 memset(&m_flags[i_local], 0, size.X);
239 void VoxelManipulator::copyTo(MapNode *dst, VoxelArea dst_area,
240 v3s16 dst_pos, v3s16 from_pos, v3s16 size)
242 for(s16 z=0; z<size.Z; z++)
243 for(s16 y=0; y<size.Y; y++)
245 s32 i_dst = dst_area.index(dst_pos.X, dst_pos.Y+y, dst_pos.Z+z);
246 s32 i_local = m_area.index(from_pos.X, from_pos.Y+y, from_pos.Z+z);
247 for (s16 x = 0; x < size.X; x++) {
248 if (m_data[i_local].getContent() != CONTENT_IGNORE)
249 dst[i_dst] = m_data[i_local];
253 //memcpy(&dst[i_dst], &m_data[i_local], size.X*sizeof(MapNode));
259 -----------------------------------------------------
262 void VoxelManipulator::clearFlag(u8 flags)
264 // 0-1ms on moderate area
265 TimeTaker timer("clearFlag", &clearflag_time);
267 v3s16 s = m_area.getExtent();
269 /*dstream<<"clearFlag clearing area of size "
270 <<""<<s.X<<"x"<<s.Y<<"x"<<s.Z<<""
275 /*for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
276 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
277 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
279 u8 f = m_flags[m_area.index(x,y,z)];
280 m_flags[m_area.index(x,y,z)] &= ~flags;
281 if(m_flags[m_area.index(x,y,z)] != f)
285 s32 volume = m_area.getVolume();
286 for(s32 i=0; i<volume; i++)
288 m_flags[i] &= ~flags;
291 /*s32 volume = m_area.getVolume();
292 for(s32 i=0; i<volume; i++)
295 m_flags[i] &= ~flags;
300 dstream<<"clearFlag changed "<<count<<" flags out of "
301 <<volume<<" nodes"<<std::endl;*/
304 void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
305 core::map<v3s16, bool> & light_sources, INodeDefManager *nodemgr)
308 v3s16(0,0,1), // back
310 v3s16(1,0,0), // right
311 v3s16(0,0,-1), // front
312 v3s16(0,-1,0), // bottom
313 v3s16(-1,0,0), // left
316 emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
318 // Loop through 6 neighbors
319 for(u16 i=0; i<6; i++)
321 // Get the position of the neighbor node
322 v3s16 n2pos = p + dirs[i];
324 u32 n2i = m_area.index(n2pos);
326 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
329 MapNode &n2 = m_data[n2i];
332 If the neighbor is dimmer than what was specified
333 as oldlight (the light of the previous node)
335 u8 light2 = n2.getLight(bank, nodemgr);
336 if(light2 < oldlight)
339 And the neighbor is transparent and it has some light
341 if(nodemgr->get(n2).light_propagates && light2 != 0)
344 Set light to 0 and add to queue
347 n2.setLight(bank, 0, nodemgr);
349 unspreadLight(bank, n2pos, light2, light_sources, nodemgr);
352 Remove from light_sources if it is there
353 NOTE: This doesn't happen nearly at all
355 /*if(light_sources.find(n2pos))
357 std::cout<<"Removed from light_sources"<<std::endl;
358 light_sources.remove(n2pos);
363 light_sources.insert(n2pos, true);
370 Goes recursively through the neighbours of the node.
372 Alters only transparent nodes.
374 If the lighting of the neighbour is lower than the lighting of
375 the node was (before changing it to 0 at the step before), the
376 lighting of the neighbour is set to 0 and then the same stuff
377 repeats for the neighbour.
379 The ending nodes of the routine are stored in light_sources.
380 This is useful when a light is removed. In such case, this
381 routine can be called for the light node and then again for
382 light_sources to re-light the area without the removed light.
384 values of from_nodes are lighting values.
386 void VoxelManipulator::unspreadLight(enum LightBank bank,
387 core::map<v3s16, u8> & from_nodes,
388 core::map<v3s16, bool> & light_sources, INodeDefManager *nodemgr)
390 if(from_nodes.size() == 0)
393 core::map<v3s16, u8>::Iterator j;
394 j = from_nodes.getIterator();
396 for(; j.atEnd() == false; j++)
398 v3s16 pos = j.getNode()->getKey();
400 //MapNode &n = m_data[m_area.index(pos)];
402 u8 oldlight = j.getNode()->getValue();
404 unspreadLight(bank, pos, oldlight, light_sources, nodemgr);
411 Goes recursively through the neighbours of the node.
413 Alters only transparent nodes.
415 If the lighting of the neighbour is lower than the lighting of
416 the node was (before changing it to 0 at the step before), the
417 lighting of the neighbour is set to 0 and then the same stuff
418 repeats for the neighbour.
420 The ending nodes of the routine are stored in light_sources.
421 This is useful when a light is removed. In such case, this
422 routine can be called for the light node and then again for
423 light_sources to re-light the area without the removed light.
425 values of from_nodes are lighting values.
427 void VoxelManipulator::unspreadLight(enum LightBank bank,
428 core::map<v3s16, u8> & from_nodes,
429 core::map<v3s16, bool> & light_sources)
432 v3s16(0,0,1), // back
434 v3s16(1,0,0), // right
435 v3s16(0,0,-1), // front
436 v3s16(0,-1,0), // bottom
437 v3s16(-1,0,0), // left
440 if(from_nodes.size() == 0)
443 core::map<v3s16, u8> unlighted_nodes;
444 core::map<v3s16, u8>::Iterator j;
445 j = from_nodes.getIterator();
447 for(; j.atEnd() == false; j++)
449 v3s16 pos = j.getNode()->getKey();
451 emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
453 //MapNode &n = m_data[m_area.index(pos)];
455 u8 oldlight = j.getNode()->getValue();
457 // Loop through 6 neighbors
458 for(u16 i=0; i<6; i++)
460 // Get the position of the neighbor node
461 v3s16 n2pos = pos + dirs[i];
463 u32 n2i = m_area.index(n2pos);
465 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
468 MapNode &n2 = m_data[n2i];
471 If the neighbor is dimmer than what was specified
472 as oldlight (the light of the previous node)
474 if(n2.getLight(bank, nodemgr) < oldlight)
477 And the neighbor is transparent and it has some light
479 if(nodemgr->get(n2).light_propagates && n2.getLight(bank, nodemgr) != 0)
482 Set light to 0 and add to queue
485 u8 current_light = n2.getLight(bank, nodemgr);
486 n2.setLight(bank, 0);
488 unlighted_nodes.insert(n2pos, current_light);
491 Remove from light_sources if it is there
492 NOTE: This doesn't happen nearly at all
494 /*if(light_sources.find(n2pos))
496 std::cout<<"Removed from light_sources"<<std::endl;
497 light_sources.remove(n2pos);
502 light_sources.insert(n2pos, true);
507 /*dstream<<"unspreadLight(): Changed block "
508 <<blockchangecount<<" times"
509 <<" for "<<from_nodes.size()<<" nodes"
512 if(unlighted_nodes.size() > 0)
513 unspreadLight(bank, unlighted_nodes, light_sources);
517 void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p,
518 INodeDefManager *nodemgr)
520 const v3s16 dirs[6] = {
521 v3s16(0,0,1), // back
523 v3s16(1,0,0), // right
524 v3s16(0,0,-1), // front
525 v3s16(0,-1,0), // bottom
526 v3s16(-1,0,0), // left
529 emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
531 u32 i = m_area.index(p);
533 if(m_flags[i] & VOXELFLAG_INEXISTENT)
536 MapNode &n = m_data[i];
538 u8 oldlight = n.getLight(bank, nodemgr);
539 u8 newlight = diminish_light(oldlight);
541 // Loop through 6 neighbors
542 for(u16 i=0; i<6; i++)
544 // Get the position of the neighbor node
545 v3s16 n2pos = p + dirs[i];
547 u32 n2i = m_area.index(n2pos);
549 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
552 MapNode &n2 = m_data[n2i];
554 u8 light2 = n2.getLight(bank, nodemgr);
557 If the neighbor is brighter than the current node,
558 add to list (it will light up this node on its turn)
560 if(light2 > undiminish_light(oldlight))
562 spreadLight(bank, n2pos, nodemgr);
565 If the neighbor is dimmer than how much light this node
566 would spread on it, add to list
568 if(light2 < newlight)
570 if(nodemgr->get(n2).light_propagates)
572 n2.setLight(bank, newlight, nodemgr);
573 spreadLight(bank, n2pos, nodemgr);
581 Lights neighbors of from_nodes, collects all them and then
584 NOTE: This is faster on small areas but will overflow the
585 stack on large areas. Thus it is not used.
587 void VoxelManipulator::spreadLight(enum LightBank bank,
588 core::map<v3s16, bool> & from_nodes)
590 if(from_nodes.size() == 0)
593 core::map<v3s16, bool> lighted_nodes;
594 core::map<v3s16, bool>::Iterator j;
595 j = from_nodes.getIterator();
597 for(; j.atEnd() == false; j++)
599 v3s16 pos = j.getNode()->getKey();
601 spreadLight(bank, pos);
608 Lights neighbors of from_nodes, collects all them and then
611 void VoxelManipulator::spreadLight(enum LightBank bank,
612 core::map<v3s16, bool> & from_nodes, INodeDefManager *nodemgr)
614 const v3s16 dirs[6] = {
615 v3s16(0,0,1), // back
617 v3s16(1,0,0), // right
618 v3s16(0,0,-1), // front
619 v3s16(0,-1,0), // bottom
620 v3s16(-1,0,0), // left
623 if(from_nodes.size() == 0)
626 core::map<v3s16, bool> lighted_nodes;
627 core::map<v3s16, bool>::Iterator j;
628 j = from_nodes.getIterator();
630 for(; j.atEnd() == false; j++)
632 v3s16 pos = j.getNode()->getKey();
634 emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
636 u32 i = m_area.index(pos);
638 if(m_flags[i] & VOXELFLAG_INEXISTENT)
641 MapNode &n = m_data[i];
643 u8 oldlight = n.getLight(bank, nodemgr);
644 u8 newlight = diminish_light(oldlight);
646 // Loop through 6 neighbors
647 for(u16 i=0; i<6; i++)
649 // Get the position of the neighbor node
650 v3s16 n2pos = pos + dirs[i];
654 u32 n2i = m_area.index(n2pos);
656 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
659 MapNode &n2 = m_data[n2i];
661 u8 light2 = n2.getLight(bank, nodemgr);
664 If the neighbor is brighter than the current node,
665 add to list (it will light up this node on its turn)
667 if(light2 > undiminish_light(oldlight))
669 lighted_nodes.insert(n2pos, true);
672 If the neighbor is dimmer than how much light this node
673 would spread on it, add to list
675 if(light2 < newlight)
677 if(nodemgr->get(n2).light_propagates)
679 n2.setLight(bank, newlight, nodemgr);
680 lighted_nodes.insert(n2pos, true);
684 catch(InvalidPositionException &e)
691 /*dstream<<"spreadLight(): Changed block "
692 <<blockchangecount<<" times"
693 <<" for "<<from_nodes.size()<<" nodes"
696 if(lighted_nodes.size() > 0)
697 spreadLight(bank, lighted_nodes, nodemgr);