]> git.lizzy.rs Git - dragonfireclient.git/blob - src/voxel.h
old water removed, some fixes here and there
[dragonfireclient.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 clearFlag(u8 flag);
399         
400         // VOXELFLAG_CHECKED2s must usually be cleared before calling
401         // -1: dead end, 0-255: pressure
402         // highest_y: Highest found water y is stored here.
403         //            Must be initialized to -32768
404         int getWaterPressure(v3s16 p, s16 &highest_y, int recur_count);
405
406         /*
407                 VOXELFLAG_CHECKED3s must usually be cleared before calling.
408
409                 active_nodes: surface-touching air nodes with flow-causing
410                 pressure. set-like dummy map container.
411
412                 Spreads pressure pr at node p to request_area or as far as
413                 there is invalid pressure.
414         */
415         void spreadWaterPressure(v3s16 p, int pr,
416                         VoxelArea request_area,
417                         core::map<v3s16, u8> &active_nodes,
418                         int recur_count);
419         
420         /*
421                 VOXELFLAG_CHECKED3s must usually be cleared before calling.
422         */
423         void updateAreaWaterPressure(VoxelArea a,
424                         core::map<v3s16, u8> &active_nodes,
425                         bool checked3_is_clear=false);
426         
427         /*
428                 Returns true if moved something
429         */
430         bool flowWater(v3s16 removed_pos,
431                         core::map<v3s16, u8> &active_nodes,
432                         int recursion_depth=0,
433                         bool debugprint=false,
434                         u32 stoptime=0
435         );
436
437         /*
438                 To flow some water, call this with the target node in
439                 active_nodes
440                 TODO: Make the active_nodes map to contain some vectors
441                       that are properly sorted according to water flow order.
442                           The current order makes water flow strangely if the
443                           first one is always taken.
444                           No, active_nodes should preserve the order stuff is
445                           added to it, in addition to adhering the water flow
446                           order.
447         */
448         void flowWater(core::map<v3s16, u8> &active_nodes,
449                         int recursion_depth=0,
450                         bool debugprint=false,
451                         u32 timelimit=50
452         );
453
454         /*
455                 Virtual functions
456         */
457         
458         /*
459                 Get the contents of the requested area from somewhere.
460                 Shall touch only nodes that have VOXELFLAG_NOT_LOADED
461                 Shall reset VOXELFLAG_NOT_LOADED
462
463                 If not found from source, add with VOXELFLAG_INEXISTENT
464         */
465         virtual void emerge(VoxelArea a, s32 caller_id=-1)
466         {
467                 //dstream<<"emerge p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;
468                 addArea(a);
469                 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
470                 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
471                 for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
472                 {
473                         s32 i = m_area.index(x,y,z);
474                         // Don't touch nodes that have already been loaded
475                         if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
476                                 continue;
477                         m_flags[i] = VOXELFLAG_INEXISTENT;
478                 }
479         }
480
481         /*
482                 Member variables
483         */
484
485         /*
486                 The area that is stored in m_data.
487                 addInternalBox should not be used if getExtent() == v3s16(0,0,0)
488                 MaxEdge is 1 higher than maximum allowed position
489         */
490         VoxelArea m_area;
491         
492         /*
493                 NULL if data size is 0 (extent (0,0,0))
494                 Data is stored as [z*h*w + y*h + x]
495         */
496         MapNode *m_data;
497
498         /*
499                 Flags of all nodes
500         */
501         u8 *m_flags;
502         
503         //TODO: Use these or remove them
504         //TODO: Would these make any speed improvement?
505         //bool m_pressure_route_valid;
506         //v3s16 m_pressure_route_surface;
507
508         /*
509                 Some settings
510         */
511         bool m_disable_water_climb;
512
513 private:
514 };
515
516 #endif
517