]> git.lizzy.rs Git - dragonfireclient.git/blob - src/mapblock.cpp
CMake stuff works now on linux and windows... and should be possible to make to work...
[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                 // This will lead to infinite memory usage because or irrlicht.
692                 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
693                 
694                 /*std::cout<<"MapBlock has "<<fastfaces_new->getSize()<<" faces "
695                                 <<"and uses "<<mesh_new->getMeshBufferCount()
696                                 <<" materials (meshbuffers)"<<std::endl;*/
697         }
698
699         /*
700                 Add special graphics:
701                 - torches
702                 
703                 TODO: Optimize by using same meshbuffer for same textures
704         */
705
706         for(s16 z=0; z<MAP_BLOCKSIZE; z++)
707         for(s16 y=0; y<MAP_BLOCKSIZE; y++)
708         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
709         {
710                 v3s16 p(x,y,z);
711
712                 MapNode &n = getNodeRef(x,y,z);
713                 
714                 if(n.d == CONTENT_TORCH)
715                 {
716                         //scene::IMeshBuffer *buf = new scene::SMeshBuffer();
717                         scene::SMeshBuffer *buf = new scene::SMeshBuffer();
718                         video::SColor c(255,255,255,255);
719
720                         video::S3DVertex vertices[4] =
721                         {
722                                 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
723                                 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
724                                 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
725                                 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
726                         };
727
728                         v3s16 dir = unpackDir(n.dir);
729
730                         for(s32 i=0; i<4; i++)
731                         {
732                                 if(dir == v3s16(1,0,0))
733                                         vertices[i].Pos.rotateXZBy(0);
734                                 if(dir == v3s16(-1,0,0))
735                                         vertices[i].Pos.rotateXZBy(180);
736                                 if(dir == v3s16(0,0,1))
737                                         vertices[i].Pos.rotateXZBy(90);
738                                 if(dir == v3s16(0,0,-1))
739                                         vertices[i].Pos.rotateXZBy(-90);
740                                 if(dir == v3s16(0,-1,0))
741                                         vertices[i].Pos.rotateXZBy(45);
742                                 if(dir == v3s16(0,1,0))
743                                         vertices[i].Pos.rotateXZBy(-45);
744
745                                 vertices[i].Pos += intToFloat(p + getPosRelative());
746                         }
747
748                         u16 indices[] = {0,1,2,2,3,0};
749                         buf->append(vertices, 4, indices, 6);
750
751                         // Set material
752                         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
753                         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
754                         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
755                         //buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
756                         buf->getMaterial().MaterialType
757                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
758                         if(dir == v3s16(0,-1,0))
759                                 buf->getMaterial().setTexture(0,
760                                                 g_irrlicht->getTexture(porting::getDataPath("torch_on_floor.png").c_str()));
761                         else if(dir == v3s16(0,1,0))
762                                 buf->getMaterial().setTexture(0,
763                                                 g_irrlicht->getTexture(porting::getDataPath("torch_on_ceiling.png").c_str()));
764                         // For backwards compatibility
765                         else if(dir == v3s16(0,0,0))
766                                 buf->getMaterial().setTexture(0,
767                                                 g_irrlicht->getTexture(porting::getDataPath("torch_on_floor.png").c_str()));
768                         else
769                                 buf->getMaterial().setTexture(0, 
770                                                 g_irrlicht->getTexture(porting::getDataPath("torch.png").c_str()));
771
772                         // Add to mesh
773                         mesh_new->addMeshBuffer(buf);
774                         buf->drop();
775                 }
776         }
777         
778         /*
779                 Do some stuff to the mesh
780         */
781
782         mesh_new->recalculateBoundingBox();
783
784         /*
785                 Delete new mesh if it is empty
786         */
787
788         if(mesh_new->getMeshBufferCount() == 0)
789         {
790                 mesh_new->drop();
791                 mesh_new = NULL;
792         }
793
794         /*
795                 Replace the mesh
796         */
797
798         mesh_mutex.Lock();
799
800         //scene::SMesh *mesh_old = mesh[daynight_i];
801         //mesh[daynight_i] = mesh_new;
802
803         scene::SMesh *mesh_old = mesh;
804         mesh = mesh_new;
805         setMeshExpired(false);
806         
807         if(mesh_old != NULL)
808         {
809                 // Remove hardware buffers of meshbuffers of mesh
810                 // NOTE: No way, this runs in a different thread and everything
811                 /*u32 c = mesh_old->getMeshBufferCount();
812                 for(u32 i=0; i<c; i++)
813                 {
814                         IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
815                 }*/
816                 
817                 /*dstream<<"mesh_old->getReferenceCount()="
818                                 <<mesh_old->getReferenceCount()<<std::endl;
819                 u32 c = mesh_old->getMeshBufferCount();
820                 for(u32 i=0; i<c; i++)
821                 {
822                         scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
823                         dstream<<"buf->getReferenceCount()="
824                                         <<buf->getReferenceCount()<<std::endl;
825                 }*/
826
827                 // Drop the mesh
828                 mesh_old->drop();
829
830                 //delete mesh_old;
831         }
832
833         mesh_mutex.Unlock();
834         
835         //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
836 }
837
838 /*void MapBlock::updateMeshes(s32 first_i)
839 {
840         assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
841         updateMesh(first_i);
842         for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
843         {
844                 if(i == first_i)
845                         continue;
846                 updateMesh(i);
847         }
848 }*/
849
850 #endif // !SERVER
851
852 /*
853         Propagates sunlight down through the block.
854         Doesn't modify nodes that are not affected by sunlight.
855         
856         Returns false if sunlight at bottom block is invalid
857         Returns true if bottom block doesn't exist.
858
859         If there is a block above, continues from it.
860         If there is no block above, assumes there is sunlight, unless
861         is_underground is set.
862
863         At the moment, all sunlighted nodes are added to light_sources.
864         - SUGG: This could be optimized
865
866         Turns sunglighted mud into grass.
867 */
868 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
869 {
870         // Whether the sunlight at the top of the bottom block is valid
871         bool block_below_is_valid = true;
872         
873         v3s16 pos_relative = getPosRelative();
874         
875         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
876         {
877                 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
878                 {
879                         bool no_sunlight = false;
880                         bool no_top_block = false;
881                         // Check if node above block has sunlight
882                         try{
883                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
884                                 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
885                                 {
886                                         no_sunlight = true;
887                                 }
888                         }
889                         catch(InvalidPositionException &e)
890                         {
891                                 no_top_block = true;
892                                 
893                                 // NOTE: This makes over-ground roofed places sunlighted
894                                 // Assume sunlight, unless is_underground==true
895                                 if(is_underground)
896                                 {
897                                         no_sunlight = true;
898                                 }
899                                 
900                                 // NOTE: As of now, it just would make everything dark.
901                                 // No sunlight here
902                                 //no_sunlight = true;
903                         }
904
905                         /*std::cout<<"("<<x<<","<<z<<"): "
906                                         <<"no_top_block="<<no_top_block
907                                         <<", is_underground="<<is_underground
908                                         <<", no_sunlight="<<no_sunlight
909                                         <<std::endl;*/
910                 
911                         s16 y = MAP_BLOCKSIZE-1;
912                         
913                         if(no_sunlight == false)
914                         {
915                                 // Continue spreading sunlight downwards through transparent
916                                 // nodes
917                                 for(; y >= 0; y--)
918                                 {
919                                         v3s16 pos(x, y, z);
920                                         
921                                         MapNode &n = getNodeRef(pos);
922
923                                         if(n.sunlight_propagates())
924                                         {
925                                                 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
926
927                                                 light_sources.insert(pos_relative + pos, true);
928                                         }
929                                         else
930                                         {
931                                                 // Turn mud into grass
932                                                 if(n.d == CONTENT_MUD)
933                                                 {
934                                                         n.d = CONTENT_GRASS;
935                                                 }
936
937                                                 // Sunlight goes no further
938                                                 break;
939                                         }
940                                 }
941                         }
942
943                         bool sunlight_should_go_down = (y==-1);
944
945                         // Fill rest with black (only transparent ones)
946                         for(; y >= 0; y--){
947                                 v3s16 pos(x, y, z);
948                                 
949                                 MapNode &n = getNodeRef(pos);
950
951                                 if(n.light_propagates())
952                                 {
953                                         n.setLight(LIGHTBANK_DAY, 0);
954                                 }
955                                 else{
956                                         break;
957                                 }
958                         }
959
960                         /*
961                                 If the block below hasn't already been marked invalid:
962
963                                 Check if the node below the block has proper sunlight at top.
964                                 If not, the block below is invalid.
965                                 
966                                 Ignore non-transparent nodes as they always have no light
967                         */
968                         try
969                         {
970                         if(block_below_is_valid)
971                         {
972                                 MapNode n = getNodeParent(v3s16(x, -1, z));
973                                 if(n.light_propagates())
974                                 {
975                                         if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
976                                                         && sunlight_should_go_down == false)
977                                                 block_below_is_valid = false;
978                                         else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
979                                                         && sunlight_should_go_down == true)
980                                                 block_below_is_valid = false;
981                                 }
982                         }//if
983                         }//try
984                         catch(InvalidPositionException &e)
985                         {
986                                 /*std::cout<<"InvalidBlockException for bottom block node"
987                                                 <<std::endl;*/
988                                 // Just no block below, no need to panic.
989                         }
990                 }
991         }
992
993         return block_below_is_valid;
994 }
995
996 void MapBlock::copyTo(VoxelManipulator &dst)
997 {
998         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
999         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1000         
1001         dst.copyFrom(data, data_area, v3s16(0,0,0),
1002                         getPosRelative(), data_size);
1003 }
1004
1005 /*void getPseudoObjects(v3f origin, f32 max_d,
1006                 core::array<DistanceSortedObject> &dest)
1007 {
1008 }*/
1009 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1010 {
1011         /*
1012                 Step objects
1013         */
1014         m_objects.step(dtime, server, daynight_ratio);
1015         
1016         /*
1017                 Spawn some objects at random.
1018
1019                 Use dayNightDiffed() to approximate being near ground level
1020         */
1021         if(m_spawn_timer < -999)
1022         {
1023                 m_spawn_timer = 60;
1024         }
1025         if(dayNightDiffed() == true && getObjectCount() == 0)
1026         {
1027                 m_spawn_timer -= dtime;
1028                 if(m_spawn_timer <= 0.0)
1029                 {
1030                         m_spawn_timer += myrand() % 300;
1031                         
1032                         v2s16 p2d(
1033                                 (myrand()%(MAP_BLOCKSIZE-1))+0,
1034                                 (myrand()%(MAP_BLOCKSIZE-1))+0
1035                         );
1036
1037                         s16 y = getGroundLevel(p2d);
1038                         
1039                         if(y >= 0)
1040                         {
1041                                 v3s16 p(p2d.X, y+1, p2d.Y);
1042
1043                                 if(getNode(p).d == CONTENT_AIR
1044                                                 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1045                                 {
1046                                         RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1047                                         addObject(obj);
1048                                 }
1049                         }
1050                 }
1051         }
1052
1053         setChangedFlag();
1054 }
1055
1056
1057 void MapBlock::updateDayNightDiff()
1058 {
1059         if(data == NULL)
1060         {
1061                 m_day_night_differs = false;
1062                 return;
1063         }
1064
1065         bool differs = false;
1066
1067         /*
1068                 Check if any lighting value differs
1069         */
1070         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1071         {
1072                 MapNode &n = data[i];
1073                 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1074                 {
1075                         differs = true;
1076                         break;
1077                 }
1078         }
1079
1080         /*
1081                 If some lighting values differ, check if the whole thing is
1082                 just air. If it is, differ = false
1083         */
1084         if(differs)
1085         {
1086                 bool only_air = true;
1087                 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1088                 {
1089                         MapNode &n = data[i];
1090                         if(n.d != CONTENT_AIR)
1091                         {
1092                                 only_air = false;
1093                                 break;
1094                         }
1095                 }
1096                 if(only_air)
1097                         differs = false;
1098         }
1099
1100         // Set member variable
1101         m_day_night_differs = differs;
1102 }
1103
1104 s16 MapBlock::getGroundLevel(v2s16 p2d)
1105 {
1106         if(isDummy())
1107                 return -3;
1108         try
1109         {
1110                 s16 y = MAP_BLOCKSIZE-1;
1111                 for(; y>=0; y--)
1112                 {
1113                         if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1114                         {
1115                                 if(y == MAP_BLOCKSIZE-1)
1116                                         return -2;
1117                                 else
1118                                         return y;
1119                         }
1120                 }
1121                 return -1;
1122         }
1123         catch(InvalidPositionException &e)
1124         {
1125                 return -3;
1126         }
1127 }
1128
1129 /*
1130         Serialization
1131 */
1132
1133 void MapBlock::serialize(std::ostream &os, u8 version)
1134 {
1135         if(!ser_ver_supported(version))
1136                 throw VersionMismatchException("ERROR: MapBlock format not supported");
1137         
1138         if(data == NULL)
1139         {
1140                 throw SerializationError("ERROR: Not writing dummy block.");
1141         }
1142         
1143         // These have no compression
1144         if(version <= 3 || version == 5 || version == 6)
1145         {
1146                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1147                 
1148                 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1149                 SharedBuffer<u8> dest(buflen);
1150
1151                 dest[0] = is_underground;
1152                 for(u32 i=0; i<nodecount; i++)
1153                 {
1154                         u32 s = 1 + i * MapNode::serializedLength(version);
1155                         data[i].serialize(&dest[s], version);
1156                 }
1157                 
1158                 os.write((char*)*dest, dest.getSize());
1159         }
1160         else if(version <= 10)
1161         {
1162                 /*
1163                         With compression.
1164                         Compress the materials and the params separately.
1165                 */
1166                 
1167                 // First byte
1168                 os.write((char*)&is_underground, 1);
1169
1170                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1171
1172                 // Get and compress materials
1173                 SharedBuffer<u8> materialdata(nodecount);
1174                 for(u32 i=0; i<nodecount; i++)
1175                 {
1176                         materialdata[i] = data[i].d;
1177                 }
1178                 compress(materialdata, os, version);
1179
1180                 // Get and compress lights
1181                 SharedBuffer<u8> lightdata(nodecount);
1182                 for(u32 i=0; i<nodecount; i++)
1183                 {
1184                         lightdata[i] = data[i].param;
1185                 }
1186                 compress(lightdata, os, version);
1187                 
1188                 if(version >= 10)
1189                 {
1190                         // Get and compress pressure
1191                         SharedBuffer<u8> pressuredata(nodecount);
1192                         for(u32 i=0; i<nodecount; i++)
1193                         {
1194                                 pressuredata[i] = data[i].pressure;
1195                         }
1196                         compress(pressuredata, os, version);
1197                 }
1198         }
1199         // All other versions (newest)
1200         else
1201         {
1202                 // First byte
1203                 u8 flags = 0;
1204                 if(is_underground)
1205                         flags |= 1;
1206                 if(m_day_night_differs)
1207                         flags |= 2;
1208                 os.write((char*)&flags, 1);
1209
1210                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1211
1212                 /*
1213                         Get data
1214                 */
1215
1216                 SharedBuffer<u8> databuf(nodecount*3);
1217
1218                 // Get contents
1219                 for(u32 i=0; i<nodecount; i++)
1220                 {
1221                         databuf[i] = data[i].d;
1222                 }
1223
1224                 // Get params
1225                 for(u32 i=0; i<nodecount; i++)
1226                 {
1227                         databuf[i+nodecount] = data[i].param;
1228                 }
1229
1230                 // Get pressure
1231                 for(u32 i=0; i<nodecount; i++)
1232                 {
1233                         databuf[i+nodecount*2] = data[i].pressure;
1234                 }
1235
1236                 /*
1237                         Compress data to output stream
1238                 */
1239
1240                 compress(databuf, os, version);
1241         }
1242 }
1243
1244 void MapBlock::deSerialize(std::istream &is, u8 version)
1245 {
1246         if(!ser_ver_supported(version))
1247                 throw VersionMismatchException("ERROR: MapBlock format not supported");
1248
1249         // These have no compression
1250         if(version <= 3 || version == 5 || version == 6)
1251         {
1252                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1253                 char tmp;
1254                 is.read(&tmp, 1);
1255                 if(is.gcount() != 1)
1256                         throw SerializationError
1257                                         ("MapBlock::deSerialize: no enough input data");
1258                 is_underground = tmp;
1259                 for(u32 i=0; i<nodecount; i++)
1260                 {
1261                         s32 len = MapNode::serializedLength(version);
1262                         SharedBuffer<u8> d(len);
1263                         is.read((char*)*d, len);
1264                         if(is.gcount() != len)
1265                                 throw SerializationError
1266                                                 ("MapBlock::deSerialize: no enough input data");
1267                         data[i].deSerialize(*d, version);
1268                 }
1269         }
1270         else if(version <= 10)
1271         {
1272                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1273
1274                 u8 t8;
1275                 is.read((char*)&t8, 1);
1276                 is_underground = t8;
1277
1278                 {
1279                         // Uncompress and set material data
1280                         std::ostringstream os(std::ios_base::binary);
1281                         decompress(is, os, version);
1282                         std::string s = os.str();
1283                         if(s.size() != nodecount)
1284                                 throw SerializationError
1285                                                 ("MapBlock::deSerialize: invalid format");
1286                         for(u32 i=0; i<s.size(); i++)
1287                         {
1288                                 data[i].d = s[i];
1289                         }
1290                 }
1291                 {
1292                         // Uncompress and set param data
1293                         std::ostringstream os(std::ios_base::binary);
1294                         decompress(is, os, version);
1295                         std::string s = os.str();
1296                         if(s.size() != nodecount)
1297                                 throw SerializationError
1298                                                 ("MapBlock::deSerialize: invalid format");
1299                         for(u32 i=0; i<s.size(); i++)
1300                         {
1301                                 data[i].param = s[i];
1302                         }
1303                 }
1304         
1305                 if(version >= 10)
1306                 {
1307                         // Uncompress and set pressure data
1308                         std::ostringstream os(std::ios_base::binary);
1309                         decompress(is, os, version);
1310                         std::string s = os.str();
1311                         if(s.size() != nodecount)
1312                                 throw SerializationError
1313                                                 ("MapBlock::deSerialize: invalid format");
1314                         for(u32 i=0; i<s.size(); i++)
1315                         {
1316                                 data[i].pressure = s[i];
1317                         }
1318                 }
1319         }
1320         // All other versions (newest)
1321         else
1322         {
1323                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1324
1325                 u8 flags;
1326                 is.read((char*)&flags, 1);
1327                 is_underground = (flags & 1) ? true : false;
1328                 m_day_night_differs = (flags & 2) ? true : false;
1329
1330                 // Uncompress data
1331                 std::ostringstream os(std::ios_base::binary);
1332                 decompress(is, os, version);
1333                 std::string s = os.str();
1334                 if(s.size() != nodecount*3)
1335                         throw SerializationError
1336                                         ("MapBlock::deSerialize: invalid format");
1337
1338                 // Set contents
1339                 for(u32 i=0; i<nodecount; i++)
1340                 {
1341                         data[i].d = s[i];
1342                 }
1343                 // Set params
1344                 for(u32 i=0; i<nodecount; i++)
1345                 {
1346                         data[i].param = s[i+nodecount];
1347                 }
1348                 // Set pressure
1349                 for(u32 i=0; i<nodecount; i++)
1350                 {
1351                         data[i].pressure = s[i+nodecount*2];
1352                 }
1353         }
1354 }
1355
1356
1357 //END