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