3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
30 m_daynight_ratio = daynight_ratio;
31 m_blockpos = block->getPos();
33 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
36 There is no harm not copying the TempMods of the neighbors
37 because they are already copied to this block
40 block->copyTempMods(m_temp_mods);
46 // Allocate this block + neighbors
48 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
49 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
52 //TimeTaker timer("copy central block data");
56 block->copyTo(m_vmanip);
59 //TimeTaker timer("copy neighbor block data");
63 Copy neighbors. This is lightning fast.
64 Copying only the borders would be *very* slow.
68 NodeContainer *parentcontainer = block->getParent();
69 // This will only work if the parent is the map
70 assert(parentcontainer->nodeContainerId() == NODECONTAINER_ID_MAP);
71 // OK, we have the map!
72 Map *map = (Map*)parentcontainer;
74 for(u16 i=0; i<6; i++)
76 const v3s16 &dir = g_6dirs[i];
77 v3s16 bp = m_blockpos + dir;
78 MapBlock *b = map->getBlockNoCreateNoEx(bp);
87 Parameters must consist of air and !air.
90 If either of the nodes doesn't exist, light is 0.
93 daynight_ratio: 0...1000
95 n2: getNodeParent(p + face_dir)
96 face_dir: axis oriented unit vector from p to p2
98 returns encoded light value.
100 u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
105 u8 l1 = n.getLightBlend(daynight_ratio);
106 u8 l2 = n2.getLightBlend(daynight_ratio);
112 // Make some nice difference to different sides
114 // This makes light come from a corner
115 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
116 light = diminish_light(diminish_light(light));
117 else if(face_dir.X == -1 || face_dir.Z == -1)
118 light = diminish_light(light);*/
120 // All neighboring faces have different shade (like in minecraft)
121 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
122 light = diminish_light(diminish_light(light));
123 else if(face_dir.Z == 1 || face_dir.Z == -1)
124 light = diminish_light(light);
128 catch(InvalidPositionException &e)
137 vertex_dirs: v3s16[4]
139 void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
142 If looked from outside the node towards the face, the corners are:
148 if(dir == v3s16(0,0,1))
150 // If looking towards z+, this is the face that is behind
151 // the center point, facing towards z+.
152 vertex_dirs[0] = v3s16(-1,-1, 1);
153 vertex_dirs[1] = v3s16( 1,-1, 1);
154 vertex_dirs[2] = v3s16( 1, 1, 1);
155 vertex_dirs[3] = v3s16(-1, 1, 1);
157 else if(dir == v3s16(0,0,-1))
160 vertex_dirs[0] = v3s16( 1,-1,-1);
161 vertex_dirs[1] = v3s16(-1,-1,-1);
162 vertex_dirs[2] = v3s16(-1, 1,-1);
163 vertex_dirs[3] = v3s16( 1, 1,-1);
165 else if(dir == v3s16(1,0,0))
168 vertex_dirs[0] = v3s16( 1,-1, 1);
169 vertex_dirs[1] = v3s16( 1,-1,-1);
170 vertex_dirs[2] = v3s16( 1, 1,-1);
171 vertex_dirs[3] = v3s16( 1, 1, 1);
173 else if(dir == v3s16(-1,0,0))
176 vertex_dirs[0] = v3s16(-1,-1,-1);
177 vertex_dirs[1] = v3s16(-1,-1, 1);
178 vertex_dirs[2] = v3s16(-1, 1, 1);
179 vertex_dirs[3] = v3s16(-1, 1,-1);
181 else if(dir == v3s16(0,1,0))
183 // faces towards Y+ (assume Z- as "down" in texture)
184 vertex_dirs[0] = v3s16( 1, 1,-1);
185 vertex_dirs[1] = v3s16(-1, 1,-1);
186 vertex_dirs[2] = v3s16(-1, 1, 1);
187 vertex_dirs[3] = v3s16( 1, 1, 1);
189 else if(dir == v3s16(0,-1,0))
191 // faces towards Y- (assume Z+ as "down" in texture)
192 vertex_dirs[0] = v3s16( 1,-1, 1);
193 vertex_dirs[1] = v3s16(-1,-1, 1);
194 vertex_dirs[2] = v3s16(-1,-1,-1);
195 vertex_dirs[3] = v3s16( 1,-1,-1);
199 inline video::SColor lightColor(u8 alpha, u8 light)
201 return video::SColor(alpha,light,light,light);
204 void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
205 v3s16 dir, v3f scale, v3f posRelative_f,
206 core::array<FastFace> &dest)
210 // Position is at the center of the cube.
215 v3s16 vertex_dirs[4];
216 getNodeVertexDirs(dir, vertex_dirs);
217 for(u16 i=0; i<4; i++)
220 BS/2*vertex_dirs[i].X,
221 BS/2*vertex_dirs[i].Y,
222 BS/2*vertex_dirs[i].Z
226 for(u16 i=0; i<4; i++)
228 vertex_pos[i].X *= scale.X;
229 vertex_pos[i].Y *= scale.Y;
230 vertex_pos[i].Z *= scale.Z;
231 vertex_pos[i] += pos + posRelative_f;
235 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
236 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
237 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
239 v3f zerovector = v3f(0,0,0);
241 u8 alpha = tile.alpha;
243 if(tile.id == TILE_WATER)
244 alpha = WATER_ALPHA;*/
246 float x0 = tile.texture.pos.X;
247 float y0 = tile.texture.pos.Y;
248 float w = tile.texture.size.X;
249 float h = tile.texture.size.Y;
251 /*video::SColor c = lightColor(alpha, li);
253 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
254 core::vector2d<f32>(x0+w*abs_scale, y0+h));
255 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
256 core::vector2d<f32>(x0, y0+h));
257 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
258 core::vector2d<f32>(x0, y0));
259 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
260 core::vector2d<f32>(x0+w*abs_scale, y0));*/
262 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0),
263 lightColor(alpha, li0),
264 core::vector2d<f32>(x0+w*abs_scale, y0+h));
265 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0),
266 lightColor(alpha, li1),
267 core::vector2d<f32>(x0, y0+h));
268 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0),
269 lightColor(alpha, li2),
270 core::vector2d<f32>(x0, y0));
271 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0),
272 lightColor(alpha, li3),
273 core::vector2d<f32>(x0+w*abs_scale, y0));
277 //f->tile = TILE_STONE;
279 dest.push_back(face);
283 Gets node tile from any place relative to block.
284 Returns TILE_NODE if doesn't exist or should not be drawn.
286 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
287 NodeModMap &temp_mods)
290 spec = mn.getTile(face_dir);
293 Check temporary modifications on this node
295 /*core::map<v3s16, NodeMod>::Node *n;
296 n = m_temp_mods.find(p);
300 struct NodeMod mod = n->getValue();*/
302 if(temp_mods.get(p, &mod))
304 if(mod.type == NODEMOD_CHANGECONTENT)
306 MapNode mn2(mod.param);
307 spec = mn2.getTile(face_dir);
309 if(mod.type == NODEMOD_CRACK)
312 Get texture id, translate it to name, append stuff to
316 // Get original texture name
317 u32 orig_id = spec.texture.id;
318 std::string orig_name = g_texturesource->getTextureName(orig_id);
320 // Create new texture name
321 std::ostringstream os;
322 os<<orig_name<<"^[crack"<<mod.param;
325 u32 new_id = g_texturesource->getTextureId(os.str());
327 /*dstream<<"MapBlock::getNodeTile(): Switching from "
328 <<orig_name<<" to "<<os.str()<<" ("
329 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
331 spec.texture = g_texturesource->getTexture(new_id);
338 u8 getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
341 Check temporary modifications on this node
343 /*core::map<v3s16, NodeMod>::Node *n;
344 n = m_temp_mods.find(p);
348 struct NodeMod mod = n->getValue();*/
350 if(temp_mods.get(p, &mod))
352 if(mod.type == NODEMOD_CHANGECONTENT)
357 if(mod.type == NODEMOD_CRACK)
360 Content doesn't change.
362 face_contents works just like it should, because
363 there should not be faces between differently cracked
366 If a semi-transparent node is cracked in front an
367 another one, it really doesn't matter whether there
368 is a cracked face drawn in between or not.
387 // Calculate lighting at the XYZ- corner of p
388 u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio)
390 u16 ambient_occlusion = 0;
393 for(u32 i=0; i<8; i++)
395 MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
396 if(content_features(n.d).param_type == CPT_LIGHT)
398 light += decode_light(n.getLightBlend(daynight_ratio));
403 if(n.d != CONTENT_IGNORE)
411 light /= light_count;
413 if(ambient_occlusion > 4)
415 ambient_occlusion -= 4;
416 light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
422 // Calculate lighting at the given corner of p
423 u8 getSmoothLight(v3s16 p, v3s16 corner,
424 VoxelManipulator &vmanip, u32 daynight_ratio)
426 if(corner.X == 1) p.X += 1;
427 else assert(corner.X == -1);
428 if(corner.Y == 1) p.Y += 1;
429 else assert(corner.Y == -1);
430 if(corner.Z == 1) p.Z += 1;
431 else assert(corner.Z == -1);
433 return getSmoothLight(p, vmanip, daynight_ratio);
438 v3s16 blockpos_nodes,
442 VoxelManipulator &vmanip,
443 NodeModMap &temp_mods,
444 bool smooth_lighting,
448 v3s16 &face_dir_corrected,
453 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
454 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
455 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
456 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
459 u8 content0 = getNodeContent(p, n0, temp_mods);
460 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
461 u8 mf = face_contents(content0, content1);
475 face_dir_corrected = face_dir;
480 p_corrected = p + face_dir;
481 face_dir_corrected = -face_dir;
484 if(smooth_lighting == false)
486 lights[0] = lights[1] = lights[2] = lights[3] =
487 decode_light(getFaceLight(daynight_ratio, n0, n1, face_dir));
491 v3s16 vertex_dirs[4];
492 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
493 for(u16 i=0; i<4; i++)
495 lights[i] = getSmoothLight(blockpos_nodes + p_corrected,
496 vertex_dirs[i], vmanip, daynight_ratio);
505 translate_dir: unit vector with only one of x, y or z
506 face_dir: unit vector with only one of x, y or z
508 void updateFastFaceRow(
517 core::array<FastFace> &dest,
518 NodeModMap &temp_mods,
519 VoxelManipulator &vmanip,
520 v3s16 blockpos_nodes,
521 bool smooth_lighting)
525 u16 continuous_tiles_count = 0;
529 v3s16 face_dir_corrected;
532 getTileInfo(blockpos_nodes, p, face_dir, daynight_ratio,
533 vmanip, temp_mods, smooth_lighting,
534 makes_face, p_corrected, face_dir_corrected, lights, tile);
536 for(u16 j=0; j<length; j++)
538 // If tiling can be done, this is set to false in the next step
539 bool next_is_different = true;
543 bool next_makes_face = false;
544 v3s16 next_p_corrected;
545 v3s16 next_face_dir_corrected;
546 u8 next_lights[4] = {0,0,0,0};
549 // If at last position, there is nothing to compare to and
550 // the face must be drawn anyway
553 p_next = p + translate_dir;
555 getTileInfo(blockpos_nodes, p_next, face_dir, daynight_ratio,
556 vmanip, temp_mods, smooth_lighting,
557 next_makes_face, next_p_corrected,
558 next_face_dir_corrected, next_lights,
561 if(next_makes_face == makes_face
562 && next_p_corrected == p_corrected
563 && next_face_dir_corrected == face_dir_corrected
564 && next_lights[0] == lights[0]
565 && next_lights[1] == lights[1]
566 && next_lights[2] == lights[2]
567 && next_lights[3] == lights[3]
568 && next_tile == tile)
570 next_is_different = false;
574 continuous_tiles_count++;
576 // This is set to true if the texture doesn't allow more tiling
577 bool end_of_texture = false;
579 If there is no texture, it can be tiled infinitely.
580 If tiled==0, it means the texture can be tiled infinitely.
581 Otherwise check tiled agains continuous_tiles_count.
583 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
585 if(tile.texture.tiled <= continuous_tiles_count)
586 end_of_texture = true;
589 // Do this to disable tiling textures
590 //end_of_texture = true; //DEBUG
592 if(next_is_different || end_of_texture)
595 Create a face if there should be one
599 // Floating point conversion of the position vector
600 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
601 // Center point of face (kind of)
602 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
605 if(translate_dir.X != 0)
607 scale.X = continuous_tiles_count;
609 if(translate_dir.Y != 0)
611 scale.Y = continuous_tiles_count;
613 if(translate_dir.Z != 0)
615 scale.Z = continuous_tiles_count;
618 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
619 sp, face_dir_corrected, scale,
620 posRelative_f, dest);
623 continuous_tiles_count = 0;
625 makes_face = next_makes_face;
626 p_corrected = next_p_corrected;
627 face_dir_corrected = next_face_dir_corrected;
628 lights[0] = next_lights[0];
629 lights[1] = next_lights[1];
630 lights[2] = next_lights[2];
631 lights[3] = next_lights[3];
640 This is used because CMeshBuffer::append() is very slow
644 video::SMaterial material;
645 core::array<u16> indices;
646 core::array<video::S3DVertex> vertices;
653 video::SMaterial material,
654 const video::S3DVertex* const vertices,
656 const u16* const indices,
660 PreMeshBuffer *p = NULL;
661 for(u32 i=0; i<m_prebuffers.size(); i++)
663 PreMeshBuffer &pp = m_prebuffers[i];
664 if(pp.material != material)
674 pp.material = material;
675 m_prebuffers.push_back(pp);
676 p = &m_prebuffers[m_prebuffers.size()-1];
679 u32 vertex_count = p->vertices.size();
680 for(u32 i=0; i<numIndices; i++)
682 u32 j = indices[i] + vertex_count;
685 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
686 // NOTE: Fix is to just add an another MeshBuffer
688 p->indices.push_back(j);
690 for(u32 i=0; i<numVertices; i++)
692 p->vertices.push_back(vertices[i]);
696 void fillMesh(scene::SMesh *mesh)
698 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
699 <<" meshbuffers"<<std::endl;*/
700 for(u32 i=0; i<m_prebuffers.size(); i++)
702 PreMeshBuffer &p = m_prebuffers[i];
704 /*dstream<<"p.vertices.size()="<<p.vertices.size()
705 <<", p.indices.size()="<<p.indices.size()
710 // This is a "Standard MeshBuffer",
711 // it's a typedeffed CMeshBuffer<video::S3DVertex>
712 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
714 buf->Material = p.material;
715 //((scene::SMeshBuffer*)buf)->Material = p.material;
717 //buf->setHardwareMappingHint(scene::EHM_STATIC);
719 mesh->addMeshBuffer(buf);
723 buf->append(p.vertices.pointer(), p.vertices.size(),
724 p.indices.pointer(), p.indices.size());
729 core::array<PreMeshBuffer> m_prebuffers;
733 // material - the material to use (for all 6 faces)
734 // collector - the MeshCollector for the resulting polygons
735 // pa - texture atlas pointer for the material
736 // c - vertex colour - used for all
737 // pos - the position of the centre of the cuboid
738 // rz,ry,rz - the radius of the cuboid in each dimension
739 // txc - texture coordinates - this is a list of texture coordinates
740 // for the opposite corners of each face - therefore, there
741 // should be (2+2)*6=24 values in the list. Alternatively, pass
742 // NULL to use the entire texture for each face. The order of
743 // the faces in the list is top-backi-right-front-left-bottom
744 // If you specified 0,0,1,1 for each face, that would be the
745 // same as passing NULL.
746 void makeCuboid(video::SMaterial &material, MeshCollector *collector,
747 AtlasPointer* pa, video::SColor &c,
748 v3f &pos, f32 rx, f32 ry, f32 rz, f32* txc)
757 video::S3DVertex v[4] =
759 video::S3DVertex(0,0,0, 0,0,0, c, tu0, tv1),
760 video::S3DVertex(0,0,0, 0,0,0, c, tu1, tv1),
761 video::S3DVertex(0,0,0, 0,0,0, c, tu1, tv0),
762 video::S3DVertex(0,0,0, 0,0,0, c, tu0, tv0)
770 v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz;
771 v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz;
772 v[2].Pos.X= rx; v[2].Pos.Y= ry; v[2].Pos.Z= rz;
773 v[3].Pos.X= rx; v[3].Pos.Y= ry, v[3].Pos.Z=-rz;
776 v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz;
777 v[1].Pos.X= rx; v[1].Pos.Y= ry; v[1].Pos.Z=-rz;
778 v[2].Pos.X= rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz;
779 v[3].Pos.X=-rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz;
782 v[0].Pos.X= rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz;
783 v[1].Pos.X= rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz;
784 v[2].Pos.X= rx; v[2].Pos.Y=-ry; v[2].Pos.Z= rz;
785 v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz;
788 v[0].Pos.X= rx; v[0].Pos.Y= ry; v[0].Pos.Z= rz;
789 v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz;
790 v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z= rz;
791 v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z= rz;
794 v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z= rz;
795 v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z=-rz;
796 v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz;
797 v[3].Pos.X=-rx; v[3].Pos.Y=-ry, v[3].Pos.Z= rz;
800 v[0].Pos.X= rx; v[0].Pos.Y=-ry; v[0].Pos.Z= rz;
801 v[1].Pos.X=-rx; v[1].Pos.Y=-ry; v[1].Pos.Z= rz;
802 v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz;
803 v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz;
809 v[0].TCoords.X=tu0+txus*txc[0]; v[0].TCoords.Y=tv0+txvs*txc[3];
810 v[1].TCoords.X=tu0+txus*txc[2]; v[1].TCoords.Y=tv0+txvs*txc[3];
811 v[2].TCoords.X=tu0+txus*txc[2]; v[2].TCoords.Y=tv0+txvs*txc[1];
812 v[3].TCoords.X=tu0+txus*txc[0]; v[3].TCoords.Y=tv0+txvs*txc[1];
816 for(u16 i=0; i<4; i++)
818 u16 indices[] = {0,1,2,2,3,0};
819 collector->append(material, v, 4, indices, 6);
825 scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
827 // 4-21ms for MAP_BLOCKSIZE=16
828 // 24-155ms for MAP_BLOCKSIZE=32
829 //TimeTaker timer1("makeMapBlockMesh()");
831 core::array<FastFace> fastfaces_new;
833 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
835 // floating point conversion
836 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
841 bool new_style_water = g_settings.getBool("new_style_water");
842 bool new_style_leaves = g_settings.getBool("new_style_leaves");
843 bool smooth_lighting = g_settings.getBool("smooth_lighting");
845 float node_water_level = 1.0;
847 node_water_level = 0.85;
850 We are including the faces of the trailing edges of the block.
851 This means that when something changes, the caller must
852 also update the meshes of the blocks at the leading edges.
854 NOTE: This is the slowest part of this method.
858 // 4-23ms for MAP_BLOCKSIZE=16
859 //TimeTaker timer2("updateMesh() collect");
862 Go through every y,z and get top(y+) faces in rows of x+
864 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
865 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
866 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
867 v3s16(0,y,z), MAP_BLOCKSIZE,
870 v3s16(0,1,0), //face dir
880 Go through every x,y and get right(x+) faces in rows of z+
882 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
883 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
884 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
885 v3s16(x,y,0), MAP_BLOCKSIZE,
898 Go through every y,z and get back(z+) faces in rows of x+
900 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
901 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
902 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
903 v3s16(0,y,z), MAP_BLOCKSIZE,
920 Convert FastFaces to SMesh
923 MeshCollector collector;
925 if(fastfaces_new.size() > 0)
927 // avg 0ms (100ms spikes when loading textures the first time)
928 //TimeTaker timer2("updateMesh() mesh building");
930 video::SMaterial material;
931 material.setFlag(video::EMF_LIGHTING, false);
932 material.setFlag(video::EMF_BILINEAR_FILTER, false);
933 material.setFlag(video::EMF_FOG_ENABLE, true);
934 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
935 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
937 for(u32 i=0; i<fastfaces_new.size(); i++)
939 FastFace &f = fastfaces_new[i];
941 const u16 indices[] = {0,1,2,2,3,0};
942 const u16 indices_alternate[] = {0,1,3,2,3,1};
944 video::ITexture *texture = f.tile.texture.atlas;
948 material.setTexture(0, texture);
950 f.tile.applyMaterialOptions(material);
952 const u16 *indices_p = indices;
955 Revert triangles for nicer looking gradient if vertices
956 1 and 3 have same color or 0 and 2 have different color.
958 if(f.vertices[0].Color != f.vertices[2].Color
959 || f.vertices[1].Color == f.vertices[3].Color)
960 indices_p = indices_alternate;
962 collector.append(material, f.vertices, 4, indices_p, 6);
967 Add special graphics:
973 //TimeTaker timer2("updateMesh() adding special stuff");
975 // Flowing water material
976 video::SMaterial material_water1;
977 material_water1.setFlag(video::EMF_LIGHTING, false);
978 material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
979 material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
980 material_water1.setFlag(video::EMF_FOG_ENABLE, true);
981 material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
982 AtlasPointer pa_water1 = g_texturesource->getTexture(
983 g_texturesource->getTextureId("water.png"));
984 material_water1.setTexture(0, pa_water1.atlas);
986 // New-style leaves material
987 video::SMaterial material_leaves1;
988 material_leaves1.setFlag(video::EMF_LIGHTING, false);
989 //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
990 material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
991 material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
992 material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
993 AtlasPointer pa_leaves1 = g_texturesource->getTexture(
994 g_texturesource->getTextureId("leaves.png"));
995 material_leaves1.setTexture(0, pa_leaves1.atlas);
998 video::SMaterial material_glass;
999 material_glass.setFlag(video::EMF_LIGHTING, false);
1000 material_glass.setFlag(video::EMF_BILINEAR_FILTER, false);
1001 material_glass.setFlag(video::EMF_FOG_ENABLE, true);
1002 material_glass.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1003 AtlasPointer pa_glass = g_texturesource->getTexture(
1004 g_texturesource->getTextureId("glass.png"));
1005 material_glass.setTexture(0, pa_glass.atlas);
1008 video::SMaterial material_wood;
1009 material_wood.setFlag(video::EMF_LIGHTING, false);
1010 material_wood.setFlag(video::EMF_BILINEAR_FILTER, false);
1011 material_wood.setFlag(video::EMF_FOG_ENABLE, true);
1012 material_wood.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1013 AtlasPointer pa_wood = g_texturesource->getTexture(
1014 g_texturesource->getTextureId("wood.png"));
1015 material_wood.setTexture(0, pa_wood.atlas);
1017 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1018 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
1019 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1023 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
1028 if(n.d == CONTENT_TORCH)
1030 video::SColor c(255,255,255,255);
1032 // Wall at X+ of node
1033 video::S3DVertex vertices[4] =
1035 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
1036 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
1037 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
1038 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
1041 v3s16 dir = unpackDir(n.dir);
1043 for(s32 i=0; i<4; i++)
1045 if(dir == v3s16(1,0,0))
1046 vertices[i].Pos.rotateXZBy(0);
1047 if(dir == v3s16(-1,0,0))
1048 vertices[i].Pos.rotateXZBy(180);
1049 if(dir == v3s16(0,0,1))
1050 vertices[i].Pos.rotateXZBy(90);
1051 if(dir == v3s16(0,0,-1))
1052 vertices[i].Pos.rotateXZBy(-90);
1053 if(dir == v3s16(0,-1,0))
1054 vertices[i].Pos.rotateXZBy(45);
1055 if(dir == v3s16(0,1,0))
1056 vertices[i].Pos.rotateXZBy(-45);
1058 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1062 video::SMaterial material;
1063 material.setFlag(video::EMF_LIGHTING, false);
1064 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
1065 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1066 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1067 material.MaterialType
1068 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1070 if(dir == v3s16(0,-1,0))
1071 material.setTexture(0,
1072 g_texturesource->getTextureRaw("torch_on_floor.png"));
1073 else if(dir == v3s16(0,1,0))
1074 material.setTexture(0,
1075 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
1076 // For backwards compatibility
1077 else if(dir == v3s16(0,0,0))
1078 material.setTexture(0,
1079 g_texturesource->getTextureRaw("torch_on_floor.png"));
1081 material.setTexture(0,
1082 g_texturesource->getTextureRaw("torch.png"));
1084 u16 indices[] = {0,1,2,2,3,0};
1085 // Add to mesh collector
1086 collector.append(material, vertices, 4, indices, 6);
1091 if(n.d == CONTENT_SIGN_WALL)
1093 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1094 video::SColor c(255,l,l,l);
1096 float d = (float)BS/16;
1097 // Wall at X+ of node
1098 video::S3DVertex vertices[4] =
1100 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
1101 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
1102 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
1103 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
1106 v3s16 dir = unpackDir(n.dir);
1108 for(s32 i=0; i<4; i++)
1110 if(dir == v3s16(1,0,0))
1111 vertices[i].Pos.rotateXZBy(0);
1112 if(dir == v3s16(-1,0,0))
1113 vertices[i].Pos.rotateXZBy(180);
1114 if(dir == v3s16(0,0,1))
1115 vertices[i].Pos.rotateXZBy(90);
1116 if(dir == v3s16(0,0,-1))
1117 vertices[i].Pos.rotateXZBy(-90);
1118 if(dir == v3s16(0,-1,0))
1119 vertices[i].Pos.rotateXYBy(-90);
1120 if(dir == v3s16(0,1,0))
1121 vertices[i].Pos.rotateXYBy(90);
1123 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1127 video::SMaterial material;
1128 material.setFlag(video::EMF_LIGHTING, false);
1129 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
1130 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1131 material.setFlag(video::EMF_FOG_ENABLE, true);
1132 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1133 material.MaterialType
1134 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1136 material.setTexture(0,
1137 g_texturesource->getTextureRaw("sign_wall.png"));
1139 u16 indices[] = {0,1,2,2,3,0};
1140 // Add to mesh collector
1141 collector.append(material, vertices, 4, indices, 6);
1144 Add flowing water to mesh
1146 else if(n.d == CONTENT_WATER)
1148 bool top_is_water = false;
1149 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1150 if(ntop.d == CONTENT_WATER || ntop.d == CONTENT_WATERSOURCE)
1151 top_is_water = true;
1154 // Use the light of the node on top if possible
1155 if(content_features(ntop.d).param_type == CPT_LIGHT)
1156 l = decode_light(ntop.getLightBlend(data->m_daynight_ratio));
1157 // Otherwise use the light of this node (the water)
1159 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1160 video::SColor c(WATER_ALPHA,l,l,l);
1162 // Neighbor water levels (key = relative position)
1163 // Includes current node
1164 core::map<v3s16, f32> neighbor_levels;
1165 core::map<v3s16, u8> neighbor_contents;
1166 core::map<v3s16, u8> neighbor_flags;
1167 const u8 neighborflag_top_is_water = 0x01;
1168 v3s16 neighbor_dirs[9] = {
1179 for(u32 i=0; i<9; i++)
1181 u8 content = CONTENT_AIR;
1182 float level = -0.5 * BS;
1185 v3s16 p2 = p + neighbor_dirs[i];
1186 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1187 if(n2.d != CONTENT_IGNORE)
1191 if(n2.d == CONTENT_WATERSOURCE)
1192 level = (-0.5+node_water_level) * BS;
1193 else if(n2.d == CONTENT_WATER)
1194 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
1195 * node_water_level) * BS;
1197 // Check node above neighbor.
1198 // NOTE: This doesn't get executed if neighbor
1201 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1202 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
1203 flags |= neighborflag_top_is_water;
1206 neighbor_levels.insert(neighbor_dirs[i], level);
1207 neighbor_contents.insert(neighbor_dirs[i], content);
1208 neighbor_flags.insert(neighbor_dirs[i], flags);
1211 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
1212 //float water_level = neighbor_levels[v3s16(0,0,0)];
1214 // Corner heights (average between four waters)
1215 f32 corner_levels[4];
1217 v3s16 halfdirs[4] = {
1223 for(u32 i=0; i<4; i++)
1225 v3s16 cornerdir = halfdirs[i];
1226 float cornerlevel = 0;
1227 u32 valid_count = 0;
1228 for(u32 j=0; j<4; j++)
1230 v3s16 neighbordir = cornerdir - halfdirs[j];
1231 u8 content = neighbor_contents[neighbordir];
1232 // Special case for source nodes
1233 if(content == CONTENT_WATERSOURCE)
1235 cornerlevel = (-0.5+node_water_level)*BS;
1239 else if(content == CONTENT_WATER)
1241 cornerlevel += neighbor_levels[neighbordir];
1244 else if(content == CONTENT_AIR)
1246 cornerlevel += -0.5*BS;
1251 cornerlevel /= valid_count;
1252 corner_levels[i] = cornerlevel;
1259 v3s16 side_dirs[4] = {
1265 s16 side_corners[4][2] = {
1271 for(u32 i=0; i<4; i++)
1273 v3s16 dir = side_dirs[i];
1276 If our topside is water and neighbor's topside
1277 is water, don't draw side face
1280 neighbor_flags[dir] & neighborflag_top_is_water)
1283 u8 neighbor_content = neighbor_contents[dir];
1285 // Don't draw face if neighbor is not air or water
1286 if(neighbor_content != CONTENT_AIR
1287 && neighbor_content != CONTENT_WATER)
1290 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1292 // Don't draw any faces if neighbor is water and top is water
1293 if(neighbor_is_water == true && top_is_water == false)
1296 video::S3DVertex vertices[4] =
1298 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1299 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1300 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1301 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1302 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1303 pa_water1.x0(), pa_water1.y1()),
1304 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1305 pa_water1.x1(), pa_water1.y1()),
1306 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1307 pa_water1.x1(), pa_water1.y0()),
1308 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1309 pa_water1.x0(), pa_water1.y0()),
1313 If our topside is water, set upper border of face
1314 at upper border of node
1318 vertices[2].Pos.Y = 0.5*BS;
1319 vertices[3].Pos.Y = 0.5*BS;
1322 Otherwise upper position of face is corner levels
1326 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1327 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1331 If neighbor is water, lower border of face is corner
1334 if(neighbor_is_water)
1336 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1337 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1340 If neighbor is not water, lower border of face is
1341 lower border of node
1345 vertices[0].Pos.Y = -0.5*BS;
1346 vertices[1].Pos.Y = -0.5*BS;
1349 for(s32 j=0; j<4; j++)
1351 if(dir == v3s16(0,0,1))
1352 vertices[j].Pos.rotateXZBy(0);
1353 if(dir == v3s16(0,0,-1))
1354 vertices[j].Pos.rotateXZBy(180);
1355 if(dir == v3s16(-1,0,0))
1356 vertices[j].Pos.rotateXZBy(90);
1357 if(dir == v3s16(1,0,-0))
1358 vertices[j].Pos.rotateXZBy(-90);
1360 vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
1363 u16 indices[] = {0,1,2,2,3,0};
1364 // Add to mesh collector
1365 collector.append(material_water1, vertices, 4, indices, 6);
1369 Generate top side, if appropriate
1372 if(top_is_water == false)
1374 video::S3DVertex vertices[4] =
1376 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1377 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1378 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1379 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1380 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1381 pa_water1.x0(), pa_water1.y1()),
1382 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1383 pa_water1.x1(), pa_water1.y1()),
1384 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1385 pa_water1.x1(), pa_water1.y0()),
1386 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1387 pa_water1.x0(), pa_water1.y0()),
1390 // This fixes a strange bug
1391 s32 corner_resolve[4] = {3,2,1,0};
1393 for(s32 i=0; i<4; i++)
1395 //vertices[i].Pos.Y += water_level;
1396 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1397 s32 j = corner_resolve[i];
1398 vertices[i].Pos.Y += corner_levels[j];
1399 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1402 u16 indices[] = {0,1,2,2,3,0};
1403 // Add to mesh collector
1404 collector.append(material_water1, vertices, 4, indices, 6);
1408 Add water sources to mesh if using new style
1410 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1412 //bool top_is_water = false;
1413 bool top_is_air = false;
1414 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1415 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1416 top_is_water = true;*/
1417 if(n.d == CONTENT_AIR)
1420 /*if(top_is_water == true)
1422 if(top_is_air == false)
1425 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1426 video::SColor c(WATER_ALPHA,l,l,l);
1428 video::S3DVertex vertices[4] =
1430 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1431 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1432 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1433 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1434 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1435 pa_water1.x0(), pa_water1.y1()),
1436 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1437 pa_water1.x1(), pa_water1.y1()),
1438 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1439 pa_water1.x1(), pa_water1.y0()),
1440 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1441 pa_water1.x0(), pa_water1.y0()),
1444 for(s32 i=0; i<4; i++)
1446 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1447 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1450 u16 indices[] = {0,1,2,2,3,0};
1451 // Add to mesh collector
1452 collector.append(material_water1, vertices, 4, indices, 6);
1455 Add leaves if using new style
1457 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1459 /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
1460 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1461 video::SColor c(255,l,l,l);
1463 for(u32 j=0; j<6; j++)
1465 video::S3DVertex vertices[4] =
1467 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1468 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1469 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1470 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1471 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1472 pa_leaves1.x0(), pa_leaves1.y1()),
1473 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1474 pa_leaves1.x1(), pa_leaves1.y1()),
1475 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1476 pa_leaves1.x1(), pa_leaves1.y0()),
1477 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1478 pa_leaves1.x0(), pa_leaves1.y0()),
1483 for(u16 i=0; i<4; i++)
1484 vertices[i].Pos.rotateXZBy(0);
1488 for(u16 i=0; i<4; i++)
1489 vertices[i].Pos.rotateXZBy(180);
1493 for(u16 i=0; i<4; i++)
1494 vertices[i].Pos.rotateXZBy(-90);
1498 for(u16 i=0; i<4; i++)
1499 vertices[i].Pos.rotateXZBy(90);
1503 for(u16 i=0; i<4; i++)
1504 vertices[i].Pos.rotateYZBy(-90);
1508 for(u16 i=0; i<4; i++)
1509 vertices[i].Pos.rotateYZBy(90);
1512 for(u16 i=0; i<4; i++)
1514 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1517 u16 indices[] = {0,1,2,2,3,0};
1518 // Add to mesh collector
1519 collector.append(material_leaves1, vertices, 4, indices, 6);
1525 else if(n.d == CONTENT_GLASS)
1527 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1528 video::SColor c(255,l,l,l);
1530 for(u32 j=0; j<6; j++)
1532 video::S3DVertex vertices[4] =
1534 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1535 pa_glass.x0(), pa_glass.y1()),
1536 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1537 pa_glass.x1(), pa_glass.y1()),
1538 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1539 pa_glass.x1(), pa_glass.y0()),
1540 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1541 pa_glass.x0(), pa_glass.y0()),
1546 for(u16 i=0; i<4; i++)
1547 vertices[i].Pos.rotateXZBy(0);
1551 for(u16 i=0; i<4; i++)
1552 vertices[i].Pos.rotateXZBy(180);
1556 for(u16 i=0; i<4; i++)
1557 vertices[i].Pos.rotateXZBy(-90);
1561 for(u16 i=0; i<4; i++)
1562 vertices[i].Pos.rotateXZBy(90);
1566 for(u16 i=0; i<4; i++)
1567 vertices[i].Pos.rotateYZBy(-90);
1571 for(u16 i=0; i<4; i++)
1572 vertices[i].Pos.rotateYZBy(90);
1575 for(u16 i=0; i<4; i++)
1577 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1580 u16 indices[] = {0,1,2,2,3,0};
1581 // Add to mesh collector
1582 collector.append(material_glass, vertices, 4, indices, 6);
1588 else if(n.d == CONTENT_FENCE)
1590 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1591 video::SColor c(255,l,l,l);
1593 const f32 post_rad=(f32)BS/10;
1594 const f32 bar_rad=(f32)BS/20;
1595 const f32 bar_len=(f32)(BS/2)-post_rad;
1597 // The post - always present
1598 v3f pos = intToFloat(p+blockpos_nodes, BS);
1606 makeCuboid(material_wood, &collector,
1608 post_rad,BS/2,post_rad, postuv);
1610 // Now a section of fence, +X, if there's a post there
1613 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1614 if(n2.d == CONTENT_FENCE)
1616 pos = intToFloat(p+blockpos_nodes, BS);
1626 makeCuboid(material_wood, &collector,
1628 bar_len,bar_rad,bar_rad, xrailuv);
1631 makeCuboid(material_wood, &collector,
1633 bar_len,bar_rad,bar_rad, xrailuv);
1636 // Now a section of fence, +Z, if there's a post there
1639 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1640 if(n2.d == CONTENT_FENCE)
1642 pos = intToFloat(p+blockpos_nodes, BS);
1652 makeCuboid(material_wood, &collector,
1654 bar_rad,bar_rad,bar_len, zrailuv);
1656 makeCuboid(material_wood, &collector,
1658 bar_rad,bar_rad,bar_len, zrailuv);
1669 Add stuff from collector to mesh
1672 scene::SMesh *mesh_new = NULL;
1673 mesh_new = new scene::SMesh();
1675 collector.fillMesh(mesh_new);
1678 Do some stuff to the mesh
1681 mesh_new->recalculateBoundingBox();
1684 Delete new mesh if it is empty
1687 if(mesh_new->getMeshBufferCount() == 0)
1696 // Usually 1-700 faces and 1-7 materials
1697 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1698 <<"and uses "<<mesh_new->getMeshBufferCount()
1699 <<" materials (meshbuffers)"<<std::endl;
1702 // Use VBO for mesh (this just would set this for ever buffer)
1703 // This will lead to infinite memory usage because or irrlicht.
1704 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1707 NOTE: If that is enabled, some kind of a queue to the main
1708 thread should be made which would call irrlicht to delete
1709 the hardware buffer and then delete the mesh
1715 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1724 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
1728 is_underground(false),
1729 m_lighting_expired(true),
1730 m_day_night_differs(false),
1731 //m_not_fully_generated(false),
1733 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED)
1739 //m_spawn_timer = -10000;
1742 m_mesh_expired = false;
1745 m_temp_mods_mutex.Init();
1749 MapBlock::~MapBlock()
1753 JMutexAutoLock lock(mesh_mutex);
1767 bool MapBlock::isValidPositionParent(v3s16 p)
1769 if(isValidPosition(p))
1774 return m_parent->isValidPosition(getPosRelative() + p);
1778 MapNode MapBlock::getNodeParent(v3s16 p)
1780 if(isValidPosition(p) == false)
1782 return m_parent->getNode(getPosRelative() + p);
1787 throw InvalidPositionException();
1788 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1792 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
1794 if(isValidPosition(p) == false)
1796 m_parent->setNode(getPosRelative() + p, n);
1801 throw InvalidPositionException();
1802 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
1806 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
1808 if(isValidPosition(p) == false)
1811 return m_parent->getNode(getPosRelative() + p);
1813 catch(InvalidPositionException &e)
1815 return MapNode(CONTENT_IGNORE);
1822 return MapNode(CONTENT_IGNORE);
1824 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1831 void MapBlock::updateMesh(u32 daynight_ratio)
1835 DEBUG: If mesh has been generated, don't generate it again
1838 JMutexAutoLock meshlock(mesh_mutex);
1845 data.fill(daynight_ratio, this);
1847 scene::SMesh *mesh_new = makeMapBlockMesh(&data);
1853 replaceMesh(mesh_new);
1858 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
1862 //scene::SMesh *mesh_old = mesh[daynight_i];
1863 //mesh[daynight_i] = mesh_new;
1865 scene::SMesh *mesh_old = mesh;
1867 setMeshExpired(false);
1869 if(mesh_old != NULL)
1871 // Remove hardware buffers of meshbuffers of mesh
1872 // NOTE: No way, this runs in a different thread and everything
1873 /*u32 c = mesh_old->getMeshBufferCount();
1874 for(u32 i=0; i<c; i++)
1876 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1879 /*dstream<<"mesh_old->getReferenceCount()="
1880 <<mesh_old->getReferenceCount()<<std::endl;
1881 u32 c = mesh_old->getMeshBufferCount();
1882 for(u32 i=0; i<c; i++)
1884 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1885 dstream<<"buf->getReferenceCount()="
1886 <<buf->getReferenceCount()<<std::endl;
1895 mesh_mutex.Unlock();
1901 Propagates sunlight down through the block.
1902 Doesn't modify nodes that are not affected by sunlight.
1904 Returns false if sunlight at bottom block is invalid.
1905 Returns true if sunlight at bottom block is valid.
1906 Returns true if bottom block doesn't exist.
1908 If there is a block above, continues from it.
1909 If there is no block above, assumes there is sunlight, unless
1910 is_underground is set or highest node is water.
1912 All sunlighted nodes are added to light_sources.
1914 If grow_grass==true, turns sunglighted mud into grass.
1916 if remove_light==true, sets non-sunlighted nodes black.
1918 if black_air_left!=NULL, it is set to true if non-sunlighted
1919 air is left in block.
1921 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1922 bool remove_light, bool *black_air_left,
1925 // Whether the sunlight at the top of the bottom block is valid
1926 bool block_below_is_valid = true;
1928 v3s16 pos_relative = getPosRelative();
1930 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1932 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1935 bool no_sunlight = false;
1936 bool no_top_block = false;
1937 // Check if node above block has sunlight
1939 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1940 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1945 catch(InvalidPositionException &e)
1947 no_top_block = true;
1949 // NOTE: This makes over-ground roofed places sunlighted
1950 // Assume sunlight, unless is_underground==true
1957 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1958 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1963 // NOTE: As of now, this just would make everything dark.
1965 //no_sunlight = true;
1968 #if 0 // Doesn't work; nothing gets light.
1969 bool no_sunlight = true;
1970 bool no_top_block = false;
1971 // Check if node above block has sunlight
1973 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1974 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1976 no_sunlight = false;
1979 catch(InvalidPositionException &e)
1981 no_top_block = true;
1985 /*std::cout<<"("<<x<<","<<z<<"): "
1986 <<"no_top_block="<<no_top_block
1987 <<", is_underground="<<is_underground
1988 <<", no_sunlight="<<no_sunlight
1991 s16 y = MAP_BLOCKSIZE-1;
1993 // This makes difference to diminishing in water.
1994 bool stopped_to_solid_object = false;
1996 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
2001 MapNode &n = getNodeRef(pos);
2003 if(current_light == 0)
2007 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
2009 // Do nothing: Sunlight is continued
2011 else if(n.light_propagates() == false)
2015 bool upper_is_air = false;
2018 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
2019 upper_is_air = true;
2021 catch(InvalidPositionException &e)
2024 // Turn mud into grass
2025 if(upper_is_air && n.d == CONTENT_MUD
2026 && current_light == LIGHT_SUN)
2028 n.d = CONTENT_GRASS;
2032 // A solid object is on the way.
2033 stopped_to_solid_object = true;
2041 current_light = diminish_light(current_light);
2044 u8 old_light = n.getLight(LIGHTBANK_DAY);
2046 if(current_light > old_light || remove_light)
2048 n.setLight(LIGHTBANK_DAY, current_light);
2051 if(diminish_light(current_light) != 0)
2053 light_sources.insert(pos_relative + pos, true);
2056 if(current_light == 0 && stopped_to_solid_object)
2060 *black_air_left = true;
2065 // Whether or not the block below should see LIGHT_SUN
2066 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
2069 If the block below hasn't already been marked invalid:
2071 Check if the node below the block has proper sunlight at top.
2072 If not, the block below is invalid.
2074 Ignore non-transparent nodes as they always have no light
2078 if(block_below_is_valid)
2080 MapNode n = getNodeParent(v3s16(x, -1, z));
2081 if(n.light_propagates())
2083 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
2084 && sunlight_should_go_down == false)
2085 block_below_is_valid = false;
2086 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
2087 && sunlight_should_go_down == true)
2088 block_below_is_valid = false;
2092 catch(InvalidPositionException &e)
2094 /*std::cout<<"InvalidBlockException for bottom block node"
2096 // Just no block below, no need to panic.
2101 return block_below_is_valid;
2105 void MapBlock::copyTo(VoxelManipulator &dst)
2107 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
2108 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
2110 // Copy from data to VoxelManipulator
2111 dst.copyFrom(data, data_area, v3s16(0,0,0),
2112 getPosRelative(), data_size);
2115 void MapBlock::copyFrom(VoxelManipulator &dst)
2117 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
2118 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
2120 // Copy from VoxelManipulator to data
2121 dst.copyTo(data, data_area, v3s16(0,0,0),
2122 getPosRelative(), data_size);
2125 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
2130 m_objects.step(dtime, server, daynight_ratio);
2136 void MapBlock::updateDayNightDiff()
2140 m_day_night_differs = false;
2144 bool differs = false;
2147 Check if any lighting value differs
2149 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2151 MapNode &n = data[i];
2152 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
2160 If some lighting values differ, check if the whole thing is
2161 just air. If it is, differ = false
2165 bool only_air = true;
2166 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2168 MapNode &n = data[i];
2169 if(n.d != CONTENT_AIR)
2179 // Set member variable
2180 m_day_night_differs = differs;
2183 s16 MapBlock::getGroundLevel(v2s16 p2d)
2189 s16 y = MAP_BLOCKSIZE-1;
2192 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
2193 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
2195 if(y == MAP_BLOCKSIZE-1)
2203 catch(InvalidPositionException &e)
2213 void MapBlock::serialize(std::ostream &os, u8 version)
2215 if(!ser_ver_supported(version))
2216 throw VersionMismatchException("ERROR: MapBlock format not supported");
2220 throw SerializationError("ERROR: Not writing dummy block.");
2223 // These have no compression
2224 if(version <= 3 || version == 5 || version == 6)
2226 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2228 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
2229 SharedBuffer<u8> dest(buflen);
2231 dest[0] = is_underground;
2232 for(u32 i=0; i<nodecount; i++)
2234 u32 s = 1 + i * MapNode::serializedLength(version);
2235 data[i].serialize(&dest[s], version);
2238 os.write((char*)*dest, dest.getSize());
2240 else if(version <= 10)
2244 Compress the materials and the params separately.
2248 os.write((char*)&is_underground, 1);
2250 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2252 // Get and compress materials
2253 SharedBuffer<u8> materialdata(nodecount);
2254 for(u32 i=0; i<nodecount; i++)
2256 materialdata[i] = data[i].d;
2258 compress(materialdata, os, version);
2260 // Get and compress lights
2261 SharedBuffer<u8> lightdata(nodecount);
2262 for(u32 i=0; i<nodecount; i++)
2264 lightdata[i] = data[i].param;
2266 compress(lightdata, os, version);
2270 // Get and compress param2
2271 SharedBuffer<u8> param2data(nodecount);
2272 for(u32 i=0; i<nodecount; i++)
2274 param2data[i] = data[i].param2;
2276 compress(param2data, os, version);
2279 // All other versions (newest)
2286 if(m_day_night_differs)
2288 if(m_lighting_expired)
2290 os.write((char*)&flags, 1);
2292 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2298 SharedBuffer<u8> databuf(nodecount*3);
2301 for(u32 i=0; i<nodecount; i++)
2303 databuf[i] = data[i].d;
2307 for(u32 i=0; i<nodecount; i++)
2309 databuf[i+nodecount] = data[i].param;
2313 for(u32 i=0; i<nodecount; i++)
2315 databuf[i+nodecount*2] = data[i].param2;
2319 Compress data to output stream
2322 compress(databuf, os, version);
2332 std::ostringstream oss(std::ios_base::binary);
2333 m_node_metadata.serialize(oss);
2334 os<<serializeString(oss.str());
2336 // This will happen if the string is longer than 65535
2337 catch(SerializationError &e)
2339 // Use an empty string
2340 os<<serializeString("");
2345 std::ostringstream oss(std::ios_base::binary);
2346 m_node_metadata.serialize(oss);
2347 compressZlib(oss.str(), os);
2348 //os<<serializeLongString(oss.str());
2354 void MapBlock::deSerialize(std::istream &is, u8 version)
2356 if(!ser_ver_supported(version))
2357 throw VersionMismatchException("ERROR: MapBlock format not supported");
2359 // These have no lighting info
2362 setLightingExpired(true);
2365 // These have no compression
2366 if(version <= 3 || version == 5 || version == 6)
2368 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2371 if(is.gcount() != 1)
2372 throw SerializationError
2373 ("MapBlock::deSerialize: no enough input data");
2374 is_underground = tmp;
2375 for(u32 i=0; i<nodecount; i++)
2377 s32 len = MapNode::serializedLength(version);
2378 SharedBuffer<u8> d(len);
2379 is.read((char*)*d, len);
2380 if(is.gcount() != len)
2381 throw SerializationError
2382 ("MapBlock::deSerialize: no enough input data");
2383 data[i].deSerialize(*d, version);
2386 else if(version <= 10)
2388 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2391 is.read((char*)&t8, 1);
2392 is_underground = t8;
2395 // Uncompress and set material data
2396 std::ostringstream os(std::ios_base::binary);
2397 decompress(is, os, version);
2398 std::string s = os.str();
2399 if(s.size() != nodecount)
2400 throw SerializationError
2401 ("MapBlock::deSerialize: invalid format");
2402 for(u32 i=0; i<s.size(); i++)
2408 // Uncompress and set param data
2409 std::ostringstream os(std::ios_base::binary);
2410 decompress(is, os, version);
2411 std::string s = os.str();
2412 if(s.size() != nodecount)
2413 throw SerializationError
2414 ("MapBlock::deSerialize: invalid format");
2415 for(u32 i=0; i<s.size(); i++)
2417 data[i].param = s[i];
2423 // Uncompress and set param2 data
2424 std::ostringstream os(std::ios_base::binary);
2425 decompress(is, os, version);
2426 std::string s = os.str();
2427 if(s.size() != nodecount)
2428 throw SerializationError
2429 ("MapBlock::deSerialize: invalid format");
2430 for(u32 i=0; i<s.size(); i++)
2432 data[i].param2 = s[i];
2436 // All other versions (newest)
2439 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2442 is.read((char*)&flags, 1);
2443 is_underground = (flags & 0x01) ? true : false;
2444 m_day_night_differs = (flags & 0x02) ? true : false;
2445 m_lighting_expired = (flags & 0x04) ? true : false;
2448 std::ostringstream os(std::ios_base::binary);
2449 decompress(is, os, version);
2450 std::string s = os.str();
2451 if(s.size() != nodecount*3)
2452 throw SerializationError
2453 ("MapBlock::deSerialize: invalid format");
2456 for(u32 i=0; i<nodecount; i++)
2461 for(u32 i=0; i<nodecount; i++)
2463 data[i].param = s[i+nodecount];
2466 for(u32 i=0; i<nodecount; i++)
2468 data[i].param2 = s[i+nodecount*2];
2480 std::string data = deSerializeString(is);
2481 std::istringstream iss(data, std::ios_base::binary);
2482 m_node_metadata.deSerialize(iss);
2486 //std::string data = deSerializeLongString(is);
2487 std::ostringstream oss(std::ios_base::binary);
2488 decompressZlib(is, oss);
2489 std::istringstream iss(oss.str(), std::ios_base::binary);
2490 m_node_metadata.deSerialize(iss);
2493 catch(SerializationError &e)
2495 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
2496 <<" while deserializing node metadata"<<std::endl;
2502 Translate nodes as specified in the translate_to fields of
2505 NOTE: This isn't really used. Should it be removed?
2507 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2509 MapNode &n = data[i];
2511 MapNode *translate_to = content_features(n.d).translate_to;
2514 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2515 <<translate_to->d<<std::endl;
2521 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
2523 // Versions up from 9 have block objects.
2526 serializeObjects(os, version);
2529 // Versions up from 15 have static objects.
2532 m_static_objects.serialize(os);
2538 writeU32(os, getTimestamp());
2542 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
2545 Versions up from 9 have block objects.
2549 updateObjects(is, version, NULL, 0);
2553 Versions up from 15 have static objects.
2557 m_static_objects.deSerialize(is);
2563 setTimestamp(readU32(is));
2567 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);