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