3 Copyright (C) 2010 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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
15 You should have received a copy of the GNU 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.
22 #include "utility.h" // For TimeTaker
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 memcpy(&dst[i_dst], &m_data[i_local], size.X*sizeof(MapNode));
253 -----------------------------------------------------
256 void VoxelManipulator::clearFlag(u8 flags)
258 // 0-1ms on moderate area
259 TimeTaker timer("clearFlag", &clearflag_time);
261 v3s16 s = m_area.getExtent();
263 /*dstream<<"clearFlag clearing area of size "
264 <<""<<s.X<<"x"<<s.Y<<"x"<<s.Z<<""
269 /*for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
270 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
271 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
273 u8 f = m_flags[m_area.index(x,y,z)];
274 m_flags[m_area.index(x,y,z)] &= ~flags;
275 if(m_flags[m_area.index(x,y,z)] != f)
279 s32 volume = m_area.getVolume();
280 for(s32 i=0; i<volume; i++)
282 m_flags[i] &= ~flags;
285 /*s32 volume = m_area.getVolume();
286 for(s32 i=0; i<volume; i++)
289 m_flags[i] &= ~flags;
294 dstream<<"clearFlag changed "<<count<<" flags out of "
295 <<volume<<" nodes"<<std::endl;*/
298 void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
299 core::map<v3s16, bool> & light_sources, INodeDefManager *nodemgr)
302 v3s16(0,0,1), // back
304 v3s16(1,0,0), // right
305 v3s16(0,0,-1), // front
306 v3s16(0,-1,0), // bottom
307 v3s16(-1,0,0), // left
310 emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
312 // Loop through 6 neighbors
313 for(u16 i=0; i<6; i++)
315 // Get the position of the neighbor node
316 v3s16 n2pos = p + dirs[i];
318 u32 n2i = m_area.index(n2pos);
320 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
323 MapNode &n2 = m_data[n2i];
326 If the neighbor is dimmer than what was specified
327 as oldlight (the light of the previous node)
329 u8 light2 = n2.getLight(bank, nodemgr);
330 if(light2 < oldlight)
333 And the neighbor is transparent and it has some light
335 if(nodemgr->get(n2).light_propagates && light2 != 0)
338 Set light to 0 and add to queue
341 n2.setLight(bank, 0, nodemgr);
343 unspreadLight(bank, n2pos, light2, light_sources, nodemgr);
346 Remove from light_sources if it is there
347 NOTE: This doesn't happen nearly at all
349 /*if(light_sources.find(n2pos))
351 std::cout<<"Removed from light_sources"<<std::endl;
352 light_sources.remove(n2pos);
357 light_sources.insert(n2pos, true);
364 Goes recursively through the neighbours of the node.
366 Alters only transparent nodes.
368 If the lighting of the neighbour is lower than the lighting of
369 the node was (before changing it to 0 at the step before), the
370 lighting of the neighbour is set to 0 and then the same stuff
371 repeats for the neighbour.
373 The ending nodes of the routine are stored in light_sources.
374 This is useful when a light is removed. In such case, this
375 routine can be called for the light node and then again for
376 light_sources to re-light the area without the removed light.
378 values of from_nodes are lighting values.
380 void VoxelManipulator::unspreadLight(enum LightBank bank,
381 core::map<v3s16, u8> & from_nodes,
382 core::map<v3s16, bool> & light_sources, INodeDefManager *nodemgr)
384 if(from_nodes.size() == 0)
387 core::map<v3s16, u8>::Iterator j;
388 j = from_nodes.getIterator();
390 for(; j.atEnd() == false; j++)
392 v3s16 pos = j.getNode()->getKey();
394 //MapNode &n = m_data[m_area.index(pos)];
396 u8 oldlight = j.getNode()->getValue();
398 unspreadLight(bank, pos, oldlight, 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 core::map<v3s16, bool> & 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 core::map<v3s16, bool> lighted_nodes;
621 core::map<v3s16, bool>::Iterator j;
622 j = from_nodes.getIterator();
624 for(; j.atEnd() == false; j++)
626 v3s16 pos = j.getNode()->getKey();
628 emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
630 u32 i = m_area.index(pos);
632 if(m_flags[i] & VOXELFLAG_INEXISTENT)
635 MapNode &n = m_data[i];
637 u8 oldlight = n.getLight(bank, nodemgr);
638 u8 newlight = diminish_light(oldlight);
640 // Loop through 6 neighbors
641 for(u16 i=0; i<6; i++)
643 // Get the position of the neighbor node
644 v3s16 n2pos = pos + dirs[i];
648 u32 n2i = m_area.index(n2pos);
650 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
653 MapNode &n2 = m_data[n2i];
655 u8 light2 = n2.getLight(bank, nodemgr);
658 If the neighbor is brighter than the current node,
659 add to list (it will light up this node on its turn)
661 if(light2 > undiminish_light(oldlight))
663 lighted_nodes.insert(n2pos, true);
666 If the neighbor is dimmer than how much light this node
667 would spread on it, add to list
669 if(light2 < newlight)
671 if(nodemgr->get(n2).light_propagates)
673 n2.setLight(bank, newlight, nodemgr);
674 lighted_nodes.insert(n2pos, true);
678 catch(InvalidPositionException &e)
685 /*dstream<<"spreadLight(): Changed block "
686 <<blockchangecount<<" times"
687 <<" for "<<from_nodes.size()<<" nodes"
690 if(lighted_nodes.size() > 0)
691 spreadLight(bank, lighted_nodes, nodemgr);