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"
31 #include "util/basic_macros.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(const v3s16 &min_edge, const v3s16 &max_edge):
72 VoxelArea(const v3s16 &p):
83 void addArea(const VoxelArea &a)
90 if(a.MinEdge.X < MinEdge.X) MinEdge.X = a.MinEdge.X;
91 if(a.MinEdge.Y < MinEdge.Y) MinEdge.Y = a.MinEdge.Y;
92 if(a.MinEdge.Z < MinEdge.Z) MinEdge.Z = a.MinEdge.Z;
93 if(a.MaxEdge.X > MaxEdge.X) MaxEdge.X = a.MaxEdge.X;
94 if(a.MaxEdge.Y > MaxEdge.Y) MaxEdge.Y = a.MaxEdge.Y;
95 if(a.MaxEdge.Z > MaxEdge.Z) MaxEdge.Z = a.MaxEdge.Z;
99 void addPoint(const v3s16 &p)
108 if(p.X < MinEdge.X) MinEdge.X = p.X;
109 if(p.Y < MinEdge.Y) MinEdge.Y = p.Y;
110 if(p.Z < MinEdge.Z) MinEdge.Z = p.Z;
111 if(p.X > MaxEdge.X) MaxEdge.X = p.X;
112 if(p.Y > MaxEdge.Y) MaxEdge.Y = p.Y;
113 if(p.Z > MaxEdge.Z) MaxEdge.Z = p.Z;
118 void pad(const v3s16 &d)
128 const v3s16 &getExtent() const
130 return m_cache_extent;
133 /* Because MaxEdge and MinEdge are included in the voxel area an empty extent
134 * is not represented by (0, 0, 0), but instead (-1, -1, -1)
136 bool hasEmptyExtent() const
138 return MaxEdge - MinEdge == v3s16(-1, -1, -1);
141 s32 getVolume() const
143 return (s32)m_cache_extent.X * (s32)m_cache_extent.Y * (s32)m_cache_extent.Z;
146 bool contains(const VoxelArea &a) const
148 // No area contains an empty area
149 // NOTE: Algorithms depend on this, so do not change.
150 if(a.hasEmptyExtent())
154 a.MinEdge.X >= MinEdge.X && a.MaxEdge.X <= MaxEdge.X &&
155 a.MinEdge.Y >= MinEdge.Y && a.MaxEdge.Y <= MaxEdge.Y &&
156 a.MinEdge.Z >= MinEdge.Z && a.MaxEdge.Z <= MaxEdge.Z
159 bool contains(v3s16 p) const
162 p.X >= MinEdge.X && p.X <= MaxEdge.X &&
163 p.Y >= MinEdge.Y && p.Y <= MaxEdge.Y &&
164 p.Z >= MinEdge.Z && p.Z <= MaxEdge.Z
167 bool contains(s32 i) const
169 return (i >= 0 && i < getVolume());
171 bool operator==(const VoxelArea &other) const
173 return (MinEdge == other.MinEdge
174 && MaxEdge == other.MaxEdge);
177 VoxelArea operator+(const v3s16 &off) const
179 return VoxelArea(MinEdge+off, MaxEdge+off);
182 VoxelArea operator-(const v3s16 &off) const
184 return VoxelArea(MinEdge-off, MaxEdge-off);
188 Returns 0-6 non-overlapping areas that can be added to
189 a to make up this area.
193 void diff(const VoxelArea &a, std::list<VoxelArea> &result)
196 This can result in a maximum of 6 areas
199 // If a is an empty area, return the current area as a whole
200 if(a.getExtent() == v3s16(0,0,0))
203 if(b.getVolume() != 0)
208 assert(contains(a)); // pre-condition
210 // Take back area, XY inclusive
212 v3s16 min(MinEdge.X, MinEdge.Y, a.MaxEdge.Z+1);
213 v3s16 max(MaxEdge.X, MaxEdge.Y, MaxEdge.Z);
214 VoxelArea b(min, max);
215 if(b.getVolume() != 0)
219 // Take front area, XY inclusive
221 v3s16 min(MinEdge.X, MinEdge.Y, MinEdge.Z);
222 v3s16 max(MaxEdge.X, MaxEdge.Y, a.MinEdge.Z-1);
223 VoxelArea b(min, max);
224 if(b.getVolume() != 0)
228 // Take top area, X inclusive
230 v3s16 min(MinEdge.X, a.MaxEdge.Y+1, a.MinEdge.Z);
231 v3s16 max(MaxEdge.X, MaxEdge.Y, a.MaxEdge.Z);
232 VoxelArea b(min, max);
233 if(b.getVolume() != 0)
237 // Take bottom area, X inclusive
239 v3s16 min(MinEdge.X, MinEdge.Y, a.MinEdge.Z);
240 v3s16 max(MaxEdge.X, a.MinEdge.Y-1, a.MaxEdge.Z);
241 VoxelArea b(min, max);
242 if(b.getVolume() != 0)
246 // Take left area, non-inclusive
248 v3s16 min(MinEdge.X, a.MinEdge.Y, a.MinEdge.Z);
249 v3s16 max(a.MinEdge.X-1, a.MaxEdge.Y, a.MaxEdge.Z);
250 VoxelArea b(min, max);
251 if(b.getVolume() != 0)
255 // Take right area, non-inclusive
257 v3s16 min(a.MaxEdge.X+1, a.MinEdge.Y, a.MinEdge.Z);
258 v3s16 max(MaxEdge.X, a.MaxEdge.Y, a.MaxEdge.Z);
259 VoxelArea b(min, max);
260 if(b.getVolume() != 0)
267 Translates position from virtual coordinates to array index
269 s32 index(s16 x, s16 y, s16 z) const
271 s32 i = (s32)(z - MinEdge.Z) * m_cache_extent.Y * m_cache_extent.X
272 + (y - MinEdge.Y) * m_cache_extent.X
276 s32 index(v3s16 p) const
278 return index(p.X, p.Y, p.Z);
281 // Translate index in the X coordinate
282 void add_x(const v3s16 &extent, u32 &i, s16 a)
286 // Translate index in the Y coordinate
287 void add_y(const v3s16 &extent, u32 &i, s16 a)
291 // Translate index in the Z coordinate
292 void add_z(const v3s16 &extent, u32 &i, s16 a)
294 i += a * extent.X*extent.Y;
296 // Translate index in space
297 void add_p(const v3s16 &extent, u32 &i, v3s16 a)
299 i += a.Z*extent.X*extent.Y + a.Y*extent.X + a.X;
303 Print method for debugging
305 void print(std::ostream &o) const
307 o << PP(MinEdge) << PP(MaxEdge) << "="
308 << m_cache_extent.X << "x" << m_cache_extent.Y << "x" << m_cache_extent.Z
309 << "=" << getVolume();
312 // Edges are inclusive
313 v3s16 MinEdge = v3s16(1,1,1);
318 m_cache_extent = MaxEdge - MinEdge + v3s16(1,1,1);
321 v3s16 m_cache_extent = v3s16(0,0,0);
325 #define VOXELFLAG_UNUSED (1 << 0)
326 // no data about that node
327 #define VOXELFLAG_NO_DATA (1 << 1)
328 // Algorithm-dependent
329 #define VOXELFLAG_CHECKED1 (1 << 2)
330 // Algorithm-dependent
331 #define VOXELFLAG_CHECKED2 (1 << 3)
332 // Algorithm-dependent
333 #define VOXELFLAG_CHECKED3 (1 << 4)
334 // Algorithm-dependent
335 #define VOXELFLAG_CHECKED4 (1 << 5)
341 VOXELPRINT_WATERPRESSURE,
342 VOXELPRINT_LIGHT_DAY,
345 class VoxelManipulator
349 virtual ~VoxelManipulator();
352 These are a bit slow and shouldn't be used internally.
353 Use m_data[m_area.index(p)] instead.
355 MapNode getNode(const v3s16 &p)
357 VoxelArea voxel_area(p);
360 if (m_flags[m_area.index(p)] & VOXELFLAG_NO_DATA) {
361 /*dstream<<"EXCEPT: VoxelManipulator::getNode(): "
362 <<"p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"
363 <<", index="<<m_area.index(p)
364 <<", flags="<<(int)m_flags[m_area.index(p)]
365 <<" is inexistent"<<std::endl;*/
366 throw InvalidPositionException
367 ("VoxelManipulator: getNode: inexistent");
370 return m_data[m_area.index(p)];
372 MapNode getNodeNoEx(const v3s16 &p)
374 VoxelArea voxel_area(p);
377 if (m_flags[m_area.index(p)] & VOXELFLAG_NO_DATA) {
378 return MapNode(CONTENT_IGNORE);
381 return m_data[m_area.index(p)];
383 MapNode getNodeNoExNoEmerge(const v3s16 &p)
385 if (!m_area.contains(p))
386 return MapNode(CONTENT_IGNORE);
387 if (m_flags[m_area.index(p)] & VOXELFLAG_NO_DATA)
388 return MapNode(CONTENT_IGNORE);
389 return m_data[m_area.index(p)];
391 // Stuff explodes if non-emerged area is touched with this.
392 // Emerge first, and check VOXELFLAG_NO_DATA if appropriate.
393 MapNode & getNodeRefUnsafe(const v3s16 &p)
395 return m_data[m_area.index(p)];
398 const MapNode & getNodeRefUnsafeCheckFlags(const v3s16 &p)
400 s32 index = m_area.index(p);
402 if (m_flags[index] & VOXELFLAG_NO_DATA)
403 return ContentIgnoreNode;
405 return m_data[index];
408 u8 & getFlagsRefUnsafe(const v3s16 &p)
410 return m_flags[m_area.index(p)];
413 bool exists(const v3s16 &p)
415 return m_area.contains(p) &&
416 !(getFlagsRefUnsafe(p) & VOXELFLAG_NO_DATA);
419 void setNode(const v3s16 &p, const MapNode &n)
421 VoxelArea voxel_area(p);
424 m_data[m_area.index(p)] = n;
425 m_flags[m_area.index(p)] &= ~VOXELFLAG_NO_DATA;
427 // TODO: Should be removed and replaced with setNode
428 void setNodeNoRef(const v3s16 &p, const MapNode &n)
434 Set stuff if available without an emerge.
435 Return false if failed.
436 This is convenient but slower than playing around directly
437 with the m_data table with indices.
439 bool setNodeNoEmerge(const v3s16 &p, MapNode n)
441 if(!m_area.contains(p))
443 m_data[m_area.index(p)] = n;
451 virtual void clear();
453 void print(std::ostream &o, INodeDefManager *nodemgr,
454 VoxelPrintMode mode=VOXELPRINT_MATERIAL);
456 void addArea(const VoxelArea &area);
459 Copy data and set flags to 0
460 dst_area.getExtent() <= src_area.getExtent()
462 void copyFrom(MapNode *src, const VoxelArea& src_area,
463 v3s16 from_pos, v3s16 to_pos, const v3s16 &size);
466 void copyTo(MapNode *dst, const VoxelArea& dst_area,
467 v3s16 dst_pos, v3s16 from_pos, const v3s16 &size);
473 void clearFlag(u8 flag);
475 // TODO: Move to voxelalgorithms.h
477 void unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
478 std::set<v3s16> & light_sources, INodeDefManager *nodemgr);
480 void spreadLight(enum LightBank bank, v3s16 p, INodeDefManager *nodemgr);
481 void spreadLight(enum LightBank bank,
482 std::set<v3s16> & from_nodes, INodeDefManager *nodemgr);
489 The area that is stored in m_data.
490 addInternalBox should not be used if getExtent() == v3s16(0,0,0)
491 MaxEdge is 1 higher than maximum allowed position
496 nullptr if data size is 0 (extent (0,0,0))
497 Data is stored as [z*h*w + y*h + x]
499 MapNode *m_data = nullptr;
504 u8 *m_flags = nullptr;
506 static const MapNode ContentIgnoreNode;