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)
136 inline video::SColor lightColor(u8 alpha, u8 light)
138 return video::SColor(alpha,light,light,light);
141 void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
142 v3s16 dir, v3f scale, v3f posRelative_f,
143 core::array<FastFace> &dest)
147 // Position is at the center of the cube.
152 // If looking towards z+, this is the face that is behind
153 // the center point, facing towards z+.
154 vertex_pos[0] = v3f(-BS/2,-BS/2,BS/2);
155 vertex_pos[1] = v3f( BS/2,-BS/2,BS/2);
156 vertex_pos[2] = v3f( BS/2, BS/2,BS/2);
157 vertex_pos[3] = v3f(-BS/2, BS/2,BS/2);
159 if(dir == v3s16(0,0,1))
161 for(u16 i=0; i<4; i++)
162 vertex_pos[i].rotateXZBy(0);
164 else if(dir == v3s16(0,0,-1))
166 for(u16 i=0; i<4; i++)
167 vertex_pos[i].rotateXZBy(180);
169 else if(dir == v3s16(1,0,0))
171 for(u16 i=0; i<4; i++)
172 vertex_pos[i].rotateXZBy(-90);
174 else if(dir == v3s16(-1,0,0))
176 for(u16 i=0; i<4; i++)
177 vertex_pos[i].rotateXZBy(90);
179 else if(dir == v3s16(0,1,0))
181 for(u16 i=0; i<4; i++)
182 vertex_pos[i].rotateYZBy(-90);
184 else if(dir == v3s16(0,-1,0))
186 for(u16 i=0; i<4; i++)
187 vertex_pos[i].rotateYZBy(90);
190 for(u16 i=0; i<4; i++)
192 vertex_pos[i].X *= scale.X;
193 vertex_pos[i].Y *= scale.Y;
194 vertex_pos[i].Z *= scale.Z;
195 vertex_pos[i] += pos + posRelative_f;
199 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
200 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
201 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
203 v3f zerovector = v3f(0,0,0);
205 u8 alpha = tile.alpha;
207 if(tile.id == TILE_WATER)
208 alpha = WATER_ALPHA;*/
210 float x0 = tile.texture.pos.X;
211 float y0 = tile.texture.pos.Y;
212 float w = tile.texture.size.X;
213 float h = tile.texture.size.Y;
215 /*video::SColor c = lightColor(alpha, li);
217 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
218 core::vector2d<f32>(x0+w*abs_scale, y0+h));
219 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
220 core::vector2d<f32>(x0, y0+h));
221 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
222 core::vector2d<f32>(x0, y0));
223 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
224 core::vector2d<f32>(x0+w*abs_scale, y0));*/
226 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0),
227 lightColor(alpha, li0),
228 core::vector2d<f32>(x0+w*abs_scale, y0+h));
229 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0),
230 lightColor(alpha, li1),
231 core::vector2d<f32>(x0, y0+h));
232 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0),
233 lightColor(alpha, li2),
234 core::vector2d<f32>(x0, y0));
235 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0),
236 lightColor(alpha, li3),
237 core::vector2d<f32>(x0+w*abs_scale, y0));
241 //f->tile = TILE_STONE;
243 dest.push_back(face);
247 Gets node tile from any place relative to block.
248 Returns TILE_NODE if doesn't exist or should not be drawn.
250 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
251 NodeModMap &temp_mods)
254 spec = mn.getTile(face_dir);
257 Check temporary modifications on this node
259 /*core::map<v3s16, NodeMod>::Node *n;
260 n = m_temp_mods.find(p);
264 struct NodeMod mod = n->getValue();*/
266 if(temp_mods.get(p, &mod))
268 if(mod.type == NODEMOD_CHANGECONTENT)
270 MapNode mn2(mod.param);
271 spec = mn2.getTile(face_dir);
273 if(mod.type == NODEMOD_CRACK)
276 Get texture id, translate it to name, append stuff to
280 // Get original texture name
281 u32 orig_id = spec.texture.id;
282 std::string orig_name = g_texturesource->getTextureName(orig_id);
284 // Create new texture name
285 std::ostringstream os;
286 os<<orig_name<<"^[crack"<<mod.param;
289 u32 new_id = g_texturesource->getTextureId(os.str());
291 /*dstream<<"MapBlock::getNodeTile(): Switching from "
292 <<orig_name<<" to "<<os.str()<<" ("
293 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
295 spec.texture = g_texturesource->getTexture(new_id);
302 u8 getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
305 Check temporary modifications on this node
307 /*core::map<v3s16, NodeMod>::Node *n;
308 n = m_temp_mods.find(p);
312 struct NodeMod mod = n->getValue();*/
314 if(temp_mods.get(p, &mod))
316 if(mod.type == NODEMOD_CHANGECONTENT)
321 if(mod.type == NODEMOD_CRACK)
324 Content doesn't change.
326 face_contents works just like it should, because
327 there should not be faces between differently cracked
330 If a semi-transparent node is cracked in front an
331 another one, it really doesn't matter whether there
332 is a cracked face drawn in between or not.
351 // Calculate lighting at the XYZ- corner of p
352 u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio)
354 u16 ambient_occlusion = 0;
357 for(u32 i=0; i<8; i++)
359 MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
360 if(content_features(n.d).param_type == CPT_LIGHT)
362 light += decode_light(n.getLightBlend(daynight_ratio));
367 if(n.d != CONTENT_IGNORE)
375 light /= light_count;
377 if(ambient_occlusion > 4)
379 ambient_occlusion -= 4;
380 light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
388 translate_dir: unit vector with only one of x, y or z
389 face_dir: unit vector with only one of x, y or z
391 void updateFastFaceRow(
400 core::array<FastFace> &dest,
401 NodeModMap &temp_mods,
402 VoxelManipulator &vmanip,
403 v3s16 blockpos_nodes,
404 bool smooth_lighting)
408 u16 continuous_tiles_count = 0;
410 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
411 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
412 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
413 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
414 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
416 for(u16 j=0; j<length; j++)
418 bool next_is_different = true;
427 // If at last position, there is nothing to compare to and
428 // the face must be drawn anyway
431 p_next = p + translate_dir;
433 n0_next = vmanip.getNodeNoEx(blockpos_nodes + p_next);
434 n1_next = vmanip.getNodeNoEx(blockpos_nodes + p_next + face_dir);
435 tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
436 tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
437 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
439 if(tile0_next == tile0
440 && tile1_next == tile1
441 && light_next == light)
443 next_is_different = false;
447 continuous_tiles_count++;
449 // This is set to true if the texture doesn't allow more tiling
450 bool end_of_texture = false;
452 If there is no texture, it can be tiled infinitely.
453 If tiled==0, it means the texture can be tiled infinitely.
454 Otherwise check tiled agains continuous_tiles_count.
456 This check has to be made for both tiles, because this is
457 a bit hackish and we know which one we're using only when
458 the decision to make the faces is made.
460 if(tile0.texture.atlas != NULL && tile0.texture.tiled != 0)
462 if(tile0.texture.tiled <= continuous_tiles_count)
463 end_of_texture = true;
465 if(tile1.texture.atlas != NULL && tile1.texture.tiled != 0)
467 if(tile1.texture.tiled <= continuous_tiles_count)
468 end_of_texture = true;
471 // Do this to disable tiling textures
472 //end_of_texture = true; //DEBUG
474 // Disable tiling of textures if smooth lighting is used
476 end_of_texture = true;
478 if(next_is_different || end_of_texture)
481 Create a face if there should be one
483 //u8 mf = face_contents(tile0, tile1);
485 u8 content0 = getNodeContent(p, n0, temp_mods);
486 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
487 u8 mf = face_contents(content0, content1);
491 // Floating point conversion of the position vector
492 v3f pf(p.X, p.Y, p.Z);
493 // Center point of face (kind of)
494 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
496 u8 li0=255, li1=255, li2=255, li3=255;
499 v3s16 p_first = p - (continuous_tiles_count-1) * translate_dir;
501 v3s16 p_map = blockpos_nodes + p;
502 v3s16 p_map_first = p_first + blockpos_nodes;
504 if(translate_dir.X != 0)
506 scale.X = continuous_tiles_count;
508 if(translate_dir.Y != 0)
510 scale.Y = continuous_tiles_count;
512 if(translate_dir.Z != 0)
514 scale.Z = continuous_tiles_count;
517 if(smooth_lighting == false)
518 li0 = li1 = li2 = li3 = decode_light(light);
520 // If node at sp (tile0) is more solid
525 if(face_dir == v3s16(0,0,1))
527 // Going along X+, faces in Z+
528 li0 = getSmoothLight(p_map_first+v3s16(0,0,1),
529 vmanip, daynight_ratio);
530 li1 = getSmoothLight(p_map+v3s16(1,0,1),
531 vmanip, daynight_ratio);
532 li2 = getSmoothLight(p_map+v3s16(1,1,1),
533 vmanip, daynight_ratio);
534 li3 = getSmoothLight(p_map_first+v3s16(0,1,1),
535 vmanip, daynight_ratio);
537 else if(face_dir == v3s16(0,1,0))
539 // Going along X+, faces in Y+
540 li0 = getSmoothLight(p_map_first+v3s16(0,1,1),
541 vmanip, daynight_ratio);
542 li1 = getSmoothLight(p_map+v3s16(1,1,1),
543 vmanip, daynight_ratio);
544 li2 = getSmoothLight(p_map+v3s16(1,1,0),
545 vmanip, daynight_ratio);
546 li3 = getSmoothLight(p_map_first+v3s16(0,1,0),
547 vmanip, daynight_ratio);
549 else if(face_dir == v3s16(1,0,0))
551 // Going along Z+, faces in X+
552 li0 = getSmoothLight(p_map_first+v3s16(1,0,1),
553 vmanip, daynight_ratio);
554 li1 = getSmoothLight(p_map+v3s16(1,0,0),
555 vmanip, daynight_ratio);
556 li2 = getSmoothLight(p_map+v3s16(1,1,0),
557 vmanip, daynight_ratio);
558 li3 = getSmoothLight(p_map_first+v3s16(1,1,1),
559 vmanip, daynight_ratio);
564 makeFastFace(tile0, li0, li1, li2, li3,
566 posRelative_f, dest);
568 // If node at sp is less solid (mf == 2)
573 // Offset to the actual solid block
575 p_map_first += face_dir;
577 if(face_dir == v3s16(0,0,1))
579 // Going along X+, faces in Z-
580 li0 = getSmoothLight(p_map+v3s16(1,0,0),
581 vmanip, daynight_ratio);
582 li1 = getSmoothLight(p_map_first+v3s16(0,0,0),
583 vmanip, daynight_ratio);
584 li2 = getSmoothLight(p_map_first+v3s16(0,1,0),
585 vmanip, daynight_ratio);
586 li3 = getSmoothLight(p_map+v3s16(1,1,0),
587 vmanip, daynight_ratio);
589 else if(face_dir == v3s16(0,1,0))
591 // Going along X+, faces in Y-
592 li0 = getSmoothLight(p_map_first+v3s16(0,0,0),
593 vmanip, daynight_ratio);
594 li1 = getSmoothLight(p_map+v3s16(1,0,0),
595 vmanip, daynight_ratio);
596 li2 = getSmoothLight(p_map+v3s16(1,0,1),
597 vmanip, daynight_ratio);
598 li3 = getSmoothLight(p_map_first+v3s16(0,0,1),
599 vmanip, daynight_ratio);
601 else if(face_dir == v3s16(1,0,0))
603 // Going along Z+, faces in X-
604 li0 = getSmoothLight(p_map_first+v3s16(0,0,0),
605 vmanip, daynight_ratio);
606 li1 = getSmoothLight(p_map+v3s16(0,0,1),
607 vmanip, daynight_ratio);
608 li2 = getSmoothLight(p_map+v3s16(0,1,1),
609 vmanip, daynight_ratio);
610 li3 = getSmoothLight(p_map_first+v3s16(0,1,0),
611 vmanip, daynight_ratio);
616 makeFastFace(tile1, li0, li1, li2, li3,
617 sp+face_dir_f, -face_dir, scale,
618 posRelative_f, dest);
622 continuous_tiles_count = 0;
635 This is used because CMeshBuffer::append() is very slow
639 video::SMaterial material;
640 core::array<u16> indices;
641 core::array<video::S3DVertex> vertices;
648 video::SMaterial material,
649 const video::S3DVertex* const vertices,
651 const u16* const indices,
655 PreMeshBuffer *p = NULL;
656 for(u32 i=0; i<m_prebuffers.size(); i++)
658 PreMeshBuffer &pp = m_prebuffers[i];
659 if(pp.material != material)
669 pp.material = material;
670 m_prebuffers.push_back(pp);
671 p = &m_prebuffers[m_prebuffers.size()-1];
674 u32 vertex_count = p->vertices.size();
675 for(u32 i=0; i<numIndices; i++)
677 u32 j = indices[i] + vertex_count;
680 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
681 // NOTE: Fix is to just add an another MeshBuffer
683 p->indices.push_back(j);
685 for(u32 i=0; i<numVertices; i++)
687 p->vertices.push_back(vertices[i]);
691 void fillMesh(scene::SMesh *mesh)
693 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
694 <<" meshbuffers"<<std::endl;*/
695 for(u32 i=0; i<m_prebuffers.size(); i++)
697 PreMeshBuffer &p = m_prebuffers[i];
699 /*dstream<<"p.vertices.size()="<<p.vertices.size()
700 <<", p.indices.size()="<<p.indices.size()
705 // This is a "Standard MeshBuffer",
706 // it's a typedeffed CMeshBuffer<video::S3DVertex>
707 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
709 buf->Material = p.material;
710 //((scene::SMeshBuffer*)buf)->Material = p.material;
712 //buf->setHardwareMappingHint(scene::EHM_STATIC);
714 mesh->addMeshBuffer(buf);
718 buf->append(p.vertices.pointer(), p.vertices.size(),
719 p.indices.pointer(), p.indices.size());
724 core::array<PreMeshBuffer> m_prebuffers;
727 scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
729 // 4-21ms for MAP_BLOCKSIZE=16
730 // 24-155ms for MAP_BLOCKSIZE=32
731 //TimeTaker timer1("makeMapBlockMesh()");
733 core::array<FastFace> fastfaces_new;
735 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
737 // floating point conversion
738 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
743 bool new_style_water = g_settings.getBool("new_style_water");
744 bool new_style_leaves = g_settings.getBool("new_style_leaves");
745 bool smooth_lighting = g_settings.getBool("smooth_lighting");
747 float node_water_level = 1.0;
749 node_water_level = 0.85;
752 We are including the faces of the trailing edges of the block.
753 This means that when something changes, the caller must
754 also update the meshes of the blocks at the leading edges.
756 NOTE: This is the slowest part of this method.
760 // 4-23ms for MAP_BLOCKSIZE=16
761 //TimeTaker timer2("updateMesh() collect");
764 Go through every y,z and get top(y+) faces in rows of x+
766 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
767 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
768 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
769 v3s16(0,y,z), MAP_BLOCKSIZE,
772 v3s16(0,1,0), //face dir
782 Go through every x,y and get right(x+) faces in rows of z+
784 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
785 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
786 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
787 v3s16(x,y,0), MAP_BLOCKSIZE,
800 Go through every y,z and get back(z+) faces in rows of x+
802 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
803 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
804 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
805 v3s16(0,y,z), MAP_BLOCKSIZE,
822 Convert FastFaces to SMesh
825 MeshCollector collector;
827 if(fastfaces_new.size() > 0)
829 // avg 0ms (100ms spikes when loading textures the first time)
830 //TimeTaker timer2("updateMesh() mesh building");
832 video::SMaterial material;
833 material.setFlag(video::EMF_LIGHTING, false);
834 material.setFlag(video::EMF_BILINEAR_FILTER, false);
835 material.setFlag(video::EMF_FOG_ENABLE, true);
836 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
837 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
839 for(u32 i=0; i<fastfaces_new.size(); i++)
841 FastFace &f = fastfaces_new[i];
843 const u16 indices[] = {0,1,2,2,3,0};
845 video::ITexture *texture = f.tile.texture.atlas;
849 material.setTexture(0, texture);
851 f.tile.applyMaterialOptions(material);
853 collector.append(material, f.vertices, 4, indices, 6);
858 Add special graphics:
864 //TimeTaker timer2("updateMesh() adding special stuff");
866 // Flowing water material
867 video::SMaterial material_water1;
868 material_water1.setFlag(video::EMF_LIGHTING, false);
869 material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
870 material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
871 material_water1.setFlag(video::EMF_FOG_ENABLE, true);
872 material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
873 AtlasPointer pa_water1 = g_texturesource->getTexture(
874 g_texturesource->getTextureId("water.png"));
875 material_water1.setTexture(0, pa_water1.atlas);
877 // New-style leaves material
878 video::SMaterial material_leaves1;
879 material_leaves1.setFlag(video::EMF_LIGHTING, false);
880 //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
881 material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
882 material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
883 material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
884 AtlasPointer pa_leaves1 = g_texturesource->getTexture(
885 g_texturesource->getTextureId("leaves.png"));
886 material_leaves1.setTexture(0, pa_leaves1.atlas);
888 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
889 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
890 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
894 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
899 if(n.d == CONTENT_TORCH)
901 video::SColor c(255,255,255,255);
903 // Wall at X+ of node
904 video::S3DVertex vertices[4] =
906 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
907 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
908 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
909 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
912 v3s16 dir = unpackDir(n.dir);
914 for(s32 i=0; i<4; i++)
916 if(dir == v3s16(1,0,0))
917 vertices[i].Pos.rotateXZBy(0);
918 if(dir == v3s16(-1,0,0))
919 vertices[i].Pos.rotateXZBy(180);
920 if(dir == v3s16(0,0,1))
921 vertices[i].Pos.rotateXZBy(90);
922 if(dir == v3s16(0,0,-1))
923 vertices[i].Pos.rotateXZBy(-90);
924 if(dir == v3s16(0,-1,0))
925 vertices[i].Pos.rotateXZBy(45);
926 if(dir == v3s16(0,1,0))
927 vertices[i].Pos.rotateXZBy(-45);
929 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
933 video::SMaterial material;
934 material.setFlag(video::EMF_LIGHTING, false);
935 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
936 material.setFlag(video::EMF_BILINEAR_FILTER, false);
937 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
938 material.MaterialType
939 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
941 if(dir == v3s16(0,-1,0))
942 material.setTexture(0,
943 g_texturesource->getTextureRaw("torch_on_floor.png"));
944 else if(dir == v3s16(0,1,0))
945 material.setTexture(0,
946 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
947 // For backwards compatibility
948 else if(dir == v3s16(0,0,0))
949 material.setTexture(0,
950 g_texturesource->getTextureRaw("torch_on_floor.png"));
952 material.setTexture(0,
953 g_texturesource->getTextureRaw("torch.png"));
955 u16 indices[] = {0,1,2,2,3,0};
956 // Add to mesh collector
957 collector.append(material, vertices, 4, indices, 6);
962 if(n.d == CONTENT_SIGN_WALL)
964 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
965 video::SColor c(255,l,l,l);
967 float d = (float)BS/16;
968 // Wall at X+ of node
969 video::S3DVertex vertices[4] =
971 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
972 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
973 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
974 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
977 v3s16 dir = unpackDir(n.dir);
979 for(s32 i=0; i<4; i++)
981 if(dir == v3s16(1,0,0))
982 vertices[i].Pos.rotateXZBy(0);
983 if(dir == v3s16(-1,0,0))
984 vertices[i].Pos.rotateXZBy(180);
985 if(dir == v3s16(0,0,1))
986 vertices[i].Pos.rotateXZBy(90);
987 if(dir == v3s16(0,0,-1))
988 vertices[i].Pos.rotateXZBy(-90);
989 if(dir == v3s16(0,-1,0))
990 vertices[i].Pos.rotateXYBy(-90);
991 if(dir == v3s16(0,1,0))
992 vertices[i].Pos.rotateXYBy(90);
994 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
998 video::SMaterial material;
999 material.setFlag(video::EMF_LIGHTING, false);
1000 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
1001 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1002 material.setFlag(video::EMF_FOG_ENABLE, true);
1003 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1004 material.MaterialType
1005 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1007 material.setTexture(0,
1008 g_texturesource->getTextureRaw("sign_wall.png"));
1010 u16 indices[] = {0,1,2,2,3,0};
1011 // Add to mesh collector
1012 collector.append(material, vertices, 4, indices, 6);
1015 Add flowing water to mesh
1017 else if(n.d == CONTENT_WATER)
1019 bool top_is_water = false;
1020 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1021 if(ntop.d == CONTENT_WATER || ntop.d == CONTENT_WATERSOURCE)
1022 top_is_water = true;
1025 // Use the light of the node on top if possible
1026 if(content_features(ntop.d).param_type == CPT_LIGHT)
1027 l = decode_light(ntop.getLightBlend(data->m_daynight_ratio));
1028 // Otherwise use the light of this node (the water)
1030 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1031 video::SColor c(WATER_ALPHA,l,l,l);
1033 // Neighbor water levels (key = relative position)
1034 // Includes current node
1035 core::map<v3s16, f32> neighbor_levels;
1036 core::map<v3s16, u8> neighbor_contents;
1037 core::map<v3s16, u8> neighbor_flags;
1038 const u8 neighborflag_top_is_water = 0x01;
1039 v3s16 neighbor_dirs[9] = {
1050 for(u32 i=0; i<9; i++)
1052 u8 content = CONTENT_AIR;
1053 float level = -0.5 * BS;
1056 v3s16 p2 = p + neighbor_dirs[i];
1057 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1058 if(n2.d != CONTENT_IGNORE)
1062 if(n2.d == CONTENT_WATERSOURCE)
1063 level = (-0.5+node_water_level) * BS;
1064 else if(n2.d == CONTENT_WATER)
1065 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
1066 * node_water_level) * BS;
1068 // Check node above neighbor.
1069 // NOTE: This doesn't get executed if neighbor
1072 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1073 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
1074 flags |= neighborflag_top_is_water;
1077 neighbor_levels.insert(neighbor_dirs[i], level);
1078 neighbor_contents.insert(neighbor_dirs[i], content);
1079 neighbor_flags.insert(neighbor_dirs[i], flags);
1082 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
1083 //float water_level = neighbor_levels[v3s16(0,0,0)];
1085 // Corner heights (average between four waters)
1086 f32 corner_levels[4];
1088 v3s16 halfdirs[4] = {
1094 for(u32 i=0; i<4; i++)
1096 v3s16 cornerdir = halfdirs[i];
1097 float cornerlevel = 0;
1098 u32 valid_count = 0;
1099 for(u32 j=0; j<4; j++)
1101 v3s16 neighbordir = cornerdir - halfdirs[j];
1102 u8 content = neighbor_contents[neighbordir];
1103 // Special case for source nodes
1104 if(content == CONTENT_WATERSOURCE)
1106 cornerlevel = (-0.5+node_water_level)*BS;
1110 else if(content == CONTENT_WATER)
1112 cornerlevel += neighbor_levels[neighbordir];
1115 else if(content == CONTENT_AIR)
1117 cornerlevel += -0.5*BS;
1122 cornerlevel /= valid_count;
1123 corner_levels[i] = cornerlevel;
1130 v3s16 side_dirs[4] = {
1136 s16 side_corners[4][2] = {
1142 for(u32 i=0; i<4; i++)
1144 v3s16 dir = side_dirs[i];
1147 If our topside is water and neighbor's topside
1148 is water, don't draw side face
1151 neighbor_flags[dir] & neighborflag_top_is_water)
1154 u8 neighbor_content = neighbor_contents[dir];
1156 // Don't draw face if neighbor is not air or water
1157 if(neighbor_content != CONTENT_AIR
1158 && neighbor_content != CONTENT_WATER)
1161 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1163 // Don't draw any faces if neighbor is water and top is water
1164 if(neighbor_is_water == true && top_is_water == false)
1167 video::S3DVertex vertices[4] =
1169 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1170 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1171 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1172 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1173 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1174 pa_water1.x0(), pa_water1.y1()),
1175 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1176 pa_water1.x1(), pa_water1.y1()),
1177 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1178 pa_water1.x1(), pa_water1.y0()),
1179 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1180 pa_water1.x0(), pa_water1.y0()),
1184 If our topside is water, set upper border of face
1185 at upper border of node
1189 vertices[2].Pos.Y = 0.5*BS;
1190 vertices[3].Pos.Y = 0.5*BS;
1193 Otherwise upper position of face is corner levels
1197 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1198 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1202 If neighbor is water, lower border of face is corner
1205 if(neighbor_is_water)
1207 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1208 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1211 If neighbor is not water, lower border of face is
1212 lower border of node
1216 vertices[0].Pos.Y = -0.5*BS;
1217 vertices[1].Pos.Y = -0.5*BS;
1220 for(s32 j=0; j<4; j++)
1222 if(dir == v3s16(0,0,1))
1223 vertices[j].Pos.rotateXZBy(0);
1224 if(dir == v3s16(0,0,-1))
1225 vertices[j].Pos.rotateXZBy(180);
1226 if(dir == v3s16(-1,0,0))
1227 vertices[j].Pos.rotateXZBy(90);
1228 if(dir == v3s16(1,0,-0))
1229 vertices[j].Pos.rotateXZBy(-90);
1231 vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
1234 u16 indices[] = {0,1,2,2,3,0};
1235 // Add to mesh collector
1236 collector.append(material_water1, vertices, 4, indices, 6);
1240 Generate top side, if appropriate
1243 if(top_is_water == false)
1245 video::S3DVertex vertices[4] =
1247 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1248 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1249 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1250 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1251 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1252 pa_water1.x0(), pa_water1.y1()),
1253 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1254 pa_water1.x1(), pa_water1.y1()),
1255 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1256 pa_water1.x1(), pa_water1.y0()),
1257 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1258 pa_water1.x0(), pa_water1.y0()),
1261 // This fixes a strange bug
1262 s32 corner_resolve[4] = {3,2,1,0};
1264 for(s32 i=0; i<4; i++)
1266 //vertices[i].Pos.Y += water_level;
1267 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1268 s32 j = corner_resolve[i];
1269 vertices[i].Pos.Y += corner_levels[j];
1270 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1273 u16 indices[] = {0,1,2,2,3,0};
1274 // Add to mesh collector
1275 collector.append(material_water1, vertices, 4, indices, 6);
1279 Add water sources to mesh if using new style
1281 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1283 //bool top_is_water = false;
1284 bool top_is_air = false;
1285 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1286 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1287 top_is_water = true;*/
1288 if(n.d == CONTENT_AIR)
1291 /*if(top_is_water == true)
1293 if(top_is_air == false)
1296 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1297 video::SColor c(WATER_ALPHA,l,l,l);
1299 video::S3DVertex vertices[4] =
1301 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1302 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1303 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1304 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1305 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1306 pa_water1.x0(), pa_water1.y1()),
1307 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1308 pa_water1.x1(), pa_water1.y1()),
1309 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1310 pa_water1.x1(), pa_water1.y0()),
1311 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1312 pa_water1.x0(), pa_water1.y0()),
1315 for(s32 i=0; i<4; i++)
1317 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1318 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1321 u16 indices[] = {0,1,2,2,3,0};
1322 // Add to mesh collector
1323 collector.append(material_water1, vertices, 4, indices, 6);
1326 Add leaves if using new style
1328 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1330 /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
1331 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1332 video::SColor c(255,l,l,l);
1334 for(u32 j=0; j<6; j++)
1336 video::S3DVertex vertices[4] =
1338 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1339 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1340 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1341 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1342 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1343 pa_leaves1.x0(), pa_leaves1.y1()),
1344 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1345 pa_leaves1.x1(), pa_leaves1.y1()),
1346 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1347 pa_leaves1.x1(), pa_leaves1.y0()),
1348 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1349 pa_leaves1.x0(), pa_leaves1.y0()),
1354 for(u16 i=0; i<4; i++)
1355 vertices[i].Pos.rotateXZBy(0);
1359 for(u16 i=0; i<4; i++)
1360 vertices[i].Pos.rotateXZBy(180);
1364 for(u16 i=0; i<4; i++)
1365 vertices[i].Pos.rotateXZBy(-90);
1369 for(u16 i=0; i<4; i++)
1370 vertices[i].Pos.rotateXZBy(90);
1374 for(u16 i=0; i<4; i++)
1375 vertices[i].Pos.rotateYZBy(-90);
1379 for(u16 i=0; i<4; i++)
1380 vertices[i].Pos.rotateYZBy(90);
1383 for(u16 i=0; i<4; i++)
1385 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1388 u16 indices[] = {0,1,2,2,3,0};
1389 // Add to mesh collector
1390 collector.append(material_leaves1, vertices, 4, indices, 6);
1396 Add stuff from collector to mesh
1399 scene::SMesh *mesh_new = NULL;
1400 mesh_new = new scene::SMesh();
1402 collector.fillMesh(mesh_new);
1405 Do some stuff to the mesh
1408 mesh_new->recalculateBoundingBox();
1411 Delete new mesh if it is empty
1414 if(mesh_new->getMeshBufferCount() == 0)
1423 // Usually 1-700 faces and 1-7 materials
1424 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1425 <<"and uses "<<mesh_new->getMeshBufferCount()
1426 <<" materials (meshbuffers)"<<std::endl;
1429 // Use VBO for mesh (this just would set this for ever buffer)
1430 // This will lead to infinite memory usage because or irrlicht.
1431 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1434 NOTE: If that is enabled, some kind of a queue to the main
1435 thread should be made which would call irrlicht to delete
1436 the hardware buffer and then delete the mesh
1442 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1451 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
1455 is_underground(false),
1456 m_lighting_expired(true),
1457 m_day_night_differs(false),
1458 //m_not_fully_generated(false),
1465 //m_spawn_timer = -10000;
1468 m_mesh_expired = false;
1471 m_temp_mods_mutex.Init();
1475 MapBlock::~MapBlock()
1479 JMutexAutoLock lock(mesh_mutex);
1493 bool MapBlock::isValidPositionParent(v3s16 p)
1495 if(isValidPosition(p))
1500 return m_parent->isValidPosition(getPosRelative() + p);
1504 MapNode MapBlock::getNodeParent(v3s16 p)
1506 if(isValidPosition(p) == false)
1508 return m_parent->getNode(getPosRelative() + p);
1513 throw InvalidPositionException();
1514 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1518 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
1520 if(isValidPosition(p) == false)
1522 m_parent->setNode(getPosRelative() + p, n);
1527 throw InvalidPositionException();
1528 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
1532 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
1534 if(isValidPosition(p) == false)
1537 return m_parent->getNode(getPosRelative() + p);
1539 catch(InvalidPositionException &e)
1541 return MapNode(CONTENT_IGNORE);
1548 return MapNode(CONTENT_IGNORE);
1550 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1557 void MapBlock::updateMesh(u32 daynight_ratio)
1561 DEBUG: If mesh has been generated, don't generate it again
1564 JMutexAutoLock meshlock(mesh_mutex);
1571 data.fill(daynight_ratio, this);
1573 scene::SMesh *mesh_new = makeMapBlockMesh(&data);
1579 replaceMesh(mesh_new);
1584 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
1588 //scene::SMesh *mesh_old = mesh[daynight_i];
1589 //mesh[daynight_i] = mesh_new;
1591 scene::SMesh *mesh_old = mesh;
1593 setMeshExpired(false);
1595 if(mesh_old != NULL)
1597 // Remove hardware buffers of meshbuffers of mesh
1598 // NOTE: No way, this runs in a different thread and everything
1599 /*u32 c = mesh_old->getMeshBufferCount();
1600 for(u32 i=0; i<c; i++)
1602 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1605 /*dstream<<"mesh_old->getReferenceCount()="
1606 <<mesh_old->getReferenceCount()<<std::endl;
1607 u32 c = mesh_old->getMeshBufferCount();
1608 for(u32 i=0; i<c; i++)
1610 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1611 dstream<<"buf->getReferenceCount()="
1612 <<buf->getReferenceCount()<<std::endl;
1621 mesh_mutex.Unlock();
1627 Propagates sunlight down through the block.
1628 Doesn't modify nodes that are not affected by sunlight.
1630 Returns false if sunlight at bottom block is invalid
1631 Returns true if bottom block doesn't exist.
1633 If there is a block above, continues from it.
1634 If there is no block above, assumes there is sunlight, unless
1635 is_underground is set or highest node is water.
1637 At the moment, all sunlighted nodes are added to light_sources.
1638 - SUGG: This could be optimized
1640 Turns sunglighted mud into grass.
1642 if remove_light==true, sets non-sunlighted nodes black.
1644 if black_air_left!=NULL, it is set to true if non-sunlighted
1645 air is left in block.
1647 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1648 bool remove_light, bool *black_air_left,
1651 // Whether the sunlight at the top of the bottom block is valid
1652 bool block_below_is_valid = true;
1654 v3s16 pos_relative = getPosRelative();
1656 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1658 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1661 bool no_sunlight = false;
1662 bool no_top_block = false;
1663 // Check if node above block has sunlight
1665 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1666 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1671 catch(InvalidPositionException &e)
1673 no_top_block = true;
1675 // NOTE: This makes over-ground roofed places sunlighted
1676 // Assume sunlight, unless is_underground==true
1683 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1684 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1689 // NOTE: As of now, this just would make everything dark.
1691 //no_sunlight = true;
1694 #if 0 // Doesn't work; nothing gets light.
1695 bool no_sunlight = true;
1696 bool no_top_block = false;
1697 // Check if node above block has sunlight
1699 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1700 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1702 no_sunlight = false;
1705 catch(InvalidPositionException &e)
1707 no_top_block = true;
1711 /*std::cout<<"("<<x<<","<<z<<"): "
1712 <<"no_top_block="<<no_top_block
1713 <<", is_underground="<<is_underground
1714 <<", no_sunlight="<<no_sunlight
1717 s16 y = MAP_BLOCKSIZE-1;
1719 // This makes difference to diminishing in water.
1720 bool stopped_to_solid_object = false;
1722 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1727 MapNode &n = getNodeRef(pos);
1729 if(current_light == 0)
1733 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1735 // Do nothing: Sunlight is continued
1737 else if(n.light_propagates() == false)
1741 bool upper_is_air = false;
1744 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1745 upper_is_air = true;
1747 catch(InvalidPositionException &e)
1750 // Turn mud into grass
1751 if(upper_is_air && n.d == CONTENT_MUD
1752 && current_light == LIGHT_SUN)
1754 n.d = CONTENT_GRASS;
1758 // A solid object is on the way.
1759 stopped_to_solid_object = true;
1767 current_light = diminish_light(current_light);
1770 u8 old_light = n.getLight(LIGHTBANK_DAY);
1772 if(current_light > old_light || remove_light)
1774 n.setLight(LIGHTBANK_DAY, current_light);
1777 if(diminish_light(current_light) != 0)
1779 light_sources.insert(pos_relative + pos, true);
1782 if(current_light == 0 && stopped_to_solid_object)
1786 *black_air_left = true;
1791 // Whether or not the block below should see LIGHT_SUN
1792 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1795 If the block below hasn't already been marked invalid:
1797 Check if the node below the block has proper sunlight at top.
1798 If not, the block below is invalid.
1800 Ignore non-transparent nodes as they always have no light
1804 if(block_below_is_valid)
1806 MapNode n = getNodeParent(v3s16(x, -1, z));
1807 if(n.light_propagates())
1809 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1810 && sunlight_should_go_down == false)
1811 block_below_is_valid = false;
1812 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1813 && sunlight_should_go_down == true)
1814 block_below_is_valid = false;
1818 catch(InvalidPositionException &e)
1820 /*std::cout<<"InvalidBlockException for bottom block node"
1822 // Just no block below, no need to panic.
1827 return block_below_is_valid;
1831 void MapBlock::copyTo(VoxelManipulator &dst)
1833 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1834 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1836 // Copy from data to VoxelManipulator
1837 dst.copyFrom(data, data_area, v3s16(0,0,0),
1838 getPosRelative(), data_size);
1841 void MapBlock::copyFrom(VoxelManipulator &dst)
1843 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1844 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1846 // Copy from VoxelManipulator to data
1847 dst.copyTo(data, data_area, v3s16(0,0,0),
1848 getPosRelative(), data_size);
1851 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1856 m_objects.step(dtime, server, daynight_ratio);
1860 Spawn some objects at random.
1862 Use dayNightDiffed() to approximate being near ground level
1864 if(m_spawn_timer < -999)
1868 if(dayNightDiffed() == true && getObjectCount() == 0)
1870 m_spawn_timer -= dtime;
1871 if(m_spawn_timer <= 0.0)
1873 m_spawn_timer += myrand() % 300;
1876 (myrand()%(MAP_BLOCKSIZE-1))+0,
1877 (myrand()%(MAP_BLOCKSIZE-1))+0
1880 s16 y = getGroundLevel(p2d);
1884 v3s16 p(p2d.X, y+1, p2d.Y);
1886 if(getNode(p).d == CONTENT_AIR
1887 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1889 RatObject *obj = new RatObject(NULL, -1, intToFloat(p, BS));
1901 void MapBlock::updateDayNightDiff()
1905 m_day_night_differs = false;
1909 bool differs = false;
1912 Check if any lighting value differs
1914 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1916 MapNode &n = data[i];
1917 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1925 If some lighting values differ, check if the whole thing is
1926 just air. If it is, differ = false
1930 bool only_air = true;
1931 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1933 MapNode &n = data[i];
1934 if(n.d != CONTENT_AIR)
1944 // Set member variable
1945 m_day_night_differs = differs;
1948 s16 MapBlock::getGroundLevel(v2s16 p2d)
1954 s16 y = MAP_BLOCKSIZE-1;
1957 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1958 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1960 if(y == MAP_BLOCKSIZE-1)
1968 catch(InvalidPositionException &e)
1978 void MapBlock::serialize(std::ostream &os, u8 version)
1980 if(!ser_ver_supported(version))
1981 throw VersionMismatchException("ERROR: MapBlock format not supported");
1985 throw SerializationError("ERROR: Not writing dummy block.");
1988 // These have no compression
1989 if(version <= 3 || version == 5 || version == 6)
1991 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1993 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1994 SharedBuffer<u8> dest(buflen);
1996 dest[0] = is_underground;
1997 for(u32 i=0; i<nodecount; i++)
1999 u32 s = 1 + i * MapNode::serializedLength(version);
2000 data[i].serialize(&dest[s], version);
2003 os.write((char*)*dest, dest.getSize());
2005 else if(version <= 10)
2009 Compress the materials and the params separately.
2013 os.write((char*)&is_underground, 1);
2015 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2017 // Get and compress materials
2018 SharedBuffer<u8> materialdata(nodecount);
2019 for(u32 i=0; i<nodecount; i++)
2021 materialdata[i] = data[i].d;
2023 compress(materialdata, os, version);
2025 // Get and compress lights
2026 SharedBuffer<u8> lightdata(nodecount);
2027 for(u32 i=0; i<nodecount; i++)
2029 lightdata[i] = data[i].param;
2031 compress(lightdata, os, version);
2035 // Get and compress param2
2036 SharedBuffer<u8> param2data(nodecount);
2037 for(u32 i=0; i<nodecount; i++)
2039 param2data[i] = data[i].param2;
2041 compress(param2data, os, version);
2044 // All other versions (newest)
2051 if(m_day_night_differs)
2053 if(m_lighting_expired)
2055 os.write((char*)&flags, 1);
2057 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2063 SharedBuffer<u8> databuf(nodecount*3);
2066 for(u32 i=0; i<nodecount; i++)
2068 databuf[i] = data[i].d;
2072 for(u32 i=0; i<nodecount; i++)
2074 databuf[i+nodecount] = data[i].param;
2078 for(u32 i=0; i<nodecount; i++)
2080 databuf[i+nodecount*2] = data[i].param2;
2084 Compress data to output stream
2087 compress(databuf, os, version);
2096 std::ostringstream oss(std::ios_base::binary);
2097 m_node_metadata.serialize(oss);
2098 os<<serializeString(oss.str());
2102 std::ostringstream oss(std::ios_base::binary);
2103 m_node_metadata.serialize(oss);
2104 compressZlib(oss.str(), os);
2105 //os<<serializeLongString(oss.str());
2111 void MapBlock::deSerialize(std::istream &is, u8 version)
2113 if(!ser_ver_supported(version))
2114 throw VersionMismatchException("ERROR: MapBlock format not supported");
2116 // These have no lighting info
2119 setLightingExpired(true);
2122 // These have no compression
2123 if(version <= 3 || version == 5 || version == 6)
2125 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2128 if(is.gcount() != 1)
2129 throw SerializationError
2130 ("MapBlock::deSerialize: no enough input data");
2131 is_underground = tmp;
2132 for(u32 i=0; i<nodecount; i++)
2134 s32 len = MapNode::serializedLength(version);
2135 SharedBuffer<u8> d(len);
2136 is.read((char*)*d, len);
2137 if(is.gcount() != len)
2138 throw SerializationError
2139 ("MapBlock::deSerialize: no enough input data");
2140 data[i].deSerialize(*d, version);
2143 else if(version <= 10)
2145 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2148 is.read((char*)&t8, 1);
2149 is_underground = t8;
2152 // Uncompress and set material data
2153 std::ostringstream os(std::ios_base::binary);
2154 decompress(is, os, version);
2155 std::string s = os.str();
2156 if(s.size() != nodecount)
2157 throw SerializationError
2158 ("MapBlock::deSerialize: invalid format");
2159 for(u32 i=0; i<s.size(); i++)
2165 // Uncompress and set param data
2166 std::ostringstream os(std::ios_base::binary);
2167 decompress(is, os, version);
2168 std::string s = os.str();
2169 if(s.size() != nodecount)
2170 throw SerializationError
2171 ("MapBlock::deSerialize: invalid format");
2172 for(u32 i=0; i<s.size(); i++)
2174 data[i].param = s[i];
2180 // Uncompress and set param2 data
2181 std::ostringstream os(std::ios_base::binary);
2182 decompress(is, os, version);
2183 std::string s = os.str();
2184 if(s.size() != nodecount)
2185 throw SerializationError
2186 ("MapBlock::deSerialize: invalid format");
2187 for(u32 i=0; i<s.size(); i++)
2189 data[i].param2 = s[i];
2193 // All other versions (newest)
2196 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2199 is.read((char*)&flags, 1);
2200 is_underground = (flags & 0x01) ? true : false;
2201 m_day_night_differs = (flags & 0x02) ? true : false;
2202 m_lighting_expired = (flags & 0x04) ? true : false;
2205 std::ostringstream os(std::ios_base::binary);
2206 decompress(is, os, version);
2207 std::string s = os.str();
2208 if(s.size() != nodecount*3)
2209 throw SerializationError
2210 ("MapBlock::deSerialize: invalid format");
2213 for(u32 i=0; i<nodecount; i++)
2218 for(u32 i=0; i<nodecount; i++)
2220 data[i].param = s[i+nodecount];
2223 for(u32 i=0; i<nodecount; i++)
2225 data[i].param2 = s[i+nodecount*2];
2237 std::string data = deSerializeString(is);
2238 std::istringstream iss(data, std::ios_base::binary);
2239 m_node_metadata.deSerialize(iss);
2243 //std::string data = deSerializeLongString(is);
2244 std::ostringstream oss(std::ios_base::binary);
2245 decompressZlib(is, oss);
2246 std::istringstream iss(oss.str(), std::ios_base::binary);
2247 m_node_metadata.deSerialize(iss);
2250 catch(SerializationError &e)
2252 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
2253 <<" while deserializing node metadata"<<std::endl;
2259 Translate nodes as specified in the translate_to fields of
2262 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2264 MapNode &n = data[i];
2266 MapNode *translate_to = content_features(n.d).translate_to;
2269 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2270 <<translate_to->d<<std::endl;