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
227 // If looking towards z+, this is the face that is behind
228 // the center point, facing towards z+.
229 vertex_pos[0] = v3f(-BS/2,-BS/2,BS/2);
230 vertex_pos[1] = v3f( BS/2,-BS/2,BS/2);
231 vertex_pos[2] = v3f( BS/2, BS/2,BS/2);
232 vertex_pos[3] = v3f(-BS/2, BS/2,BS/2);
234 if(dir == v3s16(0,0,1))
236 for(u16 i=0; i<4; i++)
237 vertex_pos[i].rotateXZBy(0);
239 else if(dir == v3s16(0,0,-1))
241 for(u16 i=0; i<4; i++)
242 vertex_pos[i].rotateXZBy(180);
244 else if(dir == v3s16(1,0,0))
246 for(u16 i=0; i<4; i++)
247 vertex_pos[i].rotateXZBy(-90);
249 else if(dir == v3s16(-1,0,0))
251 for(u16 i=0; i<4; i++)
252 vertex_pos[i].rotateXZBy(90);
254 else if(dir == v3s16(0,1,0))
256 for(u16 i=0; i<4; i++)
257 vertex_pos[i].rotateYZBy(-90);
259 else if(dir == v3s16(0,-1,0))
261 for(u16 i=0; i<4; i++)
262 vertex_pos[i].rotateYZBy(90);
265 for(u16 i=0; i<4; i++)
267 vertex_pos[i].X *= scale.X;
268 vertex_pos[i].Y *= scale.Y;
269 vertex_pos[i].Z *= scale.Z;
270 vertex_pos[i] += pos + posRelative_f;
274 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
275 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
276 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
278 v3f zerovector = v3f(0,0,0);
280 u8 alpha = tile.alpha;
282 if(tile.id == TILE_WATER)
283 alpha = WATER_ALPHA;*/
285 float x0 = tile.texture.pos.X;
286 float y0 = tile.texture.pos.Y;
287 float w = tile.texture.size.X;
288 float h = tile.texture.size.Y;
290 /*video::SColor c = lightColor(alpha, li);
292 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
293 core::vector2d<f32>(x0+w*abs_scale, y0+h));
294 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
295 core::vector2d<f32>(x0, y0+h));
296 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
297 core::vector2d<f32>(x0, y0));
298 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
299 core::vector2d<f32>(x0+w*abs_scale, y0));*/
301 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0),
302 lightColor(alpha, li0),
303 core::vector2d<f32>(x0+w*abs_scale, y0+h));
304 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0),
305 lightColor(alpha, li1),
306 core::vector2d<f32>(x0, y0+h));
307 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0),
308 lightColor(alpha, li2),
309 core::vector2d<f32>(x0, y0));
310 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0),
311 lightColor(alpha, li3),
312 core::vector2d<f32>(x0+w*abs_scale, y0));
316 //f->tile = TILE_STONE;
318 dest.push_back(face);
322 Gets node tile from any place relative to block.
323 Returns TILE_NODE if doesn't exist or should not be drawn.
325 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
326 NodeModMap &temp_mods)
329 spec = mn.getTile(face_dir);
332 Check temporary modifications on this node
334 /*core::map<v3s16, NodeMod>::Node *n;
335 n = m_temp_mods.find(p);
339 struct NodeMod mod = n->getValue();*/
341 if(temp_mods.get(p, &mod))
343 if(mod.type == NODEMOD_CHANGECONTENT)
345 MapNode mn2(mod.param);
346 spec = mn2.getTile(face_dir);
348 if(mod.type == NODEMOD_CRACK)
351 Get texture id, translate it to name, append stuff to
355 // Get original texture name
356 u32 orig_id = spec.texture.id;
357 std::string orig_name = g_texturesource->getTextureName(orig_id);
359 // Create new texture name
360 std::ostringstream os;
361 os<<orig_name<<"^[crack"<<mod.param;
364 u32 new_id = g_texturesource->getTextureId(os.str());
366 /*dstream<<"MapBlock::getNodeTile(): Switching from "
367 <<orig_name<<" to "<<os.str()<<" ("
368 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
370 spec.texture = g_texturesource->getTexture(new_id);
377 u8 getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
380 Check temporary modifications on this node
382 /*core::map<v3s16, NodeMod>::Node *n;
383 n = m_temp_mods.find(p);
387 struct NodeMod mod = n->getValue();*/
389 if(temp_mods.get(p, &mod))
391 if(mod.type == NODEMOD_CHANGECONTENT)
396 if(mod.type == NODEMOD_CRACK)
399 Content doesn't change.
401 face_contents works just like it should, because
402 there should not be faces between differently cracked
405 If a semi-transparent node is cracked in front an
406 another one, it really doesn't matter whether there
407 is a cracked face drawn in between or not.
426 // Calculate lighting at the XYZ- corner of p
427 u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio)
429 u16 ambient_occlusion = 0;
432 for(u32 i=0; i<8; i++)
434 MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
435 if(content_features(n.d).param_type == CPT_LIGHT)
437 light += decode_light(n.getLightBlend(daynight_ratio));
442 if(n.d != CONTENT_IGNORE)
450 light /= light_count;
452 if(ambient_occlusion > 4)
454 ambient_occlusion -= 4;
455 light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
461 // Calculate lighting at the given corner of p
462 u8 getSmoothLight(v3s16 p, v3s16 corner,
463 VoxelManipulator &vmanip, u32 daynight_ratio)
465 if(corner.X == 1) p.X += 1;
466 else assert(corner.X == -1);
467 if(corner.Y == 1) p.Y += 1;
468 else assert(corner.Y == -1);
469 if(corner.Z == 1) p.Z += 1;
470 else assert(corner.Z == -1);
472 return getSmoothLight(p, vmanip, daynight_ratio);
477 translate_dir: unit vector with only one of x, y or z
478 face_dir: unit vector with only one of x, y or z
480 void updateFastFaceRow(
489 core::array<FastFace> &dest,
490 NodeModMap &temp_mods,
491 VoxelManipulator &vmanip,
492 v3s16 blockpos_nodes,
493 bool smooth_lighting)
497 u16 continuous_tiles_count = 0;
499 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
500 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
501 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
502 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
503 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
505 for(u16 j=0; j<length; j++)
507 // If tiling can be done, this is set to false in the next step
508 bool next_is_different = true;
517 // If at last position, there is nothing to compare to and
518 // the face must be drawn anyway
521 p_next = p + translate_dir;
523 n0_next = vmanip.getNodeNoEx(blockpos_nodes + p_next);
524 n1_next = vmanip.getNodeNoEx(blockpos_nodes + p_next + face_dir);
525 tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
526 tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
527 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
529 if(tile0_next == tile0
530 && tile1_next == tile1
531 && light_next == light)
533 next_is_different = false;
537 continuous_tiles_count++;
539 // This is set to true if the texture doesn't allow more tiling
540 bool end_of_texture = false;
542 If there is no texture, it can be tiled infinitely.
543 If tiled==0, it means the texture can be tiled infinitely.
544 Otherwise check tiled agains continuous_tiles_count.
546 This check has to be made for both tiles, because this is
547 a bit hackish and we know which one we're using only when
548 the decision to make the faces is made.
550 if(tile0.texture.atlas != NULL && tile0.texture.tiled != 0)
552 if(tile0.texture.tiled <= continuous_tiles_count)
553 end_of_texture = true;
555 if(tile1.texture.atlas != NULL && tile1.texture.tiled != 0)
557 if(tile1.texture.tiled <= continuous_tiles_count)
558 end_of_texture = true;
561 // Do this to disable tiling textures
562 //end_of_texture = true; //DEBUG
564 // Disable tiling of textures if smooth lighting is used
566 end_of_texture = true;
568 if(next_is_different || end_of_texture)
571 Create a face if there should be one
574 u8 content0 = getNodeContent(p, n0, temp_mods);
575 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
576 u8 mf = face_contents(content0, content1);
580 // Floating point conversion of the position vector
581 v3f pf(p.X, p.Y, p.Z);
582 // Center point of face (kind of)
583 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
585 u8 li0=255, li1=255, li2=255, li3=255;
588 v3s16 p_first = p - (continuous_tiles_count-1) * translate_dir;
590 if(translate_dir.X != 0)
592 scale.X = continuous_tiles_count;
594 if(translate_dir.Y != 0)
596 scale.Y = continuous_tiles_count;
598 if(translate_dir.Z != 0)
600 scale.Z = continuous_tiles_count;
604 v3s16 p_map_leftmost;
605 v3s16 p_map_rightmost;
606 v3s16 face_dir_corrected;
612 face_dir_corrected = face_dir;
613 p_map_leftmost = p + blockpos_nodes;
614 p_map_rightmost = p_first + blockpos_nodes;
618 // Offset to the actual solid block
619 p_map_leftmost = p + blockpos_nodes + face_dir;
620 p_map_rightmost = p_first + blockpos_nodes + face_dir;
621 /*if(face_dir == v3s16(0,0,1))
623 v3s16 orig_leftmost = p_map_leftmost;
624 v3s16 orig_rightmost = p_map_leftmost;
625 p_map_leftmost = orig_rightmost;
626 p_map_rightmost = orig_leftmost;
629 face_dir_corrected = -face_dir;
633 if(smooth_lighting == false)
635 li0 = li1 = li2 = li3 = decode_light(light);
639 v3s16 vertex_dirs[4];
640 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
642 li0 = getSmoothLight(p_map_rightmost, vertex_dirs[0],
643 vmanip, daynight_ratio);
644 li1 = getSmoothLight(p_map_leftmost, vertex_dirs[1],
645 vmanip, daynight_ratio);
646 li2 = getSmoothLight(p_map_leftmost, vertex_dirs[2],
647 vmanip, daynight_ratio);
648 li3 = getSmoothLight(p_map_rightmost, vertex_dirs[3],
649 vmanip, daynight_ratio);
652 makeFastFace(tile, li0, li1, li2, li3,
653 sp, face_dir_corrected, scale,
654 posRelative_f, dest);
656 v3s16 p_map = p + blockpos_nodes;
657 v3s16 p_map_first = p_first + blockpos_nodes;
659 // If node at sp (tile0) is more solid
664 if(face_dir == v3s16(0,0,1))
666 // Going along X+, faces in Z+
667 li0 = getSmoothLight(p_map_first, v3s16(-1,-1,1),
668 vmanip, daynight_ratio);
669 li1 = getSmoothLight(p_map, v3s16(1,-1,1),
670 vmanip, daynight_ratio);
671 li2 = getSmoothLight(p_map, v3s16(1,1,1),
672 vmanip, daynight_ratio);
673 li3 = getSmoothLight(p_map_first, v3s16(-1,1,1),
674 vmanip, daynight_ratio);
676 else if(face_dir == v3s16(0,1,0))
678 // Going along X+, faces in Y+
679 li0 = getSmoothLight(p_map_first, v3s16( 1,1,-1),
680 vmanip, daynight_ratio);
681 li1 = getSmoothLight(p_map, v3s16(-1,1,-1),
682 vmanip, daynight_ratio);
683 li2 = getSmoothLight(p_map, v3s16(-1,1,1),
684 vmanip, daynight_ratio);
685 li3 = getSmoothLight(p_map_first, v3s16( 1,1,1),
686 vmanip, daynight_ratio);
688 else if(face_dir == v3s16(1,0,0))
690 // Going along Z+, faces in X+
691 li0 = getSmoothLight(p_map_first, v3s16(1,-1,1),
692 vmanip, daynight_ratio);
693 li1 = getSmoothLight(p_map, v3s16(1,-1,-1),
694 vmanip, daynight_ratio);
695 li2 = getSmoothLight(p_map, v3s16(1,1,-1),
696 vmanip, daynight_ratio);
697 li3 = getSmoothLight(p_map_first, v3s16(1,1,1),
698 vmanip, daynight_ratio);
703 makeFastFace(tile0, li0, li1, li2, li3,
705 posRelative_f, dest);
707 // If node at sp is less solid (mf == 2)
712 // Offset to the actual solid block
714 p_map_first += face_dir;
716 if(face_dir == v3s16(0,0,1))
718 // Going along X+, faces in Z-
719 li0 = getSmoothLight(p_map, v3s16(1,-1,-1),
720 vmanip, daynight_ratio);
721 li1 = getSmoothLight(p_map_first, v3s16(-1,-1,-1),
722 vmanip, daynight_ratio);
723 li2 = getSmoothLight(p_map_first, v3s16(-1,1,-1),
724 vmanip, daynight_ratio);
725 li3 = getSmoothLight(p_map, v3s16(1,1,-1),
726 vmanip, daynight_ratio);
728 else if(face_dir == v3s16(0,1,0))
730 // Going along X+, faces in Y-
731 li0 = getSmoothLight(p_map_first, v3s16(-1,-1,1),
732 vmanip, daynight_ratio);
733 li1 = getSmoothLight(p_map, v3s16(1,-1,1),
734 vmanip, daynight_ratio);
735 li2 = getSmoothLight(p_map, v3s16(1,-1,-1),
736 vmanip, daynight_ratio);
737 li3 = getSmoothLight(p_map_first, v3s16(-1,-1,-1),
738 vmanip, daynight_ratio);
740 else if(face_dir == v3s16(1,0,0))
742 // Going along Z+, faces in X-
743 li0 = getSmoothLight(p_map_first, v3s16(-1,-1,-1),
744 vmanip, daynight_ratio);
745 li1 = getSmoothLight(p_map, v3s16(-1,-1,1),
746 vmanip, daynight_ratio);
747 li2 = getSmoothLight(p_map, v3s16(-1,1,1),
748 vmanip, daynight_ratio);
749 li3 = getSmoothLight(p_map_first, v3s16(-1,1,-1),
750 vmanip, daynight_ratio);
755 makeFastFace(tile1, li0, li1, li2, li3,
756 sp+face_dir_f, -face_dir, scale,
757 posRelative_f, dest);
762 continuous_tiles_count = 0;
775 This is used because CMeshBuffer::append() is very slow
779 video::SMaterial material;
780 core::array<u16> indices;
781 core::array<video::S3DVertex> vertices;
788 video::SMaterial material,
789 const video::S3DVertex* const vertices,
791 const u16* const indices,
795 PreMeshBuffer *p = NULL;
796 for(u32 i=0; i<m_prebuffers.size(); i++)
798 PreMeshBuffer &pp = m_prebuffers[i];
799 if(pp.material != material)
809 pp.material = material;
810 m_prebuffers.push_back(pp);
811 p = &m_prebuffers[m_prebuffers.size()-1];
814 u32 vertex_count = p->vertices.size();
815 for(u32 i=0; i<numIndices; i++)
817 u32 j = indices[i] + vertex_count;
820 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
821 // NOTE: Fix is to just add an another MeshBuffer
823 p->indices.push_back(j);
825 for(u32 i=0; i<numVertices; i++)
827 p->vertices.push_back(vertices[i]);
831 void fillMesh(scene::SMesh *mesh)
833 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
834 <<" meshbuffers"<<std::endl;*/
835 for(u32 i=0; i<m_prebuffers.size(); i++)
837 PreMeshBuffer &p = m_prebuffers[i];
839 /*dstream<<"p.vertices.size()="<<p.vertices.size()
840 <<", p.indices.size()="<<p.indices.size()
845 // This is a "Standard MeshBuffer",
846 // it's a typedeffed CMeshBuffer<video::S3DVertex>
847 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
849 buf->Material = p.material;
850 //((scene::SMeshBuffer*)buf)->Material = p.material;
852 //buf->setHardwareMappingHint(scene::EHM_STATIC);
854 mesh->addMeshBuffer(buf);
858 buf->append(p.vertices.pointer(), p.vertices.size(),
859 p.indices.pointer(), p.indices.size());
864 core::array<PreMeshBuffer> m_prebuffers;
867 scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
869 // 4-21ms for MAP_BLOCKSIZE=16
870 // 24-155ms for MAP_BLOCKSIZE=32
871 //TimeTaker timer1("makeMapBlockMesh()");
873 core::array<FastFace> fastfaces_new;
875 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
877 // floating point conversion
878 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
883 bool new_style_water = g_settings.getBool("new_style_water");
884 bool new_style_leaves = g_settings.getBool("new_style_leaves");
885 bool smooth_lighting = g_settings.getBool("smooth_lighting");
887 float node_water_level = 1.0;
889 node_water_level = 0.85;
892 We are including the faces of the trailing edges of the block.
893 This means that when something changes, the caller must
894 also update the meshes of the blocks at the leading edges.
896 NOTE: This is the slowest part of this method.
900 // 4-23ms for MAP_BLOCKSIZE=16
901 //TimeTaker timer2("updateMesh() collect");
904 Go through every y,z and get top(y+) faces in rows of x+
906 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
907 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
908 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
909 v3s16(0,y,z), MAP_BLOCKSIZE,
912 v3s16(0,1,0), //face dir
922 Go through every x,y and get right(x+) faces in rows of z+
924 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
925 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
926 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
927 v3s16(x,y,0), MAP_BLOCKSIZE,
940 Go through every y,z and get back(z+) faces in rows of x+
942 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
943 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
944 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
945 v3s16(0,y,z), MAP_BLOCKSIZE,
962 Convert FastFaces to SMesh
965 MeshCollector collector;
967 if(fastfaces_new.size() > 0)
969 // avg 0ms (100ms spikes when loading textures the first time)
970 //TimeTaker timer2("updateMesh() mesh building");
972 video::SMaterial material;
973 material.setFlag(video::EMF_LIGHTING, false);
974 material.setFlag(video::EMF_BILINEAR_FILTER, false);
975 material.setFlag(video::EMF_FOG_ENABLE, true);
976 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
977 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
979 for(u32 i=0; i<fastfaces_new.size(); i++)
981 FastFace &f = fastfaces_new[i];
983 const u16 indices[] = {0,1,2,2,3,0};
985 video::ITexture *texture = f.tile.texture.atlas;
989 material.setTexture(0, texture);
991 f.tile.applyMaterialOptions(material);
993 collector.append(material, f.vertices, 4, indices, 6);
998 Add special graphics:
1004 //TimeTaker timer2("updateMesh() adding special stuff");
1006 // Flowing water material
1007 video::SMaterial material_water1;
1008 material_water1.setFlag(video::EMF_LIGHTING, false);
1009 material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
1010 material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
1011 material_water1.setFlag(video::EMF_FOG_ENABLE, true);
1012 material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
1013 AtlasPointer pa_water1 = g_texturesource->getTexture(
1014 g_texturesource->getTextureId("water.png"));
1015 material_water1.setTexture(0, pa_water1.atlas);
1017 // New-style leaves material
1018 video::SMaterial material_leaves1;
1019 material_leaves1.setFlag(video::EMF_LIGHTING, false);
1020 //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
1021 material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
1022 material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
1023 material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1024 AtlasPointer pa_leaves1 = g_texturesource->getTexture(
1025 g_texturesource->getTextureId("leaves.png"));
1026 material_leaves1.setTexture(0, pa_leaves1.atlas);
1028 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1029 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
1030 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1034 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
1039 if(n.d == CONTENT_TORCH)
1041 video::SColor c(255,255,255,255);
1043 // Wall at X+ of node
1044 video::S3DVertex vertices[4] =
1046 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
1047 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
1048 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
1049 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
1052 v3s16 dir = unpackDir(n.dir);
1054 for(s32 i=0; i<4; i++)
1056 if(dir == v3s16(1,0,0))
1057 vertices[i].Pos.rotateXZBy(0);
1058 if(dir == v3s16(-1,0,0))
1059 vertices[i].Pos.rotateXZBy(180);
1060 if(dir == v3s16(0,0,1))
1061 vertices[i].Pos.rotateXZBy(90);
1062 if(dir == v3s16(0,0,-1))
1063 vertices[i].Pos.rotateXZBy(-90);
1064 if(dir == v3s16(0,-1,0))
1065 vertices[i].Pos.rotateXZBy(45);
1066 if(dir == v3s16(0,1,0))
1067 vertices[i].Pos.rotateXZBy(-45);
1069 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1073 video::SMaterial material;
1074 material.setFlag(video::EMF_LIGHTING, false);
1075 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
1076 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1077 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1078 material.MaterialType
1079 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1081 if(dir == v3s16(0,-1,0))
1082 material.setTexture(0,
1083 g_texturesource->getTextureRaw("torch_on_floor.png"));
1084 else if(dir == v3s16(0,1,0))
1085 material.setTexture(0,
1086 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
1087 // For backwards compatibility
1088 else if(dir == v3s16(0,0,0))
1089 material.setTexture(0,
1090 g_texturesource->getTextureRaw("torch_on_floor.png"));
1092 material.setTexture(0,
1093 g_texturesource->getTextureRaw("torch.png"));
1095 u16 indices[] = {0,1,2,2,3,0};
1096 // Add to mesh collector
1097 collector.append(material, vertices, 4, indices, 6);
1102 if(n.d == CONTENT_SIGN_WALL)
1104 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1105 video::SColor c(255,l,l,l);
1107 float d = (float)BS/16;
1108 // Wall at X+ of node
1109 video::S3DVertex vertices[4] =
1111 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
1112 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
1113 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
1114 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
1117 v3s16 dir = unpackDir(n.dir);
1119 for(s32 i=0; i<4; i++)
1121 if(dir == v3s16(1,0,0))
1122 vertices[i].Pos.rotateXZBy(0);
1123 if(dir == v3s16(-1,0,0))
1124 vertices[i].Pos.rotateXZBy(180);
1125 if(dir == v3s16(0,0,1))
1126 vertices[i].Pos.rotateXZBy(90);
1127 if(dir == v3s16(0,0,-1))
1128 vertices[i].Pos.rotateXZBy(-90);
1129 if(dir == v3s16(0,-1,0))
1130 vertices[i].Pos.rotateXYBy(-90);
1131 if(dir == v3s16(0,1,0))
1132 vertices[i].Pos.rotateXYBy(90);
1134 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1138 video::SMaterial material;
1139 material.setFlag(video::EMF_LIGHTING, false);
1140 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
1141 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1142 material.setFlag(video::EMF_FOG_ENABLE, true);
1143 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1144 material.MaterialType
1145 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1147 material.setTexture(0,
1148 g_texturesource->getTextureRaw("sign_wall.png"));
1150 u16 indices[] = {0,1,2,2,3,0};
1151 // Add to mesh collector
1152 collector.append(material, vertices, 4, indices, 6);
1155 Add flowing water to mesh
1157 else if(n.d == CONTENT_WATER)
1159 bool top_is_water = false;
1160 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1161 if(ntop.d == CONTENT_WATER || ntop.d == CONTENT_WATERSOURCE)
1162 top_is_water = true;
1165 // Use the light of the node on top if possible
1166 if(content_features(ntop.d).param_type == CPT_LIGHT)
1167 l = decode_light(ntop.getLightBlend(data->m_daynight_ratio));
1168 // Otherwise use the light of this node (the water)
1170 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1171 video::SColor c(WATER_ALPHA,l,l,l);
1173 // Neighbor water levels (key = relative position)
1174 // Includes current node
1175 core::map<v3s16, f32> neighbor_levels;
1176 core::map<v3s16, u8> neighbor_contents;
1177 core::map<v3s16, u8> neighbor_flags;
1178 const u8 neighborflag_top_is_water = 0x01;
1179 v3s16 neighbor_dirs[9] = {
1190 for(u32 i=0; i<9; i++)
1192 u8 content = CONTENT_AIR;
1193 float level = -0.5 * BS;
1196 v3s16 p2 = p + neighbor_dirs[i];
1197 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1198 if(n2.d != CONTENT_IGNORE)
1202 if(n2.d == CONTENT_WATERSOURCE)
1203 level = (-0.5+node_water_level) * BS;
1204 else if(n2.d == CONTENT_WATER)
1205 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
1206 * node_water_level) * BS;
1208 // Check node above neighbor.
1209 // NOTE: This doesn't get executed if neighbor
1212 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1213 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
1214 flags |= neighborflag_top_is_water;
1217 neighbor_levels.insert(neighbor_dirs[i], level);
1218 neighbor_contents.insert(neighbor_dirs[i], content);
1219 neighbor_flags.insert(neighbor_dirs[i], flags);
1222 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
1223 //float water_level = neighbor_levels[v3s16(0,0,0)];
1225 // Corner heights (average between four waters)
1226 f32 corner_levels[4];
1228 v3s16 halfdirs[4] = {
1234 for(u32 i=0; i<4; i++)
1236 v3s16 cornerdir = halfdirs[i];
1237 float cornerlevel = 0;
1238 u32 valid_count = 0;
1239 for(u32 j=0; j<4; j++)
1241 v3s16 neighbordir = cornerdir - halfdirs[j];
1242 u8 content = neighbor_contents[neighbordir];
1243 // Special case for source nodes
1244 if(content == CONTENT_WATERSOURCE)
1246 cornerlevel = (-0.5+node_water_level)*BS;
1250 else if(content == CONTENT_WATER)
1252 cornerlevel += neighbor_levels[neighbordir];
1255 else if(content == CONTENT_AIR)
1257 cornerlevel += -0.5*BS;
1262 cornerlevel /= valid_count;
1263 corner_levels[i] = cornerlevel;
1270 v3s16 side_dirs[4] = {
1276 s16 side_corners[4][2] = {
1282 for(u32 i=0; i<4; i++)
1284 v3s16 dir = side_dirs[i];
1287 If our topside is water and neighbor's topside
1288 is water, don't draw side face
1291 neighbor_flags[dir] & neighborflag_top_is_water)
1294 u8 neighbor_content = neighbor_contents[dir];
1296 // Don't draw face if neighbor is not air or water
1297 if(neighbor_content != CONTENT_AIR
1298 && neighbor_content != CONTENT_WATER)
1301 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1303 // Don't draw any faces if neighbor is water and top is water
1304 if(neighbor_is_water == true && top_is_water == false)
1307 video::S3DVertex vertices[4] =
1309 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1310 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1311 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1312 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1313 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1314 pa_water1.x0(), pa_water1.y1()),
1315 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1316 pa_water1.x1(), pa_water1.y1()),
1317 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1318 pa_water1.x1(), pa_water1.y0()),
1319 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1320 pa_water1.x0(), pa_water1.y0()),
1324 If our topside is water, set upper border of face
1325 at upper border of node
1329 vertices[2].Pos.Y = 0.5*BS;
1330 vertices[3].Pos.Y = 0.5*BS;
1333 Otherwise upper position of face is corner levels
1337 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1338 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1342 If neighbor is water, lower border of face is corner
1345 if(neighbor_is_water)
1347 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1348 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1351 If neighbor is not water, lower border of face is
1352 lower border of node
1356 vertices[0].Pos.Y = -0.5*BS;
1357 vertices[1].Pos.Y = -0.5*BS;
1360 for(s32 j=0; j<4; j++)
1362 if(dir == v3s16(0,0,1))
1363 vertices[j].Pos.rotateXZBy(0);
1364 if(dir == v3s16(0,0,-1))
1365 vertices[j].Pos.rotateXZBy(180);
1366 if(dir == v3s16(-1,0,0))
1367 vertices[j].Pos.rotateXZBy(90);
1368 if(dir == v3s16(1,0,-0))
1369 vertices[j].Pos.rotateXZBy(-90);
1371 vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
1374 u16 indices[] = {0,1,2,2,3,0};
1375 // Add to mesh collector
1376 collector.append(material_water1, vertices, 4, indices, 6);
1380 Generate top side, if appropriate
1383 if(top_is_water == false)
1385 video::S3DVertex vertices[4] =
1387 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1388 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1389 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1390 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1391 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1392 pa_water1.x0(), pa_water1.y1()),
1393 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1394 pa_water1.x1(), pa_water1.y1()),
1395 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1396 pa_water1.x1(), pa_water1.y0()),
1397 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1398 pa_water1.x0(), pa_water1.y0()),
1401 // This fixes a strange bug
1402 s32 corner_resolve[4] = {3,2,1,0};
1404 for(s32 i=0; i<4; i++)
1406 //vertices[i].Pos.Y += water_level;
1407 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1408 s32 j = corner_resolve[i];
1409 vertices[i].Pos.Y += corner_levels[j];
1410 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1413 u16 indices[] = {0,1,2,2,3,0};
1414 // Add to mesh collector
1415 collector.append(material_water1, vertices, 4, indices, 6);
1419 Add water sources to mesh if using new style
1421 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1423 //bool top_is_water = false;
1424 bool top_is_air = false;
1425 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1426 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1427 top_is_water = true;*/
1428 if(n.d == CONTENT_AIR)
1431 /*if(top_is_water == true)
1433 if(top_is_air == false)
1436 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1437 video::SColor c(WATER_ALPHA,l,l,l);
1439 video::S3DVertex vertices[4] =
1441 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1442 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1443 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1444 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1445 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1446 pa_water1.x0(), pa_water1.y1()),
1447 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1448 pa_water1.x1(), pa_water1.y1()),
1449 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1450 pa_water1.x1(), pa_water1.y0()),
1451 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1452 pa_water1.x0(), pa_water1.y0()),
1455 for(s32 i=0; i<4; i++)
1457 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1458 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1461 u16 indices[] = {0,1,2,2,3,0};
1462 // Add to mesh collector
1463 collector.append(material_water1, vertices, 4, indices, 6);
1466 Add leaves if using new style
1468 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1470 /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
1471 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1472 video::SColor c(255,l,l,l);
1474 for(u32 j=0; j<6; j++)
1476 video::S3DVertex vertices[4] =
1478 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1479 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1480 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1481 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1482 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1483 pa_leaves1.x0(), pa_leaves1.y1()),
1484 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1485 pa_leaves1.x1(), pa_leaves1.y1()),
1486 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1487 pa_leaves1.x1(), pa_leaves1.y0()),
1488 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1489 pa_leaves1.x0(), pa_leaves1.y0()),
1494 for(u16 i=0; i<4; i++)
1495 vertices[i].Pos.rotateXZBy(0);
1499 for(u16 i=0; i<4; i++)
1500 vertices[i].Pos.rotateXZBy(180);
1504 for(u16 i=0; i<4; i++)
1505 vertices[i].Pos.rotateXZBy(-90);
1509 for(u16 i=0; i<4; i++)
1510 vertices[i].Pos.rotateXZBy(90);
1514 for(u16 i=0; i<4; i++)
1515 vertices[i].Pos.rotateYZBy(-90);
1519 for(u16 i=0; i<4; i++)
1520 vertices[i].Pos.rotateYZBy(90);
1523 for(u16 i=0; i<4; i++)
1525 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1528 u16 indices[] = {0,1,2,2,3,0};
1529 // Add to mesh collector
1530 collector.append(material_leaves1, vertices, 4, indices, 6);
1536 Add stuff from collector to mesh
1539 scene::SMesh *mesh_new = NULL;
1540 mesh_new = new scene::SMesh();
1542 collector.fillMesh(mesh_new);
1545 Do some stuff to the mesh
1548 mesh_new->recalculateBoundingBox();
1551 Delete new mesh if it is empty
1554 if(mesh_new->getMeshBufferCount() == 0)
1563 // Usually 1-700 faces and 1-7 materials
1564 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1565 <<"and uses "<<mesh_new->getMeshBufferCount()
1566 <<" materials (meshbuffers)"<<std::endl;
1569 // Use VBO for mesh (this just would set this for ever buffer)
1570 // This will lead to infinite memory usage because or irrlicht.
1571 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1574 NOTE: If that is enabled, some kind of a queue to the main
1575 thread should be made which would call irrlicht to delete
1576 the hardware buffer and then delete the mesh
1582 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1591 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
1595 is_underground(false),
1596 m_lighting_expired(true),
1597 m_day_night_differs(false),
1598 //m_not_fully_generated(false),
1605 //m_spawn_timer = -10000;
1608 m_mesh_expired = false;
1611 m_temp_mods_mutex.Init();
1615 MapBlock::~MapBlock()
1619 JMutexAutoLock lock(mesh_mutex);
1633 bool MapBlock::isValidPositionParent(v3s16 p)
1635 if(isValidPosition(p))
1640 return m_parent->isValidPosition(getPosRelative() + p);
1644 MapNode MapBlock::getNodeParent(v3s16 p)
1646 if(isValidPosition(p) == false)
1648 return m_parent->getNode(getPosRelative() + p);
1653 throw InvalidPositionException();
1654 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1658 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
1660 if(isValidPosition(p) == false)
1662 m_parent->setNode(getPosRelative() + p, n);
1667 throw InvalidPositionException();
1668 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
1672 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
1674 if(isValidPosition(p) == false)
1677 return m_parent->getNode(getPosRelative() + p);
1679 catch(InvalidPositionException &e)
1681 return MapNode(CONTENT_IGNORE);
1688 return MapNode(CONTENT_IGNORE);
1690 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1697 void MapBlock::updateMesh(u32 daynight_ratio)
1701 DEBUG: If mesh has been generated, don't generate it again
1704 JMutexAutoLock meshlock(mesh_mutex);
1711 data.fill(daynight_ratio, this);
1713 scene::SMesh *mesh_new = makeMapBlockMesh(&data);
1719 replaceMesh(mesh_new);
1724 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
1728 //scene::SMesh *mesh_old = mesh[daynight_i];
1729 //mesh[daynight_i] = mesh_new;
1731 scene::SMesh *mesh_old = mesh;
1733 setMeshExpired(false);
1735 if(mesh_old != NULL)
1737 // Remove hardware buffers of meshbuffers of mesh
1738 // NOTE: No way, this runs in a different thread and everything
1739 /*u32 c = mesh_old->getMeshBufferCount();
1740 for(u32 i=0; i<c; i++)
1742 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1745 /*dstream<<"mesh_old->getReferenceCount()="
1746 <<mesh_old->getReferenceCount()<<std::endl;
1747 u32 c = mesh_old->getMeshBufferCount();
1748 for(u32 i=0; i<c; i++)
1750 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1751 dstream<<"buf->getReferenceCount()="
1752 <<buf->getReferenceCount()<<std::endl;
1761 mesh_mutex.Unlock();
1767 Propagates sunlight down through the block.
1768 Doesn't modify nodes that are not affected by sunlight.
1770 Returns false if sunlight at bottom block is invalid
1771 Returns true if bottom block doesn't exist.
1773 If there is a block above, continues from it.
1774 If there is no block above, assumes there is sunlight, unless
1775 is_underground is set or highest node is water.
1777 At the moment, all sunlighted nodes are added to light_sources.
1778 - SUGG: This could be optimized
1780 Turns sunglighted mud into grass.
1782 if remove_light==true, sets non-sunlighted nodes black.
1784 if black_air_left!=NULL, it is set to true if non-sunlighted
1785 air is left in block.
1787 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1788 bool remove_light, bool *black_air_left,
1791 // Whether the sunlight at the top of the bottom block is valid
1792 bool block_below_is_valid = true;
1794 v3s16 pos_relative = getPosRelative();
1796 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1798 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1801 bool no_sunlight = false;
1802 bool no_top_block = false;
1803 // Check if node above block has sunlight
1805 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1806 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1811 catch(InvalidPositionException &e)
1813 no_top_block = true;
1815 // NOTE: This makes over-ground roofed places sunlighted
1816 // Assume sunlight, unless is_underground==true
1823 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1824 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1829 // NOTE: As of now, this just would make everything dark.
1831 //no_sunlight = true;
1834 #if 0 // Doesn't work; nothing gets light.
1835 bool no_sunlight = true;
1836 bool no_top_block = false;
1837 // Check if node above block has sunlight
1839 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1840 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1842 no_sunlight = false;
1845 catch(InvalidPositionException &e)
1847 no_top_block = true;
1851 /*std::cout<<"("<<x<<","<<z<<"): "
1852 <<"no_top_block="<<no_top_block
1853 <<", is_underground="<<is_underground
1854 <<", no_sunlight="<<no_sunlight
1857 s16 y = MAP_BLOCKSIZE-1;
1859 // This makes difference to diminishing in water.
1860 bool stopped_to_solid_object = false;
1862 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1867 MapNode &n = getNodeRef(pos);
1869 if(current_light == 0)
1873 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1875 // Do nothing: Sunlight is continued
1877 else if(n.light_propagates() == false)
1881 bool upper_is_air = false;
1884 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1885 upper_is_air = true;
1887 catch(InvalidPositionException &e)
1890 // Turn mud into grass
1891 if(upper_is_air && n.d == CONTENT_MUD
1892 && current_light == LIGHT_SUN)
1894 n.d = CONTENT_GRASS;
1898 // A solid object is on the way.
1899 stopped_to_solid_object = true;
1907 current_light = diminish_light(current_light);
1910 u8 old_light = n.getLight(LIGHTBANK_DAY);
1912 if(current_light > old_light || remove_light)
1914 n.setLight(LIGHTBANK_DAY, current_light);
1917 if(diminish_light(current_light) != 0)
1919 light_sources.insert(pos_relative + pos, true);
1922 if(current_light == 0 && stopped_to_solid_object)
1926 *black_air_left = true;
1931 // Whether or not the block below should see LIGHT_SUN
1932 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1935 If the block below hasn't already been marked invalid:
1937 Check if the node below the block has proper sunlight at top.
1938 If not, the block below is invalid.
1940 Ignore non-transparent nodes as they always have no light
1944 if(block_below_is_valid)
1946 MapNode n = getNodeParent(v3s16(x, -1, z));
1947 if(n.light_propagates())
1949 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1950 && sunlight_should_go_down == false)
1951 block_below_is_valid = false;
1952 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1953 && sunlight_should_go_down == true)
1954 block_below_is_valid = false;
1958 catch(InvalidPositionException &e)
1960 /*std::cout<<"InvalidBlockException for bottom block node"
1962 // Just no block below, no need to panic.
1967 return block_below_is_valid;
1971 void MapBlock::copyTo(VoxelManipulator &dst)
1973 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1974 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1976 // Copy from data to VoxelManipulator
1977 dst.copyFrom(data, data_area, v3s16(0,0,0),
1978 getPosRelative(), data_size);
1981 void MapBlock::copyFrom(VoxelManipulator &dst)
1983 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1984 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1986 // Copy from VoxelManipulator to data
1987 dst.copyTo(data, data_area, v3s16(0,0,0),
1988 getPosRelative(), data_size);
1991 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1996 m_objects.step(dtime, server, daynight_ratio);
2000 Spawn some objects at random.
2002 Use dayNightDiffed() to approximate being near ground level
2004 if(m_spawn_timer < -999)
2008 if(dayNightDiffed() == true && getObjectCount() == 0)
2010 m_spawn_timer -= dtime;
2011 if(m_spawn_timer <= 0.0)
2013 m_spawn_timer += myrand() % 300;
2016 (myrand()%(MAP_BLOCKSIZE-1))+0,
2017 (myrand()%(MAP_BLOCKSIZE-1))+0
2020 s16 y = getGroundLevel(p2d);
2024 v3s16 p(p2d.X, y+1, p2d.Y);
2026 if(getNode(p).d == CONTENT_AIR
2027 && getNode(p).getLightBlend(daynight_ratio) <= 11)
2029 RatObject *obj = new RatObject(NULL, -1, intToFloat(p, BS));
2041 void MapBlock::updateDayNightDiff()
2045 m_day_night_differs = false;
2049 bool differs = false;
2052 Check if any lighting value differs
2054 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2056 MapNode &n = data[i];
2057 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
2065 If some lighting values differ, check if the whole thing is
2066 just air. If it is, differ = false
2070 bool only_air = true;
2071 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2073 MapNode &n = data[i];
2074 if(n.d != CONTENT_AIR)
2084 // Set member variable
2085 m_day_night_differs = differs;
2088 s16 MapBlock::getGroundLevel(v2s16 p2d)
2094 s16 y = MAP_BLOCKSIZE-1;
2097 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
2098 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
2100 if(y == MAP_BLOCKSIZE-1)
2108 catch(InvalidPositionException &e)
2118 void MapBlock::serialize(std::ostream &os, u8 version)
2120 if(!ser_ver_supported(version))
2121 throw VersionMismatchException("ERROR: MapBlock format not supported");
2125 throw SerializationError("ERROR: Not writing dummy block.");
2128 // These have no compression
2129 if(version <= 3 || version == 5 || version == 6)
2131 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2133 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
2134 SharedBuffer<u8> dest(buflen);
2136 dest[0] = is_underground;
2137 for(u32 i=0; i<nodecount; i++)
2139 u32 s = 1 + i * MapNode::serializedLength(version);
2140 data[i].serialize(&dest[s], version);
2143 os.write((char*)*dest, dest.getSize());
2145 else if(version <= 10)
2149 Compress the materials and the params separately.
2153 os.write((char*)&is_underground, 1);
2155 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2157 // Get and compress materials
2158 SharedBuffer<u8> materialdata(nodecount);
2159 for(u32 i=0; i<nodecount; i++)
2161 materialdata[i] = data[i].d;
2163 compress(materialdata, os, version);
2165 // Get and compress lights
2166 SharedBuffer<u8> lightdata(nodecount);
2167 for(u32 i=0; i<nodecount; i++)
2169 lightdata[i] = data[i].param;
2171 compress(lightdata, os, version);
2175 // Get and compress param2
2176 SharedBuffer<u8> param2data(nodecount);
2177 for(u32 i=0; i<nodecount; i++)
2179 param2data[i] = data[i].param2;
2181 compress(param2data, os, version);
2184 // All other versions (newest)
2191 if(m_day_night_differs)
2193 if(m_lighting_expired)
2195 os.write((char*)&flags, 1);
2197 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2203 SharedBuffer<u8> databuf(nodecount*3);
2206 for(u32 i=0; i<nodecount; i++)
2208 databuf[i] = data[i].d;
2212 for(u32 i=0; i<nodecount; i++)
2214 databuf[i+nodecount] = data[i].param;
2218 for(u32 i=0; i<nodecount; i++)
2220 databuf[i+nodecount*2] = data[i].param2;
2224 Compress data to output stream
2227 compress(databuf, os, version);
2236 std::ostringstream oss(std::ios_base::binary);
2237 m_node_metadata.serialize(oss);
2238 os<<serializeString(oss.str());
2242 std::ostringstream oss(std::ios_base::binary);
2243 m_node_metadata.serialize(oss);
2244 compressZlib(oss.str(), os);
2245 //os<<serializeLongString(oss.str());
2251 void MapBlock::deSerialize(std::istream &is, u8 version)
2253 if(!ser_ver_supported(version))
2254 throw VersionMismatchException("ERROR: MapBlock format not supported");
2256 // These have no lighting info
2259 setLightingExpired(true);
2262 // These have no compression
2263 if(version <= 3 || version == 5 || version == 6)
2265 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2268 if(is.gcount() != 1)
2269 throw SerializationError
2270 ("MapBlock::deSerialize: no enough input data");
2271 is_underground = tmp;
2272 for(u32 i=0; i<nodecount; i++)
2274 s32 len = MapNode::serializedLength(version);
2275 SharedBuffer<u8> d(len);
2276 is.read((char*)*d, len);
2277 if(is.gcount() != len)
2278 throw SerializationError
2279 ("MapBlock::deSerialize: no enough input data");
2280 data[i].deSerialize(*d, version);
2283 else if(version <= 10)
2285 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2288 is.read((char*)&t8, 1);
2289 is_underground = t8;
2292 // Uncompress and set material data
2293 std::ostringstream os(std::ios_base::binary);
2294 decompress(is, os, version);
2295 std::string s = os.str();
2296 if(s.size() != nodecount)
2297 throw SerializationError
2298 ("MapBlock::deSerialize: invalid format");
2299 for(u32 i=0; i<s.size(); i++)
2305 // Uncompress and set param data
2306 std::ostringstream os(std::ios_base::binary);
2307 decompress(is, os, version);
2308 std::string s = os.str();
2309 if(s.size() != nodecount)
2310 throw SerializationError
2311 ("MapBlock::deSerialize: invalid format");
2312 for(u32 i=0; i<s.size(); i++)
2314 data[i].param = s[i];
2320 // Uncompress and set param2 data
2321 std::ostringstream os(std::ios_base::binary);
2322 decompress(is, os, version);
2323 std::string s = os.str();
2324 if(s.size() != nodecount)
2325 throw SerializationError
2326 ("MapBlock::deSerialize: invalid format");
2327 for(u32 i=0; i<s.size(); i++)
2329 data[i].param2 = s[i];
2333 // All other versions (newest)
2336 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2339 is.read((char*)&flags, 1);
2340 is_underground = (flags & 0x01) ? true : false;
2341 m_day_night_differs = (flags & 0x02) ? true : false;
2342 m_lighting_expired = (flags & 0x04) ? true : false;
2345 std::ostringstream os(std::ios_base::binary);
2346 decompress(is, os, version);
2347 std::string s = os.str();
2348 if(s.size() != nodecount*3)
2349 throw SerializationError
2350 ("MapBlock::deSerialize: invalid format");
2353 for(u32 i=0; i<nodecount; i++)
2358 for(u32 i=0; i<nodecount; i++)
2360 data[i].param = s[i+nodecount];
2363 for(u32 i=0; i<nodecount; i++)
2365 data[i].param2 = s[i+nodecount*2];
2377 std::string data = deSerializeString(is);
2378 std::istringstream iss(data, std::ios_base::binary);
2379 m_node_metadata.deSerialize(iss);
2383 //std::string data = deSerializeLongString(is);
2384 std::ostringstream oss(std::ios_base::binary);
2385 decompressZlib(is, oss);
2386 std::istringstream iss(oss.str(), std::ios_base::binary);
2387 m_node_metadata.deSerialize(iss);
2390 catch(SerializationError &e)
2392 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
2393 <<" while deserializing node metadata"<<std::endl;
2399 Translate nodes as specified in the translate_to fields of
2402 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2404 MapNode &n = data[i];
2406 MapNode *translate_to = content_features(n.d).translate_to;
2409 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2410 <<translate_to->d<<std::endl;