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_NO_DATA)
96 MapNode n = m_data[m_area.index(x,y,z)];
97 content_t m = n.getContent();
99 if(mode == VOXELPRINT_MATERIAL)
104 else if(mode == VOXELPRINT_WATERPRESSURE)
106 if(ndef->get(m).isLiquid())
112 else if(m == CONTENT_AIR)
121 else if(mode == VOXELPRINT_LIGHT_DAY)
123 if(ndef->get(m).light_source != 0)
125 else if(ndef->get(m).light_propagates == false)
129 u8 light = n.getLight(LIGHTBANK_DAY, ndef);
133 c = 'a' + (light-10);
145 void VoxelManipulator::addArea(const VoxelArea &area)
147 // Cancel if requested area has zero volume
148 if(area.getExtent() == v3s16(0,0,0))
151 // Cancel if m_area already contains the requested area
152 if(m_area.contains(area))
155 TimeTaker timer("addArea", &addarea_time);
157 // Calculate new area
159 // New area is the requested area if m_area has zero volume
160 if(m_area.getExtent() == v3s16(0,0,0))
164 // Else add requested area to m_area
168 new_area.addArea(area);
171 s32 new_size = new_area.getVolume();
173 /*dstream<<"adding area ";
175 dstream<<", old area ";
176 m_area.print(dstream);
177 dstream<<", new area ";
178 new_area.print(dstream);
179 dstream<<", new_size="<<new_size;
180 dstream<<std::endl;*/
182 // Allocate and clear new data
183 MapNode *new_data = new MapNode[new_size];
185 u8 *new_flags = new u8[new_size];
187 memset(new_flags, VOXELFLAG_NO_DATA, new_size);
190 s32 old_x_width = m_area.MaxEdge.X - m_area.MinEdge.X + 1;
191 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
192 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
194 unsigned int old_index = m_area.index(m_area.MinEdge.X,y,z);
195 unsigned int new_index = new_area.index(m_area.MinEdge.X,y,z);
197 memcpy(&new_data[new_index], &m_data[old_index],
198 old_x_width * sizeof(MapNode));
199 memcpy(&new_flags[new_index], &m_flags[old_index],
200 old_x_width * sizeof(u8));
203 // Replace area, data and flags
207 MapNode *old_data = m_data;
208 u8 *old_flags = m_flags;
210 /*dstream<<"old_data="<<(int)old_data<<", new_data="<<(int)new_data
211 <<", old_flags="<<(int)m_flags<<", new_flags="<<(int)new_flags<<std::endl;*/
221 //dstream<<"addArea done"<<std::endl;
224 void VoxelManipulator::copyFrom(MapNode *src, const VoxelArea& src_area,
225 v3s16 from_pos, v3s16 to_pos, v3s16 size)
227 for(s16 z=0; z<size.Z; z++)
228 for(s16 y=0; y<size.Y; y++)
230 s32 i_src = src_area.index(from_pos.X, from_pos.Y+y, from_pos.Z+z);
231 s32 i_local = m_area.index(to_pos.X, to_pos.Y+y, to_pos.Z+z);
232 memcpy(&m_data[i_local], &src[i_src], size.X*sizeof(MapNode));
233 memset(&m_flags[i_local], 0, size.X);
237 void VoxelManipulator::copyTo(MapNode *dst, const VoxelArea& dst_area,
238 v3s16 dst_pos, v3s16 from_pos, v3s16 size)
240 for(s16 z=0; z<size.Z; z++)
241 for(s16 y=0; y<size.Y; y++)
243 s32 i_dst = dst_area.index(dst_pos.X, dst_pos.Y+y, dst_pos.Z+z);
244 s32 i_local = m_area.index(from_pos.X, from_pos.Y+y, from_pos.Z+z);
245 for (s16 x = 0; x < size.X; x++) {
246 if (m_data[i_local].getContent() != CONTENT_IGNORE)
247 dst[i_dst] = m_data[i_local];
256 -----------------------------------------------------
259 void VoxelManipulator::clearFlag(u8 flags)
261 // 0-1ms on moderate area
262 TimeTaker timer("clearFlag", &clearflag_time);
264 //v3s16 s = m_area.getExtent();
266 /*dstream<<"clearFlag clearing area of size "
267 <<""<<s.X<<"x"<<s.Y<<"x"<<s.Z<<""
272 /*for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
273 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
274 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
276 u8 f = m_flags[m_area.index(x,y,z)];
277 m_flags[m_area.index(x,y,z)] &= ~flags;
278 if(m_flags[m_area.index(x,y,z)] != f)
282 s32 volume = m_area.getVolume();
283 for(s32 i=0; i<volume; i++)
285 m_flags[i] &= ~flags;
288 /*s32 volume = m_area.getVolume();
289 for(s32 i=0; i<volume; i++)
292 m_flags[i] &= ~flags;
297 dstream<<"clearFlag changed "<<count<<" flags out of "
298 <<volume<<" nodes"<<std::endl;*/
301 void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
302 std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
305 v3s16(0,0,1), // back
307 v3s16(1,0,0), // right
308 v3s16(0,0,-1), // front
309 v3s16(0,-1,0), // bottom
310 v3s16(-1,0,0), // left
313 VoxelArea voxel_area(p - v3s16(1,1,1), p + v3s16(1,1,1));
316 // Loop through 6 neighbors
317 for(u16 i=0; i<6; i++)
319 // Get the position of the neighbor node
320 v3s16 n2pos = p + dirs[i];
322 u32 n2i = m_area.index(n2pos);
324 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
327 MapNode &n2 = m_data[n2i];
330 If the neighbor is dimmer than what was specified
331 as oldlight (the light of the previous node)
333 u8 light2 = n2.getLight(bank, nodemgr);
334 if(light2 < oldlight)
337 And the neighbor is transparent and it has some light
339 if(nodemgr->get(n2).light_propagates && light2 != 0)
342 Set light to 0 and add to queue
345 n2.setLight(bank, 0, nodemgr);
347 unspreadLight(bank, n2pos, light2, light_sources, nodemgr);
350 Remove from light_sources if it is there
351 NOTE: This doesn't happen nearly at all
353 /*if(light_sources.find(n2pos))
355 std::cout<<"Removed from light_sources"<<std::endl;
356 light_sources.remove(n2pos);
361 light_sources.insert(n2pos);
368 Goes recursively through the neighbours of the node.
370 Alters only transparent nodes.
372 If the lighting of the neighbour is lower than the lighting of
373 the node was (before changing it to 0 at the step before), the
374 lighting of the neighbour is set to 0 and then the same stuff
375 repeats for the neighbour.
377 The ending nodes of the routine are stored in light_sources.
378 This is useful when a light is removed. In such case, this
379 routine can be called for the light node and then again for
380 light_sources to re-light the area without the removed light.
382 values of from_nodes are lighting values.
384 void VoxelManipulator::unspreadLight(enum LightBank bank,
385 std::map<v3s16, u8> & from_nodes,
386 std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
388 if(from_nodes.size() == 0)
391 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
392 j != from_nodes.end(); ++j)
394 unspreadLight(bank, j->first, j->second, light_sources, nodemgr);
401 Goes recursively through the neighbours of the node.
403 Alters only transparent nodes.
405 If the lighting of the neighbour is lower than the lighting of
406 the node was (before changing it to 0 at the step before), the
407 lighting of the neighbour is set to 0 and then the same stuff
408 repeats for the neighbour.
410 The ending nodes of the routine are stored in light_sources.
411 This is useful when a light is removed. In such case, this
412 routine can be called for the light node and then again for
413 light_sources to re-light the area without the removed light.
415 values of from_nodes are lighting values.
417 void VoxelManipulator::unspreadLight(enum LightBank bank,
418 core::map<v3s16, u8> & from_nodes,
419 core::map<v3s16, bool> & light_sources)
422 v3s16(0,0,1), // back
424 v3s16(1,0,0), // right
425 v3s16(0,0,-1), // front
426 v3s16(0,-1,0), // bottom
427 v3s16(-1,0,0), // left
430 if(from_nodes.size() == 0)
433 core::map<v3s16, u8> unlighted_nodes;
434 core::map<v3s16, u8>::Iterator j;
435 j = from_nodes.getIterator();
437 for(; j.atEnd() == false; j++)
439 v3s16 pos = j.getNode()->getKey();
441 addArea(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
443 //MapNode &n = m_data[m_area.index(pos)];
445 u8 oldlight = j.getNode()->getValue();
447 // Loop through 6 neighbors
448 for(u16 i=0; i<6; i++)
450 // Get the position of the neighbor node
451 v3s16 n2pos = pos + dirs[i];
453 u32 n2i = m_area.index(n2pos);
455 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
458 MapNode &n2 = m_data[n2i];
461 If the neighbor is dimmer than what was specified
462 as oldlight (the light of the previous node)
464 if(n2.getLight(bank, nodemgr) < oldlight)
467 And the neighbor is transparent and it has some light
469 if(nodemgr->get(n2).light_propagates && n2.getLight(bank, nodemgr) != 0)
472 Set light to 0 and add to queue
475 u8 current_light = n2.getLight(bank, nodemgr);
476 n2.setLight(bank, 0);
478 unlighted_nodes.insert(n2pos, current_light);
481 Remove from light_sources if it is there
482 NOTE: This doesn't happen nearly at all
484 /*if(light_sources.find(n2pos))
486 std::cout<<"Removed from light_sources"<<std::endl;
487 light_sources.remove(n2pos);
492 light_sources.insert(n2pos, true);
497 /*dstream<<"unspreadLight(): Changed block "
498 <<blockchangecount<<" times"
499 <<" for "<<from_nodes.size()<<" nodes"
502 if(unlighted_nodes.size() > 0)
503 unspreadLight(bank, unlighted_nodes, light_sources);
507 void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p,
508 INodeDefManager *nodemgr)
510 const v3s16 dirs[6] = {
511 v3s16(0,0,1), // back
513 v3s16(1,0,0), // right
514 v3s16(0,0,-1), // front
515 v3s16(0,-1,0), // bottom
516 v3s16(-1,0,0), // left
519 VoxelArea voxel_area(p - v3s16(1,1,1), p + v3s16(1,1,1));
522 u32 i = m_area.index(p);
524 if(m_flags[i] & VOXELFLAG_NO_DATA)
527 MapNode &n = m_data[i];
529 u8 oldlight = n.getLight(bank, nodemgr);
530 u8 newlight = diminish_light(oldlight);
532 // Loop through 6 neighbors
533 for(u16 i=0; i<6; i++)
535 // Get the position of the neighbor node
536 v3s16 n2pos = p + dirs[i];
538 u32 n2i = m_area.index(n2pos);
540 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
543 MapNode &n2 = m_data[n2i];
545 u8 light2 = n2.getLight(bank, nodemgr);
548 If the neighbor is brighter than the current node,
549 add to list (it will light up this node on its turn)
551 if(light2 > undiminish_light(oldlight))
553 spreadLight(bank, n2pos, nodemgr);
556 If the neighbor is dimmer than how much light this node
557 would spread on it, add to list
559 if(light2 < newlight)
561 if(nodemgr->get(n2).light_propagates)
563 n2.setLight(bank, newlight, nodemgr);
564 spreadLight(bank, n2pos, nodemgr);
572 Lights neighbors of from_nodes, collects all them and then
575 NOTE: This is faster on small areas but will overflow the
576 stack on large areas. Thus it is not used.
578 void VoxelManipulator::spreadLight(enum LightBank bank,
579 core::map<v3s16, bool> & from_nodes)
581 if(from_nodes.size() == 0)
584 core::map<v3s16, bool> lighted_nodes;
585 core::map<v3s16, bool>::Iterator j;
586 j = from_nodes.getIterator();
588 for(; j.atEnd() == false; j++)
590 v3s16 pos = j.getNode()->getKey();
592 spreadLight(bank, pos);
599 Lights neighbors of from_nodes, collects all them and then
602 void VoxelManipulator::spreadLight(enum LightBank bank,
603 std::set<v3s16> & from_nodes, INodeDefManager *nodemgr)
605 const v3s16 dirs[6] = {
606 v3s16(0,0,1), // back
608 v3s16(1,0,0), // right
609 v3s16(0,0,-1), // front
610 v3s16(0,-1,0), // bottom
611 v3s16(-1,0,0), // left
614 if(from_nodes.size() == 0)
617 std::set<v3s16> lighted_nodes;
619 for(std::set<v3s16>::iterator j = from_nodes.begin();
620 j != from_nodes.end(); ++j)
624 VoxelArea voxel_area(pos - v3s16(1,1,1), pos + v3s16(1,1,1));
627 u32 i = m_area.index(pos);
629 if(m_flags[i] & VOXELFLAG_NO_DATA)
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_NO_DATA)
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);