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.
23 #include "irrlichttypes.h"
27 #include "exceptions.h"
33 class INodeDefManager;
40 A fast voxel manipulator class.
42 In normal operation, it fetches more map when it is requested.
43 It can also be used so that all allowed area is fetched at the
44 start, using ManualMapVoxelManipulator.
52 extern u64 emerge_time;
53 extern u64 emerge_load_time;
56 This class resembles aabbox3d<s16> a lot, but has inclusive
57 edges for saner handling of integer sizes
62 // Starts as zero sized
65 VoxelArea(v3s16 min_edge, v3s16 max_edge):
80 void addArea(const VoxelArea &a)
87 if(a.MinEdge.X < MinEdge.X) MinEdge.X = a.MinEdge.X;
88 if(a.MinEdge.Y < MinEdge.Y) MinEdge.Y = a.MinEdge.Y;
89 if(a.MinEdge.Z < MinEdge.Z) MinEdge.Z = a.MinEdge.Z;
90 if(a.MaxEdge.X > MaxEdge.X) MaxEdge.X = a.MaxEdge.X;
91 if(a.MaxEdge.Y > MaxEdge.Y) MaxEdge.Y = a.MaxEdge.Y;
92 if(a.MaxEdge.Z > MaxEdge.Z) MaxEdge.Z = a.MaxEdge.Z;
94 void addPoint(const v3s16 &p)
102 if(p.X < MinEdge.X) MinEdge.X = p.X;
103 if(p.Y < MinEdge.Y) MinEdge.Y = p.Y;
104 if(p.Z < MinEdge.Z) MinEdge.Z = p.Z;
105 if(p.X > MaxEdge.X) MaxEdge.X = p.X;
106 if(p.Y > MaxEdge.Y) MaxEdge.Y = p.Y;
107 if(p.Z > MaxEdge.Z) MaxEdge.Z = p.Z;
111 void pad(const v3s16 &d)
117 /*void operator+=(v3s16 off)
123 void operator-=(v3s16 off)
133 v3s16 getExtent() const
135 return MaxEdge - MinEdge + v3s16(1,1,1);
138 /* Because MaxEdge and MinEdge are included in the voxel area an empty extent
139 * is not represented by (0, 0, 0), but instead (-1, -1, -1)
141 bool hasEmptyExtent() const
143 return MaxEdge - MinEdge == v3s16(-1, -1, -1);
146 s32 getVolume() const
148 v3s16 e = getExtent();
149 return (s32)e.X * (s32)e.Y * (s32)e.Z;
151 bool contains(const VoxelArea &a) const
153 // No area contains an empty area
154 // NOTE: Algorithms depend on this, so do not change.
155 if(a.hasEmptyExtent())
159 a.MinEdge.X >= MinEdge.X && a.MaxEdge.X <= MaxEdge.X &&
160 a.MinEdge.Y >= MinEdge.Y && a.MaxEdge.Y <= MaxEdge.Y &&
161 a.MinEdge.Z >= MinEdge.Z && a.MaxEdge.Z <= MaxEdge.Z
164 bool contains(v3s16 p) const
167 p.X >= MinEdge.X && p.X <= MaxEdge.X &&
168 p.Y >= MinEdge.Y && p.Y <= MaxEdge.Y &&
169 p.Z >= MinEdge.Z && p.Z <= MaxEdge.Z
172 bool contains(s32 i) const
174 return (i >= 0 && i < getVolume());
176 bool operator==(const VoxelArea &other) const
178 return (MinEdge == other.MinEdge
179 && MaxEdge == other.MaxEdge);
182 VoxelArea operator+(v3s16 off) const
184 return VoxelArea(MinEdge+off, MaxEdge+off);
187 VoxelArea operator-(v3s16 off) const
189 return VoxelArea(MinEdge-off, MaxEdge-off);
193 Returns 0-6 non-overlapping areas that can be added to
194 a to make up this area.
198 void diff(const VoxelArea &a, std::list<VoxelArea> &result)
201 This can result in a maximum of 6 areas
204 // If a is an empty area, return the current area as a whole
205 if(a.getExtent() == v3s16(0,0,0))
208 if(b.getVolume() != 0)
213 assert(contains(a)); // pre-condition
215 // Take back area, XY inclusive
217 v3s16 min(MinEdge.X, MinEdge.Y, a.MaxEdge.Z+1);
218 v3s16 max(MaxEdge.X, MaxEdge.Y, MaxEdge.Z);
219 VoxelArea b(min, max);
220 if(b.getVolume() != 0)
224 // Take front area, XY inclusive
226 v3s16 min(MinEdge.X, MinEdge.Y, MinEdge.Z);
227 v3s16 max(MaxEdge.X, MaxEdge.Y, a.MinEdge.Z-1);
228 VoxelArea b(min, max);
229 if(b.getVolume() != 0)
233 // Take top area, X inclusive
235 v3s16 min(MinEdge.X, a.MaxEdge.Y+1, a.MinEdge.Z);
236 v3s16 max(MaxEdge.X, MaxEdge.Y, a.MaxEdge.Z);
237 VoxelArea b(min, max);
238 if(b.getVolume() != 0)
242 // Take bottom area, X inclusive
244 v3s16 min(MinEdge.X, MinEdge.Y, a.MinEdge.Z);
245 v3s16 max(MaxEdge.X, a.MinEdge.Y-1, a.MaxEdge.Z);
246 VoxelArea b(min, max);
247 if(b.getVolume() != 0)
251 // Take left area, non-inclusive
253 v3s16 min(MinEdge.X, a.MinEdge.Y, a.MinEdge.Z);
254 v3s16 max(a.MinEdge.X-1, a.MaxEdge.Y, a.MaxEdge.Z);
255 VoxelArea b(min, max);
256 if(b.getVolume() != 0)
260 // Take right area, non-inclusive
262 v3s16 min(a.MaxEdge.X+1, a.MinEdge.Y, a.MinEdge.Z);
263 v3s16 max(MaxEdge.X, a.MaxEdge.Y, a.MaxEdge.Z);
264 VoxelArea b(min, max);
265 if(b.getVolume() != 0)
272 Translates position from virtual coordinates to array index
274 s32 index(s16 x, s16 y, s16 z) const
276 v3s16 em = getExtent();
278 s32 i = (s32)(z-off.Z)*em.Y*em.X + (y-off.Y)*em.X + (x-off.X);
279 //dstream<<" i("<<x<<","<<y<<","<<z<<")="<<i<<" ";
282 s32 index(v3s16 p) const
284 return index(p.X, p.Y, p.Z);
287 // Translate index in the X coordinate
288 void add_x(const v3s16 &extent, u32 &i, s16 a)
292 // Translate index in the Y coordinate
293 void add_y(const v3s16 &extent, u32 &i, s16 a)
297 // Translate index in the Z coordinate
298 void add_z(const v3s16 &extent, u32 &i, s16 a)
300 i += a * extent.X*extent.Y;
302 // Translate index in space
303 void add_p(const v3s16 &extent, u32 &i, v3s16 a)
305 i += a.Z*extent.X*extent.Y + a.Y*extent.X + a.X;
309 Print method for debugging
311 void print(std::ostream &o) const
313 v3s16 e = getExtent();
321 <<"="<<e.X<<"x"<<e.Y<<"x"<<e.Z<<"="<<getVolume();
324 // Edges are inclusive
325 v3s16 MinEdge = v3s16(1,1,1);
330 #define VOXELFLAG_UNUSED (1 << 0)
331 // no data about that node
332 #define VOXELFLAG_NO_DATA (1 << 1)
333 // Algorithm-dependent
334 #define VOXELFLAG_CHECKED1 (1 << 2)
335 // Algorithm-dependent
336 #define VOXELFLAG_CHECKED2 (1 << 3)
337 // Algorithm-dependent
338 #define VOXELFLAG_CHECKED3 (1 << 4)
339 // Algorithm-dependent
340 #define VOXELFLAG_CHECKED4 (1 << 5)
346 VOXELPRINT_WATERPRESSURE,
347 VOXELPRINT_LIGHT_DAY,
350 class VoxelManipulator /*: public NodeContainer*/
354 virtual ~VoxelManipulator();
357 Virtuals from NodeContainer
359 /*virtual u16 nodeContainerId() const
361 return NODECONTAINER_ID_VOXELMANIPULATOR;
363 bool isValidPosition(v3s16 p)
366 return !(m_flags[m_area.index(p)] & VOXELFLAG_NO_DATA);
370 These are a bit slow and shouldn't be used internally.
371 Use m_data[m_area.index(p)] instead.
373 MapNode getNode(v3s16 p)
375 VoxelArea voxel_area(p);
378 if(m_flags[m_area.index(p)] & VOXELFLAG_NO_DATA)
380 /*dstream<<"EXCEPT: VoxelManipulator::getNode(): "
381 <<"p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"
382 <<", index="<<m_area.index(p)
383 <<", flags="<<(int)m_flags[m_area.index(p)]
384 <<" is inexistent"<<std::endl;*/
385 throw InvalidPositionException
386 ("VoxelManipulator: getNode: inexistent");
389 return m_data[m_area.index(p)];
391 MapNode getNodeNoEx(v3s16 p)
393 VoxelArea voxel_area(p);
396 if(m_flags[m_area.index(p)] & VOXELFLAG_NO_DATA)
398 return MapNode(CONTENT_IGNORE);
401 return m_data[m_area.index(p)];
403 MapNode getNodeNoExNoEmerge(v3s16 p)
405 if(m_area.contains(p) == false)
406 return MapNode(CONTENT_IGNORE);
407 if(m_flags[m_area.index(p)] & VOXELFLAG_NO_DATA)
408 return MapNode(CONTENT_IGNORE);
409 return m_data[m_area.index(p)];
411 // Stuff explodes if non-emerged area is touched with this.
412 // Emerge first, and check VOXELFLAG_NO_DATA if appropriate.
413 MapNode & getNodeRefUnsafe(const v3s16 &p)
415 return m_data[m_area.index(p)];
418 const MapNode & getNodeRefUnsafeCheckFlags(const v3s16 &p)
420 s32 index = m_area.index(p);
422 if (m_flags[index] & VOXELFLAG_NO_DATA)
423 return ContentIgnoreNode;
425 return m_data[index];
428 u8 & getFlagsRefUnsafe(v3s16 p)
430 return m_flags[m_area.index(p)];
434 return m_area.contains(p) &&
435 !(getFlagsRefUnsafe(p) & VOXELFLAG_NO_DATA);
437 MapNode & getNodeRef(v3s16 p)
439 VoxelArea voxel_area(p);
441 if(getFlagsRefUnsafe(p) & VOXELFLAG_NO_DATA)
443 /*dstream<<"EXCEPT: VoxelManipulator::getNode(): "
444 <<"p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"
445 <<", index="<<m_area.index(p)
446 <<", flags="<<(int)getFlagsRefUnsafe(p)
447 <<" is inexistent"<<std::endl;*/
448 throw InvalidPositionException
449 ("VoxelManipulator: getNode: inexistent");
451 return getNodeRefUnsafe(p);
453 void setNode(v3s16 p, const MapNode &n)
455 VoxelArea voxel_area(p);
458 m_data[m_area.index(p)] = n;
459 m_flags[m_area.index(p)] &= ~VOXELFLAG_NO_DATA;
461 // TODO: Should be removed and replaced with setNode
462 void setNodeNoRef(v3s16 p, const MapNode &n)
467 /*void setExists(VoxelArea a)
470 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
471 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
472 for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
474 m_flags[m_area.index(x,y,z)] &= ~VOXELFLAG_NO_DATA;
478 /*MapNode & operator[](v3s16 p)
480 //dstream<<"operator[] p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;
481 if(isValidPosition(p) == false)
482 addArea(VoxelArea(p));
484 return m_data[m_area.index(p)];
488 Set stuff if available without an emerge.
489 Return false if failed.
490 This is convenient but slower than playing around directly
491 with the m_data table with indices.
493 bool setNodeNoEmerge(v3s16 p, MapNode n)
495 if(m_area.contains(p) == false)
497 m_data[m_area.index(p)] = n;
500 bool setNodeNoEmerge(s32 i, MapNode n)
502 if(m_area.contains(i) == false)
507 /*bool setContentNoEmerge(v3s16 p, u8 c)
509 if(isValidPosition(p) == false)
511 m_data[m_area.index(p)].d = c;
518 virtual void clear();
520 void print(std::ostream &o, INodeDefManager *nodemgr,
521 VoxelPrintMode mode=VOXELPRINT_MATERIAL);
523 void addArea(const VoxelArea &area);
526 Copy data and set flags to 0
527 dst_area.getExtent() <= src_area.getExtent()
529 void copyFrom(MapNode *src, const VoxelArea& src_area,
530 v3s16 from_pos, v3s16 to_pos, v3s16 size);
533 void copyTo(MapNode *dst, const VoxelArea& dst_area,
534 v3s16 dst_pos, v3s16 from_pos, v3s16 size);
540 void clearFlag(u8 flag);
542 // TODO: Move to voxelalgorithms.h
544 void unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
545 std::set<v3s16> & light_sources, INodeDefManager *nodemgr);
546 void unspreadLight(enum LightBank bank,
547 std::map<v3s16, u8> & from_nodes,
548 std::set<v3s16> & light_sources, INodeDefManager *nodemgr);
550 void spreadLight(enum LightBank bank, v3s16 p, INodeDefManager *nodemgr);
551 void spreadLight(enum LightBank bank,
552 std::set<v3s16> & from_nodes, INodeDefManager *nodemgr);
563 The area that is stored in m_data.
564 addInternalBox should not be used if getExtent() == v3s16(0,0,0)
565 MaxEdge is 1 higher than maximum allowed position
570 nullptr if data size is 0 (extent (0,0,0))
571 Data is stored as [z*h*w + y*h + x]
573 MapNode *m_data = nullptr;
578 u8 *m_flags = nullptr;
580 static const MapNode ContentIgnoreNode;