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(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 addArea(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
315 // Loop through 6 neighbors
316 for(u16 i=0; i<6; i++)
318 // Get the position of the neighbor node
319 v3s16 n2pos = p + dirs[i];
321 u32 n2i = m_area.index(n2pos);
323 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
326 MapNode &n2 = m_data[n2i];
329 If the neighbor is dimmer than what was specified
330 as oldlight (the light of the previous node)
332 u8 light2 = n2.getLight(bank, nodemgr);
333 if(light2 < oldlight)
336 And the neighbor is transparent and it has some light
338 if(nodemgr->get(n2).light_propagates && light2 != 0)
341 Set light to 0 and add to queue
344 n2.setLight(bank, 0, nodemgr);
346 unspreadLight(bank, n2pos, light2, light_sources, nodemgr);
349 Remove from light_sources if it is there
350 NOTE: This doesn't happen nearly at all
352 /*if(light_sources.find(n2pos))
354 std::cout<<"Removed from light_sources"<<std::endl;
355 light_sources.remove(n2pos);
360 light_sources.insert(n2pos);
367 Goes recursively through the neighbours of the node.
369 Alters only transparent nodes.
371 If the lighting of the neighbour is lower than the lighting of
372 the node was (before changing it to 0 at the step before), the
373 lighting of the neighbour is set to 0 and then the same stuff
374 repeats for the neighbour.
376 The ending nodes of the routine are stored in light_sources.
377 This is useful when a light is removed. In such case, this
378 routine can be called for the light node and then again for
379 light_sources to re-light the area without the removed light.
381 values of from_nodes are lighting values.
383 void VoxelManipulator::unspreadLight(enum LightBank bank,
384 std::map<v3s16, u8> & from_nodes,
385 std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
387 if(from_nodes.size() == 0)
390 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
391 j != from_nodes.end(); ++j)
393 unspreadLight(bank, j->first, j->second, light_sources, nodemgr);
400 Goes recursively through the neighbours of the node.
402 Alters only transparent nodes.
404 If the lighting of the neighbour is lower than the lighting of
405 the node was (before changing it to 0 at the step before), the
406 lighting of the neighbour is set to 0 and then the same stuff
407 repeats for the neighbour.
409 The ending nodes of the routine are stored in light_sources.
410 This is useful when a light is removed. In such case, this
411 routine can be called for the light node and then again for
412 light_sources to re-light the area without the removed light.
414 values of from_nodes are lighting values.
416 void VoxelManipulator::unspreadLight(enum LightBank bank,
417 core::map<v3s16, u8> & from_nodes,
418 core::map<v3s16, bool> & light_sources)
421 v3s16(0,0,1), // back
423 v3s16(1,0,0), // right
424 v3s16(0,0,-1), // front
425 v3s16(0,-1,0), // bottom
426 v3s16(-1,0,0), // left
429 if(from_nodes.size() == 0)
432 core::map<v3s16, u8> unlighted_nodes;
433 core::map<v3s16, u8>::Iterator j;
434 j = from_nodes.getIterator();
436 for(; j.atEnd() == false; j++)
438 v3s16 pos = j.getNode()->getKey();
440 addArea(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
442 //MapNode &n = m_data[m_area.index(pos)];
444 u8 oldlight = j.getNode()->getValue();
446 // Loop through 6 neighbors
447 for(u16 i=0; i<6; i++)
449 // Get the position of the neighbor node
450 v3s16 n2pos = pos + dirs[i];
452 u32 n2i = m_area.index(n2pos);
454 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
457 MapNode &n2 = m_data[n2i];
460 If the neighbor is dimmer than what was specified
461 as oldlight (the light of the previous node)
463 if(n2.getLight(bank, nodemgr) < oldlight)
466 And the neighbor is transparent and it has some light
468 if(nodemgr->get(n2).light_propagates && n2.getLight(bank, nodemgr) != 0)
471 Set light to 0 and add to queue
474 u8 current_light = n2.getLight(bank, nodemgr);
475 n2.setLight(bank, 0);
477 unlighted_nodes.insert(n2pos, current_light);
480 Remove from light_sources if it is there
481 NOTE: This doesn't happen nearly at all
483 /*if(light_sources.find(n2pos))
485 std::cout<<"Removed from light_sources"<<std::endl;
486 light_sources.remove(n2pos);
491 light_sources.insert(n2pos, true);
496 /*dstream<<"unspreadLight(): Changed block "
497 <<blockchangecount<<" times"
498 <<" for "<<from_nodes.size()<<" nodes"
501 if(unlighted_nodes.size() > 0)
502 unspreadLight(bank, unlighted_nodes, light_sources);
506 void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p,
507 INodeDefManager *nodemgr)
509 const v3s16 dirs[6] = {
510 v3s16(0,0,1), // back
512 v3s16(1,0,0), // right
513 v3s16(0,0,-1), // front
514 v3s16(0,-1,0), // bottom
515 v3s16(-1,0,0), // left
518 addArea(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
520 u32 i = m_area.index(p);
522 if(m_flags[i] & VOXELFLAG_NO_DATA)
525 MapNode &n = m_data[i];
527 u8 oldlight = n.getLight(bank, nodemgr);
528 u8 newlight = diminish_light(oldlight);
530 // Loop through 6 neighbors
531 for(u16 i=0; i<6; i++)
533 // Get the position of the neighbor node
534 v3s16 n2pos = p + dirs[i];
536 u32 n2i = m_area.index(n2pos);
538 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
541 MapNode &n2 = m_data[n2i];
543 u8 light2 = n2.getLight(bank, nodemgr);
546 If the neighbor is brighter than the current node,
547 add to list (it will light up this node on its turn)
549 if(light2 > undiminish_light(oldlight))
551 spreadLight(bank, n2pos, nodemgr);
554 If the neighbor is dimmer than how much light this node
555 would spread on it, add to list
557 if(light2 < newlight)
559 if(nodemgr->get(n2).light_propagates)
561 n2.setLight(bank, newlight, nodemgr);
562 spreadLight(bank, n2pos, nodemgr);
570 Lights neighbors of from_nodes, collects all them and then
573 NOTE: This is faster on small areas but will overflow the
574 stack on large areas. Thus it is not used.
576 void VoxelManipulator::spreadLight(enum LightBank bank,
577 core::map<v3s16, bool> & from_nodes)
579 if(from_nodes.size() == 0)
582 core::map<v3s16, bool> lighted_nodes;
583 core::map<v3s16, bool>::Iterator j;
584 j = from_nodes.getIterator();
586 for(; j.atEnd() == false; j++)
588 v3s16 pos = j.getNode()->getKey();
590 spreadLight(bank, pos);
597 Lights neighbors of from_nodes, collects all them and then
600 void VoxelManipulator::spreadLight(enum LightBank bank,
601 std::set<v3s16> & from_nodes, INodeDefManager *nodemgr)
603 const v3s16 dirs[6] = {
604 v3s16(0,0,1), // back
606 v3s16(1,0,0), // right
607 v3s16(0,0,-1), // front
608 v3s16(0,-1,0), // bottom
609 v3s16(-1,0,0), // left
612 if(from_nodes.size() == 0)
615 std::set<v3s16> lighted_nodes;
617 for(std::set<v3s16>::iterator j = from_nodes.begin();
618 j != from_nodes.end(); ++j)
622 addArea(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
624 u32 i = m_area.index(pos);
626 if(m_flags[i] & VOXELFLAG_NO_DATA)
629 MapNode &n = m_data[i];
631 u8 oldlight = n.getLight(bank, nodemgr);
632 u8 newlight = diminish_light(oldlight);
634 // Loop through 6 neighbors
635 for(u16 i=0; i<6; i++)
637 // Get the position of the neighbor node
638 v3s16 n2pos = pos + dirs[i];
642 u32 n2i = m_area.index(n2pos);
644 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
647 MapNode &n2 = m_data[n2i];
649 u8 light2 = n2.getLight(bank, nodemgr);
652 If the neighbor is brighter than the current node,
653 add to list (it will light up this node on its turn)
655 if(light2 > undiminish_light(oldlight))
657 lighted_nodes.insert(n2pos);
660 If the neighbor is dimmer than how much light this node
661 would spread on it, add to list
663 if(light2 < newlight)
665 if(nodemgr->get(n2).light_propagates)
667 n2.setLight(bank, newlight, nodemgr);
668 lighted_nodes.insert(n2pos);
672 catch(InvalidPositionException &e)
679 /*dstream<<"spreadLight(): Changed block "
680 <<blockchangecount<<" times"
681 <<" for "<<from_nodes.size()<<" nodes"
684 if(lighted_nodes.size() > 0)
685 spreadLight(bank, lighted_nodes, nodemgr);