]> git.lizzy.rs Git - minetest.git/blob - src/mapblock.cpp
fine-tuning of map generator and server and stuff.
[minetest.git] / src / mapblock.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "mapblock.h"
21 #include "map.h"
22 // For g_materials
23 #include "main.h"
24 #include "light.h"
25 #include <sstream>
26
27
28 /*
29         MapBlock
30 */
31
32 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
33                 m_parent(parent),
34                 m_pos(pos),
35                 changed(true),
36                 is_underground(false),
37                 m_day_night_differs(false),
38                 m_objects(this)
39 {
40         data = NULL;
41         if(dummy == false)
42                 reallocate();
43         
44         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         if remove_light==true, sets non-sunlighted nodes black.
869
870         if black_air_left!=NULL, it is set to true if non-sunlighted
871         air is left in block.
872 */
873 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
874                 bool remove_light, bool *black_air_left)
875 {
876         // Whether the sunlight at the top of the bottom block is valid
877         bool block_below_is_valid = true;
878         
879         v3s16 pos_relative = getPosRelative();
880         
881         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
882         {
883                 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
884                 {
885 #if 1
886                         bool no_sunlight = false;
887                         bool no_top_block = false;
888                         // Check if node above block has sunlight
889                         try{
890                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
891                                 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
892                                 {
893                                         no_sunlight = true;
894                                 }
895                         }
896                         catch(InvalidPositionException &e)
897                         {
898                                 no_top_block = true;
899                                 
900                                 // NOTE: This makes over-ground roofed places sunlighted
901                                 // Assume sunlight, unless is_underground==true
902                                 if(is_underground)
903                                 {
904                                         no_sunlight = true;
905                                 }
906                                 
907                                 // NOTE: As of now, it just would make everything dark.
908                                 // No sunlight here
909                                 //no_sunlight = true;
910                         }
911 #endif
912 #if 0 // Doesn't work; nothing gets light.
913                         bool no_sunlight = true;
914                         bool no_top_block = false;
915                         // Check if node above block has sunlight
916                         try{
917                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
918                                 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
919                                 {
920                                         no_sunlight = false;
921                                 }
922                         }
923                         catch(InvalidPositionException &e)
924                         {
925                                 no_top_block = true;
926                         }
927 #endif
928
929                         /*std::cout<<"("<<x<<","<<z<<"): "
930                                         <<"no_top_block="<<no_top_block
931                                         <<", is_underground="<<is_underground
932                                         <<", no_sunlight="<<no_sunlight
933                                         <<std::endl;*/
934                 
935                         s16 y = MAP_BLOCKSIZE-1;
936                         
937                         // This makes difference to diminishing in water.
938                         bool stopped_to_solid_object = false;
939                         
940                         u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
941
942                         for(; y >= 0; y--)
943                         {
944                                 v3s16 pos(x, y, z);
945                                 MapNode &n = getNodeRef(pos);
946                                 
947                                 if(current_light == 0)
948                                 {
949                                         // Do nothing
950                                 }
951                                 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
952                                 {
953                                         // Do nothing: Sunlight is continued
954                                 }
955                                 else if(n.light_propagates() == false)
956                                 {
957                                         // Turn mud into grass
958                                         if(n.d == CONTENT_MUD && current_light == LIGHT_SUN)
959                                         {
960                                                 n.d = CONTENT_GRASS;
961                                         }
962
963                                         // A solid object is on the way.
964                                         stopped_to_solid_object = true;
965                                         
966                                         // Light stops.
967                                         current_light = 0;
968                                 }
969                                 else
970                                 {
971                                         // Diminish light
972                                         current_light = diminish_light(current_light);
973                                 }
974
975                                 u8 old_light = n.getLight(LIGHTBANK_DAY);
976
977                                 if(current_light > old_light || remove_light)
978                                 {
979                                         n.setLight(LIGHTBANK_DAY, current_light);
980                                 }
981                                 
982                                 if(diminish_light(current_light) != 0)
983                                 {
984                                         light_sources.insert(pos_relative + pos, true);
985                                 }
986
987                                 if(current_light == 0 && stopped_to_solid_object)
988                                 {
989                                         if(black_air_left)
990                                         {
991                                                 *black_air_left = true;
992                                         }
993                                 }
994                         }
995
996                         // Whether or not the block below should see LIGHT_SUN
997                         bool sunlight_should_go_down = (current_light == LIGHT_SUN);
998
999                         /*
1000                                 If the block below hasn't already been marked invalid:
1001
1002                                 Check if the node below the block has proper sunlight at top.
1003                                 If not, the block below is invalid.
1004                                 
1005                                 Ignore non-transparent nodes as they always have no light
1006                         */
1007                         try
1008                         {
1009                         if(block_below_is_valid)
1010                         {
1011                                 MapNode n = getNodeParent(v3s16(x, -1, z));
1012                                 if(n.light_propagates())
1013                                 {
1014                                         if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1015                                                         && sunlight_should_go_down == false)
1016                                                 block_below_is_valid = false;
1017                                         else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1018                                                         && sunlight_should_go_down == true)
1019                                                 block_below_is_valid = false;
1020                                 }
1021                         }//if
1022                         }//try
1023                         catch(InvalidPositionException &e)
1024                         {
1025                                 /*std::cout<<"InvalidBlockException for bottom block node"
1026                                                 <<std::endl;*/
1027                                 // Just no block below, no need to panic.
1028                         }
1029                 }
1030         }
1031
1032         return block_below_is_valid;
1033 }
1034
1035 void MapBlock::copyTo(VoxelManipulator &dst)
1036 {
1037         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1038         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1039         
1040         dst.copyFrom(data, data_area, v3s16(0,0,0),
1041                         getPosRelative(), data_size);
1042 }
1043
1044 /*void getPseudoObjects(v3f origin, f32 max_d,
1045                 core::array<DistanceSortedObject> &dest)
1046 {
1047 }*/
1048 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1049 {
1050         /*
1051                 Step objects
1052         */
1053         m_objects.step(dtime, server, daynight_ratio);
1054         
1055         /*
1056                 Spawn some objects at random.
1057
1058                 Use dayNightDiffed() to approximate being near ground level
1059         */
1060         if(m_spawn_timer < -999)
1061         {
1062                 m_spawn_timer = 60;
1063         }
1064         if(dayNightDiffed() == true && getObjectCount() == 0)
1065         {
1066                 m_spawn_timer -= dtime;
1067                 if(m_spawn_timer <= 0.0)
1068                 {
1069                         m_spawn_timer += myrand() % 300;
1070                         
1071                         v2s16 p2d(
1072                                 (myrand()%(MAP_BLOCKSIZE-1))+0,
1073                                 (myrand()%(MAP_BLOCKSIZE-1))+0
1074                         );
1075
1076                         s16 y = getGroundLevel(p2d);
1077                         
1078                         if(y >= 0)
1079                         {
1080                                 v3s16 p(p2d.X, y+1, p2d.Y);
1081
1082                                 if(getNode(p).d == CONTENT_AIR
1083                                                 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1084                                 {
1085                                         RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1086                                         addObject(obj);
1087                                 }
1088                         }
1089                 }
1090         }
1091
1092         setChangedFlag();
1093 }
1094
1095
1096 void MapBlock::updateDayNightDiff()
1097 {
1098         if(data == NULL)
1099         {
1100                 m_day_night_differs = false;
1101                 return;
1102         }
1103
1104         bool differs = false;
1105
1106         /*
1107                 Check if any lighting value differs
1108         */
1109         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1110         {
1111                 MapNode &n = data[i];
1112                 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1113                 {
1114                         differs = true;
1115                         break;
1116                 }
1117         }
1118
1119         /*
1120                 If some lighting values differ, check if the whole thing is
1121                 just air. If it is, differ = false
1122         */
1123         if(differs)
1124         {
1125                 bool only_air = true;
1126                 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1127                 {
1128                         MapNode &n = data[i];
1129                         if(n.d != CONTENT_AIR)
1130                         {
1131                                 only_air = false;
1132                                 break;
1133                         }
1134                 }
1135                 if(only_air)
1136                         differs = false;
1137         }
1138
1139         // Set member variable
1140         m_day_night_differs = differs;
1141 }
1142
1143 s16 MapBlock::getGroundLevel(v2s16 p2d)
1144 {
1145         if(isDummy())
1146                 return -3;
1147         try
1148         {
1149                 s16 y = MAP_BLOCKSIZE-1;
1150                 for(; y>=0; y--)
1151                 {
1152                         if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1153                         {
1154                                 if(y == MAP_BLOCKSIZE-1)
1155                                         return -2;
1156                                 else
1157                                         return y;
1158                         }
1159                 }
1160                 return -1;
1161         }
1162         catch(InvalidPositionException &e)
1163         {
1164                 return -3;
1165         }
1166 }
1167
1168 /*
1169         Serialization
1170 */
1171
1172 void MapBlock::serialize(std::ostream &os, u8 version)
1173 {
1174         if(!ser_ver_supported(version))
1175                 throw VersionMismatchException("ERROR: MapBlock format not supported");
1176         
1177         if(data == NULL)
1178         {
1179                 throw SerializationError("ERROR: Not writing dummy block.");
1180         }
1181         
1182         // These have no compression
1183         if(version <= 3 || version == 5 || version == 6)
1184         {
1185                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1186                 
1187                 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1188                 SharedBuffer<u8> dest(buflen);
1189
1190                 dest[0] = is_underground;
1191                 for(u32 i=0; i<nodecount; i++)
1192                 {
1193                         u32 s = 1 + i * MapNode::serializedLength(version);
1194                         data[i].serialize(&dest[s], version);
1195                 }
1196                 
1197                 os.write((char*)*dest, dest.getSize());
1198         }
1199         else if(version <= 10)
1200         {
1201                 /*
1202                         With compression.
1203                         Compress the materials and the params separately.
1204                 */
1205                 
1206                 // First byte
1207                 os.write((char*)&is_underground, 1);
1208
1209                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1210
1211                 // Get and compress materials
1212                 SharedBuffer<u8> materialdata(nodecount);
1213                 for(u32 i=0; i<nodecount; i++)
1214                 {
1215                         materialdata[i] = data[i].d;
1216                 }
1217                 compress(materialdata, os, version);
1218
1219                 // Get and compress lights
1220                 SharedBuffer<u8> lightdata(nodecount);
1221                 for(u32 i=0; i<nodecount; i++)
1222                 {
1223                         lightdata[i] = data[i].param;
1224                 }
1225                 compress(lightdata, os, version);
1226                 
1227                 if(version >= 10)
1228                 {
1229                         // Get and compress pressure
1230                         SharedBuffer<u8> pressuredata(nodecount);
1231                         for(u32 i=0; i<nodecount; i++)
1232                         {
1233                                 pressuredata[i] = data[i].pressure;
1234                         }
1235                         compress(pressuredata, os, version);
1236                 }
1237         }
1238         // All other versions (newest)
1239         else
1240         {
1241                 // First byte
1242                 u8 flags = 0;
1243                 if(is_underground)
1244                         flags |= 1;
1245                 if(m_day_night_differs)
1246                         flags |= 2;
1247                 os.write((char*)&flags, 1);
1248
1249                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1250
1251                 /*
1252                         Get data
1253                 */
1254
1255                 SharedBuffer<u8> databuf(nodecount*3);
1256
1257                 // Get contents
1258                 for(u32 i=0; i<nodecount; i++)
1259                 {
1260                         databuf[i] = data[i].d;
1261                 }
1262
1263                 // Get params
1264                 for(u32 i=0; i<nodecount; i++)
1265                 {
1266                         databuf[i+nodecount] = data[i].param;
1267                 }
1268
1269                 // Get pressure
1270                 for(u32 i=0; i<nodecount; i++)
1271                 {
1272                         databuf[i+nodecount*2] = data[i].pressure;
1273                 }
1274
1275                 /*
1276                         Compress data to output stream
1277                 */
1278
1279                 compress(databuf, os, version);
1280         }
1281 }
1282
1283 void MapBlock::deSerialize(std::istream &is, u8 version)
1284 {
1285         if(!ser_ver_supported(version))
1286                 throw VersionMismatchException("ERROR: MapBlock format not supported");
1287
1288         // These have no compression
1289         if(version <= 3 || version == 5 || version == 6)
1290         {
1291                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1292                 char tmp;
1293                 is.read(&tmp, 1);
1294                 if(is.gcount() != 1)
1295                         throw SerializationError
1296                                         ("MapBlock::deSerialize: no enough input data");
1297                 is_underground = tmp;
1298                 for(u32 i=0; i<nodecount; i++)
1299                 {
1300                         s32 len = MapNode::serializedLength(version);
1301                         SharedBuffer<u8> d(len);
1302                         is.read((char*)*d, len);
1303                         if(is.gcount() != len)
1304                                 throw SerializationError
1305                                                 ("MapBlock::deSerialize: no enough input data");
1306                         data[i].deSerialize(*d, version);
1307                 }
1308         }
1309         else if(version <= 10)
1310         {
1311                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1312
1313                 u8 t8;
1314                 is.read((char*)&t8, 1);
1315                 is_underground = t8;
1316
1317                 {
1318                         // Uncompress and set material data
1319                         std::ostringstream os(std::ios_base::binary);
1320                         decompress(is, os, version);
1321                         std::string s = os.str();
1322                         if(s.size() != nodecount)
1323                                 throw SerializationError
1324                                                 ("MapBlock::deSerialize: invalid format");
1325                         for(u32 i=0; i<s.size(); i++)
1326                         {
1327                                 data[i].d = s[i];
1328                         }
1329                 }
1330                 {
1331                         // Uncompress and set param data
1332                         std::ostringstream os(std::ios_base::binary);
1333                         decompress(is, os, version);
1334                         std::string s = os.str();
1335                         if(s.size() != nodecount)
1336                                 throw SerializationError
1337                                                 ("MapBlock::deSerialize: invalid format");
1338                         for(u32 i=0; i<s.size(); i++)
1339                         {
1340                                 data[i].param = s[i];
1341                         }
1342                 }
1343         
1344                 if(version >= 10)
1345                 {
1346                         // Uncompress and set pressure data
1347                         std::ostringstream os(std::ios_base::binary);
1348                         decompress(is, os, version);
1349                         std::string s = os.str();
1350                         if(s.size() != nodecount)
1351                                 throw SerializationError
1352                                                 ("MapBlock::deSerialize: invalid format");
1353                         for(u32 i=0; i<s.size(); i++)
1354                         {
1355                                 data[i].pressure = s[i];
1356                         }
1357                 }
1358         }
1359         // All other versions (newest)
1360         else
1361         {
1362                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1363
1364                 u8 flags;
1365                 is.read((char*)&flags, 1);
1366                 is_underground = (flags & 1) ? true : false;
1367                 m_day_night_differs = (flags & 2) ? true : false;
1368
1369                 // Uncompress data
1370                 std::ostringstream os(std::ios_base::binary);
1371                 decompress(is, os, version);
1372                 std::string s = os.str();
1373                 if(s.size() != nodecount*3)
1374                         throw SerializationError
1375                                         ("MapBlock::deSerialize: invalid format");
1376
1377                 // Set contents
1378                 for(u32 i=0; i<nodecount; i++)
1379                 {
1380                         data[i].d = s[i];
1381                 }
1382                 // Set params
1383                 for(u32 i=0; i<nodecount; i++)
1384                 {
1385                         data[i].param = s[i+nodecount];
1386                 }
1387                 // Set pressure
1388                 for(u32 i=0; i<nodecount; i++)
1389                 {
1390                         data[i].pressure = s[i+nodecount*2];
1391                 }
1392         }
1393 }
1394
1395
1396 //END