3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
28 void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
30 m_daynight_ratio = daynight_ratio;
31 m_blockpos = block->getPos();
33 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
36 There is no harm not copying the TempMods of the neighbors
37 because they are already copied to this block
40 block->copyTempMods(m_temp_mods);
46 // Allocate this block + neighbors
48 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
49 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
52 //TimeTaker timer("copy central block data");
56 block->copyTo(m_vmanip);
59 //TimeTaker timer("copy neighbor block data");
63 Copy neighbors. This is lightning fast.
64 Copying only the borders would be *very* slow.
68 NodeContainer *parentcontainer = block->getParent();
69 // This will only work if the parent is the map
70 assert(parentcontainer->nodeContainerId() == NODECONTAINER_ID_MAP);
71 // OK, we have the map!
72 Map *map = (Map*)parentcontainer;
74 for(u16 i=0; i<6; i++)
76 const v3s16 &dir = g_6dirs[i];
77 v3s16 bp = m_blockpos + dir;
78 MapBlock *b = map->getBlockNoCreateNoEx(bp);
87 Parameters must consist of air and !air.
90 If either of the nodes doesn't exist, light is 0.
93 daynight_ratio: 0...1000
95 n2: getNodeParent(p + face_dir)
96 face_dir: axis oriented unit vector from p to p2
98 returns encoded light value.
100 u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
105 u8 l1 = n.getLightBlend(daynight_ratio);
106 u8 l2 = n2.getLightBlend(daynight_ratio);
112 // Make some nice difference to different sides
114 // This makes light come from a corner
115 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
116 light = diminish_light(diminish_light(light));
117 else if(face_dir.X == -1 || face_dir.Z == -1)
118 light = diminish_light(light);*/
120 // All neighboring faces have different shade (like in minecraft)
121 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
122 light = diminish_light(diminish_light(light));
123 else if(face_dir.Z == 1 || face_dir.Z == -1)
124 light = diminish_light(light);
128 catch(InvalidPositionException &e)
137 vertex_dirs: v3s16[4]
139 void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
142 If looked from outside the node towards the face, the corners are:
148 if(dir == v3s16(0,0,1))
150 // If looking towards z+, this is the face that is behind
151 // the center point, facing towards z+.
152 vertex_dirs[0] = v3s16(-1,-1, 1);
153 vertex_dirs[1] = v3s16( 1,-1, 1);
154 vertex_dirs[2] = v3s16( 1, 1, 1);
155 vertex_dirs[3] = v3s16(-1, 1, 1);
157 else if(dir == v3s16(0,0,-1))
160 vertex_dirs[0] = v3s16( 1,-1,-1);
161 vertex_dirs[1] = v3s16(-1,-1,-1);
162 vertex_dirs[2] = v3s16(-1, 1,-1);
163 vertex_dirs[3] = v3s16( 1, 1,-1);
165 else if(dir == v3s16(1,0,0))
168 vertex_dirs[0] = v3s16( 1,-1, 1);
169 vertex_dirs[1] = v3s16( 1,-1,-1);
170 vertex_dirs[2] = v3s16( 1, 1,-1);
171 vertex_dirs[3] = v3s16( 1, 1, 1);
173 else if(dir == v3s16(-1,0,0))
176 vertex_dirs[0] = v3s16(-1,-1,-1);
177 vertex_dirs[1] = v3s16(-1,-1, 1);
178 vertex_dirs[2] = v3s16(-1, 1, 1);
179 vertex_dirs[3] = v3s16(-1, 1,-1);
181 else if(dir == v3s16(0,1,0))
183 // faces towards Y+ (assume Z- as "down" in texture)
184 vertex_dirs[0] = v3s16( 1, 1,-1);
185 vertex_dirs[1] = v3s16(-1, 1,-1);
186 vertex_dirs[2] = v3s16(-1, 1, 1);
187 vertex_dirs[3] = v3s16( 1, 1, 1);
189 else if(dir == v3s16(0,-1,0))
191 // faces towards Y- (assume Z+ as "down" in texture)
192 vertex_dirs[0] = v3s16( 1,-1, 1);
193 vertex_dirs[1] = v3s16(-1,-1, 1);
194 vertex_dirs[2] = v3s16(-1,-1,-1);
195 vertex_dirs[3] = v3s16( 1,-1,-1);
199 inline video::SColor lightColor(u8 alpha, u8 light)
201 return video::SColor(alpha,light,light,light);
204 void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
205 v3s16 dir, v3f scale, v3f posRelative_f,
206 core::array<FastFace> &dest)
210 // Position is at the center of the cube.
215 v3s16 vertex_dirs[4];
216 getNodeVertexDirs(dir, vertex_dirs);
217 for(u16 i=0; i<4; i++)
220 BS/2*vertex_dirs[i].X,
221 BS/2*vertex_dirs[i].Y,
222 BS/2*vertex_dirs[i].Z
226 for(u16 i=0; i<4; i++)
228 vertex_pos[i].X *= scale.X;
229 vertex_pos[i].Y *= scale.Y;
230 vertex_pos[i].Z *= scale.Z;
231 vertex_pos[i] += pos + posRelative_f;
235 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
236 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
237 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
239 v3f zerovector = v3f(0,0,0);
241 u8 alpha = tile.alpha;
243 if(tile.id == TILE_WATER)
244 alpha = WATER_ALPHA;*/
246 float x0 = tile.texture.pos.X;
247 float y0 = tile.texture.pos.Y;
248 float w = tile.texture.size.X;
249 float h = tile.texture.size.Y;
251 /*video::SColor c = lightColor(alpha, li);
253 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
254 core::vector2d<f32>(x0+w*abs_scale, y0+h));
255 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
256 core::vector2d<f32>(x0, y0+h));
257 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
258 core::vector2d<f32>(x0, y0));
259 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
260 core::vector2d<f32>(x0+w*abs_scale, y0));*/
262 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0),
263 lightColor(alpha, li0),
264 core::vector2d<f32>(x0+w*abs_scale, y0+h));
265 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0),
266 lightColor(alpha, li1),
267 core::vector2d<f32>(x0, y0+h));
268 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0),
269 lightColor(alpha, li2),
270 core::vector2d<f32>(x0, y0));
271 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0),
272 lightColor(alpha, li3),
273 core::vector2d<f32>(x0+w*abs_scale, y0));
277 //f->tile = TILE_STONE;
279 dest.push_back(face);
283 Gets node tile from any place relative to block.
284 Returns TILE_NODE if doesn't exist or should not be drawn.
286 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
287 NodeModMap &temp_mods)
290 spec = mn.getTile(face_dir);
293 Check temporary modifications on this node
295 /*core::map<v3s16, NodeMod>::Node *n;
296 n = m_temp_mods.find(p);
300 struct NodeMod mod = n->getValue();*/
302 if(temp_mods.get(p, &mod))
304 if(mod.type == NODEMOD_CHANGECONTENT)
306 MapNode mn2(mod.param);
307 spec = mn2.getTile(face_dir);
309 if(mod.type == NODEMOD_CRACK)
312 Get texture id, translate it to name, append stuff to
316 // Get original texture name
317 u32 orig_id = spec.texture.id;
318 std::string orig_name = g_texturesource->getTextureName(orig_id);
320 // Create new texture name
321 std::ostringstream os;
322 os<<orig_name<<"^[crack"<<mod.param;
325 u32 new_id = g_texturesource->getTextureId(os.str());
327 /*dstream<<"MapBlock::getNodeTile(): Switching from "
328 <<orig_name<<" to "<<os.str()<<" ("
329 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
331 spec.texture = g_texturesource->getTexture(new_id);
338 u8 getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
341 Check temporary modifications on this node
343 /*core::map<v3s16, NodeMod>::Node *n;
344 n = m_temp_mods.find(p);
348 struct NodeMod mod = n->getValue();*/
350 if(temp_mods.get(p, &mod))
352 if(mod.type == NODEMOD_CHANGECONTENT)
357 if(mod.type == NODEMOD_CRACK)
360 Content doesn't change.
362 face_contents works just like it should, because
363 there should not be faces between differently cracked
366 If a semi-transparent node is cracked in front an
367 another one, it really doesn't matter whether there
368 is a cracked face drawn in between or not.
387 // Calculate lighting at the XYZ- corner of p
388 u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio)
390 u16 ambient_occlusion = 0;
393 for(u32 i=0; i<8; i++)
395 MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
396 if(content_features(n.d).param_type == CPT_LIGHT)
398 light += decode_light(n.getLightBlend(daynight_ratio));
403 if(n.d != CONTENT_IGNORE)
411 light /= light_count;
413 if(ambient_occlusion > 4)
415 ambient_occlusion -= 4;
416 light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
422 // Calculate lighting at the given corner of p
423 u8 getSmoothLight(v3s16 p, v3s16 corner,
424 VoxelManipulator &vmanip, u32 daynight_ratio)
426 if(corner.X == 1) p.X += 1;
427 else assert(corner.X == -1);
428 if(corner.Y == 1) p.Y += 1;
429 else assert(corner.Y == -1);
430 if(corner.Z == 1) p.Z += 1;
431 else assert(corner.Z == -1);
433 return getSmoothLight(p, vmanip, daynight_ratio);
438 v3s16 blockpos_nodes,
442 VoxelManipulator &vmanip,
443 NodeModMap &temp_mods,
444 bool smooth_lighting,
448 v3s16 &face_dir_corrected,
453 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
454 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
455 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
456 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
459 u8 content0 = getNodeContent(p, n0, temp_mods);
460 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
461 u8 mf = face_contents(content0, content1);
475 face_dir_corrected = face_dir;
480 p_corrected = p + face_dir;
481 face_dir_corrected = -face_dir;
484 if(smooth_lighting == false)
486 lights[0] = lights[1] = lights[2] = lights[3] =
487 decode_light(getFaceLight(daynight_ratio, n0, n1, face_dir));
491 v3s16 vertex_dirs[4];
492 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
493 for(u16 i=0; i<4; i++)
495 lights[i] = getSmoothLight(blockpos_nodes + p_corrected,
496 vertex_dirs[i], vmanip, daynight_ratio);
505 translate_dir: unit vector with only one of x, y or z
506 face_dir: unit vector with only one of x, y or z
508 void updateFastFaceRow(
517 core::array<FastFace> &dest,
518 NodeModMap &temp_mods,
519 VoxelManipulator &vmanip,
520 v3s16 blockpos_nodes,
521 bool smooth_lighting)
525 u16 continuous_tiles_count = 0;
529 v3s16 face_dir_corrected;
532 getTileInfo(blockpos_nodes, p, face_dir, daynight_ratio,
533 vmanip, temp_mods, smooth_lighting,
534 makes_face, p_corrected, face_dir_corrected, lights, tile);
536 for(u16 j=0; j<length; j++)
538 // If tiling can be done, this is set to false in the next step
539 bool next_is_different = true;
543 bool next_makes_face;
544 v3s16 next_p_corrected;
545 v3s16 next_face_dir_corrected;
549 // If at last position, there is nothing to compare to and
550 // the face must be drawn anyway
553 p_next = p + translate_dir;
555 getTileInfo(blockpos_nodes, p_next, face_dir, daynight_ratio,
556 vmanip, temp_mods, smooth_lighting,
557 next_makes_face, next_p_corrected,
558 next_face_dir_corrected, next_lights,
561 if(next_makes_face == makes_face
562 && next_p_corrected == p_corrected
563 && next_face_dir_corrected == face_dir_corrected
564 && next_lights[0] == lights[0]
565 && next_lights[1] == lights[1]
566 && next_lights[2] == lights[2]
567 && next_lights[3] == lights[3]
568 && next_tile == tile)
570 next_is_different = false;
574 continuous_tiles_count++;
576 // This is set to true if the texture doesn't allow more tiling
577 bool end_of_texture = false;
579 If there is no texture, it can be tiled infinitely.
580 If tiled==0, it means the texture can be tiled infinitely.
581 Otherwise check tiled agains continuous_tiles_count.
583 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
585 if(tile.texture.tiled <= continuous_tiles_count)
586 end_of_texture = true;
589 // Do this to disable tiling textures
590 //end_of_texture = true; //DEBUG
592 if(next_is_different || end_of_texture)
595 Create a face if there should be one
599 // Floating point conversion of the position vector
600 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
601 // Center point of face (kind of)
602 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
605 if(translate_dir.X != 0)
607 scale.X = continuous_tiles_count;
609 if(translate_dir.Y != 0)
611 scale.Y = continuous_tiles_count;
613 if(translate_dir.Z != 0)
615 scale.Z = continuous_tiles_count;
618 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
619 sp, face_dir_corrected, scale,
620 posRelative_f, dest);
624 v3s16 p_first = p_corrected - (continuous_tiles_count-1)
627 v3s16 p_map_leftmost;
628 v3s16 p_map_rightmost;
629 p_map_leftmost = p_corrected + blockpos_nodes;
630 p_map_rightmost = p_first + blockpos_nodes;
632 /*if(p != p_corrected)
634 if(face_dir == v3s16(0,0,1))
636 v3s16 orig_leftmost = p_map_leftmost;
637 v3s16 orig_rightmost = p_map_leftmost;
638 p_map_leftmost = orig_rightmost;
639 p_map_rightmost = orig_leftmost;
643 if(smooth_lighting == false)
645 li0 = li1 = li2 = li3 = decode_light(light);
649 v3s16 vertex_dirs[4];
650 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
652 li0 = getSmoothLight(p_map_rightmost, vertex_dirs[0],
653 vmanip, daynight_ratio);
654 li1 = getSmoothLight(p_map_leftmost, vertex_dirs[1],
655 vmanip, daynight_ratio);
656 li2 = getSmoothLight(p_map_leftmost, vertex_dirs[2],
657 vmanip, daynight_ratio);
658 li3 = getSmoothLight(p_map_rightmost, vertex_dirs[3],
659 vmanip, daynight_ratio);
661 makeFastFace(tile, li0, li1, li2, li3,
662 sp, face_dir_corrected, scale,
663 posRelative_f, dest);
667 continuous_tiles_count = 0;
669 makes_face = next_makes_face;
670 p_corrected = next_p_corrected;
671 face_dir_corrected = next_face_dir_corrected;
672 lights[0] = next_lights[0];
673 lights[1] = next_lights[1];
674 lights[2] = next_lights[2];
675 lights[3] = next_lights[3];
684 This is used because CMeshBuffer::append() is very slow
688 video::SMaterial material;
689 core::array<u16> indices;
690 core::array<video::S3DVertex> vertices;
697 video::SMaterial material,
698 const video::S3DVertex* const vertices,
700 const u16* const indices,
704 PreMeshBuffer *p = NULL;
705 for(u32 i=0; i<m_prebuffers.size(); i++)
707 PreMeshBuffer &pp = m_prebuffers[i];
708 if(pp.material != material)
718 pp.material = material;
719 m_prebuffers.push_back(pp);
720 p = &m_prebuffers[m_prebuffers.size()-1];
723 u32 vertex_count = p->vertices.size();
724 for(u32 i=0; i<numIndices; i++)
726 u32 j = indices[i] + vertex_count;
729 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
730 // NOTE: Fix is to just add an another MeshBuffer
732 p->indices.push_back(j);
734 for(u32 i=0; i<numVertices; i++)
736 p->vertices.push_back(vertices[i]);
740 void fillMesh(scene::SMesh *mesh)
742 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
743 <<" meshbuffers"<<std::endl;*/
744 for(u32 i=0; i<m_prebuffers.size(); i++)
746 PreMeshBuffer &p = m_prebuffers[i];
748 /*dstream<<"p.vertices.size()="<<p.vertices.size()
749 <<", p.indices.size()="<<p.indices.size()
754 // This is a "Standard MeshBuffer",
755 // it's a typedeffed CMeshBuffer<video::S3DVertex>
756 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
758 buf->Material = p.material;
759 //((scene::SMeshBuffer*)buf)->Material = p.material;
761 //buf->setHardwareMappingHint(scene::EHM_STATIC);
763 mesh->addMeshBuffer(buf);
767 buf->append(p.vertices.pointer(), p.vertices.size(),
768 p.indices.pointer(), p.indices.size());
773 core::array<PreMeshBuffer> m_prebuffers;
776 scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
778 // 4-21ms for MAP_BLOCKSIZE=16
779 // 24-155ms for MAP_BLOCKSIZE=32
780 //TimeTaker timer1("makeMapBlockMesh()");
782 core::array<FastFace> fastfaces_new;
784 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
786 // floating point conversion
787 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
792 bool new_style_water = g_settings.getBool("new_style_water");
793 bool new_style_leaves = g_settings.getBool("new_style_leaves");
794 bool smooth_lighting = g_settings.getBool("smooth_lighting");
796 float node_water_level = 1.0;
798 node_water_level = 0.85;
801 We are including the faces of the trailing edges of the block.
802 This means that when something changes, the caller must
803 also update the meshes of the blocks at the leading edges.
805 NOTE: This is the slowest part of this method.
809 // 4-23ms for MAP_BLOCKSIZE=16
810 //TimeTaker timer2("updateMesh() collect");
813 Go through every y,z and get top(y+) faces in rows of x+
815 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
816 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
817 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
818 v3s16(0,y,z), MAP_BLOCKSIZE,
821 v3s16(0,1,0), //face dir
831 Go through every x,y and get right(x+) faces in rows of z+
833 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
834 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
835 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
836 v3s16(x,y,0), MAP_BLOCKSIZE,
849 Go through every y,z and get back(z+) faces in rows of x+
851 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
852 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
853 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
854 v3s16(0,y,z), MAP_BLOCKSIZE,
871 Convert FastFaces to SMesh
874 MeshCollector collector;
876 if(fastfaces_new.size() > 0)
878 // avg 0ms (100ms spikes when loading textures the first time)
879 //TimeTaker timer2("updateMesh() mesh building");
881 video::SMaterial material;
882 material.setFlag(video::EMF_LIGHTING, false);
883 material.setFlag(video::EMF_BILINEAR_FILTER, false);
884 material.setFlag(video::EMF_FOG_ENABLE, true);
885 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
886 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
888 for(u32 i=0; i<fastfaces_new.size(); i++)
890 FastFace &f = fastfaces_new[i];
892 const u16 indices[] = {0,1,2,2,3,0};
894 video::ITexture *texture = f.tile.texture.atlas;
898 material.setTexture(0, texture);
900 f.tile.applyMaterialOptions(material);
902 collector.append(material, f.vertices, 4, indices, 6);
907 Add special graphics:
913 //TimeTaker timer2("updateMesh() adding special stuff");
915 // Flowing water material
916 video::SMaterial material_water1;
917 material_water1.setFlag(video::EMF_LIGHTING, false);
918 material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
919 material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
920 material_water1.setFlag(video::EMF_FOG_ENABLE, true);
921 material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
922 AtlasPointer pa_water1 = g_texturesource->getTexture(
923 g_texturesource->getTextureId("water.png"));
924 material_water1.setTexture(0, pa_water1.atlas);
926 // New-style leaves material
927 video::SMaterial material_leaves1;
928 material_leaves1.setFlag(video::EMF_LIGHTING, false);
929 //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
930 material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
931 material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
932 material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
933 AtlasPointer pa_leaves1 = g_texturesource->getTexture(
934 g_texturesource->getTextureId("leaves.png"));
935 material_leaves1.setTexture(0, pa_leaves1.atlas);
937 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
938 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
939 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
943 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
948 if(n.d == CONTENT_TORCH)
950 video::SColor c(255,255,255,255);
952 // Wall at X+ of node
953 video::S3DVertex vertices[4] =
955 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
956 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
957 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
958 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
961 v3s16 dir = unpackDir(n.dir);
963 for(s32 i=0; i<4; i++)
965 if(dir == v3s16(1,0,0))
966 vertices[i].Pos.rotateXZBy(0);
967 if(dir == v3s16(-1,0,0))
968 vertices[i].Pos.rotateXZBy(180);
969 if(dir == v3s16(0,0,1))
970 vertices[i].Pos.rotateXZBy(90);
971 if(dir == v3s16(0,0,-1))
972 vertices[i].Pos.rotateXZBy(-90);
973 if(dir == v3s16(0,-1,0))
974 vertices[i].Pos.rotateXZBy(45);
975 if(dir == v3s16(0,1,0))
976 vertices[i].Pos.rotateXZBy(-45);
978 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
982 video::SMaterial material;
983 material.setFlag(video::EMF_LIGHTING, false);
984 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
985 material.setFlag(video::EMF_BILINEAR_FILTER, false);
986 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
987 material.MaterialType
988 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
990 if(dir == v3s16(0,-1,0))
991 material.setTexture(0,
992 g_texturesource->getTextureRaw("torch_on_floor.png"));
993 else if(dir == v3s16(0,1,0))
994 material.setTexture(0,
995 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
996 // For backwards compatibility
997 else if(dir == v3s16(0,0,0))
998 material.setTexture(0,
999 g_texturesource->getTextureRaw("torch_on_floor.png"));
1001 material.setTexture(0,
1002 g_texturesource->getTextureRaw("torch.png"));
1004 u16 indices[] = {0,1,2,2,3,0};
1005 // Add to mesh collector
1006 collector.append(material, vertices, 4, indices, 6);
1011 if(n.d == CONTENT_SIGN_WALL)
1013 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1014 video::SColor c(255,l,l,l);
1016 float d = (float)BS/16;
1017 // Wall at X+ of node
1018 video::S3DVertex vertices[4] =
1020 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
1021 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
1022 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
1023 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
1026 v3s16 dir = unpackDir(n.dir);
1028 for(s32 i=0; i<4; i++)
1030 if(dir == v3s16(1,0,0))
1031 vertices[i].Pos.rotateXZBy(0);
1032 if(dir == v3s16(-1,0,0))
1033 vertices[i].Pos.rotateXZBy(180);
1034 if(dir == v3s16(0,0,1))
1035 vertices[i].Pos.rotateXZBy(90);
1036 if(dir == v3s16(0,0,-1))
1037 vertices[i].Pos.rotateXZBy(-90);
1038 if(dir == v3s16(0,-1,0))
1039 vertices[i].Pos.rotateXYBy(-90);
1040 if(dir == v3s16(0,1,0))
1041 vertices[i].Pos.rotateXYBy(90);
1043 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1047 video::SMaterial material;
1048 material.setFlag(video::EMF_LIGHTING, false);
1049 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
1050 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1051 material.setFlag(video::EMF_FOG_ENABLE, true);
1052 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1053 material.MaterialType
1054 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1056 material.setTexture(0,
1057 g_texturesource->getTextureRaw("sign_wall.png"));
1059 u16 indices[] = {0,1,2,2,3,0};
1060 // Add to mesh collector
1061 collector.append(material, vertices, 4, indices, 6);
1064 Add flowing water to mesh
1066 else if(n.d == CONTENT_WATER)
1068 bool top_is_water = false;
1069 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1070 if(ntop.d == CONTENT_WATER || ntop.d == CONTENT_WATERSOURCE)
1071 top_is_water = true;
1074 // Use the light of the node on top if possible
1075 if(content_features(ntop.d).param_type == CPT_LIGHT)
1076 l = decode_light(ntop.getLightBlend(data->m_daynight_ratio));
1077 // Otherwise use the light of this node (the water)
1079 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1080 video::SColor c(WATER_ALPHA,l,l,l);
1082 // Neighbor water levels (key = relative position)
1083 // Includes current node
1084 core::map<v3s16, f32> neighbor_levels;
1085 core::map<v3s16, u8> neighbor_contents;
1086 core::map<v3s16, u8> neighbor_flags;
1087 const u8 neighborflag_top_is_water = 0x01;
1088 v3s16 neighbor_dirs[9] = {
1099 for(u32 i=0; i<9; i++)
1101 u8 content = CONTENT_AIR;
1102 float level = -0.5 * BS;
1105 v3s16 p2 = p + neighbor_dirs[i];
1106 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1107 if(n2.d != CONTENT_IGNORE)
1111 if(n2.d == CONTENT_WATERSOURCE)
1112 level = (-0.5+node_water_level) * BS;
1113 else if(n2.d == CONTENT_WATER)
1114 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
1115 * node_water_level) * BS;
1117 // Check node above neighbor.
1118 // NOTE: This doesn't get executed if neighbor
1121 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1122 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
1123 flags |= neighborflag_top_is_water;
1126 neighbor_levels.insert(neighbor_dirs[i], level);
1127 neighbor_contents.insert(neighbor_dirs[i], content);
1128 neighbor_flags.insert(neighbor_dirs[i], flags);
1131 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
1132 //float water_level = neighbor_levels[v3s16(0,0,0)];
1134 // Corner heights (average between four waters)
1135 f32 corner_levels[4];
1137 v3s16 halfdirs[4] = {
1143 for(u32 i=0; i<4; i++)
1145 v3s16 cornerdir = halfdirs[i];
1146 float cornerlevel = 0;
1147 u32 valid_count = 0;
1148 for(u32 j=0; j<4; j++)
1150 v3s16 neighbordir = cornerdir - halfdirs[j];
1151 u8 content = neighbor_contents[neighbordir];
1152 // Special case for source nodes
1153 if(content == CONTENT_WATERSOURCE)
1155 cornerlevel = (-0.5+node_water_level)*BS;
1159 else if(content == CONTENT_WATER)
1161 cornerlevel += neighbor_levels[neighbordir];
1164 else if(content == CONTENT_AIR)
1166 cornerlevel += -0.5*BS;
1171 cornerlevel /= valid_count;
1172 corner_levels[i] = cornerlevel;
1179 v3s16 side_dirs[4] = {
1185 s16 side_corners[4][2] = {
1191 for(u32 i=0; i<4; i++)
1193 v3s16 dir = side_dirs[i];
1196 If our topside is water and neighbor's topside
1197 is water, don't draw side face
1200 neighbor_flags[dir] & neighborflag_top_is_water)
1203 u8 neighbor_content = neighbor_contents[dir];
1205 // Don't draw face if neighbor is not air or water
1206 if(neighbor_content != CONTENT_AIR
1207 && neighbor_content != CONTENT_WATER)
1210 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1212 // Don't draw any faces if neighbor is water and top is water
1213 if(neighbor_is_water == true && top_is_water == false)
1216 video::S3DVertex vertices[4] =
1218 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1219 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1220 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1221 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1222 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1223 pa_water1.x0(), pa_water1.y1()),
1224 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1225 pa_water1.x1(), pa_water1.y1()),
1226 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1227 pa_water1.x1(), pa_water1.y0()),
1228 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1229 pa_water1.x0(), pa_water1.y0()),
1233 If our topside is water, set upper border of face
1234 at upper border of node
1238 vertices[2].Pos.Y = 0.5*BS;
1239 vertices[3].Pos.Y = 0.5*BS;
1242 Otherwise upper position of face is corner levels
1246 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1247 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1251 If neighbor is water, lower border of face is corner
1254 if(neighbor_is_water)
1256 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1257 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1260 If neighbor is not water, lower border of face is
1261 lower border of node
1265 vertices[0].Pos.Y = -0.5*BS;
1266 vertices[1].Pos.Y = -0.5*BS;
1269 for(s32 j=0; j<4; j++)
1271 if(dir == v3s16(0,0,1))
1272 vertices[j].Pos.rotateXZBy(0);
1273 if(dir == v3s16(0,0,-1))
1274 vertices[j].Pos.rotateXZBy(180);
1275 if(dir == v3s16(-1,0,0))
1276 vertices[j].Pos.rotateXZBy(90);
1277 if(dir == v3s16(1,0,-0))
1278 vertices[j].Pos.rotateXZBy(-90);
1280 vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
1283 u16 indices[] = {0,1,2,2,3,0};
1284 // Add to mesh collector
1285 collector.append(material_water1, vertices, 4, indices, 6);
1289 Generate top side, if appropriate
1292 if(top_is_water == false)
1294 video::S3DVertex vertices[4] =
1296 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1297 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1298 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1299 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1300 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1301 pa_water1.x0(), pa_water1.y1()),
1302 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1303 pa_water1.x1(), pa_water1.y1()),
1304 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1305 pa_water1.x1(), pa_water1.y0()),
1306 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1307 pa_water1.x0(), pa_water1.y0()),
1310 // This fixes a strange bug
1311 s32 corner_resolve[4] = {3,2,1,0};
1313 for(s32 i=0; i<4; i++)
1315 //vertices[i].Pos.Y += water_level;
1316 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1317 s32 j = corner_resolve[i];
1318 vertices[i].Pos.Y += corner_levels[j];
1319 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1322 u16 indices[] = {0,1,2,2,3,0};
1323 // Add to mesh collector
1324 collector.append(material_water1, vertices, 4, indices, 6);
1328 Add water sources to mesh if using new style
1330 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1332 //bool top_is_water = false;
1333 bool top_is_air = false;
1334 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1335 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1336 top_is_water = true;*/
1337 if(n.d == CONTENT_AIR)
1340 /*if(top_is_water == true)
1342 if(top_is_air == false)
1345 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1346 video::SColor c(WATER_ALPHA,l,l,l);
1348 video::S3DVertex vertices[4] =
1350 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1351 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1352 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1353 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1354 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1355 pa_water1.x0(), pa_water1.y1()),
1356 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1357 pa_water1.x1(), pa_water1.y1()),
1358 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1359 pa_water1.x1(), pa_water1.y0()),
1360 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1361 pa_water1.x0(), pa_water1.y0()),
1364 for(s32 i=0; i<4; i++)
1366 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1367 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1370 u16 indices[] = {0,1,2,2,3,0};
1371 // Add to mesh collector
1372 collector.append(material_water1, vertices, 4, indices, 6);
1375 Add leaves if using new style
1377 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1379 /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
1380 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1381 video::SColor c(255,l,l,l);
1383 for(u32 j=0; j<6; j++)
1385 video::S3DVertex vertices[4] =
1387 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1388 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1389 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1390 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1391 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1392 pa_leaves1.x0(), pa_leaves1.y1()),
1393 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1394 pa_leaves1.x1(), pa_leaves1.y1()),
1395 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1396 pa_leaves1.x1(), pa_leaves1.y0()),
1397 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1398 pa_leaves1.x0(), pa_leaves1.y0()),
1403 for(u16 i=0; i<4; i++)
1404 vertices[i].Pos.rotateXZBy(0);
1408 for(u16 i=0; i<4; i++)
1409 vertices[i].Pos.rotateXZBy(180);
1413 for(u16 i=0; i<4; i++)
1414 vertices[i].Pos.rotateXZBy(-90);
1418 for(u16 i=0; i<4; i++)
1419 vertices[i].Pos.rotateXZBy(90);
1423 for(u16 i=0; i<4; i++)
1424 vertices[i].Pos.rotateYZBy(-90);
1428 for(u16 i=0; i<4; i++)
1429 vertices[i].Pos.rotateYZBy(90);
1432 for(u16 i=0; i<4; i++)
1434 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1437 u16 indices[] = {0,1,2,2,3,0};
1438 // Add to mesh collector
1439 collector.append(material_leaves1, vertices, 4, indices, 6);
1445 Add stuff from collector to mesh
1448 scene::SMesh *mesh_new = NULL;
1449 mesh_new = new scene::SMesh();
1451 collector.fillMesh(mesh_new);
1454 Do some stuff to the mesh
1457 mesh_new->recalculateBoundingBox();
1460 Delete new mesh if it is empty
1463 if(mesh_new->getMeshBufferCount() == 0)
1472 // Usually 1-700 faces and 1-7 materials
1473 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1474 <<"and uses "<<mesh_new->getMeshBufferCount()
1475 <<" materials (meshbuffers)"<<std::endl;
1478 // Use VBO for mesh (this just would set this for ever buffer)
1479 // This will lead to infinite memory usage because or irrlicht.
1480 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1483 NOTE: If that is enabled, some kind of a queue to the main
1484 thread should be made which would call irrlicht to delete
1485 the hardware buffer and then delete the mesh
1491 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1500 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
1504 is_underground(false),
1505 m_lighting_expired(true),
1506 m_day_night_differs(false),
1507 //m_not_fully_generated(false),
1514 //m_spawn_timer = -10000;
1517 m_mesh_expired = false;
1520 m_temp_mods_mutex.Init();
1524 MapBlock::~MapBlock()
1528 JMutexAutoLock lock(mesh_mutex);
1542 bool MapBlock::isValidPositionParent(v3s16 p)
1544 if(isValidPosition(p))
1549 return m_parent->isValidPosition(getPosRelative() + p);
1553 MapNode MapBlock::getNodeParent(v3s16 p)
1555 if(isValidPosition(p) == false)
1557 return m_parent->getNode(getPosRelative() + p);
1562 throw InvalidPositionException();
1563 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1567 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
1569 if(isValidPosition(p) == false)
1571 m_parent->setNode(getPosRelative() + p, n);
1576 throw InvalidPositionException();
1577 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
1581 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
1583 if(isValidPosition(p) == false)
1586 return m_parent->getNode(getPosRelative() + p);
1588 catch(InvalidPositionException &e)
1590 return MapNode(CONTENT_IGNORE);
1597 return MapNode(CONTENT_IGNORE);
1599 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1606 void MapBlock::updateMesh(u32 daynight_ratio)
1610 DEBUG: If mesh has been generated, don't generate it again
1613 JMutexAutoLock meshlock(mesh_mutex);
1620 data.fill(daynight_ratio, this);
1622 scene::SMesh *mesh_new = makeMapBlockMesh(&data);
1628 replaceMesh(mesh_new);
1633 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
1637 //scene::SMesh *mesh_old = mesh[daynight_i];
1638 //mesh[daynight_i] = mesh_new;
1640 scene::SMesh *mesh_old = mesh;
1642 setMeshExpired(false);
1644 if(mesh_old != NULL)
1646 // Remove hardware buffers of meshbuffers of mesh
1647 // NOTE: No way, this runs in a different thread and everything
1648 /*u32 c = mesh_old->getMeshBufferCount();
1649 for(u32 i=0; i<c; i++)
1651 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1654 /*dstream<<"mesh_old->getReferenceCount()="
1655 <<mesh_old->getReferenceCount()<<std::endl;
1656 u32 c = mesh_old->getMeshBufferCount();
1657 for(u32 i=0; i<c; i++)
1659 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1660 dstream<<"buf->getReferenceCount()="
1661 <<buf->getReferenceCount()<<std::endl;
1670 mesh_mutex.Unlock();
1676 Propagates sunlight down through the block.
1677 Doesn't modify nodes that are not affected by sunlight.
1679 Returns false if sunlight at bottom block is invalid
1680 Returns true if bottom block doesn't exist.
1682 If there is a block above, continues from it.
1683 If there is no block above, assumes there is sunlight, unless
1684 is_underground is set or highest node is water.
1686 At the moment, all sunlighted nodes are added to light_sources.
1687 - SUGG: This could be optimized
1689 Turns sunglighted mud into grass.
1691 if remove_light==true, sets non-sunlighted nodes black.
1693 if black_air_left!=NULL, it is set to true if non-sunlighted
1694 air is left in block.
1696 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1697 bool remove_light, bool *black_air_left,
1700 // Whether the sunlight at the top of the bottom block is valid
1701 bool block_below_is_valid = true;
1703 v3s16 pos_relative = getPosRelative();
1705 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1707 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1710 bool no_sunlight = false;
1711 bool no_top_block = false;
1712 // Check if node above block has sunlight
1714 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1715 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1720 catch(InvalidPositionException &e)
1722 no_top_block = true;
1724 // NOTE: This makes over-ground roofed places sunlighted
1725 // Assume sunlight, unless is_underground==true
1732 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1733 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1738 // NOTE: As of now, this just would make everything dark.
1740 //no_sunlight = true;
1743 #if 0 // Doesn't work; nothing gets light.
1744 bool no_sunlight = true;
1745 bool no_top_block = false;
1746 // Check if node above block has sunlight
1748 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1749 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1751 no_sunlight = false;
1754 catch(InvalidPositionException &e)
1756 no_top_block = true;
1760 /*std::cout<<"("<<x<<","<<z<<"): "
1761 <<"no_top_block="<<no_top_block
1762 <<", is_underground="<<is_underground
1763 <<", no_sunlight="<<no_sunlight
1766 s16 y = MAP_BLOCKSIZE-1;
1768 // This makes difference to diminishing in water.
1769 bool stopped_to_solid_object = false;
1771 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1776 MapNode &n = getNodeRef(pos);
1778 if(current_light == 0)
1782 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1784 // Do nothing: Sunlight is continued
1786 else if(n.light_propagates() == false)
1790 bool upper_is_air = false;
1793 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1794 upper_is_air = true;
1796 catch(InvalidPositionException &e)
1799 // Turn mud into grass
1800 if(upper_is_air && n.d == CONTENT_MUD
1801 && current_light == LIGHT_SUN)
1803 n.d = CONTENT_GRASS;
1807 // A solid object is on the way.
1808 stopped_to_solid_object = true;
1816 current_light = diminish_light(current_light);
1819 u8 old_light = n.getLight(LIGHTBANK_DAY);
1821 if(current_light > old_light || remove_light)
1823 n.setLight(LIGHTBANK_DAY, current_light);
1826 if(diminish_light(current_light) != 0)
1828 light_sources.insert(pos_relative + pos, true);
1831 if(current_light == 0 && stopped_to_solid_object)
1835 *black_air_left = true;
1840 // Whether or not the block below should see LIGHT_SUN
1841 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1844 If the block below hasn't already been marked invalid:
1846 Check if the node below the block has proper sunlight at top.
1847 If not, the block below is invalid.
1849 Ignore non-transparent nodes as they always have no light
1853 if(block_below_is_valid)
1855 MapNode n = getNodeParent(v3s16(x, -1, z));
1856 if(n.light_propagates())
1858 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1859 && sunlight_should_go_down == false)
1860 block_below_is_valid = false;
1861 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1862 && sunlight_should_go_down == true)
1863 block_below_is_valid = false;
1867 catch(InvalidPositionException &e)
1869 /*std::cout<<"InvalidBlockException for bottom block node"
1871 // Just no block below, no need to panic.
1876 return block_below_is_valid;
1880 void MapBlock::copyTo(VoxelManipulator &dst)
1882 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1883 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1885 // Copy from data to VoxelManipulator
1886 dst.copyFrom(data, data_area, v3s16(0,0,0),
1887 getPosRelative(), data_size);
1890 void MapBlock::copyFrom(VoxelManipulator &dst)
1892 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1893 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1895 // Copy from VoxelManipulator to data
1896 dst.copyTo(data, data_area, v3s16(0,0,0),
1897 getPosRelative(), data_size);
1900 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1905 m_objects.step(dtime, server, daynight_ratio);
1909 Spawn some objects at random.
1911 Use dayNightDiffed() to approximate being near ground level
1913 if(m_spawn_timer < -999)
1917 if(dayNightDiffed() == true && getObjectCount() == 0)
1919 m_spawn_timer -= dtime;
1920 if(m_spawn_timer <= 0.0)
1922 m_spawn_timer += myrand() % 300;
1925 (myrand()%(MAP_BLOCKSIZE-1))+0,
1926 (myrand()%(MAP_BLOCKSIZE-1))+0
1929 s16 y = getGroundLevel(p2d);
1933 v3s16 p(p2d.X, y+1, p2d.Y);
1935 if(getNode(p).d == CONTENT_AIR
1936 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1938 RatObject *obj = new RatObject(NULL, -1, intToFloat(p, BS));
1950 void MapBlock::updateDayNightDiff()
1954 m_day_night_differs = false;
1958 bool differs = false;
1961 Check if any lighting value differs
1963 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1965 MapNode &n = data[i];
1966 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1974 If some lighting values differ, check if the whole thing is
1975 just air. If it is, differ = false
1979 bool only_air = true;
1980 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1982 MapNode &n = data[i];
1983 if(n.d != CONTENT_AIR)
1993 // Set member variable
1994 m_day_night_differs = differs;
1997 s16 MapBlock::getGroundLevel(v2s16 p2d)
2003 s16 y = MAP_BLOCKSIZE-1;
2006 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
2007 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
2009 if(y == MAP_BLOCKSIZE-1)
2017 catch(InvalidPositionException &e)
2027 void MapBlock::serialize(std::ostream &os, u8 version)
2029 if(!ser_ver_supported(version))
2030 throw VersionMismatchException("ERROR: MapBlock format not supported");
2034 throw SerializationError("ERROR: Not writing dummy block.");
2037 // These have no compression
2038 if(version <= 3 || version == 5 || version == 6)
2040 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2042 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
2043 SharedBuffer<u8> dest(buflen);
2045 dest[0] = is_underground;
2046 for(u32 i=0; i<nodecount; i++)
2048 u32 s = 1 + i * MapNode::serializedLength(version);
2049 data[i].serialize(&dest[s], version);
2052 os.write((char*)*dest, dest.getSize());
2054 else if(version <= 10)
2058 Compress the materials and the params separately.
2062 os.write((char*)&is_underground, 1);
2064 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2066 // Get and compress materials
2067 SharedBuffer<u8> materialdata(nodecount);
2068 for(u32 i=0; i<nodecount; i++)
2070 materialdata[i] = data[i].d;
2072 compress(materialdata, os, version);
2074 // Get and compress lights
2075 SharedBuffer<u8> lightdata(nodecount);
2076 for(u32 i=0; i<nodecount; i++)
2078 lightdata[i] = data[i].param;
2080 compress(lightdata, os, version);
2084 // Get and compress param2
2085 SharedBuffer<u8> param2data(nodecount);
2086 for(u32 i=0; i<nodecount; i++)
2088 param2data[i] = data[i].param2;
2090 compress(param2data, os, version);
2093 // All other versions (newest)
2100 if(m_day_night_differs)
2102 if(m_lighting_expired)
2104 os.write((char*)&flags, 1);
2106 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2112 SharedBuffer<u8> databuf(nodecount*3);
2115 for(u32 i=0; i<nodecount; i++)
2117 databuf[i] = data[i].d;
2121 for(u32 i=0; i<nodecount; i++)
2123 databuf[i+nodecount] = data[i].param;
2127 for(u32 i=0; i<nodecount; i++)
2129 databuf[i+nodecount*2] = data[i].param2;
2133 Compress data to output stream
2136 compress(databuf, os, version);
2145 std::ostringstream oss(std::ios_base::binary);
2146 m_node_metadata.serialize(oss);
2147 os<<serializeString(oss.str());
2151 std::ostringstream oss(std::ios_base::binary);
2152 m_node_metadata.serialize(oss);
2153 compressZlib(oss.str(), os);
2154 //os<<serializeLongString(oss.str());
2160 void MapBlock::deSerialize(std::istream &is, u8 version)
2162 if(!ser_ver_supported(version))
2163 throw VersionMismatchException("ERROR: MapBlock format not supported");
2165 // These have no lighting info
2168 setLightingExpired(true);
2171 // These have no compression
2172 if(version <= 3 || version == 5 || version == 6)
2174 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2177 if(is.gcount() != 1)
2178 throw SerializationError
2179 ("MapBlock::deSerialize: no enough input data");
2180 is_underground = tmp;
2181 for(u32 i=0; i<nodecount; i++)
2183 s32 len = MapNode::serializedLength(version);
2184 SharedBuffer<u8> d(len);
2185 is.read((char*)*d, len);
2186 if(is.gcount() != len)
2187 throw SerializationError
2188 ("MapBlock::deSerialize: no enough input data");
2189 data[i].deSerialize(*d, version);
2192 else if(version <= 10)
2194 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2197 is.read((char*)&t8, 1);
2198 is_underground = t8;
2201 // Uncompress and set material data
2202 std::ostringstream os(std::ios_base::binary);
2203 decompress(is, os, version);
2204 std::string s = os.str();
2205 if(s.size() != nodecount)
2206 throw SerializationError
2207 ("MapBlock::deSerialize: invalid format");
2208 for(u32 i=0; i<s.size(); i++)
2214 // Uncompress and set param data
2215 std::ostringstream os(std::ios_base::binary);
2216 decompress(is, os, version);
2217 std::string s = os.str();
2218 if(s.size() != nodecount)
2219 throw SerializationError
2220 ("MapBlock::deSerialize: invalid format");
2221 for(u32 i=0; i<s.size(); i++)
2223 data[i].param = s[i];
2229 // Uncompress and set param2 data
2230 std::ostringstream os(std::ios_base::binary);
2231 decompress(is, os, version);
2232 std::string s = os.str();
2233 if(s.size() != nodecount)
2234 throw SerializationError
2235 ("MapBlock::deSerialize: invalid format");
2236 for(u32 i=0; i<s.size(); i++)
2238 data[i].param2 = s[i];
2242 // All other versions (newest)
2245 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2248 is.read((char*)&flags, 1);
2249 is_underground = (flags & 0x01) ? true : false;
2250 m_day_night_differs = (flags & 0x02) ? true : false;
2251 m_lighting_expired = (flags & 0x04) ? true : false;
2254 std::ostringstream os(std::ios_base::binary);
2255 decompress(is, os, version);
2256 std::string s = os.str();
2257 if(s.size() != nodecount*3)
2258 throw SerializationError
2259 ("MapBlock::deSerialize: invalid format");
2262 for(u32 i=0; i<nodecount; i++)
2267 for(u32 i=0; i<nodecount; i++)
2269 data[i].param = s[i+nodecount];
2272 for(u32 i=0; i<nodecount; i++)
2274 data[i].param2 = s[i+nodecount*2];
2286 std::string data = deSerializeString(is);
2287 std::istringstream iss(data, std::ios_base::binary);
2288 m_node_metadata.deSerialize(iss);
2292 //std::string data = deSerializeLongString(is);
2293 std::ostringstream oss(std::ios_base::binary);
2294 decompressZlib(is, oss);
2295 std::istringstream iss(oss.str(), std::ios_base::binary);
2296 m_node_metadata.deSerialize(iss);
2299 catch(SerializationError &e)
2301 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
2302 <<" while deserializing node metadata"<<std::endl;
2308 Translate nodes as specified in the translate_to fields of
2311 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2313 MapNode &n = data[i];
2315 MapNode *translate_to = content_features(n.d).translate_to;
2318 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2319 <<translate_to->d<<std::endl;