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);
1675 else if(n.d == CONTENT_RAIL)
1677 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1678 video::SColor c(255,l,l,l);
1680 bool is_rail_x [] = { false, false }; /* x-1, x+1 */
1681 bool is_rail_z [] = { false, false }; /* z-1, z+1 */
1683 MapNode n_minus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1,y,z));
1684 MapNode n_plus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1,y,z));
1685 MapNode n_minus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z-1));
1686 MapNode n_plus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z+1));
1688 if(n_minus_x.d == CONTENT_RAIL)
1689 is_rail_x[0] = true;
1690 if(n_plus_x.d == CONTENT_RAIL)
1691 is_rail_x[1] = true;
1692 if(n_minus_z.d == CONTENT_RAIL)
1693 is_rail_z[0] = true;
1694 if(n_plus_z.d == CONTENT_RAIL)
1695 is_rail_z[1] = true;
1697 float d = (float)BS/16;
1698 video::S3DVertex vertices[4] =
1700 video::S3DVertex(-BS/2,-BS/2+d,-BS/2, 0,0,0, c,
1702 video::S3DVertex(BS/2,-BS/2+d,-BS/2, 0,0,0, c,
1704 video::S3DVertex(BS/2,-BS/2+d,BS/2, 0,0,0, c,
1706 video::S3DVertex(-BS/2,-BS/2+d,BS/2, 0,0,0, c,
1710 video::SMaterial material_rail;
1711 material_rail.setFlag(video::EMF_LIGHTING, false);
1712 material_rail.setFlag(video::EMF_BACK_FACE_CULLING, false);
1713 material_rail.setFlag(video::EMF_BILINEAR_FILTER, false);
1714 material_rail.setFlag(video::EMF_FOG_ENABLE, true);
1715 material_rail.MaterialType
1716 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1718 int adjacencies = is_rail_x[0] + is_rail_x[1] + is_rail_z[0] + is_rail_z[1];
1722 material_rail.setTexture(0, g_texturesource->getTextureRaw("rail.png"));
1723 else if(adjacencies == 2)
1725 if((is_rail_x[0] && is_rail_x[1]) || (is_rail_z[0] && is_rail_z[1]))
1726 material_rail.setTexture(0, g_texturesource->getTextureRaw("rail.png"));
1728 material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_curved.png"));
1730 else if(adjacencies == 3)
1731 material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_t_junction.png"));
1732 else if(adjacencies == 4)
1733 material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_crossing.png"));
1738 if(adjacencies == 1)
1740 if(is_rail_x[0] || is_rail_x[1])
1743 else if(adjacencies == 2)
1745 if(is_rail_x[0] && is_rail_x[1])
1747 else if(is_rail_x[0] && is_rail_z[0])
1749 else if(is_rail_x[0] && is_rail_z[1])
1751 else if(is_rail_x[1] && is_rail_z[1])
1754 else if(adjacencies == 3)
1767 for(u16 i=0; i<4; i++)
1768 vertices[i].Pos.rotateXZBy(angle);
1771 for(s32 i=0; i<4; i++)
1773 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1776 u16 indices[] = {0,1,2,2,3,0};
1777 collector.append(material_rail, vertices, 4, indices, 6);
1783 Add stuff from collector to mesh
1786 scene::SMesh *mesh_new = NULL;
1787 mesh_new = new scene::SMesh();
1789 collector.fillMesh(mesh_new);
1792 Do some stuff to the mesh
1795 mesh_new->recalculateBoundingBox();
1798 Delete new mesh if it is empty
1801 if(mesh_new->getMeshBufferCount() == 0)
1810 // Usually 1-700 faces and 1-7 materials
1811 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1812 <<"and uses "<<mesh_new->getMeshBufferCount()
1813 <<" materials (meshbuffers)"<<std::endl;
1816 // Use VBO for mesh (this just would set this for ever buffer)
1817 // This will lead to infinite memory usage because or irrlicht.
1818 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1821 NOTE: If that is enabled, some kind of a queue to the main
1822 thread should be made which would call irrlicht to delete
1823 the hardware buffer and then delete the mesh
1829 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1838 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
1842 is_underground(false),
1843 m_lighting_expired(true),
1844 m_day_night_differs(false),
1845 //m_not_fully_generated(false),
1847 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED)
1853 //m_spawn_timer = -10000;
1856 m_mesh_expired = false;
1859 m_temp_mods_mutex.Init();
1863 MapBlock::~MapBlock()
1867 JMutexAutoLock lock(mesh_mutex);
1881 bool MapBlock::isValidPositionParent(v3s16 p)
1883 if(isValidPosition(p))
1888 return m_parent->isValidPosition(getPosRelative() + p);
1892 MapNode MapBlock::getNodeParent(v3s16 p)
1894 if(isValidPosition(p) == false)
1896 return m_parent->getNode(getPosRelative() + p);
1901 throw InvalidPositionException();
1902 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1906 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
1908 if(isValidPosition(p) == false)
1910 m_parent->setNode(getPosRelative() + p, n);
1915 throw InvalidPositionException();
1916 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
1920 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
1922 if(isValidPosition(p) == false)
1925 return m_parent->getNode(getPosRelative() + p);
1927 catch(InvalidPositionException &e)
1929 return MapNode(CONTENT_IGNORE);
1936 return MapNode(CONTENT_IGNORE);
1938 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1945 void MapBlock::updateMesh(u32 daynight_ratio)
1949 DEBUG: If mesh has been generated, don't generate it again
1952 JMutexAutoLock meshlock(mesh_mutex);
1959 data.fill(daynight_ratio, this);
1961 scene::SMesh *mesh_new = makeMapBlockMesh(&data);
1967 replaceMesh(mesh_new);
1972 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
1976 //scene::SMesh *mesh_old = mesh[daynight_i];
1977 //mesh[daynight_i] = mesh_new;
1979 scene::SMesh *mesh_old = mesh;
1981 setMeshExpired(false);
1983 if(mesh_old != NULL)
1985 // Remove hardware buffers of meshbuffers of mesh
1986 // NOTE: No way, this runs in a different thread and everything
1987 /*u32 c = mesh_old->getMeshBufferCount();
1988 for(u32 i=0; i<c; i++)
1990 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1993 /*dstream<<"mesh_old->getReferenceCount()="
1994 <<mesh_old->getReferenceCount()<<std::endl;
1995 u32 c = mesh_old->getMeshBufferCount();
1996 for(u32 i=0; i<c; i++)
1998 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1999 dstream<<"buf->getReferenceCount()="
2000 <<buf->getReferenceCount()<<std::endl;
2009 mesh_mutex.Unlock();
2015 Propagates sunlight down through the block.
2016 Doesn't modify nodes that are not affected by sunlight.
2018 Returns false if sunlight at bottom block is invalid.
2019 Returns true if sunlight at bottom block is valid.
2020 Returns true if bottom block doesn't exist.
2022 If there is a block above, continues from it.
2023 If there is no block above, assumes there is sunlight, unless
2024 is_underground is set or highest node is water.
2026 All sunlighted nodes are added to light_sources.
2028 If grow_grass==true, turns sunglighted mud into grass.
2030 if remove_light==true, sets non-sunlighted nodes black.
2032 if black_air_left!=NULL, it is set to true if non-sunlighted
2033 air is left in block.
2035 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
2036 bool remove_light, bool *black_air_left,
2039 // Whether the sunlight at the top of the bottom block is valid
2040 bool block_below_is_valid = true;
2042 v3s16 pos_relative = getPosRelative();
2044 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
2046 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
2049 bool no_sunlight = false;
2050 bool no_top_block = false;
2051 // Check if node above block has sunlight
2053 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
2054 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
2059 catch(InvalidPositionException &e)
2061 no_top_block = true;
2063 // NOTE: This makes over-ground roofed places sunlighted
2064 // Assume sunlight, unless is_underground==true
2071 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
2072 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
2077 // NOTE: As of now, this just would make everything dark.
2079 //no_sunlight = true;
2082 #if 0 // Doesn't work; nothing gets light.
2083 bool no_sunlight = true;
2084 bool no_top_block = false;
2085 // Check if node above block has sunlight
2087 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
2088 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
2090 no_sunlight = false;
2093 catch(InvalidPositionException &e)
2095 no_top_block = true;
2099 /*std::cout<<"("<<x<<","<<z<<"): "
2100 <<"no_top_block="<<no_top_block
2101 <<", is_underground="<<is_underground
2102 <<", no_sunlight="<<no_sunlight
2105 s16 y = MAP_BLOCKSIZE-1;
2107 // This makes difference to diminishing in water.
2108 bool stopped_to_solid_object = false;
2110 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
2115 MapNode &n = getNodeRef(pos);
2117 if(current_light == 0)
2121 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
2123 // Do nothing: Sunlight is continued
2125 else if(n.light_propagates() == false)
2129 bool upper_is_air = false;
2132 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
2133 upper_is_air = true;
2135 catch(InvalidPositionException &e)
2138 // Turn mud into grass
2139 if(upper_is_air && n.d == CONTENT_MUD
2140 && current_light == LIGHT_SUN)
2142 n.d = CONTENT_GRASS;
2146 // A solid object is on the way.
2147 stopped_to_solid_object = true;
2155 current_light = diminish_light(current_light);
2158 u8 old_light = n.getLight(LIGHTBANK_DAY);
2160 if(current_light > old_light || remove_light)
2162 n.setLight(LIGHTBANK_DAY, current_light);
2165 if(diminish_light(current_light) != 0)
2167 light_sources.insert(pos_relative + pos, true);
2170 if(current_light == 0 && stopped_to_solid_object)
2174 *black_air_left = true;
2179 // Whether or not the block below should see LIGHT_SUN
2180 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
2183 If the block below hasn't already been marked invalid:
2185 Check if the node below the block has proper sunlight at top.
2186 If not, the block below is invalid.
2188 Ignore non-transparent nodes as they always have no light
2192 if(block_below_is_valid)
2194 MapNode n = getNodeParent(v3s16(x, -1, z));
2195 if(n.light_propagates())
2197 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
2198 && sunlight_should_go_down == false)
2199 block_below_is_valid = false;
2200 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
2201 && sunlight_should_go_down == true)
2202 block_below_is_valid = false;
2206 catch(InvalidPositionException &e)
2208 /*std::cout<<"InvalidBlockException for bottom block node"
2210 // Just no block below, no need to panic.
2215 return block_below_is_valid;
2219 void MapBlock::copyTo(VoxelManipulator &dst)
2221 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
2222 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
2224 // Copy from data to VoxelManipulator
2225 dst.copyFrom(data, data_area, v3s16(0,0,0),
2226 getPosRelative(), data_size);
2229 void MapBlock::copyFrom(VoxelManipulator &dst)
2231 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
2232 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
2234 // Copy from VoxelManipulator to data
2235 dst.copyTo(data, data_area, v3s16(0,0,0),
2236 getPosRelative(), data_size);
2239 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
2244 m_objects.step(dtime, server, daynight_ratio);
2250 void MapBlock::updateDayNightDiff()
2254 m_day_night_differs = false;
2258 bool differs = false;
2261 Check if any lighting value differs
2263 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2265 MapNode &n = data[i];
2266 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
2274 If some lighting values differ, check if the whole thing is
2275 just air. If it is, differ = false
2279 bool only_air = true;
2280 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2282 MapNode &n = data[i];
2283 if(n.d != CONTENT_AIR)
2293 // Set member variable
2294 m_day_night_differs = differs;
2297 s16 MapBlock::getGroundLevel(v2s16 p2d)
2303 s16 y = MAP_BLOCKSIZE-1;
2306 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
2307 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
2309 if(y == MAP_BLOCKSIZE-1)
2317 catch(InvalidPositionException &e)
2327 void MapBlock::serialize(std::ostream &os, u8 version)
2329 if(!ser_ver_supported(version))
2330 throw VersionMismatchException("ERROR: MapBlock format not supported");
2334 throw SerializationError("ERROR: Not writing dummy block.");
2337 // These have no compression
2338 if(version <= 3 || version == 5 || version == 6)
2340 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2342 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
2343 SharedBuffer<u8> dest(buflen);
2345 dest[0] = is_underground;
2346 for(u32 i=0; i<nodecount; i++)
2348 u32 s = 1 + i * MapNode::serializedLength(version);
2349 data[i].serialize(&dest[s], version);
2352 os.write((char*)*dest, dest.getSize());
2354 else if(version <= 10)
2358 Compress the materials and the params separately.
2362 os.write((char*)&is_underground, 1);
2364 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2366 // Get and compress materials
2367 SharedBuffer<u8> materialdata(nodecount);
2368 for(u32 i=0; i<nodecount; i++)
2370 materialdata[i] = data[i].d;
2372 compress(materialdata, os, version);
2374 // Get and compress lights
2375 SharedBuffer<u8> lightdata(nodecount);
2376 for(u32 i=0; i<nodecount; i++)
2378 lightdata[i] = data[i].param;
2380 compress(lightdata, os, version);
2384 // Get and compress param2
2385 SharedBuffer<u8> param2data(nodecount);
2386 for(u32 i=0; i<nodecount; i++)
2388 param2data[i] = data[i].param2;
2390 compress(param2data, os, version);
2393 // All other versions (newest)
2400 if(m_day_night_differs)
2402 if(m_lighting_expired)
2404 os.write((char*)&flags, 1);
2406 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2412 SharedBuffer<u8> databuf(nodecount*3);
2415 for(u32 i=0; i<nodecount; i++)
2417 databuf[i] = data[i].d;
2421 for(u32 i=0; i<nodecount; i++)
2423 databuf[i+nodecount] = data[i].param;
2427 for(u32 i=0; i<nodecount; i++)
2429 databuf[i+nodecount*2] = data[i].param2;
2433 Compress data to output stream
2436 compress(databuf, os, version);
2446 std::ostringstream oss(std::ios_base::binary);
2447 m_node_metadata.serialize(oss);
2448 os<<serializeString(oss.str());
2450 // This will happen if the string is longer than 65535
2451 catch(SerializationError &e)
2453 // Use an empty string
2454 os<<serializeString("");
2459 std::ostringstream oss(std::ios_base::binary);
2460 m_node_metadata.serialize(oss);
2461 compressZlib(oss.str(), os);
2462 //os<<serializeLongString(oss.str());
2468 void MapBlock::deSerialize(std::istream &is, u8 version)
2470 if(!ser_ver_supported(version))
2471 throw VersionMismatchException("ERROR: MapBlock format not supported");
2473 // These have no lighting info
2476 setLightingExpired(true);
2479 // These have no compression
2480 if(version <= 3 || version == 5 || version == 6)
2482 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2485 if(is.gcount() != 1)
2486 throw SerializationError
2487 ("MapBlock::deSerialize: no enough input data");
2488 is_underground = tmp;
2489 for(u32 i=0; i<nodecount; i++)
2491 s32 len = MapNode::serializedLength(version);
2492 SharedBuffer<u8> d(len);
2493 is.read((char*)*d, len);
2494 if(is.gcount() != len)
2495 throw SerializationError
2496 ("MapBlock::deSerialize: no enough input data");
2497 data[i].deSerialize(*d, version);
2500 else if(version <= 10)
2502 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2505 is.read((char*)&t8, 1);
2506 is_underground = t8;
2509 // Uncompress and set material data
2510 std::ostringstream os(std::ios_base::binary);
2511 decompress(is, os, version);
2512 std::string s = os.str();
2513 if(s.size() != nodecount)
2514 throw SerializationError
2515 ("MapBlock::deSerialize: invalid format");
2516 for(u32 i=0; i<s.size(); i++)
2522 // Uncompress and set param data
2523 std::ostringstream os(std::ios_base::binary);
2524 decompress(is, os, version);
2525 std::string s = os.str();
2526 if(s.size() != nodecount)
2527 throw SerializationError
2528 ("MapBlock::deSerialize: invalid format");
2529 for(u32 i=0; i<s.size(); i++)
2531 data[i].param = s[i];
2537 // Uncompress and set param2 data
2538 std::ostringstream os(std::ios_base::binary);
2539 decompress(is, os, version);
2540 std::string s = os.str();
2541 if(s.size() != nodecount)
2542 throw SerializationError
2543 ("MapBlock::deSerialize: invalid format");
2544 for(u32 i=0; i<s.size(); i++)
2546 data[i].param2 = s[i];
2550 // All other versions (newest)
2553 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2556 is.read((char*)&flags, 1);
2557 is_underground = (flags & 0x01) ? true : false;
2558 m_day_night_differs = (flags & 0x02) ? true : false;
2559 m_lighting_expired = (flags & 0x04) ? true : false;
2562 std::ostringstream os(std::ios_base::binary);
2563 decompress(is, os, version);
2564 std::string s = os.str();
2565 if(s.size() != nodecount*3)
2566 throw SerializationError
2567 ("MapBlock::deSerialize: invalid format");
2570 for(u32 i=0; i<nodecount; i++)
2575 for(u32 i=0; i<nodecount; i++)
2577 data[i].param = s[i+nodecount];
2580 for(u32 i=0; i<nodecount; i++)
2582 data[i].param2 = s[i+nodecount*2];
2594 std::string data = deSerializeString(is);
2595 std::istringstream iss(data, std::ios_base::binary);
2596 m_node_metadata.deSerialize(iss);
2600 //std::string data = deSerializeLongString(is);
2601 std::ostringstream oss(std::ios_base::binary);
2602 decompressZlib(is, oss);
2603 std::istringstream iss(oss.str(), std::ios_base::binary);
2604 m_node_metadata.deSerialize(iss);
2607 catch(SerializationError &e)
2609 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
2610 <<" while deserializing node metadata"<<std::endl;
2616 Translate nodes as specified in the translate_to fields of
2619 NOTE: This isn't really used. Should it be removed?
2621 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2623 MapNode &n = data[i];
2625 MapNode *translate_to = content_features(n.d).translate_to;
2628 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2629 <<translate_to->d<<std::endl;
2635 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
2637 // Versions up from 9 have block objects.
2640 serializeObjects(os, version);
2643 // Versions up from 15 have static objects.
2646 m_static_objects.serialize(os);
2652 writeU32(os, getTimestamp());
2656 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
2659 Versions up from 9 have block objects.
2663 updateObjects(is, version, NULL, 0);
2667 Versions up from 15 have static objects.
2671 m_static_objects.deSerialize(is);
2677 setTimestamp(readU32(is));
2681 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);