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.
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():
44 m_disable_water_climb = false;
47 VoxelManipulator::~VoxelManipulator()
56 void VoxelManipulator::clear()
58 // Reset area to volume=0
68 void VoxelManipulator::print(std::ostream &o, VoxelPrintMode mode)
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_NOT_LOADED)
93 else if(f & VOXELFLAG_INEXISTENT)
98 u8 m = m_data[m_area.index(x,y,z)].d;
99 u8 pr = m_data[m_area.index(x,y,z)].param2;
100 if(mode == VOXELPRINT_MATERIAL)
105 else if(mode == VOXELPRINT_WATERPRESSURE)
107 if(m == CONTENT_WATER)
113 else if(m == CONTENT_AIR)
131 void VoxelManipulator::addArea(VoxelArea area)
133 // Cancel if requested area has zero volume
134 if(area.getExtent() == v3s16(0,0,0))
137 // Cancel if m_area already contains the requested area
138 if(m_area.contains(area))
141 TimeTaker timer("addArea", &addarea_time);
143 // Calculate new area
145 // New area is the requested area if m_area has zero volume
146 if(m_area.getExtent() == v3s16(0,0,0))
150 // Else add requested area to m_area
154 new_area.addArea(area);
157 s32 new_size = new_area.getVolume();
159 /*dstream<<"adding area ";
161 dstream<<", old area ";
162 m_area.print(dstream);
163 dstream<<", new area ";
164 new_area.print(dstream);
165 dstream<<", new_size="<<new_size;
166 dstream<<std::endl;*/
168 // Allocate and clear new data
169 MapNode *new_data = new MapNode[new_size];
170 u8 *new_flags = new u8[new_size];
171 for(s32 i=0; i<new_size; i++)
173 new_flags[i] = VOXELFLAG_NOT_LOADED;
178 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
179 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
180 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
182 // If loaded, copy data and flags
183 if((m_flags[m_area.index(x,y,z)] & VOXELFLAG_NOT_LOADED) == false)
185 new_data[new_area.index(x,y,z)] = m_data[m_area.index(x,y,z)];
186 new_flags[new_area.index(x,y,z)] = m_flags[m_area.index(x,y,z)];
190 // Replace area, data and flags
194 MapNode *old_data = m_data;
195 u8 *old_flags = m_flags;
197 /*dstream<<"old_data="<<(int)old_data<<", new_data="<<(int)new_data
198 <<", old_flags="<<(int)m_flags<<", new_flags="<<(int)new_flags<<std::endl;*/
208 //dstream<<"addArea done"<<std::endl;
211 void VoxelManipulator::copyFrom(MapNode *src, VoxelArea src_area,
212 v3s16 from_pos, v3s16 to_pos, v3s16 size)
214 for(s16 z=0; z<size.Z; z++)
215 for(s16 y=0; y<size.Y; y++)
217 s32 i_src = src_area.index(from_pos.X, from_pos.Y+y, from_pos.Z+z);
218 s32 i_local = m_area.index(to_pos.X, to_pos.Y+y, to_pos.Z+z);
219 memcpy(&m_data[i_local], &src[i_src], size.X*sizeof(MapNode));
220 memset(&m_flags[i_local], 0, size.X);
225 void VoxelManipulator::clearFlag(u8 flags)
227 // 0-1ms on moderate area
228 TimeTaker timer("clearFlag", &clearflag_time);
230 v3s16 s = m_area.getExtent();
232 /*dstream<<"clearFlag clearing area of size "
233 <<""<<s.X<<"x"<<s.Y<<"x"<<s.Z<<""
238 /*for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
239 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
240 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
242 u8 f = m_flags[m_area.index(x,y,z)];
243 m_flags[m_area.index(x,y,z)] &= ~flags;
244 if(m_flags[m_area.index(x,y,z)] != f)
248 s32 volume = m_area.getVolume();
249 for(s32 i=0; i<volume; i++)
251 m_flags[i] &= ~flags;
254 /*s32 volume = m_area.getVolume();
255 for(s32 i=0; i<volume; i++)
258 m_flags[i] &= ~flags;
263 dstream<<"clearFlag changed "<<count<<" flags out of "
264 <<volume<<" nodes"<<std::endl;*/
267 void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
268 core::map<v3s16, bool> & light_sources)
271 v3s16(0,0,1), // back
273 v3s16(1,0,0), // right
274 v3s16(0,0,-1), // front
275 v3s16(0,-1,0), // bottom
276 v3s16(-1,0,0), // left
279 emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
281 // Loop through 6 neighbors
282 for(u16 i=0; i<6; i++)
284 // Get the position of the neighbor node
285 v3s16 n2pos = p + dirs[i];
287 u32 n2i = m_area.index(n2pos);
289 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
292 MapNode &n2 = m_data[n2i];
295 If the neighbor is dimmer than what was specified
296 as oldlight (the light of the previous node)
298 if(n2.getLight(bank) < oldlight)
301 And the neighbor is transparent and it has some light
303 if(n2.light_propagates() && n2.getLight(bank) != 0)
306 Set light to 0 and add to queue
309 u8 current_light = n2.getLight(bank);
310 n2.setLight(bank, 0);
312 unspreadLight(bank, n2pos, current_light, light_sources);
315 Remove from light_sources if it is there
316 NOTE: This doesn't happen nearly at all
318 /*if(light_sources.find(n2pos))
320 std::cout<<"Removed from light_sources"<<std::endl;
321 light_sources.remove(n2pos);
326 light_sources.insert(n2pos, true);
333 Goes recursively through the neighbours of the node.
335 Alters only transparent nodes.
337 If the lighting of the neighbour is lower than the lighting of
338 the node was (before changing it to 0 at the step before), the
339 lighting of the neighbour is set to 0 and then the same stuff
340 repeats for the neighbour.
342 The ending nodes of the routine are stored in light_sources.
343 This is useful when a light is removed. In such case, this
344 routine can be called for the light node and then again for
345 light_sources to re-light the area without the removed light.
347 values of from_nodes are lighting values.
349 void VoxelManipulator::unspreadLight(enum LightBank bank,
350 core::map<v3s16, u8> & from_nodes,
351 core::map<v3s16, bool> & light_sources)
353 if(from_nodes.size() == 0)
356 core::map<v3s16, u8>::Iterator j;
357 j = from_nodes.getIterator();
359 for(; j.atEnd() == false; j++)
361 v3s16 pos = j.getNode()->getKey();
363 //MapNode &n = m_data[m_area.index(pos)];
365 u8 oldlight = j.getNode()->getValue();
367 unspreadLight(bank, pos, oldlight, light_sources);
374 Goes recursively through the neighbours of the node.
376 Alters only transparent nodes.
378 If the lighting of the neighbour is lower than the lighting of
379 the node was (before changing it to 0 at the step before), the
380 lighting of the neighbour is set to 0 and then the same stuff
381 repeats for the neighbour.
383 The ending nodes of the routine are stored in light_sources.
384 This is useful when a light is removed. In such case, this
385 routine can be called for the light node and then again for
386 light_sources to re-light the area without the removed light.
388 values of from_nodes are lighting values.
390 void VoxelManipulator::unspreadLight(enum LightBank bank,
391 core::map<v3s16, u8> & from_nodes,
392 core::map<v3s16, bool> & light_sources)
395 v3s16(0,0,1), // back
397 v3s16(1,0,0), // right
398 v3s16(0,0,-1), // front
399 v3s16(0,-1,0), // bottom
400 v3s16(-1,0,0), // left
403 if(from_nodes.size() == 0)
406 core::map<v3s16, u8> unlighted_nodes;
407 core::map<v3s16, u8>::Iterator j;
408 j = from_nodes.getIterator();
410 for(; j.atEnd() == false; j++)
412 v3s16 pos = j.getNode()->getKey();
414 emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
416 //MapNode &n = m_data[m_area.index(pos)];
418 u8 oldlight = j.getNode()->getValue();
420 // Loop through 6 neighbors
421 for(u16 i=0; i<6; i++)
423 // Get the position of the neighbor node
424 v3s16 n2pos = pos + dirs[i];
426 u32 n2i = m_area.index(n2pos);
428 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
431 MapNode &n2 = m_data[n2i];
434 If the neighbor is dimmer than what was specified
435 as oldlight (the light of the previous node)
437 if(n2.getLight(bank) < oldlight)
440 And the neighbor is transparent and it has some light
442 if(n2.light_propagates() && n2.getLight(bank) != 0)
445 Set light to 0 and add to queue
448 u8 current_light = n2.getLight(bank);
449 n2.setLight(bank, 0);
451 unlighted_nodes.insert(n2pos, current_light);
454 Remove from light_sources if it is there
455 NOTE: This doesn't happen nearly at all
457 /*if(light_sources.find(n2pos))
459 std::cout<<"Removed from light_sources"<<std::endl;
460 light_sources.remove(n2pos);
465 light_sources.insert(n2pos, true);
470 /*dstream<<"unspreadLight(): Changed block "
471 <<blockchangecount<<" times"
472 <<" for "<<from_nodes.size()<<" nodes"
475 if(unlighted_nodes.size() > 0)
476 unspreadLight(bank, unlighted_nodes, light_sources);
480 void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p)
482 const v3s16 dirs[6] = {
483 v3s16(0,0,1), // back
485 v3s16(1,0,0), // right
486 v3s16(0,0,-1), // front
487 v3s16(0,-1,0), // bottom
488 v3s16(-1,0,0), // left
491 emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
493 u32 i = m_area.index(p);
495 if(m_flags[i] & VOXELFLAG_INEXISTENT)
498 MapNode &n = m_data[i];
500 u8 oldlight = n.getLight(bank);
501 u8 newlight = diminish_light(oldlight);
503 // Loop through 6 neighbors
504 for(u16 i=0; i<6; i++)
506 // Get the position of the neighbor node
507 v3s16 n2pos = p + dirs[i];
509 u32 n2i = m_area.index(n2pos);
511 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
514 MapNode &n2 = m_data[n2i];
517 If the neighbor is brighter than the current node,
518 add to list (it will light up this node on its turn)
520 if(n2.getLight(bank) > undiminish_light(oldlight))
522 spreadLight(bank, n2pos);
525 If the neighbor is dimmer than how much light this node
526 would spread on it, add to list
528 if(n2.getLight(bank) < newlight)
530 if(n2.light_propagates())
532 n2.setLight(bank, newlight);
533 spreadLight(bank, n2pos);
541 Lights neighbors of from_nodes, collects all them and then
544 void VoxelManipulator::spreadLight(enum LightBank bank,
545 core::map<v3s16, bool> & from_nodes)
547 if(from_nodes.size() == 0)
550 core::map<v3s16, bool> lighted_nodes;
551 core::map<v3s16, bool>::Iterator j;
552 j = from_nodes.getIterator();
554 for(; j.atEnd() == false; j++)
556 v3s16 pos = j.getNode()->getKey();
558 spreadLight(bank, pos);
565 Lights neighbors of from_nodes, collects all them and then
568 void VoxelManipulator::spreadLight(enum LightBank bank,
569 core::map<v3s16, bool> & from_nodes)
571 const v3s16 dirs[6] = {
572 v3s16(0,0,1), // back
574 v3s16(1,0,0), // right
575 v3s16(0,0,-1), // front
576 v3s16(0,-1,0), // bottom
577 v3s16(-1,0,0), // left
580 if(from_nodes.size() == 0)
583 core::map<v3s16, bool> lighted_nodes;
584 core::map<v3s16, bool>::Iterator j;
585 j = from_nodes.getIterator();
587 for(; j.atEnd() == false; j++)
589 v3s16 pos = j.getNode()->getKey();
591 emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
593 u32 i = m_area.index(pos);
595 if(m_flags[i] & VOXELFLAG_INEXISTENT)
598 MapNode &n = m_data[i];
600 u8 oldlight = n.getLight(bank);
601 u8 newlight = diminish_light(oldlight);
603 // Loop through 6 neighbors
604 for(u16 i=0; i<6; i++)
606 // Get the position of the neighbor node
607 v3s16 n2pos = pos + dirs[i];
611 u32 n2i = m_area.index(n2pos);
613 if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
616 MapNode &n2 = m_data[n2i];
619 If the neighbor is brighter than the current node,
620 add to list (it will light up this node on its turn)
622 if(n2.getLight(bank) > undiminish_light(oldlight))
624 lighted_nodes.insert(n2pos, true);
627 If the neighbor is dimmer than how much light this node
628 would spread on it, add to list
630 if(n2.getLight(bank) < newlight)
632 if(n2.light_propagates())
634 n2.setLight(bank, newlight);
635 lighted_nodes.insert(n2pos, true);
639 catch(InvalidPositionException &e)
646 /*dstream<<"spreadLight(): Changed block "
647 <<blockchangecount<<" times"
648 <<" for "<<from_nodes.size()<<" nodes"
651 if(lighted_nodes.size() > 0)
652 spreadLight(bank, lighted_nodes);