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