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