]> git.lizzy.rs Git - dragonfireclient.git/blob - src/mapblock.cpp
water drawing glitch fix
[dragonfireclient.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                         core::map<v3s16, u8> neighbor_flags;
824                         const u8 neighborflag_top_is_water = 0x01;
825                         v3s16 neighbor_dirs[9] = {
826                                 v3s16(0,0,0),
827                                 v3s16(0,0,1),
828                                 v3s16(0,0,-1),
829                                 v3s16(1,0,0),
830                                 v3s16(-1,0,0),
831                                 v3s16(1,0,1),
832                                 v3s16(-1,0,-1),
833                                 v3s16(1,0,-1),
834                                 v3s16(-1,0,1),
835                         };
836                         for(u32 i=0; i<9; i++)
837                         {
838                                 u8 content = CONTENT_AIR;
839                                 float level = -0.5 * BS;
840                                 u8 flags = 0;
841                                 try{
842                                         // Check neighbor
843                                         v3s16 p2 = p + neighbor_dirs[i];
844                                         MapNode n2 = getNodeParent(p2);
845
846                                         content = n2.d;
847
848                                         if(n2.d == CONTENT_WATERSOURCE)
849                                                 level = 0.5 * BS;
850                                         else if(n2.d == CONTENT_WATER)
851                                                 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0) * BS;
852
853                                         // Check node above neighbor.
854                                         // NOTE: This doesn't get executed if neighbor
855                                         //       doesn't exist
856                                         p2.Y += 1;
857                                         n2 = getNodeParent(p2);
858                                         if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
859                                                 flags |= neighborflag_top_is_water;
860                                 }
861                                 catch(InvalidPositionException &e){}
862                                 
863                                 neighbor_levels.insert(neighbor_dirs[i], level);
864                                 neighbor_contents.insert(neighbor_dirs[i], content);
865                                 neighbor_flags.insert(neighbor_dirs[i], flags);
866                         }
867
868                         //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
869                         //float water_level = neighbor_levels[v3s16(0,0,0)];
870
871                         // Corner heights (average between four waters)
872                         f32 corner_levels[4];
873                         
874                         v3s16 halfdirs[4] = {
875                                 v3s16(0,0,0),
876                                 v3s16(1,0,0),
877                                 v3s16(1,0,1),
878                                 v3s16(0,0,1),
879                         };
880                         for(u32 i=0; i<4; i++)
881                         {
882                                 v3s16 cornerdir = halfdirs[i];
883                                 float cornerlevel = 0;
884                                 u32 valid_count = 0;
885                                 for(u32 j=0; j<4; j++)
886                                 {
887                                         v3s16 neighbordir = cornerdir - halfdirs[j];
888                                         u8 content = neighbor_contents[neighbordir];
889                                         // Special case for source nodes
890                                         if(content == CONTENT_WATERSOURCE)
891                                         {
892                                                 cornerlevel = 0.5*BS;
893                                                 valid_count = 1;
894                                                 break;
895                                         }
896                                         else if(content == CONTENT_WATER)
897                                         {
898                                                 cornerlevel += neighbor_levels[neighbordir];
899                                                 valid_count++;
900                                         }
901                                         else if(content == CONTENT_AIR)
902                                         {
903                                                 cornerlevel += -0.5*BS;
904                                                 valid_count++;
905                                         }
906                                 }
907                                 if(valid_count > 0)
908                                         cornerlevel /= valid_count;
909                                 corner_levels[i] = cornerlevel;
910                         }
911
912                         /*
913                                 Generate sides
914                         */
915
916                         v3s16 side_dirs[4] = {
917                                 v3s16(1,0,0),
918                                 v3s16(-1,0,0),
919                                 v3s16(0,0,1),
920                                 v3s16(0,0,-1),
921                         };
922                         s16 side_corners[4][2] = {
923                                 {1, 2},
924                                 {3, 0},
925                                 {2, 3},
926                                 {0, 1},
927                         };
928                         for(u32 i=0; i<4; i++)
929                         {
930                                 v3s16 dir = side_dirs[i];
931
932                                 /*
933                                         If our topside is water and neighbor's topside
934                                         is water, don't draw side face
935                                 */
936                                 if(top_is_water &&
937                                                 neighbor_flags[dir] & neighborflag_top_is_water)
938                                         continue;
939
940                                 u8 neighbor_content = neighbor_contents[dir];
941                                 
942                                 // Don't draw face if neighbor is not air or water
943                                 if(neighbor_content != CONTENT_AIR
944                                                 && neighbor_content != CONTENT_WATER)
945                                         continue;
946                                 
947                                 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
948                                 
949                                 // Don't draw any faces if neighbor is water and top is water
950                                 if(neighbor_is_water == true && top_is_water == false)
951                                         continue;
952                                 
953                                 video::S3DVertex vertices[4] =
954                                 {
955                                         /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
956                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
957                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
958                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
959                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
960                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
961                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
962                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
963                                 };
964                                 
965                                 /*
966                                         If our topside is water, set upper border of face
967                                         at upper border of node
968                                 */
969                                 if(top_is_water)
970                                 {
971                                         vertices[2].Pos.Y = 0.5*BS;
972                                         vertices[3].Pos.Y = 0.5*BS;
973                                 }
974                                 /*
975                                         Otherwise upper position of face is corner levels
976                                 */
977                                 else
978                                 {
979                                         vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
980                                         vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
981                                 }
982                                 
983                                 /*
984                                         If neighbor is water, lower border of face is corner
985                                         water levels
986                                 */
987                                 if(neighbor_is_water)
988                                 {
989                                         vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
990                                         vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
991                                 }
992                                 /*
993                                         If neighbor is not water, lower border of face is
994                                         lower border of node
995                                 */
996                                 else
997                                 {
998                                         vertices[0].Pos.Y = -0.5*BS;
999                                         vertices[1].Pos.Y = -0.5*BS;
1000                                 }
1001                                 
1002                                 for(s32 j=0; j<4; j++)
1003                                 {
1004                                         if(dir == v3s16(0,0,1))
1005                                                 vertices[j].Pos.rotateXZBy(0);
1006                                         if(dir == v3s16(0,0,-1))
1007                                                 vertices[j].Pos.rotateXZBy(180);
1008                                         if(dir == v3s16(-1,0,0))
1009                                                 vertices[j].Pos.rotateXZBy(90);
1010                                         if(dir == v3s16(1,0,-0))
1011                                                 vertices[j].Pos.rotateXZBy(-90);
1012
1013                                         vertices[j].Pos += intToFloat(p + getPosRelative());
1014                                 }
1015
1016                                 u16 indices[] = {0,1,2,2,3,0};
1017                                 // Add to mesh collector
1018                                 collector.append(material_w1, vertices, 4, indices, 6);
1019                         }
1020                         
1021                         /*
1022                                 Generate top side, if appropriate
1023                         */
1024                         
1025                         if(top_is_water == false)
1026                         {
1027                                 video::S3DVertex vertices[4] =
1028                                 {
1029                                         video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1030                                         video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1031                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1032                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
1033                                 };
1034
1035                                 for(s32 i=0; i<4; i++)
1036                                 {
1037                                         //vertices[i].Pos.Y += water_level;
1038                                         //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1039                                         vertices[i].Pos.Y += corner_levels[i];
1040                                         vertices[i].Pos += intToFloat(p + getPosRelative());
1041                                 }
1042
1043                                 u16 indices[] = {0,1,2,2,3,0};
1044                                 // Add to mesh collector
1045                                 collector.append(material_w1, vertices, 4, indices, 6);
1046                         }
1047                 }
1048         }
1049
1050         /*
1051                 Add stuff from collector to mesh
1052         */
1053         
1054         collector.fillMesh(mesh_new);
1055
1056         /*
1057                 Do some stuff to the mesh
1058         */
1059
1060         mesh_new->recalculateBoundingBox();
1061
1062         /*
1063                 Delete new mesh if it is empty
1064         */
1065
1066         if(mesh_new->getMeshBufferCount() == 0)
1067         {
1068                 mesh_new->drop();
1069                 mesh_new = NULL;
1070         }
1071
1072         // Use VBO for mesh (this just would set this for ever buffer)
1073         // This will lead to infinite memory usage because or irrlicht.
1074         //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1075         
1076         /*std::cout<<"MapBlock has "<<fastfaces_new.size()<<" faces "
1077                         <<"and uses "<<mesh_new->getMeshBufferCount()
1078                         <<" materials (meshbuffers)"<<std::endl;*/
1079         
1080         /*
1081                 Replace the mesh
1082         */
1083
1084         mesh_mutex.Lock();
1085
1086         //scene::SMesh *mesh_old = mesh[daynight_i];
1087         //mesh[daynight_i] = mesh_new;
1088
1089         scene::SMesh *mesh_old = mesh;
1090         mesh = mesh_new;
1091         setMeshExpired(false);
1092         
1093         if(mesh_old != NULL)
1094         {
1095                 // Remove hardware buffers of meshbuffers of mesh
1096                 // NOTE: No way, this runs in a different thread and everything
1097                 /*u32 c = mesh_old->getMeshBufferCount();
1098                 for(u32 i=0; i<c; i++)
1099                 {
1100                         IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1101                 }*/
1102                 
1103                 /*dstream<<"mesh_old->getReferenceCount()="
1104                                 <<mesh_old->getReferenceCount()<<std::endl;
1105                 u32 c = mesh_old->getMeshBufferCount();
1106                 for(u32 i=0; i<c; i++)
1107                 {
1108                         scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1109                         dstream<<"buf->getReferenceCount()="
1110                                         <<buf->getReferenceCount()<<std::endl;
1111                 }*/
1112
1113                 // Drop the mesh
1114                 mesh_old->drop();
1115
1116                 //delete mesh_old;
1117         }
1118
1119         mesh_mutex.Unlock();
1120         
1121         //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1122 }
1123
1124 /*void MapBlock::updateMeshes(s32 first_i)
1125 {
1126         assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
1127         updateMesh(first_i);
1128         for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
1129         {
1130                 if(i == first_i)
1131                         continue;
1132                 updateMesh(i);
1133         }
1134 }*/
1135
1136 #endif // !SERVER
1137
1138 /*
1139         Propagates sunlight down through the block.
1140         Doesn't modify nodes that are not affected by sunlight.
1141         
1142         Returns false if sunlight at bottom block is invalid
1143         Returns true if bottom block doesn't exist.
1144
1145         If there is a block above, continues from it.
1146         If there is no block above, assumes there is sunlight, unless
1147         is_underground is set or highest node is water.
1148
1149         At the moment, all sunlighted nodes are added to light_sources.
1150         - SUGG: This could be optimized
1151
1152         Turns sunglighted mud into grass.
1153
1154         if remove_light==true, sets non-sunlighted nodes black.
1155
1156         if black_air_left!=NULL, it is set to true if non-sunlighted
1157         air is left in block.
1158 */
1159 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1160                 bool remove_light, bool *black_air_left,
1161                 bool grow_grass)
1162 {
1163         // Whether the sunlight at the top of the bottom block is valid
1164         bool block_below_is_valid = true;
1165         
1166         v3s16 pos_relative = getPosRelative();
1167         
1168         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1169         {
1170                 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1171                 {
1172 #if 1
1173                         bool no_sunlight = false;
1174                         bool no_top_block = false;
1175                         // Check if node above block has sunlight
1176                         try{
1177                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1178                                 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1179                                 {
1180                                         no_sunlight = true;
1181                                 }
1182                         }
1183                         catch(InvalidPositionException &e)
1184                         {
1185                                 no_top_block = true;
1186                                 
1187                                 // NOTE: This makes over-ground roofed places sunlighted
1188                                 // Assume sunlight, unless is_underground==true
1189                                 if(is_underground)
1190                                 {
1191                                         no_sunlight = true;
1192                                 }
1193                                 else
1194                                 {
1195                                         MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1196                                         if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1197                                         {
1198                                                 no_sunlight = true;
1199                                         }
1200                                 }
1201                                 // NOTE: As of now, this just would make everything dark.
1202                                 // No sunlight here
1203                                 //no_sunlight = true;
1204                         }
1205 #endif
1206 #if 0 // Doesn't work; nothing gets light.
1207                         bool no_sunlight = true;
1208                         bool no_top_block = false;
1209                         // Check if node above block has sunlight
1210                         try{
1211                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1212                                 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1213                                 {
1214                                         no_sunlight = false;
1215                                 }
1216                         }
1217                         catch(InvalidPositionException &e)
1218                         {
1219                                 no_top_block = true;
1220                         }
1221 #endif
1222
1223                         /*std::cout<<"("<<x<<","<<z<<"): "
1224                                         <<"no_top_block="<<no_top_block
1225                                         <<", is_underground="<<is_underground
1226                                         <<", no_sunlight="<<no_sunlight
1227                                         <<std::endl;*/
1228                 
1229                         s16 y = MAP_BLOCKSIZE-1;
1230                         
1231                         // This makes difference to diminishing in water.
1232                         bool stopped_to_solid_object = false;
1233                         
1234                         u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1235
1236                         for(; y >= 0; y--)
1237                         {
1238                                 v3s16 pos(x, y, z);
1239                                 MapNode &n = getNodeRef(pos);
1240                                 
1241                                 if(current_light == 0)
1242                                 {
1243                                         // Do nothing
1244                                 }
1245                                 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1246                                 {
1247                                         // Do nothing: Sunlight is continued
1248                                 }
1249                                 else if(n.light_propagates() == false)
1250                                 {
1251                                         if(grow_grass)
1252                                         {
1253                                                 bool upper_is_air = false;
1254                                                 try
1255                                                 {
1256                                                         if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1257                                                                 upper_is_air = true;
1258                                                 }
1259                                                 catch(InvalidPositionException &e)
1260                                                 {
1261                                                 }
1262                                                 // Turn mud into grass
1263                                                 if(upper_is_air && n.d == CONTENT_MUD
1264                                                                 && current_light == LIGHT_SUN)
1265                                                 {
1266                                                         n.d = CONTENT_GRASS;
1267                                                 }
1268                                         }
1269
1270                                         // A solid object is on the way.
1271                                         stopped_to_solid_object = true;
1272                                         
1273                                         // Light stops.
1274                                         current_light = 0;
1275                                 }
1276                                 else
1277                                 {
1278                                         // Diminish light
1279                                         current_light = diminish_light(current_light);
1280                                 }
1281
1282                                 u8 old_light = n.getLight(LIGHTBANK_DAY);
1283
1284                                 if(current_light > old_light || remove_light)
1285                                 {
1286                                         n.setLight(LIGHTBANK_DAY, current_light);
1287                                 }
1288                                 
1289                                 if(diminish_light(current_light) != 0)
1290                                 {
1291                                         light_sources.insert(pos_relative + pos, true);
1292                                 }
1293
1294                                 if(current_light == 0 && stopped_to_solid_object)
1295                                 {
1296                                         if(black_air_left)
1297                                         {
1298                                                 *black_air_left = true;
1299                                         }
1300                                 }
1301                         }
1302
1303                         // Whether or not the block below should see LIGHT_SUN
1304                         bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1305
1306                         /*
1307                                 If the block below hasn't already been marked invalid:
1308
1309                                 Check if the node below the block has proper sunlight at top.
1310                                 If not, the block below is invalid.
1311                                 
1312                                 Ignore non-transparent nodes as they always have no light
1313                         */
1314                         try
1315                         {
1316                         if(block_below_is_valid)
1317                         {
1318                                 MapNode n = getNodeParent(v3s16(x, -1, z));
1319                                 if(n.light_propagates())
1320                                 {
1321                                         if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1322                                                         && sunlight_should_go_down == false)
1323                                                 block_below_is_valid = false;
1324                                         else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1325                                                         && sunlight_should_go_down == true)
1326                                                 block_below_is_valid = false;
1327                                 }
1328                         }//if
1329                         }//try
1330                         catch(InvalidPositionException &e)
1331                         {
1332                                 /*std::cout<<"InvalidBlockException for bottom block node"
1333                                                 <<std::endl;*/
1334                                 // Just no block below, no need to panic.
1335                         }
1336                 }
1337         }
1338
1339         return block_below_is_valid;
1340 }
1341
1342 void MapBlock::copyTo(VoxelManipulator &dst)
1343 {
1344         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1345         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1346         
1347         // Copy from data to VoxelManipulator
1348         dst.copyFrom(data, data_area, v3s16(0,0,0),
1349                         getPosRelative(), data_size);
1350 }
1351
1352 void MapBlock::copyFrom(VoxelManipulator &dst)
1353 {
1354         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1355         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1356         
1357         // Copy from VoxelManipulator to data
1358         dst.copyTo(data, data_area, v3s16(0,0,0),
1359                         getPosRelative(), data_size);
1360 }
1361
1362 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1363 {
1364         /*
1365                 Step objects
1366         */
1367         m_objects.step(dtime, server, daynight_ratio);
1368         
1369         /*
1370                 Spawn some objects at random.
1371
1372                 Use dayNightDiffed() to approximate being near ground level
1373         */
1374         if(m_spawn_timer < -999)
1375         {
1376                 m_spawn_timer = 60;
1377         }
1378         if(dayNightDiffed() == true && getObjectCount() == 0)
1379         {
1380                 m_spawn_timer -= dtime;
1381                 if(m_spawn_timer <= 0.0)
1382                 {
1383                         m_spawn_timer += myrand() % 300;
1384                         
1385                         v2s16 p2d(
1386                                 (myrand()%(MAP_BLOCKSIZE-1))+0,
1387                                 (myrand()%(MAP_BLOCKSIZE-1))+0
1388                         );
1389
1390                         s16 y = getGroundLevel(p2d);
1391                         
1392                         if(y >= 0)
1393                         {
1394                                 v3s16 p(p2d.X, y+1, p2d.Y);
1395
1396                                 if(getNode(p).d == CONTENT_AIR
1397                                                 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1398                                 {
1399                                         RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1400                                         addObject(obj);
1401                                 }
1402                         }
1403                 }
1404         }
1405
1406         setChangedFlag();
1407 }
1408
1409
1410 void MapBlock::updateDayNightDiff()
1411 {
1412         if(data == NULL)
1413         {
1414                 m_day_night_differs = false;
1415                 return;
1416         }
1417
1418         bool differs = false;
1419
1420         /*
1421                 Check if any lighting value differs
1422         */
1423         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1424         {
1425                 MapNode &n = data[i];
1426                 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1427                 {
1428                         differs = true;
1429                         break;
1430                 }
1431         }
1432
1433         /*
1434                 If some lighting values differ, check if the whole thing is
1435                 just air. If it is, differ = false
1436         */
1437         if(differs)
1438         {
1439                 bool only_air = true;
1440                 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1441                 {
1442                         MapNode &n = data[i];
1443                         if(n.d != CONTENT_AIR)
1444                         {
1445                                 only_air = false;
1446                                 break;
1447                         }
1448                 }
1449                 if(only_air)
1450                         differs = false;
1451         }
1452
1453         // Set member variable
1454         m_day_night_differs = differs;
1455 }
1456
1457 s16 MapBlock::getGroundLevel(v2s16 p2d)
1458 {
1459         if(isDummy())
1460                 return -3;
1461         try
1462         {
1463                 s16 y = MAP_BLOCKSIZE-1;
1464                 for(; y>=0; y--)
1465                 {
1466                         //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1467                         if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1468                         {
1469                                 if(y == MAP_BLOCKSIZE-1)
1470                                         return -2;
1471                                 else
1472                                         return y;
1473                         }
1474                 }
1475                 return -1;
1476         }
1477         catch(InvalidPositionException &e)
1478         {
1479                 return -3;
1480         }
1481 }
1482
1483 /*
1484         Serialization
1485 */
1486
1487 void MapBlock::serialize(std::ostream &os, u8 version)
1488 {
1489         if(!ser_ver_supported(version))
1490                 throw VersionMismatchException("ERROR: MapBlock format not supported");
1491         
1492         if(data == NULL)
1493         {
1494                 throw SerializationError("ERROR: Not writing dummy block.");
1495         }
1496         
1497         // These have no compression
1498         if(version <= 3 || version == 5 || version == 6)
1499         {
1500                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1501                 
1502                 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1503                 SharedBuffer<u8> dest(buflen);
1504
1505                 dest[0] = is_underground;
1506                 for(u32 i=0; i<nodecount; i++)
1507                 {
1508                         u32 s = 1 + i * MapNode::serializedLength(version);
1509                         data[i].serialize(&dest[s], version);
1510                 }
1511                 
1512                 os.write((char*)*dest, dest.getSize());
1513         }
1514         else if(version <= 10)
1515         {
1516                 /*
1517                         With compression.
1518                         Compress the materials and the params separately.
1519                 */
1520                 
1521                 // First byte
1522                 os.write((char*)&is_underground, 1);
1523
1524                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1525
1526                 // Get and compress materials
1527                 SharedBuffer<u8> materialdata(nodecount);
1528                 for(u32 i=0; i<nodecount; i++)
1529                 {
1530                         materialdata[i] = data[i].d;
1531                 }
1532                 compress(materialdata, os, version);
1533
1534                 // Get and compress lights
1535                 SharedBuffer<u8> lightdata(nodecount);
1536                 for(u32 i=0; i<nodecount; i++)
1537                 {
1538                         lightdata[i] = data[i].param;
1539                 }
1540                 compress(lightdata, os, version);
1541                 
1542                 if(version >= 10)
1543                 {
1544                         // Get and compress param2
1545                         SharedBuffer<u8> param2data(nodecount);
1546                         for(u32 i=0; i<nodecount; i++)
1547                         {
1548                                 param2data[i] = data[i].param2;
1549                         }
1550                         compress(param2data, os, version);
1551                 }
1552         }
1553         // All other versions (newest)
1554         else
1555         {
1556                 // First byte
1557                 u8 flags = 0;
1558                 if(is_underground)
1559                         flags |= 1;
1560                 if(m_day_night_differs)
1561                         flags |= 2;
1562                 if(m_lighting_expired)
1563                         flags |= 3;
1564                 os.write((char*)&flags, 1);
1565
1566                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1567
1568                 /*
1569                         Get data
1570                 */
1571
1572                 SharedBuffer<u8> databuf(nodecount*3);
1573
1574                 // Get contents
1575                 for(u32 i=0; i<nodecount; i++)
1576                 {
1577                         databuf[i] = data[i].d;
1578                 }
1579
1580                 // Get params
1581                 for(u32 i=0; i<nodecount; i++)
1582                 {
1583                         databuf[i+nodecount] = data[i].param;
1584                 }
1585
1586                 // Get param2
1587                 for(u32 i=0; i<nodecount; i++)
1588                 {
1589                         databuf[i+nodecount*2] = data[i].param2;
1590                 }
1591
1592                 /*
1593                         Compress data to output stream
1594                 */
1595
1596                 compress(databuf, os, version);
1597         }
1598 }
1599
1600 void MapBlock::deSerialize(std::istream &is, u8 version)
1601 {
1602         if(!ser_ver_supported(version))
1603                 throw VersionMismatchException("ERROR: MapBlock format not supported");
1604
1605         // These have no compression
1606         if(version <= 3 || version == 5 || version == 6)
1607         {
1608                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1609                 char tmp;
1610                 is.read(&tmp, 1);
1611                 if(is.gcount() != 1)
1612                         throw SerializationError
1613                                         ("MapBlock::deSerialize: no enough input data");
1614                 is_underground = tmp;
1615                 for(u32 i=0; i<nodecount; i++)
1616                 {
1617                         s32 len = MapNode::serializedLength(version);
1618                         SharedBuffer<u8> d(len);
1619                         is.read((char*)*d, len);
1620                         if(is.gcount() != len)
1621                                 throw SerializationError
1622                                                 ("MapBlock::deSerialize: no enough input data");
1623                         data[i].deSerialize(*d, version);
1624                 }
1625         }
1626         else if(version <= 10)
1627         {
1628                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1629
1630                 u8 t8;
1631                 is.read((char*)&t8, 1);
1632                 is_underground = t8;
1633
1634                 {
1635                         // Uncompress and set material data
1636                         std::ostringstream os(std::ios_base::binary);
1637                         decompress(is, os, version);
1638                         std::string s = os.str();
1639                         if(s.size() != nodecount)
1640                                 throw SerializationError
1641                                                 ("MapBlock::deSerialize: invalid format");
1642                         for(u32 i=0; i<s.size(); i++)
1643                         {
1644                                 data[i].d = s[i];
1645                         }
1646                 }
1647                 {
1648                         // Uncompress and set param data
1649                         std::ostringstream os(std::ios_base::binary);
1650                         decompress(is, os, version);
1651                         std::string s = os.str();
1652                         if(s.size() != nodecount)
1653                                 throw SerializationError
1654                                                 ("MapBlock::deSerialize: invalid format");
1655                         for(u32 i=0; i<s.size(); i++)
1656                         {
1657                                 data[i].param = s[i];
1658                         }
1659                 }
1660         
1661                 if(version >= 10)
1662                 {
1663                         // Uncompress and set param2 data
1664                         std::ostringstream os(std::ios_base::binary);
1665                         decompress(is, os, version);
1666                         std::string s = os.str();
1667                         if(s.size() != nodecount)
1668                                 throw SerializationError
1669                                                 ("MapBlock::deSerialize: invalid format");
1670                         for(u32 i=0; i<s.size(); i++)
1671                         {
1672                                 data[i].param2 = s[i];
1673                         }
1674                 }
1675         }
1676         // All other versions (newest)
1677         else
1678         {
1679                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1680
1681                 u8 flags;
1682                 is.read((char*)&flags, 1);
1683                 is_underground = (flags & 1) ? true : false;
1684                 m_day_night_differs = (flags & 2) ? true : false;
1685                 m_lighting_expired = (flags & 3) ? true : false;
1686
1687                 // Uncompress data
1688                 std::ostringstream os(std::ios_base::binary);
1689                 decompress(is, os, version);
1690                 std::string s = os.str();
1691                 if(s.size() != nodecount*3)
1692                         throw SerializationError
1693                                         ("MapBlock::deSerialize: invalid format");
1694
1695                 // Set contents
1696                 for(u32 i=0; i<nodecount; i++)
1697                 {
1698                         data[i].d = s[i];
1699                 }
1700                 // Set params
1701                 for(u32 i=0; i<nodecount; i++)
1702                 {
1703                         data[i].param = s[i+nodecount];
1704                 }
1705                 // Set param2
1706                 for(u32 i=0; i<nodecount; i++)
1707                 {
1708                         data[i].param2 = s[i+nodecount*2];
1709                 }
1710         }
1711         
1712         /*
1713                 Translate nodes as specified in the translate_to fields of
1714                 node features
1715         */
1716         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1717         {
1718                 MapNode &n = data[i];
1719
1720                 MapNode *translate_to = content_features(n.d).translate_to;
1721                 if(translate_to)
1722                 {
1723                         dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
1724                                         <<translate_to->d<<std::endl;
1725                         n = *translate_to;
1726                 }
1727         }
1728 }
1729
1730
1731 //END