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 /* The reason for this optimised code is that we're a member function
228 * and the data type/layout of m_data is know to us: it's stored as
229 * [z*h*w + y*h + x]. Therefore we can take the calls to m_area index
230 * (which performs the preceding mapping/indexing of m_data) out of the
231 * inner loop and calculate the next index as we're iterating to gain
234 * src_step and dest_step is the amount required to be added to our index
235 * every time y increments. Because the destination area may be larger
236 * than the source area we need one additional variable (otherwise we could
237 * just continue adding dest_step as is done for the source data): dest_mod.
238 * dest_mod is the difference in size between a "row" in the source data
239 * and a "row" in the destination data (I am using the term row loosely
240 * and for illustrative purposes). E.g.
242 * src <-------------------->|'''''' dest mod ''''''''
243 * dest <--------------------------------------------->
245 * dest_mod (it's essentially a modulus) is added to the destination index
246 * after every full iteration of the y span.
248 * This method falls under the category "linear array and incrementing
252 s32 src_step = src_area.getExtent().X;
253 s32 dest_step = m_area.getExtent().X;
254 s32 dest_mod = m_area.index(to_pos.X, to_pos.Y, to_pos.Z + 1)
255 - m_area.index(to_pos.X, to_pos.Y, to_pos.Z)
256 - dest_step * size.Y;
258 s32 i_src = src_area.index(from_pos.X, from_pos.Y, from_pos.Z);
259 s32 i_local = m_area.index(to_pos.X, to_pos.Y, to_pos.Z);
261 for (s16 z = 0; z < size.Z; z++) {
262 for (s16 y = 0; y < size.Y; y++) {
263 memcpy(&m_data[i_local], &src[i_src], size.X * sizeof(*m_data));
264 memset(&m_flags[i_local], 0, size.X);
266 i_local += dest_step;
272 void VoxelManipulator::copyTo(MapNode *dst, const VoxelArea& dst_area,
273 v3s16 dst_pos, v3s16 from_pos, v3s16 size)
275 for(s16 z=0; z<size.Z; z++)
276 for(s16 y=0; y<size.Y; y++)
278 s32 i_dst = dst_area.index(dst_pos.X, dst_pos.Y+y, dst_pos.Z+z);
279 s32 i_local = m_area.index(from_pos.X, from_pos.Y+y, from_pos.Z+z);
280 for (s16 x = 0; x < size.X; x++) {
281 if (m_data[i_local].getContent() != CONTENT_IGNORE)
282 dst[i_dst] = m_data[i_local];
291 -----------------------------------------------------
294 void VoxelManipulator::clearFlag(u8 flags)
296 // 0-1ms on moderate area
297 TimeTaker timer("clearFlag", &clearflag_time);
299 //v3s16 s = m_area.getExtent();
301 /*dstream<<"clearFlag clearing area of size "
302 <<""<<s.X<<"x"<<s.Y<<"x"<<s.Z<<""
307 /*for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
308 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
309 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
311 u8 f = m_flags[m_area.index(x,y,z)];
312 m_flags[m_area.index(x,y,z)] &= ~flags;
313 if(m_flags[m_area.index(x,y,z)] != f)
317 s32 volume = m_area.getVolume();
318 for(s32 i=0; i<volume; i++)
320 m_flags[i] &= ~flags;
323 /*s32 volume = m_area.getVolume();
324 for(s32 i=0; i<volume; i++)
327 m_flags[i] &= ~flags;
332 dstream<<"clearFlag changed "<<count<<" flags out of "
333 <<volume<<" nodes"<<std::endl;*/
336 void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
337 std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
340 v3s16(0,0,1), // back
342 v3s16(1,0,0), // right
343 v3s16(0,0,-1), // front
344 v3s16(0,-1,0), // bottom
345 v3s16(-1,0,0), // left
348 VoxelArea voxel_area(p - v3s16(1,1,1), p + v3s16(1,1,1));
351 // Loop through 6 neighbors
352 for(u16 i=0; i<6; i++)
354 // Get the position of the neighbor node
355 v3s16 n2pos = p + dirs[i];
357 u32 n2i = m_area.index(n2pos);
359 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
362 MapNode &n2 = m_data[n2i];
365 If the neighbor is dimmer than what was specified
366 as oldlight (the light of the previous node)
368 u8 light2 = n2.getLight(bank, nodemgr);
369 if(light2 < oldlight)
372 And the neighbor is transparent and it has some light
374 if(nodemgr->get(n2).light_propagates && light2 != 0)
377 Set light to 0 and add to queue
380 n2.setLight(bank, 0, nodemgr);
382 unspreadLight(bank, n2pos, light2, light_sources, nodemgr);
385 Remove from light_sources if it is there
386 NOTE: This doesn't happen nearly at all
388 /*if(light_sources.find(n2pos))
390 std::cout<<"Removed from light_sources"<<std::endl;
391 light_sources.remove(n2pos);
396 light_sources.insert(n2pos);
403 Goes recursively through the neighbours of the node.
405 Alters only transparent nodes.
407 If the lighting of the neighbour is lower than the lighting of
408 the node was (before changing it to 0 at the step before), the
409 lighting of the neighbour is set to 0 and then the same stuff
410 repeats for the neighbour.
412 The ending nodes of the routine are stored in light_sources.
413 This is useful when a light is removed. In such case, this
414 routine can be called for the light node and then again for
415 light_sources to re-light the area without the removed light.
417 values of from_nodes are lighting values.
419 void VoxelManipulator::unspreadLight(enum LightBank bank,
420 std::map<v3s16, u8> & from_nodes,
421 std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
423 if(from_nodes.empty())
426 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
427 j != from_nodes.end(); ++j)
429 unspreadLight(bank, j->first, j->second, light_sources, nodemgr);
436 Goes recursively through the neighbours of the node.
438 Alters only transparent nodes.
440 If the lighting of the neighbour is lower than the lighting of
441 the node was (before changing it to 0 at the step before), the
442 lighting of the neighbour is set to 0 and then the same stuff
443 repeats for the neighbour.
445 The ending nodes of the routine are stored in light_sources.
446 This is useful when a light is removed. In such case, this
447 routine can be called for the light node and then again for
448 light_sources to re-light the area without the removed light.
450 values of from_nodes are lighting values.
452 void VoxelManipulator::unspreadLight(enum LightBank bank,
453 core::map<v3s16, u8> & from_nodes,
454 core::map<v3s16, bool> & light_sources)
457 v3s16(0,0,1), // back
459 v3s16(1,0,0), // right
460 v3s16(0,0,-1), // front
461 v3s16(0,-1,0), // bottom
462 v3s16(-1,0,0), // left
465 if(from_nodes.size() == 0)
468 core::map<v3s16, u8> unlighted_nodes;
469 core::map<v3s16, u8>::Iterator j;
470 j = from_nodes.getIterator();
472 for(; j.atEnd() == false; j++)
474 v3s16 pos = j.getNode()->getKey();
476 addArea(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
478 //MapNode &n = m_data[m_area.index(pos)];
480 u8 oldlight = j.getNode()->getValue();
482 // Loop through 6 neighbors
483 for(u16 i=0; i<6; i++)
485 // Get the position of the neighbor node
486 v3s16 n2pos = pos + dirs[i];
488 u32 n2i = m_area.index(n2pos);
490 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
493 MapNode &n2 = m_data[n2i];
496 If the neighbor is dimmer than what was specified
497 as oldlight (the light of the previous node)
499 if(n2.getLight(bank, nodemgr) < oldlight)
502 And the neighbor is transparent and it has some light
504 if(nodemgr->get(n2).light_propagates && n2.getLight(bank, nodemgr) != 0)
507 Set light to 0 and add to queue
510 u8 current_light = n2.getLight(bank, nodemgr);
511 n2.setLight(bank, 0);
513 unlighted_nodes.insert(n2pos, current_light);
516 Remove from light_sources if it is there
517 NOTE: This doesn't happen nearly at all
519 /*if(light_sources.find(n2pos))
521 std::cout<<"Removed from light_sources"<<std::endl;
522 light_sources.remove(n2pos);
527 light_sources.insert(n2pos, true);
532 /*dstream<<"unspreadLight(): Changed block "
533 <<blockchangecount<<" times"
534 <<" for "<<from_nodes.size()<<" nodes"
537 if(unlighted_nodes.size() > 0)
538 unspreadLight(bank, unlighted_nodes, light_sources);
542 void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p,
543 INodeDefManager *nodemgr)
545 const v3s16 dirs[6] = {
546 v3s16(0,0,1), // back
548 v3s16(1,0,0), // right
549 v3s16(0,0,-1), // front
550 v3s16(0,-1,0), // bottom
551 v3s16(-1,0,0), // left
554 VoxelArea voxel_area(p - v3s16(1,1,1), p + v3s16(1,1,1));
557 u32 i = m_area.index(p);
559 if(m_flags[i] & VOXELFLAG_NO_DATA)
562 MapNode &n = m_data[i];
564 u8 oldlight = n.getLight(bank, nodemgr);
565 u8 newlight = diminish_light(oldlight);
567 // Loop through 6 neighbors
568 for(u16 i=0; i<6; i++)
570 // Get the position of the neighbor node
571 v3s16 n2pos = p + dirs[i];
573 u32 n2i = m_area.index(n2pos);
575 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
578 MapNode &n2 = m_data[n2i];
580 u8 light2 = n2.getLight(bank, nodemgr);
583 If the neighbor is brighter than the current node,
584 add to list (it will light up this node on its turn)
586 if(light2 > undiminish_light(oldlight))
588 spreadLight(bank, n2pos, nodemgr);
591 If the neighbor is dimmer than how much light this node
592 would spread on it, add to list
594 if(light2 < newlight)
596 if(nodemgr->get(n2).light_propagates)
598 n2.setLight(bank, newlight, nodemgr);
599 spreadLight(bank, n2pos, nodemgr);
607 Lights neighbors of from_nodes, collects all them and then
610 NOTE: This is faster on small areas but will overflow the
611 stack on large areas. Thus it is not used.
613 void VoxelManipulator::spreadLight(enum LightBank bank,
614 core::map<v3s16, bool> & from_nodes)
616 if(from_nodes.size() == 0)
619 core::map<v3s16, bool> lighted_nodes;
620 core::map<v3s16, bool>::Iterator j;
621 j = from_nodes.getIterator();
623 for(; j.atEnd() == false; j++)
625 v3s16 pos = j.getNode()->getKey();
627 spreadLight(bank, pos);
634 Lights neighbors of from_nodes, collects all them and then
637 void VoxelManipulator::spreadLight(enum LightBank bank,
638 std::set<v3s16> & from_nodes, INodeDefManager *nodemgr)
640 const v3s16 dirs[6] = {
641 v3s16(0,0,1), // back
643 v3s16(1,0,0), // right
644 v3s16(0,0,-1), // front
645 v3s16(0,-1,0), // bottom
646 v3s16(-1,0,0), // left
649 if(from_nodes.empty())
652 std::set<v3s16> lighted_nodes;
654 for(std::set<v3s16>::iterator j = from_nodes.begin();
655 j != from_nodes.end(); ++j)
659 VoxelArea voxel_area(pos - v3s16(1,1,1), pos + v3s16(1,1,1));
662 u32 i = m_area.index(pos);
664 if(m_flags[i] & VOXELFLAG_NO_DATA)
667 MapNode &n = m_data[i];
669 u8 oldlight = n.getLight(bank, nodemgr);
670 u8 newlight = diminish_light(oldlight);
672 // Loop through 6 neighbors
673 for(u16 i=0; i<6; i++)
675 // Get the position of the neighbor node
676 v3s16 n2pos = pos + dirs[i];
680 u32 n2i = m_area.index(n2pos);
682 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
685 MapNode &n2 = m_data[n2i];
687 u8 light2 = n2.getLight(bank, nodemgr);
690 If the neighbor is brighter than the current node,
691 add to list (it will light up this node on its turn)
693 if(light2 > undiminish_light(oldlight))
695 lighted_nodes.insert(n2pos);
698 If the neighbor is dimmer than how much light this node
699 would spread on it, add to list
701 if(light2 < newlight)
703 if(nodemgr->get(n2).light_propagates)
705 n2.setLight(bank, newlight, nodemgr);
706 lighted_nodes.insert(n2pos);
710 catch(InvalidPositionException &e)
717 /*dstream<<"spreadLight(): Changed block "
718 <<blockchangecount<<" times"
719 <<" for "<<from_nodes.size()<<" nodes"
722 if(!lighted_nodes.empty())
723 spreadLight(bank, lighted_nodes, nodemgr);