]> git.lizzy.rs Git - minetest.git/blob - src/mapblock.cpp
before daynight mesh cache
[minetest.git] / src / mapblock.cpp
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 #include "mapblock.h"
21 #include "map.h"
22 // For g_materials
23 #include "main.h"
24 #include "light.h"
25 #include <sstream>
26
27
28 /*
29         MapBlock
30 */
31
32 bool MapBlock::isValidPositionParent(v3s16 p)
33 {
34         if(isValidPosition(p))
35         {
36                 return true;
37         }
38         else{
39                 return m_parent->isValidPosition(getPosRelative() + p);
40         }
41 }
42
43 MapNode MapBlock::getNodeParent(v3s16 p)
44 {
45         if(isValidPosition(p) == false)
46         {
47                 return m_parent->getNode(getPosRelative() + p);
48         }
49         else
50         {
51                 if(data == NULL)
52                         throw InvalidPositionException();
53                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
54         }
55 }
56
57 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
58 {
59         if(isValidPosition(p) == false)
60         {
61                 m_parent->setNode(getPosRelative() + p, n);
62         }
63         else
64         {
65                 if(data == NULL)
66                         throw InvalidPositionException();
67                 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
68         }
69 }
70
71 FastFace * MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
72                 v3s16 dir, v3f scale, v3f posRelative_f)
73 {
74         FastFace *f = new FastFace;
75         
76         // Position is at the center of the cube.
77         v3f pos = p * BS;
78         posRelative_f *= BS;
79
80         v3f vertex_pos[4];
81         // If looking towards z+, this is the face that is behind
82         // the center point, facing towards z+.
83         vertex_pos[0] = v3f( BS/2,-BS/2,BS/2);
84         vertex_pos[1] = v3f(-BS/2,-BS/2,BS/2);
85         vertex_pos[2] = v3f(-BS/2, BS/2,BS/2);
86         vertex_pos[3] = v3f( BS/2, BS/2,BS/2);
87         
88         for(u16 i=0; i<4; i++)
89         {
90                 if(dir == v3s16(0,0,1))
91                         vertex_pos[i].rotateXZBy(0);
92                 else if(dir == v3s16(0,0,-1))
93                         vertex_pos[i].rotateXZBy(180);
94                 else if(dir == v3s16(1,0,0))
95                         vertex_pos[i].rotateXZBy(-90);
96                 else if(dir == v3s16(-1,0,0))
97                         vertex_pos[i].rotateXZBy(90);
98                 else if(dir == v3s16(0,1,0))
99                         vertex_pos[i].rotateYZBy(-90);
100                 else if(dir == v3s16(0,-1,0))
101                         vertex_pos[i].rotateYZBy(90);
102
103                 vertex_pos[i].X *= scale.X;
104                 vertex_pos[i].Y *= scale.Y;
105                 vertex_pos[i].Z *= scale.Z;
106                 vertex_pos[i] += pos + posRelative_f;
107         }
108
109         f32 abs_scale = 1.;
110         if     (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
111         else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
112         else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
113
114         v3f zerovector = v3f(0,0,0);
115         
116         u8 li = decode_light(light);
117         //u8 li = 150;
118
119         u8 alpha = 255;
120
121         if(tile.id == TILE_WATER)
122         {
123                 alpha = 128;
124         }
125
126         video::SColor c = video::SColor(alpha,li,li,li);
127
128         f->vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
129                         core::vector2d<f32>(0,1));
130         f->vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
131                         core::vector2d<f32>(abs_scale,1));
132         f->vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
133                         core::vector2d<f32>(abs_scale,0));
134         f->vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
135                         core::vector2d<f32>(0,0));
136
137         f->tile = tile;
138         //DEBUG
139         //f->tile = TILE_STONE;
140
141         return f;
142 }
143         
144 /*
145         Parameters must consist of air and !air.
146         Order doesn't matter.
147
148         If either of the nodes doesn't exist, light is 0.
149 */
150 u8 MapBlock::getFaceLight(u32 daylight_factor, v3s16 p, v3s16 face_dir)
151 {
152         try{
153                 MapNode n = getNodeParent(p);
154                 MapNode n2 = getNodeParent(p + face_dir);
155                 u8 light;
156                 u8 l1 = n.getLightBlend(daylight_factor);
157                 u8 l2 = n2.getLightBlend(daylight_factor);
158                 if(l1 > l2)
159                         light = l1;
160                 else
161                         light = l2;
162
163                 // Make some nice difference to different sides
164
165                 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
166                         light = diminish_light(diminish_light(light));
167                 else if(face_dir.X == -1 || face_dir.Z == -1)
168                         light = diminish_light(light);*/
169
170                 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
171                         light = diminish_light(diminish_light(light));
172                 else if(face_dir.Z == 1 || face_dir.Z == -1)
173                         light = diminish_light(light);
174
175                 return light;
176         }
177         catch(InvalidPositionException &e)
178         {
179                 return 0;
180         }
181 }
182
183 /*
184         Gets node tile from any place relative to block.
185         Returns TILE_NODE if doesn't exist or should not be drawn.
186 */
187 TileSpec MapBlock::getNodeTile(v3s16 p, v3s16 face_dir)
188 {
189         TileSpec spec;
190
191         spec.feature = TILEFEAT_NONE;
192         try{
193                 MapNode n = getNodeParent(p);
194                 
195                 spec.id = n.getTile(face_dir);
196         }
197         catch(InvalidPositionException &e)
198         {
199                 spec.id = TILE_NONE;
200         }
201         
202         /*
203                 Check temporary modifications on this node
204         */
205         core::map<v3s16, NodeMod>::Node *n;
206         n = m_temp_mods.find(p);
207
208         // If modified
209         if(n != NULL)
210         {
211                 struct NodeMod mod = n->getValue();
212                 if(mod.type == NODEMOD_CHANGECONTENT)
213                 {
214                         spec.id = content_tile(mod.param, face_dir);
215                 }
216                 if(mod.type == NODEMOD_CRACK)
217                 {
218                 }
219         }
220         
221         return spec;
222 }
223
224 u8 MapBlock::getNodeContent(v3s16 p)
225 {
226         /*
227                 Check temporary modifications on this node
228         */
229         core::map<v3s16, NodeMod>::Node *n;
230         n = m_temp_mods.find(p);
231
232         // If modified
233         if(n != NULL)
234         {
235                 struct NodeMod mod = n->getValue();
236                 if(mod.type == NODEMOD_CHANGECONTENT)
237                 {
238                         // Overrides content
239                         return mod.param;
240                 }
241                 if(mod.type == NODEMOD_CRACK)
242                 {
243                         /*
244                                 Content doesn't change.
245                                 
246                                 face_contents works just like it should, because
247                                 there should not be faces between differently cracked
248                                 nodes.
249
250                                 If a semi-transparent node is cracked in front an
251                                 another one, it really doesn't matter whether there
252                                 is a cracked face drawn in between or not.
253                         */
254                 }
255         }
256         
257         try{
258                 MapNode n = getNodeParent(p);
259                 
260                 return n.d;
261         }
262         catch(InvalidPositionException &e)
263         {
264                 return CONTENT_IGNORE;
265         }
266 }
267
268 /*
269         startpos:
270         translate_dir: unit vector with only one of x, y or z
271         face_dir: unit vector with only one of x, y or z
272 */
273 void MapBlock::updateFastFaceRow(
274                 u32 daylight_factor,
275                 v3s16 startpos,
276                 u16 length,
277                 v3s16 translate_dir,
278                 v3s16 face_dir,
279                 core::list<FastFace*> &dest)
280 {
281         /*
282                 Precalculate some variables
283         */
284         v3f translate_dir_f(translate_dir.X, translate_dir.Y,
285                         translate_dir.Z); // floating point conversion
286         v3f face_dir_f(face_dir.X, face_dir.Y,
287                         face_dir.Z); // floating point conversion
288         v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
289                         getPosRelative().Z); // floating point conversion
290
291         v3s16 p = startpos;
292         /*
293                 Get face light at starting position
294         */
295         u8 light = getFaceLight(daylight_factor, p, face_dir);
296         
297         u16 continuous_tiles_count = 0;
298         
299         TileSpec tile0 = getNodeTile(p, face_dir);
300         TileSpec tile1 = getNodeTile(p + face_dir, -face_dir);
301                 
302         for(u16 j=0; j<length; j++)
303         {
304                 bool next_is_different = true;
305                 
306                 v3s16 p_next;
307                 TileSpec tile0_next;
308                 TileSpec tile1_next;
309                 u8 light_next = 0;
310
311                 if(j != length - 1){
312                         p_next = p + translate_dir;
313                         tile0_next = getNodeTile(p_next, face_dir);
314                         tile1_next = getNodeTile(p_next + face_dir, -face_dir);
315                         light_next = getFaceLight(daylight_factor, p_next, face_dir);
316
317                         if(tile0_next == tile0
318                                         && tile1_next == tile1
319                                         && light_next == light)
320                         {
321                                 next_is_different = false;
322                         }
323                 }
324
325                 continuous_tiles_count++;
326                 
327                 if(next_is_different)
328                 {
329                         /*
330                                 Create a face if there should be one
331                         */
332                         //u8 mf = face_contents(tile0, tile1);
333                         // This is hackish
334                         u8 content0 = getNodeContent(p);
335                         u8 content1 = getNodeContent(p + face_dir);
336                         u8 mf = face_contents(content0, content1);
337                         
338                         if(mf != 0)
339                         {
340                                 // Floating point conversion of the position vector
341                                 v3f pf(p.X, p.Y, p.Z);
342                                 // Center point of face (kind of)
343                                 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
344                                 v3f scale(1,1,1);
345                                 if(translate_dir.X != 0){
346                                         scale.X = continuous_tiles_count;
347                                 }
348                                 if(translate_dir.Y != 0){
349                                         scale.Y = continuous_tiles_count;
350                                 }
351                                 if(translate_dir.Z != 0){
352                                         scale.Z = continuous_tiles_count;
353                                 }
354                                 
355                                 FastFace *f;
356
357                                 // If node at sp (tile0) is more solid
358                                 if(mf == 1)
359                                 {
360                                         f = makeFastFace(tile0, light,
361                                                         sp, face_dir, scale,
362                                                         posRelative_f);
363                                 }
364                                 // If node at sp is less solid (mf == 2)
365                                 else
366                                 {
367                                         f = makeFastFace(tile1, light,
368                                                         sp+face_dir_f, -face_dir, scale,
369                                                         posRelative_f);
370                                 }
371                                 dest.push_back(f);
372                         }
373
374                         continuous_tiles_count = 0;
375                         tile0 = tile0_next;
376                         tile1 = tile1_next;
377                         light = light_next;
378                 }
379                 
380                 p = p_next;
381         }
382 }
383
384 /*
385         This is used because CMeshBuffer::append() is very slow
386 */
387 struct PreMeshBuffer
388 {
389         video::SMaterial material;
390         core::array<u16> indices;
391         core::array<video::S3DVertex> vertices;
392 };
393
394 class MeshCollector
395 {
396 public:
397         void append(
398                         video::SMaterial material,
399                         const video::S3DVertex* const vertices,
400                         u32 numVertices,
401                         const u16* const indices,
402                         u32 numIndices
403                 )
404         {
405                 PreMeshBuffer *p = NULL;
406                 for(u32 i=0; i<m_prebuffers.size(); i++)
407                 {
408                         PreMeshBuffer &pp = m_prebuffers[i];
409                         if(pp.material != material)
410                                 continue;
411
412                         p = &pp;
413                         break;
414                 }
415
416                 if(p == NULL)
417                 {
418                         PreMeshBuffer pp;
419                         pp.material = material;
420                         m_prebuffers.push_back(pp);
421                         p = &m_prebuffers[m_prebuffers.size()-1];
422                 }
423
424                 u32 vertex_count = p->vertices.size();
425                 for(u32 i=0; i<numIndices; i++)
426                 {
427                         u32 j = indices[i] + vertex_count;
428                         if(j > 65535)
429                         {
430                                 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
431                                 // NOTE: Fix is to just add an another MeshBuffer
432                         }
433                         p->indices.push_back(j);
434                 }
435                 for(u32 i=0; i<numVertices; i++)
436                 {
437                         p->vertices.push_back(vertices[i]);
438                 }
439         }
440
441         void fillMesh(scene::SMesh *mesh)
442         {
443                 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
444                                 <<" meshbuffers"<<std::endl;*/
445                 for(u32 i=0; i<m_prebuffers.size(); i++)
446                 {
447                         PreMeshBuffer &p = m_prebuffers[i];
448
449                         /*dstream<<"p.vertices.size()="<<p.vertices.size()
450                                         <<", p.indices.size()="<<p.indices.size()
451                                         <<std::endl;*/
452                         
453                         // Create meshbuffer
454                         
455                         // This is a "Standard MeshBuffer",
456                         // it's a typedeffed CMeshBuffer<video::S3DVertex>
457                         scene::SMeshBuffer *buf = new scene::SMeshBuffer();
458                         // Set material
459                         buf->Material = p.material;
460                         //((scene::SMeshBuffer*)buf)->Material = p.material;
461                         // Use VBO
462                         //buf->setHardwareMappingHint(scene::EHM_STATIC);
463                         // Add to mesh
464                         mesh->addMeshBuffer(buf);
465                         // Mesh grabbed it
466                         buf->drop();
467
468                         buf->append(p.vertices.pointer(), p.vertices.size(),
469                                         p.indices.pointer(), p.indices.size());
470                 }
471         }
472
473 private:
474         core::array<PreMeshBuffer> m_prebuffers;
475 };
476
477 void MapBlock::updateMesh(u32 daylight_factor)
478 {
479         /*v3s16 p = getPosRelative();
480         std::cout<<"MapBlock("<<p.X<<","<<p.Y<<","<<p.Z<<")"
481                         <<"::updateMesh(): ";*/
482                         //<<"::updateMesh()"<<std::endl;
483         TimeTaker timer1("updateMesh()", g_device);
484         
485         /*
486                 TODO: Change this to directly generate the mesh (and get rid
487                       of FastFaces)
488         */
489
490         core::list<FastFace*> *fastfaces_new = new core::list<FastFace*>;
491         
492         /*
493                 We are including the faces of the trailing edges of the block.
494                 This means that when something changes, the caller must
495                 also update the meshes of the blocks at the leading edges.
496
497                 NOTE: This is the slowest part of this method. The other parts
498                       take around 0ms, this takes around 15-70ms.
499         */
500
501         /*
502                 Go through every y,z and get top faces in rows of x+
503         */
504         for(s16 y=0; y<MAP_BLOCKSIZE; y++){
505         //for(s16 y=-1; y<MAP_BLOCKSIZE; y++){
506                 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
507                         updateFastFaceRow(daylight_factor,
508                                         v3s16(0,y,z), MAP_BLOCKSIZE,
509                                         v3s16(1,0,0),
510                                         v3s16(0,1,0),
511                                         *fastfaces_new);
512                 }
513         }
514         /*
515                 Go through every x,y and get right faces in rows of z+
516         */
517         for(s16 x=0; x<MAP_BLOCKSIZE; x++){
518         //for(s16 x=-1; x<MAP_BLOCKSIZE; x++){
519                 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
520                         updateFastFaceRow(daylight_factor,
521                                         v3s16(x,y,0), MAP_BLOCKSIZE,
522                                         v3s16(0,0,1),
523                                         v3s16(1,0,0),
524                                         *fastfaces_new);
525                 }
526         }
527         /*
528                 Go through every y,z and get back faces in rows of x+
529         */
530         for(s16 z=0; z<MAP_BLOCKSIZE; z++){
531         //for(s16 z=-1; z<MAP_BLOCKSIZE; z++){
532                 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
533                         updateFastFaceRow(daylight_factor,
534                                         v3s16(0,y,z), MAP_BLOCKSIZE,
535                                         v3s16(1,0,0),
536                                         v3s16(0,0,1),
537                                         *fastfaces_new);
538                 }
539         }
540
541         scene::SMesh *mesh_new = NULL;
542         
543         mesh_new = new scene::SMesh();
544         
545         if(fastfaces_new->getSize() > 0)
546         {
547                 MeshCollector collector;
548
549                 core::list<FastFace*>::Iterator i = fastfaces_new->begin();
550
551                 for(; i != fastfaces_new->end(); i++)
552                 {
553                         FastFace *f = *i;
554
555                         const u16 indices[] = {0,1,2,2,3,0};
556                         
557                         if(f->tile.feature == TILEFEAT_NONE)
558                         {
559                                 collector.append(g_tile_materials[f->tile.id], f->vertices, 4,
560                                                 indices, 6);
561                         }
562                         else
563                         {
564                                 // Not implemented
565                                 assert(0);
566                         }
567                 }
568
569                 collector.fillMesh(mesh_new);
570
571                 // Use VBO for mesh (this just would set this for ever buffer)
572                 mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
573                 
574                 /*std::cout<<"MapBlock has "<<fastfaces_new->getSize()<<" faces "
575                                 <<"and uses "<<mesh_new->getMeshBufferCount()
576                                 <<" materials (meshbuffers)"<<std::endl;*/
577         }
578
579         /*
580                 Clear temporary FastFaces
581         */
582
583         core::list<FastFace*>::Iterator i;
584         i = fastfaces_new->begin();
585         for(; i != fastfaces_new->end(); i++)
586         {
587                 delete *i;
588         }
589         fastfaces_new->clear();
590         delete fastfaces_new;
591
592         /*
593                 Add special graphics:
594                 - torches
595                 
596                 TODO: Optimize by using same meshbuffer for same textures
597         */
598
599         /*scene::ISceneManager *smgr = NULL;
600         video::IVideoDriver* driver = NULL;
601         if(g_device)
602         {
603                 smgr = g_device->getSceneManager();
604                 driver = smgr->getVideoDriver();
605         }*/
606                         
607         for(s16 z=0; z<MAP_BLOCKSIZE; z++)
608         for(s16 y=0; y<MAP_BLOCKSIZE; y++)
609         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
610         {
611                 v3s16 p(x,y,z);
612
613                 MapNode &n = getNodeRef(x,y,z);
614                 
615                 if(n.d == CONTENT_TORCH)
616                 {
617                         //scene::IMeshBuffer *buf = new scene::SMeshBuffer();
618                         scene::SMeshBuffer *buf = new scene::SMeshBuffer();
619                         video::SColor c(255,255,255,255);
620
621                         video::S3DVertex vertices[4] =
622                         {
623                                 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
624                                 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
625                                 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
626                                 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
627                         };
628
629                         v3s16 dir = unpackDir(n.dir);
630
631                         for(s32 i=0; i<4; i++)
632                         {
633                                 if(dir == v3s16(1,0,0))
634                                         vertices[i].Pos.rotateXZBy(0);
635                                 if(dir == v3s16(-1,0,0))
636                                         vertices[i].Pos.rotateXZBy(180);
637                                 if(dir == v3s16(0,0,1))
638                                         vertices[i].Pos.rotateXZBy(90);
639                                 if(dir == v3s16(0,0,-1))
640                                         vertices[i].Pos.rotateXZBy(-90);
641                                 if(dir == v3s16(0,-1,0))
642                                         vertices[i].Pos.rotateXZBy(45);
643                                 if(dir == v3s16(0,1,0))
644                                         vertices[i].Pos.rotateXZBy(-45);
645
646                                 vertices[i].Pos += intToFloat(p + getPosRelative());
647                         }
648
649                         u16 indices[] = {0,1,2,2,3,0};
650                         buf->append(vertices, 4, indices, 6);
651
652                         // Set material
653                         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
654                         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
655                         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
656                         //buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
657                         buf->getMaterial().MaterialType
658                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
659                         if(dir == v3s16(0,-1,0))
660                                 buf->getMaterial().setTexture(0,
661                                                 g_texturecache.get("torch_on_floor"));
662                         else if(dir == v3s16(0,1,0))
663                                 buf->getMaterial().setTexture(0,
664                                                 g_texturecache.get("torch_on_ceiling"));
665                         // For backwards compatibility
666                         else if(dir == v3s16(0,0,0))
667                                 buf->getMaterial().setTexture(0,
668                                                 g_texturecache.get("torch_on_floor"));
669                         else
670                                 buf->getMaterial().setTexture(0, g_texturecache.get("torch"));
671
672                         // Add to mesh
673                         mesh_new->addMeshBuffer(buf);
674                         buf->drop();
675                 }
676         }
677         
678         /*
679                 Do some stuff to the mesh
680         */
681
682         mesh_new->recalculateBoundingBox();
683
684         /*
685                 Delete new mesh if it is empty
686         */
687
688         if(mesh_new->getMeshBufferCount() == 0)
689         {
690                 mesh_new->drop();
691                 mesh_new = NULL;
692         }
693
694         /*
695                 Replace the mesh
696         */
697
698         mesh_mutex.Lock();
699
700         scene::SMesh *mesh_old = mesh;
701
702         mesh = mesh_new;
703         setMeshExpired(false);
704         
705         if(mesh_old != NULL)
706         {
707                 // Remove hardware buffers of meshbuffers of mesh
708                 // NOTE: No way, this runs in a different thread and everything
709                 /*u32 c = mesh_old->getMeshBufferCount();
710                 for(u32 i=0; i<c; i++)
711                 {
712                         IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
713                 }*/
714                 // Drop the mesh
715                 mesh_old->drop();
716                 //delete mesh_old;
717         }
718
719         mesh_mutex.Unlock();
720         
721         //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
722 }
723
724 /*
725         Propagates sunlight down through the block.
726         Doesn't modify nodes that are not affected by sunlight.
727         
728         Returns false if sunlight at bottom block is invalid
729         Returns true if bottom block doesn't exist.
730
731         If there is a block above, continues from it.
732         If there is no block above, assumes there is sunlight, unless
733         is_underground is set.
734
735         At the moment, all sunlighted nodes are added to light_sources.
736         TODO: This could be optimized.
737 */
738 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
739 {
740         // Whether the sunlight at the top of the bottom block is valid
741         bool block_below_is_valid = true;
742         
743         v3s16 pos_relative = getPosRelative();
744         
745         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
746         {
747                 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
748                 {
749                         bool no_sunlight = false;
750                         bool no_top_block = false;
751                         // Check if node above block has sunlight
752                         try{
753                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
754                                 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
755                                 {
756                                         /*if(is_underground)
757                                         {
758                                                 no_sunlight = true;
759                                         }*/
760                                         no_sunlight = true;
761                                 }
762                         }
763                         catch(InvalidPositionException &e)
764                         {
765                                 no_top_block = true;
766                                 
767                                 // TODO: This makes over-ground roofed places sunlighted
768                                 // Assume sunlight, unless is_underground==true
769                                 if(is_underground)
770                                 {
771                                         no_sunlight = true;
772                                 }
773                                 
774                                 // TODO: There has to be some way to allow this behaviour
775                                 // As of now, it just makes everything dark.
776                                 // No sunlight here
777                                 //no_sunlight = true;
778                         }
779
780                         /*std::cout<<"("<<x<<","<<z<<"): "
781                                         <<"no_top_block="<<no_top_block
782                                         <<", is_underground="<<is_underground
783                                         <<", no_sunlight="<<no_sunlight
784                                         <<std::endl;*/
785                 
786                         s16 y = MAP_BLOCKSIZE-1;
787                         
788                         if(no_sunlight == false)
789                         {
790                                 // Continue spreading sunlight downwards through transparent
791                                 // nodes
792                                 for(; y >= 0; y--)
793                                 {
794                                         v3s16 pos(x, y, z);
795                                         
796                                         MapNode &n = getNodeRef(pos);
797
798                                         if(n.sunlight_propagates())
799                                         {
800                                                 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
801
802                                                 light_sources.insert(pos_relative + pos, true);
803                                         }
804                                         else{
805                                                 break;
806                                         }
807                                 }
808                         }
809
810                         bool sunlight_should_go_down = (y==-1);
811
812                         // Fill rest with black (only transparent ones)
813                         for(; y >= 0; y--){
814                                 v3s16 pos(x, y, z);
815                                 
816                                 MapNode &n = getNodeRef(pos);
817
818                                 if(n.light_propagates())
819                                 {
820                                         n.setLight(LIGHTBANK_DAY, 0);
821                                 }
822                                 else{
823                                         break;
824                                 }
825                         }
826
827                         /*
828                                 If the block below hasn't already been marked invalid:
829
830                                 Check if the node below the block has proper sunlight at top.
831                                 If not, the block below is invalid.
832                                 
833                                 Ignore non-transparent nodes as they always have no light
834                         */
835                         try
836                         {
837                         if(block_below_is_valid)
838                         {
839                                 MapNode n = getNodeParent(v3s16(x, -1, z));
840                                 if(n.light_propagates())
841                                 {
842                                         if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
843                                                         && sunlight_should_go_down == false)
844                                                 block_below_is_valid = false;
845                                         else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
846                                                         && sunlight_should_go_down == true)
847                                                 block_below_is_valid = false;
848                                 }
849                         }//if
850                         }//try
851                         catch(InvalidPositionException &e)
852                         {
853                                 /*std::cout<<"InvalidBlockException for bottom block node"
854                                                 <<std::endl;*/
855                                 // Just no block below, no need to panic.
856                         }
857                 }
858         }
859
860         return block_below_is_valid;
861 }
862
863 void MapBlock::copyTo(VoxelManipulator &dst)
864 {
865         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
866         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
867         
868         dst.copyFrom(data, data_area, v3s16(0,0,0),
869                         getPosRelative(), data_size);
870 }
871
872 /*void getPseudoObjects(v3f origin, f32 max_d,
873                 core::array<DistanceSortedObject> &dest)
874 {
875 }*/
876
877 /*
878         Serialization
879 */
880
881 void MapBlock::serialize(std::ostream &os, u8 version)
882 {
883         if(!ser_ver_supported(version))
884                 throw VersionMismatchException("ERROR: MapBlock format not supported");
885         
886         if(data == NULL)
887         {
888                 throw SerializationError("ERROR: Not writing dummy block.");
889         }
890         
891         // These have no compression
892         if(version <= 3 || version == 5 || version == 6)
893         {
894                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
895                 
896                 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
897                 SharedBuffer<u8> dest(buflen);
898
899                 dest[0] = is_underground;
900                 for(u32 i=0; i<nodecount; i++)
901                 {
902                         u32 s = 1 + i * MapNode::serializedLength(version);
903                         data[i].serialize(&dest[s], version);
904                 }
905                 
906                 os.write((char*)*dest, dest.getSize());
907         }
908         else if(version <= 10)
909         {
910                 /*
911                         With compression.
912                         Compress the materials and the params separately.
913                 */
914                 
915                 // First byte
916                 os.write((char*)&is_underground, 1);
917
918                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
919
920                 // Get and compress materials
921                 SharedBuffer<u8> materialdata(nodecount);
922                 for(u32 i=0; i<nodecount; i++)
923                 {
924                         materialdata[i] = data[i].d;
925                 }
926                 compress(materialdata, os, version);
927
928                 // Get and compress lights
929                 SharedBuffer<u8> lightdata(nodecount);
930                 for(u32 i=0; i<nodecount; i++)
931                 {
932                         lightdata[i] = data[i].param;
933                 }
934                 compress(lightdata, os, version);
935                 
936                 if(version >= 10)
937                 {
938                         // Get and compress pressure
939                         SharedBuffer<u8> pressuredata(nodecount);
940                         for(u32 i=0; i<nodecount; i++)
941                         {
942                                 pressuredata[i] = data[i].pressure;
943                         }
944                         compress(pressuredata, os, version);
945                 }
946         }
947         // All other versions (newest)
948         else
949         {
950                 // First byte
951                 os.write((char*)&is_underground, 1);
952
953                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
954
955                 /*
956                         Get data
957                 */
958
959                 SharedBuffer<u8> databuf(nodecount*3);
960
961                 // Get contents
962                 for(u32 i=0; i<nodecount; i++)
963                 {
964                         databuf[i] = data[i].d;
965                 }
966
967                 // Get params
968                 for(u32 i=0; i<nodecount; i++)
969                 {
970                         databuf[i+nodecount] = data[i].param;
971                 }
972
973                 // Get pressure
974                 for(u32 i=0; i<nodecount; i++)
975                 {
976                         databuf[i+nodecount*2] = data[i].pressure;
977                 }
978
979                 /*
980                         Compress data to output stream
981                 */
982
983                 compress(databuf, os, version);
984         }
985 }
986
987 void MapBlock::deSerialize(std::istream &is, u8 version)
988 {
989         if(!ser_ver_supported(version))
990                 throw VersionMismatchException("ERROR: MapBlock format not supported");
991
992         // These have no compression
993         if(version <= 3 || version == 5 || version == 6)
994         {
995                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
996                 char tmp;
997                 is.read(&tmp, 1);
998                 if(is.gcount() != 1)
999                         throw SerializationError
1000                                         ("MapBlock::deSerialize: no enough input data");
1001                 is_underground = tmp;
1002                 for(u32 i=0; i<nodecount; i++)
1003                 {
1004                         s32 len = MapNode::serializedLength(version);
1005                         SharedBuffer<u8> d(len);
1006                         is.read((char*)*d, len);
1007                         if(is.gcount() != len)
1008                                 throw SerializationError
1009                                                 ("MapBlock::deSerialize: no enough input data");
1010                         data[i].deSerialize(*d, version);
1011                 }
1012         }
1013         else if(version <= 10)
1014         {
1015                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1016
1017                 u8 t8;
1018                 is.read((char*)&t8, 1);
1019                 is_underground = t8;
1020
1021                 {
1022                         // Uncompress and set material data
1023                         std::ostringstream os(std::ios_base::binary);
1024                         decompress(is, os, version);
1025                         std::string s = os.str();
1026                         if(s.size() != nodecount)
1027                                 throw SerializationError
1028                                                 ("MapBlock::deSerialize: invalid format");
1029                         for(u32 i=0; i<s.size(); i++)
1030                         {
1031                                 data[i].d = s[i];
1032                         }
1033                 }
1034                 {
1035                         // Uncompress and set param data
1036                         std::ostringstream os(std::ios_base::binary);
1037                         decompress(is, os, version);
1038                         std::string s = os.str();
1039                         if(s.size() != nodecount)
1040                                 throw SerializationError
1041                                                 ("MapBlock::deSerialize: invalid format");
1042                         for(u32 i=0; i<s.size(); i++)
1043                         {
1044                                 data[i].param = s[i];
1045                         }
1046                 }
1047         
1048                 if(version >= 10)
1049                 {
1050                         // Uncompress and set pressure data
1051                         std::ostringstream os(std::ios_base::binary);
1052                         decompress(is, os, version);
1053                         std::string s = os.str();
1054                         if(s.size() != nodecount)
1055                                 throw SerializationError
1056                                                 ("MapBlock::deSerialize: invalid format");
1057                         for(u32 i=0; i<s.size(); i++)
1058                         {
1059                                 data[i].pressure = s[i];
1060                         }
1061                 }
1062         }
1063         // All other versions (newest)
1064         else
1065         {
1066                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1067
1068                 u8 t8;
1069                 is.read((char*)&t8, 1);
1070                 is_underground = t8;
1071
1072                 // Uncompress data
1073                 std::ostringstream os(std::ios_base::binary);
1074                 decompress(is, os, version);
1075                 std::string s = os.str();
1076                 if(s.size() != nodecount*3)
1077                         throw SerializationError
1078                                         ("MapBlock::deSerialize: invalid format");
1079
1080                 // Set contents
1081                 for(u32 i=0; i<nodecount; i++)
1082                 {
1083                         data[i].d = s[i];
1084                 }
1085                 // Set params
1086                 for(u32 i=0; i<nodecount; i++)
1087                 {
1088                         data[i].param = s[i+nodecount];
1089                 }
1090                 // Set pressure
1091                 for(u32 i=0; i<nodecount; i++)
1092                 {
1093                         data[i].pressure = s[i+nodecount*2];
1094                 }
1095         }
1096 }
1097
1098
1099 //END