]> git.lizzy.rs Git - minetest.git/blob - src/mapblock.h
Occlusion culling algorithm based on recursive descend (#13104)
[minetest.git] / src / mapblock.h
1 /*
2 Minetest
3 Copyright (C) 2013 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 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.
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 Lesser General Public License for more details.
14
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.
18 */
19
20 #pragma once
21
22 #include <set>
23 #include "irr_v3d.h"
24 #include "mapnode.h"
25 #include "exceptions.h"
26 #include "constants.h"
27 #include "staticobject.h"
28 #include "nodemetadata.h"
29 #include "nodetimer.h"
30 #include "modifiedstate.h"
31 #include "util/numeric.h" // getContainerPos
32 #include "settings.h"
33 #include "mapgen/mapgen.h"
34
35 class Map;
36 class NodeMetadataList;
37 class IGameDef;
38 class MapBlockMesh;
39 class VoxelManipulator;
40
41 #define BLOCK_TIMESTAMP_UNDEFINED 0xffffffff
42
43 ////
44 //// MapBlock modified reason flags
45 ////
46
47 #define MOD_REASON_INITIAL                   (1 << 0)
48 #define MOD_REASON_REALLOCATE                (1 << 1)
49 #define MOD_REASON_SET_IS_UNDERGROUND        (1 << 2)
50 #define MOD_REASON_SET_LIGHTING_COMPLETE     (1 << 3)
51 #define MOD_REASON_SET_GENERATED             (1 << 4)
52 #define MOD_REASON_SET_NODE                  (1 << 5)
53 #define MOD_REASON_SET_NODE_NO_CHECK         (1 << 6)
54 #define MOD_REASON_SET_TIMESTAMP             (1 << 7)
55 #define MOD_REASON_REPORT_META_CHANGE        (1 << 8)
56 #define MOD_REASON_CLEAR_ALL_OBJECTS         (1 << 9)
57 #define MOD_REASON_BLOCK_EXPIRED             (1 << 10)
58 #define MOD_REASON_ADD_ACTIVE_OBJECT_RAW     (1 << 11)
59 #define MOD_REASON_REMOVE_OBJECTS_REMOVE     (1 << 12)
60 #define MOD_REASON_REMOVE_OBJECTS_DEACTIVATE (1 << 13)
61 #define MOD_REASON_TOO_MANY_OBJECTS          (1 << 14)
62 #define MOD_REASON_STATIC_DATA_ADDED         (1 << 15)
63 #define MOD_REASON_STATIC_DATA_REMOVED       (1 << 16)
64 #define MOD_REASON_STATIC_DATA_CHANGED       (1 << 17)
65 #define MOD_REASON_EXPIRE_DAYNIGHTDIFF       (1 << 18)
66 #define MOD_REASON_VMANIP                    (1 << 19)
67 #define MOD_REASON_UNKNOWN                   (1 << 20)
68
69 ////
70 //// MapBlock itself
71 ////
72
73 class MapBlock
74 {
75 public:
76         MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef);
77         ~MapBlock();
78
79         /*virtual u16 nodeContainerId() const
80         {
81                 return NODECONTAINER_ID_MAPBLOCK;
82         }*/
83
84         Map * getParent()
85         {
86                 return m_parent;
87         }
88
89         void reallocate()
90         {
91                 for (u32 i = 0; i < nodecount; i++)
92                         data[i] = MapNode(CONTENT_IGNORE);
93                 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_REALLOCATE);
94         }
95
96         MapNode* getData()
97         {
98                 return data;
99         }
100
101         ////
102         //// Modification tracking methods
103         ////
104         void raiseModified(u32 mod, u32 reason=MOD_REASON_UNKNOWN)
105         {
106                 if (mod > m_modified) {
107                         m_modified = mod;
108                         m_modified_reason = reason;
109                         if (m_modified >= MOD_STATE_WRITE_AT_UNLOAD)
110                                 m_disk_timestamp = m_timestamp;
111                 } else if (mod == m_modified) {
112                         m_modified_reason |= reason;
113                 }
114                 if (mod == MOD_STATE_WRITE_NEEDED)
115                         contents_cached = false;
116         }
117
118         inline u32 getModified()
119         {
120                 return m_modified;
121         }
122
123         inline u32 getModifiedReason()
124         {
125                 return m_modified_reason;
126         }
127
128         std::string getModifiedReasonString();
129
130         inline void resetModified()
131         {
132                 m_modified = MOD_STATE_CLEAN;
133                 m_modified_reason = 0;
134         }
135
136         ////
137         //// Flags
138         ////
139
140         // is_underground getter/setter
141         inline bool getIsUnderground()
142         {
143                 return is_underground;
144         }
145
146         inline void setIsUnderground(bool a_is_underground)
147         {
148                 is_underground = a_is_underground;
149                 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_IS_UNDERGROUND);
150         }
151
152         inline void setLightingComplete(u16 newflags)
153         {
154                 if (newflags != m_lighting_complete) {
155                         m_lighting_complete = newflags;
156                         raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_LIGHTING_COMPLETE);
157                 }
158         }
159
160         inline u16 getLightingComplete()
161         {
162                 return m_lighting_complete;
163         }
164
165         inline void setLightingComplete(LightBank bank, u8 direction,
166                 bool is_complete)
167         {
168                 assert(direction >= 0 && direction <= 5);
169                 if (bank == LIGHTBANK_NIGHT) {
170                         direction += 6;
171                 }
172                 u16 newflags = m_lighting_complete;
173                 if (is_complete) {
174                         newflags |= 1 << direction;
175                 } else {
176                         newflags &= ~(1 << direction);
177                 }
178                 setLightingComplete(newflags);
179         }
180
181         inline bool isLightingComplete(LightBank bank, u8 direction)
182         {
183                 assert(direction >= 0 && direction <= 5);
184                 if (bank == LIGHTBANK_NIGHT) {
185                         direction += 6;
186                 }
187                 return (m_lighting_complete & (1 << direction)) != 0;
188         }
189
190         inline bool isGenerated()
191         {
192                 return m_generated;
193         }
194
195         inline void setGenerated(bool b)
196         {
197                 if (b != m_generated) {
198                         raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_GENERATED);
199                         m_generated = b;
200                 }
201         }
202
203         ////
204         //// Position stuff
205         ////
206
207         inline v3s16 getPos()
208         {
209                 return m_pos;
210         }
211
212         inline v3s16 getPosRelative()
213         {
214                 return m_pos_relative;
215         }
216
217         inline core::aabbox3d<s16> getBox()
218         {
219                 return core::aabbox3d<s16>(getPosRelative(),
220                                 getPosRelative()
221                                 + v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE)
222                                 - v3s16(1,1,1));
223         }
224
225         ////
226         //// Regular MapNode get-setters
227         ////
228
229         inline bool isValidPosition(s16 x, s16 y, s16 z)
230         {
231                 return x >= 0 && x < MAP_BLOCKSIZE
232                         && y >= 0 && y < MAP_BLOCKSIZE
233                         && z >= 0 && z < MAP_BLOCKSIZE;
234         }
235
236         inline bool isValidPosition(v3s16 p)
237         {
238                 return isValidPosition(p.X, p.Y, p.Z);
239         }
240
241         inline MapNode getNode(s16 x, s16 y, s16 z, bool *valid_position)
242         {
243                 *valid_position = isValidPosition(x, y, z);
244
245                 if (!*valid_position)
246                         return {CONTENT_IGNORE};
247
248                 return data[z * zstride + y * ystride + x];
249         }
250
251         inline MapNode getNode(v3s16 p, bool *valid_position)
252         {
253                 return getNode(p.X, p.Y, p.Z, valid_position);
254         }
255
256         inline MapNode getNodeNoEx(v3s16 p)
257         {
258                 bool is_valid;
259                 return getNode(p.X, p.Y, p.Z, &is_valid);
260         }
261
262         inline void setNode(s16 x, s16 y, s16 z, MapNode n)
263         {
264                 if (!isValidPosition(x, y, z))
265                         throw InvalidPositionException();
266
267                 data[z * zstride + y * ystride + x] = n;
268                 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE);
269         }
270
271         inline void setNode(v3s16 p, MapNode n)
272         {
273                 setNode(p.X, p.Y, p.Z, n);
274         }
275
276         ////
277         //// Non-checking variants of the above
278         ////
279
280         inline MapNode getNodeNoCheck(s16 x, s16 y, s16 z)
281         {
282                 return data[z * zstride + y * ystride + x];
283         }
284
285         inline MapNode getNodeNoCheck(v3s16 p)
286         {
287                 return getNodeNoCheck(p.X, p.Y, p.Z);
288         }
289
290         inline void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode n)
291         {
292                 data[z * zstride + y * ystride + x] = n;
293                 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE_NO_CHECK);
294         }
295
296         inline void setNodeNoCheck(v3s16 p, MapNode n)
297         {
298                 setNodeNoCheck(p.X, p.Y, p.Z, n);
299         }
300
301         // These functions consult the parent container if the position
302         // is not valid on this MapBlock.
303         bool isValidPositionParent(v3s16 p);
304         MapNode getNodeParent(v3s16 p, bool *is_valid_position = NULL);
305
306         // Copies data to VoxelManipulator to getPosRelative()
307         void copyTo(VoxelManipulator &dst);
308
309         // Copies data from VoxelManipulator getPosRelative()
310         void copyFrom(VoxelManipulator &dst);
311
312         // Update day-night lighting difference flag.
313         // Sets m_day_night_differs to appropriate value.
314         // These methods don't care about neighboring blocks.
315         void actuallyUpdateDayNightDiff();
316
317         // Call this to schedule what the previous function does to be done
318         // when the value is actually needed.
319         void expireDayNightDiff();
320
321         inline bool getDayNightDiff()
322         {
323                 if (m_day_night_differs_expired)
324                         actuallyUpdateDayNightDiff();
325                 return m_day_night_differs;
326         }
327
328         bool onObjectsActivation();
329         bool saveStaticObject(u16 id, const StaticObject &obj, u32 reason);
330
331         void step(float dtime, const std::function<bool(v3s16, MapNode, f32)> &on_timer_cb);
332
333         ////
334         //// Timestamp (see m_timestamp)
335         ////
336
337         // NOTE: BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
338
339         inline void setTimestamp(u32 time)
340         {
341                 m_timestamp = time;
342                 raiseModified(MOD_STATE_WRITE_AT_UNLOAD, MOD_REASON_SET_TIMESTAMP);
343         }
344
345         inline void setTimestampNoChangedFlag(u32 time)
346         {
347                 m_timestamp = time;
348         }
349
350         inline u32 getTimestamp()
351         {
352                 return m_timestamp;
353         }
354
355         inline u32 getDiskTimestamp()
356         {
357                 return m_disk_timestamp;
358         }
359
360         ////
361         //// Usage timer (see m_usage_timer)
362         ////
363
364         inline void resetUsageTimer()
365         {
366                 m_usage_timer = 0;
367         }
368
369         inline void incrementUsageTimer(float dtime)
370         {
371                 m_usage_timer += dtime;
372         }
373
374         inline float getUsageTimer()
375         {
376                 return m_usage_timer;
377         }
378
379         ////
380         //// Reference counting (see m_refcount)
381         ////
382
383         inline void refGrab()
384         {
385                 m_refcount++;
386         }
387
388         inline void refDrop()
389         {
390                 m_refcount--;
391         }
392
393         inline int refGet()
394         {
395                 return m_refcount;
396         }
397
398         ////
399         //// Node Timers
400         ////
401
402         inline NodeTimer getNodeTimer(v3s16 p)
403         {
404                 return m_node_timers.get(p);
405         }
406
407         inline void removeNodeTimer(v3s16 p)
408         {
409                 m_node_timers.remove(p);
410         }
411
412         inline void setNodeTimer(const NodeTimer &t)
413         {
414                 m_node_timers.set(t);
415         }
416
417         inline void clearNodeTimers()
418         {
419                 m_node_timers.clear();
420         }
421
422         ////
423         //// Serialization
424         ///
425
426         // These don't write or read version by itself
427         // Set disk to true for on-disk format, false for over-the-network format
428         // Precondition: version >= SER_FMT_VER_LOWEST_WRITE
429         void serialize(std::ostream &result, u8 version, bool disk, int compression_level);
430         // If disk == true: In addition to doing other things, will add
431         // unknown blocks from id-name mapping to wndef
432         void deSerialize(std::istream &is, u8 version, bool disk);
433
434         void serializeNetworkSpecific(std::ostream &os);
435         void deSerializeNetworkSpecific(std::istream &is);
436
437         bool storeActiveObject(u16 id);
438         // clearObject and return removed objects count
439         u32 clearObjects();
440
441 private:
442         /*
443                 Private methods
444         */
445
446         void deSerialize_pre22(std::istream &is, u8 version, bool disk);
447
448 public:
449         /*
450                 Public member variables
451         */
452
453 #ifndef SERVER // Only on client
454         MapBlockMesh *mesh = nullptr;
455 #endif
456
457         NodeMetadataList m_node_metadata;
458         StaticObjectList m_static_objects;
459
460         static const u32 ystride = MAP_BLOCKSIZE;
461         static const u32 zstride = MAP_BLOCKSIZE * MAP_BLOCKSIZE;
462
463         static const u32 nodecount = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
464
465         //// ABM optimizations ////
466         // Cache of content types
467         std::unordered_set<content_t> contents;
468         // True if content types are cached
469         bool contents_cached = false;
470         // True if we never want to cache content types for this block
471         bool do_not_cache_contents = false;
472         // marks the sides which are opaque: 00+Z-Z+Y-Y+X-X
473         u8 solid_sides {0};
474
475 private:
476         /*
477                 Private member variables
478         */
479
480         // NOTE: Lots of things rely on this being the Map
481         Map *m_parent;
482         // Position in blocks on parent
483         v3s16 m_pos;
484
485         /* This is the precalculated m_pos_relative value
486         * This caches the value, improving performance by removing 3 s16 multiplications
487         * at runtime on each getPosRelative call
488         * For a 5 minutes runtime with valgrind this removes 3 * 19M s16 multiplications
489         * The gain can be estimated in Release Build to 3 * 100M multiply operations for 5 mins
490         */
491         v3s16 m_pos_relative;
492
493         IGameDef *m_gamedef;
494
495         /*
496                 - On the server, this is used for telling whether the
497                   block has been modified from the one on disk.
498                 - On the client, this is used for nothing.
499         */
500         u32 m_modified = MOD_STATE_WRITE_NEEDED;
501         u32 m_modified_reason = MOD_REASON_INITIAL;
502
503         /*
504                 When propagating sunlight and the above block doesn't exist,
505                 sunlight is assumed if this is false.
506
507                 In practice this is set to true if the block is completely
508                 undeground with nothing visible above the ground except
509                 caves.
510         */
511         bool is_underground = false;
512
513         /*!
514          * Each bit indicates if light spreading was finished
515          * in a direction. (Because the neighbor could also be unloaded.)
516          * Bits (most significant first):
517          * nothing,  nothing,  nothing,  nothing,
518          * night X-, night Y-, night Z-, night Z+, night Y+, night X+,
519          * day X-,   day Y-,   day Z-,   day Z+,   day Y+,   day X+.
520         */
521         u16 m_lighting_complete = 0xFFFF;
522
523         // Whether day and night lighting differs
524         bool m_day_night_differs = false;
525         bool m_day_night_differs_expired = true;
526
527         bool m_generated = false;
528
529         /*
530                 When block is removed from active blocks, this is set to gametime.
531                 Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
532         */
533         u32 m_timestamp = BLOCK_TIMESTAMP_UNDEFINED;
534         // The on-disk (or to-be on-disk) timestamp value
535         u32 m_disk_timestamp = BLOCK_TIMESTAMP_UNDEFINED;
536
537         /*
538                 When the block is accessed, this is set to 0.
539                 Map will unload the block when this reaches a timeout.
540         */
541         float m_usage_timer = 0;
542
543         /*
544                 Reference count; currently used for determining if this block is in
545                 the list of blocks to be drawn.
546         */
547         int m_refcount = 0;
548
549         MapNode data[nodecount];
550         NodeTimerList m_node_timers;
551 };
552
553 typedef std::vector<MapBlock*> MapBlockVect;
554
555 inline bool objectpos_over_limit(v3f p)
556 {
557         const float max_limit_bs = (MAX_MAP_GENERATION_LIMIT + 0.5f) * BS;
558         return p.X < -max_limit_bs ||
559                 p.X >  max_limit_bs ||
560                 p.Y < -max_limit_bs ||
561                 p.Y >  max_limit_bs ||
562                 p.Z < -max_limit_bs ||
563                 p.Z >  max_limit_bs;
564 }
565
566 inline bool blockpos_over_max_limit(v3s16 p)
567 {
568         const s16 max_limit_bp = MAX_MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
569         return p.X < -max_limit_bp ||
570                 p.X >  max_limit_bp ||
571                 p.Y < -max_limit_bp ||
572                 p.Y >  max_limit_bp ||
573                 p.Z < -max_limit_bp ||
574                 p.Z >  max_limit_bp;
575 }
576
577 /*
578         Returns the position of the block where the node is located
579 */
580 inline v3s16 getNodeBlockPos(v3s16 p)
581 {
582         return getContainerPos(p, MAP_BLOCKSIZE);
583 }
584
585 inline void getNodeBlockPosWithOffset(v3s16 p, v3s16 &block, v3s16 &offset)
586 {
587         getContainerPosWithOffset(p, MAP_BLOCKSIZE, block, offset);
588 }
589
590 /*
591         Get a quick string to describe what a block actually contains
592 */
593 std::string analyze_block(MapBlock *block);