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);
623 continuous_tiles_count = 0;
625 makes_face = next_makes_face;
626 p_corrected = next_p_corrected;
627 face_dir_corrected = next_face_dir_corrected;
628 lights[0] = next_lights[0];
629 lights[1] = next_lights[1];
630 lights[2] = next_lights[2];
631 lights[3] = next_lights[3];
640 This is used because CMeshBuffer::append() is very slow
644 video::SMaterial material;
645 core::array<u16> indices;
646 core::array<video::S3DVertex> vertices;
653 video::SMaterial material,
654 const video::S3DVertex* const vertices,
656 const u16* const indices,
660 PreMeshBuffer *p = NULL;
661 for(u32 i=0; i<m_prebuffers.size(); i++)
663 PreMeshBuffer &pp = m_prebuffers[i];
664 if(pp.material != material)
674 pp.material = material;
675 m_prebuffers.push_back(pp);
676 p = &m_prebuffers[m_prebuffers.size()-1];
679 u32 vertex_count = p->vertices.size();
680 for(u32 i=0; i<numIndices; i++)
682 u32 j = indices[i] + vertex_count;
685 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
686 // NOTE: Fix is to just add an another MeshBuffer
688 p->indices.push_back(j);
690 for(u32 i=0; i<numVertices; i++)
692 p->vertices.push_back(vertices[i]);
696 void fillMesh(scene::SMesh *mesh)
698 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
699 <<" meshbuffers"<<std::endl;*/
700 for(u32 i=0; i<m_prebuffers.size(); i++)
702 PreMeshBuffer &p = m_prebuffers[i];
704 /*dstream<<"p.vertices.size()="<<p.vertices.size()
705 <<", p.indices.size()="<<p.indices.size()
710 // This is a "Standard MeshBuffer",
711 // it's a typedeffed CMeshBuffer<video::S3DVertex>
712 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
714 buf->Material = p.material;
715 //((scene::SMeshBuffer*)buf)->Material = p.material;
717 //buf->setHardwareMappingHint(scene::EHM_STATIC);
719 mesh->addMeshBuffer(buf);
723 buf->append(p.vertices.pointer(), p.vertices.size(),
724 p.indices.pointer(), p.indices.size());
729 core::array<PreMeshBuffer> m_prebuffers;
732 scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
734 // 4-21ms for MAP_BLOCKSIZE=16
735 // 24-155ms for MAP_BLOCKSIZE=32
736 //TimeTaker timer1("makeMapBlockMesh()");
738 core::array<FastFace> fastfaces_new;
740 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
742 // floating point conversion
743 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
748 bool new_style_water = g_settings.getBool("new_style_water");
749 bool new_style_leaves = g_settings.getBool("new_style_leaves");
750 bool smooth_lighting = g_settings.getBool("smooth_lighting");
752 float node_water_level = 1.0;
754 node_water_level = 0.85;
757 We are including the faces of the trailing edges of the block.
758 This means that when something changes, the caller must
759 also update the meshes of the blocks at the leading edges.
761 NOTE: This is the slowest part of this method.
765 // 4-23ms for MAP_BLOCKSIZE=16
766 //TimeTaker timer2("updateMesh() collect");
769 Go through every y,z and get top(y+) faces in rows of x+
771 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
772 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
773 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
774 v3s16(0,y,z), MAP_BLOCKSIZE,
777 v3s16(0,1,0), //face dir
787 Go through every x,y and get right(x+) faces in rows of z+
789 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
790 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
791 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
792 v3s16(x,y,0), MAP_BLOCKSIZE,
805 Go through every y,z and get back(z+) faces in rows of x+
807 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
808 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
809 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
810 v3s16(0,y,z), MAP_BLOCKSIZE,
827 Convert FastFaces to SMesh
830 MeshCollector collector;
832 if(fastfaces_new.size() > 0)
834 // avg 0ms (100ms spikes when loading textures the first time)
835 //TimeTaker timer2("updateMesh() mesh building");
837 video::SMaterial material;
838 material.setFlag(video::EMF_LIGHTING, false);
839 material.setFlag(video::EMF_BILINEAR_FILTER, false);
840 material.setFlag(video::EMF_FOG_ENABLE, true);
841 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
842 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
844 for(u32 i=0; i<fastfaces_new.size(); i++)
846 FastFace &f = fastfaces_new[i];
848 const u16 indices[] = {0,1,2,2,3,0};
849 const u16 indices_alternate[] = {0,1,3,2,3,1};
851 video::ITexture *texture = f.tile.texture.atlas;
855 material.setTexture(0, texture);
857 f.tile.applyMaterialOptions(material);
859 const u16 *indices_p = indices;
862 Revert triangles for nicer looking gradient if vertices
863 1 and 3 have same color or 0 and 2 have different color.
865 if(f.vertices[0].Color != f.vertices[2].Color
866 || f.vertices[1].Color == f.vertices[3].Color)
867 indices_p = indices_alternate;
869 collector.append(material, f.vertices, 4, indices_p, 6);
874 Add special graphics:
880 //TimeTaker timer2("updateMesh() adding special stuff");
882 // Flowing water material
883 video::SMaterial material_water1;
884 material_water1.setFlag(video::EMF_LIGHTING, false);
885 material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
886 material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
887 material_water1.setFlag(video::EMF_FOG_ENABLE, true);
888 material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
889 AtlasPointer pa_water1 = g_texturesource->getTexture(
890 g_texturesource->getTextureId("water.png"));
891 material_water1.setTexture(0, pa_water1.atlas);
893 // New-style leaves material
894 video::SMaterial material_leaves1;
895 material_leaves1.setFlag(video::EMF_LIGHTING, false);
896 //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
897 material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
898 material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
899 material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
900 AtlasPointer pa_leaves1 = g_texturesource->getTexture(
901 g_texturesource->getTextureId("leaves.png"));
902 material_leaves1.setTexture(0, pa_leaves1.atlas);
905 video::SMaterial material_glass;
906 material_glass.setFlag(video::EMF_LIGHTING, false);
907 material_glass.setFlag(video::EMF_BILINEAR_FILTER, false);
908 material_glass.setFlag(video::EMF_FOG_ENABLE, true);
909 material_glass.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
910 AtlasPointer pa_glass = g_texturesource->getTexture(
911 g_texturesource->getTextureId("glass.png"));
912 material_glass.setTexture(0, pa_glass.atlas);
915 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
916 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
917 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
921 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
926 if(n.d == CONTENT_TORCH)
928 video::SColor c(255,255,255,255);
930 // Wall at X+ of node
931 video::S3DVertex vertices[4] =
933 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
934 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
935 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
936 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
939 v3s16 dir = unpackDir(n.dir);
941 for(s32 i=0; i<4; i++)
943 if(dir == v3s16(1,0,0))
944 vertices[i].Pos.rotateXZBy(0);
945 if(dir == v3s16(-1,0,0))
946 vertices[i].Pos.rotateXZBy(180);
947 if(dir == v3s16(0,0,1))
948 vertices[i].Pos.rotateXZBy(90);
949 if(dir == v3s16(0,0,-1))
950 vertices[i].Pos.rotateXZBy(-90);
951 if(dir == v3s16(0,-1,0))
952 vertices[i].Pos.rotateXZBy(45);
953 if(dir == v3s16(0,1,0))
954 vertices[i].Pos.rotateXZBy(-45);
956 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
960 video::SMaterial material;
961 material.setFlag(video::EMF_LIGHTING, false);
962 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
963 material.setFlag(video::EMF_BILINEAR_FILTER, false);
964 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
965 material.MaterialType
966 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
968 if(dir == v3s16(0,-1,0))
969 material.setTexture(0,
970 g_texturesource->getTextureRaw("torch_on_floor.png"));
971 else if(dir == v3s16(0,1,0))
972 material.setTexture(0,
973 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
974 // For backwards compatibility
975 else if(dir == v3s16(0,0,0))
976 material.setTexture(0,
977 g_texturesource->getTextureRaw("torch_on_floor.png"));
979 material.setTexture(0,
980 g_texturesource->getTextureRaw("torch.png"));
982 u16 indices[] = {0,1,2,2,3,0};
983 // Add to mesh collector
984 collector.append(material, vertices, 4, indices, 6);
989 if(n.d == CONTENT_SIGN_WALL)
991 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
992 video::SColor c(255,l,l,l);
994 float d = (float)BS/16;
995 // Wall at X+ of node
996 video::S3DVertex vertices[4] =
998 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
999 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
1000 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
1001 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
1004 v3s16 dir = unpackDir(n.dir);
1006 for(s32 i=0; i<4; i++)
1008 if(dir == v3s16(1,0,0))
1009 vertices[i].Pos.rotateXZBy(0);
1010 if(dir == v3s16(-1,0,0))
1011 vertices[i].Pos.rotateXZBy(180);
1012 if(dir == v3s16(0,0,1))
1013 vertices[i].Pos.rotateXZBy(90);
1014 if(dir == v3s16(0,0,-1))
1015 vertices[i].Pos.rotateXZBy(-90);
1016 if(dir == v3s16(0,-1,0))
1017 vertices[i].Pos.rotateXYBy(-90);
1018 if(dir == v3s16(0,1,0))
1019 vertices[i].Pos.rotateXYBy(90);
1021 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1025 video::SMaterial material;
1026 material.setFlag(video::EMF_LIGHTING, false);
1027 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
1028 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1029 material.setFlag(video::EMF_FOG_ENABLE, true);
1030 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1031 material.MaterialType
1032 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1034 material.setTexture(0,
1035 g_texturesource->getTextureRaw("sign_wall.png"));
1037 u16 indices[] = {0,1,2,2,3,0};
1038 // Add to mesh collector
1039 collector.append(material, vertices, 4, indices, 6);
1042 Add flowing water to mesh
1044 else if(n.d == CONTENT_WATER)
1046 bool top_is_water = false;
1047 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1048 if(ntop.d == CONTENT_WATER || ntop.d == CONTENT_WATERSOURCE)
1049 top_is_water = true;
1052 // Use the light of the node on top if possible
1053 if(content_features(ntop.d).param_type == CPT_LIGHT)
1054 l = decode_light(ntop.getLightBlend(data->m_daynight_ratio));
1055 // Otherwise use the light of this node (the water)
1057 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1058 video::SColor c(WATER_ALPHA,l,l,l);
1060 // Neighbor water levels (key = relative position)
1061 // Includes current node
1062 core::map<v3s16, f32> neighbor_levels;
1063 core::map<v3s16, u8> neighbor_contents;
1064 core::map<v3s16, u8> neighbor_flags;
1065 const u8 neighborflag_top_is_water = 0x01;
1066 v3s16 neighbor_dirs[9] = {
1077 for(u32 i=0; i<9; i++)
1079 u8 content = CONTENT_AIR;
1080 float level = -0.5 * BS;
1083 v3s16 p2 = p + neighbor_dirs[i];
1084 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1085 if(n2.d != CONTENT_IGNORE)
1089 if(n2.d == CONTENT_WATERSOURCE)
1090 level = (-0.5+node_water_level) * BS;
1091 else if(n2.d == CONTENT_WATER)
1092 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
1093 * node_water_level) * BS;
1095 // Check node above neighbor.
1096 // NOTE: This doesn't get executed if neighbor
1099 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1100 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
1101 flags |= neighborflag_top_is_water;
1104 neighbor_levels.insert(neighbor_dirs[i], level);
1105 neighbor_contents.insert(neighbor_dirs[i], content);
1106 neighbor_flags.insert(neighbor_dirs[i], flags);
1109 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
1110 //float water_level = neighbor_levels[v3s16(0,0,0)];
1112 // Corner heights (average between four waters)
1113 f32 corner_levels[4];
1115 v3s16 halfdirs[4] = {
1121 for(u32 i=0; i<4; i++)
1123 v3s16 cornerdir = halfdirs[i];
1124 float cornerlevel = 0;
1125 u32 valid_count = 0;
1126 for(u32 j=0; j<4; j++)
1128 v3s16 neighbordir = cornerdir - halfdirs[j];
1129 u8 content = neighbor_contents[neighbordir];
1130 // Special case for source nodes
1131 if(content == CONTENT_WATERSOURCE)
1133 cornerlevel = (-0.5+node_water_level)*BS;
1137 else if(content == CONTENT_WATER)
1139 cornerlevel += neighbor_levels[neighbordir];
1142 else if(content == CONTENT_AIR)
1144 cornerlevel += -0.5*BS;
1149 cornerlevel /= valid_count;
1150 corner_levels[i] = cornerlevel;
1157 v3s16 side_dirs[4] = {
1163 s16 side_corners[4][2] = {
1169 for(u32 i=0; i<4; i++)
1171 v3s16 dir = side_dirs[i];
1174 If our topside is water and neighbor's topside
1175 is water, don't draw side face
1178 neighbor_flags[dir] & neighborflag_top_is_water)
1181 u8 neighbor_content = neighbor_contents[dir];
1183 // Don't draw face if neighbor is not air or water
1184 if(neighbor_content != CONTENT_AIR
1185 && neighbor_content != CONTENT_WATER)
1188 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1190 // Don't draw any faces if neighbor is water and top is water
1191 if(neighbor_is_water == true && top_is_water == false)
1194 video::S3DVertex vertices[4] =
1196 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1197 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1198 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1199 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1200 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1201 pa_water1.x0(), pa_water1.y1()),
1202 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1203 pa_water1.x1(), pa_water1.y1()),
1204 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1205 pa_water1.x1(), pa_water1.y0()),
1206 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1207 pa_water1.x0(), pa_water1.y0()),
1211 If our topside is water, set upper border of face
1212 at upper border of node
1216 vertices[2].Pos.Y = 0.5*BS;
1217 vertices[3].Pos.Y = 0.5*BS;
1220 Otherwise upper position of face is corner levels
1224 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1225 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1229 If neighbor is water, lower border of face is corner
1232 if(neighbor_is_water)
1234 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1235 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1238 If neighbor is not water, lower border of face is
1239 lower border of node
1243 vertices[0].Pos.Y = -0.5*BS;
1244 vertices[1].Pos.Y = -0.5*BS;
1247 for(s32 j=0; j<4; j++)
1249 if(dir == v3s16(0,0,1))
1250 vertices[j].Pos.rotateXZBy(0);
1251 if(dir == v3s16(0,0,-1))
1252 vertices[j].Pos.rotateXZBy(180);
1253 if(dir == v3s16(-1,0,0))
1254 vertices[j].Pos.rotateXZBy(90);
1255 if(dir == v3s16(1,0,-0))
1256 vertices[j].Pos.rotateXZBy(-90);
1258 vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
1261 u16 indices[] = {0,1,2,2,3,0};
1262 // Add to mesh collector
1263 collector.append(material_water1, vertices, 4, indices, 6);
1267 Generate top side, if appropriate
1270 if(top_is_water == false)
1272 video::S3DVertex vertices[4] =
1274 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1275 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1276 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1277 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1278 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1279 pa_water1.x0(), pa_water1.y1()),
1280 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1281 pa_water1.x1(), pa_water1.y1()),
1282 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1283 pa_water1.x1(), pa_water1.y0()),
1284 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1285 pa_water1.x0(), pa_water1.y0()),
1288 // This fixes a strange bug
1289 s32 corner_resolve[4] = {3,2,1,0};
1291 for(s32 i=0; i<4; i++)
1293 //vertices[i].Pos.Y += water_level;
1294 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1295 s32 j = corner_resolve[i];
1296 vertices[i].Pos.Y += corner_levels[j];
1297 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1300 u16 indices[] = {0,1,2,2,3,0};
1301 // Add to mesh collector
1302 collector.append(material_water1, vertices, 4, indices, 6);
1306 Add water sources to mesh if using new style
1308 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1310 //bool top_is_water = false;
1311 bool top_is_air = false;
1312 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1313 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1314 top_is_water = true;*/
1315 if(n.d == CONTENT_AIR)
1318 /*if(top_is_water == true)
1320 if(top_is_air == false)
1323 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1324 video::SColor c(WATER_ALPHA,l,l,l);
1326 video::S3DVertex vertices[4] =
1328 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1329 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1330 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1331 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1332 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1333 pa_water1.x0(), pa_water1.y1()),
1334 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1335 pa_water1.x1(), pa_water1.y1()),
1336 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1337 pa_water1.x1(), pa_water1.y0()),
1338 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1339 pa_water1.x0(), pa_water1.y0()),
1342 for(s32 i=0; i<4; i++)
1344 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1345 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1348 u16 indices[] = {0,1,2,2,3,0};
1349 // Add to mesh collector
1350 collector.append(material_water1, vertices, 4, indices, 6);
1353 Add leaves if using new style
1355 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1357 /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
1358 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1359 video::SColor c(255,l,l,l);
1361 for(u32 j=0; j<6; j++)
1363 video::S3DVertex vertices[4] =
1365 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1366 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1367 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1368 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1369 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1370 pa_leaves1.x0(), pa_leaves1.y1()),
1371 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1372 pa_leaves1.x1(), pa_leaves1.y1()),
1373 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1374 pa_leaves1.x1(), pa_leaves1.y0()),
1375 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1376 pa_leaves1.x0(), pa_leaves1.y0()),
1381 for(u16 i=0; i<4; i++)
1382 vertices[i].Pos.rotateXZBy(0);
1386 for(u16 i=0; i<4; i++)
1387 vertices[i].Pos.rotateXZBy(180);
1391 for(u16 i=0; i<4; i++)
1392 vertices[i].Pos.rotateXZBy(-90);
1396 for(u16 i=0; i<4; i++)
1397 vertices[i].Pos.rotateXZBy(90);
1401 for(u16 i=0; i<4; i++)
1402 vertices[i].Pos.rotateYZBy(-90);
1406 for(u16 i=0; i<4; i++)
1407 vertices[i].Pos.rotateYZBy(90);
1410 for(u16 i=0; i<4; i++)
1412 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1415 u16 indices[] = {0,1,2,2,3,0};
1416 // Add to mesh collector
1417 collector.append(material_leaves1, vertices, 4, indices, 6);
1423 else if(n.d == CONTENT_GLASS)
1425 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1426 video::SColor c(255,l,l,l);
1428 for(u32 j=0; j<6; j++)
1430 video::S3DVertex vertices[4] =
1432 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1433 pa_glass.x0(), pa_glass.y1()),
1434 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1435 pa_glass.x1(), pa_glass.y1()),
1436 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1437 pa_glass.x1(), pa_glass.y0()),
1438 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1439 pa_glass.x0(), pa_glass.y0()),
1444 for(u16 i=0; i<4; i++)
1445 vertices[i].Pos.rotateXZBy(0);
1449 for(u16 i=0; i<4; i++)
1450 vertices[i].Pos.rotateXZBy(180);
1454 for(u16 i=0; i<4; i++)
1455 vertices[i].Pos.rotateXZBy(-90);
1459 for(u16 i=0; i<4; i++)
1460 vertices[i].Pos.rotateXZBy(90);
1464 for(u16 i=0; i<4; i++)
1465 vertices[i].Pos.rotateYZBy(-90);
1469 for(u16 i=0; i<4; i++)
1470 vertices[i].Pos.rotateYZBy(90);
1473 for(u16 i=0; i<4; i++)
1475 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1478 u16 indices[] = {0,1,2,2,3,0};
1479 // Add to mesh collector
1480 collector.append(material_glass, vertices, 4, indices, 6);
1489 Add stuff from collector to mesh
1492 scene::SMesh *mesh_new = NULL;
1493 mesh_new = new scene::SMesh();
1495 collector.fillMesh(mesh_new);
1498 Do some stuff to the mesh
1501 mesh_new->recalculateBoundingBox();
1504 Delete new mesh if it is empty
1507 if(mesh_new->getMeshBufferCount() == 0)
1516 // Usually 1-700 faces and 1-7 materials
1517 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1518 <<"and uses "<<mesh_new->getMeshBufferCount()
1519 <<" materials (meshbuffers)"<<std::endl;
1522 // Use VBO for mesh (this just would set this for ever buffer)
1523 // This will lead to infinite memory usage because or irrlicht.
1524 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1527 NOTE: If that is enabled, some kind of a queue to the main
1528 thread should be made which would call irrlicht to delete
1529 the hardware buffer and then delete the mesh
1535 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1544 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
1548 is_underground(false),
1549 m_lighting_expired(true),
1550 m_day_night_differs(false),
1551 //m_not_fully_generated(false),
1558 //m_spawn_timer = -10000;
1561 m_mesh_expired = false;
1564 m_temp_mods_mutex.Init();
1568 MapBlock::~MapBlock()
1572 JMutexAutoLock lock(mesh_mutex);
1586 bool MapBlock::isValidPositionParent(v3s16 p)
1588 if(isValidPosition(p))
1593 return m_parent->isValidPosition(getPosRelative() + p);
1597 MapNode MapBlock::getNodeParent(v3s16 p)
1599 if(isValidPosition(p) == false)
1601 return m_parent->getNode(getPosRelative() + p);
1606 throw InvalidPositionException();
1607 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1611 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
1613 if(isValidPosition(p) == false)
1615 m_parent->setNode(getPosRelative() + p, n);
1620 throw InvalidPositionException();
1621 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
1625 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
1627 if(isValidPosition(p) == false)
1630 return m_parent->getNode(getPosRelative() + p);
1632 catch(InvalidPositionException &e)
1634 return MapNode(CONTENT_IGNORE);
1641 return MapNode(CONTENT_IGNORE);
1643 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1650 void MapBlock::updateMesh(u32 daynight_ratio)
1654 DEBUG: If mesh has been generated, don't generate it again
1657 JMutexAutoLock meshlock(mesh_mutex);
1664 data.fill(daynight_ratio, this);
1666 scene::SMesh *mesh_new = makeMapBlockMesh(&data);
1672 replaceMesh(mesh_new);
1677 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
1681 //scene::SMesh *mesh_old = mesh[daynight_i];
1682 //mesh[daynight_i] = mesh_new;
1684 scene::SMesh *mesh_old = mesh;
1686 setMeshExpired(false);
1688 if(mesh_old != NULL)
1690 // Remove hardware buffers of meshbuffers of mesh
1691 // NOTE: No way, this runs in a different thread and everything
1692 /*u32 c = mesh_old->getMeshBufferCount();
1693 for(u32 i=0; i<c; i++)
1695 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1698 /*dstream<<"mesh_old->getReferenceCount()="
1699 <<mesh_old->getReferenceCount()<<std::endl;
1700 u32 c = mesh_old->getMeshBufferCount();
1701 for(u32 i=0; i<c; i++)
1703 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1704 dstream<<"buf->getReferenceCount()="
1705 <<buf->getReferenceCount()<<std::endl;
1714 mesh_mutex.Unlock();
1720 Propagates sunlight down through the block.
1721 Doesn't modify nodes that are not affected by sunlight.
1723 Returns false if sunlight at bottom block is invalid
1724 Returns true if bottom block doesn't exist.
1726 If there is a block above, continues from it.
1727 If there is no block above, assumes there is sunlight, unless
1728 is_underground is set or highest node is water.
1730 At the moment, all sunlighted nodes are added to light_sources.
1731 - SUGG: This could be optimized
1733 Turns sunglighted mud into grass.
1735 if remove_light==true, sets non-sunlighted nodes black.
1737 if black_air_left!=NULL, it is set to true if non-sunlighted
1738 air is left in block.
1740 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1741 bool remove_light, bool *black_air_left,
1744 // Whether the sunlight at the top of the bottom block is valid
1745 bool block_below_is_valid = true;
1747 v3s16 pos_relative = getPosRelative();
1749 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1751 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1754 bool no_sunlight = false;
1755 bool no_top_block = false;
1756 // Check if node above block has sunlight
1758 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1759 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1764 catch(InvalidPositionException &e)
1766 no_top_block = true;
1768 // NOTE: This makes over-ground roofed places sunlighted
1769 // Assume sunlight, unless is_underground==true
1776 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1777 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1782 // NOTE: As of now, this just would make everything dark.
1784 //no_sunlight = true;
1787 #if 0 // Doesn't work; nothing gets light.
1788 bool no_sunlight = true;
1789 bool no_top_block = false;
1790 // Check if node above block has sunlight
1792 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1793 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1795 no_sunlight = false;
1798 catch(InvalidPositionException &e)
1800 no_top_block = true;
1804 /*std::cout<<"("<<x<<","<<z<<"): "
1805 <<"no_top_block="<<no_top_block
1806 <<", is_underground="<<is_underground
1807 <<", no_sunlight="<<no_sunlight
1810 s16 y = MAP_BLOCKSIZE-1;
1812 // This makes difference to diminishing in water.
1813 bool stopped_to_solid_object = false;
1815 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1820 MapNode &n = getNodeRef(pos);
1822 if(current_light == 0)
1826 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1828 // Do nothing: Sunlight is continued
1830 else if(n.light_propagates() == false)
1834 bool upper_is_air = false;
1837 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1838 upper_is_air = true;
1840 catch(InvalidPositionException &e)
1843 // Turn mud into grass
1844 if(upper_is_air && n.d == CONTENT_MUD
1845 && current_light == LIGHT_SUN)
1847 n.d = CONTENT_GRASS;
1851 // A solid object is on the way.
1852 stopped_to_solid_object = true;
1860 current_light = diminish_light(current_light);
1863 u8 old_light = n.getLight(LIGHTBANK_DAY);
1865 if(current_light > old_light || remove_light)
1867 n.setLight(LIGHTBANK_DAY, current_light);
1870 if(diminish_light(current_light) != 0)
1872 light_sources.insert(pos_relative + pos, true);
1875 if(current_light == 0 && stopped_to_solid_object)
1879 *black_air_left = true;
1884 // Whether or not the block below should see LIGHT_SUN
1885 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1888 If the block below hasn't already been marked invalid:
1890 Check if the node below the block has proper sunlight at top.
1891 If not, the block below is invalid.
1893 Ignore non-transparent nodes as they always have no light
1897 if(block_below_is_valid)
1899 MapNode n = getNodeParent(v3s16(x, -1, z));
1900 if(n.light_propagates())
1902 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1903 && sunlight_should_go_down == false)
1904 block_below_is_valid = false;
1905 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1906 && sunlight_should_go_down == true)
1907 block_below_is_valid = false;
1911 catch(InvalidPositionException &e)
1913 /*std::cout<<"InvalidBlockException for bottom block node"
1915 // Just no block below, no need to panic.
1920 return block_below_is_valid;
1924 void MapBlock::copyTo(VoxelManipulator &dst)
1926 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1927 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1929 // Copy from data to VoxelManipulator
1930 dst.copyFrom(data, data_area, v3s16(0,0,0),
1931 getPosRelative(), data_size);
1934 void MapBlock::copyFrom(VoxelManipulator &dst)
1936 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1937 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1939 // Copy from VoxelManipulator to data
1940 dst.copyTo(data, data_area, v3s16(0,0,0),
1941 getPosRelative(), data_size);
1944 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1949 m_objects.step(dtime, server, daynight_ratio);
1953 Spawn some objects at random.
1955 Use dayNightDiffed() to approximate being near ground level
1957 if(m_spawn_timer < -999)
1961 if(dayNightDiffed() == true && getObjectCount() == 0)
1963 m_spawn_timer -= dtime;
1964 if(m_spawn_timer <= 0.0)
1966 m_spawn_timer += myrand() % 300;
1969 (myrand()%(MAP_BLOCKSIZE-1))+0,
1970 (myrand()%(MAP_BLOCKSIZE-1))+0
1973 s16 y = getGroundLevel(p2d);
1977 v3s16 p(p2d.X, y+1, p2d.Y);
1979 if(getNode(p).d == CONTENT_AIR
1980 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1982 RatObject *obj = new RatObject(NULL, -1, intToFloat(p, BS));
1994 void MapBlock::updateDayNightDiff()
1998 m_day_night_differs = false;
2002 bool differs = false;
2005 Check if any lighting value differs
2007 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2009 MapNode &n = data[i];
2010 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
2018 If some lighting values differ, check if the whole thing is
2019 just air. If it is, differ = false
2023 bool only_air = true;
2024 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2026 MapNode &n = data[i];
2027 if(n.d != CONTENT_AIR)
2037 // Set member variable
2038 m_day_night_differs = differs;
2041 s16 MapBlock::getGroundLevel(v2s16 p2d)
2047 s16 y = MAP_BLOCKSIZE-1;
2050 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
2051 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
2053 if(y == MAP_BLOCKSIZE-1)
2061 catch(InvalidPositionException &e)
2071 void MapBlock::serialize(std::ostream &os, u8 version)
2073 if(!ser_ver_supported(version))
2074 throw VersionMismatchException("ERROR: MapBlock format not supported");
2078 throw SerializationError("ERROR: Not writing dummy block.");
2081 // These have no compression
2082 if(version <= 3 || version == 5 || version == 6)
2084 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2086 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
2087 SharedBuffer<u8> dest(buflen);
2089 dest[0] = is_underground;
2090 for(u32 i=0; i<nodecount; i++)
2092 u32 s = 1 + i * MapNode::serializedLength(version);
2093 data[i].serialize(&dest[s], version);
2096 os.write((char*)*dest, dest.getSize());
2098 else if(version <= 10)
2102 Compress the materials and the params separately.
2106 os.write((char*)&is_underground, 1);
2108 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2110 // Get and compress materials
2111 SharedBuffer<u8> materialdata(nodecount);
2112 for(u32 i=0; i<nodecount; i++)
2114 materialdata[i] = data[i].d;
2116 compress(materialdata, os, version);
2118 // Get and compress lights
2119 SharedBuffer<u8> lightdata(nodecount);
2120 for(u32 i=0; i<nodecount; i++)
2122 lightdata[i] = data[i].param;
2124 compress(lightdata, os, version);
2128 // Get and compress param2
2129 SharedBuffer<u8> param2data(nodecount);
2130 for(u32 i=0; i<nodecount; i++)
2132 param2data[i] = data[i].param2;
2134 compress(param2data, os, version);
2137 // All other versions (newest)
2144 if(m_day_night_differs)
2146 if(m_lighting_expired)
2148 os.write((char*)&flags, 1);
2150 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2156 SharedBuffer<u8> databuf(nodecount*3);
2159 for(u32 i=0; i<nodecount; i++)
2161 databuf[i] = data[i].d;
2165 for(u32 i=0; i<nodecount; i++)
2167 databuf[i+nodecount] = data[i].param;
2171 for(u32 i=0; i<nodecount; i++)
2173 databuf[i+nodecount*2] = data[i].param2;
2177 Compress data to output stream
2180 compress(databuf, os, version);
2190 std::ostringstream oss(std::ios_base::binary);
2191 m_node_metadata.serialize(oss);
2192 os<<serializeString(oss.str());
2194 // This will happen if the string is longer than 65535
2195 catch(SerializationError &e)
2197 // Use an empty string
2198 os<<serializeString("");
2203 std::ostringstream oss(std::ios_base::binary);
2204 m_node_metadata.serialize(oss);
2205 compressZlib(oss.str(), os);
2206 //os<<serializeLongString(oss.str());
2212 void MapBlock::deSerialize(std::istream &is, u8 version)
2214 if(!ser_ver_supported(version))
2215 throw VersionMismatchException("ERROR: MapBlock format not supported");
2217 // These have no lighting info
2220 setLightingExpired(true);
2223 // These have no compression
2224 if(version <= 3 || version == 5 || version == 6)
2226 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2229 if(is.gcount() != 1)
2230 throw SerializationError
2231 ("MapBlock::deSerialize: no enough input data");
2232 is_underground = tmp;
2233 for(u32 i=0; i<nodecount; i++)
2235 s32 len = MapNode::serializedLength(version);
2236 SharedBuffer<u8> d(len);
2237 is.read((char*)*d, len);
2238 if(is.gcount() != len)
2239 throw SerializationError
2240 ("MapBlock::deSerialize: no enough input data");
2241 data[i].deSerialize(*d, version);
2244 else if(version <= 10)
2246 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2249 is.read((char*)&t8, 1);
2250 is_underground = t8;
2253 // Uncompress and set material data
2254 std::ostringstream os(std::ios_base::binary);
2255 decompress(is, os, version);
2256 std::string s = os.str();
2257 if(s.size() != nodecount)
2258 throw SerializationError
2259 ("MapBlock::deSerialize: invalid format");
2260 for(u32 i=0; i<s.size(); i++)
2266 // Uncompress and set param data
2267 std::ostringstream os(std::ios_base::binary);
2268 decompress(is, os, version);
2269 std::string s = os.str();
2270 if(s.size() != nodecount)
2271 throw SerializationError
2272 ("MapBlock::deSerialize: invalid format");
2273 for(u32 i=0; i<s.size(); i++)
2275 data[i].param = s[i];
2281 // Uncompress and set param2 data
2282 std::ostringstream os(std::ios_base::binary);
2283 decompress(is, os, version);
2284 std::string s = os.str();
2285 if(s.size() != nodecount)
2286 throw SerializationError
2287 ("MapBlock::deSerialize: invalid format");
2288 for(u32 i=0; i<s.size(); i++)
2290 data[i].param2 = s[i];
2294 // All other versions (newest)
2297 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2300 is.read((char*)&flags, 1);
2301 is_underground = (flags & 0x01) ? true : false;
2302 m_day_night_differs = (flags & 0x02) ? true : false;
2303 m_lighting_expired = (flags & 0x04) ? true : false;
2306 std::ostringstream os(std::ios_base::binary);
2307 decompress(is, os, version);
2308 std::string s = os.str();
2309 if(s.size() != nodecount*3)
2310 throw SerializationError
2311 ("MapBlock::deSerialize: invalid format");
2314 for(u32 i=0; i<nodecount; i++)
2319 for(u32 i=0; i<nodecount; i++)
2321 data[i].param = s[i+nodecount];
2324 for(u32 i=0; i<nodecount; i++)
2326 data[i].param2 = s[i+nodecount*2];
2338 std::string data = deSerializeString(is);
2339 std::istringstream iss(data, std::ios_base::binary);
2340 m_node_metadata.deSerialize(iss);
2344 //std::string data = deSerializeLongString(is);
2345 std::ostringstream oss(std::ios_base::binary);
2346 decompressZlib(is, oss);
2347 std::istringstream iss(oss.str(), std::ios_base::binary);
2348 m_node_metadata.deSerialize(iss);
2351 catch(SerializationError &e)
2353 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
2354 <<" while deserializing node metadata"<<std::endl;
2360 Translate nodes as specified in the translate_to fields of
2363 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2365 MapNode &n = data[i];
2367 MapNode *translate_to = content_features(n.d).translate_to;
2370 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2371 <<translate_to->d<<std::endl;