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