Luanti 5.15.0-dev
 
Loading...
Searching...
No Matches
voxel.h
Go to the documentation of this file.
1// Luanti
2// SPDX-License-Identifier: LGPL-2.1-or-later
3// Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5#pragma once
6
7#include "irrlichttypes.h"
8#include "irr_v3d.h"
9#include <iostream>
10#include <cassert>
11#include "exceptions.h"
12#include "mapnode.h"
14
15class NodeDefManager;
16
17// For VC++
18#undef min
19#undef max
20
21/*
22 A fast voxel manipulator class.
23
24 In normal operation, it fetches more map when it is requested.
25 It can also be used so that all allowed area is fetched at the
26 start, using ManualMapVoxelManipulator.
27
28 Not thread-safe.
29*/
30
31/*
32 Debug stuff
33*/
34extern u64 emerge_time;
35
36/*
37 This class resembles aabbox3d<s16> a lot, but has inclusive
38 edges for saner handling of integer sizes
39*/
41{
42public:
43 // Starts as zero sized
44 constexpr VoxelArea() = default;
45
46 VoxelArea(const v3s16 &min_edge, const v3s16 &max_edge):
47 MinEdge(min_edge),
48 MaxEdge(max_edge)
49 {
51 }
52
53 VoxelArea(const v3s16 &p):
54 MinEdge(p),
55 MaxEdge(p)
56 {
58 }
59
60 /*
61 Modifying methods
62 */
63
64 void addArea(const VoxelArea &a)
65 {
66 if (hasEmptyExtent())
67 {
68 *this = a;
69 return;
70 }
71 if(a.MinEdge.X < MinEdge.X) MinEdge.X = a.MinEdge.X;
72 if(a.MinEdge.Y < MinEdge.Y) MinEdge.Y = a.MinEdge.Y;
73 if(a.MinEdge.Z < MinEdge.Z) MinEdge.Z = a.MinEdge.Z;
74 if(a.MaxEdge.X > MaxEdge.X) MaxEdge.X = a.MaxEdge.X;
75 if(a.MaxEdge.Y > MaxEdge.Y) MaxEdge.Y = a.MaxEdge.Y;
76 if(a.MaxEdge.Z > MaxEdge.Z) MaxEdge.Z = a.MaxEdge.Z;
78 }
79
80 void addPoint(const v3s16 &p)
81 {
82 if(hasEmptyExtent())
83 {
84 MinEdge = p;
85 MaxEdge = p;
87 return;
88 }
89 if(p.X < MinEdge.X) MinEdge.X = p.X;
90 if(p.Y < MinEdge.Y) MinEdge.Y = p.Y;
91 if(p.Z < MinEdge.Z) MinEdge.Z = p.Z;
92 if(p.X > MaxEdge.X) MaxEdge.X = p.X;
93 if(p.Y > MaxEdge.Y) MaxEdge.Y = p.Y;
94 if(p.Z > MaxEdge.Z) MaxEdge.Z = p.Z;
96 }
97
98 // Pad with d nodes
99 void pad(const v3s16 &d)
100 {
101 MinEdge -= d;
102 MaxEdge += d;
103 cacheExtent();
104 }
105
106 /*
107 const methods
108 */
109
110 const v3s32 &getExtent() const
111 {
112 return m_cache_extent;
113 }
114
115 bool hasEmptyExtent() const
116 {
117 return !m_cache_extent.X || !m_cache_extent.Y || !m_cache_extent.Z;
118 }
119
120 u32 getVolume() const
121 {
122 // FIXME: possible integer overflow here
123 return (u32)m_cache_extent.X * (u32)m_cache_extent.Y * (u32)m_cache_extent.Z;
124 }
125
126 bool contains(const VoxelArea &a) const
127 {
128 // No area contains an empty area
129 // NOTE: Algorithms depend on this, so do not change.
130 if(a.hasEmptyExtent())
131 return false;
132
133 return(
134 a.MinEdge.X >= MinEdge.X && a.MaxEdge.X <= MaxEdge.X &&
135 a.MinEdge.Y >= MinEdge.Y && a.MaxEdge.Y <= MaxEdge.Y &&
136 a.MinEdge.Z >= MinEdge.Z && a.MaxEdge.Z <= MaxEdge.Z
137 );
138 }
139 bool contains(v3s16 p) const
140 {
141 return(
142 p.X >= MinEdge.X && p.X <= MaxEdge.X &&
143 p.Y >= MinEdge.Y && p.Y <= MaxEdge.Y &&
144 p.Z >= MinEdge.Z && p.Z <= MaxEdge.Z
145 );
146 }
147 bool contains(s32 i) const
148 {
149 return i >= 0 && static_cast<u32>(i) < getVolume();
150 }
151
152 bool operator==(const VoxelArea &other) const
153 {
154 return (MinEdge == other.MinEdge
155 && MaxEdge == other.MaxEdge);
156 }
157
158 VoxelArea operator+(const v3s16 &off) const
159 {
160 return {MinEdge+off, MaxEdge+off};
161 }
162
163 VoxelArea operator-(const v3s16 &off) const
164 {
165 return {MinEdge-off, MaxEdge-off};
166 }
167
168 /*
169 Returns the intersection of this area and `a`.
170 */
172 {
173 // This is an example of an operation that would be simpler with
174 // non-inclusive edges, but oh well.
175 VoxelArea ret;
176
177 if (a.MaxEdge.X < MinEdge.X || a.MinEdge.X > MaxEdge.X)
178 return VoxelArea();
179 if (a.MaxEdge.Y < MinEdge.Y || a.MinEdge.Y > MaxEdge.Y)
180 return VoxelArea();
181 if (a.MaxEdge.Z < MinEdge.Z || a.MinEdge.Z > MaxEdge.Z)
182 return VoxelArea();
183 ret.MinEdge.X = std::max(a.MinEdge.X, MinEdge.X);
184 ret.MaxEdge.X = std::min(a.MaxEdge.X, MaxEdge.X);
185 ret.MinEdge.Y = std::max(a.MinEdge.Y, MinEdge.Y);
186 ret.MaxEdge.Y = std::min(a.MaxEdge.Y, MaxEdge.Y);
187 ret.MinEdge.Z = std::max(a.MinEdge.Z, MinEdge.Z);
188 ret.MaxEdge.Z = std::min(a.MaxEdge.Z, MaxEdge.Z);
189 ret.cacheExtent();
190
191 return ret;
192 }
193
201 template <typename C>
202 void diff(const VoxelArea &a, C &result) const
203 {
204 // If a is an empty area, return the current area as a whole
205 if(a.hasEmptyExtent())
206 {
207 VoxelArea b = *this;
208 if (!b.hasEmptyExtent())
209 result.push_back(b);
210 return;
211 }
212
213 assert(contains(a)); // pre-condition
214
215 const auto &take = [&result] (v3s16 min, v3s16 max) {
216 VoxelArea b(min, max);
217 if (!b.hasEmptyExtent())
218 result.push_back(b);
219 };
220
221 // Take back area, XY inclusive
222 {
223 v3s16 min(MinEdge.X, MinEdge.Y, a.MaxEdge.Z+1);
224 v3s16 max(MaxEdge.X, MaxEdge.Y, MaxEdge.Z);
225 take(min, max);
226 }
227
228 // Take front area, XY inclusive
229 {
230 v3s16 min(MinEdge.X, MinEdge.Y, MinEdge.Z);
231 v3s16 max(MaxEdge.X, MaxEdge.Y, a.MinEdge.Z-1);
232 take(min, max);
233 }
234
235 // Take top area, X inclusive
236 {
237 v3s16 min(MinEdge.X, a.MaxEdge.Y+1, a.MinEdge.Z);
238 v3s16 max(MaxEdge.X, MaxEdge.Y, a.MaxEdge.Z);
239 take(min, max);
240 }
241
242 // Take bottom area, X inclusive
243 {
244 v3s16 min(MinEdge.X, MinEdge.Y, a.MinEdge.Z);
245 v3s16 max(MaxEdge.X, a.MinEdge.Y-1, a.MaxEdge.Z);
246 take(min, max);
247 }
248
249 // Take left area, non-inclusive
250 {
251 v3s16 min(MinEdge.X, a.MinEdge.Y, a.MinEdge.Z);
252 v3s16 max(a.MinEdge.X-1, a.MaxEdge.Y, a.MaxEdge.Z);
253 take(min, max);
254 }
255
256 // Take right area, non-inclusive
257 {
258 v3s16 min(a.MaxEdge.X+1, a.MinEdge.Y, a.MinEdge.Z);
259 v3s16 max(MaxEdge.X, a.MaxEdge.Y, a.MaxEdge.Z);
260 take(min, max);
261 }
262 }
263
264 /*
265 Translates position from virtual coordinates to array index
266 */
267 s32 index(s16 x, s16 y, s16 z) const
268 {
269 s32 i = (s32)(z - MinEdge.Z) * m_cache_extent.Y * m_cache_extent.X
270 + (y - MinEdge.Y) * m_cache_extent.X
271 + (x - MinEdge.X);
272 return i;
273 }
274 s32 index(v3s16 p) const
275 {
276 return index(p.X, p.Y, p.Z);
277 }
278
282 static void add_x(const v3s32 &extent, u32 &i, s16 a)
283 {
284 (void)extent;
285 i += a;
286 }
287
291 static void add_y(const v3s32 &extent, u32 &i, s16 a)
292 {
293 i += a * extent.X;
294 }
295
299 static void add_z(const v3s32 &extent, u32 &i, s16 a)
300 {
301 i += a * extent.X * extent.Y;
302 }
303
307 static void add_p(const v3s32 &extent, u32 &i, v3s16 a)
308 {
309 i += a.Z * extent.X * extent.Y + a.Y * extent.X + a.X;
310 }
311
312 /*
313 Print method for debugging
314 */
315 void print(std::ostream &o) const
316 {
317 o << MinEdge << MaxEdge << "="
318 << m_cache_extent.X << "x" << m_cache_extent.Y << "x" << m_cache_extent.Z
319 << "=" << getVolume();
320 }
321
328
329private:
331 {
333 MaxEdge.X - MinEdge.X + 1,
334 MaxEdge.Y - MinEdge.Y + 1,
335 MaxEdge.Z - MinEdge.Z + 1
336 };
337 // If positions were sorted correctly this must always hold.
338 // Note that this still permits empty areas (where MinEdge = MaxEdge + 1).
339 assert(m_cache_extent.X >= 0 && m_cache_extent.X <= MAX_EXTENT);
340 assert(m_cache_extent.Y >= 0 && m_cache_extent.Y <= MAX_EXTENT);
341 assert(m_cache_extent.Z >= 0 && m_cache_extent.Z <= MAX_EXTENT);
342 }
343
344 static constexpr s32 MAX_EXTENT = S16_MAX - S16_MIN + 1;
346};
347
348enum : u8 {
349 VOXELFLAG_NO_DATA = 1 << 0, // no data about that node
350 VOXELFLAG_CHECKED1 = 1 << 1, // Algorithm-dependent
351 VOXELFLAG_CHECKED2 = 1 << 2, // Algorithm-dependent
352 VOXELFLAG_CHECKED3 = 1 << 3, // Algorithm-dependent
353 VOXELFLAG_CHECKED4 = 1 << 4, // Algorithm-dependent
354};
355
363
365{
366public:
367 VoxelManipulator() = default;
368 virtual ~VoxelManipulator();
369
370 /*
371 These are a bit slow and shouldn't be used internally.
372 Use m_data[m_area.index(p)] instead.
373 */
375 {
376 VoxelArea voxel_area(p);
377 addArea(voxel_area);
378
379 const s32 index = m_area.index(p);
380
381 if (m_flags[index] & VOXELFLAG_NO_DATA) {
383 ("VoxelManipulator: getNode: inexistent");
384 }
385
386 return m_data[index];
387 }
389 {
390 VoxelArea voxel_area(p);
391 addArea(voxel_area);
392
393 const s32 index = m_area.index(p);
394
395 if (m_flags[index] & VOXELFLAG_NO_DATA) {
396 return {CONTENT_IGNORE};
397 }
398
399 return m_data[index];
400 }
402 {
403 if (!m_area.contains(p))
404 return {CONTENT_IGNORE};
405 const s32 index = m_area.index(p);
406 if (m_flags[index] & VOXELFLAG_NO_DATA)
407 return {CONTENT_IGNORE};
408 return m_data[index];
409 }
410 // Stuff explodes if non-emerged area is touched with this.
411 // Emerge first, and check VOXELFLAG_NO_DATA if appropriate.
413 {
414 return m_data[m_area.index(p)];
415 }
416
418 {
419 s32 index = m_area.index(p);
420
421 if (m_flags[index] & VOXELFLAG_NO_DATA)
422 return ContentIgnoreNode;
423
424 return m_data[index];
425 }
426
428 {
429 return m_flags[m_area.index(p)];
430 }
431
432 bool exists(const v3s16 &p)
433 {
434 return m_area.contains(p) &&
436 }
437
438 void setNode(const v3s16 &p, const MapNode &n)
439 {
440 VoxelArea voxel_area(p);
441 addArea(voxel_area);
442
443 const s32 index = m_area.index(p);
444
445 m_data[index] = n;
446 m_flags[index] &= ~VOXELFLAG_NO_DATA;
447 }
448
449 /*
450 Set stuff if available without an emerge.
451 Return false if failed.
452 This is convenient but slower than playing around directly
453 with the m_data table with indices.
454 */
456 {
457 if(!m_area.contains(p))
458 return false;
459 const s32 index = m_area.index(p);
460 m_data[index] = n;
461 m_flags[index] &= ~VOXELFLAG_NO_DATA;
462 return true;
463 }
464
465 /*
466 Control
467 */
468
469 void clear();
470
471 void print(std::ostream &o, const NodeDefManager *nodemgr,
473
474 void addArea(const VoxelArea &area);
475
476 void setFlags(const VoxelArea &area, u8 flag);
477 void clearFlags(const VoxelArea &area, u8 flag);
478
479 /*
480 Copy data and set flags to 0
481 dst_area.getExtent() <= src_area.getExtent()
482 */
483 void copyFrom(MapNode *src, bool is_mono_block, const VoxelArea& src_area,
484 v3s16 from_pos, v3s16 to_pos, const v3s16 &size);
485
486 // Copy data
487 void copyTo(MapNode *dst, const VoxelArea& dst_area,
488 v3s16 dst_pos, v3s16 from_pos, const v3s16 &size) const;
489
490 /*
491 Member variables
492 */
493
494 /*
495 The area that is stored in m_data.
496 MaxEdge is 1 higher than maximum allowed position.
497 */
499
500 /*
501 nullptr if data size is 0 (empty extent)
502 Data is stored as [z*h*w + y*h + x]
503 */
504 MapNode *m_data = nullptr;
505
506 /*
507 Flags of all nodes
508 */
509 u8 *m_flags = nullptr;
510
512};
Definition exceptions.h:108
This class is for getting the actual properties of nodes from their content ID.
Definition nodedef.h:509
Definition voxel.h:41
v3s16 MinEdge
Minimum edge of the area (inclusive)
Definition voxel.h:324
void addArea(const VoxelArea &a)
Definition voxel.h:64
bool contains(s32 i) const
Definition voxel.h:147
VoxelArea intersect(const VoxelArea &a) const
Definition voxel.h:171
static void add_p(const v3s32 &extent, u32 &i, v3s16 a)
Translate index in space.
Definition voxel.h:307
static constexpr s32 MAX_EXTENT
Definition voxel.h:344
s32 index(s16 x, s16 y, s16 z) const
Definition voxel.h:267
VoxelArea(const v3s16 &min_edge, const v3s16 &max_edge)
Definition voxel.h:46
const v3s32 & getExtent() const
Definition voxel.h:110
static void add_z(const v3s32 &extent, u32 &i, s16 a)
Translate index in the Z coordinate.
Definition voxel.h:299
u32 getVolume() const
Definition voxel.h:120
VoxelArea operator-(const v3s16 &off) const
Definition voxel.h:163
bool contains(const VoxelArea &a) const
Definition voxel.h:126
static void add_y(const v3s32 &extent, u32 &i, s16 a)
Translate index in the Y coordinate.
Definition voxel.h:291
void cacheExtent()
Definition voxel.h:330
void addPoint(const v3s16 &p)
Definition voxel.h:80
v3s16 MaxEdge
Maximum edge of the area (inclusive)
Definition voxel.h:327
static void add_x(const v3s32 &extent, u32 &i, s16 a)
Translate index in the X coordinate.
Definition voxel.h:282
VoxelArea operator+(const v3s16 &off) const
Definition voxel.h:158
bool operator==(const VoxelArea &other) const
Definition voxel.h:152
s32 index(v3s16 p) const
Definition voxel.h:274
bool hasEmptyExtent() const
Definition voxel.h:115
void print(std::ostream &o) const
Definition voxel.h:315
void diff(const VoxelArea &a, C &result) const
Returns 0-6 non-overlapping areas that can be added to a to make up this area.
Definition voxel.h:202
void pad(const v3s16 &d)
Definition voxel.h:99
bool contains(v3s16 p) const
Definition voxel.h:139
v3s32 m_cache_extent
Definition voxel.h:345
constexpr VoxelArea()=default
VoxelArea(const v3s16 &p)
Definition voxel.h:53
Definition voxel.h:365
MapNode * m_data
Definition voxel.h:504
MapNode getNodeNoEx(const v3s16 &p)
Definition voxel.h:388
void copyFrom(MapNode *src, bool is_mono_block, const VoxelArea &src_area, v3s16 from_pos, v3s16 to_pos, const v3s16 &size)
Definition voxel.cpp:179
void print(std::ostream &o, const NodeDefManager *nodemgr, VoxelPrintMode mode=VOXELPRINT_MATERIAL) const
Definition voxel.cpp:36
const MapNode & getNodeRefUnsafeCheckFlags(const v3s16 &p) const
Definition voxel.h:417
u8 * m_flags
Definition voxel.h:509
void setFlags(const VoxelArea &area, u8 flag)
Definition voxel.cpp:253
void copyTo(MapNode *dst, const VoxelArea &dst_area, v3s16 dst_pos, v3s16 from_pos, const v3s16 &size) const
Definition voxel.cpp:231
void setNode(const v3s16 &p, const MapNode &n)
Definition voxel.h:438
VoxelManipulator()=default
MapNode getNodeNoExNoEmerge(const v3s16 &p) const
Definition voxel.h:401
static const MapNode ContentIgnoreNode
Definition voxel.h:511
bool setNodeNoEmerge(const v3s16 &p, MapNode n)
Definition voxel.h:455
void addArea(const VoxelArea &area)
Definition voxel.cpp:126
bool exists(const v3s16 &p)
Definition voxel.h:432
u8 & getFlagsRefUnsafe(const v3s16 &p)
Definition voxel.h:427
void clearFlags(const VoxelArea &area, u8 flag)
Definition voxel.cpp:270
VoxelArea m_area
Definition voxel.h:498
MapNode & getNodeRefUnsafe(const v3s16 &p)
Definition voxel.h:412
void clear()
Definition voxel.cpp:23
virtual ~VoxelManipulator()
Definition voxel.cpp:18
MapNode getNode(const v3s16 &p)
Definition voxel.h:374
core::vector3d< s32 > v3s32
Definition irr_v3d.h:15
core::vector3d< s16 > v3s16
Definition irr_v3d.h:13
#define S16_MAX
Definition irrlichttypes.h:17
#define S16_MIN
Definition irrlichttypes.h:12
#define CONTENT_IGNORE
Definition mapnode.h:57
Definition mapnode.h:123
static std::string p(std::string path)
Definition test_filesys.cpp:64
constexpr v3f x
Definition test_irr_matrix4.cpp:18
constexpr v3f y
Definition test_irr_matrix4.cpp:19
constexpr v3f z
Definition test_irr_matrix4.cpp:20
@ VOXELFLAG_NO_DATA
Definition voxel.h:349
@ VOXELFLAG_CHECKED3
Definition voxel.h:352
@ VOXELFLAG_CHECKED4
Definition voxel.h:353
@ VOXELFLAG_CHECKED1
Definition voxel.h:350
@ VOXELFLAG_CHECKED2
Definition voxel.h:351
VoxelPrintMode
Definition voxel.h:357
@ VOXELPRINT_WATERPRESSURE
Definition voxel.h:360
@ VOXELPRINT_NOTHING
Definition voxel.h:358
@ VOXELPRINT_LIGHT_DAY
Definition voxel.h:361
@ VOXELPRINT_MATERIAL
Definition voxel.h:359
u64 emerge_time
Definition voxel.cpp:16