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