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