3 Copyright (C) 2010 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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
15 You should have received a copy of the GNU 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.
22 #include "utility.h" // For TimeTaker
24 #include "content_mapnode.h"
31 u32 emerge_load_time = 0;
32 u32 clearflag_time = 0;
33 //u32 getwaterpressure_time = 0;
34 //u32 spreadwaterpressure_time = 0;
35 u32 updateareawaterpressure_time = 0;
36 u32 flowwater_pre_time = 0;
39 VoxelManipulator::VoxelManipulator():
45 VoxelManipulator::~VoxelManipulator()
54 void VoxelManipulator::clear()
56 // Reset area to volume=0
66 void VoxelManipulator::print(std::ostream &o, VoxelPrintMode mode)
68 v3s16 em = m_area.getExtent();
69 v3s16 of = m_area.MinEdge;
70 o<<"size: "<<em.X<<"x"<<em.Y<<"x"<<em.Z
71 <<" offset: ("<<of.X<<","<<of.Y<<","<<of.Z<<")"<<std::endl;
73 for(s32 y=m_area.MaxEdge.Y; y>=m_area.MinEdge.Y; y--)
75 if(em.X >= 3 && em.Y >= 3)
77 if (y==m_area.MinEdge.Y+2) o<<"^ ";
78 else if(y==m_area.MinEdge.Y+1) o<<"| ";
79 else if(y==m_area.MinEdge.Y+0) o<<"y x-> ";
83 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
85 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
87 u8 f = m_flags[m_area.index(x,y,z)];
89 if(f & VOXELFLAG_NOT_LOADED)
91 else if(f & VOXELFLAG_INEXISTENT)
96 content_t m = m_data[m_area.index(x,y,z)].getContent();
97 u8 pr = m_data[m_area.index(x,y,z)].param2;
98 if(mode == VOXELPRINT_MATERIAL)
103 else if(mode == VOXELPRINT_WATERPRESSURE)
105 if(m == CONTENT_WATER)
111 else if(m == CONTENT_AIR)
129 void VoxelManipulator::addArea(VoxelArea area)
131 // Cancel if requested area has zero volume
132 if(area.getExtent() == v3s16(0,0,0))
135 // Cancel if m_area already contains the requested area
136 if(m_area.contains(area))
139 TimeTaker timer("addArea", &addarea_time);
141 // Calculate new area
143 // New area is the requested area if m_area has zero volume
144 if(m_area.getExtent() == v3s16(0,0,0))
148 // Else add requested area to m_area
152 new_area.addArea(area);
155 s32 new_size = new_area.getVolume();
157 /*dstream<<"adding area ";
159 dstream<<", old area ";
160 m_area.print(dstream);
161 dstream<<", new area ";
162 new_area.print(dstream);
163 dstream<<", new_size="<<new_size;
164 dstream<<std::endl;*/
166 // Allocate and clear new data
167 MapNode *new_data = new MapNode[new_size];
168 u8 *new_flags = new u8[new_size];
169 for(s32 i=0; i<new_size; i++)
171 new_flags[i] = VOXELFLAG_NOT_LOADED;
176 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
177 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
178 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
180 // If loaded, copy data and flags
181 if((m_flags[m_area.index(x,y,z)] & VOXELFLAG_NOT_LOADED) == false)
183 new_data[new_area.index(x,y,z)] = m_data[m_area.index(x,y,z)];
184 new_flags[new_area.index(x,y,z)] = m_flags[m_area.index(x,y,z)];
188 // Replace area, data and flags
192 MapNode *old_data = m_data;
193 u8 *old_flags = m_flags;
195 /*dstream<<"old_data="<<(int)old_data<<", new_data="<<(int)new_data
196 <<", old_flags="<<(int)m_flags<<", new_flags="<<(int)new_flags<<std::endl;*/
206 //dstream<<"addArea done"<<std::endl;
209 void VoxelManipulator::copyFrom(MapNode *src, VoxelArea src_area,
210 v3s16 from_pos, v3s16 to_pos, v3s16 size)
212 for(s16 z=0; z<size.Z; z++)
213 for(s16 y=0; y<size.Y; y++)
215 s32 i_src = src_area.index(from_pos.X, from_pos.Y+y, from_pos.Z+z);
216 s32 i_local = m_area.index(to_pos.X, to_pos.Y+y, to_pos.Z+z);
217 memcpy(&m_data[i_local], &src[i_src], size.X*sizeof(MapNode));
218 memset(&m_flags[i_local], 0, size.X);
222 void VoxelManipulator::copyTo(MapNode *dst, VoxelArea dst_area,
223 v3s16 dst_pos, v3s16 from_pos, v3s16 size)
225 for(s16 z=0; z<size.Z; z++)
226 for(s16 y=0; y<size.Y; y++)
228 s32 i_dst = dst_area.index(dst_pos.X, dst_pos.Y+y, dst_pos.Z+z);
229 s32 i_local = m_area.index(from_pos.X, from_pos.Y+y, from_pos.Z+z);
230 memcpy(&dst[i_dst], &m_data[i_local], size.X*sizeof(MapNode));
236 -----------------------------------------------------
239 void VoxelManipulator::clearFlag(u8 flags)
241 // 0-1ms on moderate area
242 TimeTaker timer("clearFlag", &clearflag_time);
244 v3s16 s = m_area.getExtent();
246 /*dstream<<"clearFlag clearing area of size "
247 <<""<<s.X<<"x"<<s.Y<<"x"<<s.Z<<""
252 /*for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
253 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
254 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
256 u8 f = m_flags[m_area.index(x,y,z)];
257 m_flags[m_area.index(x,y,z)] &= ~flags;
258 if(m_flags[m_area.index(x,y,z)] != f)
262 s32 volume = m_area.getVolume();
263 for(s32 i=0; i<volume; i++)
265 m_flags[i] &= ~flags;
268 /*s32 volume = m_area.getVolume();
269 for(s32 i=0; i<volume; i++)
272 m_flags[i] &= ~flags;
277 dstream<<"clearFlag changed "<<count<<" flags out of "
278 <<volume<<" nodes"<<std::endl;*/
281 void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
282 core::map<v3s16, bool> & light_sources)
285 v3s16(0,0,1), // back
287 v3s16(1,0,0), // right
288 v3s16(0,0,-1), // front
289 v3s16(0,-1,0), // bottom
290 v3s16(-1,0,0), // left
293 emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
295 // Loop through 6 neighbors
296 for(u16 i=0; i<6; i++)
298 // Get the position of the neighbor node
299 v3s16 n2pos = p + dirs[i];
301 u32 n2i = m_area.index(n2pos);
303 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
306 MapNode &n2 = m_data[n2i];
309 If the neighbor is dimmer than what was specified
310 as oldlight (the light of the previous node)
312 if(n2.getLight(bank) < oldlight)
315 And the neighbor is transparent and it has some light
317 if(n2.light_propagates() && n2.getLight(bank) != 0)
320 Set light to 0 and add to queue
323 u8 current_light = n2.getLight(bank);
324 n2.setLight(bank, 0);
326 unspreadLight(bank, n2pos, current_light, light_sources);
329 Remove from light_sources if it is there
330 NOTE: This doesn't happen nearly at all
332 /*if(light_sources.find(n2pos))
334 std::cout<<"Removed from light_sources"<<std::endl;
335 light_sources.remove(n2pos);
340 light_sources.insert(n2pos, true);
347 Goes recursively through the neighbours of the node.
349 Alters only transparent nodes.
351 If the lighting of the neighbour is lower than the lighting of
352 the node was (before changing it to 0 at the step before), the
353 lighting of the neighbour is set to 0 and then the same stuff
354 repeats for the neighbour.
356 The ending nodes of the routine are stored in light_sources.
357 This is useful when a light is removed. In such case, this
358 routine can be called for the light node and then again for
359 light_sources to re-light the area without the removed light.
361 values of from_nodes are lighting values.
363 void VoxelManipulator::unspreadLight(enum LightBank bank,
364 core::map<v3s16, u8> & from_nodes,
365 core::map<v3s16, bool> & light_sources)
367 if(from_nodes.size() == 0)
370 core::map<v3s16, u8>::Iterator j;
371 j = from_nodes.getIterator();
373 for(; j.atEnd() == false; j++)
375 v3s16 pos = j.getNode()->getKey();
377 //MapNode &n = m_data[m_area.index(pos)];
379 u8 oldlight = j.getNode()->getValue();
381 unspreadLight(bank, pos, oldlight, light_sources);
388 Goes recursively through the neighbours of the node.
390 Alters only transparent nodes.
392 If the lighting of the neighbour is lower than the lighting of
393 the node was (before changing it to 0 at the step before), the
394 lighting of the neighbour is set to 0 and then the same stuff
395 repeats for the neighbour.
397 The ending nodes of the routine are stored in light_sources.
398 This is useful when a light is removed. In such case, this
399 routine can be called for the light node and then again for
400 light_sources to re-light the area without the removed light.
402 values of from_nodes are lighting values.
404 void VoxelManipulator::unspreadLight(enum LightBank bank,
405 core::map<v3s16, u8> & from_nodes,
406 core::map<v3s16, bool> & light_sources)
409 v3s16(0,0,1), // back
411 v3s16(1,0,0), // right
412 v3s16(0,0,-1), // front
413 v3s16(0,-1,0), // bottom
414 v3s16(-1,0,0), // left
417 if(from_nodes.size() == 0)
420 core::map<v3s16, u8> unlighted_nodes;
421 core::map<v3s16, u8>::Iterator j;
422 j = from_nodes.getIterator();
424 for(; j.atEnd() == false; j++)
426 v3s16 pos = j.getNode()->getKey();
428 emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
430 //MapNode &n = m_data[m_area.index(pos)];
432 u8 oldlight = j.getNode()->getValue();
434 // Loop through 6 neighbors
435 for(u16 i=0; i<6; i++)
437 // Get the position of the neighbor node
438 v3s16 n2pos = pos + dirs[i];
440 u32 n2i = m_area.index(n2pos);
442 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
445 MapNode &n2 = m_data[n2i];
448 If the neighbor is dimmer than what was specified
449 as oldlight (the light of the previous node)
451 if(n2.getLight(bank) < oldlight)
454 And the neighbor is transparent and it has some light
456 if(n2.light_propagates() && n2.getLight(bank) != 0)
459 Set light to 0 and add to queue
462 u8 current_light = n2.getLight(bank);
463 n2.setLight(bank, 0);
465 unlighted_nodes.insert(n2pos, current_light);
468 Remove from light_sources if it is there
469 NOTE: This doesn't happen nearly at all
471 /*if(light_sources.find(n2pos))
473 std::cout<<"Removed from light_sources"<<std::endl;
474 light_sources.remove(n2pos);
479 light_sources.insert(n2pos, true);
484 /*dstream<<"unspreadLight(): Changed block "
485 <<blockchangecount<<" times"
486 <<" for "<<from_nodes.size()<<" nodes"
489 if(unlighted_nodes.size() > 0)
490 unspreadLight(bank, unlighted_nodes, light_sources);
494 void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p)
496 const v3s16 dirs[6] = {
497 v3s16(0,0,1), // back
499 v3s16(1,0,0), // right
500 v3s16(0,0,-1), // front
501 v3s16(0,-1,0), // bottom
502 v3s16(-1,0,0), // left
505 emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
507 u32 i = m_area.index(p);
509 if(m_flags[i] & VOXELFLAG_INEXISTENT)
512 MapNode &n = m_data[i];
514 u8 oldlight = n.getLight(bank);
515 u8 newlight = diminish_light(oldlight);
517 // Loop through 6 neighbors
518 for(u16 i=0; i<6; i++)
520 // Get the position of the neighbor node
521 v3s16 n2pos = p + dirs[i];
523 u32 n2i = m_area.index(n2pos);
525 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
528 MapNode &n2 = m_data[n2i];
531 If the neighbor is brighter than the current node,
532 add to list (it will light up this node on its turn)
534 if(n2.getLight(bank) > undiminish_light(oldlight))
536 spreadLight(bank, n2pos);
539 If the neighbor is dimmer than how much light this node
540 would spread on it, add to list
542 if(n2.getLight(bank) < newlight)
544 if(n2.light_propagates())
546 n2.setLight(bank, newlight);
547 spreadLight(bank, n2pos);
555 Lights neighbors of from_nodes, collects all them and then
558 NOTE: This is faster on small areas but will overflow the
559 stack on large areas. Thus it is not used.
561 void VoxelManipulator::spreadLight(enum LightBank bank,
562 core::map<v3s16, bool> & from_nodes)
564 if(from_nodes.size() == 0)
567 core::map<v3s16, bool> lighted_nodes;
568 core::map<v3s16, bool>::Iterator j;
569 j = from_nodes.getIterator();
571 for(; j.atEnd() == false; j++)
573 v3s16 pos = j.getNode()->getKey();
575 spreadLight(bank, pos);
582 Lights neighbors of from_nodes, collects all them and then
585 void VoxelManipulator::spreadLight(enum LightBank bank,
586 core::map<v3s16, bool> & from_nodes)
588 const v3s16 dirs[6] = {
589 v3s16(0,0,1), // back
591 v3s16(1,0,0), // right
592 v3s16(0,0,-1), // front
593 v3s16(0,-1,0), // bottom
594 v3s16(-1,0,0), // left
597 if(from_nodes.size() == 0)
600 core::map<v3s16, bool> lighted_nodes;
601 core::map<v3s16, bool>::Iterator j;
602 j = from_nodes.getIterator();
604 for(; j.atEnd() == false; j++)
606 v3s16 pos = j.getNode()->getKey();
608 emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
610 u32 i = m_area.index(pos);
612 if(m_flags[i] & VOXELFLAG_INEXISTENT)
615 MapNode &n = m_data[i];
617 u8 oldlight = n.getLight(bank);
618 u8 newlight = diminish_light(oldlight);
620 // Loop through 6 neighbors
621 for(u16 i=0; i<6; i++)
623 // Get the position of the neighbor node
624 v3s16 n2pos = pos + dirs[i];
628 u32 n2i = m_area.index(n2pos);
630 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
633 MapNode &n2 = m_data[n2i];
636 If the neighbor is brighter than the current node,
637 add to list (it will light up this node on its turn)
639 if(n2.getLight(bank) > undiminish_light(oldlight))
641 lighted_nodes.insert(n2pos, true);
644 If the neighbor is dimmer than how much light this node
645 would spread on it, add to list
647 if(n2.getLight(bank) < newlight)
649 if(n2.light_propagates())
651 n2.setLight(bank, newlight);
652 lighted_nodes.insert(n2pos, true);
656 catch(InvalidPositionException &e)
663 /*dstream<<"spreadLight(): Changed block "
664 <<blockchangecount<<" times"
665 <<" for "<<from_nodes.size()<<" nodes"
668 if(lighted_nodes.size() > 0)
669 spreadLight(bank, lighted_nodes);