commit before content-tile separation
[oweals/minetest.git] / src / voxel.h
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
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.
9
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.
14
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.
18 */
19
20 #ifndef VOXEL_HEADER
21 #define VOXEL_HEADER
22
23 #include "common_irrlicht.h"
24 #include <iostream>
25 #include "debug.h"
26 #include "mapnode.h"
27
28 // For VC++
29 #undef min
30 #undef max
31
32 /*
33         A fast voxel manipulator class
34
35         Not thread-safe.
36 */
37
38 /*
39         Debug stuff
40 */
41 extern u32 emerge_time;
42 extern u32 emerge_load_time;
43
44 /*
45         This class resembles aabbox3d<s16> a lot, but has inclusive
46         edges for saner handling of integer sizes
47 */
48 class VoxelArea
49 {
50 public:
51         // Starts as zero sized
52         VoxelArea():
53                 MinEdge(1,1,1),
54                 MaxEdge(0,0,0)
55         {
56         }
57         VoxelArea(v3s16 min_edge, v3s16 max_edge):
58                 MinEdge(min_edge),
59                 MaxEdge(max_edge)
60         {
61         }
62         VoxelArea(v3s16 p):
63                 MinEdge(p),
64                 MaxEdge(p)
65         {
66         }
67         
68         /*
69                 Modifying methods
70         */
71
72         void addArea(VoxelArea &a)
73         {
74                 if(getExtent() == v3s16(0,0,0))
75                 {
76                         *this = a;
77                         return;
78                 }
79                 if(a.MinEdge.X < MinEdge.X) MinEdge.X = a.MinEdge.X;
80                 if(a.MinEdge.Y < MinEdge.Y) MinEdge.Y = a.MinEdge.Y;
81                 if(a.MinEdge.Z < MinEdge.Z) MinEdge.Z = a.MinEdge.Z;
82                 if(a.MaxEdge.X > MaxEdge.X) MaxEdge.X = a.MaxEdge.X;
83                 if(a.MaxEdge.Y > MaxEdge.Y) MaxEdge.Y = a.MaxEdge.Y;
84                 if(a.MaxEdge.Z > MaxEdge.Z) MaxEdge.Z = a.MaxEdge.Z;
85         }
86         void addPoint(v3s16 p)
87         {
88                 if(getExtent() == v3s16(0,0,0))
89                 {
90                         MinEdge = p;
91                         MaxEdge = p;
92                         return;
93                 }
94                 if(p.X < MinEdge.X) MinEdge.X = p.X;
95                 if(p.Y < MinEdge.Y) MinEdge.Y = p.Y;
96                 if(p.Z < MinEdge.Z) MinEdge.Z = p.Z;
97                 if(p.X > MaxEdge.X) MaxEdge.X = p.X;
98                 if(p.Y > MaxEdge.Y) MaxEdge.Y = p.Y;
99                 if(p.Z > MaxEdge.Z) MaxEdge.Z = p.Z;
100         }
101         
102         // Pad with d nodes
103         void pad(v3s16 d)
104         {
105                 MinEdge -= d;
106                 MaxEdge += d;
107         }
108         
109         /*void operator+=(v3s16 off)
110         {
111                 MinEdge += off;
112                 MaxEdge += off;
113         }
114
115         void operator-=(v3s16 off)
116         {
117                 MinEdge -= off;
118                 MaxEdge -= off;
119         }*/
120
121         /*
122                 const methods
123         */
124
125         v3s16 getExtent() const
126         {
127                 return MaxEdge - MinEdge + v3s16(1,1,1);
128         }
129         s32 getVolume() const
130         {
131                 v3s16 e = getExtent();
132                 return (s32)e.X * (s32)e.Y * (s32)e.Z;
133         }
134         bool contains(const VoxelArea &a) const
135         {
136                 // No area contains an empty area
137                 // NOTE: Algorithms depend on this, so do not change.
138                 if(a.getExtent() == v3s16(0,0,0))
139                         return false;
140
141                 return(
142                         a.MinEdge.X >= MinEdge.X && a.MaxEdge.X <= MaxEdge.X &&
143                         a.MinEdge.Y >= MinEdge.Y && a.MaxEdge.Y <= MaxEdge.Y &&
144                         a.MinEdge.Z >= MinEdge.Z && a.MaxEdge.Z <= MaxEdge.Z
145                 );
146         }
147         bool contains(v3s16 p) const
148         {
149                 return(
150                         p.X >= MinEdge.X && p.X <= MaxEdge.X &&
151                         p.Y >= MinEdge.Y && p.Y <= MaxEdge.Y &&
152                         p.Z >= MinEdge.Z && p.Z <= MaxEdge.Z
153                 );
154         }
155         bool operator==(const VoxelArea &other) const
156         {
157                 return (MinEdge == other.MinEdge
158                                 && MaxEdge == other.MaxEdge);
159         }
160
161         VoxelArea operator+(v3s16 off) const
162         {
163                 return VoxelArea(MinEdge+off, MaxEdge+off);
164         }
165
166         VoxelArea operator-(v3s16 off) const
167         {
168                 return VoxelArea(MinEdge-off, MaxEdge-off);
169         }
170
171         /*
172                 Returns 0-6 non-overlapping areas that can be added to
173                 a to make up this area.
174
175                 a: area inside *this
176         */
177         void diff(const VoxelArea &a, core::list<VoxelArea> &result)
178         {
179                 /*
180                         This can result in a maximum of 6 areas
181                 */
182
183                 // If a is an empty area, return the current area as a whole
184                 if(a.getExtent() == v3s16(0,0,0))
185                 {
186                         VoxelArea b = *this;
187                         if(b.getVolume() != 0)
188                                 result.push_back(b);
189                         return;
190                 }
191
192                 assert(contains(a));
193                 
194                 // Take back area, XY inclusive
195                 {
196                         v3s16 min(MinEdge.X, MinEdge.Y, a.MaxEdge.Z+1);
197                         v3s16 max(MaxEdge.X, MaxEdge.Y, MaxEdge.Z);
198                         VoxelArea b(min, max);
199                         if(b.getVolume() != 0)
200                                 result.push_back(b);
201                 }
202
203                 // Take front area, XY inclusive
204                 {
205                         v3s16 min(MinEdge.X, MinEdge.Y, MinEdge.Z);
206                         v3s16 max(MaxEdge.X, MaxEdge.Y, a.MinEdge.Z-1);
207                         VoxelArea b(min, max);
208                         if(b.getVolume() != 0)
209                                 result.push_back(b);
210                 }
211
212                 // Take top area, X inclusive
213                 {
214                         v3s16 min(MinEdge.X, a.MaxEdge.Y+1, a.MinEdge.Z);
215                         v3s16 max(MaxEdge.X, MaxEdge.Y, a.MaxEdge.Z);
216                         VoxelArea b(min, max);
217                         if(b.getVolume() != 0)
218                                 result.push_back(b);
219                 }
220
221                 // Take bottom area, X inclusive
222                 {
223                         v3s16 min(MinEdge.X, MinEdge.Y, a.MinEdge.Z);
224                         v3s16 max(MaxEdge.X, a.MinEdge.Y-1, a.MaxEdge.Z);
225                         VoxelArea b(min, max);
226                         if(b.getVolume() != 0)
227                                 result.push_back(b);
228                 }
229
230                 // Take left area, non-inclusive
231                 {
232                         v3s16 min(MinEdge.X, a.MinEdge.Y, a.MinEdge.Z);
233                         v3s16 max(a.MinEdge.X-1, a.MaxEdge.Y, a.MaxEdge.Z);
234                         VoxelArea b(min, max);
235                         if(b.getVolume() != 0)
236                                 result.push_back(b);
237                 }
238
239                 // Take right area, non-inclusive
240                 {
241                         v3s16 min(a.MaxEdge.X+1, a.MinEdge.Y, a.MinEdge.Z);
242                         v3s16 max(MaxEdge.X, a.MaxEdge.Y, a.MaxEdge.Z);
243                         VoxelArea b(min, max);
244                         if(b.getVolume() != 0)
245                                 result.push_back(b);
246                 }
247
248         }
249         
250         /*
251                 Translates position from virtual coordinates to array index
252         */
253         s32 index(s16 x, s16 y, s16 z) const
254         {
255                 v3s16 em = getExtent();
256                 v3s16 off = MinEdge;
257                 s32 i = (s32)(z-off.Z)*em.Y*em.X + (y-off.Y)*em.X + (x-off.X);
258                 //dstream<<" i("<<x<<","<<y<<","<<z<<")="<<i<<" ";
259                 return i;
260         }
261         s32 index(v3s16 p) const
262         {
263                 return index(p.X, p.Y, p.Z);
264         }
265
266         void print(std::ostream &o) const
267         {
268                 v3s16 e = getExtent();
269                 o<<"("<<MinEdge.X
270                  <<","<<MinEdge.Y
271                  <<","<<MinEdge.Z
272                  <<")("<<MaxEdge.X
273                  <<","<<MaxEdge.Y
274                  <<","<<MaxEdge.Z
275                  <<")"
276                  <<"="<<e.X<<"x"<<e.Y<<"x"<<e.Z<<"="<<getVolume();
277         }
278
279         // Edges are inclusive
280         v3s16 MinEdge;
281         v3s16 MaxEdge;
282 };
283
284 // Hasn't been copied from source (emerged)
285 #define VOXELFLAG_NOT_LOADED (1<<0)
286 // Checked as being inexistent in source
287 #define VOXELFLAG_INEXISTENT (1<<1)
288 // Algorithm-dependent
289 // flowWater: "visited"
290 #define VOXELFLAG_CHECKED (1<<2)
291 // Algorithm-dependent
292 // getWaterPressure: "visited"
293 #define VOXELFLAG_CHECKED2 (1<<3)
294 // Algorithm-dependent
295 // spreadWaterPressure: "visited"
296 #define VOXELFLAG_CHECKED3 (1<<4)
297 // Algorithm-dependent
298 // water: "pressure check route node"
299 #define VOXELFLAG_CHECKED4 (1<<5)
300
301 enum VoxelPrintMode
302 {
303         VOXELPRINT_NOTHING,
304         VOXELPRINT_MATERIAL,
305         VOXELPRINT_WATERPRESSURE,
306 };
307
308 class VoxelManipulator /*: public NodeContainer*/
309 {
310 public:
311         VoxelManipulator();
312         virtual ~VoxelManipulator();
313         
314         /*
315                 Virtuals from NodeContainer
316         */
317         /*virtual u16 nodeContainerId() const
318         {
319                 return NODECONTAINER_ID_VOXELMANIPULATOR;
320         }
321         bool isValidPosition(v3s16 p)
322         {
323                 emerge(p);
324                 return !(m_flags[m_area.index(p)] & VOXELFLAG_INEXISTENT);
325         }*/
326         // These are a bit slow and shouldn't be used internally
327         MapNode getNode(v3s16 p)
328         {
329                 emerge(p);
330
331                 if(m_flags[m_area.index(p)] & VOXELFLAG_INEXISTENT)
332                 {
333                         dstream<<"EXCEPT: VoxelManipulator::getNode(): "
334                                         <<"p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"
335                                         <<", index="<<m_area.index(p)
336                                         <<", flags="<<(int)m_flags[m_area.index(p)]
337                                         <<" is inexistent"<<std::endl;
338                         throw InvalidPositionException
339                         ("VoxelManipulator: getNode: inexistent");
340                 }
341
342                 return m_data[m_area.index(p)];
343         }
344         void setNode(v3s16 p, MapNode &n)
345         {
346                 emerge(p);
347                 
348                 m_data[m_area.index(p)] = n;
349                 m_flags[m_area.index(p)] &= ~VOXELFLAG_INEXISTENT;
350                 m_flags[m_area.index(p)] &= ~VOXELFLAG_NOT_LOADED;
351         }
352         void setNodeNoRef(v3s16 p, MapNode n)
353         {
354                 setNode(p, n);
355         }
356
357         /*void setExists(VoxelArea a)
358         {
359                 emerge(a);
360                 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
361                 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
362                 for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
363                 {
364                         m_flags[m_area.index(x,y,z)] &= ~VOXELFLAG_INEXISTENT;
365                 }
366         }*/
367
368         /*MapNode & operator[](v3s16 p)
369         {
370                 //dstream<<"operator[] p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;
371                 if(isValidPosition(p) == false)
372                         emerge(VoxelArea(p));
373                 
374                 return m_data[m_area.index(p)];
375         }*/
376
377         /*
378                 Control
379         */
380
381         virtual void clear();
382
383         void print(std::ostream &o, VoxelPrintMode mode=VOXELPRINT_MATERIAL);
384         
385         void addArea(VoxelArea area);
386
387         /*
388                 Copy data and set flags to 0
389                 dst_area.getExtent() <= src_area.getExtent()
390         */
391         void copyFrom(MapNode *src, VoxelArea src_area,
392                         v3s16 from_pos, v3s16 to_pos, v3s16 size);
393
394         /*
395                 Algorithms
396         */
397
398         void interpolate(VoxelArea area);
399
400         void clearFlag(u8 flag);
401         
402         // VOXELFLAG_CHECKED2s must usually be cleared before calling
403         // -1: dead end, 0-255: pressure
404         // highest_y: Highest found water y is stored here.
405         //            Must be initialized to -32768
406         int getWaterPressure(v3s16 p, s16 &highest_y, int recur_count);
407
408         /*
409                 VOXELFLAG_CHECKED3s must usually be cleared before calling.
410
411                 active_nodes: surface-touching air nodes with flow-causing
412                 pressure. set-like dummy map container.
413
414                 Spreads pressure pr at node p to request_area or as far as
415                 there is invalid pressure.
416         */
417         void spreadWaterPressure(v3s16 p, int pr,
418                         VoxelArea request_area,
419                         core::map<v3s16, u8> &active_nodes,
420                         int recur_count);
421         
422         /*
423                 VOXELFLAG_CHECKED3s must usually be cleared before calling.
424         */
425         void updateAreaWaterPressure(VoxelArea a,
426                         core::map<v3s16, u8> &active_nodes,
427                         bool checked3_is_clear=false);
428         
429         /*
430                 Returns true if moved something
431         */
432         bool flowWater(v3s16 removed_pos,
433                         core::map<v3s16, u8> &active_nodes,
434                         int recursion_depth=0,
435                         bool debugprint=false,
436                         u32 stoptime=0
437         );
438
439         /*
440                 To flow some water, call this with the target node in
441                 active_nodes
442                 TODO: Make the active_nodes map to contain some vectors
443                       that are properly sorted according to water flow order.
444                           The current order makes water flow strangely if the
445                           first one is always taken.
446                           No, active_nodes should preserve the order stuff is
447                           added to it, in addition to adhering the water flow
448                           order.
449         */
450         void flowWater(core::map<v3s16, u8> &active_nodes,
451                         int recursion_depth=0,
452                         bool debugprint=false,
453                         u32 timelimit=50
454         );
455
456         /*
457                 Virtual functions
458         */
459         
460         /*
461                 Get the contents of the requested area from somewhere.
462                 Shall touch only nodes that have VOXELFLAG_NOT_LOADED
463                 Shall reset VOXELFLAG_NOT_LOADED
464
465                 If not found from source, add with VOXELFLAG_INEXISTENT
466         */
467         virtual void emerge(VoxelArea a, s32 caller_id=-1)
468         {
469                 //dstream<<"emerge p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;
470                 addArea(a);
471                 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
472                 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
473                 for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
474                 {
475                         s32 i = m_area.index(x,y,z);
476                         // Don't touch nodes that have already been loaded
477                         if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
478                                 continue;
479                         m_flags[i] = VOXELFLAG_INEXISTENT;
480                 }
481         }
482
483         /*
484                 Member variables
485         */
486
487         /*
488                 The area that is stored in m_data.
489                 addInternalBox should not be used if getExtent() == v3s16(0,0,0)
490                 MaxEdge is 1 higher than maximum allowed position
491         */
492         VoxelArea m_area;
493         
494         /*
495                 NULL if data size is 0 (extent (0,0,0))
496                 Data is stored as [z*h*w + y*h + x]
497         */
498         MapNode *m_data;
499
500         /*
501                 Flags of all nodes
502         */
503         u8 *m_flags;
504         
505         //TODO: Use these or remove them
506         //TODO: Would these make any speed improvement?
507         //bool m_pressure_route_valid;
508         //v3s16 m_pressure_route_surface;
509
510         /*
511                 Some settings
512         */
513         bool m_disable_water_climb;
514
515 private:
516 };
517
518 #endif
519