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 // Disable tiling of textures if smooth lighting is used
594 end_of_texture = true;
596 if(next_is_different || end_of_texture)
599 Create a face if there should be one
603 // Floating point conversion of the position vector
604 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
605 // Center point of face (kind of)
606 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
609 if(translate_dir.X != 0)
611 scale.X = continuous_tiles_count;
613 if(translate_dir.Y != 0)
615 scale.Y = continuous_tiles_count;
617 if(translate_dir.Z != 0)
619 scale.Z = continuous_tiles_count;
622 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
623 sp, face_dir_corrected, scale,
624 posRelative_f, dest);
628 v3s16 p_first = p_corrected - (continuous_tiles_count-1)
631 v3s16 p_map_leftmost;
632 v3s16 p_map_rightmost;
633 p_map_leftmost = p_corrected + blockpos_nodes;
634 p_map_rightmost = p_first + blockpos_nodes;
636 /*if(p != p_corrected)
638 if(face_dir == v3s16(0,0,1))
640 v3s16 orig_leftmost = p_map_leftmost;
641 v3s16 orig_rightmost = p_map_leftmost;
642 p_map_leftmost = orig_rightmost;
643 p_map_rightmost = orig_leftmost;
647 if(smooth_lighting == false)
649 li0 = li1 = li2 = li3 = decode_light(light);
653 v3s16 vertex_dirs[4];
654 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
656 li0 = getSmoothLight(p_map_rightmost, vertex_dirs[0],
657 vmanip, daynight_ratio);
658 li1 = getSmoothLight(p_map_leftmost, vertex_dirs[1],
659 vmanip, daynight_ratio);
660 li2 = getSmoothLight(p_map_leftmost, vertex_dirs[2],
661 vmanip, daynight_ratio);
662 li3 = getSmoothLight(p_map_rightmost, vertex_dirs[3],
663 vmanip, daynight_ratio);
665 makeFastFace(tile, li0, li1, li2, li3,
666 sp, face_dir_corrected, scale,
667 posRelative_f, dest);
671 continuous_tiles_count = 0;
673 makes_face = next_makes_face;
674 p_corrected = next_p_corrected;
675 face_dir_corrected = next_face_dir_corrected;
676 lights[0] = next_lights[0];
677 lights[1] = next_lights[1];
678 lights[2] = next_lights[2];
679 lights[3] = next_lights[3];
688 This is used because CMeshBuffer::append() is very slow
692 video::SMaterial material;
693 core::array<u16> indices;
694 core::array<video::S3DVertex> vertices;
701 video::SMaterial material,
702 const video::S3DVertex* const vertices,
704 const u16* const indices,
708 PreMeshBuffer *p = NULL;
709 for(u32 i=0; i<m_prebuffers.size(); i++)
711 PreMeshBuffer &pp = m_prebuffers[i];
712 if(pp.material != material)
722 pp.material = material;
723 m_prebuffers.push_back(pp);
724 p = &m_prebuffers[m_prebuffers.size()-1];
727 u32 vertex_count = p->vertices.size();
728 for(u32 i=0; i<numIndices; i++)
730 u32 j = indices[i] + vertex_count;
733 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
734 // NOTE: Fix is to just add an another MeshBuffer
736 p->indices.push_back(j);
738 for(u32 i=0; i<numVertices; i++)
740 p->vertices.push_back(vertices[i]);
744 void fillMesh(scene::SMesh *mesh)
746 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
747 <<" meshbuffers"<<std::endl;*/
748 for(u32 i=0; i<m_prebuffers.size(); i++)
750 PreMeshBuffer &p = m_prebuffers[i];
752 /*dstream<<"p.vertices.size()="<<p.vertices.size()
753 <<", p.indices.size()="<<p.indices.size()
758 // This is a "Standard MeshBuffer",
759 // it's a typedeffed CMeshBuffer<video::S3DVertex>
760 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
762 buf->Material = p.material;
763 //((scene::SMeshBuffer*)buf)->Material = p.material;
765 //buf->setHardwareMappingHint(scene::EHM_STATIC);
767 mesh->addMeshBuffer(buf);
771 buf->append(p.vertices.pointer(), p.vertices.size(),
772 p.indices.pointer(), p.indices.size());
777 core::array<PreMeshBuffer> m_prebuffers;
780 scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
782 // 4-21ms for MAP_BLOCKSIZE=16
783 // 24-155ms for MAP_BLOCKSIZE=32
784 //TimeTaker timer1("makeMapBlockMesh()");
786 core::array<FastFace> fastfaces_new;
788 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
790 // floating point conversion
791 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
796 bool new_style_water = g_settings.getBool("new_style_water");
797 bool new_style_leaves = g_settings.getBool("new_style_leaves");
798 bool smooth_lighting = g_settings.getBool("smooth_lighting");
800 float node_water_level = 1.0;
802 node_water_level = 0.85;
805 We are including the faces of the trailing edges of the block.
806 This means that when something changes, the caller must
807 also update the meshes of the blocks at the leading edges.
809 NOTE: This is the slowest part of this method.
813 // 4-23ms for MAP_BLOCKSIZE=16
814 //TimeTaker timer2("updateMesh() collect");
817 Go through every y,z and get top(y+) faces in rows of x+
819 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
820 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
821 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
822 v3s16(0,y,z), MAP_BLOCKSIZE,
825 v3s16(0,1,0), //face dir
835 Go through every x,y and get right(x+) faces in rows of z+
837 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
838 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
839 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
840 v3s16(x,y,0), MAP_BLOCKSIZE,
853 Go through every y,z and get back(z+) faces in rows of x+
855 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
856 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
857 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
858 v3s16(0,y,z), MAP_BLOCKSIZE,
875 Convert FastFaces to SMesh
878 MeshCollector collector;
880 if(fastfaces_new.size() > 0)
882 // avg 0ms (100ms spikes when loading textures the first time)
883 //TimeTaker timer2("updateMesh() mesh building");
885 video::SMaterial material;
886 material.setFlag(video::EMF_LIGHTING, false);
887 material.setFlag(video::EMF_BILINEAR_FILTER, false);
888 material.setFlag(video::EMF_FOG_ENABLE, true);
889 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
890 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
892 for(u32 i=0; i<fastfaces_new.size(); i++)
894 FastFace &f = fastfaces_new[i];
896 const u16 indices[] = {0,1,2,2,3,0};
898 video::ITexture *texture = f.tile.texture.atlas;
902 material.setTexture(0, texture);
904 f.tile.applyMaterialOptions(material);
906 collector.append(material, f.vertices, 4, indices, 6);
911 Add special graphics:
917 //TimeTaker timer2("updateMesh() adding special stuff");
919 // Flowing water material
920 video::SMaterial material_water1;
921 material_water1.setFlag(video::EMF_LIGHTING, false);
922 material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
923 material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
924 material_water1.setFlag(video::EMF_FOG_ENABLE, true);
925 material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
926 AtlasPointer pa_water1 = g_texturesource->getTexture(
927 g_texturesource->getTextureId("water.png"));
928 material_water1.setTexture(0, pa_water1.atlas);
930 // New-style leaves material
931 video::SMaterial material_leaves1;
932 material_leaves1.setFlag(video::EMF_LIGHTING, false);
933 //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
934 material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
935 material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
936 material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
937 AtlasPointer pa_leaves1 = g_texturesource->getTexture(
938 g_texturesource->getTextureId("leaves.png"));
939 material_leaves1.setTexture(0, pa_leaves1.atlas);
941 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
942 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
943 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
947 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
952 if(n.d == CONTENT_TORCH)
954 video::SColor c(255,255,255,255);
956 // Wall at X+ of node
957 video::S3DVertex vertices[4] =
959 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
960 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
961 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
962 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
965 v3s16 dir = unpackDir(n.dir);
967 for(s32 i=0; i<4; i++)
969 if(dir == v3s16(1,0,0))
970 vertices[i].Pos.rotateXZBy(0);
971 if(dir == v3s16(-1,0,0))
972 vertices[i].Pos.rotateXZBy(180);
973 if(dir == v3s16(0,0,1))
974 vertices[i].Pos.rotateXZBy(90);
975 if(dir == v3s16(0,0,-1))
976 vertices[i].Pos.rotateXZBy(-90);
977 if(dir == v3s16(0,-1,0))
978 vertices[i].Pos.rotateXZBy(45);
979 if(dir == v3s16(0,1,0))
980 vertices[i].Pos.rotateXZBy(-45);
982 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
986 video::SMaterial material;
987 material.setFlag(video::EMF_LIGHTING, false);
988 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
989 material.setFlag(video::EMF_BILINEAR_FILTER, false);
990 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
991 material.MaterialType
992 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
994 if(dir == v3s16(0,-1,0))
995 material.setTexture(0,
996 g_texturesource->getTextureRaw("torch_on_floor.png"));
997 else if(dir == v3s16(0,1,0))
998 material.setTexture(0,
999 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
1000 // For backwards compatibility
1001 else if(dir == v3s16(0,0,0))
1002 material.setTexture(0,
1003 g_texturesource->getTextureRaw("torch_on_floor.png"));
1005 material.setTexture(0,
1006 g_texturesource->getTextureRaw("torch.png"));
1008 u16 indices[] = {0,1,2,2,3,0};
1009 // Add to mesh collector
1010 collector.append(material, vertices, 4, indices, 6);
1015 if(n.d == CONTENT_SIGN_WALL)
1017 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1018 video::SColor c(255,l,l,l);
1020 float d = (float)BS/16;
1021 // Wall at X+ of node
1022 video::S3DVertex vertices[4] =
1024 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
1025 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
1026 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
1027 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
1030 v3s16 dir = unpackDir(n.dir);
1032 for(s32 i=0; i<4; i++)
1034 if(dir == v3s16(1,0,0))
1035 vertices[i].Pos.rotateXZBy(0);
1036 if(dir == v3s16(-1,0,0))
1037 vertices[i].Pos.rotateXZBy(180);
1038 if(dir == v3s16(0,0,1))
1039 vertices[i].Pos.rotateXZBy(90);
1040 if(dir == v3s16(0,0,-1))
1041 vertices[i].Pos.rotateXZBy(-90);
1042 if(dir == v3s16(0,-1,0))
1043 vertices[i].Pos.rotateXYBy(-90);
1044 if(dir == v3s16(0,1,0))
1045 vertices[i].Pos.rotateXYBy(90);
1047 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1051 video::SMaterial material;
1052 material.setFlag(video::EMF_LIGHTING, false);
1053 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
1054 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1055 material.setFlag(video::EMF_FOG_ENABLE, true);
1056 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1057 material.MaterialType
1058 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1060 material.setTexture(0,
1061 g_texturesource->getTextureRaw("sign_wall.png"));
1063 u16 indices[] = {0,1,2,2,3,0};
1064 // Add to mesh collector
1065 collector.append(material, vertices, 4, indices, 6);
1068 Add flowing water to mesh
1070 else if(n.d == CONTENT_WATER)
1072 bool top_is_water = false;
1073 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1074 if(ntop.d == CONTENT_WATER || ntop.d == CONTENT_WATERSOURCE)
1075 top_is_water = true;
1078 // Use the light of the node on top if possible
1079 if(content_features(ntop.d).param_type == CPT_LIGHT)
1080 l = decode_light(ntop.getLightBlend(data->m_daynight_ratio));
1081 // Otherwise use the light of this node (the water)
1083 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1084 video::SColor c(WATER_ALPHA,l,l,l);
1086 // Neighbor water levels (key = relative position)
1087 // Includes current node
1088 core::map<v3s16, f32> neighbor_levels;
1089 core::map<v3s16, u8> neighbor_contents;
1090 core::map<v3s16, u8> neighbor_flags;
1091 const u8 neighborflag_top_is_water = 0x01;
1092 v3s16 neighbor_dirs[9] = {
1103 for(u32 i=0; i<9; i++)
1105 u8 content = CONTENT_AIR;
1106 float level = -0.5 * BS;
1109 v3s16 p2 = p + neighbor_dirs[i];
1110 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1111 if(n2.d != CONTENT_IGNORE)
1115 if(n2.d == CONTENT_WATERSOURCE)
1116 level = (-0.5+node_water_level) * BS;
1117 else if(n2.d == CONTENT_WATER)
1118 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
1119 * node_water_level) * BS;
1121 // Check node above neighbor.
1122 // NOTE: This doesn't get executed if neighbor
1125 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1126 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
1127 flags |= neighborflag_top_is_water;
1130 neighbor_levels.insert(neighbor_dirs[i], level);
1131 neighbor_contents.insert(neighbor_dirs[i], content);
1132 neighbor_flags.insert(neighbor_dirs[i], flags);
1135 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
1136 //float water_level = neighbor_levels[v3s16(0,0,0)];
1138 // Corner heights (average between four waters)
1139 f32 corner_levels[4];
1141 v3s16 halfdirs[4] = {
1147 for(u32 i=0; i<4; i++)
1149 v3s16 cornerdir = halfdirs[i];
1150 float cornerlevel = 0;
1151 u32 valid_count = 0;
1152 for(u32 j=0; j<4; j++)
1154 v3s16 neighbordir = cornerdir - halfdirs[j];
1155 u8 content = neighbor_contents[neighbordir];
1156 // Special case for source nodes
1157 if(content == CONTENT_WATERSOURCE)
1159 cornerlevel = (-0.5+node_water_level)*BS;
1163 else if(content == CONTENT_WATER)
1165 cornerlevel += neighbor_levels[neighbordir];
1168 else if(content == CONTENT_AIR)
1170 cornerlevel += -0.5*BS;
1175 cornerlevel /= valid_count;
1176 corner_levels[i] = cornerlevel;
1183 v3s16 side_dirs[4] = {
1189 s16 side_corners[4][2] = {
1195 for(u32 i=0; i<4; i++)
1197 v3s16 dir = side_dirs[i];
1200 If our topside is water and neighbor's topside
1201 is water, don't draw side face
1204 neighbor_flags[dir] & neighborflag_top_is_water)
1207 u8 neighbor_content = neighbor_contents[dir];
1209 // Don't draw face if neighbor is not air or water
1210 if(neighbor_content != CONTENT_AIR
1211 && neighbor_content != CONTENT_WATER)
1214 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1216 // Don't draw any faces if neighbor is water and top is water
1217 if(neighbor_is_water == true && top_is_water == false)
1220 video::S3DVertex vertices[4] =
1222 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1223 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1224 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1225 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1226 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1227 pa_water1.x0(), pa_water1.y1()),
1228 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1229 pa_water1.x1(), pa_water1.y1()),
1230 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1231 pa_water1.x1(), pa_water1.y0()),
1232 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1233 pa_water1.x0(), pa_water1.y0()),
1237 If our topside is water, set upper border of face
1238 at upper border of node
1242 vertices[2].Pos.Y = 0.5*BS;
1243 vertices[3].Pos.Y = 0.5*BS;
1246 Otherwise upper position of face is corner levels
1250 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1251 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1255 If neighbor is water, lower border of face is corner
1258 if(neighbor_is_water)
1260 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1261 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1264 If neighbor is not water, lower border of face is
1265 lower border of node
1269 vertices[0].Pos.Y = -0.5*BS;
1270 vertices[1].Pos.Y = -0.5*BS;
1273 for(s32 j=0; j<4; j++)
1275 if(dir == v3s16(0,0,1))
1276 vertices[j].Pos.rotateXZBy(0);
1277 if(dir == v3s16(0,0,-1))
1278 vertices[j].Pos.rotateXZBy(180);
1279 if(dir == v3s16(-1,0,0))
1280 vertices[j].Pos.rotateXZBy(90);
1281 if(dir == v3s16(1,0,-0))
1282 vertices[j].Pos.rotateXZBy(-90);
1284 vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
1287 u16 indices[] = {0,1,2,2,3,0};
1288 // Add to mesh collector
1289 collector.append(material_water1, vertices, 4, indices, 6);
1293 Generate top side, if appropriate
1296 if(top_is_water == false)
1298 video::S3DVertex vertices[4] =
1300 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1301 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1302 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1303 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1304 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1305 pa_water1.x0(), pa_water1.y1()),
1306 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1307 pa_water1.x1(), pa_water1.y1()),
1308 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1309 pa_water1.x1(), pa_water1.y0()),
1310 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1311 pa_water1.x0(), pa_water1.y0()),
1314 // This fixes a strange bug
1315 s32 corner_resolve[4] = {3,2,1,0};
1317 for(s32 i=0; i<4; i++)
1319 //vertices[i].Pos.Y += water_level;
1320 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1321 s32 j = corner_resolve[i];
1322 vertices[i].Pos.Y += corner_levels[j];
1323 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1326 u16 indices[] = {0,1,2,2,3,0};
1327 // Add to mesh collector
1328 collector.append(material_water1, vertices, 4, indices, 6);
1332 Add water sources to mesh if using new style
1334 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1336 //bool top_is_water = false;
1337 bool top_is_air = false;
1338 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1339 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1340 top_is_water = true;*/
1341 if(n.d == CONTENT_AIR)
1344 /*if(top_is_water == true)
1346 if(top_is_air == false)
1349 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1350 video::SColor c(WATER_ALPHA,l,l,l);
1352 video::S3DVertex vertices[4] =
1354 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1355 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1356 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1357 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1358 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1359 pa_water1.x0(), pa_water1.y1()),
1360 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1361 pa_water1.x1(), pa_water1.y1()),
1362 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1363 pa_water1.x1(), pa_water1.y0()),
1364 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1365 pa_water1.x0(), pa_water1.y0()),
1368 for(s32 i=0; i<4; i++)
1370 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1371 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1374 u16 indices[] = {0,1,2,2,3,0};
1375 // Add to mesh collector
1376 collector.append(material_water1, vertices, 4, indices, 6);
1379 Add leaves if using new style
1381 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1383 /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
1384 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1385 video::SColor c(255,l,l,l);
1387 for(u32 j=0; j<6; j++)
1389 video::S3DVertex vertices[4] =
1391 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1392 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1393 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1394 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1395 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1396 pa_leaves1.x0(), pa_leaves1.y1()),
1397 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1398 pa_leaves1.x1(), pa_leaves1.y1()),
1399 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1400 pa_leaves1.x1(), pa_leaves1.y0()),
1401 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1402 pa_leaves1.x0(), pa_leaves1.y0()),
1407 for(u16 i=0; i<4; i++)
1408 vertices[i].Pos.rotateXZBy(0);
1412 for(u16 i=0; i<4; i++)
1413 vertices[i].Pos.rotateXZBy(180);
1417 for(u16 i=0; i<4; i++)
1418 vertices[i].Pos.rotateXZBy(-90);
1422 for(u16 i=0; i<4; i++)
1423 vertices[i].Pos.rotateXZBy(90);
1427 for(u16 i=0; i<4; i++)
1428 vertices[i].Pos.rotateYZBy(-90);
1432 for(u16 i=0; i<4; i++)
1433 vertices[i].Pos.rotateYZBy(90);
1436 for(u16 i=0; i<4; i++)
1438 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1441 u16 indices[] = {0,1,2,2,3,0};
1442 // Add to mesh collector
1443 collector.append(material_leaves1, vertices, 4, indices, 6);
1449 Add stuff from collector to mesh
1452 scene::SMesh *mesh_new = NULL;
1453 mesh_new = new scene::SMesh();
1455 collector.fillMesh(mesh_new);
1458 Do some stuff to the mesh
1461 mesh_new->recalculateBoundingBox();
1464 Delete new mesh if it is empty
1467 if(mesh_new->getMeshBufferCount() == 0)
1476 // Usually 1-700 faces and 1-7 materials
1477 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1478 <<"and uses "<<mesh_new->getMeshBufferCount()
1479 <<" materials (meshbuffers)"<<std::endl;
1482 // Use VBO for mesh (this just would set this for ever buffer)
1483 // This will lead to infinite memory usage because or irrlicht.
1484 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1487 NOTE: If that is enabled, some kind of a queue to the main
1488 thread should be made which would call irrlicht to delete
1489 the hardware buffer and then delete the mesh
1495 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1504 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
1508 is_underground(false),
1509 m_lighting_expired(true),
1510 m_day_night_differs(false),
1511 //m_not_fully_generated(false),
1518 //m_spawn_timer = -10000;
1521 m_mesh_expired = false;
1524 m_temp_mods_mutex.Init();
1528 MapBlock::~MapBlock()
1532 JMutexAutoLock lock(mesh_mutex);
1546 bool MapBlock::isValidPositionParent(v3s16 p)
1548 if(isValidPosition(p))
1553 return m_parent->isValidPosition(getPosRelative() + p);
1557 MapNode MapBlock::getNodeParent(v3s16 p)
1559 if(isValidPosition(p) == false)
1561 return m_parent->getNode(getPosRelative() + p);
1566 throw InvalidPositionException();
1567 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1571 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
1573 if(isValidPosition(p) == false)
1575 m_parent->setNode(getPosRelative() + p, n);
1580 throw InvalidPositionException();
1581 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
1585 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
1587 if(isValidPosition(p) == false)
1590 return m_parent->getNode(getPosRelative() + p);
1592 catch(InvalidPositionException &e)
1594 return MapNode(CONTENT_IGNORE);
1601 return MapNode(CONTENT_IGNORE);
1603 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1610 void MapBlock::updateMesh(u32 daynight_ratio)
1614 DEBUG: If mesh has been generated, don't generate it again
1617 JMutexAutoLock meshlock(mesh_mutex);
1624 data.fill(daynight_ratio, this);
1626 scene::SMesh *mesh_new = makeMapBlockMesh(&data);
1632 replaceMesh(mesh_new);
1637 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
1641 //scene::SMesh *mesh_old = mesh[daynight_i];
1642 //mesh[daynight_i] = mesh_new;
1644 scene::SMesh *mesh_old = mesh;
1646 setMeshExpired(false);
1648 if(mesh_old != NULL)
1650 // Remove hardware buffers of meshbuffers of mesh
1651 // NOTE: No way, this runs in a different thread and everything
1652 /*u32 c = mesh_old->getMeshBufferCount();
1653 for(u32 i=0; i<c; i++)
1655 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1658 /*dstream<<"mesh_old->getReferenceCount()="
1659 <<mesh_old->getReferenceCount()<<std::endl;
1660 u32 c = mesh_old->getMeshBufferCount();
1661 for(u32 i=0; i<c; i++)
1663 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1664 dstream<<"buf->getReferenceCount()="
1665 <<buf->getReferenceCount()<<std::endl;
1674 mesh_mutex.Unlock();
1680 Propagates sunlight down through the block.
1681 Doesn't modify nodes that are not affected by sunlight.
1683 Returns false if sunlight at bottom block is invalid
1684 Returns true if bottom block doesn't exist.
1686 If there is a block above, continues from it.
1687 If there is no block above, assumes there is sunlight, unless
1688 is_underground is set or highest node is water.
1690 At the moment, all sunlighted nodes are added to light_sources.
1691 - SUGG: This could be optimized
1693 Turns sunglighted mud into grass.
1695 if remove_light==true, sets non-sunlighted nodes black.
1697 if black_air_left!=NULL, it is set to true if non-sunlighted
1698 air is left in block.
1700 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1701 bool remove_light, bool *black_air_left,
1704 // Whether the sunlight at the top of the bottom block is valid
1705 bool block_below_is_valid = true;
1707 v3s16 pos_relative = getPosRelative();
1709 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1711 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1714 bool no_sunlight = false;
1715 bool no_top_block = false;
1716 // Check if node above block has sunlight
1718 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1719 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1724 catch(InvalidPositionException &e)
1726 no_top_block = true;
1728 // NOTE: This makes over-ground roofed places sunlighted
1729 // Assume sunlight, unless is_underground==true
1736 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1737 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1742 // NOTE: As of now, this just would make everything dark.
1744 //no_sunlight = true;
1747 #if 0 // Doesn't work; nothing gets light.
1748 bool no_sunlight = true;
1749 bool no_top_block = false;
1750 // Check if node above block has sunlight
1752 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1753 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1755 no_sunlight = false;
1758 catch(InvalidPositionException &e)
1760 no_top_block = true;
1764 /*std::cout<<"("<<x<<","<<z<<"): "
1765 <<"no_top_block="<<no_top_block
1766 <<", is_underground="<<is_underground
1767 <<", no_sunlight="<<no_sunlight
1770 s16 y = MAP_BLOCKSIZE-1;
1772 // This makes difference to diminishing in water.
1773 bool stopped_to_solid_object = false;
1775 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1780 MapNode &n = getNodeRef(pos);
1782 if(current_light == 0)
1786 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1788 // Do nothing: Sunlight is continued
1790 else if(n.light_propagates() == false)
1794 bool upper_is_air = false;
1797 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1798 upper_is_air = true;
1800 catch(InvalidPositionException &e)
1803 // Turn mud into grass
1804 if(upper_is_air && n.d == CONTENT_MUD
1805 && current_light == LIGHT_SUN)
1807 n.d = CONTENT_GRASS;
1811 // A solid object is on the way.
1812 stopped_to_solid_object = true;
1820 current_light = diminish_light(current_light);
1823 u8 old_light = n.getLight(LIGHTBANK_DAY);
1825 if(current_light > old_light || remove_light)
1827 n.setLight(LIGHTBANK_DAY, current_light);
1830 if(diminish_light(current_light) != 0)
1832 light_sources.insert(pos_relative + pos, true);
1835 if(current_light == 0 && stopped_to_solid_object)
1839 *black_air_left = true;
1844 // Whether or not the block below should see LIGHT_SUN
1845 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1848 If the block below hasn't already been marked invalid:
1850 Check if the node below the block has proper sunlight at top.
1851 If not, the block below is invalid.
1853 Ignore non-transparent nodes as they always have no light
1857 if(block_below_is_valid)
1859 MapNode n = getNodeParent(v3s16(x, -1, z));
1860 if(n.light_propagates())
1862 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1863 && sunlight_should_go_down == false)
1864 block_below_is_valid = false;
1865 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1866 && sunlight_should_go_down == true)
1867 block_below_is_valid = false;
1871 catch(InvalidPositionException &e)
1873 /*std::cout<<"InvalidBlockException for bottom block node"
1875 // Just no block below, no need to panic.
1880 return block_below_is_valid;
1884 void MapBlock::copyTo(VoxelManipulator &dst)
1886 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1887 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1889 // Copy from data to VoxelManipulator
1890 dst.copyFrom(data, data_area, v3s16(0,0,0),
1891 getPosRelative(), data_size);
1894 void MapBlock::copyFrom(VoxelManipulator &dst)
1896 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1897 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1899 // Copy from VoxelManipulator to data
1900 dst.copyTo(data, data_area, v3s16(0,0,0),
1901 getPosRelative(), data_size);
1904 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1909 m_objects.step(dtime, server, daynight_ratio);
1913 Spawn some objects at random.
1915 Use dayNightDiffed() to approximate being near ground level
1917 if(m_spawn_timer < -999)
1921 if(dayNightDiffed() == true && getObjectCount() == 0)
1923 m_spawn_timer -= dtime;
1924 if(m_spawn_timer <= 0.0)
1926 m_spawn_timer += myrand() % 300;
1929 (myrand()%(MAP_BLOCKSIZE-1))+0,
1930 (myrand()%(MAP_BLOCKSIZE-1))+0
1933 s16 y = getGroundLevel(p2d);
1937 v3s16 p(p2d.X, y+1, p2d.Y);
1939 if(getNode(p).d == CONTENT_AIR
1940 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1942 RatObject *obj = new RatObject(NULL, -1, intToFloat(p, BS));
1954 void MapBlock::updateDayNightDiff()
1958 m_day_night_differs = false;
1962 bool differs = false;
1965 Check if any lighting value differs
1967 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1969 MapNode &n = data[i];
1970 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1978 If some lighting values differ, check if the whole thing is
1979 just air. If it is, differ = false
1983 bool only_air = true;
1984 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1986 MapNode &n = data[i];
1987 if(n.d != CONTENT_AIR)
1997 // Set member variable
1998 m_day_night_differs = differs;
2001 s16 MapBlock::getGroundLevel(v2s16 p2d)
2007 s16 y = MAP_BLOCKSIZE-1;
2010 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
2011 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
2013 if(y == MAP_BLOCKSIZE-1)
2021 catch(InvalidPositionException &e)
2031 void MapBlock::serialize(std::ostream &os, u8 version)
2033 if(!ser_ver_supported(version))
2034 throw VersionMismatchException("ERROR: MapBlock format not supported");
2038 throw SerializationError("ERROR: Not writing dummy block.");
2041 // These have no compression
2042 if(version <= 3 || version == 5 || version == 6)
2044 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2046 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
2047 SharedBuffer<u8> dest(buflen);
2049 dest[0] = is_underground;
2050 for(u32 i=0; i<nodecount; i++)
2052 u32 s = 1 + i * MapNode::serializedLength(version);
2053 data[i].serialize(&dest[s], version);
2056 os.write((char*)*dest, dest.getSize());
2058 else if(version <= 10)
2062 Compress the materials and the params separately.
2066 os.write((char*)&is_underground, 1);
2068 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2070 // Get and compress materials
2071 SharedBuffer<u8> materialdata(nodecount);
2072 for(u32 i=0; i<nodecount; i++)
2074 materialdata[i] = data[i].d;
2076 compress(materialdata, os, version);
2078 // Get and compress lights
2079 SharedBuffer<u8> lightdata(nodecount);
2080 for(u32 i=0; i<nodecount; i++)
2082 lightdata[i] = data[i].param;
2084 compress(lightdata, os, version);
2088 // Get and compress param2
2089 SharedBuffer<u8> param2data(nodecount);
2090 for(u32 i=0; i<nodecount; i++)
2092 param2data[i] = data[i].param2;
2094 compress(param2data, os, version);
2097 // All other versions (newest)
2104 if(m_day_night_differs)
2106 if(m_lighting_expired)
2108 os.write((char*)&flags, 1);
2110 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2116 SharedBuffer<u8> databuf(nodecount*3);
2119 for(u32 i=0; i<nodecount; i++)
2121 databuf[i] = data[i].d;
2125 for(u32 i=0; i<nodecount; i++)
2127 databuf[i+nodecount] = data[i].param;
2131 for(u32 i=0; i<nodecount; i++)
2133 databuf[i+nodecount*2] = data[i].param2;
2137 Compress data to output stream
2140 compress(databuf, os, version);
2149 std::ostringstream oss(std::ios_base::binary);
2150 m_node_metadata.serialize(oss);
2151 os<<serializeString(oss.str());
2155 std::ostringstream oss(std::ios_base::binary);
2156 m_node_metadata.serialize(oss);
2157 compressZlib(oss.str(), os);
2158 //os<<serializeLongString(oss.str());
2164 void MapBlock::deSerialize(std::istream &is, u8 version)
2166 if(!ser_ver_supported(version))
2167 throw VersionMismatchException("ERROR: MapBlock format not supported");
2169 // These have no lighting info
2172 setLightingExpired(true);
2175 // These have no compression
2176 if(version <= 3 || version == 5 || version == 6)
2178 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2181 if(is.gcount() != 1)
2182 throw SerializationError
2183 ("MapBlock::deSerialize: no enough input data");
2184 is_underground = tmp;
2185 for(u32 i=0; i<nodecount; i++)
2187 s32 len = MapNode::serializedLength(version);
2188 SharedBuffer<u8> d(len);
2189 is.read((char*)*d, len);
2190 if(is.gcount() != len)
2191 throw SerializationError
2192 ("MapBlock::deSerialize: no enough input data");
2193 data[i].deSerialize(*d, version);
2196 else if(version <= 10)
2198 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2201 is.read((char*)&t8, 1);
2202 is_underground = t8;
2205 // Uncompress and set material data
2206 std::ostringstream os(std::ios_base::binary);
2207 decompress(is, os, version);
2208 std::string s = os.str();
2209 if(s.size() != nodecount)
2210 throw SerializationError
2211 ("MapBlock::deSerialize: invalid format");
2212 for(u32 i=0; i<s.size(); i++)
2218 // Uncompress and set param data
2219 std::ostringstream os(std::ios_base::binary);
2220 decompress(is, os, version);
2221 std::string s = os.str();
2222 if(s.size() != nodecount)
2223 throw SerializationError
2224 ("MapBlock::deSerialize: invalid format");
2225 for(u32 i=0; i<s.size(); i++)
2227 data[i].param = s[i];
2233 // Uncompress and set param2 data
2234 std::ostringstream os(std::ios_base::binary);
2235 decompress(is, os, version);
2236 std::string s = os.str();
2237 if(s.size() != nodecount)
2238 throw SerializationError
2239 ("MapBlock::deSerialize: invalid format");
2240 for(u32 i=0; i<s.size(); i++)
2242 data[i].param2 = s[i];
2246 // All other versions (newest)
2249 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2252 is.read((char*)&flags, 1);
2253 is_underground = (flags & 0x01) ? true : false;
2254 m_day_night_differs = (flags & 0x02) ? true : false;
2255 m_lighting_expired = (flags & 0x04) ? true : false;
2258 std::ostringstream os(std::ios_base::binary);
2259 decompress(is, os, version);
2260 std::string s = os.str();
2261 if(s.size() != nodecount*3)
2262 throw SerializationError
2263 ("MapBlock::deSerialize: invalid format");
2266 for(u32 i=0; i<nodecount; i++)
2271 for(u32 i=0; i<nodecount; i++)
2273 data[i].param = s[i+nodecount];
2276 for(u32 i=0; i<nodecount; i++)
2278 data[i].param2 = s[i+nodecount*2];
2290 std::string data = deSerializeString(is);
2291 std::istringstream iss(data, std::ios_base::binary);
2292 m_node_metadata.deSerialize(iss);
2296 //std::string data = deSerializeLongString(is);
2297 std::ostringstream oss(std::ios_base::binary);
2298 decompressZlib(is, oss);
2299 std::istringstream iss(oss.str(), std::ios_base::binary);
2300 m_node_metadata.deSerialize(iss);
2303 catch(SerializationError &e)
2305 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
2306 <<" while deserializing node metadata"<<std::endl;
2312 Translate nodes as specified in the translate_to fields of
2315 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2317 MapNode &n = data[i];
2319 MapNode *translate_to = content_features(n.d).translate_to;
2322 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2323 <<translate_to->d<<std::endl;