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 *nodemgr,
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 content_t m = m_data[m_area.index(x,y,z)].getContent();
98 u8 pr = m_data[m_area.index(x,y,z)].param2;
99 if(mode == VOXELPRINT_MATERIAL)
104 else if(mode == VOXELPRINT_WATERPRESSURE)
106 if(nodemgr->get(m).isLiquid())
112 else if(m == CONTENT_AIR)
130 void VoxelManipulator::addArea(VoxelArea area)
132 // Cancel if requested area has zero volume
133 if(area.getExtent() == v3s16(0,0,0))
136 // Cancel if m_area already contains the requested area
137 if(m_area.contains(area))
140 TimeTaker timer("addArea", &addarea_time);
142 // Calculate new area
144 // New area is the requested area if m_area has zero volume
145 if(m_area.getExtent() == v3s16(0,0,0))
149 // Else add requested area to m_area
153 new_area.addArea(area);
156 s32 new_size = new_area.getVolume();
158 /*dstream<<"adding area ";
160 dstream<<", old area ";
161 m_area.print(dstream);
162 dstream<<", new area ";
163 new_area.print(dstream);
164 dstream<<", new_size="<<new_size;
165 dstream<<std::endl;*/
167 // Allocate and clear new data
168 MapNode *new_data = new MapNode[new_size];
169 u8 *new_flags = new u8[new_size];
170 for(s32 i=0; i<new_size; i++)
172 new_flags[i] = VOXELFLAG_NOT_LOADED;
177 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
178 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
179 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
181 // If loaded, copy data and flags
182 if((m_flags[m_area.index(x,y,z)] & VOXELFLAG_NOT_LOADED) == false)
184 new_data[new_area.index(x,y,z)] = m_data[m_area.index(x,y,z)];
185 new_flags[new_area.index(x,y,z)] = m_flags[m_area.index(x,y,z)];
189 // Replace area, data and flags
193 MapNode *old_data = m_data;
194 u8 *old_flags = m_flags;
196 /*dstream<<"old_data="<<(int)old_data<<", new_data="<<(int)new_data
197 <<", old_flags="<<(int)m_flags<<", new_flags="<<(int)new_flags<<std::endl;*/
207 //dstream<<"addArea done"<<std::endl;
210 void VoxelManipulator::copyFrom(MapNode *src, VoxelArea src_area,
211 v3s16 from_pos, v3s16 to_pos, v3s16 size)
213 for(s16 z=0; z<size.Z; z++)
214 for(s16 y=0; y<size.Y; y++)
216 s32 i_src = src_area.index(from_pos.X, from_pos.Y+y, from_pos.Z+z);
217 s32 i_local = m_area.index(to_pos.X, to_pos.Y+y, to_pos.Z+z);
218 memcpy(&m_data[i_local], &src[i_src], size.X*sizeof(MapNode));
219 memset(&m_flags[i_local], 0, size.X);
223 void VoxelManipulator::copyTo(MapNode *dst, VoxelArea dst_area,
224 v3s16 dst_pos, v3s16 from_pos, v3s16 size)
226 for(s16 z=0; z<size.Z; z++)
227 for(s16 y=0; y<size.Y; y++)
229 s32 i_dst = dst_area.index(dst_pos.X, dst_pos.Y+y, dst_pos.Z+z);
230 s32 i_local = m_area.index(from_pos.X, from_pos.Y+y, from_pos.Z+z);
231 memcpy(&dst[i_dst], &m_data[i_local], size.X*sizeof(MapNode));
237 -----------------------------------------------------
240 void VoxelManipulator::clearFlag(u8 flags)
242 // 0-1ms on moderate area
243 TimeTaker timer("clearFlag", &clearflag_time);
245 v3s16 s = m_area.getExtent();
247 /*dstream<<"clearFlag clearing area of size "
248 <<""<<s.X<<"x"<<s.Y<<"x"<<s.Z<<""
253 /*for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
254 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
255 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
257 u8 f = m_flags[m_area.index(x,y,z)];
258 m_flags[m_area.index(x,y,z)] &= ~flags;
259 if(m_flags[m_area.index(x,y,z)] != f)
263 s32 volume = m_area.getVolume();
264 for(s32 i=0; i<volume; i++)
266 m_flags[i] &= ~flags;
269 /*s32 volume = m_area.getVolume();
270 for(s32 i=0; i<volume; i++)
273 m_flags[i] &= ~flags;
278 dstream<<"clearFlag changed "<<count<<" flags out of "
279 <<volume<<" nodes"<<std::endl;*/
282 void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
283 core::map<v3s16, bool> & light_sources, INodeDefManager *nodemgr)
286 v3s16(0,0,1), // back
288 v3s16(1,0,0), // right
289 v3s16(0,0,-1), // front
290 v3s16(0,-1,0), // bottom
291 v3s16(-1,0,0), // left
294 emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
296 // Loop through 6 neighbors
297 for(u16 i=0; i<6; i++)
299 // Get the position of the neighbor node
300 v3s16 n2pos = p + dirs[i];
302 u32 n2i = m_area.index(n2pos);
304 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
307 MapNode &n2 = m_data[n2i];
310 If the neighbor is dimmer than what was specified
311 as oldlight (the light of the previous node)
313 if(n2.getLight(bank, nodemgr) < oldlight)
316 And the neighbor is transparent and it has some light
318 if(nodemgr->get(n2).light_propagates && n2.getLight(bank, nodemgr) != 0)
321 Set light to 0 and add to queue
324 u8 current_light = n2.getLight(bank, nodemgr);
325 n2.setLight(bank, 0, nodemgr);
327 unspreadLight(bank, n2pos, current_light, light_sources, nodemgr);
330 Remove from light_sources if it is there
331 NOTE: This doesn't happen nearly at all
333 /*if(light_sources.find(n2pos))
335 std::cout<<"Removed from light_sources"<<std::endl;
336 light_sources.remove(n2pos);
341 light_sources.insert(n2pos, true);
348 Goes recursively through the neighbours of the node.
350 Alters only transparent nodes.
352 If the lighting of the neighbour is lower than the lighting of
353 the node was (before changing it to 0 at the step before), the
354 lighting of the neighbour is set to 0 and then the same stuff
355 repeats for the neighbour.
357 The ending nodes of the routine are stored in light_sources.
358 This is useful when a light is removed. In such case, this
359 routine can be called for the light node and then again for
360 light_sources to re-light the area without the removed light.
362 values of from_nodes are lighting values.
364 void VoxelManipulator::unspreadLight(enum LightBank bank,
365 core::map<v3s16, u8> & from_nodes,
366 core::map<v3s16, bool> & light_sources, INodeDefManager *nodemgr)
368 if(from_nodes.size() == 0)
371 core::map<v3s16, u8>::Iterator j;
372 j = from_nodes.getIterator();
374 for(; j.atEnd() == false; j++)
376 v3s16 pos = j.getNode()->getKey();
378 //MapNode &n = m_data[m_area.index(pos)];
380 u8 oldlight = j.getNode()->getValue();
382 unspreadLight(bank, pos, oldlight, light_sources, nodemgr);
389 Goes recursively through the neighbours of the node.
391 Alters only transparent nodes.
393 If the lighting of the neighbour is lower than the lighting of
394 the node was (before changing it to 0 at the step before), the
395 lighting of the neighbour is set to 0 and then the same stuff
396 repeats for the neighbour.
398 The ending nodes of the routine are stored in light_sources.
399 This is useful when a light is removed. In such case, this
400 routine can be called for the light node and then again for
401 light_sources to re-light the area without the removed light.
403 values of from_nodes are lighting values.
405 void VoxelManipulator::unspreadLight(enum LightBank bank,
406 core::map<v3s16, u8> & from_nodes,
407 core::map<v3s16, bool> & light_sources)
410 v3s16(0,0,1), // back
412 v3s16(1,0,0), // right
413 v3s16(0,0,-1), // front
414 v3s16(0,-1,0), // bottom
415 v3s16(-1,0,0), // left
418 if(from_nodes.size() == 0)
421 core::map<v3s16, u8> unlighted_nodes;
422 core::map<v3s16, u8>::Iterator j;
423 j = from_nodes.getIterator();
425 for(; j.atEnd() == false; j++)
427 v3s16 pos = j.getNode()->getKey();
429 emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
431 //MapNode &n = m_data[m_area.index(pos)];
433 u8 oldlight = j.getNode()->getValue();
435 // Loop through 6 neighbors
436 for(u16 i=0; i<6; i++)
438 // Get the position of the neighbor node
439 v3s16 n2pos = pos + dirs[i];
441 u32 n2i = m_area.index(n2pos);
443 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
446 MapNode &n2 = m_data[n2i];
449 If the neighbor is dimmer than what was specified
450 as oldlight (the light of the previous node)
452 if(n2.getLight(bank, nodemgr) < oldlight)
455 And the neighbor is transparent and it has some light
457 if(nodemgr->get(n2).light_propagates && n2.getLight(bank, nodemgr) != 0)
460 Set light to 0 and add to queue
463 u8 current_light = n2.getLight(bank, nodemgr);
464 n2.setLight(bank, 0);
466 unlighted_nodes.insert(n2pos, current_light);
469 Remove from light_sources if it is there
470 NOTE: This doesn't happen nearly at all
472 /*if(light_sources.find(n2pos))
474 std::cout<<"Removed from light_sources"<<std::endl;
475 light_sources.remove(n2pos);
480 light_sources.insert(n2pos, true);
485 /*dstream<<"unspreadLight(): Changed block "
486 <<blockchangecount<<" times"
487 <<" for "<<from_nodes.size()<<" nodes"
490 if(unlighted_nodes.size() > 0)
491 unspreadLight(bank, unlighted_nodes, light_sources);
495 void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p,
496 INodeDefManager *nodemgr)
498 const v3s16 dirs[6] = {
499 v3s16(0,0,1), // back
501 v3s16(1,0,0), // right
502 v3s16(0,0,-1), // front
503 v3s16(0,-1,0), // bottom
504 v3s16(-1,0,0), // left
507 emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
509 u32 i = m_area.index(p);
511 if(m_flags[i] & VOXELFLAG_INEXISTENT)
514 MapNode &n = m_data[i];
516 u8 oldlight = n.getLight(bank, nodemgr);
517 u8 newlight = diminish_light(oldlight);
519 // Loop through 6 neighbors
520 for(u16 i=0; i<6; i++)
522 // Get the position of the neighbor node
523 v3s16 n2pos = p + dirs[i];
525 u32 n2i = m_area.index(n2pos);
527 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
530 MapNode &n2 = m_data[n2i];
533 If the neighbor is brighter than the current node,
534 add to list (it will light up this node on its turn)
536 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
538 spreadLight(bank, n2pos, nodemgr);
541 If the neighbor is dimmer than how much light this node
542 would spread on it, add to list
544 if(n2.getLight(bank, nodemgr) < newlight)
546 if(nodemgr->get(n2).light_propagates)
548 n2.setLight(bank, newlight, nodemgr);
549 spreadLight(bank, n2pos, nodemgr);
557 Lights neighbors of from_nodes, collects all them and then
560 NOTE: This is faster on small areas but will overflow the
561 stack on large areas. Thus it is not used.
563 void VoxelManipulator::spreadLight(enum LightBank bank,
564 core::map<v3s16, bool> & from_nodes)
566 if(from_nodes.size() == 0)
569 core::map<v3s16, bool> lighted_nodes;
570 core::map<v3s16, bool>::Iterator j;
571 j = from_nodes.getIterator();
573 for(; j.atEnd() == false; j++)
575 v3s16 pos = j.getNode()->getKey();
577 spreadLight(bank, pos);
584 Lights neighbors of from_nodes, collects all them and then
587 void VoxelManipulator::spreadLight(enum LightBank bank,
588 core::map<v3s16, bool> & from_nodes, INodeDefManager *nodemgr)
590 const v3s16 dirs[6] = {
591 v3s16(0,0,1), // back
593 v3s16(1,0,0), // right
594 v3s16(0,0,-1), // front
595 v3s16(0,-1,0), // bottom
596 v3s16(-1,0,0), // left
599 if(from_nodes.size() == 0)
602 core::map<v3s16, bool> lighted_nodes;
603 core::map<v3s16, bool>::Iterator j;
604 j = from_nodes.getIterator();
606 for(; j.atEnd() == false; j++)
608 v3s16 pos = j.getNode()->getKey();
610 emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
612 u32 i = m_area.index(pos);
614 if(m_flags[i] & VOXELFLAG_INEXISTENT)
617 MapNode &n = m_data[i];
619 u8 oldlight = n.getLight(bank, nodemgr);
620 u8 newlight = diminish_light(oldlight);
622 // Loop through 6 neighbors
623 for(u16 i=0; i<6; i++)
625 // Get the position of the neighbor node
626 v3s16 n2pos = pos + dirs[i];
630 u32 n2i = m_area.index(n2pos);
632 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
635 MapNode &n2 = m_data[n2i];
638 If the neighbor is brighter than the current node,
639 add to list (it will light up this node on its turn)
641 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
643 lighted_nodes.insert(n2pos, true);
646 If the neighbor is dimmer than how much light this node
647 would spread on it, add to list
649 if(n2.getLight(bank, nodemgr) < newlight)
651 if(nodemgr->get(n2).light_propagates)
653 n2.setLight(bank, newlight, nodemgr);
654 lighted_nodes.insert(n2pos, true);
658 catch(InvalidPositionException &e)
665 /*dstream<<"spreadLight(): Changed block "
666 <<blockchangecount<<" times"
667 <<" for "<<from_nodes.size()<<" nodes"
670 if(lighted_nodes.size() > 0)
671 spreadLight(bank, lighted_nodes, nodemgr);