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()
40 VoxelManipulator::~VoxelManipulator()
45 void VoxelManipulator::clear()
47 // Reset area to volume=0
55 void VoxelManipulator::print(std::ostream &o, INodeDefManager *ndef,
58 v3s16 em = m_area.getExtent();
59 v3s16 of = m_area.MinEdge;
60 o<<"size: "<<em.X<<"x"<<em.Y<<"x"<<em.Z
61 <<" offset: ("<<of.X<<","<<of.Y<<","<<of.Z<<")"<<std::endl;
63 for(s32 y=m_area.MaxEdge.Y; y>=m_area.MinEdge.Y; y--)
65 if(em.X >= 3 && em.Y >= 3)
67 if (y==m_area.MinEdge.Y+2) o<<"^ ";
68 else if(y==m_area.MinEdge.Y+1) o<<"| ";
69 else if(y==m_area.MinEdge.Y+0) o<<"y x-> ";
73 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
75 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
77 u8 f = m_flags[m_area.index(x,y,z)];
79 if(f & VOXELFLAG_NO_DATA)
84 MapNode n = m_data[m_area.index(x,y,z)];
85 content_t m = n.getContent();
87 if(mode == VOXELPRINT_MATERIAL)
92 else if(mode == VOXELPRINT_WATERPRESSURE)
94 if(ndef->get(m).isLiquid())
100 else if(m == CONTENT_AIR)
109 else if(mode == VOXELPRINT_LIGHT_DAY)
111 if(ndef->get(m).light_source != 0)
113 else if(ndef->get(m).light_propagates == false)
117 u8 light = n.getLight(LIGHTBANK_DAY, ndef);
121 c = 'a' + (light-10);
133 void VoxelManipulator::addArea(const VoxelArea &area)
135 // Cancel if requested area has zero volume
136 if (area.hasEmptyExtent())
139 // Cancel if m_area already contains the requested area
140 if(m_area.contains(area))
143 TimeTaker timer("addArea", &addarea_time);
145 // Calculate new area
147 // New area is the requested area if m_area has zero volume
148 if(m_area.hasEmptyExtent())
152 // Else add requested area to m_area
156 new_area.addArea(area);
159 s32 new_size = new_area.getVolume();
161 /*dstream<<"adding area ";
163 dstream<<", old area ";
164 m_area.print(dstream);
165 dstream<<", new area ";
166 new_area.print(dstream);
167 dstream<<", new_size="<<new_size;
168 dstream<<std::endl;*/
170 // Allocate new data and clear flags
171 MapNode *new_data = new MapNode[new_size];
173 u8 *new_flags = new u8[new_size];
175 memset(new_flags, VOXELFLAG_NO_DATA, new_size);
178 s32 old_x_width = m_area.MaxEdge.X - m_area.MinEdge.X + 1;
179 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
180 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
182 unsigned int old_index = m_area.index(m_area.MinEdge.X,y,z);
183 unsigned int new_index = new_area.index(m_area.MinEdge.X,y,z);
185 memcpy(&new_data[new_index], &m_data[old_index],
186 old_x_width * sizeof(MapNode));
187 memcpy(&new_flags[new_index], &m_flags[old_index],
188 old_x_width * sizeof(u8));
191 // Replace area, data and flags
195 MapNode *old_data = m_data;
196 u8 *old_flags = m_flags;
198 /*dstream<<"old_data="<<(int)old_data<<", new_data="<<(int)new_data
199 <<", old_flags="<<(int)m_flags<<", new_flags="<<(int)new_flags<<std::endl;*/
207 //dstream<<"addArea done"<<std::endl;
210 void VoxelManipulator::copyFrom(MapNode *src, const VoxelArea& src_area,
211 v3s16 from_pos, v3s16 to_pos, v3s16 size)
213 /* The reason for this optimised code is that we're a member function
214 * and the data type/layout of m_data is know to us: it's stored as
215 * [z*h*w + y*h + x]. Therefore we can take the calls to m_area index
216 * (which performs the preceding mapping/indexing of m_data) out of the
217 * inner loop and calculate the next index as we're iterating to gain
220 * src_step and dest_step is the amount required to be added to our index
221 * every time y increments. Because the destination area may be larger
222 * than the source area we need one additional variable (otherwise we could
223 * just continue adding dest_step as is done for the source data): dest_mod.
224 * dest_mod is the difference in size between a "row" in the source data
225 * and a "row" in the destination data (I am using the term row loosely
226 * and for illustrative purposes). E.g.
228 * src <-------------------->|'''''' dest mod ''''''''
229 * dest <--------------------------------------------->
231 * dest_mod (it's essentially a modulus) is added to the destination index
232 * after every full iteration of the y span.
234 * This method falls under the category "linear array and incrementing
238 s32 src_step = src_area.getExtent().X;
239 s32 dest_step = m_area.getExtent().X;
240 s32 dest_mod = m_area.index(to_pos.X, to_pos.Y, to_pos.Z + 1)
241 - m_area.index(to_pos.X, to_pos.Y, to_pos.Z)
242 - dest_step * size.Y;
244 s32 i_src = src_area.index(from_pos.X, from_pos.Y, from_pos.Z);
245 s32 i_local = m_area.index(to_pos.X, to_pos.Y, to_pos.Z);
247 for (s16 z = 0; z < size.Z; z++) {
248 for (s16 y = 0; y < size.Y; y++) {
249 memcpy(&m_data[i_local], &src[i_src], size.X * sizeof(*m_data));
250 memset(&m_flags[i_local], 0, size.X);
252 i_local += dest_step;
258 void VoxelManipulator::copyTo(MapNode *dst, const VoxelArea& dst_area,
259 v3s16 dst_pos, v3s16 from_pos, v3s16 size)
261 for(s16 z=0; z<size.Z; z++)
262 for(s16 y=0; y<size.Y; y++)
264 s32 i_dst = dst_area.index(dst_pos.X, dst_pos.Y+y, dst_pos.Z+z);
265 s32 i_local = m_area.index(from_pos.X, from_pos.Y+y, from_pos.Z+z);
266 for (s16 x = 0; x < size.X; x++) {
267 if (m_data[i_local].getContent() != CONTENT_IGNORE)
268 dst[i_dst] = m_data[i_local];
277 -----------------------------------------------------
280 void VoxelManipulator::clearFlag(u8 flags)
282 // 0-1ms on moderate area
283 TimeTaker timer("clearFlag", &clearflag_time);
285 //v3s16 s = m_area.getExtent();
287 /*dstream<<"clearFlag clearing area of size "
288 <<""<<s.X<<"x"<<s.Y<<"x"<<s.Z<<""
293 /*for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
294 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
295 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
297 u8 f = m_flags[m_area.index(x,y,z)];
298 m_flags[m_area.index(x,y,z)] &= ~flags;
299 if(m_flags[m_area.index(x,y,z)] != f)
303 s32 volume = m_area.getVolume();
304 for(s32 i=0; i<volume; i++)
306 m_flags[i] &= ~flags;
309 /*s32 volume = m_area.getVolume();
310 for(s32 i=0; i<volume; i++)
313 m_flags[i] &= ~flags;
318 dstream<<"clearFlag changed "<<count<<" flags out of "
319 <<volume<<" nodes"<<std::endl;*/
322 void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
323 std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
326 v3s16(0,0,1), // back
328 v3s16(1,0,0), // right
329 v3s16(0,0,-1), // front
330 v3s16(0,-1,0), // bottom
331 v3s16(-1,0,0), // left
334 VoxelArea voxel_area(p - v3s16(1,1,1), p + v3s16(1,1,1));
337 // Loop through 6 neighbors
338 for(u16 i=0; i<6; i++)
340 // Get the position of the neighbor node
341 v3s16 n2pos = p + dirs[i];
343 u32 n2i = m_area.index(n2pos);
345 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
348 MapNode &n2 = m_data[n2i];
351 If the neighbor is dimmer than what was specified
352 as oldlight (the light of the previous node)
354 u8 light2 = n2.getLight(bank, nodemgr);
355 if(light2 < oldlight)
358 And the neighbor is transparent and it has some light
360 if(nodemgr->get(n2).light_propagates && light2 != 0)
363 Set light to 0 and add to queue
366 n2.setLight(bank, 0, nodemgr);
368 unspreadLight(bank, n2pos, light2, light_sources, nodemgr);
371 Remove from light_sources if it is there
372 NOTE: This doesn't happen nearly at all
374 /*if(light_sources.find(n2pos))
376 std::cout<<"Removed from light_sources"<<std::endl;
377 light_sources.remove(n2pos);
382 light_sources.insert(n2pos);
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 std::map<v3s16, u8> & from_nodes,
406 std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
408 if(from_nodes.empty())
411 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
412 j != from_nodes.end(); ++j)
414 unspreadLight(bank, j->first, j->second, light_sources, nodemgr);
418 void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p,
419 INodeDefManager *nodemgr)
421 const v3s16 dirs[6] = {
422 v3s16(0,0,1), // back
424 v3s16(1,0,0), // right
425 v3s16(0,0,-1), // front
426 v3s16(0,-1,0), // bottom
427 v3s16(-1,0,0), // left
430 VoxelArea voxel_area(p - v3s16(1,1,1), p + v3s16(1,1,1));
433 u32 i = m_area.index(p);
435 if(m_flags[i] & VOXELFLAG_NO_DATA)
438 MapNode &n = m_data[i];
440 u8 oldlight = n.getLight(bank, nodemgr);
441 u8 newlight = diminish_light(oldlight);
443 // Loop through 6 neighbors
444 for(u16 i=0; i<6; i++)
446 // Get the position of the neighbor node
447 v3s16 n2pos = p + dirs[i];
449 u32 n2i = m_area.index(n2pos);
451 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
454 MapNode &n2 = m_data[n2i];
456 u8 light2 = n2.getLight(bank, nodemgr);
459 If the neighbor is brighter than the current node,
460 add to list (it will light up this node on its turn)
462 if(light2 > undiminish_light(oldlight))
464 spreadLight(bank, n2pos, nodemgr);
467 If the neighbor is dimmer than how much light this node
468 would spread on it, add to list
470 if(light2 < newlight)
472 if(nodemgr->get(n2).light_propagates)
474 n2.setLight(bank, newlight, nodemgr);
475 spreadLight(bank, n2pos, nodemgr);
482 const MapNode VoxelManipulator::ContentIgnoreNode = MapNode(CONTENT_IGNORE);
485 Lights neighbors of from_nodes, collects all them and then
488 void VoxelManipulator::spreadLight(enum LightBank bank,
489 std::set<v3s16> & from_nodes, INodeDefManager *nodemgr)
491 const v3s16 dirs[6] = {
492 v3s16(0,0,1), // back
494 v3s16(1,0,0), // right
495 v3s16(0,0,-1), // front
496 v3s16(0,-1,0), // bottom
497 v3s16(-1,0,0), // left
500 if(from_nodes.empty())
503 std::set<v3s16> lighted_nodes;
505 for(std::set<v3s16>::iterator j = from_nodes.begin();
506 j != from_nodes.end(); ++j)
510 VoxelArea voxel_area(pos - v3s16(1,1,1), pos + v3s16(1,1,1));
513 u32 i = m_area.index(pos);
515 if(m_flags[i] & VOXELFLAG_NO_DATA)
518 MapNode &n = m_data[i];
520 u8 oldlight = n.getLight(bank, nodemgr);
521 u8 newlight = diminish_light(oldlight);
523 // Loop through 6 neighbors
524 for(u16 i=0; i<6; i++)
526 // Get the position of the neighbor node
527 v3s16 n2pos = pos + dirs[i];
531 u32 n2i = m_area.index(n2pos);
533 if(m_flags[n2i] & VOXELFLAG_NO_DATA)
536 MapNode &n2 = m_data[n2i];
538 u8 light2 = n2.getLight(bank, nodemgr);
541 If the neighbor is brighter than the current node,
542 add to list (it will light up this node on its turn)
544 if(light2 > undiminish_light(oldlight))
546 lighted_nodes.insert(n2pos);
549 If the neighbor is dimmer than how much light this node
550 would spread on it, add to list
552 if(light2 < newlight)
554 if(nodemgr->get(n2).light_propagates)
556 n2.setLight(bank, newlight, nodemgr);
557 lighted_nodes.insert(n2pos);
561 catch(InvalidPositionException &e)
568 /*dstream<<"spreadLight(): Changed block "
569 <<blockchangecount<<" times"
570 <<" for "<<from_nodes.size()<<" nodes"
573 if(!lighted_nodes.empty())
574 spreadLight(bank, lighted_nodes, nodemgr);