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