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