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