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()
51 void VoxelManipulator::clear()
53 // Reset area to volume=0
61 void VoxelManipulator::print(std::ostream &o, INodeDefManager *ndef,
64 v3s16 em = m_area.getExtent();
65 v3s16 of = m_area.MinEdge;
66 o<<"size: "<<em.X<<"x"<<em.Y<<"x"<<em.Z
67 <<" offset: ("<<of.X<<","<<of.Y<<","<<of.Z<<")"<<std::endl;
69 for(s32 y=m_area.MaxEdge.Y; y>=m_area.MinEdge.Y; y--)
71 if(em.X >= 3 && em.Y >= 3)
73 if (y==m_area.MinEdge.Y+2) o<<"^ ";
74 else if(y==m_area.MinEdge.Y+1) o<<"| ";
75 else if(y==m_area.MinEdge.Y+0) o<<"y x-> ";
79 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
81 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
83 u8 f = m_flags[m_area.index(x,y,z)];
85 if(f & VOXELFLAG_NO_DATA)
90 MapNode n = m_data[m_area.index(x,y,z)];
91 content_t m = n.getContent();
93 if(mode == VOXELPRINT_MATERIAL)
98 else if(mode == VOXELPRINT_WATERPRESSURE)
100 if(ndef->get(m).isLiquid())
106 else if(m == CONTENT_AIR)
115 else if(mode == VOXELPRINT_LIGHT_DAY)
117 if(ndef->get(m).light_source != 0)
119 else if(ndef->get(m).light_propagates == false)
123 u8 light = n.getLight(LIGHTBANK_DAY, ndef);
127 c = 'a' + (light-10);
139 void VoxelManipulator::addArea(const VoxelArea &area)
141 // Cancel if requested area has zero volume
142 if(area.getExtent() == v3s16(0,0,0))
145 // Cancel if m_area already contains the requested area
146 if(m_area.contains(area))
149 TimeTaker timer("addArea", &addarea_time);
151 // Calculate new area
153 // New area is the requested area if m_area has zero volume
154 if(m_area.getExtent() == v3s16(0,0,0))
158 // Else add requested area to m_area
162 new_area.addArea(area);
165 s32 new_size = new_area.getVolume();
167 /*dstream<<"adding area ";
169 dstream<<", old area ";
170 m_area.print(dstream);
171 dstream<<", new area ";
172 new_area.print(dstream);
173 dstream<<", new_size="<<new_size;
174 dstream<<std::endl;*/
176 // Allocate and clear new data
177 // FIXME: UGLY KLUDGE because MapNode default constructor is FUBAR; it
178 // initialises data that is going to be overwritten anyway
179 MapNode *new_data = (MapNode*)new char[new_size * sizeof (*new_data)];
181 u8 *new_flags = new u8[new_size];
183 memset(new_flags, VOXELFLAG_NO_DATA, new_size);
186 s32 old_x_width = m_area.MaxEdge.X - m_area.MinEdge.X + 1;
187 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
188 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
190 unsigned int old_index = m_area.index(m_area.MinEdge.X,y,z);
191 unsigned int new_index = new_area.index(m_area.MinEdge.X,y,z);
193 memcpy(&new_data[new_index], &m_data[old_index],
194 old_x_width * sizeof(MapNode));
195 memcpy(&new_flags[new_index], &m_flags[old_index],
196 old_x_width * sizeof(u8));
199 // Replace area, data and flags
203 MapNode *old_data = m_data;
204 u8 *old_flags = m_flags;
206 /*dstream<<"old_data="<<(int)old_data<<", new_data="<<(int)new_data
207 <<", old_flags="<<(int)m_flags<<", new_flags="<<(int)new_flags<<std::endl;*/
215 //dstream<<"addArea done"<<std::endl;
218 void VoxelManipulator::copyFrom(MapNode *src, const VoxelArea& src_area,
219 v3s16 from_pos, v3s16 to_pos, v3s16 size)
221 /* The reason for this optimised code is that we're a member function
222 * and the data type/layout of m_data is know to us: it's stored as
223 * [z*h*w + y*h + x]. Therefore we can take the calls to m_area index
224 * (which performs the preceding mapping/indexing of m_data) out of the
225 * inner loop and calculate the next index as we're iterating to gain
228 * src_step and dest_step is the amount required to be added to our index
229 * every time y increments. Because the destination area may be larger
230 * than the source area we need one additional variable (otherwise we could
231 * just continue adding dest_step as is done for the source data): dest_mod.
232 * dest_mod is the difference in size between a "row" in the source data
233 * and a "row" in the destination data (I am using the term row loosely
234 * and for illustrative purposes). E.g.
236 * src <-------------------->|'''''' dest mod ''''''''
237 * dest <--------------------------------------------->
239 * dest_mod (it's essentially a modulus) is added to the destination index
240 * after every full iteration of the y span.
242 * This method falls under the category "linear array and incrementing
246 s32 src_step = src_area.getExtent().X;
247 s32 dest_step = m_area.getExtent().X;
248 s32 dest_mod = m_area.index(to_pos.X, to_pos.Y, to_pos.Z + 1)
249 - m_area.index(to_pos.X, to_pos.Y, to_pos.Z)
250 - dest_step * size.Y;
252 s32 i_src = src_area.index(from_pos.X, from_pos.Y, from_pos.Z);
253 s32 i_local = m_area.index(to_pos.X, to_pos.Y, to_pos.Z);
255 for (s16 z = 0; z < size.Z; z++) {
256 for (s16 y = 0; y < size.Y; y++) {
257 memcpy(&m_data[i_local], &src[i_src], size.X * sizeof(*m_data));
258 memset(&m_flags[i_local], 0, size.X);
260 i_local += dest_step;
266 void VoxelManipulator::copyTo(MapNode *dst, const VoxelArea& dst_area,
267 v3s16 dst_pos, v3s16 from_pos, v3s16 size)
269 for(s16 z=0; z<size.Z; z++)
270 for(s16 y=0; y<size.Y; y++)
272 s32 i_dst = dst_area.index(dst_pos.X, dst_pos.Y+y, dst_pos.Z+z);
273 s32 i_local = m_area.index(from_pos.X, from_pos.Y+y, from_pos.Z+z);
274 for (s16 x = 0; x < size.X; x++) {
275 if (m_data[i_local].getContent() != CONTENT_IGNORE)
276 dst[i_dst] = m_data[i_local];
285 -----------------------------------------------------
288 void VoxelManipulator::clearFlag(u8 flags)
290 // 0-1ms on moderate area
291 TimeTaker timer("clearFlag", &clearflag_time);
293 //v3s16 s = m_area.getExtent();
295 /*dstream<<"clearFlag clearing area of size "
296 <<""<<s.X<<"x"<<s.Y<<"x"<<s.Z<<""
301 /*for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
302 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
303 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
305 u8 f = m_flags[m_area.index(x,y,z)];
306 m_flags[m_area.index(x,y,z)] &= ~flags;
307 if(m_flags[m_area.index(x,y,z)] != f)
311 s32 volume = m_area.getVolume();
312 for(s32 i=0; i<volume; i++)
314 m_flags[i] &= ~flags;
317 /*s32 volume = m_area.getVolume();
318 for(s32 i=0; i<volume; i++)
321 m_flags[i] &= ~flags;
326 dstream<<"clearFlag changed "<<count<<" flags out of "
327 <<volume<<" nodes"<<std::endl;*/
330 void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
331 std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
334 v3s16(0,0,1), // back
336 v3s16(1,0,0), // right
337 v3s16(0,0,-1), // front
338 v3s16(0,-1,0), // bottom
339 v3s16(-1,0,0), // left
342 VoxelArea voxel_area(p - v3s16(1,1,1), p + v3s16(1,1,1));
345 // Loop through 6 neighbors
346 for(u16 i=0; i<6; i++)
348 // Get the position of the neighbor node
349 v3s16 n2pos = p + dirs[i];
351 u32 n2i = m_area.index(n2pos);
353 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
356 MapNode &n2 = m_data[n2i];
359 If the neighbor is dimmer than what was specified
360 as oldlight (the light of the previous node)
362 u8 light2 = n2.getLight(bank, nodemgr);
363 if(light2 < oldlight)
366 And the neighbor is transparent and it has some light
368 if(nodemgr->get(n2).light_propagates && light2 != 0)
371 Set light to 0 and add to queue
374 n2.setLight(bank, 0, nodemgr);
376 unspreadLight(bank, n2pos, light2, light_sources, nodemgr);
379 Remove from light_sources if it is there
380 NOTE: This doesn't happen nearly at all
382 /*if(light_sources.find(n2pos))
384 std::cout<<"Removed from light_sources"<<std::endl;
385 light_sources.remove(n2pos);
390 light_sources.insert(n2pos);
397 Goes recursively through the neighbours of the node.
399 Alters only transparent nodes.
401 If the lighting of the neighbour is lower than the lighting of
402 the node was (before changing it to 0 at the step before), the
403 lighting of the neighbour is set to 0 and then the same stuff
404 repeats for the neighbour.
406 The ending nodes of the routine are stored in light_sources.
407 This is useful when a light is removed. In such case, this
408 routine can be called for the light node and then again for
409 light_sources to re-light the area without the removed light.
411 values of from_nodes are lighting values.
413 void VoxelManipulator::unspreadLight(enum LightBank bank,
414 std::map<v3s16, u8> & from_nodes,
415 std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
417 if(from_nodes.empty())
420 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
421 j != from_nodes.end(); ++j)
423 unspreadLight(bank, j->first, j->second, light_sources, nodemgr);
430 Goes recursively through the neighbours of the node.
432 Alters only transparent nodes.
434 If the lighting of the neighbour is lower than the lighting of
435 the node was (before changing it to 0 at the step before), the
436 lighting of the neighbour is set to 0 and then the same stuff
437 repeats for the neighbour.
439 The ending nodes of the routine are stored in light_sources.
440 This is useful when a light is removed. In such case, this
441 routine can be called for the light node and then again for
442 light_sources to re-light the area without the removed light.
444 values of from_nodes are lighting values.
446 void VoxelManipulator::unspreadLight(enum LightBank bank,
447 core::map<v3s16, u8> & from_nodes,
448 core::map<v3s16, bool> & light_sources)
451 v3s16(0,0,1), // back
453 v3s16(1,0,0), // right
454 v3s16(0,0,-1), // front
455 v3s16(0,-1,0), // bottom
456 v3s16(-1,0,0), // left
459 if(from_nodes.size() == 0)
462 core::map<v3s16, u8> unlighted_nodes;
463 core::map<v3s16, u8>::Iterator j;
464 j = from_nodes.getIterator();
466 for(; j.atEnd() == false; j++)
468 v3s16 pos = j.getNode()->getKey();
470 addArea(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
472 //MapNode &n = m_data[m_area.index(pos)];
474 u8 oldlight = j.getNode()->getValue();
476 // Loop through 6 neighbors
477 for(u16 i=0; i<6; i++)
479 // Get the position of the neighbor node
480 v3s16 n2pos = pos + dirs[i];
482 u32 n2i = m_area.index(n2pos);
484 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
487 MapNode &n2 = m_data[n2i];
490 If the neighbor is dimmer than what was specified
491 as oldlight (the light of the previous node)
493 if(n2.getLight(bank, nodemgr) < oldlight)
496 And the neighbor is transparent and it has some light
498 if(nodemgr->get(n2).light_propagates && n2.getLight(bank, nodemgr) != 0)
501 Set light to 0 and add to queue
504 u8 current_light = n2.getLight(bank, nodemgr);
505 n2.setLight(bank, 0);
507 unlighted_nodes.insert(n2pos, current_light);
510 Remove from light_sources if it is there
511 NOTE: This doesn't happen nearly at all
513 /*if(light_sources.find(n2pos))
515 std::cout<<"Removed from light_sources"<<std::endl;
516 light_sources.remove(n2pos);
521 light_sources.insert(n2pos, true);
526 /*dstream<<"unspreadLight(): Changed block "
527 <<blockchangecount<<" times"
528 <<" for "<<from_nodes.size()<<" nodes"
531 if(unlighted_nodes.size() > 0)
532 unspreadLight(bank, unlighted_nodes, light_sources);
536 void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p,
537 INodeDefManager *nodemgr)
539 const v3s16 dirs[6] = {
540 v3s16(0,0,1), // back
542 v3s16(1,0,0), // right
543 v3s16(0,0,-1), // front
544 v3s16(0,-1,0), // bottom
545 v3s16(-1,0,0), // left
548 VoxelArea voxel_area(p - v3s16(1,1,1), p + v3s16(1,1,1));
551 u32 i = m_area.index(p);
553 if(m_flags[i] & VOXELFLAG_NO_DATA)
556 MapNode &n = m_data[i];
558 u8 oldlight = n.getLight(bank, nodemgr);
559 u8 newlight = diminish_light(oldlight);
561 // Loop through 6 neighbors
562 for(u16 i=0; i<6; i++)
564 // Get the position of the neighbor node
565 v3s16 n2pos = p + dirs[i];
567 u32 n2i = m_area.index(n2pos);
569 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
572 MapNode &n2 = m_data[n2i];
574 u8 light2 = n2.getLight(bank, nodemgr);
577 If the neighbor is brighter than the current node,
578 add to list (it will light up this node on its turn)
580 if(light2 > undiminish_light(oldlight))
582 spreadLight(bank, n2pos, nodemgr);
585 If the neighbor is dimmer than how much light this node
586 would spread on it, add to list
588 if(light2 < newlight)
590 if(nodemgr->get(n2).light_propagates)
592 n2.setLight(bank, newlight, nodemgr);
593 spreadLight(bank, n2pos, nodemgr);
601 Lights neighbors of from_nodes, collects all them and then
604 NOTE: This is faster on small areas but will overflow the
605 stack on large areas. Thus it is not used.
607 void VoxelManipulator::spreadLight(enum LightBank bank,
608 core::map<v3s16, bool> & from_nodes)
610 if(from_nodes.size() == 0)
613 core::map<v3s16, bool> lighted_nodes;
614 core::map<v3s16, bool>::Iterator j;
615 j = from_nodes.getIterator();
617 for(; j.atEnd() == false; j++)
619 v3s16 pos = j.getNode()->getKey();
621 spreadLight(bank, pos);
628 Lights neighbors of from_nodes, collects all them and then
631 void VoxelManipulator::spreadLight(enum LightBank bank,
632 std::set<v3s16> & from_nodes, INodeDefManager *nodemgr)
634 const v3s16 dirs[6] = {
635 v3s16(0,0,1), // back
637 v3s16(1,0,0), // right
638 v3s16(0,0,-1), // front
639 v3s16(0,-1,0), // bottom
640 v3s16(-1,0,0), // left
643 if(from_nodes.empty())
646 std::set<v3s16> lighted_nodes;
648 for(std::set<v3s16>::iterator j = from_nodes.begin();
649 j != from_nodes.end(); ++j)
653 VoxelArea voxel_area(pos - v3s16(1,1,1), pos + v3s16(1,1,1));
656 u32 i = m_area.index(pos);
658 if(m_flags[i] & VOXELFLAG_NO_DATA)
661 MapNode &n = m_data[i];
663 u8 oldlight = n.getLight(bank, nodemgr);
664 u8 newlight = diminish_light(oldlight);
666 // Loop through 6 neighbors
667 for(u16 i=0; i<6; i++)
669 // Get the position of the neighbor node
670 v3s16 n2pos = pos + dirs[i];
674 u32 n2i = m_area.index(n2pos);
676 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
679 MapNode &n2 = m_data[n2i];
681 u8 light2 = n2.getLight(bank, nodemgr);
684 If the neighbor is brighter than the current node,
685 add to list (it will light up this node on its turn)
687 if(light2 > undiminish_light(oldlight))
689 lighted_nodes.insert(n2pos);
692 If the neighbor is dimmer than how much light this node
693 would spread on it, add to list
695 if(light2 < newlight)
697 if(nodemgr->get(n2).light_propagates)
699 n2.setLight(bank, newlight, nodemgr);
700 lighted_nodes.insert(n2pos);
704 catch(InvalidPositionException &e)
711 /*dstream<<"spreadLight(): Changed block "
712 <<blockchangecount<<" times"
713 <<" for "<<from_nodes.size()<<" nodes"
716 if(!lighted_nodes.empty())
717 spreadLight(bank, lighted_nodes, nodemgr);