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