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.
27 // TODO: Move content-aware mesh generation to a separate file
28 #include "content_mapnode.h"
31 void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
33 m_daynight_ratio = daynight_ratio;
34 m_blockpos = block->getPos();
36 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
39 There is no harm not copying the TempMods of the neighbors
40 because they are already copied to this block
43 block->copyTempMods(m_temp_mods);
49 // Allocate this block + neighbors
51 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
52 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
55 //TimeTaker timer("copy central block data");
59 block->copyTo(m_vmanip);
62 //TimeTaker timer("copy neighbor block data");
66 Copy neighbors. This is lightning fast.
67 Copying only the borders would be *very* slow.
71 NodeContainer *parentcontainer = block->getParent();
72 // This will only work if the parent is the map
73 assert(parentcontainer->nodeContainerId() == NODECONTAINER_ID_MAP);
74 // OK, we have the map!
75 Map *map = (Map*)parentcontainer;
77 for(u16 i=0; i<6; i++)
79 const v3s16 &dir = g_6dirs[i];
80 v3s16 bp = m_blockpos + dir;
81 MapBlock *b = map->getBlockNoCreateNoEx(bp);
90 Parameters must consist of air and !air.
93 If either of the nodes doesn't exist, light is 0.
96 daynight_ratio: 0...1000
98 n2: getNodeParent(p + face_dir)
99 face_dir: axis oriented unit vector from p to p2
101 returns encoded light value.
103 u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
108 u8 l1 = n.getLightBlend(daynight_ratio);
109 u8 l2 = n2.getLightBlend(daynight_ratio);
115 // Make some nice difference to different sides
117 // This makes light come from a corner
118 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
119 light = diminish_light(diminish_light(light));
120 else if(face_dir.X == -1 || face_dir.Z == -1)
121 light = diminish_light(light);*/
123 // All neighboring faces have different shade (like in minecraft)
124 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
125 light = diminish_light(diminish_light(light));
126 else if(face_dir.Z == 1 || face_dir.Z == -1)
127 light = diminish_light(light);
131 catch(InvalidPositionException &e)
140 vertex_dirs: v3s16[4]
142 void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
145 If looked from outside the node towards the face, the corners are:
151 if(dir == v3s16(0,0,1))
153 // If looking towards z+, this is the face that is behind
154 // the center point, facing towards z+.
155 vertex_dirs[0] = v3s16(-1,-1, 1);
156 vertex_dirs[1] = v3s16( 1,-1, 1);
157 vertex_dirs[2] = v3s16( 1, 1, 1);
158 vertex_dirs[3] = v3s16(-1, 1, 1);
160 else if(dir == v3s16(0,0,-1))
163 vertex_dirs[0] = v3s16( 1,-1,-1);
164 vertex_dirs[1] = v3s16(-1,-1,-1);
165 vertex_dirs[2] = v3s16(-1, 1,-1);
166 vertex_dirs[3] = v3s16( 1, 1,-1);
168 else if(dir == v3s16(1,0,0))
171 vertex_dirs[0] = v3s16( 1,-1, 1);
172 vertex_dirs[1] = v3s16( 1,-1,-1);
173 vertex_dirs[2] = v3s16( 1, 1,-1);
174 vertex_dirs[3] = v3s16( 1, 1, 1);
176 else if(dir == v3s16(-1,0,0))
179 vertex_dirs[0] = v3s16(-1,-1,-1);
180 vertex_dirs[1] = v3s16(-1,-1, 1);
181 vertex_dirs[2] = v3s16(-1, 1, 1);
182 vertex_dirs[3] = v3s16(-1, 1,-1);
184 else if(dir == v3s16(0,1,0))
186 // faces towards Y+ (assume Z- as "down" in texture)
187 vertex_dirs[0] = v3s16( 1, 1,-1);
188 vertex_dirs[1] = v3s16(-1, 1,-1);
189 vertex_dirs[2] = v3s16(-1, 1, 1);
190 vertex_dirs[3] = v3s16( 1, 1, 1);
192 else if(dir == v3s16(0,-1,0))
194 // faces towards Y- (assume Z+ as "down" in texture)
195 vertex_dirs[0] = v3s16( 1,-1, 1);
196 vertex_dirs[1] = v3s16(-1,-1, 1);
197 vertex_dirs[2] = v3s16(-1,-1,-1);
198 vertex_dirs[3] = v3s16( 1,-1,-1);
202 inline video::SColor lightColor(u8 alpha, u8 light)
204 return video::SColor(alpha,light,light,light);
207 void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
208 v3s16 dir, v3f scale, v3f posRelative_f,
209 core::array<FastFace> &dest)
213 // Position is at the center of the cube.
218 v3s16 vertex_dirs[4];
219 getNodeVertexDirs(dir, vertex_dirs);
220 for(u16 i=0; i<4; i++)
223 BS/2*vertex_dirs[i].X,
224 BS/2*vertex_dirs[i].Y,
225 BS/2*vertex_dirs[i].Z
229 for(u16 i=0; i<4; i++)
231 vertex_pos[i].X *= scale.X;
232 vertex_pos[i].Y *= scale.Y;
233 vertex_pos[i].Z *= scale.Z;
234 vertex_pos[i] += pos + posRelative_f;
238 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
239 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
240 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
242 v3f zerovector = v3f(0,0,0);
244 u8 alpha = tile.alpha;
246 if(tile.id == TILE_WATER)
247 alpha = WATER_ALPHA;*/
249 float x0 = tile.texture.pos.X;
250 float y0 = tile.texture.pos.Y;
251 float w = tile.texture.size.X;
252 float h = tile.texture.size.Y;
254 /*video::SColor c = lightColor(alpha, li);
256 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
257 core::vector2d<f32>(x0+w*abs_scale, y0+h));
258 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
259 core::vector2d<f32>(x0, y0+h));
260 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
261 core::vector2d<f32>(x0, y0));
262 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
263 core::vector2d<f32>(x0+w*abs_scale, y0));*/
265 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0),
266 lightColor(alpha, li0),
267 core::vector2d<f32>(x0+w*abs_scale, y0+h));
268 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0),
269 lightColor(alpha, li1),
270 core::vector2d<f32>(x0, y0+h));
271 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0),
272 lightColor(alpha, li2),
273 core::vector2d<f32>(x0, y0));
274 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0),
275 lightColor(alpha, li3),
276 core::vector2d<f32>(x0+w*abs_scale, y0));
280 //f->tile = TILE_STONE;
282 dest.push_back(face);
286 Gets node tile from any place relative to block.
287 Returns TILE_NODE if doesn't exist or should not be drawn.
289 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
290 NodeModMap &temp_mods)
293 spec = mn.getTile(face_dir);
296 Check temporary modifications on this node
298 /*core::map<v3s16, NodeMod>::Node *n;
299 n = m_temp_mods.find(p);
303 struct NodeMod mod = n->getValue();*/
305 if(temp_mods.get(p, &mod))
307 if(mod.type == NODEMOD_CHANGECONTENT)
309 MapNode mn2(mod.param);
310 spec = mn2.getTile(face_dir);
312 if(mod.type == NODEMOD_CRACK)
315 Get texture id, translate it to name, append stuff to
319 // Get original texture name
320 u32 orig_id = spec.texture.id;
321 std::string orig_name = g_texturesource->getTextureName(orig_id);
323 // Create new texture name
324 std::ostringstream os;
325 os<<orig_name<<"^[crack"<<mod.param;
328 u32 new_id = g_texturesource->getTextureId(os.str());
330 /*dstream<<"MapBlock::getNodeTile(): Switching from "
331 <<orig_name<<" to "<<os.str()<<" ("
332 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
334 spec.texture = g_texturesource->getTexture(new_id);
341 u8 getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
344 Check temporary modifications on this node
346 /*core::map<v3s16, NodeMod>::Node *n;
347 n = m_temp_mods.find(p);
351 struct NodeMod mod = n->getValue();*/
353 if(temp_mods.get(p, &mod))
355 if(mod.type == NODEMOD_CHANGECONTENT)
360 if(mod.type == NODEMOD_CRACK)
363 Content doesn't change.
365 face_contents works just like it should, because
366 there should not be faces between differently cracked
369 If a semi-transparent node is cracked in front an
370 another one, it really doesn't matter whether there
371 is a cracked face drawn in between or not.
390 // Calculate lighting at the XYZ- corner of p
391 u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio)
393 u16 ambient_occlusion = 0;
396 for(u32 i=0; i<8; i++)
398 MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
399 if(content_features(n.d).param_type == CPT_LIGHT)
401 light += decode_light(n.getLightBlend(daynight_ratio));
406 if(n.d != CONTENT_IGNORE)
414 light /= light_count;
416 if(ambient_occlusion > 4)
418 ambient_occlusion -= 4;
419 light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
425 // Calculate lighting at the given corner of p
426 u8 getSmoothLight(v3s16 p, v3s16 corner,
427 VoxelManipulator &vmanip, u32 daynight_ratio)
429 if(corner.X == 1) p.X += 1;
430 else assert(corner.X == -1);
431 if(corner.Y == 1) p.Y += 1;
432 else assert(corner.Y == -1);
433 if(corner.Z == 1) p.Z += 1;
434 else assert(corner.Z == -1);
436 return getSmoothLight(p, vmanip, daynight_ratio);
441 v3s16 blockpos_nodes,
445 VoxelManipulator &vmanip,
446 NodeModMap &temp_mods,
447 bool smooth_lighting,
451 v3s16 &face_dir_corrected,
456 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
457 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
458 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
459 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
462 u8 content0 = getNodeContent(p, n0, temp_mods);
463 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
464 u8 mf = face_contents(content0, content1);
478 face_dir_corrected = face_dir;
483 p_corrected = p + face_dir;
484 face_dir_corrected = -face_dir;
487 if(smooth_lighting == false)
489 lights[0] = lights[1] = lights[2] = lights[3] =
490 decode_light(getFaceLight(daynight_ratio, n0, n1, face_dir));
494 v3s16 vertex_dirs[4];
495 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
496 for(u16 i=0; i<4; i++)
498 lights[i] = getSmoothLight(blockpos_nodes + p_corrected,
499 vertex_dirs[i], vmanip, daynight_ratio);
508 translate_dir: unit vector with only one of x, y or z
509 face_dir: unit vector with only one of x, y or z
511 void updateFastFaceRow(
520 core::array<FastFace> &dest,
521 NodeModMap &temp_mods,
522 VoxelManipulator &vmanip,
523 v3s16 blockpos_nodes,
524 bool smooth_lighting)
528 u16 continuous_tiles_count = 0;
532 v3s16 face_dir_corrected;
535 getTileInfo(blockpos_nodes, p, face_dir, daynight_ratio,
536 vmanip, temp_mods, smooth_lighting,
537 makes_face, p_corrected, face_dir_corrected, lights, tile);
539 for(u16 j=0; j<length; j++)
541 // If tiling can be done, this is set to false in the next step
542 bool next_is_different = true;
546 bool next_makes_face = false;
547 v3s16 next_p_corrected;
548 v3s16 next_face_dir_corrected;
549 u8 next_lights[4] = {0,0,0,0};
552 // If at last position, there is nothing to compare to and
553 // the face must be drawn anyway
556 p_next = p + translate_dir;
558 getTileInfo(blockpos_nodes, p_next, face_dir, daynight_ratio,
559 vmanip, temp_mods, smooth_lighting,
560 next_makes_face, next_p_corrected,
561 next_face_dir_corrected, next_lights,
564 if(next_makes_face == makes_face
565 && next_p_corrected == p_corrected
566 && next_face_dir_corrected == face_dir_corrected
567 && next_lights[0] == lights[0]
568 && next_lights[1] == lights[1]
569 && next_lights[2] == lights[2]
570 && next_lights[3] == lights[3]
571 && next_tile == tile)
573 next_is_different = false;
577 continuous_tiles_count++;
579 // This is set to true if the texture doesn't allow more tiling
580 bool end_of_texture = false;
582 If there is no texture, it can be tiled infinitely.
583 If tiled==0, it means the texture can be tiled infinitely.
584 Otherwise check tiled agains continuous_tiles_count.
586 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
588 if(tile.texture.tiled <= continuous_tiles_count)
589 end_of_texture = true;
592 // Do this to disable tiling textures
593 //end_of_texture = true; //DEBUG
595 if(next_is_different || end_of_texture)
598 Create a face if there should be one
602 // Floating point conversion of the position vector
603 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
604 // Center point of face (kind of)
605 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
608 if(translate_dir.X != 0)
610 scale.X = continuous_tiles_count;
612 if(translate_dir.Y != 0)
614 scale.Y = continuous_tiles_count;
616 if(translate_dir.Z != 0)
618 scale.Z = continuous_tiles_count;
621 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
622 sp, face_dir_corrected, scale,
623 posRelative_f, dest);
626 continuous_tiles_count = 0;
628 makes_face = next_makes_face;
629 p_corrected = next_p_corrected;
630 face_dir_corrected = next_face_dir_corrected;
631 lights[0] = next_lights[0];
632 lights[1] = next_lights[1];
633 lights[2] = next_lights[2];
634 lights[3] = next_lights[3];
643 This is used because CMeshBuffer::append() is very slow
647 video::SMaterial material;
648 core::array<u16> indices;
649 core::array<video::S3DVertex> vertices;
656 video::SMaterial material,
657 const video::S3DVertex* const vertices,
659 const u16* const indices,
663 PreMeshBuffer *p = NULL;
664 for(u32 i=0; i<m_prebuffers.size(); i++)
666 PreMeshBuffer &pp = m_prebuffers[i];
667 if(pp.material != material)
677 pp.material = material;
678 m_prebuffers.push_back(pp);
679 p = &m_prebuffers[m_prebuffers.size()-1];
682 u32 vertex_count = p->vertices.size();
683 for(u32 i=0; i<numIndices; i++)
685 u32 j = indices[i] + vertex_count;
688 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
689 // NOTE: Fix is to just add an another MeshBuffer
691 p->indices.push_back(j);
693 for(u32 i=0; i<numVertices; i++)
695 p->vertices.push_back(vertices[i]);
699 void fillMesh(scene::SMesh *mesh)
701 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
702 <<" meshbuffers"<<std::endl;*/
703 for(u32 i=0; i<m_prebuffers.size(); i++)
705 PreMeshBuffer &p = m_prebuffers[i];
707 /*dstream<<"p.vertices.size()="<<p.vertices.size()
708 <<", p.indices.size()="<<p.indices.size()
713 // This is a "Standard MeshBuffer",
714 // it's a typedeffed CMeshBuffer<video::S3DVertex>
715 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
717 buf->Material = p.material;
718 //((scene::SMeshBuffer*)buf)->Material = p.material;
720 //buf->setHardwareMappingHint(scene::EHM_STATIC);
722 mesh->addMeshBuffer(buf);
726 buf->append(p.vertices.pointer(), p.vertices.size(),
727 p.indices.pointer(), p.indices.size());
732 core::array<PreMeshBuffer> m_prebuffers;
736 // material - the material to use (for all 6 faces)
737 // collector - the MeshCollector for the resulting polygons
738 // pa - texture atlas pointer for the material
739 // c - vertex colour - used for all
740 // pos - the position of the centre of the cuboid
741 // rz,ry,rz - the radius of the cuboid in each dimension
742 // txc - texture coordinates - this is a list of texture coordinates
743 // for the opposite corners of each face - therefore, there
744 // should be (2+2)*6=24 values in the list. Alternatively, pass
745 // NULL to use the entire texture for each face. The order of
746 // the faces in the list is top-backi-right-front-left-bottom
747 // If you specified 0,0,1,1 for each face, that would be the
748 // same as passing NULL.
749 void makeCuboid(video::SMaterial &material, MeshCollector *collector,
750 AtlasPointer* pa, video::SColor &c,
751 v3f &pos, f32 rx, f32 ry, f32 rz, f32* txc)
760 video::S3DVertex v[4] =
762 video::S3DVertex(0,0,0, 0,0,0, c, tu0, tv1),
763 video::S3DVertex(0,0,0, 0,0,0, c, tu1, tv1),
764 video::S3DVertex(0,0,0, 0,0,0, c, tu1, tv0),
765 video::S3DVertex(0,0,0, 0,0,0, c, tu0, tv0)
773 v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz;
774 v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz;
775 v[2].Pos.X= rx; v[2].Pos.Y= ry; v[2].Pos.Z= rz;
776 v[3].Pos.X= rx; v[3].Pos.Y= ry, v[3].Pos.Z=-rz;
779 v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz;
780 v[1].Pos.X= rx; v[1].Pos.Y= ry; v[1].Pos.Z=-rz;
781 v[2].Pos.X= rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz;
782 v[3].Pos.X=-rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz;
785 v[0].Pos.X= rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz;
786 v[1].Pos.X= rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz;
787 v[2].Pos.X= rx; v[2].Pos.Y=-ry; v[2].Pos.Z= rz;
788 v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz;
791 v[0].Pos.X= rx; v[0].Pos.Y= ry; v[0].Pos.Z= rz;
792 v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz;
793 v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z= rz;
794 v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z= rz;
797 v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z= rz;
798 v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z=-rz;
799 v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz;
800 v[3].Pos.X=-rx; v[3].Pos.Y=-ry, v[3].Pos.Z= rz;
803 v[0].Pos.X= rx; v[0].Pos.Y=-ry; v[0].Pos.Z= rz;
804 v[1].Pos.X=-rx; v[1].Pos.Y=-ry; v[1].Pos.Z= rz;
805 v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz;
806 v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz;
812 v[0].TCoords.X=tu0+txus*txc[0]; v[0].TCoords.Y=tv0+txvs*txc[3];
813 v[1].TCoords.X=tu0+txus*txc[2]; v[1].TCoords.Y=tv0+txvs*txc[3];
814 v[2].TCoords.X=tu0+txus*txc[2]; v[2].TCoords.Y=tv0+txvs*txc[1];
815 v[3].TCoords.X=tu0+txus*txc[0]; v[3].TCoords.Y=tv0+txvs*txc[1];
819 for(u16 i=0; i<4; i++)
821 u16 indices[] = {0,1,2,2,3,0};
822 collector->append(material, v, 4, indices, 6);
828 scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
830 // 4-21ms for MAP_BLOCKSIZE=16
831 // 24-155ms for MAP_BLOCKSIZE=32
832 //TimeTaker timer1("makeMapBlockMesh()");
834 core::array<FastFace> fastfaces_new;
836 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
838 // floating point conversion
839 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
844 bool new_style_water = g_settings.getBool("new_style_water");
845 bool new_style_leaves = g_settings.getBool("new_style_leaves");
846 bool smooth_lighting = g_settings.getBool("smooth_lighting");
848 float node_water_level = 1.0;
850 node_water_level = 0.85;
853 We are including the faces of the trailing edges of the block.
854 This means that when something changes, the caller must
855 also update the meshes of the blocks at the leading edges.
857 NOTE: This is the slowest part of this method.
861 // 4-23ms for MAP_BLOCKSIZE=16
862 //TimeTaker timer2("updateMesh() collect");
865 Go through every y,z and get top(y+) faces in rows of x+
867 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
868 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
869 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
870 v3s16(0,y,z), MAP_BLOCKSIZE,
873 v3s16(0,1,0), //face dir
883 Go through every x,y and get right(x+) faces in rows of z+
885 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
886 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
887 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
888 v3s16(x,y,0), MAP_BLOCKSIZE,
901 Go through every y,z and get back(z+) faces in rows of x+
903 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
904 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
905 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
906 v3s16(0,y,z), MAP_BLOCKSIZE,
923 Convert FastFaces to SMesh
926 MeshCollector collector;
928 if(fastfaces_new.size() > 0)
930 // avg 0ms (100ms spikes when loading textures the first time)
931 //TimeTaker timer2("updateMesh() mesh building");
933 video::SMaterial material;
934 material.setFlag(video::EMF_LIGHTING, false);
935 material.setFlag(video::EMF_BILINEAR_FILTER, false);
936 material.setFlag(video::EMF_FOG_ENABLE, true);
937 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
938 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
940 for(u32 i=0; i<fastfaces_new.size(); i++)
942 FastFace &f = fastfaces_new[i];
944 const u16 indices[] = {0,1,2,2,3,0};
945 const u16 indices_alternate[] = {0,1,3,2,3,1};
947 video::ITexture *texture = f.tile.texture.atlas;
951 material.setTexture(0, texture);
953 f.tile.applyMaterialOptions(material);
955 const u16 *indices_p = indices;
958 Revert triangles for nicer looking gradient if vertices
959 1 and 3 have same color or 0 and 2 have different color.
961 if(f.vertices[0].Color != f.vertices[2].Color
962 || f.vertices[1].Color == f.vertices[3].Color)
963 indices_p = indices_alternate;
965 collector.append(material, f.vertices, 4, indices_p, 6);
970 Add special graphics:
976 //TimeTaker timer2("updateMesh() adding special stuff");
978 // Flowing water material
979 video::SMaterial material_water1;
980 material_water1.setFlag(video::EMF_LIGHTING, false);
981 material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
982 material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
983 material_water1.setFlag(video::EMF_FOG_ENABLE, true);
984 material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
985 AtlasPointer pa_water1 = g_texturesource->getTexture(
986 g_texturesource->getTextureId("water.png"));
987 material_water1.setTexture(0, pa_water1.atlas);
989 // New-style leaves material
990 video::SMaterial material_leaves1;
991 material_leaves1.setFlag(video::EMF_LIGHTING, false);
992 //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
993 material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
994 material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
995 material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
996 AtlasPointer pa_leaves1 = g_texturesource->getTexture(
997 g_texturesource->getTextureId("leaves.png"));
998 material_leaves1.setTexture(0, pa_leaves1.atlas);
1001 video::SMaterial material_glass;
1002 material_glass.setFlag(video::EMF_LIGHTING, false);
1003 material_glass.setFlag(video::EMF_BILINEAR_FILTER, false);
1004 material_glass.setFlag(video::EMF_FOG_ENABLE, true);
1005 material_glass.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1006 AtlasPointer pa_glass = g_texturesource->getTexture(
1007 g_texturesource->getTextureId("glass.png"));
1008 material_glass.setTexture(0, pa_glass.atlas);
1011 video::SMaterial material_wood;
1012 material_wood.setFlag(video::EMF_LIGHTING, false);
1013 material_wood.setFlag(video::EMF_BILINEAR_FILTER, false);
1014 material_wood.setFlag(video::EMF_FOG_ENABLE, true);
1015 material_wood.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1016 AtlasPointer pa_wood = g_texturesource->getTexture(
1017 g_texturesource->getTextureId("wood.png"));
1018 material_wood.setTexture(0, pa_wood.atlas);
1020 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1021 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
1022 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1026 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
1031 if(n.d == CONTENT_TORCH)
1033 video::SColor c(255,255,255,255);
1035 // Wall at X+ of node
1036 video::S3DVertex vertices[4] =
1038 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
1039 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
1040 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
1041 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
1044 v3s16 dir = unpackDir(n.dir);
1046 for(s32 i=0; i<4; i++)
1048 if(dir == v3s16(1,0,0))
1049 vertices[i].Pos.rotateXZBy(0);
1050 if(dir == v3s16(-1,0,0))
1051 vertices[i].Pos.rotateXZBy(180);
1052 if(dir == v3s16(0,0,1))
1053 vertices[i].Pos.rotateXZBy(90);
1054 if(dir == v3s16(0,0,-1))
1055 vertices[i].Pos.rotateXZBy(-90);
1056 if(dir == v3s16(0,-1,0))
1057 vertices[i].Pos.rotateXZBy(45);
1058 if(dir == v3s16(0,1,0))
1059 vertices[i].Pos.rotateXZBy(-45);
1061 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1065 video::SMaterial material;
1066 material.setFlag(video::EMF_LIGHTING, false);
1067 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
1068 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1069 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1070 material.MaterialType
1071 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1073 if(dir == v3s16(0,-1,0))
1074 material.setTexture(0,
1075 g_texturesource->getTextureRaw("torch_on_floor.png"));
1076 else if(dir == v3s16(0,1,0))
1077 material.setTexture(0,
1078 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
1079 // For backwards compatibility
1080 else if(dir == v3s16(0,0,0))
1081 material.setTexture(0,
1082 g_texturesource->getTextureRaw("torch_on_floor.png"));
1084 material.setTexture(0,
1085 g_texturesource->getTextureRaw("torch.png"));
1087 u16 indices[] = {0,1,2,2,3,0};
1088 // Add to mesh collector
1089 collector.append(material, vertices, 4, indices, 6);
1094 if(n.d == CONTENT_SIGN_WALL)
1096 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1097 video::SColor c(255,l,l,l);
1099 float d = (float)BS/16;
1100 // Wall at X+ of node
1101 video::S3DVertex vertices[4] =
1103 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
1104 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
1105 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
1106 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
1109 v3s16 dir = unpackDir(n.dir);
1111 for(s32 i=0; i<4; i++)
1113 if(dir == v3s16(1,0,0))
1114 vertices[i].Pos.rotateXZBy(0);
1115 if(dir == v3s16(-1,0,0))
1116 vertices[i].Pos.rotateXZBy(180);
1117 if(dir == v3s16(0,0,1))
1118 vertices[i].Pos.rotateXZBy(90);
1119 if(dir == v3s16(0,0,-1))
1120 vertices[i].Pos.rotateXZBy(-90);
1121 if(dir == v3s16(0,-1,0))
1122 vertices[i].Pos.rotateXYBy(-90);
1123 if(dir == v3s16(0,1,0))
1124 vertices[i].Pos.rotateXYBy(90);
1126 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1130 video::SMaterial material;
1131 material.setFlag(video::EMF_LIGHTING, false);
1132 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
1133 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1134 material.setFlag(video::EMF_FOG_ENABLE, true);
1135 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1136 material.MaterialType
1137 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1139 material.setTexture(0,
1140 g_texturesource->getTextureRaw("sign_wall.png"));
1142 u16 indices[] = {0,1,2,2,3,0};
1143 // Add to mesh collector
1144 collector.append(material, vertices, 4, indices, 6);
1147 Add flowing water to mesh
1149 else if(n.d == CONTENT_WATER)
1151 bool top_is_water = false;
1152 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1153 if(ntop.d == CONTENT_WATER || ntop.d == CONTENT_WATERSOURCE)
1154 top_is_water = true;
1157 // Use the light of the node on top if possible
1158 if(content_features(ntop.d).param_type == CPT_LIGHT)
1159 l = decode_light(ntop.getLightBlend(data->m_daynight_ratio));
1160 // Otherwise use the light of this node (the water)
1162 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1163 video::SColor c(WATER_ALPHA,l,l,l);
1165 // Neighbor water levels (key = relative position)
1166 // Includes current node
1167 core::map<v3s16, f32> neighbor_levels;
1168 core::map<v3s16, u8> neighbor_contents;
1169 core::map<v3s16, u8> neighbor_flags;
1170 const u8 neighborflag_top_is_water = 0x01;
1171 v3s16 neighbor_dirs[9] = {
1182 for(u32 i=0; i<9; i++)
1184 u8 content = CONTENT_AIR;
1185 float level = -0.5 * BS;
1188 v3s16 p2 = p + neighbor_dirs[i];
1189 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1190 if(n2.d != CONTENT_IGNORE)
1194 if(n2.d == CONTENT_WATERSOURCE)
1195 level = (-0.5+node_water_level) * BS;
1196 else if(n2.d == CONTENT_WATER)
1197 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
1198 * node_water_level) * BS;
1200 // Check node above neighbor.
1201 // NOTE: This doesn't get executed if neighbor
1204 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1205 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
1206 flags |= neighborflag_top_is_water;
1209 neighbor_levels.insert(neighbor_dirs[i], level);
1210 neighbor_contents.insert(neighbor_dirs[i], content);
1211 neighbor_flags.insert(neighbor_dirs[i], flags);
1214 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
1215 //float water_level = neighbor_levels[v3s16(0,0,0)];
1217 // Corner heights (average between four waters)
1218 f32 corner_levels[4];
1220 v3s16 halfdirs[4] = {
1226 for(u32 i=0; i<4; i++)
1228 v3s16 cornerdir = halfdirs[i];
1229 float cornerlevel = 0;
1230 u32 valid_count = 0;
1231 for(u32 j=0; j<4; j++)
1233 v3s16 neighbordir = cornerdir - halfdirs[j];
1234 u8 content = neighbor_contents[neighbordir];
1235 // Special case for source nodes
1236 if(content == CONTENT_WATERSOURCE)
1238 cornerlevel = (-0.5+node_water_level)*BS;
1242 else if(content == CONTENT_WATER)
1244 cornerlevel += neighbor_levels[neighbordir];
1247 else if(content == CONTENT_AIR)
1249 cornerlevel += -0.5*BS;
1254 cornerlevel /= valid_count;
1255 corner_levels[i] = cornerlevel;
1262 v3s16 side_dirs[4] = {
1268 s16 side_corners[4][2] = {
1274 for(u32 i=0; i<4; i++)
1276 v3s16 dir = side_dirs[i];
1279 If our topside is water and neighbor's topside
1280 is water, don't draw side face
1283 neighbor_flags[dir] & neighborflag_top_is_water)
1286 u8 neighbor_content = neighbor_contents[dir];
1288 // Don't draw face if neighbor is not air or water
1289 if(neighbor_content != CONTENT_AIR
1290 && neighbor_content != CONTENT_WATER)
1293 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1295 // Don't draw any faces if neighbor is water and top is water
1296 if(neighbor_is_water == true && top_is_water == false)
1299 video::S3DVertex vertices[4] =
1301 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1302 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1303 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1304 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1305 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1306 pa_water1.x0(), pa_water1.y1()),
1307 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1308 pa_water1.x1(), pa_water1.y1()),
1309 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1310 pa_water1.x1(), pa_water1.y0()),
1311 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1312 pa_water1.x0(), pa_water1.y0()),
1316 If our topside is water, set upper border of face
1317 at upper border of node
1321 vertices[2].Pos.Y = 0.5*BS;
1322 vertices[3].Pos.Y = 0.5*BS;
1325 Otherwise upper position of face is corner levels
1329 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1330 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1334 If neighbor is water, lower border of face is corner
1337 if(neighbor_is_water)
1339 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1340 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1343 If neighbor is not water, lower border of face is
1344 lower border of node
1348 vertices[0].Pos.Y = -0.5*BS;
1349 vertices[1].Pos.Y = -0.5*BS;
1352 for(s32 j=0; j<4; j++)
1354 if(dir == v3s16(0,0,1))
1355 vertices[j].Pos.rotateXZBy(0);
1356 if(dir == v3s16(0,0,-1))
1357 vertices[j].Pos.rotateXZBy(180);
1358 if(dir == v3s16(-1,0,0))
1359 vertices[j].Pos.rotateXZBy(90);
1360 if(dir == v3s16(1,0,-0))
1361 vertices[j].Pos.rotateXZBy(-90);
1363 vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
1366 u16 indices[] = {0,1,2,2,3,0};
1367 // Add to mesh collector
1368 collector.append(material_water1, vertices, 4, indices, 6);
1372 Generate top side, if appropriate
1375 if(top_is_water == false)
1377 video::S3DVertex vertices[4] =
1379 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1380 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1381 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1382 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1383 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1384 pa_water1.x0(), pa_water1.y1()),
1385 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1386 pa_water1.x1(), pa_water1.y1()),
1387 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1388 pa_water1.x1(), pa_water1.y0()),
1389 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1390 pa_water1.x0(), pa_water1.y0()),
1393 // This fixes a strange bug
1394 s32 corner_resolve[4] = {3,2,1,0};
1396 for(s32 i=0; i<4; i++)
1398 //vertices[i].Pos.Y += water_level;
1399 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1400 s32 j = corner_resolve[i];
1401 vertices[i].Pos.Y += corner_levels[j];
1402 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1405 u16 indices[] = {0,1,2,2,3,0};
1406 // Add to mesh collector
1407 collector.append(material_water1, vertices, 4, indices, 6);
1411 Add water sources to mesh if using new style
1413 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1415 //bool top_is_water = false;
1416 bool top_is_air = false;
1417 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1418 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1419 top_is_water = true;*/
1420 if(n.d == CONTENT_AIR)
1423 /*if(top_is_water == true)
1425 if(top_is_air == false)
1428 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1429 video::SColor c(WATER_ALPHA,l,l,l);
1431 video::S3DVertex vertices[4] =
1433 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1434 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1435 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1436 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1437 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1438 pa_water1.x0(), pa_water1.y1()),
1439 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1440 pa_water1.x1(), pa_water1.y1()),
1441 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1442 pa_water1.x1(), pa_water1.y0()),
1443 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1444 pa_water1.x0(), pa_water1.y0()),
1447 for(s32 i=0; i<4; i++)
1449 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1450 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1453 u16 indices[] = {0,1,2,2,3,0};
1454 // Add to mesh collector
1455 collector.append(material_water1, vertices, 4, indices, 6);
1458 Add leaves if using new style
1460 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1462 /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
1463 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1464 video::SColor c(255,l,l,l);
1466 for(u32 j=0; j<6; j++)
1468 video::S3DVertex vertices[4] =
1470 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1471 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1472 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1473 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1474 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1475 pa_leaves1.x0(), pa_leaves1.y1()),
1476 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1477 pa_leaves1.x1(), pa_leaves1.y1()),
1478 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1479 pa_leaves1.x1(), pa_leaves1.y0()),
1480 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1481 pa_leaves1.x0(), pa_leaves1.y0()),
1486 for(u16 i=0; i<4; i++)
1487 vertices[i].Pos.rotateXZBy(0);
1491 for(u16 i=0; i<4; i++)
1492 vertices[i].Pos.rotateXZBy(180);
1496 for(u16 i=0; i<4; i++)
1497 vertices[i].Pos.rotateXZBy(-90);
1501 for(u16 i=0; i<4; i++)
1502 vertices[i].Pos.rotateXZBy(90);
1506 for(u16 i=0; i<4; i++)
1507 vertices[i].Pos.rotateYZBy(-90);
1511 for(u16 i=0; i<4; i++)
1512 vertices[i].Pos.rotateYZBy(90);
1515 for(u16 i=0; i<4; i++)
1517 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1520 u16 indices[] = {0,1,2,2,3,0};
1521 // Add to mesh collector
1522 collector.append(material_leaves1, vertices, 4, indices, 6);
1528 else if(n.d == CONTENT_GLASS)
1530 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1531 video::SColor c(255,l,l,l);
1533 for(u32 j=0; j<6; j++)
1535 video::S3DVertex vertices[4] =
1537 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1538 pa_glass.x0(), pa_glass.y1()),
1539 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1540 pa_glass.x1(), pa_glass.y1()),
1541 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1542 pa_glass.x1(), pa_glass.y0()),
1543 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1544 pa_glass.x0(), pa_glass.y0()),
1549 for(u16 i=0; i<4; i++)
1550 vertices[i].Pos.rotateXZBy(0);
1554 for(u16 i=0; i<4; i++)
1555 vertices[i].Pos.rotateXZBy(180);
1559 for(u16 i=0; i<4; i++)
1560 vertices[i].Pos.rotateXZBy(-90);
1564 for(u16 i=0; i<4; i++)
1565 vertices[i].Pos.rotateXZBy(90);
1569 for(u16 i=0; i<4; i++)
1570 vertices[i].Pos.rotateYZBy(-90);
1574 for(u16 i=0; i<4; i++)
1575 vertices[i].Pos.rotateYZBy(90);
1578 for(u16 i=0; i<4; i++)
1580 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1583 u16 indices[] = {0,1,2,2,3,0};
1584 // Add to mesh collector
1585 collector.append(material_glass, vertices, 4, indices, 6);
1591 else if(n.d == CONTENT_FENCE)
1593 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1594 video::SColor c(255,l,l,l);
1596 const f32 post_rad=(f32)BS/10;
1597 const f32 bar_rad=(f32)BS/20;
1598 const f32 bar_len=(f32)(BS/2)-post_rad;
1600 // The post - always present
1601 v3f pos = intToFloat(p+blockpos_nodes, BS);
1609 makeCuboid(material_wood, &collector,
1611 post_rad,BS/2,post_rad, postuv);
1613 // Now a section of fence, +X, if there's a post there
1616 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1617 if(n2.d == CONTENT_FENCE)
1619 pos = intToFloat(p+blockpos_nodes, BS);
1629 makeCuboid(material_wood, &collector,
1631 bar_len,bar_rad,bar_rad, xrailuv);
1634 makeCuboid(material_wood, &collector,
1636 bar_len,bar_rad,bar_rad, xrailuv);
1639 // Now a section of fence, +Z, if there's a post there
1642 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1643 if(n2.d == CONTENT_FENCE)
1645 pos = intToFloat(p+blockpos_nodes, BS);
1655 makeCuboid(material_wood, &collector,
1657 bar_rad,bar_rad,bar_len, zrailuv);
1659 makeCuboid(material_wood, &collector,
1661 bar_rad,bar_rad,bar_len, zrailuv);
1672 Add stuff from collector to mesh
1675 scene::SMesh *mesh_new = NULL;
1676 mesh_new = new scene::SMesh();
1678 collector.fillMesh(mesh_new);
1681 Do some stuff to the mesh
1684 mesh_new->recalculateBoundingBox();
1687 Delete new mesh if it is empty
1690 if(mesh_new->getMeshBufferCount() == 0)
1699 // Usually 1-700 faces and 1-7 materials
1700 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1701 <<"and uses "<<mesh_new->getMeshBufferCount()
1702 <<" materials (meshbuffers)"<<std::endl;
1705 // Use VBO for mesh (this just would set this for ever buffer)
1706 // This will lead to infinite memory usage because or irrlicht.
1707 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1710 NOTE: If that is enabled, some kind of a queue to the main
1711 thread should be made which would call irrlicht to delete
1712 the hardware buffer and then delete the mesh
1718 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1727 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
1731 is_underground(false),
1732 m_lighting_expired(true),
1733 m_day_night_differs(false),
1734 //m_not_fully_generated(false),
1736 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED)
1742 //m_spawn_timer = -10000;
1745 m_mesh_expired = false;
1748 m_temp_mods_mutex.Init();
1752 MapBlock::~MapBlock()
1756 JMutexAutoLock lock(mesh_mutex);
1770 bool MapBlock::isValidPositionParent(v3s16 p)
1772 if(isValidPosition(p))
1777 return m_parent->isValidPosition(getPosRelative() + p);
1781 MapNode MapBlock::getNodeParent(v3s16 p)
1783 if(isValidPosition(p) == false)
1785 return m_parent->getNode(getPosRelative() + p);
1790 throw InvalidPositionException();
1791 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1795 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
1797 if(isValidPosition(p) == false)
1799 m_parent->setNode(getPosRelative() + p, n);
1804 throw InvalidPositionException();
1805 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
1809 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
1811 if(isValidPosition(p) == false)
1814 return m_parent->getNode(getPosRelative() + p);
1816 catch(InvalidPositionException &e)
1818 return MapNode(CONTENT_IGNORE);
1825 return MapNode(CONTENT_IGNORE);
1827 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1834 void MapBlock::updateMesh(u32 daynight_ratio)
1838 DEBUG: If mesh has been generated, don't generate it again
1841 JMutexAutoLock meshlock(mesh_mutex);
1848 data.fill(daynight_ratio, this);
1850 scene::SMesh *mesh_new = makeMapBlockMesh(&data);
1856 replaceMesh(mesh_new);
1861 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
1865 //scene::SMesh *mesh_old = mesh[daynight_i];
1866 //mesh[daynight_i] = mesh_new;
1868 scene::SMesh *mesh_old = mesh;
1870 setMeshExpired(false);
1872 if(mesh_old != NULL)
1874 // Remove hardware buffers of meshbuffers of mesh
1875 // NOTE: No way, this runs in a different thread and everything
1876 /*u32 c = mesh_old->getMeshBufferCount();
1877 for(u32 i=0; i<c; i++)
1879 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1882 /*dstream<<"mesh_old->getReferenceCount()="
1883 <<mesh_old->getReferenceCount()<<std::endl;
1884 u32 c = mesh_old->getMeshBufferCount();
1885 for(u32 i=0; i<c; i++)
1887 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1888 dstream<<"buf->getReferenceCount()="
1889 <<buf->getReferenceCount()<<std::endl;
1898 mesh_mutex.Unlock();
1904 Propagates sunlight down through the block.
1905 Doesn't modify nodes that are not affected by sunlight.
1907 Returns false if sunlight at bottom block is invalid.
1908 Returns true if sunlight at bottom block is valid.
1909 Returns true if bottom block doesn't exist.
1911 If there is a block above, continues from it.
1912 If there is no block above, assumes there is sunlight, unless
1913 is_underground is set or highest node is water.
1915 All sunlighted nodes are added to light_sources.
1917 If grow_grass==true, turns sunglighted mud into grass.
1919 if remove_light==true, sets non-sunlighted nodes black.
1921 if black_air_left!=NULL, it is set to true if non-sunlighted
1922 air is left in block.
1924 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1925 bool remove_light, bool *black_air_left,
1928 // Whether the sunlight at the top of the bottom block is valid
1929 bool block_below_is_valid = true;
1931 v3s16 pos_relative = getPosRelative();
1933 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1935 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1938 bool no_sunlight = false;
1939 bool no_top_block = false;
1940 // Check if node above block has sunlight
1942 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1943 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1948 catch(InvalidPositionException &e)
1950 no_top_block = true;
1952 // NOTE: This makes over-ground roofed places sunlighted
1953 // Assume sunlight, unless is_underground==true
1960 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1961 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1966 // NOTE: As of now, this just would make everything dark.
1968 //no_sunlight = true;
1971 #if 0 // Doesn't work; nothing gets light.
1972 bool no_sunlight = true;
1973 bool no_top_block = false;
1974 // Check if node above block has sunlight
1976 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1977 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1979 no_sunlight = false;
1982 catch(InvalidPositionException &e)
1984 no_top_block = true;
1988 /*std::cout<<"("<<x<<","<<z<<"): "
1989 <<"no_top_block="<<no_top_block
1990 <<", is_underground="<<is_underground
1991 <<", no_sunlight="<<no_sunlight
1994 s16 y = MAP_BLOCKSIZE-1;
1996 // This makes difference to diminishing in water.
1997 bool stopped_to_solid_object = false;
1999 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
2004 MapNode &n = getNodeRef(pos);
2006 if(current_light == 0)
2010 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
2012 // Do nothing: Sunlight is continued
2014 else if(n.light_propagates() == false)
2018 bool upper_is_air = false;
2021 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
2022 upper_is_air = true;
2024 catch(InvalidPositionException &e)
2027 // Turn mud into grass
2028 if(upper_is_air && n.d == CONTENT_MUD
2029 && current_light == LIGHT_SUN)
2031 n.d = CONTENT_GRASS;
2035 // A solid object is on the way.
2036 stopped_to_solid_object = true;
2044 current_light = diminish_light(current_light);
2047 u8 old_light = n.getLight(LIGHTBANK_DAY);
2049 if(current_light > old_light || remove_light)
2051 n.setLight(LIGHTBANK_DAY, current_light);
2054 if(diminish_light(current_light) != 0)
2056 light_sources.insert(pos_relative + pos, true);
2059 if(current_light == 0 && stopped_to_solid_object)
2063 *black_air_left = true;
2068 // Whether or not the block below should see LIGHT_SUN
2069 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
2072 If the block below hasn't already been marked invalid:
2074 Check if the node below the block has proper sunlight at top.
2075 If not, the block below is invalid.
2077 Ignore non-transparent nodes as they always have no light
2081 if(block_below_is_valid)
2083 MapNode n = getNodeParent(v3s16(x, -1, z));
2084 if(n.light_propagates())
2086 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
2087 && sunlight_should_go_down == false)
2088 block_below_is_valid = false;
2089 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
2090 && sunlight_should_go_down == true)
2091 block_below_is_valid = false;
2095 catch(InvalidPositionException &e)
2097 /*std::cout<<"InvalidBlockException for bottom block node"
2099 // Just no block below, no need to panic.
2104 return block_below_is_valid;
2108 void MapBlock::copyTo(VoxelManipulator &dst)
2110 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
2111 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
2113 // Copy from data to VoxelManipulator
2114 dst.copyFrom(data, data_area, v3s16(0,0,0),
2115 getPosRelative(), data_size);
2118 void MapBlock::copyFrom(VoxelManipulator &dst)
2120 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
2121 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
2123 // Copy from VoxelManipulator to data
2124 dst.copyTo(data, data_area, v3s16(0,0,0),
2125 getPosRelative(), data_size);
2128 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
2133 m_objects.step(dtime, server, daynight_ratio);
2139 void MapBlock::updateDayNightDiff()
2143 m_day_night_differs = false;
2147 bool differs = false;
2150 Check if any lighting value differs
2152 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2154 MapNode &n = data[i];
2155 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
2163 If some lighting values differ, check if the whole thing is
2164 just air. If it is, differ = false
2168 bool only_air = true;
2169 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2171 MapNode &n = data[i];
2172 if(n.d != CONTENT_AIR)
2182 // Set member variable
2183 m_day_night_differs = differs;
2186 s16 MapBlock::getGroundLevel(v2s16 p2d)
2192 s16 y = MAP_BLOCKSIZE-1;
2195 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
2196 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
2198 if(y == MAP_BLOCKSIZE-1)
2206 catch(InvalidPositionException &e)
2216 void MapBlock::serialize(std::ostream &os, u8 version)
2218 if(!ser_ver_supported(version))
2219 throw VersionMismatchException("ERROR: MapBlock format not supported");
2223 throw SerializationError("ERROR: Not writing dummy block.");
2226 // These have no compression
2227 if(version <= 3 || version == 5 || version == 6)
2229 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2231 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
2232 SharedBuffer<u8> dest(buflen);
2234 dest[0] = is_underground;
2235 for(u32 i=0; i<nodecount; i++)
2237 u32 s = 1 + i * MapNode::serializedLength(version);
2238 data[i].serialize(&dest[s], version);
2241 os.write((char*)*dest, dest.getSize());
2243 else if(version <= 10)
2247 Compress the materials and the params separately.
2251 os.write((char*)&is_underground, 1);
2253 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2255 // Get and compress materials
2256 SharedBuffer<u8> materialdata(nodecount);
2257 for(u32 i=0; i<nodecount; i++)
2259 materialdata[i] = data[i].d;
2261 compress(materialdata, os, version);
2263 // Get and compress lights
2264 SharedBuffer<u8> lightdata(nodecount);
2265 for(u32 i=0; i<nodecount; i++)
2267 lightdata[i] = data[i].param;
2269 compress(lightdata, os, version);
2273 // Get and compress param2
2274 SharedBuffer<u8> param2data(nodecount);
2275 for(u32 i=0; i<nodecount; i++)
2277 param2data[i] = data[i].param2;
2279 compress(param2data, os, version);
2282 // All other versions (newest)
2289 if(m_day_night_differs)
2291 if(m_lighting_expired)
2293 os.write((char*)&flags, 1);
2295 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2301 SharedBuffer<u8> databuf(nodecount*3);
2304 for(u32 i=0; i<nodecount; i++)
2306 databuf[i] = data[i].d;
2310 for(u32 i=0; i<nodecount; i++)
2312 databuf[i+nodecount] = data[i].param;
2316 for(u32 i=0; i<nodecount; i++)
2318 databuf[i+nodecount*2] = data[i].param2;
2322 Compress data to output stream
2325 compress(databuf, os, version);
2335 std::ostringstream oss(std::ios_base::binary);
2336 m_node_metadata.serialize(oss);
2337 os<<serializeString(oss.str());
2339 // This will happen if the string is longer than 65535
2340 catch(SerializationError &e)
2342 // Use an empty string
2343 os<<serializeString("");
2348 std::ostringstream oss(std::ios_base::binary);
2349 m_node_metadata.serialize(oss);
2350 compressZlib(oss.str(), os);
2351 //os<<serializeLongString(oss.str());
2357 void MapBlock::deSerialize(std::istream &is, u8 version)
2359 if(!ser_ver_supported(version))
2360 throw VersionMismatchException("ERROR: MapBlock format not supported");
2362 // These have no lighting info
2365 setLightingExpired(true);
2368 // These have no compression
2369 if(version <= 3 || version == 5 || version == 6)
2371 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2374 if(is.gcount() != 1)
2375 throw SerializationError
2376 ("MapBlock::deSerialize: no enough input data");
2377 is_underground = tmp;
2378 for(u32 i=0; i<nodecount; i++)
2380 s32 len = MapNode::serializedLength(version);
2381 SharedBuffer<u8> d(len);
2382 is.read((char*)*d, len);
2383 if(is.gcount() != len)
2384 throw SerializationError
2385 ("MapBlock::deSerialize: no enough input data");
2386 data[i].deSerialize(*d, version);
2389 else if(version <= 10)
2391 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2394 is.read((char*)&t8, 1);
2395 is_underground = t8;
2398 // Uncompress and set material data
2399 std::ostringstream os(std::ios_base::binary);
2400 decompress(is, os, version);
2401 std::string s = os.str();
2402 if(s.size() != nodecount)
2403 throw SerializationError
2404 ("MapBlock::deSerialize: invalid format");
2405 for(u32 i=0; i<s.size(); i++)
2411 // Uncompress and set param data
2412 std::ostringstream os(std::ios_base::binary);
2413 decompress(is, os, version);
2414 std::string s = os.str();
2415 if(s.size() != nodecount)
2416 throw SerializationError
2417 ("MapBlock::deSerialize: invalid format");
2418 for(u32 i=0; i<s.size(); i++)
2420 data[i].param = s[i];
2426 // Uncompress and set param2 data
2427 std::ostringstream os(std::ios_base::binary);
2428 decompress(is, os, version);
2429 std::string s = os.str();
2430 if(s.size() != nodecount)
2431 throw SerializationError
2432 ("MapBlock::deSerialize: invalid format");
2433 for(u32 i=0; i<s.size(); i++)
2435 data[i].param2 = s[i];
2439 // All other versions (newest)
2442 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2445 is.read((char*)&flags, 1);
2446 is_underground = (flags & 0x01) ? true : false;
2447 m_day_night_differs = (flags & 0x02) ? true : false;
2448 m_lighting_expired = (flags & 0x04) ? true : false;
2451 std::ostringstream os(std::ios_base::binary);
2452 decompress(is, os, version);
2453 std::string s = os.str();
2454 if(s.size() != nodecount*3)
2455 throw SerializationError
2456 ("MapBlock::deSerialize: decompress resulted in size"
2457 " other than nodecount*3");
2460 for(u32 i=0; i<nodecount; i++)
2465 for(u32 i=0; i<nodecount; i++)
2467 data[i].param = s[i+nodecount];
2470 for(u32 i=0; i<nodecount; i++)
2472 data[i].param2 = s[i+nodecount*2];
2484 std::string data = deSerializeString(is);
2485 std::istringstream iss(data, std::ios_base::binary);
2486 m_node_metadata.deSerialize(iss);
2490 //std::string data = deSerializeLongString(is);
2491 std::ostringstream oss(std::ios_base::binary);
2492 decompressZlib(is, oss);
2493 std::istringstream iss(oss.str(), std::ios_base::binary);
2494 m_node_metadata.deSerialize(iss);
2497 catch(SerializationError &e)
2499 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
2500 <<" while deserializing node metadata"<<std::endl;
2506 Translate nodes as specified in the translate_to fields of
2509 NOTE: This isn't really used. Should it be removed?
2511 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2513 MapNode &n = data[i];
2515 MapNode *translate_to = content_features(n.d).translate_to;
2518 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2519 <<translate_to->d<<std::endl;
2525 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
2527 // Versions up from 9 have block objects.
2530 serializeObjects(os, version);
2533 // Versions up from 15 have static objects.
2536 m_static_objects.serialize(os);
2542 writeU32(os, getTimestamp());
2546 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
2549 Versions up from 9 have block objects.
2553 updateObjects(is, version, NULL, 0);
2557 Versions up from 15 have static objects.
2561 m_static_objects.deSerialize(is);
2567 setTimestamp(readU32(is));
2571 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);