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