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 std::set<v3s16> & 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);
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 std::map<v3s16, u8> & from_nodes,
388 std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
390 if(from_nodes.size() == 0)
393 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
394 j != from_nodes.end(); ++j)
396 unspreadLight(bank, j->first, j->second, light_sources, nodemgr);
403 Goes recursively through the neighbours of the node.
405 Alters only transparent nodes.
407 If the lighting of the neighbour is lower than the lighting of
408 the node was (before changing it to 0 at the step before), the
409 lighting of the neighbour is set to 0 and then the same stuff
410 repeats for the neighbour.
412 The ending nodes of the routine are stored in light_sources.
413 This is useful when a light is removed. In such case, this
414 routine can be called for the light node and then again for
415 light_sources to re-light the area without the removed light.
417 values of from_nodes are lighting values.
419 void VoxelManipulator::unspreadLight(enum LightBank bank,
420 core::map<v3s16, u8> & from_nodes,
421 core::map<v3s16, bool> & light_sources)
424 v3s16(0,0,1), // back
426 v3s16(1,0,0), // right
427 v3s16(0,0,-1), // front
428 v3s16(0,-1,0), // bottom
429 v3s16(-1,0,0), // left
432 if(from_nodes.size() == 0)
435 core::map<v3s16, u8> unlighted_nodes;
436 core::map<v3s16, u8>::Iterator j;
437 j = from_nodes.getIterator();
439 for(; j.atEnd() == false; j++)
441 v3s16 pos = j.getNode()->getKey();
443 emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
445 //MapNode &n = m_data[m_area.index(pos)];
447 u8 oldlight = j.getNode()->getValue();
449 // Loop through 6 neighbors
450 for(u16 i=0; i<6; i++)
452 // Get the position of the neighbor node
453 v3s16 n2pos = pos + dirs[i];
455 u32 n2i = m_area.index(n2pos);
457 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
460 MapNode &n2 = m_data[n2i];
463 If the neighbor is dimmer than what was specified
464 as oldlight (the light of the previous node)
466 if(n2.getLight(bank, nodemgr) < oldlight)
469 And the neighbor is transparent and it has some light
471 if(nodemgr->get(n2).light_propagates && n2.getLight(bank, nodemgr) != 0)
474 Set light to 0 and add to queue
477 u8 current_light = n2.getLight(bank, nodemgr);
478 n2.setLight(bank, 0);
480 unlighted_nodes.insert(n2pos, current_light);
483 Remove from light_sources if it is there
484 NOTE: This doesn't happen nearly at all
486 /*if(light_sources.find(n2pos))
488 std::cout<<"Removed from light_sources"<<std::endl;
489 light_sources.remove(n2pos);
494 light_sources.insert(n2pos, true);
499 /*dstream<<"unspreadLight(): Changed block "
500 <<blockchangecount<<" times"
501 <<" for "<<from_nodes.size()<<" nodes"
504 if(unlighted_nodes.size() > 0)
505 unspreadLight(bank, unlighted_nodes, light_sources);
509 void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p,
510 INodeDefManager *nodemgr)
512 const v3s16 dirs[6] = {
513 v3s16(0,0,1), // back
515 v3s16(1,0,0), // right
516 v3s16(0,0,-1), // front
517 v3s16(0,-1,0), // bottom
518 v3s16(-1,0,0), // left
521 emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
523 u32 i = m_area.index(p);
525 if(m_flags[i] & VOXELFLAG_INEXISTENT)
528 MapNode &n = m_data[i];
530 u8 oldlight = n.getLight(bank, nodemgr);
531 u8 newlight = diminish_light(oldlight);
533 // Loop through 6 neighbors
534 for(u16 i=0; i<6; i++)
536 // Get the position of the neighbor node
537 v3s16 n2pos = p + dirs[i];
539 u32 n2i = m_area.index(n2pos);
541 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
544 MapNode &n2 = m_data[n2i];
546 u8 light2 = n2.getLight(bank, nodemgr);
549 If the neighbor is brighter than the current node,
550 add to list (it will light up this node on its turn)
552 if(light2 > undiminish_light(oldlight))
554 spreadLight(bank, n2pos, nodemgr);
557 If the neighbor is dimmer than how much light this node
558 would spread on it, add to list
560 if(light2 < newlight)
562 if(nodemgr->get(n2).light_propagates)
564 n2.setLight(bank, newlight, nodemgr);
565 spreadLight(bank, n2pos, nodemgr);
573 Lights neighbors of from_nodes, collects all them and then
576 NOTE: This is faster on small areas but will overflow the
577 stack on large areas. Thus it is not used.
579 void VoxelManipulator::spreadLight(enum LightBank bank,
580 core::map<v3s16, bool> & from_nodes)
582 if(from_nodes.size() == 0)
585 core::map<v3s16, bool> lighted_nodes;
586 core::map<v3s16, bool>::Iterator j;
587 j = from_nodes.getIterator();
589 for(; j.atEnd() == false; j++)
591 v3s16 pos = j.getNode()->getKey();
593 spreadLight(bank, pos);
600 Lights neighbors of from_nodes, collects all them and then
603 void VoxelManipulator::spreadLight(enum LightBank bank,
604 std::set<v3s16> & from_nodes, INodeDefManager *nodemgr)
606 const v3s16 dirs[6] = {
607 v3s16(0,0,1), // back
609 v3s16(1,0,0), // right
610 v3s16(0,0,-1), // front
611 v3s16(0,-1,0), // bottom
612 v3s16(-1,0,0), // left
615 if(from_nodes.size() == 0)
618 std::set<v3s16> lighted_nodes;
620 for(std::set<v3s16>::iterator j = from_nodes.begin();
621 j != from_nodes.end(); ++j)
625 emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
627 u32 i = m_area.index(pos);
629 if(m_flags[i] & VOXELFLAG_INEXISTENT)
632 MapNode &n = m_data[i];
634 u8 oldlight = n.getLight(bank, nodemgr);
635 u8 newlight = diminish_light(oldlight);
637 // Loop through 6 neighbors
638 for(u16 i=0; i<6; i++)
640 // Get the position of the neighbor node
641 v3s16 n2pos = pos + dirs[i];
645 u32 n2i = m_area.index(n2pos);
647 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
650 MapNode &n2 = m_data[n2i];
652 u8 light2 = n2.getLight(bank, nodemgr);
655 If the neighbor is brighter than the current node,
656 add to list (it will light up this node on its turn)
658 if(light2 > undiminish_light(oldlight))
660 lighted_nodes.insert(n2pos);
663 If the neighbor is dimmer than how much light this node
664 would spread on it, add to list
666 if(light2 < newlight)
668 if(nodemgr->get(n2).light_propagates)
670 n2.setLight(bank, newlight, nodemgr);
671 lighted_nodes.insert(n2pos);
675 catch(InvalidPositionException &e)
682 /*dstream<<"spreadLight(): Changed block "
683 <<blockchangecount<<" times"
684 <<" for "<<from_nodes.size()<<" nodes"
687 if(lighted_nodes.size() > 0)
688 spreadLight(bank, lighted_nodes, nodemgr);