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);
904 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
905 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
906 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
910 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
915 if(n.d == CONTENT_TORCH)
917 video::SColor c(255,255,255,255);
919 // Wall at X+ of node
920 video::S3DVertex vertices[4] =
922 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
923 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
924 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
925 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
928 v3s16 dir = unpackDir(n.dir);
930 for(s32 i=0; i<4; i++)
932 if(dir == v3s16(1,0,0))
933 vertices[i].Pos.rotateXZBy(0);
934 if(dir == v3s16(-1,0,0))
935 vertices[i].Pos.rotateXZBy(180);
936 if(dir == v3s16(0,0,1))
937 vertices[i].Pos.rotateXZBy(90);
938 if(dir == v3s16(0,0,-1))
939 vertices[i].Pos.rotateXZBy(-90);
940 if(dir == v3s16(0,-1,0))
941 vertices[i].Pos.rotateXZBy(45);
942 if(dir == v3s16(0,1,0))
943 vertices[i].Pos.rotateXZBy(-45);
945 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
949 video::SMaterial material;
950 material.setFlag(video::EMF_LIGHTING, false);
951 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
952 material.setFlag(video::EMF_BILINEAR_FILTER, false);
953 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
954 material.MaterialType
955 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
957 if(dir == v3s16(0,-1,0))
958 material.setTexture(0,
959 g_texturesource->getTextureRaw("torch_on_floor.png"));
960 else if(dir == v3s16(0,1,0))
961 material.setTexture(0,
962 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
963 // For backwards compatibility
964 else if(dir == v3s16(0,0,0))
965 material.setTexture(0,
966 g_texturesource->getTextureRaw("torch_on_floor.png"));
968 material.setTexture(0,
969 g_texturesource->getTextureRaw("torch.png"));
971 u16 indices[] = {0,1,2,2,3,0};
972 // Add to mesh collector
973 collector.append(material, vertices, 4, indices, 6);
978 if(n.d == CONTENT_SIGN_WALL)
980 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
981 video::SColor c(255,l,l,l);
983 float d = (float)BS/16;
984 // Wall at X+ of node
985 video::S3DVertex vertices[4] =
987 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
988 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
989 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
990 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
993 v3s16 dir = unpackDir(n.dir);
995 for(s32 i=0; i<4; i++)
997 if(dir == v3s16(1,0,0))
998 vertices[i].Pos.rotateXZBy(0);
999 if(dir == v3s16(-1,0,0))
1000 vertices[i].Pos.rotateXZBy(180);
1001 if(dir == v3s16(0,0,1))
1002 vertices[i].Pos.rotateXZBy(90);
1003 if(dir == v3s16(0,0,-1))
1004 vertices[i].Pos.rotateXZBy(-90);
1005 if(dir == v3s16(0,-1,0))
1006 vertices[i].Pos.rotateXYBy(-90);
1007 if(dir == v3s16(0,1,0))
1008 vertices[i].Pos.rotateXYBy(90);
1010 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1014 video::SMaterial material;
1015 material.setFlag(video::EMF_LIGHTING, false);
1016 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
1017 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1018 material.setFlag(video::EMF_FOG_ENABLE, true);
1019 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1020 material.MaterialType
1021 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1023 material.setTexture(0,
1024 g_texturesource->getTextureRaw("sign_wall.png"));
1026 u16 indices[] = {0,1,2,2,3,0};
1027 // Add to mesh collector
1028 collector.append(material, vertices, 4, indices, 6);
1031 Add flowing water to mesh
1033 else if(n.d == CONTENT_WATER)
1035 bool top_is_water = false;
1036 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1037 if(ntop.d == CONTENT_WATER || ntop.d == CONTENT_WATERSOURCE)
1038 top_is_water = true;
1041 // Use the light of the node on top if possible
1042 if(content_features(ntop.d).param_type == CPT_LIGHT)
1043 l = decode_light(ntop.getLightBlend(data->m_daynight_ratio));
1044 // Otherwise use the light of this node (the water)
1046 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1047 video::SColor c(WATER_ALPHA,l,l,l);
1049 // Neighbor water levels (key = relative position)
1050 // Includes current node
1051 core::map<v3s16, f32> neighbor_levels;
1052 core::map<v3s16, u8> neighbor_contents;
1053 core::map<v3s16, u8> neighbor_flags;
1054 const u8 neighborflag_top_is_water = 0x01;
1055 v3s16 neighbor_dirs[9] = {
1066 for(u32 i=0; i<9; i++)
1068 u8 content = CONTENT_AIR;
1069 float level = -0.5 * BS;
1072 v3s16 p2 = p + neighbor_dirs[i];
1073 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1074 if(n2.d != CONTENT_IGNORE)
1078 if(n2.d == CONTENT_WATERSOURCE)
1079 level = (-0.5+node_water_level) * BS;
1080 else if(n2.d == CONTENT_WATER)
1081 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
1082 * node_water_level) * BS;
1084 // Check node above neighbor.
1085 // NOTE: This doesn't get executed if neighbor
1088 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1089 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
1090 flags |= neighborflag_top_is_water;
1093 neighbor_levels.insert(neighbor_dirs[i], level);
1094 neighbor_contents.insert(neighbor_dirs[i], content);
1095 neighbor_flags.insert(neighbor_dirs[i], flags);
1098 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
1099 //float water_level = neighbor_levels[v3s16(0,0,0)];
1101 // Corner heights (average between four waters)
1102 f32 corner_levels[4];
1104 v3s16 halfdirs[4] = {
1110 for(u32 i=0; i<4; i++)
1112 v3s16 cornerdir = halfdirs[i];
1113 float cornerlevel = 0;
1114 u32 valid_count = 0;
1115 for(u32 j=0; j<4; j++)
1117 v3s16 neighbordir = cornerdir - halfdirs[j];
1118 u8 content = neighbor_contents[neighbordir];
1119 // Special case for source nodes
1120 if(content == CONTENT_WATERSOURCE)
1122 cornerlevel = (-0.5+node_water_level)*BS;
1126 else if(content == CONTENT_WATER)
1128 cornerlevel += neighbor_levels[neighbordir];
1131 else if(content == CONTENT_AIR)
1133 cornerlevel += -0.5*BS;
1138 cornerlevel /= valid_count;
1139 corner_levels[i] = cornerlevel;
1146 v3s16 side_dirs[4] = {
1152 s16 side_corners[4][2] = {
1158 for(u32 i=0; i<4; i++)
1160 v3s16 dir = side_dirs[i];
1163 If our topside is water and neighbor's topside
1164 is water, don't draw side face
1167 neighbor_flags[dir] & neighborflag_top_is_water)
1170 u8 neighbor_content = neighbor_contents[dir];
1172 // Don't draw face if neighbor is not air or water
1173 if(neighbor_content != CONTENT_AIR
1174 && neighbor_content != CONTENT_WATER)
1177 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1179 // Don't draw any faces if neighbor is water and top is water
1180 if(neighbor_is_water == true && top_is_water == false)
1183 video::S3DVertex vertices[4] =
1185 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1186 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1187 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1188 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1189 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1190 pa_water1.x0(), pa_water1.y1()),
1191 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1192 pa_water1.x1(), pa_water1.y1()),
1193 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1194 pa_water1.x1(), pa_water1.y0()),
1195 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1196 pa_water1.x0(), pa_water1.y0()),
1200 If our topside is water, set upper border of face
1201 at upper border of node
1205 vertices[2].Pos.Y = 0.5*BS;
1206 vertices[3].Pos.Y = 0.5*BS;
1209 Otherwise upper position of face is corner levels
1213 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1214 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1218 If neighbor is water, lower border of face is corner
1221 if(neighbor_is_water)
1223 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1224 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1227 If neighbor is not water, lower border of face is
1228 lower border of node
1232 vertices[0].Pos.Y = -0.5*BS;
1233 vertices[1].Pos.Y = -0.5*BS;
1236 for(s32 j=0; j<4; j++)
1238 if(dir == v3s16(0,0,1))
1239 vertices[j].Pos.rotateXZBy(0);
1240 if(dir == v3s16(0,0,-1))
1241 vertices[j].Pos.rotateXZBy(180);
1242 if(dir == v3s16(-1,0,0))
1243 vertices[j].Pos.rotateXZBy(90);
1244 if(dir == v3s16(1,0,-0))
1245 vertices[j].Pos.rotateXZBy(-90);
1247 vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
1250 u16 indices[] = {0,1,2,2,3,0};
1251 // Add to mesh collector
1252 collector.append(material_water1, vertices, 4, indices, 6);
1256 Generate top side, if appropriate
1259 if(top_is_water == false)
1261 video::S3DVertex vertices[4] =
1263 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1264 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1265 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1266 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1267 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1268 pa_water1.x0(), pa_water1.y1()),
1269 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1270 pa_water1.x1(), pa_water1.y1()),
1271 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1272 pa_water1.x1(), pa_water1.y0()),
1273 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1274 pa_water1.x0(), pa_water1.y0()),
1277 // This fixes a strange bug
1278 s32 corner_resolve[4] = {3,2,1,0};
1280 for(s32 i=0; i<4; i++)
1282 //vertices[i].Pos.Y += water_level;
1283 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1284 s32 j = corner_resolve[i];
1285 vertices[i].Pos.Y += corner_levels[j];
1286 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1289 u16 indices[] = {0,1,2,2,3,0};
1290 // Add to mesh collector
1291 collector.append(material_water1, vertices, 4, indices, 6);
1295 Add water sources to mesh if using new style
1297 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1299 //bool top_is_water = false;
1300 bool top_is_air = false;
1301 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1302 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1303 top_is_water = true;*/
1304 if(n.d == CONTENT_AIR)
1307 /*if(top_is_water == true)
1309 if(top_is_air == false)
1312 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1313 video::SColor c(WATER_ALPHA,l,l,l);
1315 video::S3DVertex vertices[4] =
1317 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1318 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1319 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1320 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1321 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1322 pa_water1.x0(), pa_water1.y1()),
1323 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1324 pa_water1.x1(), pa_water1.y1()),
1325 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1326 pa_water1.x1(), pa_water1.y0()),
1327 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1328 pa_water1.x0(), pa_water1.y0()),
1331 for(s32 i=0; i<4; i++)
1333 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1334 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1337 u16 indices[] = {0,1,2,2,3,0};
1338 // Add to mesh collector
1339 collector.append(material_water1, vertices, 4, indices, 6);
1342 Add leaves if using new style
1344 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1346 /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
1347 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1348 video::SColor c(255,l,l,l);
1350 for(u32 j=0; j<6; j++)
1352 video::S3DVertex vertices[4] =
1354 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1355 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1356 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1357 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1358 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1359 pa_leaves1.x0(), pa_leaves1.y1()),
1360 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1361 pa_leaves1.x1(), pa_leaves1.y1()),
1362 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1363 pa_leaves1.x1(), pa_leaves1.y0()),
1364 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1365 pa_leaves1.x0(), pa_leaves1.y0()),
1370 for(u16 i=0; i<4; i++)
1371 vertices[i].Pos.rotateXZBy(0);
1375 for(u16 i=0; i<4; i++)
1376 vertices[i].Pos.rotateXZBy(180);
1380 for(u16 i=0; i<4; i++)
1381 vertices[i].Pos.rotateXZBy(-90);
1385 for(u16 i=0; i<4; i++)
1386 vertices[i].Pos.rotateXZBy(90);
1390 for(u16 i=0; i<4; i++)
1391 vertices[i].Pos.rotateYZBy(-90);
1395 for(u16 i=0; i<4; i++)
1396 vertices[i].Pos.rotateYZBy(90);
1399 for(u16 i=0; i<4; i++)
1401 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1404 u16 indices[] = {0,1,2,2,3,0};
1405 // Add to mesh collector
1406 collector.append(material_leaves1, vertices, 4, indices, 6);
1412 Add stuff from collector to mesh
1415 scene::SMesh *mesh_new = NULL;
1416 mesh_new = new scene::SMesh();
1418 collector.fillMesh(mesh_new);
1421 Do some stuff to the mesh
1424 mesh_new->recalculateBoundingBox();
1427 Delete new mesh if it is empty
1430 if(mesh_new->getMeshBufferCount() == 0)
1439 // Usually 1-700 faces and 1-7 materials
1440 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1441 <<"and uses "<<mesh_new->getMeshBufferCount()
1442 <<" materials (meshbuffers)"<<std::endl;
1445 // Use VBO for mesh (this just would set this for ever buffer)
1446 // This will lead to infinite memory usage because or irrlicht.
1447 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1450 NOTE: If that is enabled, some kind of a queue to the main
1451 thread should be made which would call irrlicht to delete
1452 the hardware buffer and then delete the mesh
1458 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1467 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
1471 is_underground(false),
1472 m_lighting_expired(true),
1473 m_day_night_differs(false),
1474 //m_not_fully_generated(false),
1481 //m_spawn_timer = -10000;
1484 m_mesh_expired = false;
1487 m_temp_mods_mutex.Init();
1491 MapBlock::~MapBlock()
1495 JMutexAutoLock lock(mesh_mutex);
1509 bool MapBlock::isValidPositionParent(v3s16 p)
1511 if(isValidPosition(p))
1516 return m_parent->isValidPosition(getPosRelative() + p);
1520 MapNode MapBlock::getNodeParent(v3s16 p)
1522 if(isValidPosition(p) == false)
1524 return m_parent->getNode(getPosRelative() + p);
1529 throw InvalidPositionException();
1530 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1534 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
1536 if(isValidPosition(p) == false)
1538 m_parent->setNode(getPosRelative() + p, n);
1543 throw InvalidPositionException();
1544 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
1548 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
1550 if(isValidPosition(p) == false)
1553 return m_parent->getNode(getPosRelative() + p);
1555 catch(InvalidPositionException &e)
1557 return MapNode(CONTENT_IGNORE);
1564 return MapNode(CONTENT_IGNORE);
1566 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1573 void MapBlock::updateMesh(u32 daynight_ratio)
1577 DEBUG: If mesh has been generated, don't generate it again
1580 JMutexAutoLock meshlock(mesh_mutex);
1587 data.fill(daynight_ratio, this);
1589 scene::SMesh *mesh_new = makeMapBlockMesh(&data);
1595 replaceMesh(mesh_new);
1600 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
1604 //scene::SMesh *mesh_old = mesh[daynight_i];
1605 //mesh[daynight_i] = mesh_new;
1607 scene::SMesh *mesh_old = mesh;
1609 setMeshExpired(false);
1611 if(mesh_old != NULL)
1613 // Remove hardware buffers of meshbuffers of mesh
1614 // NOTE: No way, this runs in a different thread and everything
1615 /*u32 c = mesh_old->getMeshBufferCount();
1616 for(u32 i=0; i<c; i++)
1618 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1621 /*dstream<<"mesh_old->getReferenceCount()="
1622 <<mesh_old->getReferenceCount()<<std::endl;
1623 u32 c = mesh_old->getMeshBufferCount();
1624 for(u32 i=0; i<c; i++)
1626 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1627 dstream<<"buf->getReferenceCount()="
1628 <<buf->getReferenceCount()<<std::endl;
1637 mesh_mutex.Unlock();
1643 Propagates sunlight down through the block.
1644 Doesn't modify nodes that are not affected by sunlight.
1646 Returns false if sunlight at bottom block is invalid
1647 Returns true if bottom block doesn't exist.
1649 If there is a block above, continues from it.
1650 If there is no block above, assumes there is sunlight, unless
1651 is_underground is set or highest node is water.
1653 At the moment, all sunlighted nodes are added to light_sources.
1654 - SUGG: This could be optimized
1656 Turns sunglighted mud into grass.
1658 if remove_light==true, sets non-sunlighted nodes black.
1660 if black_air_left!=NULL, it is set to true if non-sunlighted
1661 air is left in block.
1663 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1664 bool remove_light, bool *black_air_left,
1667 // Whether the sunlight at the top of the bottom block is valid
1668 bool block_below_is_valid = true;
1670 v3s16 pos_relative = getPosRelative();
1672 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1674 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1677 bool no_sunlight = false;
1678 bool no_top_block = false;
1679 // Check if node above block has sunlight
1681 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1682 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1687 catch(InvalidPositionException &e)
1689 no_top_block = true;
1691 // NOTE: This makes over-ground roofed places sunlighted
1692 // Assume sunlight, unless is_underground==true
1699 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1700 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1705 // NOTE: As of now, this just would make everything dark.
1707 //no_sunlight = true;
1710 #if 0 // Doesn't work; nothing gets light.
1711 bool no_sunlight = true;
1712 bool no_top_block = false;
1713 // Check if node above block has sunlight
1715 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1716 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1718 no_sunlight = false;
1721 catch(InvalidPositionException &e)
1723 no_top_block = true;
1727 /*std::cout<<"("<<x<<","<<z<<"): "
1728 <<"no_top_block="<<no_top_block
1729 <<", is_underground="<<is_underground
1730 <<", no_sunlight="<<no_sunlight
1733 s16 y = MAP_BLOCKSIZE-1;
1735 // This makes difference to diminishing in water.
1736 bool stopped_to_solid_object = false;
1738 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1743 MapNode &n = getNodeRef(pos);
1745 if(current_light == 0)
1749 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1751 // Do nothing: Sunlight is continued
1753 else if(n.light_propagates() == false)
1757 bool upper_is_air = false;
1760 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1761 upper_is_air = true;
1763 catch(InvalidPositionException &e)
1766 // Turn mud into grass
1767 if(upper_is_air && n.d == CONTENT_MUD
1768 && current_light == LIGHT_SUN)
1770 n.d = CONTENT_GRASS;
1774 // A solid object is on the way.
1775 stopped_to_solid_object = true;
1783 current_light = diminish_light(current_light);
1786 u8 old_light = n.getLight(LIGHTBANK_DAY);
1788 if(current_light > old_light || remove_light)
1790 n.setLight(LIGHTBANK_DAY, current_light);
1793 if(diminish_light(current_light) != 0)
1795 light_sources.insert(pos_relative + pos, true);
1798 if(current_light == 0 && stopped_to_solid_object)
1802 *black_air_left = true;
1807 // Whether or not the block below should see LIGHT_SUN
1808 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1811 If the block below hasn't already been marked invalid:
1813 Check if the node below the block has proper sunlight at top.
1814 If not, the block below is invalid.
1816 Ignore non-transparent nodes as they always have no light
1820 if(block_below_is_valid)
1822 MapNode n = getNodeParent(v3s16(x, -1, z));
1823 if(n.light_propagates())
1825 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1826 && sunlight_should_go_down == false)
1827 block_below_is_valid = false;
1828 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1829 && sunlight_should_go_down == true)
1830 block_below_is_valid = false;
1834 catch(InvalidPositionException &e)
1836 /*std::cout<<"InvalidBlockException for bottom block node"
1838 // Just no block below, no need to panic.
1843 return block_below_is_valid;
1847 void MapBlock::copyTo(VoxelManipulator &dst)
1849 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1850 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1852 // Copy from data to VoxelManipulator
1853 dst.copyFrom(data, data_area, v3s16(0,0,0),
1854 getPosRelative(), data_size);
1857 void MapBlock::copyFrom(VoxelManipulator &dst)
1859 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1860 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1862 // Copy from VoxelManipulator to data
1863 dst.copyTo(data, data_area, v3s16(0,0,0),
1864 getPosRelative(), data_size);
1867 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1872 m_objects.step(dtime, server, daynight_ratio);
1876 Spawn some objects at random.
1878 Use dayNightDiffed() to approximate being near ground level
1880 if(m_spawn_timer < -999)
1884 if(dayNightDiffed() == true && getObjectCount() == 0)
1886 m_spawn_timer -= dtime;
1887 if(m_spawn_timer <= 0.0)
1889 m_spawn_timer += myrand() % 300;
1892 (myrand()%(MAP_BLOCKSIZE-1))+0,
1893 (myrand()%(MAP_BLOCKSIZE-1))+0
1896 s16 y = getGroundLevel(p2d);
1900 v3s16 p(p2d.X, y+1, p2d.Y);
1902 if(getNode(p).d == CONTENT_AIR
1903 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1905 RatObject *obj = new RatObject(NULL, -1, intToFloat(p, BS));
1917 void MapBlock::updateDayNightDiff()
1921 m_day_night_differs = false;
1925 bool differs = false;
1928 Check if any lighting value differs
1930 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1932 MapNode &n = data[i];
1933 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1941 If some lighting values differ, check if the whole thing is
1942 just air. If it is, differ = false
1946 bool only_air = true;
1947 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1949 MapNode &n = data[i];
1950 if(n.d != CONTENT_AIR)
1960 // Set member variable
1961 m_day_night_differs = differs;
1964 s16 MapBlock::getGroundLevel(v2s16 p2d)
1970 s16 y = MAP_BLOCKSIZE-1;
1973 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1974 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1976 if(y == MAP_BLOCKSIZE-1)
1984 catch(InvalidPositionException &e)
1994 void MapBlock::serialize(std::ostream &os, u8 version)
1996 if(!ser_ver_supported(version))
1997 throw VersionMismatchException("ERROR: MapBlock format not supported");
2001 throw SerializationError("ERROR: Not writing dummy block.");
2004 // These have no compression
2005 if(version <= 3 || version == 5 || version == 6)
2007 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2009 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
2010 SharedBuffer<u8> dest(buflen);
2012 dest[0] = is_underground;
2013 for(u32 i=0; i<nodecount; i++)
2015 u32 s = 1 + i * MapNode::serializedLength(version);
2016 data[i].serialize(&dest[s], version);
2019 os.write((char*)*dest, dest.getSize());
2021 else if(version <= 10)
2025 Compress the materials and the params separately.
2029 os.write((char*)&is_underground, 1);
2031 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2033 // Get and compress materials
2034 SharedBuffer<u8> materialdata(nodecount);
2035 for(u32 i=0; i<nodecount; i++)
2037 materialdata[i] = data[i].d;
2039 compress(materialdata, os, version);
2041 // Get and compress lights
2042 SharedBuffer<u8> lightdata(nodecount);
2043 for(u32 i=0; i<nodecount; i++)
2045 lightdata[i] = data[i].param;
2047 compress(lightdata, os, version);
2051 // Get and compress param2
2052 SharedBuffer<u8> param2data(nodecount);
2053 for(u32 i=0; i<nodecount; i++)
2055 param2data[i] = data[i].param2;
2057 compress(param2data, os, version);
2060 // All other versions (newest)
2067 if(m_day_night_differs)
2069 if(m_lighting_expired)
2071 os.write((char*)&flags, 1);
2073 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2079 SharedBuffer<u8> databuf(nodecount*3);
2082 for(u32 i=0; i<nodecount; i++)
2084 databuf[i] = data[i].d;
2088 for(u32 i=0; i<nodecount; i++)
2090 databuf[i+nodecount] = data[i].param;
2094 for(u32 i=0; i<nodecount; i++)
2096 databuf[i+nodecount*2] = data[i].param2;
2100 Compress data to output stream
2103 compress(databuf, os, version);
2113 std::ostringstream oss(std::ios_base::binary);
2114 m_node_metadata.serialize(oss);
2115 os<<serializeString(oss.str());
2117 // This will happen if the string is longer than 65535
2118 catch(SerializationError &e)
2120 // Use an empty string
2121 os<<serializeString("");
2126 std::ostringstream oss(std::ios_base::binary);
2127 m_node_metadata.serialize(oss);
2128 compressZlib(oss.str(), os);
2129 //os<<serializeLongString(oss.str());
2135 void MapBlock::deSerialize(std::istream &is, u8 version)
2137 if(!ser_ver_supported(version))
2138 throw VersionMismatchException("ERROR: MapBlock format not supported");
2140 // These have no lighting info
2143 setLightingExpired(true);
2146 // These have no compression
2147 if(version <= 3 || version == 5 || version == 6)
2149 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2152 if(is.gcount() != 1)
2153 throw SerializationError
2154 ("MapBlock::deSerialize: no enough input data");
2155 is_underground = tmp;
2156 for(u32 i=0; i<nodecount; i++)
2158 s32 len = MapNode::serializedLength(version);
2159 SharedBuffer<u8> d(len);
2160 is.read((char*)*d, len);
2161 if(is.gcount() != len)
2162 throw SerializationError
2163 ("MapBlock::deSerialize: no enough input data");
2164 data[i].deSerialize(*d, version);
2167 else if(version <= 10)
2169 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2172 is.read((char*)&t8, 1);
2173 is_underground = t8;
2176 // Uncompress and set material data
2177 std::ostringstream os(std::ios_base::binary);
2178 decompress(is, os, version);
2179 std::string s = os.str();
2180 if(s.size() != nodecount)
2181 throw SerializationError
2182 ("MapBlock::deSerialize: invalid format");
2183 for(u32 i=0; i<s.size(); i++)
2189 // Uncompress and set param data
2190 std::ostringstream os(std::ios_base::binary);
2191 decompress(is, os, version);
2192 std::string s = os.str();
2193 if(s.size() != nodecount)
2194 throw SerializationError
2195 ("MapBlock::deSerialize: invalid format");
2196 for(u32 i=0; i<s.size(); i++)
2198 data[i].param = s[i];
2204 // Uncompress and set param2 data
2205 std::ostringstream os(std::ios_base::binary);
2206 decompress(is, os, version);
2207 std::string s = os.str();
2208 if(s.size() != nodecount)
2209 throw SerializationError
2210 ("MapBlock::deSerialize: invalid format");
2211 for(u32 i=0; i<s.size(); i++)
2213 data[i].param2 = s[i];
2217 // All other versions (newest)
2220 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2223 is.read((char*)&flags, 1);
2224 is_underground = (flags & 0x01) ? true : false;
2225 m_day_night_differs = (flags & 0x02) ? true : false;
2226 m_lighting_expired = (flags & 0x04) ? true : false;
2229 std::ostringstream os(std::ios_base::binary);
2230 decompress(is, os, version);
2231 std::string s = os.str();
2232 if(s.size() != nodecount*3)
2233 throw SerializationError
2234 ("MapBlock::deSerialize: invalid format");
2237 for(u32 i=0; i<nodecount; i++)
2242 for(u32 i=0; i<nodecount; i++)
2244 data[i].param = s[i+nodecount];
2247 for(u32 i=0; i<nodecount; i++)
2249 data[i].param2 = s[i+nodecount*2];
2261 std::string data = deSerializeString(is);
2262 std::istringstream iss(data, std::ios_base::binary);
2263 m_node_metadata.deSerialize(iss);
2267 //std::string data = deSerializeLongString(is);
2268 std::ostringstream oss(std::ios_base::binary);
2269 decompressZlib(is, oss);
2270 std::istringstream iss(oss.str(), std::ios_base::binary);
2271 m_node_metadata.deSerialize(iss);
2274 catch(SerializationError &e)
2276 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
2277 <<" while deserializing node metadata"<<std::endl;
2283 Translate nodes as specified in the translate_to fields of
2286 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2288 MapNode &n = data[i];
2290 MapNode *translate_to = content_features(n.d).translate_to;
2293 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2294 <<translate_to->d<<std::endl;