before adding day/night lighting
[oweals/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(v3s16 p, v3s16 face_dir)
151 {
152         try{
153                 MapNode n = getNodeParent(p);
154                 MapNode n2 = getNodeParent(p + face_dir);
155                 u8 light;
156                 /*if(n.solidness() < n2.solidness())
157                         light = n.getLight();
158                 else
159                         light = n2.getLight();*/
160                 if(n.getLight() > n2.getLight())
161                         light = n.getLight();
162                 else
163                         light = n2.getLight();
164
165                 // Make some nice difference to different sides
166
167                 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
168                         light = diminish_light(diminish_light(light));
169                 else if(face_dir.X == -1 || face_dir.Z == -1)
170                         light = diminish_light(light);*/
171
172                 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
173                         light = diminish_light(diminish_light(light));
174                 else if(face_dir.Z == 1 || face_dir.Z == -1)
175                         light = diminish_light(light);
176
177                 return light;
178         }
179         catch(InvalidPositionException &e)
180         {
181                 return 0;
182         }
183 }
184
185 /*
186         Gets node tile from any place relative to block.
187         Returns TILE_NODE if doesn't exist or should not be drawn.
188 */
189 TileSpec MapBlock::getNodeTile(v3s16 p, v3s16 face_dir)
190 {
191         TileSpec spec;
192
193         spec.feature = TILEFEAT_NONE;
194         try{
195                 MapNode n = getNodeParent(p);
196                 
197                 spec.id = n.getTile(face_dir);
198         }
199         catch(InvalidPositionException &e)
200         {
201                 spec.id = TILE_NONE;
202         }
203         
204         /*
205                 Check temporary modifications on this node
206         */
207         core::map<v3s16, NodeMod>::Node *n;
208         n = m_temp_mods.find(p);
209
210         // If modified
211         if(n != NULL)
212         {
213                 struct NodeMod mod = n->getValue();
214                 if(mod.type == NODEMOD_CHANGECONTENT)
215                 {
216                         spec.id = content_tile(mod.param, face_dir);
217                 }
218                 if(mod.type == NODEMOD_CRACK)
219                 {
220                 }
221         }
222         
223         return spec;
224 }
225
226 u8 MapBlock::getNodeContent(v3s16 p)
227 {
228         /*
229                 Check temporary modifications on this node
230         */
231         core::map<v3s16, NodeMod>::Node *n;
232         n = m_temp_mods.find(p);
233
234         // If modified
235         if(n != NULL)
236         {
237                 struct NodeMod mod = n->getValue();
238                 if(mod.type == NODEMOD_CHANGECONTENT)
239                 {
240                         // Overrides content
241                         return mod.param;
242                 }
243                 if(mod.type == NODEMOD_CRACK)
244                 {
245                         /*
246                                 Content doesn't change.
247                                 
248                                 face_contents works just like it should, because
249                                 there should not be faces between differently cracked
250                                 nodes.
251
252                                 If a semi-transparent node is cracked in front an
253                                 another one, it really doesn't matter whether there
254                                 is a cracked face drawn in between or not.
255                         */
256                 }
257         }
258         
259         try{
260                 MapNode n = getNodeParent(p);
261                 
262                 return n.d;
263         }
264         catch(InvalidPositionException &e)
265         {
266                 return CONTENT_IGNORE;
267         }
268 }
269
270 /*
271         startpos:
272         translate_dir: unit vector with only one of x, y or z
273         face_dir: unit vector with only one of x, y or z
274 */
275 void MapBlock::updateFastFaceRow(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(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(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()
478 {
479         /*v3s16 p = getPosRelative();
480         std::cout<<"MapBlock("<<p.X<<","<<p.Y<<","<<p.Z<<")"
481                         <<"::updateMesh(): ";*/
482                         //<<"::updateMesh()"<<std::endl;
483         
484         /*
485                 TODO: Change this to directly generate the mesh (and get rid
486                       of FastFaces)
487         */
488
489         core::list<FastFace*> *fastfaces_new = new core::list<FastFace*>;
490         
491         /*
492                 We are including the faces of the trailing edges of the block.
493                 This means that when something changes, the caller must
494                 also update the meshes of the blocks at the leading edges.
495         */
496
497         /*
498                 Go through every y,z and get top faces in rows of x+
499         */
500         for(s16 y=0; y<MAP_BLOCKSIZE; y++){
501         //for(s16 y=-1; y<MAP_BLOCKSIZE; y++){
502                 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
503                         updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE,
504                                         v3s16(1,0,0),
505                                         v3s16(0,1,0),
506                                         *fastfaces_new);
507                 }
508         }
509         /*
510                 Go through every x,y and get right faces in rows of z+
511         */
512         for(s16 x=0; x<MAP_BLOCKSIZE; x++){
513         //for(s16 x=-1; x<MAP_BLOCKSIZE; x++){
514                 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
515                         updateFastFaceRow(v3s16(x,y,0), MAP_BLOCKSIZE,
516                                         v3s16(0,0,1),
517                                         v3s16(1,0,0),
518                                         *fastfaces_new);
519                 }
520         }
521         /*
522                 Go through every y,z and get back faces in rows of x+
523         */
524         for(s16 z=0; z<MAP_BLOCKSIZE; z++){
525         //for(s16 z=-1; z<MAP_BLOCKSIZE; z++){
526                 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
527                         updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE,
528                                         v3s16(1,0,0),
529                                         v3s16(0,0,1),
530                                         *fastfaces_new);
531                 }
532         }
533
534         scene::SMesh *mesh_new = NULL;
535         
536         mesh_new = new scene::SMesh();
537         
538         if(fastfaces_new->getSize() > 0)
539         {
540                 MeshCollector collector;
541
542                 core::list<FastFace*>::Iterator i = fastfaces_new->begin();
543
544                 for(; i != fastfaces_new->end(); i++)
545                 {
546                         FastFace *f = *i;
547
548                         const u16 indices[] = {0,1,2,2,3,0};
549                         
550                         if(f->tile.feature == TILEFEAT_NONE)
551                         {
552                                 collector.append(g_tile_materials[f->tile.id], f->vertices, 4,
553                                                 indices, 6);
554                         }
555                         else
556                         {
557                                 // Not implemented
558                                 assert(0);
559                         }
560                 }
561
562                 collector.fillMesh(mesh_new);
563
564                 // Use VBO for mesh (this just would set this for ever buffer)
565                 mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
566                 
567                 /*std::cout<<"MapBlock has "<<fastfaces_new->getSize()<<" faces "
568                                 <<"and uses "<<mesh_new->getMeshBufferCount()
569                                 <<" materials (meshbuffers)"<<std::endl;*/
570         }
571         
572         /*
573                 Clear temporary FastFaces
574         */
575
576         core::list<FastFace*>::Iterator i;
577         i = fastfaces_new->begin();
578         for(; i != fastfaces_new->end(); i++)
579         {
580                 delete *i;
581         }
582         fastfaces_new->clear();
583         delete fastfaces_new;
584
585         /*
586                 Add special graphics:
587                 - torches
588                 
589                 TODO: Optimize by using same meshbuffer for same textures
590         */
591
592         /*scene::ISceneManager *smgr = NULL;
593         video::IVideoDriver* driver = NULL;
594         if(g_device)
595         {
596                 smgr = g_device->getSceneManager();
597                 driver = smgr->getVideoDriver();
598         }*/
599                         
600         for(s16 z=0; z<MAP_BLOCKSIZE; z++)
601         for(s16 y=0; y<MAP_BLOCKSIZE; y++)
602         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
603         {
604                 v3s16 p(x,y,z);
605
606                 MapNode &n = getNodeRef(x,y,z);
607                 
608                 if(n.d == CONTENT_TORCH)
609                 {
610                         //scene::IMeshBuffer *buf = new scene::SMeshBuffer();
611                         scene::SMeshBuffer *buf = new scene::SMeshBuffer();
612                         video::SColor c(255,255,255,255);
613
614                         video::S3DVertex vertices[4] =
615                         {
616                                 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
617                                 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
618                                 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
619                                 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
620                         };
621
622                         v3s16 dir = unpackDir(n.dir);
623
624                         for(s32 i=0; i<4; i++)
625                         {
626                                 if(dir == v3s16(1,0,0))
627                                         vertices[i].Pos.rotateXZBy(0);
628                                 if(dir == v3s16(-1,0,0))
629                                         vertices[i].Pos.rotateXZBy(180);
630                                 if(dir == v3s16(0,0,1))
631                                         vertices[i].Pos.rotateXZBy(90);
632                                 if(dir == v3s16(0,0,-1))
633                                         vertices[i].Pos.rotateXZBy(-90);
634                                 if(dir == v3s16(0,-1,0))
635                                         vertices[i].Pos.rotateXZBy(45);
636                                 if(dir == v3s16(0,1,0))
637                                         vertices[i].Pos.rotateXZBy(-45);
638
639                                 vertices[i].Pos += intToFloat(p + getPosRelative());
640                         }
641
642                         u16 indices[] = {0,1,2,2,3,0};
643                         buf->append(vertices, 4, indices, 6);
644
645                         // Set material
646                         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
647                         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
648                         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
649                         //buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
650                         buf->getMaterial().MaterialType
651                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
652                         if(dir == v3s16(0,-1,0))
653                                 buf->getMaterial().setTexture(0,
654                                                 g_texturecache.get("torch_on_floor"));
655                         else if(dir == v3s16(0,1,0))
656                                 buf->getMaterial().setTexture(0,
657                                                 g_texturecache.get("torch_on_ceiling"));
658                         // For backwards compatibility
659                         else if(dir == v3s16(0,0,0))
660                                 buf->getMaterial().setTexture(0,
661                                                 g_texturecache.get("torch_on_floor"));
662                         else
663                                 buf->getMaterial().setTexture(0, g_texturecache.get("torch"));
664
665                         // Add to mesh
666                         mesh_new->addMeshBuffer(buf);
667                         buf->drop();
668                 }
669         }
670
671         /*
672                 Do some stuff to the mesh
673         */
674
675         mesh_new->recalculateBoundingBox();
676
677         /*
678                 Delete new mesh if it is empty
679         */
680
681         if(mesh_new->getMeshBufferCount() == 0)
682         {
683                 mesh_new->drop();
684                 mesh_new = NULL;
685         }
686
687         /*
688                 Replace the mesh
689         */
690
691         mesh_mutex.Lock();
692
693         scene::SMesh *mesh_old = mesh;
694
695         mesh = mesh_new;
696         
697         if(mesh_old != NULL)
698         {
699                 // Remove hardware buffers of meshbuffers of mesh
700                 // NOTE: No way, this runs in a different thread and everything
701                 /*u32 c = mesh_old->getMeshBufferCount();
702                 for(u32 i=0; i<c; i++)
703                 {
704                         IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
705                 }*/
706                 // Drop the mesh
707                 mesh_old->drop();
708                 //delete mesh_old;
709         }
710
711         mesh_mutex.Unlock();
712         
713         //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
714 }
715
716 /*
717         Propagates sunlight down through the block.
718         Doesn't modify nodes that are not affected by sunlight.
719         
720         Returns false if sunlight at bottom block is invalid
721         Returns true if bottom block doesn't exist.
722
723         If there is a block above, continues from it.
724         If there is no block above, assumes there is sunlight, unless
725         is_underground is set.
726
727         At the moment, all sunlighted nodes are added to light_sources.
728         TODO: This could be optimized.
729 */
730 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
731 {
732         // Whether the sunlight at the top of the bottom block is valid
733         bool block_below_is_valid = true;
734         
735         v3s16 pos_relative = getPosRelative();
736         
737         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
738         {
739                 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
740                 {
741                         bool no_sunlight = false;
742                         bool no_top_block = false;
743                         // Check if node above block has sunlight
744                         try{
745                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
746                                 if(n.getLight() != LIGHT_SUN)
747                                 {
748                                         /*if(is_underground)
749                                         {
750                                                 no_sunlight = true;
751                                         }*/
752                                         no_sunlight = true;
753                                 }
754                         }
755                         catch(InvalidPositionException &e)
756                         {
757                                 no_top_block = true;
758                                 
759                                 // TODO: This makes over-ground roofed places sunlighted
760                                 // Assume sunlight, unless is_underground==true
761                                 if(is_underground)
762                                 {
763                                         no_sunlight = true;
764                                 }
765                                 
766                                 // TODO: There has to be some way to allow this behaviour
767                                 // As of now, it just makes everything dark.
768                                 // No sunlight here
769                                 //no_sunlight = true;
770                         }
771
772                         /*std::cout<<"("<<x<<","<<z<<"): "
773                                         <<"no_top_block="<<no_top_block
774                                         <<", is_underground="<<is_underground
775                                         <<", no_sunlight="<<no_sunlight
776                                         <<std::endl;*/
777                 
778                         s16 y = MAP_BLOCKSIZE-1;
779                         
780                         if(no_sunlight == false)
781                         {
782                                 // Continue spreading sunlight downwards through transparent
783                                 // nodes
784                                 for(; y >= 0; y--)
785                                 {
786                                         v3s16 pos(x, y, z);
787                                         
788                                         MapNode &n = getNodeRef(pos);
789
790                                         if(n.sunlight_propagates())
791                                         {
792                                                 n.setLight(LIGHT_SUN);
793
794                                                 light_sources.insert(pos_relative + pos, true);
795                                         }
796                                         else{
797                                                 break;
798                                         }
799                                 }
800                         }
801
802                         bool sunlight_should_go_down = (y==-1);
803
804                         // Fill rest with black (only transparent ones)
805                         for(; y >= 0; y--){
806                                 v3s16 pos(x, y, z);
807                                 
808                                 MapNode &n = getNodeRef(pos);
809
810                                 if(n.light_propagates())
811                                 {
812                                         n.setLight(0);
813                                 }
814                                 else{
815                                         break;
816                                 }
817                         }
818
819                         /*
820                                 If the block below hasn't already been marked invalid:
821
822                                 Check if the node below the block has proper sunlight at top.
823                                 If not, the block below is invalid.
824                                 
825                                 Ignore non-transparent nodes as they always have no light
826                         */
827                         try
828                         {
829                         if(block_below_is_valid)
830                         {
831                                 MapNode n = getNodeParent(v3s16(x, -1, z));
832                                 if(n.light_propagates())
833                                 {
834                                         if(n.getLight() == LIGHT_SUN
835                                                         && sunlight_should_go_down == false)
836                                                 block_below_is_valid = false;
837                                         else if(n.getLight() != LIGHT_SUN
838                                                         && sunlight_should_go_down == true)
839                                                 block_below_is_valid = false;
840                                 }
841                         }//if
842                         }//try
843                         catch(InvalidPositionException &e)
844                         {
845                                 /*std::cout<<"InvalidBlockException for bottom block node"
846                                                 <<std::endl;*/
847                                 // Just no block below, no need to panic.
848                         }
849                 }
850         }
851
852         return block_below_is_valid;
853 }
854
855 void MapBlock::copyTo(VoxelManipulator &dst)
856 {
857         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
858         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
859         
860         dst.copyFrom(data, data_area, v3s16(0,0,0),
861                         getPosRelative(), data_size);
862 }
863
864 /*void getPseudoObjects(v3f origin, f32 max_d,
865                 core::array<DistanceSortedObject> &dest)
866 {
867 }*/
868
869 /*
870         Serialization
871 */
872
873 void MapBlock::serialize(std::ostream &os, u8 version)
874 {
875         if(!ser_ver_supported(version))
876                 throw VersionMismatchException("ERROR: MapBlock format not supported");
877         
878         if(data == NULL)
879         {
880                 throw SerializationError("ERROR: Not writing dummy block.");
881         }
882         
883         // These have no compression
884         if(version <= 3 || version == 5 || version == 6)
885         {
886                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
887                 
888                 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
889                 SharedBuffer<u8> dest(buflen);
890
891                 dest[0] = is_underground;
892                 for(u32 i=0; i<nodecount; i++)
893                 {
894                         u32 s = 1 + i * MapNode::serializedLength(version);
895                         data[i].serialize(&dest[s], version);
896                 }
897                 
898                 os.write((char*)*dest, dest.getSize());
899         }
900         else if(version <= 10)
901         {
902                 /*
903                         With compression.
904                         Compress the materials and the params separately.
905                 */
906                 
907                 // First byte
908                 os.write((char*)&is_underground, 1);
909
910                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
911
912                 // Get and compress materials
913                 SharedBuffer<u8> materialdata(nodecount);
914                 for(u32 i=0; i<nodecount; i++)
915                 {
916                         materialdata[i] = data[i].d;
917                 }
918                 compress(materialdata, os, version);
919
920                 // Get and compress lights
921                 SharedBuffer<u8> lightdata(nodecount);
922                 for(u32 i=0; i<nodecount; i++)
923                 {
924                         lightdata[i] = data[i].param;
925                 }
926                 compress(lightdata, os, version);
927                 
928                 if(version >= 10)
929                 {
930                         // Get and compress pressure
931                         SharedBuffer<u8> pressuredata(nodecount);
932                         for(u32 i=0; i<nodecount; i++)
933                         {
934                                 pressuredata[i] = data[i].pressure;
935                         }
936                         compress(pressuredata, os, version);
937                 }
938         }
939         // All other versions (newest)
940         else
941         {
942                 // First byte
943                 os.write((char*)&is_underground, 1);
944
945                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
946
947                 /*
948                         Get data
949                 */
950
951                 SharedBuffer<u8> databuf(nodecount*3);
952
953                 // Get contents
954                 for(u32 i=0; i<nodecount; i++)
955                 {
956                         databuf[i] = data[i].d;
957                 }
958
959                 // Get params
960                 for(u32 i=0; i<nodecount; i++)
961                 {
962                         databuf[i+nodecount] = data[i].param;
963                 }
964
965                 // Get pressure
966                 for(u32 i=0; i<nodecount; i++)
967                 {
968                         databuf[i+nodecount*2] = data[i].pressure;
969                 }
970
971                 /*
972                         Compress data to output stream
973                 */
974
975                 compress(databuf, os, version);
976         }
977 }
978
979 void MapBlock::deSerialize(std::istream &is, u8 version)
980 {
981         if(!ser_ver_supported(version))
982                 throw VersionMismatchException("ERROR: MapBlock format not supported");
983
984         // These have no compression
985         if(version <= 3 || version == 5 || version == 6)
986         {
987                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
988                 char tmp;
989                 is.read(&tmp, 1);
990                 if(is.gcount() != 1)
991                         throw SerializationError
992                                         ("MapBlock::deSerialize: no enough input data");
993                 is_underground = tmp;
994                 for(u32 i=0; i<nodecount; i++)
995                 {
996                         s32 len = MapNode::serializedLength(version);
997                         SharedBuffer<u8> d(len);
998                         is.read((char*)*d, len);
999                         if(is.gcount() != len)
1000                                 throw SerializationError
1001                                                 ("MapBlock::deSerialize: no enough input data");
1002                         data[i].deSerialize(*d, version);
1003                 }
1004         }
1005         else if(version <= 10)
1006         {
1007                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1008
1009                 u8 t8;
1010                 is.read((char*)&t8, 1);
1011                 is_underground = t8;
1012
1013                 {
1014                         // Uncompress and set material data
1015                         std::ostringstream os(std::ios_base::binary);
1016                         decompress(is, os, version);
1017                         std::string s = os.str();
1018                         if(s.size() != nodecount)
1019                                 throw SerializationError
1020                                                 ("MapBlock::deSerialize: invalid format");
1021                         for(u32 i=0; i<s.size(); i++)
1022                         {
1023                                 data[i].d = s[i];
1024                         }
1025                 }
1026                 {
1027                         // Uncompress and set param data
1028                         std::ostringstream os(std::ios_base::binary);
1029                         decompress(is, os, version);
1030                         std::string s = os.str();
1031                         if(s.size() != nodecount)
1032                                 throw SerializationError
1033                                                 ("MapBlock::deSerialize: invalid format");
1034                         for(u32 i=0; i<s.size(); i++)
1035                         {
1036                                 data[i].param = s[i];
1037                         }
1038                 }
1039         
1040                 if(version >= 10)
1041                 {
1042                         // Uncompress and set pressure data
1043                         std::ostringstream os(std::ios_base::binary);
1044                         decompress(is, os, version);
1045                         std::string s = os.str();
1046                         if(s.size() != nodecount)
1047                                 throw SerializationError
1048                                                 ("MapBlock::deSerialize: invalid format");
1049                         for(u32 i=0; i<s.size(); i++)
1050                         {
1051                                 data[i].pressure = s[i];
1052                         }
1053                 }
1054         }
1055         // All other versions (newest)
1056         else
1057         {
1058                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1059
1060                 u8 t8;
1061                 is.read((char*)&t8, 1);
1062                 is_underground = t8;
1063
1064                 // Uncompress data
1065                 std::ostringstream os(std::ios_base::binary);
1066                 decompress(is, os, version);
1067                 std::string s = os.str();
1068                 if(s.size() != nodecount*3)
1069                         throw SerializationError
1070                                         ("MapBlock::deSerialize: invalid format");
1071
1072                 // Set contents
1073                 for(u32 i=0; i<nodecount; i++)
1074                 {
1075                         data[i].d = s[i];
1076                 }
1077                 // Set params
1078                 for(u32 i=0; i<nodecount; i++)
1079                 {
1080                         data[i].param = s[i+nodecount];
1081                 }
1082                 // Set pressure
1083                 for(u32 i=0; i<nodecount; i++)
1084                 {
1085                         data[i].pressure = s[i+nodecount*2];
1086                 }
1087         }
1088 }
1089
1090
1091 //END