3 Copyright (C) 2010 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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
15 You should have received a copy of the GNU 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 "common_irrlicht.h"
32 A fast voxel manipulator class
40 extern u32 emerge_time;
41 extern u32 emerge_load_time;
44 This class resembles aabbox3d<s16> a lot, but has inclusive
45 edges for saner handling of integer sizes
50 // Starts as zero sized
56 VoxelArea(v3s16 min_edge, v3s16 max_edge):
71 void addArea(VoxelArea &a)
73 if(getExtent() == v3s16(0,0,0))
78 if(a.MinEdge.X < MinEdge.X) MinEdge.X = a.MinEdge.X;
79 if(a.MinEdge.Y < MinEdge.Y) MinEdge.Y = a.MinEdge.Y;
80 if(a.MinEdge.Z < MinEdge.Z) MinEdge.Z = a.MinEdge.Z;
81 if(a.MaxEdge.X > MaxEdge.X) MaxEdge.X = a.MaxEdge.X;
82 if(a.MaxEdge.Y > MaxEdge.Y) MaxEdge.Y = a.MaxEdge.Y;
83 if(a.MaxEdge.Z > MaxEdge.Z) MaxEdge.Z = a.MaxEdge.Z;
85 void addPoint(v3s16 p)
87 if(getExtent() == v3s16(0,0,0))
93 if(p.X < MinEdge.X) MinEdge.X = p.X;
94 if(p.Y < MinEdge.Y) MinEdge.Y = p.Y;
95 if(p.Z < MinEdge.Z) MinEdge.Z = p.Z;
96 if(p.X > MaxEdge.X) MaxEdge.X = p.X;
97 if(p.Y > MaxEdge.Y) MaxEdge.Y = p.Y;
98 if(p.Z > MaxEdge.Z) MaxEdge.Z = p.Z;
108 /*void operator+=(v3s16 off)
114 void operator-=(v3s16 off)
124 v3s16 getExtent() const
126 return MaxEdge - MinEdge + v3s16(1,1,1);
128 s32 getVolume() const
130 v3s16 e = getExtent();
131 return (s32)e.X * (s32)e.Y * (s32)e.Z;
133 bool contains(const VoxelArea &a) const
135 // No area contains an empty area
136 // NOTE: Algorithms depend on this, so do not change.
137 if(a.getExtent() == v3s16(0,0,0))
141 a.MinEdge.X >= MinEdge.X && a.MaxEdge.X <= MaxEdge.X &&
142 a.MinEdge.Y >= MinEdge.Y && a.MaxEdge.Y <= MaxEdge.Y &&
143 a.MinEdge.Z >= MinEdge.Z && a.MaxEdge.Z <= MaxEdge.Z
146 bool contains(v3s16 p) const
149 p.X >= MinEdge.X && p.X <= MaxEdge.X &&
150 p.Y >= MinEdge.Y && p.Y <= MaxEdge.Y &&
151 p.Z >= MinEdge.Z && p.Z <= MaxEdge.Z
154 bool operator==(const VoxelArea &other) const
156 return (MinEdge == other.MinEdge
157 && MaxEdge == other.MaxEdge);
160 VoxelArea operator+(v3s16 off) const
162 return VoxelArea(MinEdge+off, MaxEdge+off);
165 VoxelArea operator-(v3s16 off) const
167 return VoxelArea(MinEdge-off, MaxEdge-off);
171 Returns 0-6 non-overlapping areas that can be added to
172 a to make up this area.
176 void diff(const VoxelArea &a, core::list<VoxelArea> &result)
179 This can result in a maximum of 6 areas
182 // If a is an empty area, return the current area as a whole
183 if(a.getExtent() == v3s16(0,0,0))
186 if(b.getVolume() != 0)
193 // Take back area, XY inclusive
195 v3s16 min(MinEdge.X, MinEdge.Y, a.MaxEdge.Z+1);
196 v3s16 max(MaxEdge.X, MaxEdge.Y, MaxEdge.Z);
197 VoxelArea b(min, max);
198 if(b.getVolume() != 0)
202 // Take front area, XY inclusive
204 v3s16 min(MinEdge.X, MinEdge.Y, MinEdge.Z);
205 v3s16 max(MaxEdge.X, MaxEdge.Y, a.MinEdge.Z-1);
206 VoxelArea b(min, max);
207 if(b.getVolume() != 0)
211 // Take top area, X inclusive
213 v3s16 min(MinEdge.X, a.MaxEdge.Y+1, a.MinEdge.Z);
214 v3s16 max(MaxEdge.X, MaxEdge.Y, a.MaxEdge.Z);
215 VoxelArea b(min, max);
216 if(b.getVolume() != 0)
220 // Take bottom area, X inclusive
222 v3s16 min(MinEdge.X, MinEdge.Y, a.MinEdge.Z);
223 v3s16 max(MaxEdge.X, a.MinEdge.Y-1, a.MaxEdge.Z);
224 VoxelArea b(min, max);
225 if(b.getVolume() != 0)
229 // Take left area, non-inclusive
231 v3s16 min(MinEdge.X, a.MinEdge.Y, a.MinEdge.Z);
232 v3s16 max(a.MinEdge.X-1, a.MaxEdge.Y, a.MaxEdge.Z);
233 VoxelArea b(min, max);
234 if(b.getVolume() != 0)
238 // Take right area, non-inclusive
240 v3s16 min(a.MaxEdge.X+1, a.MinEdge.Y, a.MinEdge.Z);
241 v3s16 max(MaxEdge.X, a.MaxEdge.Y, a.MaxEdge.Z);
242 VoxelArea b(min, max);
243 if(b.getVolume() != 0)
250 Translates position from virtual coordinates to array index
252 s32 index(s16 x, s16 y, s16 z) const
254 v3s16 em = getExtent();
256 s32 i = (s32)(z-off.Z)*em.Y*em.X + (y-off.Y)*em.X + (x-off.X);
257 //dstream<<" i("<<x<<","<<y<<","<<z<<")="<<i<<" ";
260 s32 index(v3s16 p) const
262 return index(p.X, p.Y, p.Z);
265 void print(std::ostream &o) const
267 v3s16 e = getExtent();
275 <<"="<<e.X<<"x"<<e.Y<<"x"<<e.Z<<"="<<getVolume();
278 // Edges are inclusive
283 // Hasn't been copied from source (emerged)
284 #define VOXELFLAG_NOT_LOADED (1<<0)
285 // Checked as being inexistent in source
286 #define VOXELFLAG_INEXISTENT (1<<1)
287 // Algorithm-dependent
288 // flowWater: "visited"
289 #define VOXELFLAG_CHECKED (1<<2)
290 // Algorithm-dependent
291 // getWaterPressure: "visited"
292 #define VOXELFLAG_CHECKED2 (1<<3)
293 // Algorithm-dependent
294 // spreadWaterPressure: "visited"
295 #define VOXELFLAG_CHECKED3 (1<<4)
296 // Algorithm-dependent
297 // water: "pressure check route node"
298 #define VOXELFLAG_CHECKED4 (1<<5)
304 VOXELPRINT_WATERPRESSURE,
307 class VoxelManipulator /*: public NodeContainer*/
311 virtual ~VoxelManipulator();
314 Virtuals from NodeContainer
316 /*virtual u16 nodeContainerId() const
318 return NODECONTAINER_ID_VOXELMANIPULATOR;
320 bool isValidPosition(v3s16 p)
323 return !(m_flags[m_area.index(p)] & VOXELFLAG_INEXISTENT);
325 // These are a bit slow and shouldn't be used internally
326 MapNode getNode(v3s16 p)
330 if(m_flags[m_area.index(p)] & VOXELFLAG_INEXISTENT)
332 dstream<<"EXCEPT: VoxelManipulator::getNode(): "
333 <<"p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"
334 <<", index="<<m_area.index(p)
335 <<", flags="<<(int)m_flags[m_area.index(p)]
336 <<" is inexistent"<<std::endl;
337 throw InvalidPositionException
338 ("VoxelManipulator: getNode: inexistent");
341 return m_data[m_area.index(p)];
343 void setNode(v3s16 p, MapNode &n)
347 m_data[m_area.index(p)] = n;
348 m_flags[m_area.index(p)] &= ~VOXELFLAG_INEXISTENT;
349 m_flags[m_area.index(p)] &= ~VOXELFLAG_NOT_LOADED;
351 void setNodeNoRef(v3s16 p, MapNode n)
356 /*void setExists(VoxelArea a)
359 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
360 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
361 for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
363 m_flags[m_area.index(x,y,z)] &= ~VOXELFLAG_INEXISTENT;
367 /*MapNode & operator[](v3s16 p)
369 //dstream<<"operator[] p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;
370 if(isValidPosition(p) == false)
371 emerge(VoxelArea(p));
373 return m_data[m_area.index(p)];
380 virtual void clear();
382 void print(std::ostream &o, VoxelPrintMode mode=VOXELPRINT_MATERIAL);
384 void addArea(VoxelArea area);
387 Copy data and set flags to 0
388 dst_area.getExtent() <= src_area.getExtent()
390 void copyFrom(MapNode *src, VoxelArea src_area,
391 v3s16 from_pos, v3s16 to_pos, v3s16 size);
397 void interpolate(VoxelArea area);
399 void clearFlag(u8 flag);
401 // VOXELFLAG_CHECKED2s must usually be cleared before calling
402 // -1: dead end, 0-255: pressure
403 // highest_y: Highest found water y is stored here.
404 // Must be initialized to -32768
405 int getWaterPressure(v3s16 p, s16 &highest_y, int recur_count);
408 VOXELFLAG_CHECKED3s must usually be cleared before calling.
410 active_nodes: surface-touching air nodes with flow-causing
411 pressure. set-like dummy map container.
413 Spreads pressure pr at node p to request_area or as far as
414 there is invalid pressure.
416 void spreadWaterPressure(v3s16 p, int pr,
417 VoxelArea request_area,
418 core::map<v3s16, u8> &active_nodes,
422 VOXELFLAG_CHECKED3s must usually be cleared before calling.
424 void updateAreaWaterPressure(VoxelArea a,
425 core::map<v3s16, u8> &active_nodes,
426 bool checked3_is_clear=false);
429 Returns true if moved something
431 bool flowWater(v3s16 removed_pos,
432 core::map<v3s16, u8> &active_nodes,
433 int recursion_depth=0,
434 bool debugprint=false,
439 To flow some water, call this with the target node in
441 TODO: Make the active_nodes map to contain some vectors
442 that are properly sorted according to water flow order.
443 The current order makes water flow strangely if the
444 first one is always taken.
445 No, active_nodes should preserve the order stuff is
446 added to it, in addition to adhering the water flow
449 void flowWater(core::map<v3s16, u8> &active_nodes,
450 int recursion_depth=0,
451 bool debugprint=false,
460 Get the contents of the requested area from somewhere.
461 Shall touch only nodes that have VOXELFLAG_NOT_LOADED
462 Shall reset VOXELFLAG_NOT_LOADED
464 If not found from source, add with VOXELFLAG_INEXISTENT
466 virtual void emerge(VoxelArea a, s32 caller_id=-1)
468 //dstream<<"emerge p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;
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 s32 i = m_area.index(x,y,z);
475 // Don't touch nodes that have already been loaded
476 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
478 m_flags[i] = VOXELFLAG_INEXISTENT;
487 The area that is stored in m_data.
488 addInternalBox should not be used if getExtent() == v3s16(0,0,0)
489 MaxEdge is 1 higher than maximum allowed position
494 NULL if data size is 0 (extent (0,0,0))
495 Data is stored as [z*h*w + y*h + x]
504 //TODO: Use these or remove them
505 //TODO: Would these make any speed improvement?
506 //bool m_pressure_route_valid;
507 //v3s16 m_pressure_route_surface;
512 bool m_disable_water_climb;