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 // FIXME: UGLY KLUDGE because MapNode default constructor is FUBAR; it
184 // initialises data that is going to be overwritten anyway
185 MapNode *new_data = (MapNode*)new char[new_size * sizeof (*new_data)];
187 u8 *new_flags = new u8[new_size];
189 memset(new_flags, VOXELFLAG_NO_DATA, new_size);
192 s32 old_x_width = m_area.MaxEdge.X - m_area.MinEdge.X + 1;
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++)
196 unsigned int old_index = m_area.index(m_area.MinEdge.X,y,z);
197 unsigned int new_index = new_area.index(m_area.MinEdge.X,y,z);
199 memcpy(&new_data[new_index], &m_data[old_index],
200 old_x_width * sizeof(MapNode));
201 memcpy(&new_flags[new_index], &m_flags[old_index],
202 old_x_width * sizeof(u8));
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, const VoxelArea& src_area,
227 v3s16 from_pos, v3s16 to_pos, v3s16 size)
229 /* The reason for this optimised code is that we're a member function
230 * and the data type/layout of m_data is know to us: it's stored as
231 * [z*h*w + y*h + x]. Therefore we can take the calls to m_area index
232 * (which performs the preceding mapping/indexing of m_data) out of the
233 * inner loop and calculate the next index as we're iterating to gain
236 * src_step and dest_step is the amount required to be added to our index
237 * every time y increments. Because the destination area may be larger
238 * than the source area we need one additional variable (otherwise we could
239 * just continue adding dest_step as is done for the source data): dest_mod.
240 * dest_mod is the difference in size between a "row" in the source data
241 * and a "row" in the destination data (I am using the term row loosely
242 * and for illustrative purposes). E.g.
244 * src <-------------------->|'''''' dest mod ''''''''
245 * dest <--------------------------------------------->
247 * dest_mod (it's essentially a modulus) is added to the destination index
248 * after every full iteration of the y span.
250 * This method falls under the category "linear array and incrementing
254 s32 src_step = src_area.getExtent().X;
255 s32 dest_step = m_area.getExtent().X;
256 s32 dest_mod = m_area.index(to_pos.X, to_pos.Y, to_pos.Z + 1)
257 - m_area.index(to_pos.X, to_pos.Y, to_pos.Z)
258 - dest_step * size.Y;
260 s32 i_src = src_area.index(from_pos.X, from_pos.Y, from_pos.Z);
261 s32 i_local = m_area.index(to_pos.X, to_pos.Y, to_pos.Z);
263 for (s16 z = 0; z < size.Z; z++) {
264 for (s16 y = 0; y < size.Y; y++) {
265 memcpy(&m_data[i_local], &src[i_src], size.X * sizeof(*m_data));
266 memset(&m_flags[i_local], 0, size.X);
268 i_local += dest_step;
274 void VoxelManipulator::copyTo(MapNode *dst, const VoxelArea& dst_area,
275 v3s16 dst_pos, v3s16 from_pos, v3s16 size)
277 for(s16 z=0; z<size.Z; z++)
278 for(s16 y=0; y<size.Y; y++)
280 s32 i_dst = dst_area.index(dst_pos.X, dst_pos.Y+y, dst_pos.Z+z);
281 s32 i_local = m_area.index(from_pos.X, from_pos.Y+y, from_pos.Z+z);
282 for (s16 x = 0; x < size.X; x++) {
283 if (m_data[i_local].getContent() != CONTENT_IGNORE)
284 dst[i_dst] = m_data[i_local];
293 -----------------------------------------------------
296 void VoxelManipulator::clearFlag(u8 flags)
298 // 0-1ms on moderate area
299 TimeTaker timer("clearFlag", &clearflag_time);
301 //v3s16 s = m_area.getExtent();
303 /*dstream<<"clearFlag clearing area of size "
304 <<""<<s.X<<"x"<<s.Y<<"x"<<s.Z<<""
309 /*for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
310 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
311 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
313 u8 f = m_flags[m_area.index(x,y,z)];
314 m_flags[m_area.index(x,y,z)] &= ~flags;
315 if(m_flags[m_area.index(x,y,z)] != f)
319 s32 volume = m_area.getVolume();
320 for(s32 i=0; i<volume; i++)
322 m_flags[i] &= ~flags;
325 /*s32 volume = m_area.getVolume();
326 for(s32 i=0; i<volume; i++)
329 m_flags[i] &= ~flags;
334 dstream<<"clearFlag changed "<<count<<" flags out of "
335 <<volume<<" nodes"<<std::endl;*/
338 void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
339 std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
342 v3s16(0,0,1), // back
344 v3s16(1,0,0), // right
345 v3s16(0,0,-1), // front
346 v3s16(0,-1,0), // bottom
347 v3s16(-1,0,0), // left
350 VoxelArea voxel_area(p - v3s16(1,1,1), p + v3s16(1,1,1));
353 // Loop through 6 neighbors
354 for(u16 i=0; i<6; i++)
356 // Get the position of the neighbor node
357 v3s16 n2pos = p + dirs[i];
359 u32 n2i = m_area.index(n2pos);
361 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
364 MapNode &n2 = m_data[n2i];
367 If the neighbor is dimmer than what was specified
368 as oldlight (the light of the previous node)
370 u8 light2 = n2.getLight(bank, nodemgr);
371 if(light2 < oldlight)
374 And the neighbor is transparent and it has some light
376 if(nodemgr->get(n2).light_propagates && light2 != 0)
379 Set light to 0 and add to queue
382 n2.setLight(bank, 0, nodemgr);
384 unspreadLight(bank, n2pos, light2, light_sources, nodemgr);
387 Remove from light_sources if it is there
388 NOTE: This doesn't happen nearly at all
390 /*if(light_sources.find(n2pos))
392 std::cout<<"Removed from light_sources"<<std::endl;
393 light_sources.remove(n2pos);
398 light_sources.insert(n2pos);
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 std::map<v3s16, u8> & from_nodes,
423 std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
425 if(from_nodes.empty())
428 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
429 j != from_nodes.end(); ++j)
431 unspreadLight(bank, j->first, j->second, light_sources, nodemgr);
438 Goes recursively through the neighbours of the node.
440 Alters only transparent nodes.
442 If the lighting of the neighbour is lower than the lighting of
443 the node was (before changing it to 0 at the step before), the
444 lighting of the neighbour is set to 0 and then the same stuff
445 repeats for the neighbour.
447 The ending nodes of the routine are stored in light_sources.
448 This is useful when a light is removed. In such case, this
449 routine can be called for the light node and then again for
450 light_sources to re-light the area without the removed light.
452 values of from_nodes are lighting values.
454 void VoxelManipulator::unspreadLight(enum LightBank bank,
455 core::map<v3s16, u8> & from_nodes,
456 core::map<v3s16, bool> & light_sources)
459 v3s16(0,0,1), // back
461 v3s16(1,0,0), // right
462 v3s16(0,0,-1), // front
463 v3s16(0,-1,0), // bottom
464 v3s16(-1,0,0), // left
467 if(from_nodes.size() == 0)
470 core::map<v3s16, u8> unlighted_nodes;
471 core::map<v3s16, u8>::Iterator j;
472 j = from_nodes.getIterator();
474 for(; j.atEnd() == false; j++)
476 v3s16 pos = j.getNode()->getKey();
478 addArea(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
480 //MapNode &n = m_data[m_area.index(pos)];
482 u8 oldlight = j.getNode()->getValue();
484 // Loop through 6 neighbors
485 for(u16 i=0; i<6; i++)
487 // Get the position of the neighbor node
488 v3s16 n2pos = pos + dirs[i];
490 u32 n2i = m_area.index(n2pos);
492 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
495 MapNode &n2 = m_data[n2i];
498 If the neighbor is dimmer than what was specified
499 as oldlight (the light of the previous node)
501 if(n2.getLight(bank, nodemgr) < oldlight)
504 And the neighbor is transparent and it has some light
506 if(nodemgr->get(n2).light_propagates && n2.getLight(bank, nodemgr) != 0)
509 Set light to 0 and add to queue
512 u8 current_light = n2.getLight(bank, nodemgr);
513 n2.setLight(bank, 0);
515 unlighted_nodes.insert(n2pos, current_light);
518 Remove from light_sources if it is there
519 NOTE: This doesn't happen nearly at all
521 /*if(light_sources.find(n2pos))
523 std::cout<<"Removed from light_sources"<<std::endl;
524 light_sources.remove(n2pos);
529 light_sources.insert(n2pos, true);
534 /*dstream<<"unspreadLight(): Changed block "
535 <<blockchangecount<<" times"
536 <<" for "<<from_nodes.size()<<" nodes"
539 if(unlighted_nodes.size() > 0)
540 unspreadLight(bank, unlighted_nodes, light_sources);
544 void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p,
545 INodeDefManager *nodemgr)
547 const v3s16 dirs[6] = {
548 v3s16(0,0,1), // back
550 v3s16(1,0,0), // right
551 v3s16(0,0,-1), // front
552 v3s16(0,-1,0), // bottom
553 v3s16(-1,0,0), // left
556 VoxelArea voxel_area(p - v3s16(1,1,1), p + v3s16(1,1,1));
559 u32 i = m_area.index(p);
561 if(m_flags[i] & VOXELFLAG_NO_DATA)
564 MapNode &n = m_data[i];
566 u8 oldlight = n.getLight(bank, nodemgr);
567 u8 newlight = diminish_light(oldlight);
569 // Loop through 6 neighbors
570 for(u16 i=0; i<6; i++)
572 // Get the position of the neighbor node
573 v3s16 n2pos = p + dirs[i];
575 u32 n2i = m_area.index(n2pos);
577 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
580 MapNode &n2 = m_data[n2i];
582 u8 light2 = n2.getLight(bank, nodemgr);
585 If the neighbor is brighter than the current node,
586 add to list (it will light up this node on its turn)
588 if(light2 > undiminish_light(oldlight))
590 spreadLight(bank, n2pos, nodemgr);
593 If the neighbor is dimmer than how much light this node
594 would spread on it, add to list
596 if(light2 < newlight)
598 if(nodemgr->get(n2).light_propagates)
600 n2.setLight(bank, newlight, nodemgr);
601 spreadLight(bank, n2pos, nodemgr);
609 Lights neighbors of from_nodes, collects all them and then
612 NOTE: This is faster on small areas but will overflow the
613 stack on large areas. Thus it is not used.
615 void VoxelManipulator::spreadLight(enum LightBank bank,
616 core::map<v3s16, bool> & from_nodes)
618 if(from_nodes.size() == 0)
621 core::map<v3s16, bool> lighted_nodes;
622 core::map<v3s16, bool>::Iterator j;
623 j = from_nodes.getIterator();
625 for(; j.atEnd() == false; j++)
627 v3s16 pos = j.getNode()->getKey();
629 spreadLight(bank, pos);
636 Lights neighbors of from_nodes, collects all them and then
639 void VoxelManipulator::spreadLight(enum LightBank bank,
640 std::set<v3s16> & from_nodes, INodeDefManager *nodemgr)
642 const v3s16 dirs[6] = {
643 v3s16(0,0,1), // back
645 v3s16(1,0,0), // right
646 v3s16(0,0,-1), // front
647 v3s16(0,-1,0), // bottom
648 v3s16(-1,0,0), // left
651 if(from_nodes.empty())
654 std::set<v3s16> lighted_nodes;
656 for(std::set<v3s16>::iterator j = from_nodes.begin();
657 j != from_nodes.end(); ++j)
661 VoxelArea voxel_area(pos - v3s16(1,1,1), pos + v3s16(1,1,1));
664 u32 i = m_area.index(pos);
666 if(m_flags[i] & VOXELFLAG_NO_DATA)
669 MapNode &n = m_data[i];
671 u8 oldlight = n.getLight(bank, nodemgr);
672 u8 newlight = diminish_light(oldlight);
674 // Loop through 6 neighbors
675 for(u16 i=0; i<6; i++)
677 // Get the position of the neighbor node
678 v3s16 n2pos = pos + dirs[i];
682 u32 n2i = m_area.index(n2pos);
684 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
687 MapNode &n2 = m_data[n2i];
689 u8 light2 = n2.getLight(bank, nodemgr);
692 If the neighbor is brighter than the current node,
693 add to list (it will light up this node on its turn)
695 if(light2 > undiminish_light(oldlight))
697 lighted_nodes.insert(n2pos);
700 If the neighbor is dimmer than how much light this node
701 would spread on it, add to list
703 if(light2 < newlight)
705 if(nodemgr->get(n2).light_propagates)
707 n2.setLight(bank, newlight, nodemgr);
708 lighted_nodes.insert(n2pos);
712 catch(InvalidPositionException &e)
719 /*dstream<<"spreadLight(): Changed block "
720 <<blockchangecount<<" times"
721 <<" for "<<from_nodes.size()<<" nodes"
724 if(!lighted_nodes.empty())
725 spreadLight(bank, lighted_nodes, nodemgr);