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