generate-time lighting optimization
[oweals/minetest.git] / src / mapblock.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 MAPBLOCK_HEADER
21 #define MAPBLOCK_HEADER
22
23 #include <jmutex.h>
24 #include <jmutexautolock.h>
25 #include <exception>
26 #include "debug.h"
27 #include "common_irrlicht.h"
28 #include "mapnode.h"
29 #include "exceptions.h"
30 #include "serialization.h"
31 #include "constants.h"
32 #include "mapblockobject.h"
33 #include "voxel.h"
34
35 #define MAP_BLOCKSIZE 16
36
37 // Named by looking towards z+
38 enum{
39         FACE_BACK=0,
40         FACE_TOP,
41         FACE_RIGHT,
42         FACE_FRONT,
43         FACE_BOTTOM,
44         FACE_LEFT
45 };
46
47 struct FastFace
48 {
49         TileSpec tile;
50         video::S3DVertex vertices[4]; // Precalculated vertices
51 };
52
53 enum NodeModType
54 {
55         NODEMOD_NONE,
56         NODEMOD_CHANGECONTENT, //param is content id
57         NODEMOD_CRACK // param is crack progression
58 };
59
60 struct NodeMod
61 {
62         NodeMod(enum NodeModType a_type=NODEMOD_NONE, u16 a_param=0)
63         {
64                 type = a_type;
65                 param = a_param;
66         }
67         enum NodeModType type;
68         u16 param;
69 };
70
71 enum
72 {
73         NODECONTAINER_ID_MAPBLOCK,
74         NODECONTAINER_ID_MAPSECTOR,
75         NODECONTAINER_ID_MAP,
76         NODECONTAINER_ID_MAPBLOCKCACHE,
77         NODECONTAINER_ID_VOXELMANIPULATOR,
78 };
79
80 class NodeContainer
81 {
82 public:
83         virtual bool isValidPosition(v3s16 p) = 0;
84         virtual MapNode getNode(v3s16 p) = 0;
85         virtual void setNode(v3s16 p, MapNode & n) = 0;
86         virtual u16 nodeContainerId() const = 0;
87 };
88
89 class MapBlock : public NodeContainer
90 {
91 public:
92         MapBlock(NodeContainer *parent, v3s16 pos, bool dummy=false);
93         ~MapBlock();
94         
95         virtual u16 nodeContainerId() const
96         {
97                 return NODECONTAINER_ID_MAPBLOCK;
98         }
99
100         NodeContainer * getParent()
101         {
102                 return m_parent;
103         }
104
105         bool isDummy()
106         {
107                 return (data == NULL);
108         }
109
110         void unDummify()
111         {
112                 assert(isDummy());
113                 reallocate();
114         }
115         
116         bool getChangedFlag()
117         {
118                 return changed;
119         }
120
121         void resetChangedFlag()
122         {
123                 changed = false;
124         }
125
126         void setChangedFlag()
127         {
128                 changed = true;
129         }
130 #ifndef SERVER
131         void setMeshExpired(bool expired)
132         {
133                 m_mesh_expired = expired;
134         }
135         
136         bool getMeshExpired()
137         {
138                 return m_mesh_expired;
139         }
140 #endif
141         v3s16 getPos()
142         {
143                 return m_pos;
144         }
145                 
146         v3s16 getPosRelative()
147         {
148                 return m_pos * MAP_BLOCKSIZE;
149         }
150                 
151         bool getIsUnderground()
152         {
153                 return is_underground;
154         }
155
156         void setIsUnderground(bool a_is_underground)
157         {
158                 is_underground = a_is_underground;
159                 setChangedFlag();
160         }
161
162         core::aabbox3d<s16> getBox()
163         {
164                 return core::aabbox3d<s16>(getPosRelative(),
165                                 getPosRelative()
166                                 + v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE)
167                                 - v3s16(1,1,1));
168         }
169         
170         void reallocate()
171         {
172                 if(data != NULL)
173                         delete[] data;
174                 u32 l = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
175                 data = new MapNode[l];
176                 for(u32 i=0; i<l; i++){
177                         data[i] = MapNode();
178                 }
179                 setChangedFlag();
180         }
181
182         bool isValidPosition(v3s16 p)
183         {
184                 if(data == NULL)
185                         return false;
186                 return (p.X >= 0 && p.X < MAP_BLOCKSIZE
187                                 && p.Y >= 0 && p.Y < MAP_BLOCKSIZE
188                                 && p.Z >= 0 && p.Z < MAP_BLOCKSIZE);
189         }
190
191         /*
192                 Regular MapNode get-setters
193         */
194         
195         MapNode getNode(s16 x, s16 y, s16 z)
196         {
197                 if(data == NULL)
198                         throw InvalidPositionException();
199                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
200                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
201                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
202                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
203         }
204         
205         MapNode getNode(v3s16 p)
206         {
207                 return getNode(p.X, p.Y, p.Z);
208         }
209         
210         void setNode(s16 x, s16 y, s16 z, MapNode & n)
211         {
212                 if(data == NULL)
213                         throw InvalidPositionException();
214                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
215                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
216                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
217                 data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x] = n;
218                 setChangedFlag();
219         }
220         
221         void setNode(v3s16 p, MapNode & n)
222         {
223                 setNode(p.X, p.Y, p.Z, n);
224         }
225
226         /*
227                 Non-checking variants of the above
228         */
229
230         MapNode getNodeNoCheck(s16 x, s16 y, s16 z)
231         {
232                 if(data == NULL)
233                         throw InvalidPositionException();
234                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
235         }
236         
237         MapNode getNodeNoCheck(v3s16 p)
238         {
239                 return getNodeNoCheck(p.X, p.Y, p.Z);
240         }
241         
242         void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode & n)
243         {
244                 if(data == NULL)
245                         throw InvalidPositionException();
246                 data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x] = n;
247                 setChangedFlag();
248         }
249         
250         void setNodeNoCheck(v3s16 p, MapNode & n)
251         {
252                 setNodeNoCheck(p.X, p.Y, p.Z, n);
253         }
254
255         /*
256                 These functions consult the parent container if the position
257                 is not valid on this MapBlock.
258         */
259         bool isValidPositionParent(v3s16 p);
260         MapNode getNodeParent(v3s16 p);
261         void setNodeParent(v3s16 p, MapNode & n);
262         MapNode getNodeParentNoEx(v3s16 p);
263
264         void drawbox(s16 x0, s16 y0, s16 z0, s16 w, s16 h, s16 d, MapNode node)
265         {
266                 for(u16 z=0; z<d; z++)
267                         for(u16 y=0; y<h; y++)
268                                 for(u16 x=0; x<w; x++)
269                                         setNode(x0+x, y0+y, z0+z, node);
270         }
271
272         u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
273                         v3s16 face_dir);
274         
275         u8 getFaceLight(u32 daynight_ratio, v3s16 p, v3s16 face_dir)
276         {
277                 return getFaceLight(daynight_ratio,
278                                 getNodeParentNoEx(p),
279                                 getNodeParentNoEx(p + face_dir),
280                                 face_dir);
281         }
282         
283 #ifndef SERVER
284         // light = 0...255
285         static void makeFastFace(TileSpec tile, u8 light, v3f p,
286                         v3s16 dir, v3f scale, v3f posRelative_f,
287                         core::array<FastFace> &dest);
288         
289         TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir);
290         u8 getNodeContent(v3s16 p, MapNode mn);
291
292         /*
293                 startpos:
294                 translate_dir: unit vector with only one of x, y or z
295                 face_dir: unit vector with only one of x, y or z
296         */
297         void updateFastFaceRow(
298                         u32 daynight_ratio,
299                         v3f posRelative_f,
300                         v3s16 startpos,
301                         u16 length,
302                         v3s16 translate_dir,
303                         v3f translate_dir_f,
304                         v3s16 face_dir,
305                         v3f face_dir_f,
306                         core::array<FastFace> &dest);
307
308         void updateMesh(u32 daynight_ratio);
309         /*void updateMesh(s32 daynight_i);
310         // Updates all DAYNIGHT_CACHE_COUNT meshes
311         void updateMeshes(s32 first_i=0);*/
312 #endif // !SERVER
313         
314         // See comments in mapblock.cpp
315         bool propagateSunlight(core::map<v3s16, bool> & light_sources,
316                         bool remove_light=false, bool *black_air_left=NULL);
317         
318         // Copies data to VoxelManipulator to getPosRelative()
319         void copyTo(VoxelManipulator &dst);
320
321         /*
322                 Object stuff
323         */
324         
325         void serializeObjects(std::ostream &os, u8 version)
326         {
327                 m_objects.serialize(os, version);
328         }
329         // If smgr!=NULL, new objects are added to the scene
330         void updateObjects(std::istream &is, u8 version,
331                         scene::ISceneManager *smgr, u32 daynight_ratio)
332         {
333                 m_objects.update(is, version, smgr, daynight_ratio);
334
335                 setChangedFlag();
336         }
337         void clearObjects()
338         {
339                 m_objects.clear();
340
341                 setChangedFlag();
342         }
343         void addObject(MapBlockObject *object)
344                         throw(ContainerFullException, AlreadyExistsException)
345         {
346                 m_objects.add(object);
347
348                 setChangedFlag();
349         }
350         void removeObject(s16 id)
351         {
352                 m_objects.remove(id);
353
354                 setChangedFlag();
355         }
356         MapBlockObject * getObject(s16 id)
357         {
358                 return m_objects.get(id);
359         }
360         JMutexAutoLock * getObjectLock()
361         {
362                 return m_objects.getLock();
363         }
364
365         /*
366                 Moves objects, deletes objects and spawns new objects
367         */
368         void stepObjects(float dtime, bool server, u32 daynight_ratio);
369
370         /*void wrapObject(MapBlockObject *object)
371         {
372                 m_objects.wrapObject(object);
373
374                 setChangedFlag();
375         }*/
376
377         // origin is relative to block
378         void getObjects(v3f origin, f32 max_d,
379                         core::array<DistanceSortedObject> &dest)
380         {
381                 m_objects.getObjects(origin, max_d, dest);
382         }
383
384         /*void getPseudoObjects(v3f origin, f32 max_d,
385                         core::array<DistanceSortedObject> &dest);*/
386
387         s32 getObjectCount()
388         {
389                 return m_objects.getCount();
390         }
391
392 #ifndef SERVER
393         /*
394                 Methods for setting temporary modifications to nodes for
395                 drawing
396         */
397         void setTempMod(v3s16 p, NodeMod mod)
398         {
399                 /*dstream<<"setTempMod called on block"
400                                 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
401                                 <<", mod.type="<<mod.type
402                                 <<", mod.param="<<mod.param
403                                 <<std::endl;*/
404                 JMutexAutoLock lock(m_temp_mods_mutex);
405                 m_temp_mods[p] = mod;
406         }
407         // Returns true if there was one
408         bool getTempMod(v3s16 p, struct NodeMod *mod)
409         {
410                 JMutexAutoLock lock(m_temp_mods_mutex);
411                 core::map<v3s16, NodeMod>::Node *n;
412                 n = m_temp_mods.find(p);
413                 if(n == NULL)
414                         return false;
415                 if(mod)
416                         *mod = n->getValue();
417                 return true;
418         }
419         void clearTempMod(v3s16 p)
420         {
421                 JMutexAutoLock lock(m_temp_mods_mutex);
422                 if(m_temp_mods.find(p))
423                         m_temp_mods.remove(p);
424         }
425         void clearTempMods()
426         {
427                 JMutexAutoLock lock(m_temp_mods_mutex);
428                 m_temp_mods.clear();
429         }
430 #endif
431
432         /*
433                 Day-night lighting difference
434                 
435                 These methods don't care about neighboring blocks.
436                 It means that to know if a block really doesn't need a mesh
437                 update between day and night, the neighboring blocks have
438                 to be taken into account. Use Map::dayNightDiffed().
439         */
440         void updateDayNightDiff();
441
442         bool dayNightDiffed()
443         {
444                 return m_day_night_differs;
445         }
446
447         /*
448                 Miscellaneous stuff
449         */
450         
451         /*
452                 Tries to measure ground level.
453                 Return value:
454                         -1 = only air
455                         -2 = only ground
456                         -3 = random fail
457                         0...MAP_BLOCKSIZE-1 = ground level
458         */
459         s16 getGroundLevel(v2s16 p2d);
460
461         /*
462                 Serialization
463         */
464         
465         // Doesn't write version by itself
466         void serialize(std::ostream &os, u8 version);
467
468         void deSerialize(std::istream &is, u8 version);
469
470         /*
471                 Public member variables
472         */
473
474 #ifndef SERVER
475         //scene::SMesh *mesh[DAYNIGHT_CACHE_COUNT];
476         scene::SMesh *mesh;
477         JMutex mesh_mutex;
478 #endif
479
480 private:
481
482         /*
483                 Used only internally, because changes can't be tracked
484         */
485
486         MapNode & getNodeRef(s16 x, s16 y, s16 z)
487         {
488                 if(data == NULL)
489                         throw InvalidPositionException();
490                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
491                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
492                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
493                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
494         }
495         MapNode & getNodeRef(v3s16 &p)
496         {
497                 return getNodeRef(p.X, p.Y, p.Z);
498         }
499
500         
501         NodeContainer *m_parent;
502         // Position in blocks on parent
503         v3s16 m_pos;
504         /*
505                 If NULL, block is a dummy block.
506                 Dummy blocks are used for caching not-found-on-disk blocks.
507         */
508         MapNode * data;
509         /*
510                 - On the client, this is used for checking whether to
511                   recalculate the face cache. (Is it anymore?)
512                 - On the server, this is used for telling whether the
513                   block has been changed from the one on disk.
514         */
515         bool changed;
516         /*
517                 Used for some initial lighting stuff.
518                 At least /has been/ used. 8)
519                 It's probably useless now.
520         */
521         bool is_underground;
522         
523         // Whether day and night lighting differs
524         bool m_day_night_differs;
525         
526         MapBlockObjectList m_objects;
527
528         // Object spawning stuff
529         float m_spawn_timer;
530         
531 #ifndef SERVER
532         bool m_mesh_expired;
533         
534         // Temporary modifications to nodes
535         // These are only used when drawing
536         core::map<v3s16, NodeMod> m_temp_mods;
537         JMutex m_temp_mods_mutex;
538 #endif
539 };
540
541 inline bool blockpos_over_limit(v3s16 p)
542 {
543         return
544           (p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
545         || p.X >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
546         || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
547         || p.Y >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
548         || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
549         || p.Z >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE);
550 }
551
552 /*
553         Returns the position of the block where the node is located
554 */
555 inline v3s16 getNodeBlockPos(v3s16 p)
556 {
557         return getContainerPos(p, MAP_BLOCKSIZE);
558 }
559
560 inline v2s16 getNodeSectorPos(v2s16 p)
561 {
562         return getContainerPos(p, MAP_BLOCKSIZE);
563 }
564
565 inline s16 getNodeBlockY(s16 y)
566 {
567         return getContainerPos(y, MAP_BLOCKSIZE);
568 }
569
570 #endif
571