]> git.lizzy.rs Git - minetest.git/blob - src/mapblock.cpp
bug-fixin'
[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_settings and g_irrlicht
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                 Avoid interlocks by copying m_temp_mods
605         */
606         NodeModMap temp_mods;
607         {
608                 JMutexAutoLock lock(m_temp_mods_mutex);
609                 m_temp_mods.copy(temp_mods);
610         }
611         
612         /*
613                 Some settings
614         */
615         bool new_style_water = g_settings.getBool("new_style_water");
616         bool new_style_leaves = g_settings.getBool("new_style_leaves");
617         
618         float node_water_level = 1.0;
619         if(new_style_water)
620                 node_water_level = 0.9;
621         
622         /*
623                 We are including the faces of the trailing edges of the block.
624                 This means that when something changes, the caller must
625                 also update the meshes of the blocks at the leading edges.
626
627                 NOTE: This is the slowest part of this method.
628         */
629         
630         {
631                 // 4-23ms for MAP_BLOCKSIZE=16
632                 //TimeTaker timer2("updateMesh() collect");
633
634                 /*
635                         Go through every y,z and get top faces in rows of x+
636                 */
637                 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
638                         for(s16 z=0; z<MAP_BLOCKSIZE; z++){
639                                 updateFastFaceRow(daynight_ratio, posRelative_f,
640                                                 v3s16(0,y,z), MAP_BLOCKSIZE,
641                                                 v3s16(1,0,0), //dir
642                                                 v3f  (1,0,0),
643                                                 v3s16(0,1,0), //face dir
644                                                 v3f  (0,1,0),
645                                                 fastfaces_new,
646                                                 temp_mods);
647                         }
648                 }
649                 /*
650                         Go through every x,y and get right faces in rows of z+
651                 */
652                 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
653                         for(s16 y=0; y<MAP_BLOCKSIZE; y++){
654                                 updateFastFaceRow(daynight_ratio, posRelative_f,
655                                                 v3s16(x,y,0), MAP_BLOCKSIZE,
656                                                 v3s16(0,0,1),
657                                                 v3f  (0,0,1),
658                                                 v3s16(1,0,0),
659                                                 v3f  (1,0,0),
660                                                 fastfaces_new,
661                                                 temp_mods);
662                         }
663                 }
664                 /*
665                         Go through every y,z and get back faces in rows of x+
666                 */
667                 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
668                         for(s16 y=0; y<MAP_BLOCKSIZE; y++){
669                                 updateFastFaceRow(daynight_ratio, posRelative_f,
670                                                 v3s16(0,y,z), MAP_BLOCKSIZE,
671                                                 v3s16(1,0,0),
672                                                 v3f  (1,0,0),
673                                                 v3s16(0,0,1),
674                                                 v3f  (0,0,1),
675                                                 fastfaces_new,
676                                                 temp_mods);
677                         }
678                 }
679         }
680
681         // End of slow part
682
683         /*
684                 Convert FastFaces to SMesh
685         */
686
687         scene::SMesh *mesh_new = NULL;
688         
689         mesh_new = new scene::SMesh();
690         
691         MeshCollector collector;
692
693         if(fastfaces_new.size() > 0)
694         {
695                 // avg 0ms (100ms spikes when loading textures the first time)
696                 //TimeTaker timer2("updateMesh() mesh building");
697
698                 video::SMaterial material;
699                 material.Lighting = false;
700                 material.BackfaceCulling = false;
701                 material.setFlag(video::EMF_BILINEAR_FILTER, false);
702                 material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
703                 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
704                 material.setFlag(video::EMF_FOG_ENABLE, true);
705
706                 for(u32 i=0; i<fastfaces_new.size(); i++)
707                 {
708                         FastFace &f = fastfaces_new[i];
709
710                         const u16 indices[] = {0,1,2,2,3,0};
711
712                         video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
713                         if(texture == NULL)
714                                 continue;
715
716                         material.setTexture(0, texture);
717                         
718                         f.tile.applyMaterialOptions(material);
719                         
720                         /*if(f.tile.alpha != 255)
721                                 material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
722                         else
723                                 material.MaterialType = video::EMT_SOLID;*/
724                         
725                         collector.append(material, f.vertices, 4, indices, 6);
726                 }
727         }
728
729         /*
730                 Add special graphics:
731                 - torches
732                 - flowing water
733         */
734
735         // 0ms
736         //TimeTaker timer2("updateMesh() adding special stuff");
737
738         // Flowing water material
739         video::SMaterial material_water1;
740         material_water1.setFlag(video::EMF_LIGHTING, false);
741         material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
742         material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
743         material_water1.setFlag(video::EMF_FOG_ENABLE, true);
744         material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
745         material_water1.setTexture(0, g_irrlicht->getTexture("water.png"));
746
747         // New-style leaves material
748         video::SMaterial material_leaves1;
749         material_leaves1.setFlag(video::EMF_LIGHTING, false);
750         //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
751         material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
752         material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
753         material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
754         material_leaves1.setTexture(0, g_irrlicht->getTexture("leaves.png"));
755
756         for(s16 z=0; z<MAP_BLOCKSIZE; z++)
757         for(s16 y=0; y<MAP_BLOCKSIZE; y++)
758         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
759         {
760                 v3s16 p(x,y,z);
761
762                 MapNode &n = getNodeRef(x,y,z);
763                 
764                 /*
765                         Add torches to mesh
766                 */
767                 if(n.d == CONTENT_TORCH)
768                 {
769                         video::SColor c(255,255,255,255);
770
771                         video::S3DVertex vertices[4] =
772                         {
773                                 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
774                                 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
775                                 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
776                                 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
777                         };
778
779                         v3s16 dir = unpackDir(n.dir);
780
781                         for(s32 i=0; i<4; i++)
782                         {
783                                 if(dir == v3s16(1,0,0))
784                                         vertices[i].Pos.rotateXZBy(0);
785                                 if(dir == v3s16(-1,0,0))
786                                         vertices[i].Pos.rotateXZBy(180);
787                                 if(dir == v3s16(0,0,1))
788                                         vertices[i].Pos.rotateXZBy(90);
789                                 if(dir == v3s16(0,0,-1))
790                                         vertices[i].Pos.rotateXZBy(-90);
791                                 if(dir == v3s16(0,-1,0))
792                                         vertices[i].Pos.rotateXZBy(45);
793                                 if(dir == v3s16(0,1,0))
794                                         vertices[i].Pos.rotateXZBy(-45);
795
796                                 vertices[i].Pos += intToFloat(p + getPosRelative());
797                         }
798
799                         // Set material
800                         video::SMaterial material;
801                         material.setFlag(video::EMF_LIGHTING, false);
802                         material.setFlag(video::EMF_BACK_FACE_CULLING, false);
803                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
804                         //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
805                         material.MaterialType
806                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
807                         if(dir == v3s16(0,-1,0))
808                                 material.setTexture(0,
809                                                 g_irrlicht->getTexture("torch_on_floor.png"));
810                         else if(dir == v3s16(0,1,0))
811                                 material.setTexture(0,
812                                                 g_irrlicht->getTexture("torch_on_ceiling.png"));
813                         // For backwards compatibility
814                         else if(dir == v3s16(0,0,0))
815                                 material.setTexture(0,
816                                                 g_irrlicht->getTexture("torch_on_floor.png"));
817                         else
818                                 material.setTexture(0, 
819                                                 g_irrlicht->getTexture("torch.png"));
820
821                         u16 indices[] = {0,1,2,2,3,0};
822                         // Add to mesh collector
823                         collector.append(material, vertices, 4, indices, 6);
824                 }
825                 /*
826                         Add flowing water to mesh
827                 */
828                 else if(n.d == CONTENT_WATER)
829                 {
830                         bool top_is_water = false;
831                         try{
832                                 MapNode n = getNodeParent(v3s16(x,y+1,z));
833                                 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
834                                         top_is_water = true;
835                         }catch(InvalidPositionException &e){}
836                         
837                         u8 l = decode_light(n.getLightBlend(daynight_ratio));
838                         video::SColor c(WATER_ALPHA,l,l,l);
839                         
840                         // Neighbor water levels (key = relative position)
841                         // Includes current node
842                         core::map<v3s16, f32> neighbor_levels;
843                         core::map<v3s16, u8> neighbor_contents;
844                         core::map<v3s16, u8> neighbor_flags;
845                         const u8 neighborflag_top_is_water = 0x01;
846                         v3s16 neighbor_dirs[9] = {
847                                 v3s16(0,0,0),
848                                 v3s16(0,0,1),
849                                 v3s16(0,0,-1),
850                                 v3s16(1,0,0),
851                                 v3s16(-1,0,0),
852                                 v3s16(1,0,1),
853                                 v3s16(-1,0,-1),
854                                 v3s16(1,0,-1),
855                                 v3s16(-1,0,1),
856                         };
857                         for(u32 i=0; i<9; i++)
858                         {
859                                 u8 content = CONTENT_AIR;
860                                 float level = -0.5 * BS;
861                                 u8 flags = 0;
862                                 try{
863                                         // Check neighbor
864                                         v3s16 p2 = p + neighbor_dirs[i];
865                                         MapNode n2 = getNodeParent(p2);
866
867                                         content = n2.d;
868
869                                         if(n2.d == CONTENT_WATERSOURCE)
870                                                 level = (-0.5+node_water_level) * BS;
871                                         else if(n2.d == CONTENT_WATER)
872                                                 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
873                                                                 * node_water_level) * BS;
874
875                                         // Check node above neighbor.
876                                         // NOTE: This doesn't get executed if neighbor
877                                         //       doesn't exist
878                                         p2.Y += 1;
879                                         n2 = getNodeParent(p2);
880                                         if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
881                                                 flags |= neighborflag_top_is_water;
882                                 }
883                                 catch(InvalidPositionException &e){}
884                                 
885                                 neighbor_levels.insert(neighbor_dirs[i], level);
886                                 neighbor_contents.insert(neighbor_dirs[i], content);
887                                 neighbor_flags.insert(neighbor_dirs[i], flags);
888                         }
889
890                         //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
891                         //float water_level = neighbor_levels[v3s16(0,0,0)];
892
893                         // Corner heights (average between four waters)
894                         f32 corner_levels[4];
895                         
896                         v3s16 halfdirs[4] = {
897                                 v3s16(0,0,0),
898                                 v3s16(1,0,0),
899                                 v3s16(1,0,1),
900                                 v3s16(0,0,1),
901                         };
902                         for(u32 i=0; i<4; i++)
903                         {
904                                 v3s16 cornerdir = halfdirs[i];
905                                 float cornerlevel = 0;
906                                 u32 valid_count = 0;
907                                 for(u32 j=0; j<4; j++)
908                                 {
909                                         v3s16 neighbordir = cornerdir - halfdirs[j];
910                                         u8 content = neighbor_contents[neighbordir];
911                                         // Special case for source nodes
912                                         if(content == CONTENT_WATERSOURCE)
913                                         {
914                                                 cornerlevel = (-0.5+node_water_level)*BS;
915                                                 valid_count = 1;
916                                                 break;
917                                         }
918                                         else if(content == CONTENT_WATER)
919                                         {
920                                                 cornerlevel += neighbor_levels[neighbordir];
921                                                 valid_count++;
922                                         }
923                                         else if(content == CONTENT_AIR)
924                                         {
925                                                 cornerlevel += -0.5*BS;
926                                                 valid_count++;
927                                         }
928                                 }
929                                 if(valid_count > 0)
930                                         cornerlevel /= valid_count;
931                                 corner_levels[i] = cornerlevel;
932                         }
933
934                         /*
935                                 Generate sides
936                         */
937
938                         v3s16 side_dirs[4] = {
939                                 v3s16(1,0,0),
940                                 v3s16(-1,0,0),
941                                 v3s16(0,0,1),
942                                 v3s16(0,0,-1),
943                         };
944                         s16 side_corners[4][2] = {
945                                 {1, 2},
946                                 {3, 0},
947                                 {2, 3},
948                                 {0, 1},
949                         };
950                         for(u32 i=0; i<4; i++)
951                         {
952                                 v3s16 dir = side_dirs[i];
953
954                                 /*
955                                         If our topside is water and neighbor's topside
956                                         is water, don't draw side face
957                                 */
958                                 if(top_is_water &&
959                                                 neighbor_flags[dir] & neighborflag_top_is_water)
960                                         continue;
961
962                                 u8 neighbor_content = neighbor_contents[dir];
963                                 
964                                 // Don't draw face if neighbor is not air or water
965                                 if(neighbor_content != CONTENT_AIR
966                                                 && neighbor_content != CONTENT_WATER)
967                                         continue;
968                                 
969                                 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
970                                 
971                                 // Don't draw any faces if neighbor is water and top is water
972                                 if(neighbor_is_water == true && top_is_water == false)
973                                         continue;
974                                 
975                                 video::S3DVertex vertices[4] =
976                                 {
977                                         /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
978                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
979                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
980                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
981                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
982                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
983                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
984                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
985                                 };
986                                 
987                                 /*
988                                         If our topside is water, set upper border of face
989                                         at upper border of node
990                                 */
991                                 if(top_is_water)
992                                 {
993                                         vertices[2].Pos.Y = 0.5*BS;
994                                         vertices[3].Pos.Y = 0.5*BS;
995                                 }
996                                 /*
997                                         Otherwise upper position of face is corner levels
998                                 */
999                                 else
1000                                 {
1001                                         vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1002                                         vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1003                                 }
1004                                 
1005                                 /*
1006                                         If neighbor is water, lower border of face is corner
1007                                         water levels
1008                                 */
1009                                 if(neighbor_is_water)
1010                                 {
1011                                         vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1012                                         vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1013                                 }
1014                                 /*
1015                                         If neighbor is not water, lower border of face is
1016                                         lower border of node
1017                                 */
1018                                 else
1019                                 {
1020                                         vertices[0].Pos.Y = -0.5*BS;
1021                                         vertices[1].Pos.Y = -0.5*BS;
1022                                 }
1023                                 
1024                                 for(s32 j=0; j<4; j++)
1025                                 {
1026                                         if(dir == v3s16(0,0,1))
1027                                                 vertices[j].Pos.rotateXZBy(0);
1028                                         if(dir == v3s16(0,0,-1))
1029                                                 vertices[j].Pos.rotateXZBy(180);
1030                                         if(dir == v3s16(-1,0,0))
1031                                                 vertices[j].Pos.rotateXZBy(90);
1032                                         if(dir == v3s16(1,0,-0))
1033                                                 vertices[j].Pos.rotateXZBy(-90);
1034
1035                                         vertices[j].Pos += intToFloat(p + getPosRelative());
1036                                 }
1037
1038                                 u16 indices[] = {0,1,2,2,3,0};
1039                                 // Add to mesh collector
1040                                 collector.append(material_water1, vertices, 4, indices, 6);
1041                         }
1042                         
1043                         /*
1044                                 Generate top side, if appropriate
1045                         */
1046                         
1047                         if(top_is_water == false)
1048                         {
1049                                 video::S3DVertex vertices[4] =
1050                                 {
1051                                         video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1052                                         video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1053                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1054                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
1055                                 };
1056
1057                                 for(s32 i=0; i<4; i++)
1058                                 {
1059                                         //vertices[i].Pos.Y += water_level;
1060                                         //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1061                                         vertices[i].Pos.Y += corner_levels[i];
1062                                         vertices[i].Pos += intToFloat(p + getPosRelative());
1063                                 }
1064
1065                                 u16 indices[] = {0,1,2,2,3,0};
1066                                 // Add to mesh collector
1067                                 collector.append(material_water1, vertices, 4, indices, 6);
1068                         }
1069                 }
1070                 /*
1071                         Add water sources to mesh if using new style
1072                 */
1073                 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1074                 {
1075                         //bool top_is_water = false;
1076                         bool top_is_air = false;
1077                         try{
1078                                 MapNode n = getNodeParent(v3s16(x,y+1,z));
1079                                 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1080                                         top_is_water = true;*/
1081                                 if(n.d == CONTENT_AIR)
1082                                         top_is_air = true;
1083                         }catch(InvalidPositionException &e){}
1084                         
1085                         /*if(top_is_water == true)
1086                                 continue;*/
1087                         if(top_is_air == false)
1088                                 continue;
1089
1090                         u8 l = decode_light(n.getLightBlend(daynight_ratio));
1091                         video::SColor c(WATER_ALPHA,l,l,l);
1092                         
1093                         video::S3DVertex vertices[4] =
1094                         {
1095                                 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1096                                 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1097                                 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1098                                 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
1099                         };
1100
1101                         for(s32 i=0; i<4; i++)
1102                         {
1103                                 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1104                                 vertices[i].Pos += intToFloat(p + getPosRelative());
1105                         }
1106
1107                         u16 indices[] = {0,1,2,2,3,0};
1108                         // Add to mesh collector
1109                         collector.append(material_water1, vertices, 4, indices, 6);
1110                 }
1111                 /*
1112                         Add leaves if using new style
1113                 */
1114                 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1115                 {
1116                         u8 l = decode_light(n.getLightBlend(daynight_ratio));
1117                         video::SColor c(255,l,l,l);
1118
1119                         for(u32 j=0; j<6; j++)
1120                         {
1121                                 video::S3DVertex vertices[4] =
1122                                 {
1123                                         video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1124                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1125                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1126                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),
1127                                 };
1128
1129                                 if(j == 0)
1130                                 {
1131                                         for(u16 i=0; i<4; i++)
1132                                                 vertices[i].Pos.rotateXZBy(0);
1133                                 }
1134                                 else if(j == 1)
1135                                 {
1136                                         for(u16 i=0; i<4; i++)
1137                                                 vertices[i].Pos.rotateXZBy(180);
1138                                 }
1139                                 else if(j == 2)
1140                                 {
1141                                         for(u16 i=0; i<4; i++)
1142                                                 vertices[i].Pos.rotateXZBy(-90);
1143                                 }
1144                                 else if(j == 3)
1145                                 {
1146                                         for(u16 i=0; i<4; i++)
1147                                                 vertices[i].Pos.rotateXZBy(90);
1148                                 }
1149                                 else if(j == 4)
1150                                 {
1151                                         for(u16 i=0; i<4; i++)
1152                                                 vertices[i].Pos.rotateYZBy(-90);
1153                                 }
1154                                 else if(j == 5)
1155                                 {
1156                                         for(u16 i=0; i<4; i++)
1157                                                 vertices[i].Pos.rotateYZBy(90);
1158                                 }
1159
1160                                 for(u16 i=0; i<4; i++)
1161                                 {
1162                                         vertices[i].Pos += intToFloat(p + getPosRelative());
1163                                 }
1164
1165                                 u16 indices[] = {0,1,2,2,3,0};
1166                                 // Add to mesh collector
1167                                 collector.append(material_leaves1, vertices, 4, indices, 6);
1168                         }
1169                 }
1170         }
1171
1172         /*
1173                 Add stuff from collector to mesh
1174         */
1175         
1176         collector.fillMesh(mesh_new);
1177
1178         /*
1179                 Do some stuff to the mesh
1180         */
1181
1182         mesh_new->recalculateBoundingBox();
1183
1184         /*
1185                 Delete new mesh if it is empty
1186         */
1187
1188         if(mesh_new->getMeshBufferCount() == 0)
1189         {
1190                 mesh_new->drop();
1191                 mesh_new = NULL;
1192         }
1193
1194         // Use VBO for mesh (this just would set this for ever buffer)
1195         // This will lead to infinite memory usage because or irrlicht.
1196         //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1197         
1198         /*std::cout<<"MapBlock has "<<fastfaces_new.size()<<" faces "
1199                         <<"and uses "<<mesh_new->getMeshBufferCount()
1200                         <<" materials (meshbuffers)"<<std::endl;*/
1201         
1202         /*
1203                 Replace the mesh
1204         */
1205
1206         mesh_mutex.Lock();
1207
1208         //scene::SMesh *mesh_old = mesh[daynight_i];
1209         //mesh[daynight_i] = mesh_new;
1210
1211         scene::SMesh *mesh_old = mesh;
1212         mesh = mesh_new;
1213         setMeshExpired(false);
1214         
1215         if(mesh_old != NULL)
1216         {
1217                 // Remove hardware buffers of meshbuffers of mesh
1218                 // NOTE: No way, this runs in a different thread and everything
1219                 /*u32 c = mesh_old->getMeshBufferCount();
1220                 for(u32 i=0; i<c; i++)
1221                 {
1222                         IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1223                 }*/
1224                 
1225                 /*dstream<<"mesh_old->getReferenceCount()="
1226                                 <<mesh_old->getReferenceCount()<<std::endl;
1227                 u32 c = mesh_old->getMeshBufferCount();
1228                 for(u32 i=0; i<c; i++)
1229                 {
1230                         scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1231                         dstream<<"buf->getReferenceCount()="
1232                                         <<buf->getReferenceCount()<<std::endl;
1233                 }*/
1234
1235                 // Drop the mesh
1236                 mesh_old->drop();
1237
1238                 //delete mesh_old;
1239         }
1240
1241         mesh_mutex.Unlock();
1242         
1243         //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1244 }
1245
1246 /*void MapBlock::updateMeshes(s32 first_i)
1247 {
1248         assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
1249         updateMesh(first_i);
1250         for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
1251         {
1252                 if(i == first_i)
1253                         continue;
1254                 updateMesh(i);
1255         }
1256 }*/
1257
1258 #endif // !SERVER
1259
1260 /*
1261         Propagates sunlight down through the block.
1262         Doesn't modify nodes that are not affected by sunlight.
1263         
1264         Returns false if sunlight at bottom block is invalid
1265         Returns true if bottom block doesn't exist.
1266
1267         If there is a block above, continues from it.
1268         If there is no block above, assumes there is sunlight, unless
1269         is_underground is set or highest node is water.
1270
1271         At the moment, all sunlighted nodes are added to light_sources.
1272         - SUGG: This could be optimized
1273
1274         Turns sunglighted mud into grass.
1275
1276         if remove_light==true, sets non-sunlighted nodes black.
1277
1278         if black_air_left!=NULL, it is set to true if non-sunlighted
1279         air is left in block.
1280 */
1281 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1282                 bool remove_light, bool *black_air_left,
1283                 bool grow_grass)
1284 {
1285         // Whether the sunlight at the top of the bottom block is valid
1286         bool block_below_is_valid = true;
1287         
1288         v3s16 pos_relative = getPosRelative();
1289         
1290         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1291         {
1292                 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1293                 {
1294 #if 1
1295                         bool no_sunlight = false;
1296                         bool no_top_block = false;
1297                         // Check if node above block has sunlight
1298                         try{
1299                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1300                                 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1301                                 {
1302                                         no_sunlight = true;
1303                                 }
1304                         }
1305                         catch(InvalidPositionException &e)
1306                         {
1307                                 no_top_block = true;
1308                                 
1309                                 // NOTE: This makes over-ground roofed places sunlighted
1310                                 // Assume sunlight, unless is_underground==true
1311                                 if(is_underground)
1312                                 {
1313                                         no_sunlight = true;
1314                                 }
1315                                 else
1316                                 {
1317                                         MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1318                                         if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1319                                         {
1320                                                 no_sunlight = true;
1321                                         }
1322                                 }
1323                                 // NOTE: As of now, this just would make everything dark.
1324                                 // No sunlight here
1325                                 //no_sunlight = true;
1326                         }
1327 #endif
1328 #if 0 // Doesn't work; nothing gets light.
1329                         bool no_sunlight = true;
1330                         bool no_top_block = false;
1331                         // Check if node above block has sunlight
1332                         try{
1333                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1334                                 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1335                                 {
1336                                         no_sunlight = false;
1337                                 }
1338                         }
1339                         catch(InvalidPositionException &e)
1340                         {
1341                                 no_top_block = true;
1342                         }
1343 #endif
1344
1345                         /*std::cout<<"("<<x<<","<<z<<"): "
1346                                         <<"no_top_block="<<no_top_block
1347                                         <<", is_underground="<<is_underground
1348                                         <<", no_sunlight="<<no_sunlight
1349                                         <<std::endl;*/
1350                 
1351                         s16 y = MAP_BLOCKSIZE-1;
1352                         
1353                         // This makes difference to diminishing in water.
1354                         bool stopped_to_solid_object = false;
1355                         
1356                         u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1357
1358                         for(; y >= 0; y--)
1359                         {
1360                                 v3s16 pos(x, y, z);
1361                                 MapNode &n = getNodeRef(pos);
1362                                 
1363                                 if(current_light == 0)
1364                                 {
1365                                         // Do nothing
1366                                 }
1367                                 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1368                                 {
1369                                         // Do nothing: Sunlight is continued
1370                                 }
1371                                 else if(n.light_propagates() == false)
1372                                 {
1373                                         if(grow_grass)
1374                                         {
1375                                                 bool upper_is_air = false;
1376                                                 try
1377                                                 {
1378                                                         if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1379                                                                 upper_is_air = true;
1380                                                 }
1381                                                 catch(InvalidPositionException &e)
1382                                                 {
1383                                                 }
1384                                                 // Turn mud into grass
1385                                                 if(upper_is_air && n.d == CONTENT_MUD
1386                                                                 && current_light == LIGHT_SUN)
1387                                                 {
1388                                                         n.d = CONTENT_GRASS;
1389                                                 }
1390                                         }
1391
1392                                         // A solid object is on the way.
1393                                         stopped_to_solid_object = true;
1394                                         
1395                                         // Light stops.
1396                                         current_light = 0;
1397                                 }
1398                                 else
1399                                 {
1400                                         // Diminish light
1401                                         current_light = diminish_light(current_light);
1402                                 }
1403
1404                                 u8 old_light = n.getLight(LIGHTBANK_DAY);
1405
1406                                 if(current_light > old_light || remove_light)
1407                                 {
1408                                         n.setLight(LIGHTBANK_DAY, current_light);
1409                                 }
1410                                 
1411                                 if(diminish_light(current_light) != 0)
1412                                 {
1413                                         light_sources.insert(pos_relative + pos, true);
1414                                 }
1415
1416                                 if(current_light == 0 && stopped_to_solid_object)
1417                                 {
1418                                         if(black_air_left)
1419                                         {
1420                                                 *black_air_left = true;
1421                                         }
1422                                 }
1423                         }
1424
1425                         // Whether or not the block below should see LIGHT_SUN
1426                         bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1427
1428                         /*
1429                                 If the block below hasn't already been marked invalid:
1430
1431                                 Check if the node below the block has proper sunlight at top.
1432                                 If not, the block below is invalid.
1433                                 
1434                                 Ignore non-transparent nodes as they always have no light
1435                         */
1436                         try
1437                         {
1438                         if(block_below_is_valid)
1439                         {
1440                                 MapNode n = getNodeParent(v3s16(x, -1, z));
1441                                 if(n.light_propagates())
1442                                 {
1443                                         if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1444                                                         && sunlight_should_go_down == false)
1445                                                 block_below_is_valid = false;
1446                                         else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1447                                                         && sunlight_should_go_down == true)
1448                                                 block_below_is_valid = false;
1449                                 }
1450                         }//if
1451                         }//try
1452                         catch(InvalidPositionException &e)
1453                         {
1454                                 /*std::cout<<"InvalidBlockException for bottom block node"
1455                                                 <<std::endl;*/
1456                                 // Just no block below, no need to panic.
1457                         }
1458                 }
1459         }
1460
1461         return block_below_is_valid;
1462 }
1463
1464 void MapBlock::copyTo(VoxelManipulator &dst)
1465 {
1466         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1467         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1468         
1469         // Copy from data to VoxelManipulator
1470         dst.copyFrom(data, data_area, v3s16(0,0,0),
1471                         getPosRelative(), data_size);
1472 }
1473
1474 void MapBlock::copyFrom(VoxelManipulator &dst)
1475 {
1476         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1477         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1478         
1479         // Copy from VoxelManipulator to data
1480         dst.copyTo(data, data_area, v3s16(0,0,0),
1481                         getPosRelative(), data_size);
1482 }
1483
1484 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1485 {
1486         /*
1487                 Step objects
1488         */
1489         m_objects.step(dtime, server, daynight_ratio);
1490         
1491         /*
1492                 Spawn some objects at random.
1493
1494                 Use dayNightDiffed() to approximate being near ground level
1495         */
1496         if(m_spawn_timer < -999)
1497         {
1498                 m_spawn_timer = 60;
1499         }
1500         if(dayNightDiffed() == true && getObjectCount() == 0)
1501         {
1502                 m_spawn_timer -= dtime;
1503                 if(m_spawn_timer <= 0.0)
1504                 {
1505                         m_spawn_timer += myrand() % 300;
1506                         
1507                         v2s16 p2d(
1508                                 (myrand()%(MAP_BLOCKSIZE-1))+0,
1509                                 (myrand()%(MAP_BLOCKSIZE-1))+0
1510                         );
1511
1512                         s16 y = getGroundLevel(p2d);
1513                         
1514                         if(y >= 0)
1515                         {
1516                                 v3s16 p(p2d.X, y+1, p2d.Y);
1517
1518                                 if(getNode(p).d == CONTENT_AIR
1519                                                 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1520                                 {
1521                                         RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1522                                         addObject(obj);
1523                                 }
1524                         }
1525                 }
1526         }
1527
1528         setChangedFlag();
1529 }
1530
1531
1532 void MapBlock::updateDayNightDiff()
1533 {
1534         if(data == NULL)
1535         {
1536                 m_day_night_differs = false;
1537                 return;
1538         }
1539
1540         bool differs = false;
1541
1542         /*
1543                 Check if any lighting value differs
1544         */
1545         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1546         {
1547                 MapNode &n = data[i];
1548                 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1549                 {
1550                         differs = true;
1551                         break;
1552                 }
1553         }
1554
1555         /*
1556                 If some lighting values differ, check if the whole thing is
1557                 just air. If it is, differ = false
1558         */
1559         if(differs)
1560         {
1561                 bool only_air = true;
1562                 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1563                 {
1564                         MapNode &n = data[i];
1565                         if(n.d != CONTENT_AIR)
1566                         {
1567                                 only_air = false;
1568                                 break;
1569                         }
1570                 }
1571                 if(only_air)
1572                         differs = false;
1573         }
1574
1575         // Set member variable
1576         m_day_night_differs = differs;
1577 }
1578
1579 s16 MapBlock::getGroundLevel(v2s16 p2d)
1580 {
1581         if(isDummy())
1582                 return -3;
1583         try
1584         {
1585                 s16 y = MAP_BLOCKSIZE-1;
1586                 for(; y>=0; y--)
1587                 {
1588                         //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1589                         if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1590                         {
1591                                 if(y == MAP_BLOCKSIZE-1)
1592                                         return -2;
1593                                 else
1594                                         return y;
1595                         }
1596                 }
1597                 return -1;
1598         }
1599         catch(InvalidPositionException &e)
1600         {
1601                 return -3;
1602         }
1603 }
1604
1605 /*
1606         Serialization
1607 */
1608
1609 void MapBlock::serialize(std::ostream &os, u8 version)
1610 {
1611         if(!ser_ver_supported(version))
1612                 throw VersionMismatchException("ERROR: MapBlock format not supported");
1613         
1614         if(data == NULL)
1615         {
1616                 throw SerializationError("ERROR: Not writing dummy block.");
1617         }
1618         
1619         // These have no compression
1620         if(version <= 3 || version == 5 || version == 6)
1621         {
1622                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1623                 
1624                 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1625                 SharedBuffer<u8> dest(buflen);
1626
1627                 dest[0] = is_underground;
1628                 for(u32 i=0; i<nodecount; i++)
1629                 {
1630                         u32 s = 1 + i * MapNode::serializedLength(version);
1631                         data[i].serialize(&dest[s], version);
1632                 }
1633                 
1634                 os.write((char*)*dest, dest.getSize());
1635         }
1636         else if(version <= 10)
1637         {
1638                 /*
1639                         With compression.
1640                         Compress the materials and the params separately.
1641                 */
1642                 
1643                 // First byte
1644                 os.write((char*)&is_underground, 1);
1645
1646                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1647
1648                 // Get and compress materials
1649                 SharedBuffer<u8> materialdata(nodecount);
1650                 for(u32 i=0; i<nodecount; i++)
1651                 {
1652                         materialdata[i] = data[i].d;
1653                 }
1654                 compress(materialdata, os, version);
1655
1656                 // Get and compress lights
1657                 SharedBuffer<u8> lightdata(nodecount);
1658                 for(u32 i=0; i<nodecount; i++)
1659                 {
1660                         lightdata[i] = data[i].param;
1661                 }
1662                 compress(lightdata, os, version);
1663                 
1664                 if(version >= 10)
1665                 {
1666                         // Get and compress param2
1667                         SharedBuffer<u8> param2data(nodecount);
1668                         for(u32 i=0; i<nodecount; i++)
1669                         {
1670                                 param2data[i] = data[i].param2;
1671                         }
1672                         compress(param2data, os, version);
1673                 }
1674         }
1675         // All other versions (newest)
1676         else
1677         {
1678                 // First byte
1679                 u8 flags = 0;
1680                 if(is_underground)
1681                         flags |= 1;
1682                 if(m_day_night_differs)
1683                         flags |= 2;
1684                 if(m_lighting_expired)
1685                         flags |= 3;
1686                 os.write((char*)&flags, 1);
1687
1688                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1689
1690                 /*
1691                         Get data
1692                 */
1693
1694                 SharedBuffer<u8> databuf(nodecount*3);
1695
1696                 // Get contents
1697                 for(u32 i=0; i<nodecount; i++)
1698                 {
1699                         databuf[i] = data[i].d;
1700                 }
1701
1702                 // Get params
1703                 for(u32 i=0; i<nodecount; i++)
1704                 {
1705                         databuf[i+nodecount] = data[i].param;
1706                 }
1707
1708                 // Get param2
1709                 for(u32 i=0; i<nodecount; i++)
1710                 {
1711                         databuf[i+nodecount*2] = data[i].param2;
1712                 }
1713
1714                 /*
1715                         Compress data to output stream
1716                 */
1717
1718                 compress(databuf, os, version);
1719         }
1720 }
1721
1722 void MapBlock::deSerialize(std::istream &is, u8 version)
1723 {
1724         if(!ser_ver_supported(version))
1725                 throw VersionMismatchException("ERROR: MapBlock format not supported");
1726
1727         // These have no compression
1728         if(version <= 3 || version == 5 || version == 6)
1729         {
1730                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1731                 char tmp;
1732                 is.read(&tmp, 1);
1733                 if(is.gcount() != 1)
1734                         throw SerializationError
1735                                         ("MapBlock::deSerialize: no enough input data");
1736                 is_underground = tmp;
1737                 for(u32 i=0; i<nodecount; i++)
1738                 {
1739                         s32 len = MapNode::serializedLength(version);
1740                         SharedBuffer<u8> d(len);
1741                         is.read((char*)*d, len);
1742                         if(is.gcount() != len)
1743                                 throw SerializationError
1744                                                 ("MapBlock::deSerialize: no enough input data");
1745                         data[i].deSerialize(*d, version);
1746                 }
1747         }
1748         else if(version <= 10)
1749         {
1750                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1751
1752                 u8 t8;
1753                 is.read((char*)&t8, 1);
1754                 is_underground = t8;
1755
1756                 {
1757                         // Uncompress and set material data
1758                         std::ostringstream os(std::ios_base::binary);
1759                         decompress(is, os, version);
1760                         std::string s = os.str();
1761                         if(s.size() != nodecount)
1762                                 throw SerializationError
1763                                                 ("MapBlock::deSerialize: invalid format");
1764                         for(u32 i=0; i<s.size(); i++)
1765                         {
1766                                 data[i].d = s[i];
1767                         }
1768                 }
1769                 {
1770                         // Uncompress and set param data
1771                         std::ostringstream os(std::ios_base::binary);
1772                         decompress(is, os, version);
1773                         std::string s = os.str();
1774                         if(s.size() != nodecount)
1775                                 throw SerializationError
1776                                                 ("MapBlock::deSerialize: invalid format");
1777                         for(u32 i=0; i<s.size(); i++)
1778                         {
1779                                 data[i].param = s[i];
1780                         }
1781                 }
1782         
1783                 if(version >= 10)
1784                 {
1785                         // Uncompress and set param2 data
1786                         std::ostringstream os(std::ios_base::binary);
1787                         decompress(is, os, version);
1788                         std::string s = os.str();
1789                         if(s.size() != nodecount)
1790                                 throw SerializationError
1791                                                 ("MapBlock::deSerialize: invalid format");
1792                         for(u32 i=0; i<s.size(); i++)
1793                         {
1794                                 data[i].param2 = s[i];
1795                         }
1796                 }
1797         }
1798         // All other versions (newest)
1799         else
1800         {
1801                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1802
1803                 u8 flags;
1804                 is.read((char*)&flags, 1);
1805                 is_underground = (flags & 1) ? true : false;
1806                 m_day_night_differs = (flags & 2) ? true : false;
1807                 m_lighting_expired = (flags & 3) ? true : false;
1808
1809                 // Uncompress data
1810                 std::ostringstream os(std::ios_base::binary);
1811                 decompress(is, os, version);
1812                 std::string s = os.str();
1813                 if(s.size() != nodecount*3)
1814                         throw SerializationError
1815                                         ("MapBlock::deSerialize: invalid format");
1816
1817                 // Set contents
1818                 for(u32 i=0; i<nodecount; i++)
1819                 {
1820                         data[i].d = s[i];
1821                 }
1822                 // Set params
1823                 for(u32 i=0; i<nodecount; i++)
1824                 {
1825                         data[i].param = s[i+nodecount];
1826                 }
1827                 // Set param2
1828                 for(u32 i=0; i<nodecount; i++)
1829                 {
1830                         data[i].param2 = s[i+nodecount*2];
1831                 }
1832         }
1833         
1834         /*
1835                 Translate nodes as specified in the translate_to fields of
1836                 node features
1837         */
1838         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1839         {
1840                 MapNode &n = data[i];
1841
1842                 MapNode *translate_to = content_features(n.d).translate_to;
1843                 if(translate_to)
1844                 {
1845                         dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
1846                                         <<translate_to->d<<std::endl;
1847                         n = *translate_to;
1848                 }
1849         }
1850 }
1851
1852
1853 //END