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