]> git.lizzy.rs Git - minetest.git/blob - src/mapblock.cpp
This map generator is starting to look pretty good now... also, disabled loading...
[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 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
33                 m_parent(parent),
34                 m_pos(pos),
35                 changed(true),
36                 is_underground(false),
37                 m_day_night_differs(false),
38                 m_objects(this)
39 {
40         data = NULL;
41         if(dummy == false)
42                 reallocate();
43         
44         m_spawn_timer = -10000;
45
46 #ifndef SERVER
47         m_mesh_expired = false;
48         mesh_mutex.Init();
49         mesh = NULL;
50         m_temp_mods_mutex.Init();
51 #endif
52 }
53
54 MapBlock::~MapBlock()
55 {
56 #ifndef SERVER
57         {
58                 JMutexAutoLock lock(mesh_mutex);
59                 
60                 if(mesh)
61                 {
62                         mesh->drop();
63                         mesh = NULL;
64                 }
65         }
66 #endif
67
68         if(data)
69                 delete[] data;
70 }
71
72 bool MapBlock::isValidPositionParent(v3s16 p)
73 {
74         if(isValidPosition(p))
75         {
76                 return true;
77         }
78         else{
79                 return m_parent->isValidPosition(getPosRelative() + p);
80         }
81 }
82
83 MapNode MapBlock::getNodeParent(v3s16 p)
84 {
85         if(isValidPosition(p) == false)
86         {
87                 return m_parent->getNode(getPosRelative() + p);
88         }
89         else
90         {
91                 if(data == NULL)
92                         throw InvalidPositionException();
93                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
94         }
95 }
96
97 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
98 {
99         if(isValidPosition(p) == false)
100         {
101                 m_parent->setNode(getPosRelative() + p, n);
102         }
103         else
104         {
105                 if(data == NULL)
106                         throw InvalidPositionException();
107                 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
108         }
109 }
110
111 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
112 {
113         if(isValidPosition(p) == false)
114         {
115                 try{
116                         return m_parent->getNode(getPosRelative() + p);
117                 }
118                 catch(InvalidPositionException &e)
119                 {
120                         return MapNode(CONTENT_IGNORE);
121                 }
122         }
123         else
124         {
125                 if(data == NULL)
126                 {
127                         return MapNode(CONTENT_IGNORE);
128                 }
129                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
130         }
131 }
132
133 /*
134         Parameters must consist of air and !air.
135         Order doesn't matter.
136
137         If either of the nodes doesn't exist, light is 0.
138         
139         parameters:
140                 daynight_ratio: 0...1000
141                 n: getNodeParent(p)
142                 n2: getNodeParent(p + face_dir)
143                 face_dir: axis oriented unit vector from p to p2
144         
145         returns encoded light value.
146 */
147 u8 MapBlock::getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
148                 v3s16 face_dir)
149 {
150         try{
151                 // DEBUG
152                 /*{
153                         if(n.d == CONTENT_WATER)
154                         {
155                                 u8 l = n.param2*2;
156                                 if(l > LIGHT_MAX)
157                                         l = LIGHT_MAX;
158                                 return l;
159                         }
160                         if(n2.d == CONTENT_WATER)
161                         {
162                                 u8 l = n2.param2*2;
163                                 if(l > LIGHT_MAX)
164                                         l = LIGHT_MAX;
165                                 return l;
166                         }
167                 }*/
168
169
170                 u8 light;
171                 u8 l1 = n.getLightBlend(daynight_ratio);
172                 u8 l2 = n2.getLightBlend(daynight_ratio);
173                 if(l1 > l2)
174                         light = l1;
175                 else
176                         light = l2;
177
178                 // Make some nice difference to different sides
179
180                 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
181                         light = diminish_light(diminish_light(light));
182                 else if(face_dir.X == -1 || face_dir.Z == -1)
183                         light = diminish_light(light);*/
184
185                 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
186                         light = diminish_light(diminish_light(light));
187                 else if(face_dir.Z == 1 || face_dir.Z == -1)
188                         light = diminish_light(light);
189
190                 return light;
191         }
192         catch(InvalidPositionException &e)
193         {
194                 return 0;
195         }
196 }
197
198 #ifndef SERVER
199
200 void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
201                 v3s16 dir, v3f scale, v3f posRelative_f,
202                 core::array<FastFace> &dest)
203 {
204         FastFace face;
205         
206         // Position is at the center of the cube.
207         v3f pos = p * BS;
208         posRelative_f *= BS;
209
210         v3f vertex_pos[4];
211         // If looking towards z+, this is the face that is behind
212         // the center point, facing towards z+.
213         vertex_pos[0] = v3f( BS/2,-BS/2,BS/2);
214         vertex_pos[1] = v3f(-BS/2,-BS/2,BS/2);
215         vertex_pos[2] = v3f(-BS/2, BS/2,BS/2);
216         vertex_pos[3] = v3f( BS/2, BS/2,BS/2);
217         
218         if(dir == v3s16(0,0,1))
219         {
220                 for(u16 i=0; i<4; i++)
221                         vertex_pos[i].rotateXZBy(0);
222         }
223         else if(dir == v3s16(0,0,-1))
224         {
225                 for(u16 i=0; i<4; i++)
226                         vertex_pos[i].rotateXZBy(180);
227         }
228         else if(dir == v3s16(1,0,0))
229         {
230                 for(u16 i=0; i<4; i++)
231                         vertex_pos[i].rotateXZBy(-90);
232         }
233         else if(dir == v3s16(-1,0,0))
234         {
235                 for(u16 i=0; i<4; i++)
236                         vertex_pos[i].rotateXZBy(90);
237         }
238         else if(dir == v3s16(0,1,0))
239         {
240                 for(u16 i=0; i<4; i++)
241                         vertex_pos[i].rotateYZBy(-90);
242         }
243         else if(dir == v3s16(0,-1,0))
244         {
245                 for(u16 i=0; i<4; i++)
246                         vertex_pos[i].rotateYZBy(90);
247         }
248
249         for(u16 i=0; i<4; i++)
250         {
251                 vertex_pos[i].X *= scale.X;
252                 vertex_pos[i].Y *= scale.Y;
253                 vertex_pos[i].Z *= scale.Z;
254                 vertex_pos[i] += pos + posRelative_f;
255         }
256
257         f32 abs_scale = 1.;
258         if     (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
259         else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
260         else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
261
262         v3f zerovector = v3f(0,0,0);
263         
264         //u8 li = decode_light(light);
265         u8 li = light;
266         //u8 li = 255; //DEBUG
267
268         u8 alpha = tile.alpha;
269         /*u8 alpha = 255;
270         if(tile.id == TILE_WATER)
271                 alpha = WATER_ALPHA;*/
272
273         video::SColor c = video::SColor(alpha,li,li,li);
274
275         face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
276                         core::vector2d<f32>(0,1));
277         face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
278                         core::vector2d<f32>(abs_scale,1));
279         face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
280                         core::vector2d<f32>(abs_scale,0));
281         face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
282                         core::vector2d<f32>(0,0));
283
284         face.tile = tile;
285         //DEBUG
286         //f->tile = TILE_STONE;
287         
288         dest.push_back(face);
289         //return f;
290 }
291         
292 /*
293         Gets node tile from any place relative to block.
294         Returns TILE_NODE if doesn't exist or should not be drawn.
295 */
296 TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
297                 NodeModMap &temp_mods)
298 {
299         TileSpec spec;
300         spec = mn.getTile(face_dir);
301         
302         /*
303                 Check temporary modifications on this node
304         */
305         /*core::map<v3s16, NodeMod>::Node *n;
306         n = m_temp_mods.find(p);
307         // If modified
308         if(n != NULL)
309         {
310                 struct NodeMod mod = n->getValue();*/
311         NodeMod mod;
312         if(temp_mods.get(p, &mod))
313         {
314                 if(mod.type == NODEMOD_CHANGECONTENT)
315                 {
316                         MapNode mn2(mod.param);
317                         spec = mn2.getTile(face_dir);
318                 }
319                 if(mod.type == NODEMOD_CRACK)
320                 {
321                         std::ostringstream os;
322                         os<<"[crack"<<mod.param;
323
324                         textureid_t tid = g_irrlicht->getTextureId(os.str());
325                         spec.spec.addTid(tid);
326                 }
327         }
328         
329         return spec;
330 }
331
332 u8 MapBlock::getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
333 {
334         /*
335                 Check temporary modifications on this node
336         */
337         /*core::map<v3s16, NodeMod>::Node *n;
338         n = m_temp_mods.find(p);
339         // If modified
340         if(n != NULL)
341         {
342                 struct NodeMod mod = n->getValue();*/
343         NodeMod mod;
344         if(temp_mods.get(p, &mod))
345         {
346                 if(mod.type == NODEMOD_CHANGECONTENT)
347                 {
348                         // Overrides content
349                         return mod.param;
350                 }
351                 if(mod.type == NODEMOD_CRACK)
352                 {
353                         /*
354                                 Content doesn't change.
355                                 
356                                 face_contents works just like it should, because
357                                 there should not be faces between differently cracked
358                                 nodes.
359
360                                 If a semi-transparent node is cracked in front an
361                                 another one, it really doesn't matter whether there
362                                 is a cracked face drawn in between or not.
363                         */
364                 }
365         }
366
367         return mn.d;
368 }
369
370 /*
371         startpos:
372         translate_dir: unit vector with only one of x, y or z
373         face_dir: unit vector with only one of x, y or z
374 */
375 void MapBlock::updateFastFaceRow(
376                 u32 daynight_ratio,
377                 v3f posRelative_f,
378                 v3s16 startpos,
379                 u16 length,
380                 v3s16 translate_dir,
381                 v3f translate_dir_f,
382                 v3s16 face_dir,
383                 v3f face_dir_f,
384                 core::array<FastFace> &dest,
385                 NodeModMap &temp_mods)
386 {
387         v3s16 p = startpos;
388         
389         u16 continuous_tiles_count = 0;
390         
391         MapNode n0 = getNodeParentNoEx(p);
392         MapNode n1 = getNodeParentNoEx(p + face_dir);
393
394         u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
395                 
396         TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
397         TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
398
399         for(u16 j=0; j<length; j++)
400         {
401                 bool next_is_different = true;
402                 
403                 v3s16 p_next;
404                 MapNode n0_next;
405                 MapNode n1_next;
406                 TileSpec tile0_next;
407                 TileSpec tile1_next;
408                 u8 light_next = 0;
409
410                 if(j != length - 1)
411                 {
412                         p_next = p + translate_dir;
413                         n0_next = getNodeParentNoEx(p_next);
414                         n1_next = getNodeParentNoEx(p_next + face_dir);
415                         tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
416                         tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
417                         light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
418
419                         if(tile0_next == tile0
420                                         && tile1_next == tile1
421                                         && light_next == light)
422                         {
423                                 next_is_different = false;
424                         }
425                 }
426
427                 continuous_tiles_count++;
428                 
429                 if(next_is_different)
430                 {
431                         /*
432                                 Create a face if there should be one
433                         */
434                         //u8 mf = face_contents(tile0, tile1);
435                         // This is hackish
436                         u8 content0 = getNodeContent(p, n0, temp_mods);
437                         u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
438                         u8 mf = face_contents(content0, content1);
439                         
440                         if(mf != 0)
441                         {
442                                 // Floating point conversion of the position vector
443                                 v3f pf(p.X, p.Y, p.Z);
444                                 // Center point of face (kind of)
445                                 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
446                                 v3f scale(1,1,1);
447                                 if(translate_dir.X != 0){
448                                         scale.X = continuous_tiles_count;
449                                 }
450                                 if(translate_dir.Y != 0){
451                                         scale.Y = continuous_tiles_count;
452                                 }
453                                 if(translate_dir.Z != 0){
454                                         scale.Z = continuous_tiles_count;
455                                 }
456                                 
457                                 //FastFace *f;
458
459                                 // If node at sp (tile0) is more solid
460                                 if(mf == 1)
461                                 {
462                                         makeFastFace(tile0, decode_light(light),
463                                                         sp, face_dir, scale,
464                                                         posRelative_f, dest);
465                                 }
466                                 // If node at sp is less solid (mf == 2)
467                                 else
468                                 {
469                                         makeFastFace(tile1, decode_light(light),
470                                                         sp+face_dir_f, -face_dir, scale,
471                                                         posRelative_f, dest);
472                                 }
473                                 //dest.push_back(f);
474                         }
475
476                         continuous_tiles_count = 0;
477                         n0 = n0_next;
478                         n1 = n1_next;
479                         tile0 = tile0_next;
480                         tile1 = tile1_next;
481                         light = light_next;
482                 }
483                 
484                 p = p_next;
485         }
486 }
487
488 /*
489         This is used because CMeshBuffer::append() is very slow
490 */
491 struct PreMeshBuffer
492 {
493         video::SMaterial material;
494         core::array<u16> indices;
495         core::array<video::S3DVertex> vertices;
496 };
497
498 class MeshCollector
499 {
500 public:
501         void append(
502                         video::SMaterial material,
503                         const video::S3DVertex* const vertices,
504                         u32 numVertices,
505                         const u16* const indices,
506                         u32 numIndices
507                 )
508         {
509                 PreMeshBuffer *p = NULL;
510                 for(u32 i=0; i<m_prebuffers.size(); i++)
511                 {
512                         PreMeshBuffer &pp = m_prebuffers[i];
513                         if(pp.material != material)
514                                 continue;
515
516                         p = &pp;
517                         break;
518                 }
519
520                 if(p == NULL)
521                 {
522                         PreMeshBuffer pp;
523                         pp.material = material;
524                         m_prebuffers.push_back(pp);
525                         p = &m_prebuffers[m_prebuffers.size()-1];
526                 }
527
528                 u32 vertex_count = p->vertices.size();
529                 for(u32 i=0; i<numIndices; i++)
530                 {
531                         u32 j = indices[i] + vertex_count;
532                         if(j > 65535)
533                         {
534                                 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
535                                 // NOTE: Fix is to just add an another MeshBuffer
536                         }
537                         p->indices.push_back(j);
538                 }
539                 for(u32 i=0; i<numVertices; i++)
540                 {
541                         p->vertices.push_back(vertices[i]);
542                 }
543         }
544
545         void fillMesh(scene::SMesh *mesh)
546         {
547                 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
548                                 <<" meshbuffers"<<std::endl;*/
549                 for(u32 i=0; i<m_prebuffers.size(); i++)
550                 {
551                         PreMeshBuffer &p = m_prebuffers[i];
552
553                         /*dstream<<"p.vertices.size()="<<p.vertices.size()
554                                         <<", p.indices.size()="<<p.indices.size()
555                                         <<std::endl;*/
556                         
557                         // Create meshbuffer
558                         
559                         // This is a "Standard MeshBuffer",
560                         // it's a typedeffed CMeshBuffer<video::S3DVertex>
561                         scene::SMeshBuffer *buf = new scene::SMeshBuffer();
562                         // Set material
563                         buf->Material = p.material;
564                         //((scene::SMeshBuffer*)buf)->Material = p.material;
565                         // Use VBO
566                         //buf->setHardwareMappingHint(scene::EHM_STATIC);
567                         // Add to mesh
568                         mesh->addMeshBuffer(buf);
569                         // Mesh grabbed it
570                         buf->drop();
571
572                         buf->append(p.vertices.pointer(), p.vertices.size(),
573                                         p.indices.pointer(), p.indices.size());
574                 }
575         }
576
577 private:
578         core::array<PreMeshBuffer> m_prebuffers;
579 };
580
581 void MapBlock::updateMesh(u32 daynight_ratio)
582 {
583 #if 0
584         /*
585                 DEBUG: If mesh has been generated, don't generate it again
586         */
587         {
588                 JMutexAutoLock meshlock(mesh_mutex);
589                 if(mesh != NULL)
590                         return;
591         }
592 #endif
593         
594         // 4-21ms for MAP_BLOCKSIZE=16
595         // 24-155ms for MAP_BLOCKSIZE=32
596         //TimeTaker timer1("updateMesh()");
597
598         core::array<FastFace> fastfaces_new;
599         
600         v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
601                         getPosRelative().Z); // floating point conversion
602         
603         
604         /*
605                 Avoid interlocks by copying m_temp_mods
606         */
607         NodeModMap temp_mods;
608         {
609                 JMutexAutoLock lock(m_temp_mods_mutex);
610                 m_temp_mods.copy(temp_mods);
611         }
612
613         /*
614                 We are including the faces of the trailing edges of the block.
615                 This means that when something changes, the caller must
616                 also update the meshes of the blocks at the leading edges.
617
618                 NOTE: This is the slowest part of this method.
619         */
620         
621         {
622                 // 4-23ms for MAP_BLOCKSIZE=16
623                 //TimeTaker timer2("updateMesh() collect");
624
625                 /*
626                         Go through every y,z and get top faces in rows of x+
627                 */
628                 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
629                         for(s16 z=0; z<MAP_BLOCKSIZE; z++){
630                                 updateFastFaceRow(daynight_ratio, posRelative_f,
631                                                 v3s16(0,y,z), MAP_BLOCKSIZE,
632                                                 v3s16(1,0,0), //dir
633                                                 v3f  (1,0,0),
634                                                 v3s16(0,1,0), //face dir
635                                                 v3f  (0,1,0),
636                                                 fastfaces_new,
637                                                 temp_mods);
638                         }
639                 }
640                 /*
641                         Go through every x,y and get right faces in rows of z+
642                 */
643                 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
644                         for(s16 y=0; y<MAP_BLOCKSIZE; y++){
645                                 updateFastFaceRow(daynight_ratio, posRelative_f,
646                                                 v3s16(x,y,0), MAP_BLOCKSIZE,
647                                                 v3s16(0,0,1),
648                                                 v3f  (0,0,1),
649                                                 v3s16(1,0,0),
650                                                 v3f  (1,0,0),
651                                                 fastfaces_new,
652                                                 temp_mods);
653                         }
654                 }
655                 /*
656                         Go through every y,z and get back faces in rows of x+
657                 */
658                 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
659                         for(s16 y=0; y<MAP_BLOCKSIZE; y++){
660                                 updateFastFaceRow(daynight_ratio, posRelative_f,
661                                                 v3s16(0,y,z), MAP_BLOCKSIZE,
662                                                 v3s16(1,0,0),
663                                                 v3f  (1,0,0),
664                                                 v3s16(0,0,1),
665                                                 v3f  (0,0,1),
666                                                 fastfaces_new,
667                                                 temp_mods);
668                         }
669                 }
670         }
671
672         // End of slow part
673
674         /*
675                 Convert FastFaces to SMesh
676         */
677
678         scene::SMesh *mesh_new = NULL;
679         
680         mesh_new = new scene::SMesh();
681         
682         MeshCollector collector;
683
684         if(fastfaces_new.size() > 0)
685         {
686                 // avg 0ms (100ms spikes when loading textures the first time)
687                 //TimeTaker timer2("updateMesh() mesh building");
688
689                 video::SMaterial material;
690                 material.Lighting = false;
691                 material.BackfaceCulling = false;
692                 material.setFlag(video::EMF_BILINEAR_FILTER, false);
693                 material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
694                 material.setFlag(video::EMF_FOG_ENABLE, true);
695
696                 for(u32 i=0; i<fastfaces_new.size(); i++)
697                 {
698                         FastFace &f = fastfaces_new[i];
699
700                         const u16 indices[] = {0,1,2,2,3,0};
701
702                         video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
703                         if(texture == NULL)
704                                 continue;
705
706                         material.setTexture(0, texture);
707                         if(f.tile.alpha != 255)
708                                 material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
709                         else
710                                 material.MaterialType = video::EMT_SOLID;
711                         
712                         collector.append(material, f.vertices, 4, indices, 6);
713                 }
714         }
715
716         /*
717                 Add special graphics:
718                 - torches
719                 - flowing water
720         */
721
722         // 0ms
723         //TimeTaker timer2("updateMesh() adding special stuff");
724
725         // Flowing water material
726         video::SMaterial material_w1;
727         material_w1.setFlag(video::EMF_LIGHTING, false);
728         material_w1.setFlag(video::EMF_BACK_FACE_CULLING, false);
729         material_w1.setFlag(video::EMF_BILINEAR_FILTER, false);
730         material_w1.setFlag(video::EMF_FOG_ENABLE, true);
731         material_w1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
732         material_w1.setTexture(0,
733                         g_irrlicht->getTexture("water.png"));
734
735         for(s16 z=0; z<MAP_BLOCKSIZE; z++)
736         for(s16 y=0; y<MAP_BLOCKSIZE; y++)
737         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
738         {
739                 v3s16 p(x,y,z);
740
741                 MapNode &n = getNodeRef(x,y,z);
742                 
743                 /*
744                         Add torches to mesh
745                 */
746                 if(n.d == CONTENT_TORCH)
747                 {
748                         video::SColor c(255,255,255,255);
749
750                         video::S3DVertex vertices[4] =
751                         {
752                                 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
753                                 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
754                                 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
755                                 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
756                         };
757
758                         v3s16 dir = unpackDir(n.dir);
759
760                         for(s32 i=0; i<4; i++)
761                         {
762                                 if(dir == v3s16(1,0,0))
763                                         vertices[i].Pos.rotateXZBy(0);
764                                 if(dir == v3s16(-1,0,0))
765                                         vertices[i].Pos.rotateXZBy(180);
766                                 if(dir == v3s16(0,0,1))
767                                         vertices[i].Pos.rotateXZBy(90);
768                                 if(dir == v3s16(0,0,-1))
769                                         vertices[i].Pos.rotateXZBy(-90);
770                                 if(dir == v3s16(0,-1,0))
771                                         vertices[i].Pos.rotateXZBy(45);
772                                 if(dir == v3s16(0,1,0))
773                                         vertices[i].Pos.rotateXZBy(-45);
774
775                                 vertices[i].Pos += intToFloat(p + getPosRelative());
776                         }
777
778                         // Set material
779                         video::SMaterial material;
780                         material.setFlag(video::EMF_LIGHTING, false);
781                         material.setFlag(video::EMF_BACK_FACE_CULLING, false);
782                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
783                         //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
784                         material.MaterialType
785                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
786                         if(dir == v3s16(0,-1,0))
787                                 material.setTexture(0,
788                                                 g_irrlicht->getTexture("torch_on_floor.png"));
789                         else if(dir == v3s16(0,1,0))
790                                 material.setTexture(0,
791                                                 g_irrlicht->getTexture("torch_on_ceiling.png"));
792                         // For backwards compatibility
793                         else if(dir == v3s16(0,0,0))
794                                 material.setTexture(0,
795                                                 g_irrlicht->getTexture("torch_on_floor.png"));
796                         else
797                                 material.setTexture(0, 
798                                                 g_irrlicht->getTexture("torch.png"));
799
800                         u16 indices[] = {0,1,2,2,3,0};
801                         // Add to mesh collector
802                         collector.append(material, vertices, 4, indices, 6);
803                 }
804                 /*
805                         Add flowing water to mesh
806                 */
807                 else if(n.d == CONTENT_WATER)
808                 {
809                         bool top_is_water = false;
810                         try{
811                                 MapNode n = getNodeParent(v3s16(x,y+1,z));
812                                 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
813                                         top_is_water = true;
814                         }catch(InvalidPositionException &e){}
815                         
816                         u8 l = decode_light(n.getLightBlend(daynight_ratio));
817                         video::SColor c(WATER_ALPHA,l,l,l);
818                         
819                         // Neighbor water levels (key = relative position)
820                         // Includes current node
821                         core::map<v3s16, f32> neighbor_levels;
822                         core::map<v3s16, u8> neighbor_contents;
823                         v3s16 neighbor_dirs[9] = {
824                                 v3s16(0,0,0),
825                                 v3s16(0,0,1),
826                                 v3s16(0,0,-1),
827                                 v3s16(1,0,0),
828                                 v3s16(-1,0,0),
829                                 v3s16(1,0,1),
830                                 v3s16(-1,0,-1),
831                                 v3s16(1,0,-1),
832                                 v3s16(-1,0,1),
833                         };
834                         for(u32 i=0; i<9; i++)
835                         {
836                                 u8 content = CONTENT_AIR;
837                                 float level = -0.5 * BS;
838                                 try{
839                                         v3s16 p2 = p + neighbor_dirs[i];
840                                         MapNode n2 = getNodeParent(p2);
841
842                                         content = n2.d;
843
844                                         if(n2.d == CONTENT_WATERSOURCE)
845                                                 level = 0.5 * BS;
846                                         else if(n2.d == CONTENT_WATER)
847                                                 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0) * BS;
848                                 }
849                                 catch(InvalidPositionException &e){}
850                                 
851                                 neighbor_levels.insert(neighbor_dirs[i], level);
852                                 neighbor_contents.insert(neighbor_dirs[i], content);
853                         }
854
855                         //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
856                         //float water_level = neighbor_levels[v3s16(0,0,0)];
857
858                         // Corner heights (average between four waters)
859                         f32 corner_levels[4];
860                         
861                         v3s16 halfdirs[4] = {
862                                 v3s16(0,0,0),
863                                 v3s16(1,0,0),
864                                 v3s16(1,0,1),
865                                 v3s16(0,0,1),
866                         };
867                         for(u32 i=0; i<4; i++)
868                         {
869                                 v3s16 cornerdir = halfdirs[i];
870                                 float cornerlevel = 0;
871                                 u32 valid_count = 0;
872                                 for(u32 j=0; j<4; j++)
873                                 {
874                                         v3s16 neighbordir = cornerdir - halfdirs[j];
875                                         u8 content = neighbor_contents[neighbordir];
876                                         // Special case for source nodes
877                                         if(content == CONTENT_WATERSOURCE)
878                                         {
879                                                 cornerlevel = 0.5*BS;
880                                                 valid_count = 1;
881                                                 break;
882                                         }
883                                         else if(content == CONTENT_WATER)
884                                         {
885                                                 cornerlevel += neighbor_levels[neighbordir];
886                                                 valid_count++;
887                                         }
888                                         else if(content == CONTENT_AIR)
889                                         {
890                                                 cornerlevel += -0.5*BS;
891                                                 valid_count++;
892                                         }
893                                 }
894                                 if(valid_count > 0)
895                                         cornerlevel /= valid_count;
896                                 corner_levels[i] = cornerlevel;
897                         }
898
899                         /*
900                                 Generate sides
901                         */
902
903                         v3s16 side_dirs[4] = {
904                                 v3s16(1,0,0),
905                                 v3s16(-1,0,0),
906                                 v3s16(0,0,1),
907                                 v3s16(0,0,-1),
908                         };
909                         s16 side_corners[4][2] = {
910                                 {1, 2},
911                                 {3, 0},
912                                 {2, 3},
913                                 {0, 1},
914                         };
915                         for(u32 i=0; i<4; i++)
916                         {
917                                 v3s16 dir = side_dirs[i];
918
919                                 //float neighbor_level = neighbor_levels[dir];
920                                 /*if(neighbor_level > -0.5*BS + 0.001)
921                                         continue;*/
922                                 /*if(neighbor_level > water_level - 0.1*BS)
923                                         continue;*/
924
925                                 u8 neighbor_content = neighbor_contents[dir];
926
927                                 if(neighbor_content != CONTENT_AIR
928                                                 && neighbor_content != CONTENT_WATER)
929                                         continue;
930                                 
931                                 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
932
933                                 if(neighbor_is_water == true && top_is_water == false)
934                                         continue;
935                                 
936                                 video::S3DVertex vertices[4] =
937                                 {
938                                         /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
939                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
940                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
941                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
942                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
943                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
944                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
945                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
946                                 };
947                                 
948                                 if(top_is_water)
949                                 {
950                                         vertices[2].Pos.Y = 0.5*BS;
951                                         vertices[3].Pos.Y = 0.5*BS;
952                                 }
953                                 else
954                                 {
955                                         vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
956                                         vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
957                                 }
958
959                                 if(neighbor_is_water)
960                                 {
961                                         vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
962                                         vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
963                                 }
964                                 else
965                                 {
966                                         vertices[0].Pos.Y = -0.5*BS;
967                                         vertices[1].Pos.Y = -0.5*BS;
968                                 }
969                                 
970                                 for(s32 j=0; j<4; j++)
971                                 {
972                                         if(dir == v3s16(0,0,1))
973                                                 vertices[j].Pos.rotateXZBy(0);
974                                         if(dir == v3s16(0,0,-1))
975                                                 vertices[j].Pos.rotateXZBy(180);
976                                         if(dir == v3s16(-1,0,0))
977                                                 vertices[j].Pos.rotateXZBy(90);
978                                         if(dir == v3s16(1,0,-0))
979                                                 vertices[j].Pos.rotateXZBy(-90);
980
981                                         vertices[j].Pos += intToFloat(p + getPosRelative());
982                                 }
983
984                                 u16 indices[] = {0,1,2,2,3,0};
985                                 // Add to mesh collector
986                                 collector.append(material_w1, vertices, 4, indices, 6);
987                         }
988                         
989                         /*
990                                 Generate top side, if appropriate
991                         */
992                         
993                         if(top_is_water == false)
994                         {
995                                 video::S3DVertex vertices[4] =
996                                 {
997                                         video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
998                                         video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
999                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1000                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
1001                                 };
1002
1003                                 for(s32 i=0; i<4; i++)
1004                                 {
1005                                         //vertices[i].Pos.Y += water_level;
1006                                         //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1007                                         vertices[i].Pos.Y += corner_levels[i];
1008                                         vertices[i].Pos += intToFloat(p + getPosRelative());
1009                                 }
1010
1011                                 u16 indices[] = {0,1,2,2,3,0};
1012                                 // Add to mesh collector
1013                                 collector.append(material_w1, vertices, 4, indices, 6);
1014                         }
1015                 }
1016         }
1017
1018         /*
1019                 Add stuff from collector to mesh
1020         */
1021         
1022         collector.fillMesh(mesh_new);
1023
1024         /*
1025                 Do some stuff to the mesh
1026         */
1027
1028         mesh_new->recalculateBoundingBox();
1029
1030         /*
1031                 Delete new mesh if it is empty
1032         */
1033
1034         if(mesh_new->getMeshBufferCount() == 0)
1035         {
1036                 mesh_new->drop();
1037                 mesh_new = NULL;
1038         }
1039
1040         // Use VBO for mesh (this just would set this for ever buffer)
1041         // This will lead to infinite memory usage because or irrlicht.
1042         //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1043         
1044         /*std::cout<<"MapBlock has "<<fastfaces_new.size()<<" faces "
1045                         <<"and uses "<<mesh_new->getMeshBufferCount()
1046                         <<" materials (meshbuffers)"<<std::endl;*/
1047         
1048         /*
1049                 Replace the mesh
1050         */
1051
1052         mesh_mutex.Lock();
1053
1054         //scene::SMesh *mesh_old = mesh[daynight_i];
1055         //mesh[daynight_i] = mesh_new;
1056
1057         scene::SMesh *mesh_old = mesh;
1058         mesh = mesh_new;
1059         setMeshExpired(false);
1060         
1061         if(mesh_old != NULL)
1062         {
1063                 // Remove hardware buffers of meshbuffers of mesh
1064                 // NOTE: No way, this runs in a different thread and everything
1065                 /*u32 c = mesh_old->getMeshBufferCount();
1066                 for(u32 i=0; i<c; i++)
1067                 {
1068                         IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1069                 }*/
1070                 
1071                 /*dstream<<"mesh_old->getReferenceCount()="
1072                                 <<mesh_old->getReferenceCount()<<std::endl;
1073                 u32 c = mesh_old->getMeshBufferCount();
1074                 for(u32 i=0; i<c; i++)
1075                 {
1076                         scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1077                         dstream<<"buf->getReferenceCount()="
1078                                         <<buf->getReferenceCount()<<std::endl;
1079                 }*/
1080
1081                 // Drop the mesh
1082                 mesh_old->drop();
1083
1084                 //delete mesh_old;
1085         }
1086
1087         mesh_mutex.Unlock();
1088         
1089         //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1090 }
1091
1092 /*void MapBlock::updateMeshes(s32 first_i)
1093 {
1094         assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
1095         updateMesh(first_i);
1096         for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
1097         {
1098                 if(i == first_i)
1099                         continue;
1100                 updateMesh(i);
1101         }
1102 }*/
1103
1104 #endif // !SERVER
1105
1106 /*
1107         Propagates sunlight down through the block.
1108         Doesn't modify nodes that are not affected by sunlight.
1109         
1110         Returns false if sunlight at bottom block is invalid
1111         Returns true if bottom block doesn't exist.
1112
1113         If there is a block above, continues from it.
1114         If there is no block above, assumes there is sunlight, unless
1115         is_underground is set or highest node is water.
1116
1117         At the moment, all sunlighted nodes are added to light_sources.
1118         - SUGG: This could be optimized
1119
1120         Turns sunglighted mud into grass.
1121
1122         if remove_light==true, sets non-sunlighted nodes black.
1123
1124         if black_air_left!=NULL, it is set to true if non-sunlighted
1125         air is left in block.
1126 */
1127 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1128                 bool remove_light, bool *black_air_left,
1129                 bool grow_grass)
1130 {
1131         // Whether the sunlight at the top of the bottom block is valid
1132         bool block_below_is_valid = true;
1133         
1134         v3s16 pos_relative = getPosRelative();
1135         
1136         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1137         {
1138                 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1139                 {
1140 #if 1
1141                         bool no_sunlight = false;
1142                         bool no_top_block = false;
1143                         // Check if node above block has sunlight
1144                         try{
1145                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1146                                 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1147                                 {
1148                                         no_sunlight = true;
1149                                 }
1150                         }
1151                         catch(InvalidPositionException &e)
1152                         {
1153                                 no_top_block = true;
1154                                 
1155                                 // NOTE: This makes over-ground roofed places sunlighted
1156                                 // Assume sunlight, unless is_underground==true
1157                                 if(is_underground)
1158                                 {
1159                                         no_sunlight = true;
1160                                 }
1161                                 else
1162                                 {
1163                                         MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1164                                         if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1165                                         {
1166                                                 no_sunlight = true;
1167                                         }
1168                                 }
1169                                 // NOTE: As of now, this just would make everything dark.
1170                                 // No sunlight here
1171                                 //no_sunlight = true;
1172                         }
1173 #endif
1174 #if 0 // Doesn't work; nothing gets light.
1175                         bool no_sunlight = true;
1176                         bool no_top_block = false;
1177                         // Check if node above block has sunlight
1178                         try{
1179                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1180                                 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1181                                 {
1182                                         no_sunlight = false;
1183                                 }
1184                         }
1185                         catch(InvalidPositionException &e)
1186                         {
1187                                 no_top_block = true;
1188                         }
1189 #endif
1190
1191                         /*std::cout<<"("<<x<<","<<z<<"): "
1192                                         <<"no_top_block="<<no_top_block
1193                                         <<", is_underground="<<is_underground
1194                                         <<", no_sunlight="<<no_sunlight
1195                                         <<std::endl;*/
1196                 
1197                         s16 y = MAP_BLOCKSIZE-1;
1198                         
1199                         // This makes difference to diminishing in water.
1200                         bool stopped_to_solid_object = false;
1201                         
1202                         u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1203
1204                         for(; y >= 0; y--)
1205                         {
1206                                 v3s16 pos(x, y, z);
1207                                 MapNode &n = getNodeRef(pos);
1208                                 
1209                                 if(current_light == 0)
1210                                 {
1211                                         // Do nothing
1212                                 }
1213                                 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1214                                 {
1215                                         // Do nothing: Sunlight is continued
1216                                 }
1217                                 else if(n.light_propagates() == false)
1218                                 {
1219                                         if(grow_grass)
1220                                         {
1221                                                 bool upper_is_air = false;
1222                                                 try
1223                                                 {
1224                                                         if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1225                                                                 upper_is_air = true;
1226                                                 }
1227                                                 catch(InvalidPositionException &e)
1228                                                 {
1229                                                 }
1230                                                 // Turn mud into grass
1231                                                 if(upper_is_air && n.d == CONTENT_MUD
1232                                                                 && current_light == LIGHT_SUN)
1233                                                 {
1234                                                         n.d = CONTENT_GRASS;
1235                                                 }
1236                                         }
1237
1238                                         // A solid object is on the way.
1239                                         stopped_to_solid_object = true;
1240                                         
1241                                         // Light stops.
1242                                         current_light = 0;
1243                                 }
1244                                 else
1245                                 {
1246                                         // Diminish light
1247                                         current_light = diminish_light(current_light);
1248                                 }
1249
1250                                 u8 old_light = n.getLight(LIGHTBANK_DAY);
1251
1252                                 if(current_light > old_light || remove_light)
1253                                 {
1254                                         n.setLight(LIGHTBANK_DAY, current_light);
1255                                 }
1256                                 
1257                                 if(diminish_light(current_light) != 0)
1258                                 {
1259                                         light_sources.insert(pos_relative + pos, true);
1260                                 }
1261
1262                                 if(current_light == 0 && stopped_to_solid_object)
1263                                 {
1264                                         if(black_air_left)
1265                                         {
1266                                                 *black_air_left = true;
1267                                         }
1268                                 }
1269                         }
1270
1271                         // Whether or not the block below should see LIGHT_SUN
1272                         bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1273
1274                         /*
1275                                 If the block below hasn't already been marked invalid:
1276
1277                                 Check if the node below the block has proper sunlight at top.
1278                                 If not, the block below is invalid.
1279                                 
1280                                 Ignore non-transparent nodes as they always have no light
1281                         */
1282                         try
1283                         {
1284                         if(block_below_is_valid)
1285                         {
1286                                 MapNode n = getNodeParent(v3s16(x, -1, z));
1287                                 if(n.light_propagates())
1288                                 {
1289                                         if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1290                                                         && sunlight_should_go_down == false)
1291                                                 block_below_is_valid = false;
1292                                         else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1293                                                         && sunlight_should_go_down == true)
1294                                                 block_below_is_valid = false;
1295                                 }
1296                         }//if
1297                         }//try
1298                         catch(InvalidPositionException &e)
1299                         {
1300                                 /*std::cout<<"InvalidBlockException for bottom block node"
1301                                                 <<std::endl;*/
1302                                 // Just no block below, no need to panic.
1303                         }
1304                 }
1305         }
1306
1307         return block_below_is_valid;
1308 }
1309
1310 void MapBlock::copyTo(VoxelManipulator &dst)
1311 {
1312         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1313         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1314         
1315         // Copy from data to VoxelManipulator
1316         dst.copyFrom(data, data_area, v3s16(0,0,0),
1317                         getPosRelative(), data_size);
1318 }
1319
1320 void MapBlock::copyFrom(VoxelManipulator &dst)
1321 {
1322         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1323         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1324         
1325         // Copy from VoxelManipulator to data
1326         dst.copyTo(data, data_area, v3s16(0,0,0),
1327                         getPosRelative(), data_size);
1328 }
1329
1330 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1331 {
1332         /*
1333                 Step objects
1334         */
1335         m_objects.step(dtime, server, daynight_ratio);
1336         
1337         /*
1338                 Spawn some objects at random.
1339
1340                 Use dayNightDiffed() to approximate being near ground level
1341         */
1342         if(m_spawn_timer < -999)
1343         {
1344                 m_spawn_timer = 60;
1345         }
1346         if(dayNightDiffed() == true && getObjectCount() == 0)
1347         {
1348                 m_spawn_timer -= dtime;
1349                 if(m_spawn_timer <= 0.0)
1350                 {
1351                         m_spawn_timer += myrand() % 300;
1352                         
1353                         v2s16 p2d(
1354                                 (myrand()%(MAP_BLOCKSIZE-1))+0,
1355                                 (myrand()%(MAP_BLOCKSIZE-1))+0
1356                         );
1357
1358                         s16 y = getGroundLevel(p2d);
1359                         
1360                         if(y >= 0)
1361                         {
1362                                 v3s16 p(p2d.X, y+1, p2d.Y);
1363
1364                                 if(getNode(p).d == CONTENT_AIR
1365                                                 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1366                                 {
1367                                         RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1368                                         addObject(obj);
1369                                 }
1370                         }
1371                 }
1372         }
1373
1374         setChangedFlag();
1375 }
1376
1377
1378 void MapBlock::updateDayNightDiff()
1379 {
1380         if(data == NULL)
1381         {
1382                 m_day_night_differs = false;
1383                 return;
1384         }
1385
1386         bool differs = false;
1387
1388         /*
1389                 Check if any lighting value differs
1390         */
1391         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1392         {
1393                 MapNode &n = data[i];
1394                 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1395                 {
1396                         differs = true;
1397                         break;
1398                 }
1399         }
1400
1401         /*
1402                 If some lighting values differ, check if the whole thing is
1403                 just air. If it is, differ = false
1404         */
1405         if(differs)
1406         {
1407                 bool only_air = true;
1408                 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1409                 {
1410                         MapNode &n = data[i];
1411                         if(n.d != CONTENT_AIR)
1412                         {
1413                                 only_air = false;
1414                                 break;
1415                         }
1416                 }
1417                 if(only_air)
1418                         differs = false;
1419         }
1420
1421         // Set member variable
1422         m_day_night_differs = differs;
1423 }
1424
1425 s16 MapBlock::getGroundLevel(v2s16 p2d)
1426 {
1427         if(isDummy())
1428                 return -3;
1429         try
1430         {
1431                 s16 y = MAP_BLOCKSIZE-1;
1432                 for(; y>=0; y--)
1433                 {
1434                         //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1435                         if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1436                         {
1437                                 if(y == MAP_BLOCKSIZE-1)
1438                                         return -2;
1439                                 else
1440                                         return y;
1441                         }
1442                 }
1443                 return -1;
1444         }
1445         catch(InvalidPositionException &e)
1446         {
1447                 return -3;
1448         }
1449 }
1450
1451 /*
1452         Serialization
1453 */
1454
1455 void MapBlock::serialize(std::ostream &os, u8 version)
1456 {
1457         if(!ser_ver_supported(version))
1458                 throw VersionMismatchException("ERROR: MapBlock format not supported");
1459         
1460         if(data == NULL)
1461         {
1462                 throw SerializationError("ERROR: Not writing dummy block.");
1463         }
1464         
1465         // These have no compression
1466         if(version <= 3 || version == 5 || version == 6)
1467         {
1468                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1469                 
1470                 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1471                 SharedBuffer<u8> dest(buflen);
1472
1473                 dest[0] = is_underground;
1474                 for(u32 i=0; i<nodecount; i++)
1475                 {
1476                         u32 s = 1 + i * MapNode::serializedLength(version);
1477                         data[i].serialize(&dest[s], version);
1478                 }
1479                 
1480                 os.write((char*)*dest, dest.getSize());
1481         }
1482         else if(version <= 10)
1483         {
1484                 /*
1485                         With compression.
1486                         Compress the materials and the params separately.
1487                 */
1488                 
1489                 // First byte
1490                 os.write((char*)&is_underground, 1);
1491
1492                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1493
1494                 // Get and compress materials
1495                 SharedBuffer<u8> materialdata(nodecount);
1496                 for(u32 i=0; i<nodecount; i++)
1497                 {
1498                         materialdata[i] = data[i].d;
1499                 }
1500                 compress(materialdata, os, version);
1501
1502                 // Get and compress lights
1503                 SharedBuffer<u8> lightdata(nodecount);
1504                 for(u32 i=0; i<nodecount; i++)
1505                 {
1506                         lightdata[i] = data[i].param;
1507                 }
1508                 compress(lightdata, os, version);
1509                 
1510                 if(version >= 10)
1511                 {
1512                         // Get and compress param2
1513                         SharedBuffer<u8> param2data(nodecount);
1514                         for(u32 i=0; i<nodecount; i++)
1515                         {
1516                                 param2data[i] = data[i].param2;
1517                         }
1518                         compress(param2data, os, version);
1519                 }
1520         }
1521         // All other versions (newest)
1522         else
1523         {
1524                 // First byte
1525                 u8 flags = 0;
1526                 if(is_underground)
1527                         flags |= 1;
1528                 if(m_day_night_differs)
1529                         flags |= 2;
1530                 if(m_lighting_expired)
1531                         flags |= 3;
1532                 os.write((char*)&flags, 1);
1533
1534                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1535
1536                 /*
1537                         Get data
1538                 */
1539
1540                 SharedBuffer<u8> databuf(nodecount*3);
1541
1542                 // Get contents
1543                 for(u32 i=0; i<nodecount; i++)
1544                 {
1545                         databuf[i] = data[i].d;
1546                 }
1547
1548                 // Get params
1549                 for(u32 i=0; i<nodecount; i++)
1550                 {
1551                         databuf[i+nodecount] = data[i].param;
1552                 }
1553
1554                 // Get param2
1555                 for(u32 i=0; i<nodecount; i++)
1556                 {
1557                         databuf[i+nodecount*2] = data[i].param2;
1558                 }
1559
1560                 /*
1561                         Compress data to output stream
1562                 */
1563
1564                 compress(databuf, os, version);
1565         }
1566 }
1567
1568 void MapBlock::deSerialize(std::istream &is, u8 version)
1569 {
1570         if(!ser_ver_supported(version))
1571                 throw VersionMismatchException("ERROR: MapBlock format not supported");
1572
1573         // These have no compression
1574         if(version <= 3 || version == 5 || version == 6)
1575         {
1576                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1577                 char tmp;
1578                 is.read(&tmp, 1);
1579                 if(is.gcount() != 1)
1580                         throw SerializationError
1581                                         ("MapBlock::deSerialize: no enough input data");
1582                 is_underground = tmp;
1583                 for(u32 i=0; i<nodecount; i++)
1584                 {
1585                         s32 len = MapNode::serializedLength(version);
1586                         SharedBuffer<u8> d(len);
1587                         is.read((char*)*d, len);
1588                         if(is.gcount() != len)
1589                                 throw SerializationError
1590                                                 ("MapBlock::deSerialize: no enough input data");
1591                         data[i].deSerialize(*d, version);
1592                 }
1593         }
1594         else if(version <= 10)
1595         {
1596                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1597
1598                 u8 t8;
1599                 is.read((char*)&t8, 1);
1600                 is_underground = t8;
1601
1602                 {
1603                         // Uncompress and set material data
1604                         std::ostringstream os(std::ios_base::binary);
1605                         decompress(is, os, version);
1606                         std::string s = os.str();
1607                         if(s.size() != nodecount)
1608                                 throw SerializationError
1609                                                 ("MapBlock::deSerialize: invalid format");
1610                         for(u32 i=0; i<s.size(); i++)
1611                         {
1612                                 data[i].d = s[i];
1613                         }
1614                 }
1615                 {
1616                         // Uncompress and set param data
1617                         std::ostringstream os(std::ios_base::binary);
1618                         decompress(is, os, version);
1619                         std::string s = os.str();
1620                         if(s.size() != nodecount)
1621                                 throw SerializationError
1622                                                 ("MapBlock::deSerialize: invalid format");
1623                         for(u32 i=0; i<s.size(); i++)
1624                         {
1625                                 data[i].param = s[i];
1626                         }
1627                 }
1628         
1629                 if(version >= 10)
1630                 {
1631                         // Uncompress and set param2 data
1632                         std::ostringstream os(std::ios_base::binary);
1633                         decompress(is, os, version);
1634                         std::string s = os.str();
1635                         if(s.size() != nodecount)
1636                                 throw SerializationError
1637                                                 ("MapBlock::deSerialize: invalid format");
1638                         for(u32 i=0; i<s.size(); i++)
1639                         {
1640                                 data[i].param2 = s[i];
1641                         }
1642                 }
1643         }
1644         // All other versions (newest)
1645         else
1646         {
1647                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1648
1649                 u8 flags;
1650                 is.read((char*)&flags, 1);
1651                 is_underground = (flags & 1) ? true : false;
1652                 m_day_night_differs = (flags & 2) ? true : false;
1653                 m_lighting_expired = (flags & 3) ? true : false;
1654
1655                 // Uncompress data
1656                 std::ostringstream os(std::ios_base::binary);
1657                 decompress(is, os, version);
1658                 std::string s = os.str();
1659                 if(s.size() != nodecount*3)
1660                         throw SerializationError
1661                                         ("MapBlock::deSerialize: invalid format");
1662
1663                 // Set contents
1664                 for(u32 i=0; i<nodecount; i++)
1665                 {
1666                         data[i].d = s[i];
1667                 }
1668                 // Set params
1669                 for(u32 i=0; i<nodecount; i++)
1670                 {
1671                         data[i].param = s[i+nodecount];
1672                 }
1673                 // Set param2
1674                 for(u32 i=0; i<nodecount; i++)
1675                 {
1676                         data[i].param2 = s[i+nodecount*2];
1677                 }
1678         }
1679 }
1680
1681
1682 //END