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);
991 video::SMaterial material_papyrus;
992 material_papyrus.setFlag(video::EMF_LIGHTING, false);
993 material_papyrus.setFlag(video::EMF_BILINEAR_FILTER, false);
994 material_papyrus.setFlag(video::EMF_FOG_ENABLE, true);
995 material_papyrus.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
996 AtlasPointer pa_papyrus = g_texturesource->getTexture(
997 g_texturesource->getTextureId("papyrus.png"));
998 material_papyrus.setTexture(0, pa_papyrus.atlas);
1000 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1001 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
1002 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1006 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
1011 if(n.d == CONTENT_TORCH)
1013 video::SColor c(255,255,255,255);
1015 // Wall at X+ of node
1016 video::S3DVertex vertices[4] =
1018 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
1019 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
1020 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
1021 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
1024 v3s16 dir = unpackDir(n.dir);
1026 for(s32 i=0; i<4; i++)
1028 if(dir == v3s16(1,0,0))
1029 vertices[i].Pos.rotateXZBy(0);
1030 if(dir == v3s16(-1,0,0))
1031 vertices[i].Pos.rotateXZBy(180);
1032 if(dir == v3s16(0,0,1))
1033 vertices[i].Pos.rotateXZBy(90);
1034 if(dir == v3s16(0,0,-1))
1035 vertices[i].Pos.rotateXZBy(-90);
1036 if(dir == v3s16(0,-1,0))
1037 vertices[i].Pos.rotateXZBy(45);
1038 if(dir == v3s16(0,1,0))
1039 vertices[i].Pos.rotateXZBy(-45);
1041 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1045 video::SMaterial material;
1046 material.setFlag(video::EMF_LIGHTING, false);
1047 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
1048 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1049 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1050 material.MaterialType
1051 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1053 if(dir == v3s16(0,-1,0))
1054 material.setTexture(0,
1055 g_texturesource->getTextureRaw("torch_on_floor.png"));
1056 else if(dir == v3s16(0,1,0))
1057 material.setTexture(0,
1058 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
1059 // For backwards compatibility
1060 else if(dir == v3s16(0,0,0))
1061 material.setTexture(0,
1062 g_texturesource->getTextureRaw("torch_on_floor.png"));
1064 material.setTexture(0,
1065 g_texturesource->getTextureRaw("torch.png"));
1067 u16 indices[] = {0,1,2,2,3,0};
1068 // Add to mesh collector
1069 collector.append(material, vertices, 4, indices, 6);
1074 if(n.d == CONTENT_SIGN_WALL)
1076 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1077 video::SColor c(255,l,l,l);
1079 float d = (float)BS/16;
1080 // Wall at X+ of node
1081 video::S3DVertex vertices[4] =
1083 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
1084 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
1085 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
1086 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
1089 v3s16 dir = unpackDir(n.dir);
1091 for(s32 i=0; i<4; i++)
1093 if(dir == v3s16(1,0,0))
1094 vertices[i].Pos.rotateXZBy(0);
1095 if(dir == v3s16(-1,0,0))
1096 vertices[i].Pos.rotateXZBy(180);
1097 if(dir == v3s16(0,0,1))
1098 vertices[i].Pos.rotateXZBy(90);
1099 if(dir == v3s16(0,0,-1))
1100 vertices[i].Pos.rotateXZBy(-90);
1101 if(dir == v3s16(0,-1,0))
1102 vertices[i].Pos.rotateXYBy(-90);
1103 if(dir == v3s16(0,1,0))
1104 vertices[i].Pos.rotateXYBy(90);
1106 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1110 video::SMaterial material;
1111 material.setFlag(video::EMF_LIGHTING, false);
1112 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
1113 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1114 material.setFlag(video::EMF_FOG_ENABLE, true);
1115 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1116 material.MaterialType
1117 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1119 material.setTexture(0,
1120 g_texturesource->getTextureRaw("sign_wall.png"));
1122 u16 indices[] = {0,1,2,2,3,0};
1123 // Add to mesh collector
1124 collector.append(material, vertices, 4, indices, 6);
1127 Add flowing water to mesh
1129 else if(n.d == CONTENT_WATER)
1131 bool top_is_water = false;
1132 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1133 if(ntop.d == CONTENT_WATER || ntop.d == CONTENT_WATERSOURCE)
1134 top_is_water = true;
1137 // Use the light of the node on top if possible
1138 if(content_features(ntop.d).param_type == CPT_LIGHT)
1139 l = decode_light(ntop.getLightBlend(data->m_daynight_ratio));
1140 // Otherwise use the light of this node (the water)
1142 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1143 video::SColor c(WATER_ALPHA,l,l,l);
1145 // Neighbor water levels (key = relative position)
1146 // Includes current node
1147 core::map<v3s16, f32> neighbor_levels;
1148 core::map<v3s16, u8> neighbor_contents;
1149 core::map<v3s16, u8> neighbor_flags;
1150 const u8 neighborflag_top_is_water = 0x01;
1151 v3s16 neighbor_dirs[9] = {
1162 for(u32 i=0; i<9; i++)
1164 u8 content = CONTENT_AIR;
1165 float level = -0.5 * BS;
1168 v3s16 p2 = p + neighbor_dirs[i];
1169 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1170 if(n2.d != CONTENT_IGNORE)
1174 if(n2.d == CONTENT_WATERSOURCE)
1175 level = (-0.5+node_water_level) * BS;
1176 else if(n2.d == CONTENT_WATER)
1177 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
1178 * node_water_level) * BS;
1180 // Check node above neighbor.
1181 // NOTE: This doesn't get executed if neighbor
1184 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1185 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
1186 flags |= neighborflag_top_is_water;
1189 neighbor_levels.insert(neighbor_dirs[i], level);
1190 neighbor_contents.insert(neighbor_dirs[i], content);
1191 neighbor_flags.insert(neighbor_dirs[i], flags);
1194 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
1195 //float water_level = neighbor_levels[v3s16(0,0,0)];
1197 // Corner heights (average between four waters)
1198 f32 corner_levels[4];
1200 v3s16 halfdirs[4] = {
1206 for(u32 i=0; i<4; i++)
1208 v3s16 cornerdir = halfdirs[i];
1209 float cornerlevel = 0;
1210 u32 valid_count = 0;
1211 for(u32 j=0; j<4; j++)
1213 v3s16 neighbordir = cornerdir - halfdirs[j];
1214 u8 content = neighbor_contents[neighbordir];
1215 // Special case for source nodes
1216 if(content == CONTENT_WATERSOURCE)
1218 cornerlevel = (-0.5+node_water_level)*BS;
1222 else if(content == CONTENT_WATER)
1224 cornerlevel += neighbor_levels[neighbordir];
1227 else if(content == CONTENT_AIR)
1229 cornerlevel += -0.5*BS;
1234 cornerlevel /= valid_count;
1235 corner_levels[i] = cornerlevel;
1242 v3s16 side_dirs[4] = {
1248 s16 side_corners[4][2] = {
1254 for(u32 i=0; i<4; i++)
1256 v3s16 dir = side_dirs[i];
1259 If our topside is water and neighbor's topside
1260 is water, don't draw side face
1263 neighbor_flags[dir] & neighborflag_top_is_water)
1266 u8 neighbor_content = neighbor_contents[dir];
1268 // Don't draw face if neighbor is not air or water
1269 if(neighbor_content != CONTENT_AIR
1270 && neighbor_content != CONTENT_WATER)
1273 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1275 // Don't draw any faces if neighbor is water and top is water
1276 if(neighbor_is_water == true && top_is_water == false)
1279 video::S3DVertex vertices[4] =
1281 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1282 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1283 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1284 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1285 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1286 pa_water1.x0(), pa_water1.y1()),
1287 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1288 pa_water1.x1(), pa_water1.y1()),
1289 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1290 pa_water1.x1(), pa_water1.y0()),
1291 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1292 pa_water1.x0(), pa_water1.y0()),
1296 If our topside is water, set upper border of face
1297 at upper border of node
1301 vertices[2].Pos.Y = 0.5*BS;
1302 vertices[3].Pos.Y = 0.5*BS;
1305 Otherwise upper position of face is corner levels
1309 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1310 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1314 If neighbor is water, lower border of face is corner
1317 if(neighbor_is_water)
1319 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1320 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1323 If neighbor is not water, lower border of face is
1324 lower border of node
1328 vertices[0].Pos.Y = -0.5*BS;
1329 vertices[1].Pos.Y = -0.5*BS;
1332 for(s32 j=0; j<4; j++)
1334 if(dir == v3s16(0,0,1))
1335 vertices[j].Pos.rotateXZBy(0);
1336 if(dir == v3s16(0,0,-1))
1337 vertices[j].Pos.rotateXZBy(180);
1338 if(dir == v3s16(-1,0,0))
1339 vertices[j].Pos.rotateXZBy(90);
1340 if(dir == v3s16(1,0,-0))
1341 vertices[j].Pos.rotateXZBy(-90);
1343 vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
1346 u16 indices[] = {0,1,2,2,3,0};
1347 // Add to mesh collector
1348 collector.append(material_water1, vertices, 4, indices, 6);
1352 Generate top side, if appropriate
1355 if(top_is_water == false)
1357 video::S3DVertex vertices[4] =
1359 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1360 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1361 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1362 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1363 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1364 pa_water1.x0(), pa_water1.y1()),
1365 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1366 pa_water1.x1(), pa_water1.y1()),
1367 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1368 pa_water1.x1(), pa_water1.y0()),
1369 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1370 pa_water1.x0(), pa_water1.y0()),
1373 // This fixes a strange bug
1374 s32 corner_resolve[4] = {3,2,1,0};
1376 for(s32 i=0; i<4; i++)
1378 //vertices[i].Pos.Y += water_level;
1379 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1380 s32 j = corner_resolve[i];
1381 vertices[i].Pos.Y += corner_levels[j];
1382 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1385 u16 indices[] = {0,1,2,2,3,0};
1386 // Add to mesh collector
1387 collector.append(material_water1, vertices, 4, indices, 6);
1391 Add water sources to mesh if using new style
1393 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1395 //bool top_is_water = false;
1396 bool top_is_air = false;
1397 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1398 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1399 top_is_water = true;*/
1400 if(n.d == CONTENT_AIR)
1403 /*if(top_is_water == true)
1405 if(top_is_air == false)
1408 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1409 video::SColor c(WATER_ALPHA,l,l,l);
1411 video::S3DVertex vertices[4] =
1413 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1414 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1415 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1416 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1417 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1418 pa_water1.x0(), pa_water1.y1()),
1419 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1420 pa_water1.x1(), pa_water1.y1()),
1421 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1422 pa_water1.x1(), pa_water1.y0()),
1423 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1424 pa_water1.x0(), pa_water1.y0()),
1427 for(s32 i=0; i<4; i++)
1429 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1430 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1433 u16 indices[] = {0,1,2,2,3,0};
1434 // Add to mesh collector
1435 collector.append(material_water1, vertices, 4, indices, 6);
1438 Add leaves if using new style
1440 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1442 /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
1443 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1444 video::SColor c(255,l,l,l);
1446 for(u32 j=0; j<6; j++)
1448 video::S3DVertex vertices[4] =
1450 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1451 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1452 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1453 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1454 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1455 pa_leaves1.x0(), pa_leaves1.y1()),
1456 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1457 pa_leaves1.x1(), pa_leaves1.y1()),
1458 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1459 pa_leaves1.x1(), pa_leaves1.y0()),
1460 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1461 pa_leaves1.x0(), pa_leaves1.y0()),
1466 for(u16 i=0; i<4; i++)
1467 vertices[i].Pos.rotateXZBy(0);
1471 for(u16 i=0; i<4; i++)
1472 vertices[i].Pos.rotateXZBy(180);
1476 for(u16 i=0; i<4; i++)
1477 vertices[i].Pos.rotateXZBy(-90);
1481 for(u16 i=0; i<4; i++)
1482 vertices[i].Pos.rotateXZBy(90);
1486 for(u16 i=0; i<4; i++)
1487 vertices[i].Pos.rotateYZBy(-90);
1491 for(u16 i=0; i<4; i++)
1492 vertices[i].Pos.rotateYZBy(90);
1495 for(u16 i=0; i<4; i++)
1497 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1500 u16 indices[] = {0,1,2,2,3,0};
1501 // Add to mesh collector
1502 collector.append(material_leaves1, vertices, 4, indices, 6);
1508 else if(n.d == CONTENT_GLASS)
1510 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1511 video::SColor c(255,l,l,l);
1513 for(u32 j=0; j<6; j++)
1515 video::S3DVertex vertices[4] =
1517 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1518 pa_glass.x0(), pa_glass.y1()),
1519 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1520 pa_glass.x1(), pa_glass.y1()),
1521 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1522 pa_glass.x1(), pa_glass.y0()),
1523 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1524 pa_glass.x0(), pa_glass.y0()),
1529 for(u16 i=0; i<4; i++)
1530 vertices[i].Pos.rotateXZBy(0);
1534 for(u16 i=0; i<4; i++)
1535 vertices[i].Pos.rotateXZBy(180);
1539 for(u16 i=0; i<4; i++)
1540 vertices[i].Pos.rotateXZBy(-90);
1544 for(u16 i=0; i<4; i++)
1545 vertices[i].Pos.rotateXZBy(90);
1549 for(u16 i=0; i<4; i++)
1550 vertices[i].Pos.rotateYZBy(-90);
1554 for(u16 i=0; i<4; i++)
1555 vertices[i].Pos.rotateYZBy(90);
1558 for(u16 i=0; i<4; i++)
1560 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1563 u16 indices[] = {0,1,2,2,3,0};
1564 // Add to mesh collector
1565 collector.append(material_glass, vertices, 4, indices, 6);
1571 else if(n.d == CONTENT_FENCE)
1573 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1574 video::SColor c(255,l,l,l);
1576 const f32 post_rad=(f32)BS/(32.0/4.0);
1577 const f32 bar_rad=(f32)BS/(32.0/2.0);
1578 const f32 bar_len=(f32)(BS/2)-post_rad;
1580 // The post - always present
1581 v3f pos = intToFloat(p+blockpos_nodes, BS);
1582 makeCuboid(material_wood, &collector,
1584 post_rad,BS/2,post_rad);
1586 // Now a section of fence, +X, if there's a post there
1589 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1590 if(n2.d == CONTENT_FENCE)
1592 pos = intToFloat(p+blockpos_nodes, BS);
1595 makeCuboid(material_wood, &collector,
1597 bar_len,bar_rad,bar_rad);
1600 makeCuboid(material_wood, &collector,
1602 bar_len,bar_rad,bar_rad);
1605 // Now a section of fence, +Z, if there's a post there
1608 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1609 if(n2.d == CONTENT_FENCE)
1611 pos = intToFloat(p+blockpos_nodes, BS);
1614 makeCuboid(material_wood, &collector,
1616 bar_rad,bar_rad,bar_len);
1618 makeCuboid(material_wood, &collector,
1620 bar_rad,bar_rad,bar_len);
1625 else if(n.d == CONTENT_PAPYRUS)
1627 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1628 video::SColor c(255,l,l,l);
1630 for(u32 j=0; j<4; j++)
1632 video::S3DVertex vertices[4] =
1634 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c,
1635 pa_papyrus.x0(), pa_papyrus.y1()),
1636 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c,
1637 pa_papyrus.x1(), pa_papyrus.y1()),
1638 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c,
1639 pa_papyrus.x1(), pa_papyrus.y0()),
1640 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c,
1641 pa_papyrus.x0(), pa_papyrus.y0()),
1646 for(u16 i=0; i<4; i++)
1647 vertices[i].Pos.rotateXZBy(45);
1651 for(u16 i=0; i<4; i++)
1652 vertices[i].Pos.rotateXZBy(-45);
1656 for(u16 i=0; i<4; i++)
1657 vertices[i].Pos.rotateXZBy(135);
1661 for(u16 i=0; i<4; i++)
1662 vertices[i].Pos.rotateXZBy(-135);
1665 for(u16 i=0; i<4; i++)
1667 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1670 u16 indices[] = {0,1,2,2,3,0};
1671 // Add to mesh collector
1672 collector.append(material_papyrus, vertices, 4, indices, 6);
1679 Add stuff from collector to mesh
1682 scene::SMesh *mesh_new = NULL;
1683 mesh_new = new scene::SMesh();
1685 collector.fillMesh(mesh_new);
1688 Do some stuff to the mesh
1691 mesh_new->recalculateBoundingBox();
1694 Delete new mesh if it is empty
1697 if(mesh_new->getMeshBufferCount() == 0)
1706 // Usually 1-700 faces and 1-7 materials
1707 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1708 <<"and uses "<<mesh_new->getMeshBufferCount()
1709 <<" materials (meshbuffers)"<<std::endl;
1712 // Use VBO for mesh (this just would set this for ever buffer)
1713 // This will lead to infinite memory usage because or irrlicht.
1714 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1717 NOTE: If that is enabled, some kind of a queue to the main
1718 thread should be made which would call irrlicht to delete
1719 the hardware buffer and then delete the mesh
1725 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1734 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
1738 is_underground(false),
1739 m_lighting_expired(true),
1740 m_day_night_differs(false),
1741 //m_not_fully_generated(false),
1743 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED)
1749 //m_spawn_timer = -10000;
1752 m_mesh_expired = false;
1755 m_temp_mods_mutex.Init();
1759 MapBlock::~MapBlock()
1763 JMutexAutoLock lock(mesh_mutex);
1777 bool MapBlock::isValidPositionParent(v3s16 p)
1779 if(isValidPosition(p))
1784 return m_parent->isValidPosition(getPosRelative() + p);
1788 MapNode MapBlock::getNodeParent(v3s16 p)
1790 if(isValidPosition(p) == false)
1792 return m_parent->getNode(getPosRelative() + p);
1797 throw InvalidPositionException();
1798 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1802 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
1804 if(isValidPosition(p) == false)
1806 m_parent->setNode(getPosRelative() + p, n);
1811 throw InvalidPositionException();
1812 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
1816 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
1818 if(isValidPosition(p) == false)
1821 return m_parent->getNode(getPosRelative() + p);
1823 catch(InvalidPositionException &e)
1825 return MapNode(CONTENT_IGNORE);
1832 return MapNode(CONTENT_IGNORE);
1834 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1841 void MapBlock::updateMesh(u32 daynight_ratio)
1845 DEBUG: If mesh has been generated, don't generate it again
1848 JMutexAutoLock meshlock(mesh_mutex);
1855 data.fill(daynight_ratio, this);
1857 scene::SMesh *mesh_new = makeMapBlockMesh(&data);
1863 replaceMesh(mesh_new);
1868 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
1872 //scene::SMesh *mesh_old = mesh[daynight_i];
1873 //mesh[daynight_i] = mesh_new;
1875 scene::SMesh *mesh_old = mesh;
1877 setMeshExpired(false);
1879 if(mesh_old != NULL)
1881 // Remove hardware buffers of meshbuffers of mesh
1882 // NOTE: No way, this runs in a different thread and everything
1883 /*u32 c = mesh_old->getMeshBufferCount();
1884 for(u32 i=0; i<c; i++)
1886 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1889 /*dstream<<"mesh_old->getReferenceCount()="
1890 <<mesh_old->getReferenceCount()<<std::endl;
1891 u32 c = mesh_old->getMeshBufferCount();
1892 for(u32 i=0; i<c; i++)
1894 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1895 dstream<<"buf->getReferenceCount()="
1896 <<buf->getReferenceCount()<<std::endl;
1905 mesh_mutex.Unlock();
1911 Propagates sunlight down through the block.
1912 Doesn't modify nodes that are not affected by sunlight.
1914 Returns false if sunlight at bottom block is invalid.
1915 Returns true if sunlight at bottom block is valid.
1916 Returns true if bottom block doesn't exist.
1918 If there is a block above, continues from it.
1919 If there is no block above, assumes there is sunlight, unless
1920 is_underground is set or highest node is water.
1922 All sunlighted nodes are added to light_sources.
1924 If grow_grass==true, turns sunglighted mud into grass.
1926 if remove_light==true, sets non-sunlighted nodes black.
1928 if black_air_left!=NULL, it is set to true if non-sunlighted
1929 air is left in block.
1931 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1932 bool remove_light, bool *black_air_left,
1935 // Whether the sunlight at the top of the bottom block is valid
1936 bool block_below_is_valid = true;
1938 v3s16 pos_relative = getPosRelative();
1940 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1942 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1945 bool no_sunlight = false;
1946 bool no_top_block = false;
1947 // Check if node above block has sunlight
1949 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1950 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1955 catch(InvalidPositionException &e)
1957 no_top_block = true;
1959 // NOTE: This makes over-ground roofed places sunlighted
1960 // Assume sunlight, unless is_underground==true
1967 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1968 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1973 // NOTE: As of now, this just would make everything dark.
1975 //no_sunlight = true;
1978 #if 0 // Doesn't work; nothing gets light.
1979 bool no_sunlight = true;
1980 bool no_top_block = false;
1981 // Check if node above block has sunlight
1983 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1984 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1986 no_sunlight = false;
1989 catch(InvalidPositionException &e)
1991 no_top_block = true;
1995 /*std::cout<<"("<<x<<","<<z<<"): "
1996 <<"no_top_block="<<no_top_block
1997 <<", is_underground="<<is_underground
1998 <<", no_sunlight="<<no_sunlight
2001 s16 y = MAP_BLOCKSIZE-1;
2003 // This makes difference to diminishing in water.
2004 bool stopped_to_solid_object = false;
2006 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
2011 MapNode &n = getNodeRef(pos);
2013 if(current_light == 0)
2017 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
2019 // Do nothing: Sunlight is continued
2021 else if(n.light_propagates() == false)
2025 bool upper_is_air = false;
2028 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
2029 upper_is_air = true;
2031 catch(InvalidPositionException &e)
2034 // Turn mud into grass
2035 if(upper_is_air && n.d == CONTENT_MUD
2036 && current_light == LIGHT_SUN)
2038 n.d = CONTENT_GRASS;
2042 // A solid object is on the way.
2043 stopped_to_solid_object = true;
2051 current_light = diminish_light(current_light);
2054 u8 old_light = n.getLight(LIGHTBANK_DAY);
2056 if(current_light > old_light || remove_light)
2058 n.setLight(LIGHTBANK_DAY, current_light);
2061 if(diminish_light(current_light) != 0)
2063 light_sources.insert(pos_relative + pos, true);
2066 if(current_light == 0 && stopped_to_solid_object)
2070 *black_air_left = true;
2075 // Whether or not the block below should see LIGHT_SUN
2076 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
2079 If the block below hasn't already been marked invalid:
2081 Check if the node below the block has proper sunlight at top.
2082 If not, the block below is invalid.
2084 Ignore non-transparent nodes as they always have no light
2088 if(block_below_is_valid)
2090 MapNode n = getNodeParent(v3s16(x, -1, z));
2091 if(n.light_propagates())
2093 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
2094 && sunlight_should_go_down == false)
2095 block_below_is_valid = false;
2096 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
2097 && sunlight_should_go_down == true)
2098 block_below_is_valid = false;
2102 catch(InvalidPositionException &e)
2104 /*std::cout<<"InvalidBlockException for bottom block node"
2106 // Just no block below, no need to panic.
2111 return block_below_is_valid;
2115 void MapBlock::copyTo(VoxelManipulator &dst)
2117 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
2118 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
2120 // Copy from data to VoxelManipulator
2121 dst.copyFrom(data, data_area, v3s16(0,0,0),
2122 getPosRelative(), data_size);
2125 void MapBlock::copyFrom(VoxelManipulator &dst)
2127 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
2128 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
2130 // Copy from VoxelManipulator to data
2131 dst.copyTo(data, data_area, v3s16(0,0,0),
2132 getPosRelative(), data_size);
2135 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
2140 m_objects.step(dtime, server, daynight_ratio);
2146 void MapBlock::updateDayNightDiff()
2150 m_day_night_differs = false;
2154 bool differs = false;
2157 Check if any lighting value differs
2159 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2161 MapNode &n = data[i];
2162 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
2170 If some lighting values differ, check if the whole thing is
2171 just air. If it is, differ = false
2175 bool only_air = true;
2176 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2178 MapNode &n = data[i];
2179 if(n.d != CONTENT_AIR)
2189 // Set member variable
2190 m_day_night_differs = differs;
2193 s16 MapBlock::getGroundLevel(v2s16 p2d)
2199 s16 y = MAP_BLOCKSIZE-1;
2202 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
2203 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
2205 if(y == MAP_BLOCKSIZE-1)
2213 catch(InvalidPositionException &e)
2223 void MapBlock::serialize(std::ostream &os, u8 version)
2225 if(!ser_ver_supported(version))
2226 throw VersionMismatchException("ERROR: MapBlock format not supported");
2230 throw SerializationError("ERROR: Not writing dummy block.");
2233 // These have no compression
2234 if(version <= 3 || version == 5 || version == 6)
2236 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2238 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
2239 SharedBuffer<u8> dest(buflen);
2241 dest[0] = is_underground;
2242 for(u32 i=0; i<nodecount; i++)
2244 u32 s = 1 + i * MapNode::serializedLength(version);
2245 data[i].serialize(&dest[s], version);
2248 os.write((char*)*dest, dest.getSize());
2250 else if(version <= 10)
2254 Compress the materials and the params separately.
2258 os.write((char*)&is_underground, 1);
2260 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2262 // Get and compress materials
2263 SharedBuffer<u8> materialdata(nodecount);
2264 for(u32 i=0; i<nodecount; i++)
2266 materialdata[i] = data[i].d;
2268 compress(materialdata, os, version);
2270 // Get and compress lights
2271 SharedBuffer<u8> lightdata(nodecount);
2272 for(u32 i=0; i<nodecount; i++)
2274 lightdata[i] = data[i].param;
2276 compress(lightdata, os, version);
2280 // Get and compress param2
2281 SharedBuffer<u8> param2data(nodecount);
2282 for(u32 i=0; i<nodecount; i++)
2284 param2data[i] = data[i].param2;
2286 compress(param2data, os, version);
2289 // All other versions (newest)
2296 if(m_day_night_differs)
2298 if(m_lighting_expired)
2300 os.write((char*)&flags, 1);
2302 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2308 SharedBuffer<u8> databuf(nodecount*3);
2311 for(u32 i=0; i<nodecount; i++)
2313 databuf[i] = data[i].d;
2317 for(u32 i=0; i<nodecount; i++)
2319 databuf[i+nodecount] = data[i].param;
2323 for(u32 i=0; i<nodecount; i++)
2325 databuf[i+nodecount*2] = data[i].param2;
2329 Compress data to output stream
2332 compress(databuf, os, version);
2342 std::ostringstream oss(std::ios_base::binary);
2343 m_node_metadata.serialize(oss);
2344 os<<serializeString(oss.str());
2346 // This will happen if the string is longer than 65535
2347 catch(SerializationError &e)
2349 // Use an empty string
2350 os<<serializeString("");
2355 std::ostringstream oss(std::ios_base::binary);
2356 m_node_metadata.serialize(oss);
2357 compressZlib(oss.str(), os);
2358 //os<<serializeLongString(oss.str());
2364 void MapBlock::deSerialize(std::istream &is, u8 version)
2366 if(!ser_ver_supported(version))
2367 throw VersionMismatchException("ERROR: MapBlock format not supported");
2369 // These have no lighting info
2372 setLightingExpired(true);
2375 // These have no compression
2376 if(version <= 3 || version == 5 || version == 6)
2378 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2381 if(is.gcount() != 1)
2382 throw SerializationError
2383 ("MapBlock::deSerialize: no enough input data");
2384 is_underground = tmp;
2385 for(u32 i=0; i<nodecount; i++)
2387 s32 len = MapNode::serializedLength(version);
2388 SharedBuffer<u8> d(len);
2389 is.read((char*)*d, len);
2390 if(is.gcount() != len)
2391 throw SerializationError
2392 ("MapBlock::deSerialize: no enough input data");
2393 data[i].deSerialize(*d, version);
2396 else if(version <= 10)
2398 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2401 is.read((char*)&t8, 1);
2402 is_underground = t8;
2405 // Uncompress and set material data
2406 std::ostringstream os(std::ios_base::binary);
2407 decompress(is, os, version);
2408 std::string s = os.str();
2409 if(s.size() != nodecount)
2410 throw SerializationError
2411 ("MapBlock::deSerialize: invalid format");
2412 for(u32 i=0; i<s.size(); i++)
2418 // Uncompress and set param data
2419 std::ostringstream os(std::ios_base::binary);
2420 decompress(is, os, version);
2421 std::string s = os.str();
2422 if(s.size() != nodecount)
2423 throw SerializationError
2424 ("MapBlock::deSerialize: invalid format");
2425 for(u32 i=0; i<s.size(); i++)
2427 data[i].param = s[i];
2433 // Uncompress and set param2 data
2434 std::ostringstream os(std::ios_base::binary);
2435 decompress(is, os, version);
2436 std::string s = os.str();
2437 if(s.size() != nodecount)
2438 throw SerializationError
2439 ("MapBlock::deSerialize: invalid format");
2440 for(u32 i=0; i<s.size(); i++)
2442 data[i].param2 = s[i];
2446 // All other versions (newest)
2449 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2452 is.read((char*)&flags, 1);
2453 is_underground = (flags & 0x01) ? true : false;
2454 m_day_night_differs = (flags & 0x02) ? true : false;
2455 m_lighting_expired = (flags & 0x04) ? true : false;
2458 std::ostringstream os(std::ios_base::binary);
2459 decompress(is, os, version);
2460 std::string s = os.str();
2461 if(s.size() != nodecount*3)
2462 throw SerializationError
2463 ("MapBlock::deSerialize: invalid format");
2466 for(u32 i=0; i<nodecount; i++)
2471 for(u32 i=0; i<nodecount; i++)
2473 data[i].param = s[i+nodecount];
2476 for(u32 i=0; i<nodecount; i++)
2478 data[i].param2 = s[i+nodecount*2];
2490 std::string data = deSerializeString(is);
2491 std::istringstream iss(data, std::ios_base::binary);
2492 m_node_metadata.deSerialize(iss);
2496 //std::string data = deSerializeLongString(is);
2497 std::ostringstream oss(std::ios_base::binary);
2498 decompressZlib(is, oss);
2499 std::istringstream iss(oss.str(), std::ios_base::binary);
2500 m_node_metadata.deSerialize(iss);
2503 catch(SerializationError &e)
2505 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
2506 <<" while deserializing node metadata"<<std::endl;
2512 Translate nodes as specified in the translate_to fields of
2515 NOTE: This isn't really used. Should it be removed?
2517 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2519 MapNode &n = data[i];
2521 MapNode *translate_to = content_features(n.d).translate_to;
2524 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2525 <<translate_to->d<<std::endl;
2531 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
2533 // Versions up from 9 have block objects.
2536 serializeObjects(os, version);
2539 // Versions up from 15 have static objects.
2542 m_static_objects.serialize(os);
2548 writeU32(os, getTimestamp());
2552 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
2555 Versions up from 9 have block objects.
2559 updateObjects(is, version, NULL, 0);
2563 Versions up from 15 have static objects.
2567 m_static_objects.deSerialize(is);
2573 setTimestamp(readU32(is));
2577 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);