3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
30 m_daynight_ratio = daynight_ratio;
31 m_blockpos = block->getPos();
33 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
36 There is no harm not copying the TempMods of the neighbors
37 because they are already copied to this block
40 block->copyTempMods(m_temp_mods);
46 // Allocate this block + neighbors
48 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
49 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
52 //TimeTaker timer("copy central block data");
56 block->copyTo(m_vmanip);
59 //TimeTaker timer("copy neighbor block data");
63 Copy neighbors. This is lightning fast.
64 Copying only the borders would be *very* slow.
68 NodeContainer *parentcontainer = block->getParent();
69 // This will only work if the parent is the map
70 assert(parentcontainer->nodeContainerId() == NODECONTAINER_ID_MAP);
71 // OK, we have the map!
72 Map *map = (Map*)parentcontainer;
74 for(u16 i=0; i<6; i++)
76 const v3s16 &dir = g_6dirs[i];
77 v3s16 bp = m_blockpos + dir;
78 MapBlock *b = map->getBlockNoCreateNoEx(bp);
87 Parameters must consist of air and !air.
90 If either of the nodes doesn't exist, light is 0.
93 daynight_ratio: 0...1000
95 n2: getNodeParent(p + face_dir)
96 face_dir: axis oriented unit vector from p to p2
98 returns encoded light value.
100 u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
105 u8 l1 = n.getLightBlend(daynight_ratio);
106 u8 l2 = n2.getLightBlend(daynight_ratio);
112 // Make some nice difference to different sides
114 // This makes light come from a corner
115 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
116 light = diminish_light(diminish_light(light));
117 else if(face_dir.X == -1 || face_dir.Z == -1)
118 light = diminish_light(light);*/
120 // All neighboring faces have different shade (like in minecraft)
121 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
122 light = diminish_light(diminish_light(light));
123 else if(face_dir.Z == 1 || face_dir.Z == -1)
124 light = diminish_light(light);
128 catch(InvalidPositionException &e)
137 vertex_dirs: v3s16[4]
139 void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
142 If looked from outside the node towards the face, the corners are:
148 if(dir == v3s16(0,0,1))
150 // If looking towards z+, this is the face that is behind
151 // the center point, facing towards z+.
152 vertex_dirs[0] = v3s16(-1,-1, 1);
153 vertex_dirs[1] = v3s16( 1,-1, 1);
154 vertex_dirs[2] = v3s16( 1, 1, 1);
155 vertex_dirs[3] = v3s16(-1, 1, 1);
157 else if(dir == v3s16(0,0,-1))
160 vertex_dirs[0] = v3s16( 1,-1,-1);
161 vertex_dirs[1] = v3s16(-1,-1,-1);
162 vertex_dirs[2] = v3s16(-1, 1,-1);
163 vertex_dirs[3] = v3s16( 1, 1,-1);
165 else if(dir == v3s16(1,0,0))
168 vertex_dirs[0] = v3s16( 1,-1, 1);
169 vertex_dirs[1] = v3s16( 1,-1,-1);
170 vertex_dirs[2] = v3s16( 1, 1,-1);
171 vertex_dirs[3] = v3s16( 1, 1, 1);
173 else if(dir == v3s16(-1,0,0))
176 vertex_dirs[0] = v3s16(-1,-1,-1);
177 vertex_dirs[1] = v3s16(-1,-1, 1);
178 vertex_dirs[2] = v3s16(-1, 1, 1);
179 vertex_dirs[3] = v3s16(-1, 1,-1);
181 else if(dir == v3s16(0,1,0))
183 // faces towards Y+ (assume Z- as "down" in texture)
184 vertex_dirs[0] = v3s16( 1, 1,-1);
185 vertex_dirs[1] = v3s16(-1, 1,-1);
186 vertex_dirs[2] = v3s16(-1, 1, 1);
187 vertex_dirs[3] = v3s16( 1, 1, 1);
189 else if(dir == v3s16(0,-1,0))
191 // faces towards Y- (assume Z+ as "down" in texture)
192 vertex_dirs[0] = v3s16( 1,-1, 1);
193 vertex_dirs[1] = v3s16(-1,-1, 1);
194 vertex_dirs[2] = v3s16(-1,-1,-1);
195 vertex_dirs[3] = v3s16( 1,-1,-1);
199 inline video::SColor lightColor(u8 alpha, u8 light)
201 return video::SColor(alpha,light,light,light);
204 void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
205 v3s16 dir, v3f scale, v3f posRelative_f,
206 core::array<FastFace> &dest)
210 // Position is at the center of the cube.
215 v3s16 vertex_dirs[4];
216 getNodeVertexDirs(dir, vertex_dirs);
217 for(u16 i=0; i<4; i++)
220 BS/2*vertex_dirs[i].X,
221 BS/2*vertex_dirs[i].Y,
222 BS/2*vertex_dirs[i].Z
226 for(u16 i=0; i<4; i++)
228 vertex_pos[i].X *= scale.X;
229 vertex_pos[i].Y *= scale.Y;
230 vertex_pos[i].Z *= scale.Z;
231 vertex_pos[i] += pos + posRelative_f;
235 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
236 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
237 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
239 v3f zerovector = v3f(0,0,0);
241 u8 alpha = tile.alpha;
243 if(tile.id == TILE_WATER)
244 alpha = WATER_ALPHA;*/
246 float x0 = tile.texture.pos.X;
247 float y0 = tile.texture.pos.Y;
248 float w = tile.texture.size.X;
249 float h = tile.texture.size.Y;
251 /*video::SColor c = lightColor(alpha, li);
253 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
254 core::vector2d<f32>(x0+w*abs_scale, y0+h));
255 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
256 core::vector2d<f32>(x0, y0+h));
257 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
258 core::vector2d<f32>(x0, y0));
259 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
260 core::vector2d<f32>(x0+w*abs_scale, y0));*/
262 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0),
263 lightColor(alpha, li0),
264 core::vector2d<f32>(x0+w*abs_scale, y0+h));
265 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0),
266 lightColor(alpha, li1),
267 core::vector2d<f32>(x0, y0+h));
268 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0),
269 lightColor(alpha, li2),
270 core::vector2d<f32>(x0, y0));
271 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0),
272 lightColor(alpha, li3),
273 core::vector2d<f32>(x0+w*abs_scale, y0));
277 //f->tile = TILE_STONE;
279 dest.push_back(face);
283 Gets node tile from any place relative to block.
284 Returns TILE_NODE if doesn't exist or should not be drawn.
286 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
287 NodeModMap &temp_mods)
290 spec = mn.getTile(face_dir);
293 Check temporary modifications on this node
295 /*core::map<v3s16, NodeMod>::Node *n;
296 n = m_temp_mods.find(p);
300 struct NodeMod mod = n->getValue();*/
302 if(temp_mods.get(p, &mod))
304 if(mod.type == NODEMOD_CHANGECONTENT)
306 MapNode mn2(mod.param);
307 spec = mn2.getTile(face_dir);
309 if(mod.type == NODEMOD_CRACK)
312 Get texture id, translate it to name, append stuff to
316 // Get original texture name
317 u32 orig_id = spec.texture.id;
318 std::string orig_name = g_texturesource->getTextureName(orig_id);
320 // Create new texture name
321 std::ostringstream os;
322 os<<orig_name<<"^[crack"<<mod.param;
325 u32 new_id = g_texturesource->getTextureId(os.str());
327 /*dstream<<"MapBlock::getNodeTile(): Switching from "
328 <<orig_name<<" to "<<os.str()<<" ("
329 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
331 spec.texture = g_texturesource->getTexture(new_id);
338 u8 getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
341 Check temporary modifications on this node
343 /*core::map<v3s16, NodeMod>::Node *n;
344 n = m_temp_mods.find(p);
348 struct NodeMod mod = n->getValue();*/
350 if(temp_mods.get(p, &mod))
352 if(mod.type == NODEMOD_CHANGECONTENT)
357 if(mod.type == NODEMOD_CRACK)
360 Content doesn't change.
362 face_contents works just like it should, because
363 there should not be faces between differently cracked
366 If a semi-transparent node is cracked in front an
367 another one, it really doesn't matter whether there
368 is a cracked face drawn in between or not.
387 // Calculate lighting at the XYZ- corner of p
388 u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio)
390 u16 ambient_occlusion = 0;
393 for(u32 i=0; i<8; i++)
395 MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
396 if(content_features(n.d).param_type == CPT_LIGHT)
398 light += decode_light(n.getLightBlend(daynight_ratio));
403 if(n.d != CONTENT_IGNORE)
411 light /= light_count;
413 if(ambient_occlusion > 4)
415 ambient_occlusion -= 4;
416 light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
422 // Calculate lighting at the given corner of p
423 u8 getSmoothLight(v3s16 p, v3s16 corner,
424 VoxelManipulator &vmanip, u32 daynight_ratio)
426 if(corner.X == 1) p.X += 1;
427 else assert(corner.X == -1);
428 if(corner.Y == 1) p.Y += 1;
429 else assert(corner.Y == -1);
430 if(corner.Z == 1) p.Z += 1;
431 else assert(corner.Z == -1);
433 return getSmoothLight(p, vmanip, daynight_ratio);
438 v3s16 blockpos_nodes,
442 VoxelManipulator &vmanip,
443 NodeModMap &temp_mods,
444 bool smooth_lighting,
448 v3s16 &face_dir_corrected,
453 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
454 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
455 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
456 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
459 u8 content0 = getNodeContent(p, n0, temp_mods);
460 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
461 u8 mf = face_contents(content0, content1);
475 face_dir_corrected = face_dir;
480 p_corrected = p + face_dir;
481 face_dir_corrected = -face_dir;
484 if(smooth_lighting == false)
486 lights[0] = lights[1] = lights[2] = lights[3] =
487 decode_light(getFaceLight(daynight_ratio, n0, n1, face_dir));
491 v3s16 vertex_dirs[4];
492 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
493 for(u16 i=0; i<4; i++)
495 lights[i] = getSmoothLight(blockpos_nodes + p_corrected,
496 vertex_dirs[i], vmanip, daynight_ratio);
505 translate_dir: unit vector with only one of x, y or z
506 face_dir: unit vector with only one of x, y or z
508 void updateFastFaceRow(
517 core::array<FastFace> &dest,
518 NodeModMap &temp_mods,
519 VoxelManipulator &vmanip,
520 v3s16 blockpos_nodes,
521 bool smooth_lighting)
525 u16 continuous_tiles_count = 0;
529 v3s16 face_dir_corrected;
532 getTileInfo(blockpos_nodes, p, face_dir, daynight_ratio,
533 vmanip, temp_mods, smooth_lighting,
534 makes_face, p_corrected, face_dir_corrected, lights, tile);
536 for(u16 j=0; j<length; j++)
538 // If tiling can be done, this is set to false in the next step
539 bool next_is_different = true;
543 bool next_makes_face;
544 v3s16 next_p_corrected;
545 v3s16 next_face_dir_corrected;
549 // If at last position, there is nothing to compare to and
550 // the face must be drawn anyway
553 p_next = p + translate_dir;
555 getTileInfo(blockpos_nodes, p_next, face_dir, daynight_ratio,
556 vmanip, temp_mods, smooth_lighting,
557 next_makes_face, next_p_corrected,
558 next_face_dir_corrected, next_lights,
561 if(next_makes_face == makes_face
562 && next_p_corrected == p_corrected
563 && next_face_dir_corrected == face_dir_corrected
564 && next_lights[0] == lights[0]
565 && next_lights[1] == lights[1]
566 && next_lights[2] == lights[2]
567 && next_lights[3] == lights[3]
568 && next_tile == tile)
570 next_is_different = false;
574 continuous_tiles_count++;
576 // This is set to true if the texture doesn't allow more tiling
577 bool end_of_texture = false;
579 If there is no texture, it can be tiled infinitely.
580 If tiled==0, it means the texture can be tiled infinitely.
581 Otherwise check tiled agains continuous_tiles_count.
583 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
585 if(tile.texture.tiled <= continuous_tiles_count)
586 end_of_texture = true;
589 // Do this to disable tiling textures
590 //end_of_texture = true; //DEBUG
592 if(next_is_different || end_of_texture)
595 Create a face if there should be one
599 // Floating point conversion of the position vector
600 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
601 // Center point of face (kind of)
602 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
605 if(translate_dir.X != 0)
607 scale.X = continuous_tiles_count;
609 if(translate_dir.Y != 0)
611 scale.Y = continuous_tiles_count;
613 if(translate_dir.Z != 0)
615 scale.Z = continuous_tiles_count;
618 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
619 sp, face_dir_corrected, scale,
620 posRelative_f, dest);
623 continuous_tiles_count = 0;
625 makes_face = next_makes_face;
626 p_corrected = next_p_corrected;
627 face_dir_corrected = next_face_dir_corrected;
628 lights[0] = next_lights[0];
629 lights[1] = next_lights[1];
630 lights[2] = next_lights[2];
631 lights[3] = next_lights[3];
640 This is used because CMeshBuffer::append() is very slow
644 video::SMaterial material;
645 core::array<u16> indices;
646 core::array<video::S3DVertex> vertices;
653 video::SMaterial material,
654 const video::S3DVertex* const vertices,
656 const u16* const indices,
660 PreMeshBuffer *p = NULL;
661 for(u32 i=0; i<m_prebuffers.size(); i++)
663 PreMeshBuffer &pp = m_prebuffers[i];
664 if(pp.material != material)
674 pp.material = material;
675 m_prebuffers.push_back(pp);
676 p = &m_prebuffers[m_prebuffers.size()-1];
679 u32 vertex_count = p->vertices.size();
680 for(u32 i=0; i<numIndices; i++)
682 u32 j = indices[i] + vertex_count;
685 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
686 // NOTE: Fix is to just add an another MeshBuffer
688 p->indices.push_back(j);
690 for(u32 i=0; i<numVertices; i++)
692 p->vertices.push_back(vertices[i]);
696 void fillMesh(scene::SMesh *mesh)
698 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
699 <<" meshbuffers"<<std::endl;*/
700 for(u32 i=0; i<m_prebuffers.size(); i++)
702 PreMeshBuffer &p = m_prebuffers[i];
704 /*dstream<<"p.vertices.size()="<<p.vertices.size()
705 <<", p.indices.size()="<<p.indices.size()
710 // This is a "Standard MeshBuffer",
711 // it's a typedeffed CMeshBuffer<video::S3DVertex>
712 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
714 buf->Material = p.material;
715 //((scene::SMeshBuffer*)buf)->Material = p.material;
717 //buf->setHardwareMappingHint(scene::EHM_STATIC);
719 mesh->addMeshBuffer(buf);
723 buf->append(p.vertices.pointer(), p.vertices.size(),
724 p.indices.pointer(), p.indices.size());
729 core::array<PreMeshBuffer> m_prebuffers;
732 void makeCuboid(video::SMaterial &material, MeshCollector *collector,
733 AtlasPointer* pa, video::SColor &c,
734 v3f &pos, f32 rx, f32 ry, f32 rz)
736 video::S3DVertex v[4] =
738 video::S3DVertex(0,0,0, 0,0,0, c,
740 video::S3DVertex(0,0,0, 0,0,0, c,
742 video::S3DVertex(0,0,0, 0,0,0, c,
744 video::S3DVertex(0,0,0, 0,0,0, c,
753 v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz;
754 v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz;
755 v[2].Pos.X= rx; v[2].Pos.Y= ry; v[2].Pos.Z= rz;
756 v[3].Pos.X= rx; v[3].Pos.Y= ry, v[3].Pos.Z=-rz;
759 v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz;
760 v[1].Pos.X= rx; v[1].Pos.Y= ry; v[1].Pos.Z=-rz;
761 v[2].Pos.X= rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz;
762 v[3].Pos.X=-rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz;
765 v[0].Pos.X= rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz;
766 v[1].Pos.X= rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz;
767 v[2].Pos.X= rx; v[2].Pos.Y=-ry; v[2].Pos.Z= rz;
768 v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz;
771 v[0].Pos.X= rx; v[0].Pos.Y= ry; v[0].Pos.Z= rz;
772 v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz;
773 v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z= rz;
774 v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z= rz;
777 v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z= rz;
778 v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z=-rz;
779 v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz;
780 v[3].Pos.X=-rx; v[3].Pos.Y=-ry, v[3].Pos.Z= rz;
783 v[0].Pos.X= rx; v[0].Pos.Y=-ry; v[0].Pos.Z= rz;
784 v[1].Pos.X=-rx; v[1].Pos.Y=-ry; v[1].Pos.Z= rz;
785 v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz;
786 v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz;
789 for(u16 i=0; i<4; i++)
791 u16 indices[] = {0,1,2,2,3,0};
792 collector->append(material, v, 4, indices, 6);
798 scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
800 // 4-21ms for MAP_BLOCKSIZE=16
801 // 24-155ms for MAP_BLOCKSIZE=32
802 //TimeTaker timer1("makeMapBlockMesh()");
804 core::array<FastFace> fastfaces_new;
806 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
808 // floating point conversion
809 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
814 bool new_style_water = g_settings.getBool("new_style_water");
815 bool new_style_leaves = g_settings.getBool("new_style_leaves");
816 bool smooth_lighting = g_settings.getBool("smooth_lighting");
818 float node_water_level = 1.0;
820 node_water_level = 0.85;
823 We are including the faces of the trailing edges of the block.
824 This means that when something changes, the caller must
825 also update the meshes of the blocks at the leading edges.
827 NOTE: This is the slowest part of this method.
831 // 4-23ms for MAP_BLOCKSIZE=16
832 //TimeTaker timer2("updateMesh() collect");
835 Go through every y,z and get top(y+) faces in rows of x+
837 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
838 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
839 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
840 v3s16(0,y,z), MAP_BLOCKSIZE,
843 v3s16(0,1,0), //face dir
853 Go through every x,y and get right(x+) faces in rows of z+
855 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
856 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
857 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
858 v3s16(x,y,0), MAP_BLOCKSIZE,
871 Go through every y,z and get back(z+) faces in rows of x+
873 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
874 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
875 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
876 v3s16(0,y,z), MAP_BLOCKSIZE,
893 Convert FastFaces to SMesh
896 MeshCollector collector;
898 if(fastfaces_new.size() > 0)
900 // avg 0ms (100ms spikes when loading textures the first time)
901 //TimeTaker timer2("updateMesh() mesh building");
903 video::SMaterial material;
904 material.setFlag(video::EMF_LIGHTING, false);
905 material.setFlag(video::EMF_BILINEAR_FILTER, false);
906 material.setFlag(video::EMF_FOG_ENABLE, true);
907 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
908 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
910 for(u32 i=0; i<fastfaces_new.size(); i++)
912 FastFace &f = fastfaces_new[i];
914 const u16 indices[] = {0,1,2,2,3,0};
915 const u16 indices_alternate[] = {0,1,3,2,3,1};
917 video::ITexture *texture = f.tile.texture.atlas;
921 material.setTexture(0, texture);
923 f.tile.applyMaterialOptions(material);
925 const u16 *indices_p = indices;
928 Revert triangles for nicer looking gradient if vertices
929 1 and 3 have same color or 0 and 2 have different color.
931 if(f.vertices[0].Color != f.vertices[2].Color
932 || f.vertices[1].Color == f.vertices[3].Color)
933 indices_p = indices_alternate;
935 collector.append(material, f.vertices, 4, indices_p, 6);
940 Add special graphics:
946 //TimeTaker timer2("updateMesh() adding special stuff");
948 // Flowing water material
949 video::SMaterial material_water1;
950 material_water1.setFlag(video::EMF_LIGHTING, false);
951 material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
952 material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
953 material_water1.setFlag(video::EMF_FOG_ENABLE, true);
954 material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
955 AtlasPointer pa_water1 = g_texturesource->getTexture(
956 g_texturesource->getTextureId("water.png"));
957 material_water1.setTexture(0, pa_water1.atlas);
959 // New-style leaves material
960 video::SMaterial material_leaves1;
961 material_leaves1.setFlag(video::EMF_LIGHTING, false);
962 //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
963 material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
964 material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
965 material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
966 AtlasPointer pa_leaves1 = g_texturesource->getTexture(
967 g_texturesource->getTextureId("leaves.png"));
968 material_leaves1.setTexture(0, pa_leaves1.atlas);
971 video::SMaterial material_glass;
972 material_glass.setFlag(video::EMF_LIGHTING, false);
973 material_glass.setFlag(video::EMF_BILINEAR_FILTER, false);
974 material_glass.setFlag(video::EMF_FOG_ENABLE, true);
975 material_glass.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
976 AtlasPointer pa_glass = g_texturesource->getTexture(
977 g_texturesource->getTextureId("glass.png"));
978 material_glass.setTexture(0, pa_glass.atlas);
981 video::SMaterial material_wood;
982 material_wood.setFlag(video::EMF_LIGHTING, false);
983 material_wood.setFlag(video::EMF_BILINEAR_FILTER, false);
984 material_wood.setFlag(video::EMF_FOG_ENABLE, true);
985 material_wood.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
986 AtlasPointer pa_wood = g_texturesource->getTexture(
987 g_texturesource->getTextureId("wood.png"));
988 material_wood.setTexture(0, pa_wood.atlas);
990 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
991 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
992 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
996 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
1001 if(n.d == CONTENT_TORCH)
1003 video::SColor c(255,255,255,255);
1005 // Wall at X+ of node
1006 video::S3DVertex vertices[4] =
1008 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
1009 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
1010 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
1011 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
1014 v3s16 dir = unpackDir(n.dir);
1016 for(s32 i=0; i<4; i++)
1018 if(dir == v3s16(1,0,0))
1019 vertices[i].Pos.rotateXZBy(0);
1020 if(dir == v3s16(-1,0,0))
1021 vertices[i].Pos.rotateXZBy(180);
1022 if(dir == v3s16(0,0,1))
1023 vertices[i].Pos.rotateXZBy(90);
1024 if(dir == v3s16(0,0,-1))
1025 vertices[i].Pos.rotateXZBy(-90);
1026 if(dir == v3s16(0,-1,0))
1027 vertices[i].Pos.rotateXZBy(45);
1028 if(dir == v3s16(0,1,0))
1029 vertices[i].Pos.rotateXZBy(-45);
1031 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1035 video::SMaterial material;
1036 material.setFlag(video::EMF_LIGHTING, false);
1037 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
1038 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1039 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1040 material.MaterialType
1041 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1043 if(dir == v3s16(0,-1,0))
1044 material.setTexture(0,
1045 g_texturesource->getTextureRaw("torch_on_floor.png"));
1046 else if(dir == v3s16(0,1,0))
1047 material.setTexture(0,
1048 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
1049 // For backwards compatibility
1050 else if(dir == v3s16(0,0,0))
1051 material.setTexture(0,
1052 g_texturesource->getTextureRaw("torch_on_floor.png"));
1054 material.setTexture(0,
1055 g_texturesource->getTextureRaw("torch.png"));
1057 u16 indices[] = {0,1,2,2,3,0};
1058 // Add to mesh collector
1059 collector.append(material, vertices, 4, indices, 6);
1064 if(n.d == CONTENT_SIGN_WALL)
1066 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1067 video::SColor c(255,l,l,l);
1069 float d = (float)BS/16;
1070 // Wall at X+ of node
1071 video::S3DVertex vertices[4] =
1073 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
1074 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
1075 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
1076 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
1079 v3s16 dir = unpackDir(n.dir);
1081 for(s32 i=0; i<4; i++)
1083 if(dir == v3s16(1,0,0))
1084 vertices[i].Pos.rotateXZBy(0);
1085 if(dir == v3s16(-1,0,0))
1086 vertices[i].Pos.rotateXZBy(180);
1087 if(dir == v3s16(0,0,1))
1088 vertices[i].Pos.rotateXZBy(90);
1089 if(dir == v3s16(0,0,-1))
1090 vertices[i].Pos.rotateXZBy(-90);
1091 if(dir == v3s16(0,-1,0))
1092 vertices[i].Pos.rotateXYBy(-90);
1093 if(dir == v3s16(0,1,0))
1094 vertices[i].Pos.rotateXYBy(90);
1096 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1100 video::SMaterial material;
1101 material.setFlag(video::EMF_LIGHTING, false);
1102 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
1103 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1104 material.setFlag(video::EMF_FOG_ENABLE, true);
1105 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1106 material.MaterialType
1107 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1109 material.setTexture(0,
1110 g_texturesource->getTextureRaw("sign_wall.png"));
1112 u16 indices[] = {0,1,2,2,3,0};
1113 // Add to mesh collector
1114 collector.append(material, vertices, 4, indices, 6);
1117 Add flowing water to mesh
1119 else if(n.d == CONTENT_WATER)
1121 bool top_is_water = false;
1122 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1123 if(ntop.d == CONTENT_WATER || ntop.d == CONTENT_WATERSOURCE)
1124 top_is_water = true;
1127 // Use the light of the node on top if possible
1128 if(content_features(ntop.d).param_type == CPT_LIGHT)
1129 l = decode_light(ntop.getLightBlend(data->m_daynight_ratio));
1130 // Otherwise use the light of this node (the water)
1132 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1133 video::SColor c(WATER_ALPHA,l,l,l);
1135 // Neighbor water levels (key = relative position)
1136 // Includes current node
1137 core::map<v3s16, f32> neighbor_levels;
1138 core::map<v3s16, u8> neighbor_contents;
1139 core::map<v3s16, u8> neighbor_flags;
1140 const u8 neighborflag_top_is_water = 0x01;
1141 v3s16 neighbor_dirs[9] = {
1152 for(u32 i=0; i<9; i++)
1154 u8 content = CONTENT_AIR;
1155 float level = -0.5 * BS;
1158 v3s16 p2 = p + neighbor_dirs[i];
1159 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1160 if(n2.d != CONTENT_IGNORE)
1164 if(n2.d == CONTENT_WATERSOURCE)
1165 level = (-0.5+node_water_level) * BS;
1166 else if(n2.d == CONTENT_WATER)
1167 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
1168 * node_water_level) * BS;
1170 // Check node above neighbor.
1171 // NOTE: This doesn't get executed if neighbor
1174 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1175 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
1176 flags |= neighborflag_top_is_water;
1179 neighbor_levels.insert(neighbor_dirs[i], level);
1180 neighbor_contents.insert(neighbor_dirs[i], content);
1181 neighbor_flags.insert(neighbor_dirs[i], flags);
1184 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
1185 //float water_level = neighbor_levels[v3s16(0,0,0)];
1187 // Corner heights (average between four waters)
1188 f32 corner_levels[4];
1190 v3s16 halfdirs[4] = {
1196 for(u32 i=0; i<4; i++)
1198 v3s16 cornerdir = halfdirs[i];
1199 float cornerlevel = 0;
1200 u32 valid_count = 0;
1201 for(u32 j=0; j<4; j++)
1203 v3s16 neighbordir = cornerdir - halfdirs[j];
1204 u8 content = neighbor_contents[neighbordir];
1205 // Special case for source nodes
1206 if(content == CONTENT_WATERSOURCE)
1208 cornerlevel = (-0.5+node_water_level)*BS;
1212 else if(content == CONTENT_WATER)
1214 cornerlevel += neighbor_levels[neighbordir];
1217 else if(content == CONTENT_AIR)
1219 cornerlevel += -0.5*BS;
1224 cornerlevel /= valid_count;
1225 corner_levels[i] = cornerlevel;
1232 v3s16 side_dirs[4] = {
1238 s16 side_corners[4][2] = {
1244 for(u32 i=0; i<4; i++)
1246 v3s16 dir = side_dirs[i];
1249 If our topside is water and neighbor's topside
1250 is water, don't draw side face
1253 neighbor_flags[dir] & neighborflag_top_is_water)
1256 u8 neighbor_content = neighbor_contents[dir];
1258 // Don't draw face if neighbor is not air or water
1259 if(neighbor_content != CONTENT_AIR
1260 && neighbor_content != CONTENT_WATER)
1263 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1265 // Don't draw any faces if neighbor is water and top is water
1266 if(neighbor_is_water == true && top_is_water == false)
1269 video::S3DVertex vertices[4] =
1271 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1272 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1273 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1274 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1275 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1276 pa_water1.x0(), pa_water1.y1()),
1277 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1278 pa_water1.x1(), pa_water1.y1()),
1279 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1280 pa_water1.x1(), pa_water1.y0()),
1281 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1282 pa_water1.x0(), pa_water1.y0()),
1286 If our topside is water, set upper border of face
1287 at upper border of node
1291 vertices[2].Pos.Y = 0.5*BS;
1292 vertices[3].Pos.Y = 0.5*BS;
1295 Otherwise upper position of face is corner levels
1299 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1300 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1304 If neighbor is water, lower border of face is corner
1307 if(neighbor_is_water)
1309 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1310 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1313 If neighbor is not water, lower border of face is
1314 lower border of node
1318 vertices[0].Pos.Y = -0.5*BS;
1319 vertices[1].Pos.Y = -0.5*BS;
1322 for(s32 j=0; j<4; j++)
1324 if(dir == v3s16(0,0,1))
1325 vertices[j].Pos.rotateXZBy(0);
1326 if(dir == v3s16(0,0,-1))
1327 vertices[j].Pos.rotateXZBy(180);
1328 if(dir == v3s16(-1,0,0))
1329 vertices[j].Pos.rotateXZBy(90);
1330 if(dir == v3s16(1,0,-0))
1331 vertices[j].Pos.rotateXZBy(-90);
1333 vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
1336 u16 indices[] = {0,1,2,2,3,0};
1337 // Add to mesh collector
1338 collector.append(material_water1, vertices, 4, indices, 6);
1342 Generate top side, if appropriate
1345 if(top_is_water == false)
1347 video::S3DVertex vertices[4] =
1349 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1350 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1351 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1352 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1353 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1354 pa_water1.x0(), pa_water1.y1()),
1355 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1356 pa_water1.x1(), pa_water1.y1()),
1357 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1358 pa_water1.x1(), pa_water1.y0()),
1359 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1360 pa_water1.x0(), pa_water1.y0()),
1363 // This fixes a strange bug
1364 s32 corner_resolve[4] = {3,2,1,0};
1366 for(s32 i=0; i<4; i++)
1368 //vertices[i].Pos.Y += water_level;
1369 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1370 s32 j = corner_resolve[i];
1371 vertices[i].Pos.Y += corner_levels[j];
1372 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1375 u16 indices[] = {0,1,2,2,3,0};
1376 // Add to mesh collector
1377 collector.append(material_water1, vertices, 4, indices, 6);
1381 Add water sources to mesh if using new style
1383 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1385 //bool top_is_water = false;
1386 bool top_is_air = false;
1387 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1388 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1389 top_is_water = true;*/
1390 if(n.d == CONTENT_AIR)
1393 /*if(top_is_water == true)
1395 if(top_is_air == false)
1398 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1399 video::SColor c(WATER_ALPHA,l,l,l);
1401 video::S3DVertex vertices[4] =
1403 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1404 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1405 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1406 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1407 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1408 pa_water1.x0(), pa_water1.y1()),
1409 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1410 pa_water1.x1(), pa_water1.y1()),
1411 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1412 pa_water1.x1(), pa_water1.y0()),
1413 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1414 pa_water1.x0(), pa_water1.y0()),
1417 for(s32 i=0; i<4; i++)
1419 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1420 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1423 u16 indices[] = {0,1,2,2,3,0};
1424 // Add to mesh collector
1425 collector.append(material_water1, vertices, 4, indices, 6);
1428 Add leaves if using new style
1430 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1432 /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
1433 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1434 video::SColor c(255,l,l,l);
1436 for(u32 j=0; j<6; j++)
1438 video::S3DVertex vertices[4] =
1440 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1441 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1442 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1443 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1444 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1445 pa_leaves1.x0(), pa_leaves1.y1()),
1446 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1447 pa_leaves1.x1(), pa_leaves1.y1()),
1448 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1449 pa_leaves1.x1(), pa_leaves1.y0()),
1450 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1451 pa_leaves1.x0(), pa_leaves1.y0()),
1456 for(u16 i=0; i<4; i++)
1457 vertices[i].Pos.rotateXZBy(0);
1461 for(u16 i=0; i<4; i++)
1462 vertices[i].Pos.rotateXZBy(180);
1466 for(u16 i=0; i<4; i++)
1467 vertices[i].Pos.rotateXZBy(-90);
1471 for(u16 i=0; i<4; i++)
1472 vertices[i].Pos.rotateXZBy(90);
1476 for(u16 i=0; i<4; i++)
1477 vertices[i].Pos.rotateYZBy(-90);
1481 for(u16 i=0; i<4; i++)
1482 vertices[i].Pos.rotateYZBy(90);
1485 for(u16 i=0; i<4; i++)
1487 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1490 u16 indices[] = {0,1,2,2,3,0};
1491 // Add to mesh collector
1492 collector.append(material_leaves1, vertices, 4, indices, 6);
1498 else if(n.d == CONTENT_GLASS)
1500 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1501 video::SColor c(255,l,l,l);
1503 for(u32 j=0; j<6; j++)
1505 video::S3DVertex vertices[4] =
1507 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1508 pa_glass.x0(), pa_glass.y1()),
1509 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1510 pa_glass.x1(), pa_glass.y1()),
1511 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1512 pa_glass.x1(), pa_glass.y0()),
1513 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1514 pa_glass.x0(), pa_glass.y0()),
1519 for(u16 i=0; i<4; i++)
1520 vertices[i].Pos.rotateXZBy(0);
1524 for(u16 i=0; i<4; i++)
1525 vertices[i].Pos.rotateXZBy(180);
1529 for(u16 i=0; i<4; i++)
1530 vertices[i].Pos.rotateXZBy(-90);
1534 for(u16 i=0; i<4; i++)
1535 vertices[i].Pos.rotateXZBy(90);
1539 for(u16 i=0; i<4; i++)
1540 vertices[i].Pos.rotateYZBy(-90);
1544 for(u16 i=0; i<4; i++)
1545 vertices[i].Pos.rotateYZBy(90);
1548 for(u16 i=0; i<4; i++)
1550 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1553 u16 indices[] = {0,1,2,2,3,0};
1554 // Add to mesh collector
1555 collector.append(material_glass, vertices, 4, indices, 6);
1561 else if(n.d == CONTENT_FENCE)
1563 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1564 video::SColor c(255,l,l,l);
1566 const f32 post_rad=(f32)BS/(32.0/4.0);
1567 const f32 bar_rad=(f32)BS/(32.0/2.0);
1568 const f32 bar_len=(f32)(BS/2)-post_rad;
1570 // The post - always present
1571 v3f pos = intToFloat(p+blockpos_nodes, BS);
1572 makeCuboid(material_wood, &collector,
1574 post_rad,BS/2,post_rad);
1576 // Now a section of fence, +X, if there's a post there
1579 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1580 if(n2.d == CONTENT_FENCE)
1582 pos = intToFloat(p+blockpos_nodes, BS);
1585 makeCuboid(material_wood, &collector,
1587 bar_len,bar_rad,bar_rad);
1590 makeCuboid(material_wood, &collector,
1592 bar_len,bar_rad,bar_rad);
1595 // Now a section of fence, +Z, if there's a post there
1598 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1599 if(n2.d == CONTENT_FENCE)
1601 pos = intToFloat(p+blockpos_nodes, BS);
1604 makeCuboid(material_wood, &collector,
1606 bar_rad,bar_rad,bar_len);
1608 makeCuboid(material_wood, &collector,
1610 bar_rad,bar_rad,bar_len);
1621 Add stuff from collector to mesh
1624 scene::SMesh *mesh_new = NULL;
1625 mesh_new = new scene::SMesh();
1627 collector.fillMesh(mesh_new);
1630 Do some stuff to the mesh
1633 mesh_new->recalculateBoundingBox();
1636 Delete new mesh if it is empty
1639 if(mesh_new->getMeshBufferCount() == 0)
1648 // Usually 1-700 faces and 1-7 materials
1649 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1650 <<"and uses "<<mesh_new->getMeshBufferCount()
1651 <<" materials (meshbuffers)"<<std::endl;
1654 // Use VBO for mesh (this just would set this for ever buffer)
1655 // This will lead to infinite memory usage because or irrlicht.
1656 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1659 NOTE: If that is enabled, some kind of a queue to the main
1660 thread should be made which would call irrlicht to delete
1661 the hardware buffer and then delete the mesh
1667 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1676 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
1680 is_underground(false),
1681 m_lighting_expired(true),
1682 m_day_night_differs(false),
1683 //m_not_fully_generated(false),
1685 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED)
1691 //m_spawn_timer = -10000;
1694 m_mesh_expired = false;
1697 m_temp_mods_mutex.Init();
1701 MapBlock::~MapBlock()
1705 JMutexAutoLock lock(mesh_mutex);
1719 bool MapBlock::isValidPositionParent(v3s16 p)
1721 if(isValidPosition(p))
1726 return m_parent->isValidPosition(getPosRelative() + p);
1730 MapNode MapBlock::getNodeParent(v3s16 p)
1732 if(isValidPosition(p) == false)
1734 return m_parent->getNode(getPosRelative() + p);
1739 throw InvalidPositionException();
1740 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1744 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
1746 if(isValidPosition(p) == false)
1748 m_parent->setNode(getPosRelative() + p, n);
1753 throw InvalidPositionException();
1754 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
1758 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
1760 if(isValidPosition(p) == false)
1763 return m_parent->getNode(getPosRelative() + p);
1765 catch(InvalidPositionException &e)
1767 return MapNode(CONTENT_IGNORE);
1774 return MapNode(CONTENT_IGNORE);
1776 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1783 void MapBlock::updateMesh(u32 daynight_ratio)
1787 DEBUG: If mesh has been generated, don't generate it again
1790 JMutexAutoLock meshlock(mesh_mutex);
1797 data.fill(daynight_ratio, this);
1799 scene::SMesh *mesh_new = makeMapBlockMesh(&data);
1805 replaceMesh(mesh_new);
1810 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
1814 //scene::SMesh *mesh_old = mesh[daynight_i];
1815 //mesh[daynight_i] = mesh_new;
1817 scene::SMesh *mesh_old = mesh;
1819 setMeshExpired(false);
1821 if(mesh_old != NULL)
1823 // Remove hardware buffers of meshbuffers of mesh
1824 // NOTE: No way, this runs in a different thread and everything
1825 /*u32 c = mesh_old->getMeshBufferCount();
1826 for(u32 i=0; i<c; i++)
1828 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1831 /*dstream<<"mesh_old->getReferenceCount()="
1832 <<mesh_old->getReferenceCount()<<std::endl;
1833 u32 c = mesh_old->getMeshBufferCount();
1834 for(u32 i=0; i<c; i++)
1836 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1837 dstream<<"buf->getReferenceCount()="
1838 <<buf->getReferenceCount()<<std::endl;
1847 mesh_mutex.Unlock();
1853 Propagates sunlight down through the block.
1854 Doesn't modify nodes that are not affected by sunlight.
1856 Returns false if sunlight at bottom block is invalid.
1857 Returns true if sunlight at bottom block is valid.
1858 Returns true if bottom block doesn't exist.
1860 If there is a block above, continues from it.
1861 If there is no block above, assumes there is sunlight, unless
1862 is_underground is set or highest node is water.
1864 All sunlighted nodes are added to light_sources.
1866 If grow_grass==true, turns sunglighted mud into grass.
1868 if remove_light==true, sets non-sunlighted nodes black.
1870 if black_air_left!=NULL, it is set to true if non-sunlighted
1871 air is left in block.
1873 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1874 bool remove_light, bool *black_air_left,
1877 // Whether the sunlight at the top of the bottom block is valid
1878 bool block_below_is_valid = true;
1880 v3s16 pos_relative = getPosRelative();
1882 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1884 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1887 bool no_sunlight = false;
1888 bool no_top_block = false;
1889 // Check if node above block has sunlight
1891 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1892 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1897 catch(InvalidPositionException &e)
1899 no_top_block = true;
1901 // NOTE: This makes over-ground roofed places sunlighted
1902 // Assume sunlight, unless is_underground==true
1909 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1910 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1915 // NOTE: As of now, this just would make everything dark.
1917 //no_sunlight = true;
1920 #if 0 // Doesn't work; nothing gets light.
1921 bool no_sunlight = true;
1922 bool no_top_block = false;
1923 // Check if node above block has sunlight
1925 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1926 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1928 no_sunlight = false;
1931 catch(InvalidPositionException &e)
1933 no_top_block = true;
1937 /*std::cout<<"("<<x<<","<<z<<"): "
1938 <<"no_top_block="<<no_top_block
1939 <<", is_underground="<<is_underground
1940 <<", no_sunlight="<<no_sunlight
1943 s16 y = MAP_BLOCKSIZE-1;
1945 // This makes difference to diminishing in water.
1946 bool stopped_to_solid_object = false;
1948 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1953 MapNode &n = getNodeRef(pos);
1955 if(current_light == 0)
1959 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1961 // Do nothing: Sunlight is continued
1963 else if(n.light_propagates() == false)
1967 bool upper_is_air = false;
1970 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1971 upper_is_air = true;
1973 catch(InvalidPositionException &e)
1976 // Turn mud into grass
1977 if(upper_is_air && n.d == CONTENT_MUD
1978 && current_light == LIGHT_SUN)
1980 n.d = CONTENT_GRASS;
1984 // A solid object is on the way.
1985 stopped_to_solid_object = true;
1993 current_light = diminish_light(current_light);
1996 u8 old_light = n.getLight(LIGHTBANK_DAY);
1998 if(current_light > old_light || remove_light)
2000 n.setLight(LIGHTBANK_DAY, current_light);
2003 if(diminish_light(current_light) != 0)
2005 light_sources.insert(pos_relative + pos, true);
2008 if(current_light == 0 && stopped_to_solid_object)
2012 *black_air_left = true;
2017 // Whether or not the block below should see LIGHT_SUN
2018 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
2021 If the block below hasn't already been marked invalid:
2023 Check if the node below the block has proper sunlight at top.
2024 If not, the block below is invalid.
2026 Ignore non-transparent nodes as they always have no light
2030 if(block_below_is_valid)
2032 MapNode n = getNodeParent(v3s16(x, -1, z));
2033 if(n.light_propagates())
2035 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
2036 && sunlight_should_go_down == false)
2037 block_below_is_valid = false;
2038 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
2039 && sunlight_should_go_down == true)
2040 block_below_is_valid = false;
2044 catch(InvalidPositionException &e)
2046 /*std::cout<<"InvalidBlockException for bottom block node"
2048 // Just no block below, no need to panic.
2053 return block_below_is_valid;
2057 void MapBlock::copyTo(VoxelManipulator &dst)
2059 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
2060 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
2062 // Copy from data to VoxelManipulator
2063 dst.copyFrom(data, data_area, v3s16(0,0,0),
2064 getPosRelative(), data_size);
2067 void MapBlock::copyFrom(VoxelManipulator &dst)
2069 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
2070 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
2072 // Copy from VoxelManipulator to data
2073 dst.copyTo(data, data_area, v3s16(0,0,0),
2074 getPosRelative(), data_size);
2077 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
2082 m_objects.step(dtime, server, daynight_ratio);
2088 void MapBlock::updateDayNightDiff()
2092 m_day_night_differs = false;
2096 bool differs = false;
2099 Check if any lighting value differs
2101 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2103 MapNode &n = data[i];
2104 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
2112 If some lighting values differ, check if the whole thing is
2113 just air. If it is, differ = false
2117 bool only_air = true;
2118 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2120 MapNode &n = data[i];
2121 if(n.d != CONTENT_AIR)
2131 // Set member variable
2132 m_day_night_differs = differs;
2135 s16 MapBlock::getGroundLevel(v2s16 p2d)
2141 s16 y = MAP_BLOCKSIZE-1;
2144 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
2145 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
2147 if(y == MAP_BLOCKSIZE-1)
2155 catch(InvalidPositionException &e)
2165 void MapBlock::serialize(std::ostream &os, u8 version)
2167 if(!ser_ver_supported(version))
2168 throw VersionMismatchException("ERROR: MapBlock format not supported");
2172 throw SerializationError("ERROR: Not writing dummy block.");
2175 // These have no compression
2176 if(version <= 3 || version == 5 || version == 6)
2178 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2180 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
2181 SharedBuffer<u8> dest(buflen);
2183 dest[0] = is_underground;
2184 for(u32 i=0; i<nodecount; i++)
2186 u32 s = 1 + i * MapNode::serializedLength(version);
2187 data[i].serialize(&dest[s], version);
2190 os.write((char*)*dest, dest.getSize());
2192 else if(version <= 10)
2196 Compress the materials and the params separately.
2200 os.write((char*)&is_underground, 1);
2202 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2204 // Get and compress materials
2205 SharedBuffer<u8> materialdata(nodecount);
2206 for(u32 i=0; i<nodecount; i++)
2208 materialdata[i] = data[i].d;
2210 compress(materialdata, os, version);
2212 // Get and compress lights
2213 SharedBuffer<u8> lightdata(nodecount);
2214 for(u32 i=0; i<nodecount; i++)
2216 lightdata[i] = data[i].param;
2218 compress(lightdata, os, version);
2222 // Get and compress param2
2223 SharedBuffer<u8> param2data(nodecount);
2224 for(u32 i=0; i<nodecount; i++)
2226 param2data[i] = data[i].param2;
2228 compress(param2data, os, version);
2231 // All other versions (newest)
2238 if(m_day_night_differs)
2240 if(m_lighting_expired)
2242 os.write((char*)&flags, 1);
2244 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2250 SharedBuffer<u8> databuf(nodecount*3);
2253 for(u32 i=0; i<nodecount; i++)
2255 databuf[i] = data[i].d;
2259 for(u32 i=0; i<nodecount; i++)
2261 databuf[i+nodecount] = data[i].param;
2265 for(u32 i=0; i<nodecount; i++)
2267 databuf[i+nodecount*2] = data[i].param2;
2271 Compress data to output stream
2274 compress(databuf, os, version);
2284 std::ostringstream oss(std::ios_base::binary);
2285 m_node_metadata.serialize(oss);
2286 os<<serializeString(oss.str());
2288 // This will happen if the string is longer than 65535
2289 catch(SerializationError &e)
2291 // Use an empty string
2292 os<<serializeString("");
2297 std::ostringstream oss(std::ios_base::binary);
2298 m_node_metadata.serialize(oss);
2299 compressZlib(oss.str(), os);
2300 //os<<serializeLongString(oss.str());
2306 void MapBlock::deSerialize(std::istream &is, u8 version)
2308 if(!ser_ver_supported(version))
2309 throw VersionMismatchException("ERROR: MapBlock format not supported");
2311 // These have no lighting info
2314 setLightingExpired(true);
2317 // These have no compression
2318 if(version <= 3 || version == 5 || version == 6)
2320 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2323 if(is.gcount() != 1)
2324 throw SerializationError
2325 ("MapBlock::deSerialize: no enough input data");
2326 is_underground = tmp;
2327 for(u32 i=0; i<nodecount; i++)
2329 s32 len = MapNode::serializedLength(version);
2330 SharedBuffer<u8> d(len);
2331 is.read((char*)*d, len);
2332 if(is.gcount() != len)
2333 throw SerializationError
2334 ("MapBlock::deSerialize: no enough input data");
2335 data[i].deSerialize(*d, version);
2338 else if(version <= 10)
2340 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2343 is.read((char*)&t8, 1);
2344 is_underground = t8;
2347 // Uncompress and set material data
2348 std::ostringstream os(std::ios_base::binary);
2349 decompress(is, os, version);
2350 std::string s = os.str();
2351 if(s.size() != nodecount)
2352 throw SerializationError
2353 ("MapBlock::deSerialize: invalid format");
2354 for(u32 i=0; i<s.size(); i++)
2360 // Uncompress and set param data
2361 std::ostringstream os(std::ios_base::binary);
2362 decompress(is, os, version);
2363 std::string s = os.str();
2364 if(s.size() != nodecount)
2365 throw SerializationError
2366 ("MapBlock::deSerialize: invalid format");
2367 for(u32 i=0; i<s.size(); i++)
2369 data[i].param = s[i];
2375 // Uncompress and set param2 data
2376 std::ostringstream os(std::ios_base::binary);
2377 decompress(is, os, version);
2378 std::string s = os.str();
2379 if(s.size() != nodecount)
2380 throw SerializationError
2381 ("MapBlock::deSerialize: invalid format");
2382 for(u32 i=0; i<s.size(); i++)
2384 data[i].param2 = s[i];
2388 // All other versions (newest)
2391 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2394 is.read((char*)&flags, 1);
2395 is_underground = (flags & 0x01) ? true : false;
2396 m_day_night_differs = (flags & 0x02) ? true : false;
2397 m_lighting_expired = (flags & 0x04) ? true : false;
2400 std::ostringstream os(std::ios_base::binary);
2401 decompress(is, os, version);
2402 std::string s = os.str();
2403 if(s.size() != nodecount*3)
2404 throw SerializationError
2405 ("MapBlock::deSerialize: invalid format");
2408 for(u32 i=0; i<nodecount; i++)
2413 for(u32 i=0; i<nodecount; i++)
2415 data[i].param = s[i+nodecount];
2418 for(u32 i=0; i<nodecount; i++)
2420 data[i].param2 = s[i+nodecount*2];
2432 std::string data = deSerializeString(is);
2433 std::istringstream iss(data, std::ios_base::binary);
2434 m_node_metadata.deSerialize(iss);
2438 //std::string data = deSerializeLongString(is);
2439 std::ostringstream oss(std::ios_base::binary);
2440 decompressZlib(is, oss);
2441 std::istringstream iss(oss.str(), std::ios_base::binary);
2442 m_node_metadata.deSerialize(iss);
2445 catch(SerializationError &e)
2447 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
2448 <<" while deserializing node metadata"<<std::endl;
2454 Translate nodes as specified in the translate_to fields of
2457 NOTE: This isn't really used. Should it be removed?
2459 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2461 MapNode &n = data[i];
2463 MapNode *translate_to = content_features(n.d).translate_to;
2466 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2467 <<translate_to->d<<std::endl;
2473 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
2475 // Versions up from 9 have block objects.
2478 serializeObjects(os, version);
2481 // Versions up from 15 have static objects.
2484 m_static_objects.serialize(os);
2490 writeU32(os, getTimestamp());
2494 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
2497 Versions up from 9 have block objects.
2501 updateObjects(is, version, NULL, 0);
2505 Versions up from 15 have static objects.
2509 m_static_objects.deSerialize(is);
2515 setTimestamp(readU32(is));
2519 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);