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