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 u64 emerge_load_time = 0;
33 u64 clearflag_time = 0;
36 VoxelManipulator::VoxelManipulator():
42 VoxelManipulator::~VoxelManipulator()
47 void VoxelManipulator::clear()
49 // Reset area to volume=0
57 void VoxelManipulator::print(std::ostream &o, INodeDefManager *ndef,
60 v3s16 em = m_area.getExtent();
61 v3s16 of = m_area.MinEdge;
62 o<<"size: "<<em.X<<"x"<<em.Y<<"x"<<em.Z
63 <<" offset: ("<<of.X<<","<<of.Y<<","<<of.Z<<")"<<std::endl;
65 for(s32 y=m_area.MaxEdge.Y; y>=m_area.MinEdge.Y; y--)
67 if(em.X >= 3 && em.Y >= 3)
69 if (y==m_area.MinEdge.Y+2) o<<"^ ";
70 else if(y==m_area.MinEdge.Y+1) o<<"| ";
71 else if(y==m_area.MinEdge.Y+0) o<<"y x-> ";
75 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
77 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
79 u8 f = m_flags[m_area.index(x,y,z)];
81 if(f & VOXELFLAG_NO_DATA)
86 MapNode n = m_data[m_area.index(x,y,z)];
87 content_t m = n.getContent();
89 if(mode == VOXELPRINT_MATERIAL)
94 else if(mode == VOXELPRINT_WATERPRESSURE)
96 if(ndef->get(m).isLiquid())
102 else if(m == CONTENT_AIR)
111 else if(mode == VOXELPRINT_LIGHT_DAY)
113 if(ndef->get(m).light_source != 0)
115 else if(ndef->get(m).light_propagates == false)
119 u8 light = n.getLight(LIGHTBANK_DAY, ndef);
123 c = 'a' + (light-10);
135 void VoxelManipulator::addArea(const VoxelArea &area)
137 // Cancel if requested area has zero volume
138 if (area.hasEmptyExtent())
141 // Cancel if m_area already contains the requested area
142 if(m_area.contains(area))
145 TimeTaker timer("addArea", &addarea_time);
147 // Calculate new area
149 // New area is the requested area if m_area has zero volume
150 if(m_area.hasEmptyExtent())
154 // Else add requested area to m_area
158 new_area.addArea(area);
161 s32 new_size = new_area.getVolume();
163 /*dstream<<"adding area ";
165 dstream<<", old area ";
166 m_area.print(dstream);
167 dstream<<", new area ";
168 new_area.print(dstream);
169 dstream<<", new_size="<<new_size;
170 dstream<<std::endl;*/
172 // Allocate new data and clear flags
173 MapNode *new_data = new MapNode[new_size];
175 u8 *new_flags = new u8[new_size];
177 memset(new_flags, VOXELFLAG_NO_DATA, new_size);
180 s32 old_x_width = m_area.MaxEdge.X - m_area.MinEdge.X + 1;
181 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
182 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
184 unsigned int old_index = m_area.index(m_area.MinEdge.X,y,z);
185 unsigned int new_index = new_area.index(m_area.MinEdge.X,y,z);
187 memcpy(&new_data[new_index], &m_data[old_index],
188 old_x_width * sizeof(MapNode));
189 memcpy(&new_flags[new_index], &m_flags[old_index],
190 old_x_width * sizeof(u8));
193 // Replace area, data and flags
197 MapNode *old_data = m_data;
198 u8 *old_flags = m_flags;
200 /*dstream<<"old_data="<<(int)old_data<<", new_data="<<(int)new_data
201 <<", old_flags="<<(int)m_flags<<", new_flags="<<(int)new_flags<<std::endl;*/
209 //dstream<<"addArea done"<<std::endl;
212 void VoxelManipulator::copyFrom(MapNode *src, const VoxelArea& src_area,
213 v3s16 from_pos, v3s16 to_pos, v3s16 size)
215 /* The reason for this optimised code is that we're a member function
216 * and the data type/layout of m_data is know to us: it's stored as
217 * [z*h*w + y*h + x]. Therefore we can take the calls to m_area index
218 * (which performs the preceding mapping/indexing of m_data) out of the
219 * inner loop and calculate the next index as we're iterating to gain
222 * src_step and dest_step is the amount required to be added to our index
223 * every time y increments. Because the destination area may be larger
224 * than the source area we need one additional variable (otherwise we could
225 * just continue adding dest_step as is done for the source data): dest_mod.
226 * dest_mod is the difference in size between a "row" in the source data
227 * and a "row" in the destination data (I am using the term row loosely
228 * and for illustrative purposes). E.g.
230 * src <-------------------->|'''''' dest mod ''''''''
231 * dest <--------------------------------------------->
233 * dest_mod (it's essentially a modulus) is added to the destination index
234 * after every full iteration of the y span.
236 * This method falls under the category "linear array and incrementing
240 s32 src_step = src_area.getExtent().X;
241 s32 dest_step = m_area.getExtent().X;
242 s32 dest_mod = m_area.index(to_pos.X, to_pos.Y, to_pos.Z + 1)
243 - m_area.index(to_pos.X, to_pos.Y, to_pos.Z)
244 - dest_step * size.Y;
246 s32 i_src = src_area.index(from_pos.X, from_pos.Y, from_pos.Z);
247 s32 i_local = m_area.index(to_pos.X, to_pos.Y, to_pos.Z);
249 for (s16 z = 0; z < size.Z; z++) {
250 for (s16 y = 0; y < size.Y; y++) {
251 memcpy(&m_data[i_local], &src[i_src], size.X * sizeof(*m_data));
252 memset(&m_flags[i_local], 0, size.X);
254 i_local += dest_step;
260 void VoxelManipulator::copyTo(MapNode *dst, const VoxelArea& dst_area,
261 v3s16 dst_pos, v3s16 from_pos, v3s16 size)
263 for(s16 z=0; z<size.Z; z++)
264 for(s16 y=0; y<size.Y; y++)
266 s32 i_dst = dst_area.index(dst_pos.X, dst_pos.Y+y, dst_pos.Z+z);
267 s32 i_local = m_area.index(from_pos.X, from_pos.Y+y, from_pos.Z+z);
268 for (s16 x = 0; x < size.X; x++) {
269 if (m_data[i_local].getContent() != CONTENT_IGNORE)
270 dst[i_dst] = m_data[i_local];
279 -----------------------------------------------------
282 void VoxelManipulator::clearFlag(u8 flags)
284 // 0-1ms on moderate area
285 TimeTaker timer("clearFlag", &clearflag_time);
287 //v3s16 s = m_area.getExtent();
289 /*dstream<<"clearFlag clearing area of size "
290 <<""<<s.X<<"x"<<s.Y<<"x"<<s.Z<<""
295 /*for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
296 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
297 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
299 u8 f = m_flags[m_area.index(x,y,z)];
300 m_flags[m_area.index(x,y,z)] &= ~flags;
301 if(m_flags[m_area.index(x,y,z)] != f)
305 s32 volume = m_area.getVolume();
306 for(s32 i=0; i<volume; i++)
308 m_flags[i] &= ~flags;
311 /*s32 volume = m_area.getVolume();
312 for(s32 i=0; i<volume; i++)
315 m_flags[i] &= ~flags;
320 dstream<<"clearFlag changed "<<count<<" flags out of "
321 <<volume<<" nodes"<<std::endl;*/
324 void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
325 std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
328 v3s16(0,0,1), // back
330 v3s16(1,0,0), // right
331 v3s16(0,0,-1), // front
332 v3s16(0,-1,0), // bottom
333 v3s16(-1,0,0), // left
336 VoxelArea voxel_area(p - v3s16(1,1,1), p + v3s16(1,1,1));
339 // Loop through 6 neighbors
340 for(u16 i=0; i<6; i++)
342 // Get the position of the neighbor node
343 v3s16 n2pos = p + dirs[i];
345 u32 n2i = m_area.index(n2pos);
347 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
350 MapNode &n2 = m_data[n2i];
353 If the neighbor is dimmer than what was specified
354 as oldlight (the light of the previous node)
356 u8 light2 = n2.getLight(bank, nodemgr);
357 if(light2 < oldlight)
360 And the neighbor is transparent and it has some light
362 if(nodemgr->get(n2).light_propagates && light2 != 0)
365 Set light to 0 and add to queue
368 n2.setLight(bank, 0, nodemgr);
370 unspreadLight(bank, n2pos, light2, light_sources, nodemgr);
373 Remove from light_sources if it is there
374 NOTE: This doesn't happen nearly at all
376 /*if(light_sources.find(n2pos))
378 std::cout<<"Removed from light_sources"<<std::endl;
379 light_sources.remove(n2pos);
384 light_sources.insert(n2pos);
390 Goes recursively through the neighbours of the node.
392 Alters only transparent nodes.
394 If the lighting of the neighbour is lower than the lighting of
395 the node was (before changing it to 0 at the step before), the
396 lighting of the neighbour is set to 0 and then the same stuff
397 repeats for the neighbour.
399 The ending nodes of the routine are stored in light_sources.
400 This is useful when a light is removed. In such case, this
401 routine can be called for the light node and then again for
402 light_sources to re-light the area without the removed light.
404 values of from_nodes are lighting values.
406 void VoxelManipulator::unspreadLight(enum LightBank bank,
407 std::map<v3s16, u8> & from_nodes,
408 std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
410 if(from_nodes.empty())
413 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
414 j != from_nodes.end(); ++j)
416 unspreadLight(bank, j->first, j->second, light_sources, nodemgr);
420 void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p,
421 INodeDefManager *nodemgr)
423 const v3s16 dirs[6] = {
424 v3s16(0,0,1), // back
426 v3s16(1,0,0), // right
427 v3s16(0,0,-1), // front
428 v3s16(0,-1,0), // bottom
429 v3s16(-1,0,0), // left
432 VoxelArea voxel_area(p - v3s16(1,1,1), p + v3s16(1,1,1));
435 u32 i = m_area.index(p);
437 if(m_flags[i] & VOXELFLAG_NO_DATA)
440 MapNode &n = m_data[i];
442 u8 oldlight = n.getLight(bank, nodemgr);
443 u8 newlight = diminish_light(oldlight);
445 // Loop through 6 neighbors
446 for(u16 i=0; i<6; i++)
448 // Get the position of the neighbor node
449 v3s16 n2pos = p + dirs[i];
451 u32 n2i = m_area.index(n2pos);
453 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
456 MapNode &n2 = m_data[n2i];
458 u8 light2 = n2.getLight(bank, nodemgr);
461 If the neighbor is brighter than the current node,
462 add to list (it will light up this node on its turn)
464 if(light2 > undiminish_light(oldlight))
466 spreadLight(bank, n2pos, nodemgr);
469 If the neighbor is dimmer than how much light this node
470 would spread on it, add to list
472 if(light2 < newlight)
474 if(nodemgr->get(n2).light_propagates)
476 n2.setLight(bank, newlight, nodemgr);
477 spreadLight(bank, n2pos, nodemgr);
484 const MapNode VoxelManipulator::ContentIgnoreNode = MapNode(CONTENT_IGNORE);
487 Lights neighbors of from_nodes, collects all them and then
490 void VoxelManipulator::spreadLight(enum LightBank bank,
491 std::set<v3s16> & from_nodes, INodeDefManager *nodemgr)
493 const v3s16 dirs[6] = {
494 v3s16(0,0,1), // back
496 v3s16(1,0,0), // right
497 v3s16(0,0,-1), // front
498 v3s16(0,-1,0), // bottom
499 v3s16(-1,0,0), // left
502 if(from_nodes.empty())
505 std::set<v3s16> lighted_nodes;
507 for(std::set<v3s16>::iterator j = from_nodes.begin();
508 j != from_nodes.end(); ++j)
512 VoxelArea voxel_area(pos - v3s16(1,1,1), pos + v3s16(1,1,1));
515 u32 i = m_area.index(pos);
517 if(m_flags[i] & VOXELFLAG_NO_DATA)
520 MapNode &n = m_data[i];
522 u8 oldlight = n.getLight(bank, nodemgr);
523 u8 newlight = diminish_light(oldlight);
525 // Loop through 6 neighbors
526 for(u16 i=0; i<6; i++)
528 // Get the position of the neighbor node
529 v3s16 n2pos = pos + dirs[i];
533 u32 n2i = m_area.index(n2pos);
535 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
538 MapNode &n2 = m_data[n2i];
540 u8 light2 = n2.getLight(bank, nodemgr);
543 If the neighbor is brighter than the current node,
544 add to list (it will light up this node on its turn)
546 if(light2 > undiminish_light(oldlight))
548 lighted_nodes.insert(n2pos);
551 If the neighbor is dimmer than how much light this node
552 would spread on it, add to list
554 if(light2 < newlight)
556 if(nodemgr->get(n2).light_propagates)
558 n2.setLight(bank, newlight, nodemgr);
559 lighted_nodes.insert(n2pos);
563 catch(InvalidPositionException &e)
570 /*dstream<<"spreadLight(): Changed block "
571 <<blockchangecount<<" times"
572 <<" for "<<from_nodes.size()<<" nodes"
575 if(!lighted_nodes.empty())
576 spreadLight(bank, lighted_nodes, nodemgr);