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