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];
187 u8 *new_flags = new u8[new_size];
189 memset(new_flags, VOXELFLAG_NOT_LOADED, new_size);
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 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)
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];
207 // Replace area, data and flags
211 MapNode *old_data = m_data;
212 u8 *old_flags = m_flags;
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;*/
225 //dstream<<"addArea done"<<std::endl;
228 void VoxelManipulator::copyFrom(MapNode *src, VoxelArea src_area,
229 v3s16 from_pos, v3s16 to_pos, v3s16 size)
231 for(s16 z=0; z<size.Z; z++)
232 for(s16 y=0; y<size.Y; y++)
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);
241 void VoxelManipulator::copyTo(MapNode *dst, VoxelArea dst_area,
242 v3s16 dst_pos, v3s16 from_pos, v3s16 size)
244 for(s16 z=0; z<size.Z; z++)
245 for(s16 y=0; y<size.Y; y++)
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];
255 //memcpy(&dst[i_dst], &m_data[i_local], size.X*sizeof(MapNode));
261 -----------------------------------------------------
264 void VoxelManipulator::clearFlag(u8 flags)
266 // 0-1ms on moderate area
267 TimeTaker timer("clearFlag", &clearflag_time);
269 //v3s16 s = m_area.getExtent();
271 /*dstream<<"clearFlag clearing area of size "
272 <<""<<s.X<<"x"<<s.Y<<"x"<<s.Z<<""
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++)
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)
287 s32 volume = m_area.getVolume();
288 for(s32 i=0; i<volume; i++)
290 m_flags[i] &= ~flags;
293 /*s32 volume = m_area.getVolume();
294 for(s32 i=0; i<volume; i++)
297 m_flags[i] &= ~flags;
302 dstream<<"clearFlag changed "<<count<<" flags out of "
303 <<volume<<" nodes"<<std::endl;*/
306 void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
307 std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
310 v3s16(0,0,1), // back
312 v3s16(1,0,0), // right
313 v3s16(0,0,-1), // front
314 v3s16(0,-1,0), // bottom
315 v3s16(-1,0,0), // left
318 emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
320 // Loop through 6 neighbors
321 for(u16 i=0; i<6; i++)
323 // Get the position of the neighbor node
324 v3s16 n2pos = p + dirs[i];
326 u32 n2i = m_area.index(n2pos);
328 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
331 MapNode &n2 = m_data[n2i];
334 If the neighbor is dimmer than what was specified
335 as oldlight (the light of the previous node)
337 u8 light2 = n2.getLight(bank, nodemgr);
338 if(light2 < oldlight)
341 And the neighbor is transparent and it has some light
343 if(nodemgr->get(n2).light_propagates && light2 != 0)
346 Set light to 0 and add to queue
349 n2.setLight(bank, 0, nodemgr);
351 unspreadLight(bank, n2pos, light2, light_sources, nodemgr);
354 Remove from light_sources if it is there
355 NOTE: This doesn't happen nearly at all
357 /*if(light_sources.find(n2pos))
359 std::cout<<"Removed from light_sources"<<std::endl;
360 light_sources.remove(n2pos);
365 light_sources.insert(n2pos);
372 Goes recursively through the neighbours of the node.
374 Alters only transparent nodes.
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.
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.
386 values of from_nodes are lighting values.
388 void VoxelManipulator::unspreadLight(enum LightBank bank,
389 std::map<v3s16, u8> & from_nodes,
390 std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
392 if(from_nodes.size() == 0)
395 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
396 j != from_nodes.end(); ++j)
398 unspreadLight(bank, j->first, j->second, light_sources, nodemgr);
405 Goes recursively through the neighbours of the node.
407 Alters only transparent nodes.
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.
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.
419 values of from_nodes are lighting values.
421 void VoxelManipulator::unspreadLight(enum LightBank bank,
422 core::map<v3s16, u8> & from_nodes,
423 core::map<v3s16, bool> & light_sources)
426 v3s16(0,0,1), // back
428 v3s16(1,0,0), // right
429 v3s16(0,0,-1), // front
430 v3s16(0,-1,0), // bottom
431 v3s16(-1,0,0), // left
434 if(from_nodes.size() == 0)
437 core::map<v3s16, u8> unlighted_nodes;
438 core::map<v3s16, u8>::Iterator j;
439 j = from_nodes.getIterator();
441 for(; j.atEnd() == false; j++)
443 v3s16 pos = j.getNode()->getKey();
445 emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
447 //MapNode &n = m_data[m_area.index(pos)];
449 u8 oldlight = j.getNode()->getValue();
451 // Loop through 6 neighbors
452 for(u16 i=0; i<6; i++)
454 // Get the position of the neighbor node
455 v3s16 n2pos = pos + dirs[i];
457 u32 n2i = m_area.index(n2pos);
459 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
462 MapNode &n2 = m_data[n2i];
465 If the neighbor is dimmer than what was specified
466 as oldlight (the light of the previous node)
468 if(n2.getLight(bank, nodemgr) < oldlight)
471 And the neighbor is transparent and it has some light
473 if(nodemgr->get(n2).light_propagates && n2.getLight(bank, nodemgr) != 0)
476 Set light to 0 and add to queue
479 u8 current_light = n2.getLight(bank, nodemgr);
480 n2.setLight(bank, 0);
482 unlighted_nodes.insert(n2pos, current_light);
485 Remove from light_sources if it is there
486 NOTE: This doesn't happen nearly at all
488 /*if(light_sources.find(n2pos))
490 std::cout<<"Removed from light_sources"<<std::endl;
491 light_sources.remove(n2pos);
496 light_sources.insert(n2pos, true);
501 /*dstream<<"unspreadLight(): Changed block "
502 <<blockchangecount<<" times"
503 <<" for "<<from_nodes.size()<<" nodes"
506 if(unlighted_nodes.size() > 0)
507 unspreadLight(bank, unlighted_nodes, light_sources);
511 void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p,
512 INodeDefManager *nodemgr)
514 const v3s16 dirs[6] = {
515 v3s16(0,0,1), // back
517 v3s16(1,0,0), // right
518 v3s16(0,0,-1), // front
519 v3s16(0,-1,0), // bottom
520 v3s16(-1,0,0), // left
523 emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
525 u32 i = m_area.index(p);
527 if(m_flags[i] & VOXELFLAG_INEXISTENT)
530 MapNode &n = m_data[i];
532 u8 oldlight = n.getLight(bank, nodemgr);
533 u8 newlight = diminish_light(oldlight);
535 // Loop through 6 neighbors
536 for(u16 i=0; i<6; i++)
538 // Get the position of the neighbor node
539 v3s16 n2pos = p + dirs[i];
541 u32 n2i = m_area.index(n2pos);
543 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
546 MapNode &n2 = m_data[n2i];
548 u8 light2 = n2.getLight(bank, nodemgr);
551 If the neighbor is brighter than the current node,
552 add to list (it will light up this node on its turn)
554 if(light2 > undiminish_light(oldlight))
556 spreadLight(bank, n2pos, nodemgr);
559 If the neighbor is dimmer than how much light this node
560 would spread on it, add to list
562 if(light2 < newlight)
564 if(nodemgr->get(n2).light_propagates)
566 n2.setLight(bank, newlight, nodemgr);
567 spreadLight(bank, n2pos, nodemgr);
575 Lights neighbors of from_nodes, collects all them and then
578 NOTE: This is faster on small areas but will overflow the
579 stack on large areas. Thus it is not used.
581 void VoxelManipulator::spreadLight(enum LightBank bank,
582 core::map<v3s16, bool> & from_nodes)
584 if(from_nodes.size() == 0)
587 core::map<v3s16, bool> lighted_nodes;
588 core::map<v3s16, bool>::Iterator j;
589 j = from_nodes.getIterator();
591 for(; j.atEnd() == false; j++)
593 v3s16 pos = j.getNode()->getKey();
595 spreadLight(bank, pos);
602 Lights neighbors of from_nodes, collects all them and then
605 void VoxelManipulator::spreadLight(enum LightBank bank,
606 std::set<v3s16> & from_nodes, INodeDefManager *nodemgr)
608 const v3s16 dirs[6] = {
609 v3s16(0,0,1), // back
611 v3s16(1,0,0), // right
612 v3s16(0,0,-1), // front
613 v3s16(0,-1,0), // bottom
614 v3s16(-1,0,0), // left
617 if(from_nodes.size() == 0)
620 std::set<v3s16> lighted_nodes;
622 for(std::set<v3s16>::iterator j = from_nodes.begin();
623 j != from_nodes.end(); ++j)
627 emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
629 u32 i = m_area.index(pos);
631 if(m_flags[i] & VOXELFLAG_INEXISTENT)
634 MapNode &n = m_data[i];
636 u8 oldlight = n.getLight(bank, nodemgr);
637 u8 newlight = diminish_light(oldlight);
639 // Loop through 6 neighbors
640 for(u16 i=0; i<6; i++)
642 // Get the position of the neighbor node
643 v3s16 n2pos = pos + dirs[i];
647 u32 n2i = m_area.index(n2pos);
649 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
652 MapNode &n2 = m_data[n2i];
654 u8 light2 = n2.getLight(bank, nodemgr);
657 If the neighbor is brighter than the current node,
658 add to list (it will light up this node on its turn)
660 if(light2 > undiminish_light(oldlight))
662 lighted_nodes.insert(n2pos);
665 If the neighbor is dimmer than how much light this node
666 would spread on it, add to list
668 if(light2 < newlight)
670 if(nodemgr->get(n2).light_propagates)
672 n2.setLight(bank, newlight, nodemgr);
673 lighted_nodes.insert(n2pos);
677 catch(InvalidPositionException &e)
684 /*dstream<<"spreadLight(): Changed block "
685 <<blockchangecount<<" times"
686 <<" for "<<from_nodes.size()<<" nodes"
689 if(lighted_nodes.size() > 0)
690 spreadLight(bank, lighted_nodes, nodemgr);