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.
22 #include "irrlichttypes.h"
26 #include "exceptions.h"
30 #include "util/basic_macros.h"
39 A fast voxel manipulator class.
41 In normal operation, it fetches more map when it is requested.
42 It can also be used so that all allowed area is fetched at the
43 start, using ManualMapVoxelManipulator.
51 extern u64 emerge_time;
52 extern u64 emerge_load_time;
55 This class resembles aabbox3d<s16> a lot, but has inclusive
56 edges for saner handling of integer sizes
61 // Starts as zero sized
62 VoxelArea() = default;
64 VoxelArea(const v3s16 &min_edge, const v3s16 &max_edge):
71 VoxelArea(const v3s16 &p):
82 void addArea(const VoxelArea &a)
89 if(a.MinEdge.X < MinEdge.X) MinEdge.X = a.MinEdge.X;
90 if(a.MinEdge.Y < MinEdge.Y) MinEdge.Y = a.MinEdge.Y;
91 if(a.MinEdge.Z < MinEdge.Z) MinEdge.Z = a.MinEdge.Z;
92 if(a.MaxEdge.X > MaxEdge.X) MaxEdge.X = a.MaxEdge.X;
93 if(a.MaxEdge.Y > MaxEdge.Y) MaxEdge.Y = a.MaxEdge.Y;
94 if(a.MaxEdge.Z > MaxEdge.Z) MaxEdge.Z = a.MaxEdge.Z;
98 void addPoint(const v3s16 &p)
107 if(p.X < MinEdge.X) MinEdge.X = p.X;
108 if(p.Y < MinEdge.Y) MinEdge.Y = p.Y;
109 if(p.Z < MinEdge.Z) MinEdge.Z = p.Z;
110 if(p.X > MaxEdge.X) MaxEdge.X = p.X;
111 if(p.Y > MaxEdge.Y) MaxEdge.Y = p.Y;
112 if(p.Z > MaxEdge.Z) MaxEdge.Z = p.Z;
117 void pad(const v3s16 &d)
127 const v3s16 &getExtent() const
129 return m_cache_extent;
132 /* Because MaxEdge and MinEdge are included in the voxel area an empty extent
133 * is not represented by (0, 0, 0), but instead (-1, -1, -1)
135 bool hasEmptyExtent() const
137 return MaxEdge - MinEdge == v3s16(-1, -1, -1);
140 s32 getVolume() const
142 return (s32)m_cache_extent.X * (s32)m_cache_extent.Y * (s32)m_cache_extent.Z;
145 bool contains(const VoxelArea &a) const
147 // No area contains an empty area
148 // NOTE: Algorithms depend on this, so do not change.
149 if(a.hasEmptyExtent())
153 a.MinEdge.X >= MinEdge.X && a.MaxEdge.X <= MaxEdge.X &&
154 a.MinEdge.Y >= MinEdge.Y && a.MaxEdge.Y <= MaxEdge.Y &&
155 a.MinEdge.Z >= MinEdge.Z && a.MaxEdge.Z <= MaxEdge.Z
158 bool contains(v3s16 p) const
161 p.X >= MinEdge.X && p.X <= MaxEdge.X &&
162 p.Y >= MinEdge.Y && p.Y <= MaxEdge.Y &&
163 p.Z >= MinEdge.Z && p.Z <= MaxEdge.Z
166 bool contains(s32 i) const
168 return (i >= 0 && i < getVolume());
170 bool operator==(const VoxelArea &other) const
172 return (MinEdge == other.MinEdge
173 && MaxEdge == other.MaxEdge);
176 VoxelArea operator+(const v3s16 &off) const
178 return {MinEdge+off, MaxEdge+off};
181 VoxelArea operator-(const v3s16 &off) const
183 return {MinEdge-off, MaxEdge-off};
187 Returns 0-6 non-overlapping areas that can be added to
188 a to make up this area.
192 void diff(const VoxelArea &a, std::list<VoxelArea> &result)
195 This can result in a maximum of 6 areas
198 // If a is an empty area, return the current area as a whole
199 if(a.getExtent() == v3s16(0,0,0))
202 if(b.getVolume() != 0)
207 assert(contains(a)); // pre-condition
209 // Take back area, XY inclusive
211 v3s16 min(MinEdge.X, MinEdge.Y, a.MaxEdge.Z+1);
212 v3s16 max(MaxEdge.X, MaxEdge.Y, MaxEdge.Z);
213 VoxelArea b(min, max);
214 if(b.getVolume() != 0)
218 // Take front area, XY inclusive
220 v3s16 min(MinEdge.X, MinEdge.Y, MinEdge.Z);
221 v3s16 max(MaxEdge.X, MaxEdge.Y, a.MinEdge.Z-1);
222 VoxelArea b(min, max);
223 if(b.getVolume() != 0)
227 // Take top area, X inclusive
229 v3s16 min(MinEdge.X, a.MaxEdge.Y+1, a.MinEdge.Z);
230 v3s16 max(MaxEdge.X, MaxEdge.Y, a.MaxEdge.Z);
231 VoxelArea b(min, max);
232 if(b.getVolume() != 0)
236 // Take bottom area, X inclusive
238 v3s16 min(MinEdge.X, MinEdge.Y, a.MinEdge.Z);
239 v3s16 max(MaxEdge.X, a.MinEdge.Y-1, a.MaxEdge.Z);
240 VoxelArea b(min, max);
241 if(b.getVolume() != 0)
245 // Take left area, non-inclusive
247 v3s16 min(MinEdge.X, a.MinEdge.Y, a.MinEdge.Z);
248 v3s16 max(a.MinEdge.X-1, a.MaxEdge.Y, a.MaxEdge.Z);
249 VoxelArea b(min, max);
250 if(b.getVolume() != 0)
254 // Take right area, non-inclusive
256 v3s16 min(a.MaxEdge.X+1, a.MinEdge.Y, a.MinEdge.Z);
257 v3s16 max(MaxEdge.X, a.MaxEdge.Y, a.MaxEdge.Z);
258 VoxelArea b(min, max);
259 if(b.getVolume() != 0)
266 Translates position from virtual coordinates to array index
268 s32 index(s16 x, s16 y, s16 z) const
270 s32 i = (s32)(z - MinEdge.Z) * m_cache_extent.Y * m_cache_extent.X
271 + (y - MinEdge.Y) * m_cache_extent.X
275 s32 index(v3s16 p) const
277 return index(p.X, p.Y, p.Z);
280 // Translate index in the X coordinate
281 void add_x(const v3s16 &extent, u32 &i, s16 a)
285 // Translate index in the Y coordinate
286 void add_y(const v3s16 &extent, u32 &i, s16 a)
290 // Translate index in the Z coordinate
291 void add_z(const v3s16 &extent, u32 &i, s16 a)
293 i += a * extent.X*extent.Y;
295 // Translate index in space
296 void add_p(const v3s16 &extent, u32 &i, v3s16 a)
298 i += a.Z*extent.X*extent.Y + a.Y*extent.X + a.X;
302 Print method for debugging
304 void print(std::ostream &o) const
306 o << PP(MinEdge) << PP(MaxEdge) << "="
307 << m_cache_extent.X << "x" << m_cache_extent.Y << "x" << m_cache_extent.Z
308 << "=" << getVolume();
311 // Edges are inclusive
312 v3s16 MinEdge = v3s16(1,1,1);
317 m_cache_extent = MaxEdge - MinEdge + v3s16(1,1,1);
320 v3s16 m_cache_extent = v3s16(0,0,0);
324 #define VOXELFLAG_UNUSED (1 << 0)
325 // no data about that node
326 #define VOXELFLAG_NO_DATA (1 << 1)
327 // Algorithm-dependent
328 #define VOXELFLAG_CHECKED1 (1 << 2)
329 // Algorithm-dependent
330 #define VOXELFLAG_CHECKED2 (1 << 3)
331 // Algorithm-dependent
332 #define VOXELFLAG_CHECKED3 (1 << 4)
333 // Algorithm-dependent
334 #define VOXELFLAG_CHECKED4 (1 << 5)
340 VOXELPRINT_WATERPRESSURE,
341 VOXELPRINT_LIGHT_DAY,
344 class VoxelManipulator
347 VoxelManipulator() = default;
348 virtual ~VoxelManipulator();
351 These are a bit slow and shouldn't be used internally.
352 Use m_data[m_area.index(p)] instead.
354 MapNode getNode(const v3s16 &p)
356 VoxelArea voxel_area(p);
359 if (m_flags[m_area.index(p)] & VOXELFLAG_NO_DATA) {
360 /*dstream<<"EXCEPT: VoxelManipulator::getNode(): "
361 <<"p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"
362 <<", index="<<m_area.index(p)
363 <<", flags="<<(int)m_flags[m_area.index(p)]
364 <<" is inexistent"<<std::endl;*/
365 throw InvalidPositionException
366 ("VoxelManipulator: getNode: inexistent");
369 return m_data[m_area.index(p)];
371 MapNode getNodeNoEx(const v3s16 &p)
373 VoxelArea voxel_area(p);
376 if (m_flags[m_area.index(p)] & VOXELFLAG_NO_DATA) {
377 return {CONTENT_IGNORE};
380 return m_data[m_area.index(p)];
382 MapNode getNodeNoExNoEmerge(const v3s16 &p)
384 if (!m_area.contains(p))
385 return {CONTENT_IGNORE};
386 if (m_flags[m_area.index(p)] & VOXELFLAG_NO_DATA)
387 return {CONTENT_IGNORE};
388 return m_data[m_area.index(p)];
390 // Stuff explodes if non-emerged area is touched with this.
391 // Emerge first, and check VOXELFLAG_NO_DATA if appropriate.
392 MapNode & getNodeRefUnsafe(const v3s16 &p)
394 return m_data[m_area.index(p)];
397 const MapNode & getNodeRefUnsafeCheckFlags(const v3s16 &p)
399 s32 index = m_area.index(p);
401 if (m_flags[index] & VOXELFLAG_NO_DATA)
402 return ContentIgnoreNode;
404 return m_data[index];
407 u8 & getFlagsRefUnsafe(const v3s16 &p)
409 return m_flags[m_area.index(p)];
412 bool exists(const v3s16 &p)
414 return m_area.contains(p) &&
415 !(getFlagsRefUnsafe(p) & VOXELFLAG_NO_DATA);
418 void setNode(const v3s16 &p, const MapNode &n)
420 VoxelArea voxel_area(p);
423 m_data[m_area.index(p)] = n;
424 m_flags[m_area.index(p)] &= ~VOXELFLAG_NO_DATA;
426 // TODO: Should be removed and replaced with setNode
427 void setNodeNoRef(const v3s16 &p, const MapNode &n)
433 Set stuff if available without an emerge.
434 Return false if failed.
435 This is convenient but slower than playing around directly
436 with the m_data table with indices.
438 bool setNodeNoEmerge(const v3s16 &p, MapNode n)
440 if(!m_area.contains(p))
442 m_data[m_area.index(p)] = n;
450 virtual void clear();
452 void print(std::ostream &o, const NodeDefManager *nodemgr,
453 VoxelPrintMode mode=VOXELPRINT_MATERIAL);
455 void addArea(const VoxelArea &area);
458 Copy data and set flags to 0
459 dst_area.getExtent() <= src_area.getExtent()
461 void copyFrom(MapNode *src, const VoxelArea& src_area,
462 v3s16 from_pos, v3s16 to_pos, const v3s16 &size);
465 void copyTo(MapNode *dst, const VoxelArea& dst_area,
466 v3s16 dst_pos, v3s16 from_pos, const v3s16 &size);
472 void clearFlag(u8 flag);
479 The area that is stored in m_data.
480 addInternalBox should not be used if getExtent() == v3s16(0,0,0)
481 MaxEdge is 1 higher than maximum allowed position
486 nullptr if data size is 0 (extent (0,0,0))
487 Data is stored as [z*h*w + y*h + x]
489 MapNode *m_data = nullptr;
494 u8 *m_flags = nullptr;
496 static const MapNode ContentIgnoreNode;