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)
136 inline video::SColor lightColor(u8 alpha, u8 light)
138 return video::SColor(alpha,light,light,light);
141 void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
142 v3s16 dir, v3f scale, v3f posRelative_f,
143 core::array<FastFace> &dest)
147 // Position is at the center of the cube.
152 // If looking towards z+, this is the face that is behind
153 // the center point, facing towards z+.
154 vertex_pos[0] = v3f(-BS/2,-BS/2,BS/2);
155 vertex_pos[1] = v3f( BS/2,-BS/2,BS/2);
156 vertex_pos[2] = v3f( BS/2, BS/2,BS/2);
157 vertex_pos[3] = v3f(-BS/2, BS/2,BS/2);
159 if(dir == v3s16(0,0,1))
161 for(u16 i=0; i<4; i++)
162 vertex_pos[i].rotateXZBy(0);
164 else if(dir == v3s16(0,0,-1))
166 for(u16 i=0; i<4; i++)
167 vertex_pos[i].rotateXZBy(180);
169 else if(dir == v3s16(1,0,0))
171 for(u16 i=0; i<4; i++)
172 vertex_pos[i].rotateXZBy(-90);
174 else if(dir == v3s16(-1,0,0))
176 for(u16 i=0; i<4; i++)
177 vertex_pos[i].rotateXZBy(90);
179 else if(dir == v3s16(0,1,0))
181 for(u16 i=0; i<4; i++)
182 vertex_pos[i].rotateYZBy(-90);
184 else if(dir == v3s16(0,-1,0))
186 for(u16 i=0; i<4; i++)
187 vertex_pos[i].rotateYZBy(90);
190 for(u16 i=0; i<4; i++)
192 vertex_pos[i].X *= scale.X;
193 vertex_pos[i].Y *= scale.Y;
194 vertex_pos[i].Z *= scale.Z;
195 vertex_pos[i] += pos + posRelative_f;
199 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
200 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
201 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
203 v3f zerovector = v3f(0,0,0);
205 u8 alpha = tile.alpha;
207 if(tile.id == TILE_WATER)
208 alpha = WATER_ALPHA;*/
210 float x0 = tile.texture.pos.X;
211 float y0 = tile.texture.pos.Y;
212 float w = tile.texture.size.X;
213 float h = tile.texture.size.Y;
215 /*video::SColor c = lightColor(alpha, li);
217 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
218 core::vector2d<f32>(x0+w*abs_scale, y0+h));
219 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
220 core::vector2d<f32>(x0, y0+h));
221 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
222 core::vector2d<f32>(x0, y0));
223 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
224 core::vector2d<f32>(x0+w*abs_scale, y0));*/
226 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0),
227 lightColor(alpha, li0),
228 core::vector2d<f32>(x0+w*abs_scale, y0+h));
229 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0),
230 lightColor(alpha, li1),
231 core::vector2d<f32>(x0, y0+h));
232 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0),
233 lightColor(alpha, li2),
234 core::vector2d<f32>(x0, y0));
235 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0),
236 lightColor(alpha, li3),
237 core::vector2d<f32>(x0+w*abs_scale, y0));
241 //f->tile = TILE_STONE;
243 dest.push_back(face);
247 Gets node tile from any place relative to block.
248 Returns TILE_NODE if doesn't exist or should not be drawn.
250 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
251 NodeModMap &temp_mods)
254 spec = mn.getTile(face_dir);
257 Check temporary modifications on this node
259 /*core::map<v3s16, NodeMod>::Node *n;
260 n = m_temp_mods.find(p);
264 struct NodeMod mod = n->getValue();*/
266 if(temp_mods.get(p, &mod))
268 if(mod.type == NODEMOD_CHANGECONTENT)
270 MapNode mn2(mod.param);
271 spec = mn2.getTile(face_dir);
273 if(mod.type == NODEMOD_CRACK)
276 Get texture id, translate it to name, append stuff to
280 // Get original texture name
281 u32 orig_id = spec.texture.id;
282 std::string orig_name = g_texturesource->getTextureName(orig_id);
284 // Create new texture name
285 std::ostringstream os;
286 os<<orig_name<<"^[crack"<<mod.param;
289 u32 new_id = g_texturesource->getTextureId(os.str());
291 /*dstream<<"MapBlock::getNodeTile(): Switching from "
292 <<orig_name<<" to "<<os.str()<<" ("
293 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
295 spec.texture = g_texturesource->getTexture(new_id);
302 u8 getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
305 Check temporary modifications on this node
307 /*core::map<v3s16, NodeMod>::Node *n;
308 n = m_temp_mods.find(p);
312 struct NodeMod mod = n->getValue();*/
314 if(temp_mods.get(p, &mod))
316 if(mod.type == NODEMOD_CHANGECONTENT)
321 if(mod.type == NODEMOD_CRACK)
324 Content doesn't change.
326 face_contents works just like it should, because
327 there should not be faces between differently cracked
330 If a semi-transparent node is cracked in front an
331 another one, it really doesn't matter whether there
332 is a cracked face drawn in between or not.
351 // Calculate lighting at the XYZ- corner of p
352 u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio)
354 u16 ambient_occlusion = 0;
357 for(u32 i=0; i<8; i++)
359 MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
360 if(content_features(n.d).param_type == CPT_LIGHT)
362 light += decode_light(n.getLightBlend(daynight_ratio));
367 if(n.d != CONTENT_IGNORE)
375 light /= light_count;
377 if(ambient_occlusion > 4)
379 ambient_occlusion -= 4;
380 light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
388 translate_dir: unit vector with only one of x, y or z
389 face_dir: unit vector with only one of x, y or z
391 void updateFastFaceRow(
400 core::array<FastFace> &dest,
401 NodeModMap &temp_mods,
402 VoxelManipulator &vmanip,
403 v3s16 blockpos_nodes)
407 u16 continuous_tiles_count = 0;
409 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
410 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
411 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
412 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
413 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
415 for(u16 j=0; j<length; j++)
417 bool next_is_different = true;
426 // If at last position, there is nothing to compare to and
427 // the face must be drawn anyway
430 p_next = p + translate_dir;
432 n0_next = vmanip.getNodeNoEx(blockpos_nodes + p_next);
433 n1_next = vmanip.getNodeNoEx(blockpos_nodes + p_next + face_dir);
434 tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
435 tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
436 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
438 if(tile0_next == tile0
439 && tile1_next == tile1
440 && light_next == light)
442 next_is_different = false;
446 continuous_tiles_count++;
448 // This is set to true if the texture doesn't allow more tiling
449 bool end_of_texture = false;
451 If there is no texture, it can be tiled infinitely.
452 If tiled==0, it means the texture can be tiled infinitely.
453 Otherwise check tiled agains continuous_tiles_count.
455 This check has to be made for both tiles, because this is
456 a bit hackish and we know which one we're using only when
457 the decision to make the faces is made.
459 if(tile0.texture.atlas != NULL && tile0.texture.tiled != 0)
461 if(tile0.texture.tiled <= continuous_tiles_count)
462 end_of_texture = true;
464 if(tile1.texture.atlas != NULL && tile1.texture.tiled != 0)
466 if(tile1.texture.tiled <= continuous_tiles_count)
467 end_of_texture = true;
470 end_of_texture = true; //DEBUG
472 if(next_is_different || end_of_texture)
475 Create a face if there should be one
477 //u8 mf = face_contents(tile0, tile1);
479 u8 content0 = getNodeContent(p, n0, temp_mods);
480 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
481 u8 mf = face_contents(content0, content1);
485 // Floating point conversion of the position vector
486 v3f pf(p.X, p.Y, p.Z);
487 // Center point of face (kind of)
488 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
490 u8 li0=255, li1=255, li2=255, li3=255;
493 v3s16 p_first = p - (continuous_tiles_count-1) * translate_dir;
495 v3s16 p_map = blockpos_nodes + p;
496 v3s16 p_map_first = p_first + blockpos_nodes;
498 if(translate_dir.X != 0)
500 scale.X = continuous_tiles_count;
502 if(translate_dir.Y != 0)
504 scale.Y = continuous_tiles_count;
506 if(translate_dir.Z != 0)
508 scale.Z = continuous_tiles_count;
511 //u8 li = decode_light(light);
513 // If node at sp (tile0) is more solid
516 if(face_dir == v3s16(0,0,1))
518 // Going along X+, faces in Z+
519 li0 = getSmoothLight(p_map_first+v3s16(0,0,1),
520 vmanip, daynight_ratio);
521 li1 = getSmoothLight(p_map+v3s16(1,0,1),
522 vmanip, daynight_ratio);
523 li2 = getSmoothLight(p_map+v3s16(1,1,1),
524 vmanip, daynight_ratio);
525 li3 = getSmoothLight(p_map_first+v3s16(0,1,1),
526 vmanip, daynight_ratio);
528 else if(face_dir == v3s16(0,1,0))
530 // Going along X+, faces in Y+
531 li0 = getSmoothLight(p_map_first+v3s16(0,1,1),
532 vmanip, daynight_ratio);
533 li1 = getSmoothLight(p_map+v3s16(1,1,1),
534 vmanip, daynight_ratio);
535 li2 = getSmoothLight(p_map+v3s16(1,1,0),
536 vmanip, daynight_ratio);
537 li3 = getSmoothLight(p_map_first+v3s16(0,1,0),
538 vmanip, daynight_ratio);
540 else if(face_dir == v3s16(1,0,0))
542 // Going along Z+, faces in X+
543 li0 = getSmoothLight(p_map_first+v3s16(1,0,1),
544 vmanip, daynight_ratio);
545 li1 = getSmoothLight(p_map+v3s16(1,0,0),
546 vmanip, daynight_ratio);
547 li2 = getSmoothLight(p_map+v3s16(1,1,0),
548 vmanip, daynight_ratio);
549 li3 = getSmoothLight(p_map_first+v3s16(1,1,1),
550 vmanip, daynight_ratio);
554 //makeFastFace(tile0, li, li, li, li,
555 makeFastFace(tile0, li0, li1, li2, li3,
557 posRelative_f, dest);
559 // If node at sp is less solid (mf == 2)
562 // Offset to the actual solid block
564 p_map_first += face_dir;
566 if(face_dir == v3s16(0,0,1))
568 // Going along X+, faces in Z-
569 li0 = getSmoothLight(p_map+v3s16(1,0,0),
570 vmanip, daynight_ratio);
571 li1 = getSmoothLight(p_map_first+v3s16(0,0,0),
572 vmanip, daynight_ratio);
573 li2 = getSmoothLight(p_map_first+v3s16(0,1,0),
574 vmanip, daynight_ratio);
575 li3 = getSmoothLight(p_map+v3s16(1,1,0),
576 vmanip, daynight_ratio);
578 else if(face_dir == v3s16(0,1,0))
580 // Going along X+, faces in Y-
581 li0 = getSmoothLight(p_map_first+v3s16(0,0,0),
582 vmanip, daynight_ratio);
583 li1 = getSmoothLight(p_map+v3s16(1,0,0),
584 vmanip, daynight_ratio);
585 li2 = getSmoothLight(p_map+v3s16(1,0,1),
586 vmanip, daynight_ratio);
587 li3 = getSmoothLight(p_map_first+v3s16(0,0,1),
588 vmanip, daynight_ratio);
590 else if(face_dir == v3s16(1,0,0))
592 // Going along Z+, faces in X-
593 li0 = getSmoothLight(p_map_first+v3s16(0,0,0),
594 vmanip, daynight_ratio);
595 li1 = getSmoothLight(p_map+v3s16(0,0,1),
596 vmanip, daynight_ratio);
597 li2 = getSmoothLight(p_map+v3s16(0,1,1),
598 vmanip, daynight_ratio);
599 li3 = getSmoothLight(p_map_first+v3s16(0,1,0),
600 vmanip, daynight_ratio);
604 //makeFastFace(tile1, li, li, li, li,
605 makeFastFace(tile1, li0, li1, li2, li3,
606 sp+face_dir_f, -face_dir, scale,
607 posRelative_f, dest);
611 continuous_tiles_count = 0;
624 This is used because CMeshBuffer::append() is very slow
628 video::SMaterial material;
629 core::array<u16> indices;
630 core::array<video::S3DVertex> vertices;
637 video::SMaterial material,
638 const video::S3DVertex* const vertices,
640 const u16* const indices,
644 PreMeshBuffer *p = NULL;
645 for(u32 i=0; i<m_prebuffers.size(); i++)
647 PreMeshBuffer &pp = m_prebuffers[i];
648 if(pp.material != material)
658 pp.material = material;
659 m_prebuffers.push_back(pp);
660 p = &m_prebuffers[m_prebuffers.size()-1];
663 u32 vertex_count = p->vertices.size();
664 for(u32 i=0; i<numIndices; i++)
666 u32 j = indices[i] + vertex_count;
669 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
670 // NOTE: Fix is to just add an another MeshBuffer
672 p->indices.push_back(j);
674 for(u32 i=0; i<numVertices; i++)
676 p->vertices.push_back(vertices[i]);
680 void fillMesh(scene::SMesh *mesh)
682 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
683 <<" meshbuffers"<<std::endl;*/
684 for(u32 i=0; i<m_prebuffers.size(); i++)
686 PreMeshBuffer &p = m_prebuffers[i];
688 /*dstream<<"p.vertices.size()="<<p.vertices.size()
689 <<", p.indices.size()="<<p.indices.size()
694 // This is a "Standard MeshBuffer",
695 // it's a typedeffed CMeshBuffer<video::S3DVertex>
696 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
698 buf->Material = p.material;
699 //((scene::SMeshBuffer*)buf)->Material = p.material;
701 //buf->setHardwareMappingHint(scene::EHM_STATIC);
703 mesh->addMeshBuffer(buf);
707 buf->append(p.vertices.pointer(), p.vertices.size(),
708 p.indices.pointer(), p.indices.size());
713 core::array<PreMeshBuffer> m_prebuffers;
716 scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
718 // 4-21ms for MAP_BLOCKSIZE=16
719 // 24-155ms for MAP_BLOCKSIZE=32
720 //TimeTaker timer1("makeMapBlockMesh()");
722 core::array<FastFace> fastfaces_new;
724 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
726 // floating point conversion
727 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
732 bool new_style_water = g_settings.getBool("new_style_water");
733 bool new_style_leaves = g_settings.getBool("new_style_leaves");
735 float node_water_level = 1.0;
737 node_water_level = 0.85;
740 We are including the faces of the trailing edges of the block.
741 This means that when something changes, the caller must
742 also update the meshes of the blocks at the leading edges.
744 NOTE: This is the slowest part of this method.
748 // 4-23ms for MAP_BLOCKSIZE=16
749 //TimeTaker timer2("updateMesh() collect");
752 Go through every y,z and get top(y+) faces in rows of x+
754 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
755 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
756 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
757 v3s16(0,y,z), MAP_BLOCKSIZE,
760 v3s16(0,1,0), //face dir
769 Go through every x,y and get right(x+) faces in rows of z+
771 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
772 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
773 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
774 v3s16(x,y,0), MAP_BLOCKSIZE,
786 Go through every y,z and get back(z+) faces in rows of x+
788 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
789 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
790 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
791 v3s16(0,y,z), MAP_BLOCKSIZE,
807 Convert FastFaces to SMesh
810 MeshCollector collector;
812 if(fastfaces_new.size() > 0)
814 // avg 0ms (100ms spikes when loading textures the first time)
815 //TimeTaker timer2("updateMesh() mesh building");
817 video::SMaterial material;
818 material.setFlag(video::EMF_LIGHTING, false);
819 material.setFlag(video::EMF_BILINEAR_FILTER, false);
820 material.setFlag(video::EMF_FOG_ENABLE, true);
821 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
822 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
824 for(u32 i=0; i<fastfaces_new.size(); i++)
826 FastFace &f = fastfaces_new[i];
828 const u16 indices[] = {0,1,2,2,3,0};
830 video::ITexture *texture = f.tile.texture.atlas;
834 material.setTexture(0, texture);
836 f.tile.applyMaterialOptions(material);
838 collector.append(material, f.vertices, 4, indices, 6);
843 Add special graphics:
849 //TimeTaker timer2("updateMesh() adding special stuff");
851 // Flowing water material
852 video::SMaterial material_water1;
853 material_water1.setFlag(video::EMF_LIGHTING, false);
854 material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
855 material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
856 material_water1.setFlag(video::EMF_FOG_ENABLE, true);
857 material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
858 AtlasPointer pa_water1 = g_texturesource->getTexture(
859 g_texturesource->getTextureId("water.png"));
860 material_water1.setTexture(0, pa_water1.atlas);
862 // New-style leaves material
863 video::SMaterial material_leaves1;
864 material_leaves1.setFlag(video::EMF_LIGHTING, false);
865 //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
866 material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
867 material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
868 material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
869 AtlasPointer pa_leaves1 = g_texturesource->getTexture(
870 g_texturesource->getTextureId("leaves.png"));
871 material_leaves1.setTexture(0, pa_leaves1.atlas);
873 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
874 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
875 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
879 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
884 if(n.d == CONTENT_TORCH)
886 video::SColor c(255,255,255,255);
888 // Wall at X+ of node
889 video::S3DVertex vertices[4] =
891 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
892 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
893 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
894 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
897 v3s16 dir = unpackDir(n.dir);
899 for(s32 i=0; i<4; i++)
901 if(dir == v3s16(1,0,0))
902 vertices[i].Pos.rotateXZBy(0);
903 if(dir == v3s16(-1,0,0))
904 vertices[i].Pos.rotateXZBy(180);
905 if(dir == v3s16(0,0,1))
906 vertices[i].Pos.rotateXZBy(90);
907 if(dir == v3s16(0,0,-1))
908 vertices[i].Pos.rotateXZBy(-90);
909 if(dir == v3s16(0,-1,0))
910 vertices[i].Pos.rotateXZBy(45);
911 if(dir == v3s16(0,1,0))
912 vertices[i].Pos.rotateXZBy(-45);
914 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
918 video::SMaterial material;
919 material.setFlag(video::EMF_LIGHTING, false);
920 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
921 material.setFlag(video::EMF_BILINEAR_FILTER, false);
922 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
923 material.MaterialType
924 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
926 if(dir == v3s16(0,-1,0))
927 material.setTexture(0,
928 g_texturesource->getTextureRaw("torch_on_floor.png"));
929 else if(dir == v3s16(0,1,0))
930 material.setTexture(0,
931 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
932 // For backwards compatibility
933 else if(dir == v3s16(0,0,0))
934 material.setTexture(0,
935 g_texturesource->getTextureRaw("torch_on_floor.png"));
937 material.setTexture(0,
938 g_texturesource->getTextureRaw("torch.png"));
940 u16 indices[] = {0,1,2,2,3,0};
941 // Add to mesh collector
942 collector.append(material, vertices, 4, indices, 6);
947 if(n.d == CONTENT_SIGN_WALL)
949 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
950 video::SColor c(255,l,l,l);
952 float d = (float)BS/16;
953 // Wall at X+ of node
954 video::S3DVertex vertices[4] =
956 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
957 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
958 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
959 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
962 v3s16 dir = unpackDir(n.dir);
964 for(s32 i=0; i<4; i++)
966 if(dir == v3s16(1,0,0))
967 vertices[i].Pos.rotateXZBy(0);
968 if(dir == v3s16(-1,0,0))
969 vertices[i].Pos.rotateXZBy(180);
970 if(dir == v3s16(0,0,1))
971 vertices[i].Pos.rotateXZBy(90);
972 if(dir == v3s16(0,0,-1))
973 vertices[i].Pos.rotateXZBy(-90);
974 if(dir == v3s16(0,-1,0))
975 vertices[i].Pos.rotateXYBy(-90);
976 if(dir == v3s16(0,1,0))
977 vertices[i].Pos.rotateXYBy(90);
979 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
983 video::SMaterial material;
984 material.setFlag(video::EMF_LIGHTING, false);
985 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
986 material.setFlag(video::EMF_BILINEAR_FILTER, false);
987 material.setFlag(video::EMF_FOG_ENABLE, true);
988 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
989 material.MaterialType
990 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
992 material.setTexture(0,
993 g_texturesource->getTextureRaw("sign_wall.png"));
995 u16 indices[] = {0,1,2,2,3,0};
996 // Add to mesh collector
997 collector.append(material, vertices, 4, indices, 6);
1000 Add flowing water to mesh
1002 else if(n.d == CONTENT_WATER)
1004 bool top_is_water = false;
1005 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1006 if(ntop.d == CONTENT_WATER || ntop.d == CONTENT_WATERSOURCE)
1007 top_is_water = true;
1010 // Use the light of the node on top if possible
1011 if(content_features(ntop.d).param_type == CPT_LIGHT)
1012 l = decode_light(ntop.getLightBlend(data->m_daynight_ratio));
1013 // Otherwise use the light of this node (the water)
1015 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1016 video::SColor c(WATER_ALPHA,l,l,l);
1018 // Neighbor water levels (key = relative position)
1019 // Includes current node
1020 core::map<v3s16, f32> neighbor_levels;
1021 core::map<v3s16, u8> neighbor_contents;
1022 core::map<v3s16, u8> neighbor_flags;
1023 const u8 neighborflag_top_is_water = 0x01;
1024 v3s16 neighbor_dirs[9] = {
1035 for(u32 i=0; i<9; i++)
1037 u8 content = CONTENT_AIR;
1038 float level = -0.5 * BS;
1041 v3s16 p2 = p + neighbor_dirs[i];
1042 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1043 if(n2.d != CONTENT_IGNORE)
1047 if(n2.d == CONTENT_WATERSOURCE)
1048 level = (-0.5+node_water_level) * BS;
1049 else if(n2.d == CONTENT_WATER)
1050 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
1051 * node_water_level) * BS;
1053 // Check node above neighbor.
1054 // NOTE: This doesn't get executed if neighbor
1057 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1058 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
1059 flags |= neighborflag_top_is_water;
1062 neighbor_levels.insert(neighbor_dirs[i], level);
1063 neighbor_contents.insert(neighbor_dirs[i], content);
1064 neighbor_flags.insert(neighbor_dirs[i], flags);
1067 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
1068 //float water_level = neighbor_levels[v3s16(0,0,0)];
1070 // Corner heights (average between four waters)
1071 f32 corner_levels[4];
1073 v3s16 halfdirs[4] = {
1079 for(u32 i=0; i<4; i++)
1081 v3s16 cornerdir = halfdirs[i];
1082 float cornerlevel = 0;
1083 u32 valid_count = 0;
1084 for(u32 j=0; j<4; j++)
1086 v3s16 neighbordir = cornerdir - halfdirs[j];
1087 u8 content = neighbor_contents[neighbordir];
1088 // Special case for source nodes
1089 if(content == CONTENT_WATERSOURCE)
1091 cornerlevel = (-0.5+node_water_level)*BS;
1095 else if(content == CONTENT_WATER)
1097 cornerlevel += neighbor_levels[neighbordir];
1100 else if(content == CONTENT_AIR)
1102 cornerlevel += -0.5*BS;
1107 cornerlevel /= valid_count;
1108 corner_levels[i] = cornerlevel;
1115 v3s16 side_dirs[4] = {
1121 s16 side_corners[4][2] = {
1127 for(u32 i=0; i<4; i++)
1129 v3s16 dir = side_dirs[i];
1132 If our topside is water and neighbor's topside
1133 is water, don't draw side face
1136 neighbor_flags[dir] & neighborflag_top_is_water)
1139 u8 neighbor_content = neighbor_contents[dir];
1141 // Don't draw face if neighbor is not air or water
1142 if(neighbor_content != CONTENT_AIR
1143 && neighbor_content != CONTENT_WATER)
1146 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1148 // Don't draw any faces if neighbor is water and top is water
1149 if(neighbor_is_water == true && top_is_water == false)
1152 video::S3DVertex vertices[4] =
1154 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1155 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1156 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1157 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1158 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1159 pa_water1.x0(), pa_water1.y1()),
1160 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1161 pa_water1.x1(), pa_water1.y1()),
1162 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1163 pa_water1.x1(), pa_water1.y0()),
1164 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1165 pa_water1.x0(), pa_water1.y0()),
1169 If our topside is water, set upper border of face
1170 at upper border of node
1174 vertices[2].Pos.Y = 0.5*BS;
1175 vertices[3].Pos.Y = 0.5*BS;
1178 Otherwise upper position of face is corner levels
1182 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1183 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1187 If neighbor is water, lower border of face is corner
1190 if(neighbor_is_water)
1192 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1193 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1196 If neighbor is not water, lower border of face is
1197 lower border of node
1201 vertices[0].Pos.Y = -0.5*BS;
1202 vertices[1].Pos.Y = -0.5*BS;
1205 for(s32 j=0; j<4; j++)
1207 if(dir == v3s16(0,0,1))
1208 vertices[j].Pos.rotateXZBy(0);
1209 if(dir == v3s16(0,0,-1))
1210 vertices[j].Pos.rotateXZBy(180);
1211 if(dir == v3s16(-1,0,0))
1212 vertices[j].Pos.rotateXZBy(90);
1213 if(dir == v3s16(1,0,-0))
1214 vertices[j].Pos.rotateXZBy(-90);
1216 vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
1219 u16 indices[] = {0,1,2,2,3,0};
1220 // Add to mesh collector
1221 collector.append(material_water1, vertices, 4, indices, 6);
1225 Generate top side, if appropriate
1228 if(top_is_water == false)
1230 video::S3DVertex vertices[4] =
1232 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1233 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1234 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1235 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1236 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1237 pa_water1.x0(), pa_water1.y1()),
1238 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1239 pa_water1.x1(), pa_water1.y1()),
1240 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1241 pa_water1.x1(), pa_water1.y0()),
1242 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1243 pa_water1.x0(), pa_water1.y0()),
1246 // This fixes a strange bug
1247 s32 corner_resolve[4] = {3,2,1,0};
1249 for(s32 i=0; i<4; i++)
1251 //vertices[i].Pos.Y += water_level;
1252 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1253 s32 j = corner_resolve[i];
1254 vertices[i].Pos.Y += corner_levels[j];
1255 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1258 u16 indices[] = {0,1,2,2,3,0};
1259 // Add to mesh collector
1260 collector.append(material_water1, vertices, 4, indices, 6);
1264 Add water sources to mesh if using new style
1266 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1268 //bool top_is_water = false;
1269 bool top_is_air = false;
1270 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1271 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1272 top_is_water = true;*/
1273 if(n.d == CONTENT_AIR)
1276 /*if(top_is_water == true)
1278 if(top_is_air == false)
1281 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1282 video::SColor c(WATER_ALPHA,l,l,l);
1284 video::S3DVertex vertices[4] =
1286 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1287 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1288 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1289 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1290 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1291 pa_water1.x0(), pa_water1.y1()),
1292 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1293 pa_water1.x1(), pa_water1.y1()),
1294 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1295 pa_water1.x1(), pa_water1.y0()),
1296 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1297 pa_water1.x0(), pa_water1.y0()),
1300 for(s32 i=0; i<4; i++)
1302 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1303 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1306 u16 indices[] = {0,1,2,2,3,0};
1307 // Add to mesh collector
1308 collector.append(material_water1, vertices, 4, indices, 6);
1311 Add leaves if using new style
1313 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1315 /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
1316 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1317 video::SColor c(255,l,l,l);
1319 for(u32 j=0; j<6; j++)
1321 video::S3DVertex vertices[4] =
1323 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1324 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1325 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1326 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1327 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1328 pa_leaves1.x0(), pa_leaves1.y1()),
1329 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1330 pa_leaves1.x1(), pa_leaves1.y1()),
1331 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1332 pa_leaves1.x1(), pa_leaves1.y0()),
1333 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1334 pa_leaves1.x0(), pa_leaves1.y0()),
1339 for(u16 i=0; i<4; i++)
1340 vertices[i].Pos.rotateXZBy(0);
1344 for(u16 i=0; i<4; i++)
1345 vertices[i].Pos.rotateXZBy(180);
1349 for(u16 i=0; i<4; i++)
1350 vertices[i].Pos.rotateXZBy(-90);
1354 for(u16 i=0; i<4; i++)
1355 vertices[i].Pos.rotateXZBy(90);
1359 for(u16 i=0; i<4; i++)
1360 vertices[i].Pos.rotateYZBy(-90);
1364 for(u16 i=0; i<4; i++)
1365 vertices[i].Pos.rotateYZBy(90);
1368 for(u16 i=0; i<4; i++)
1370 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1373 u16 indices[] = {0,1,2,2,3,0};
1374 // Add to mesh collector
1375 collector.append(material_leaves1, vertices, 4, indices, 6);
1381 Add stuff from collector to mesh
1384 scene::SMesh *mesh_new = NULL;
1385 mesh_new = new scene::SMesh();
1387 collector.fillMesh(mesh_new);
1390 Do some stuff to the mesh
1393 mesh_new->recalculateBoundingBox();
1396 Delete new mesh if it is empty
1399 if(mesh_new->getMeshBufferCount() == 0)
1408 // Usually 1-700 faces and 1-7 materials
1409 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1410 <<"and uses "<<mesh_new->getMeshBufferCount()
1411 <<" materials (meshbuffers)"<<std::endl;
1414 // Use VBO for mesh (this just would set this for ever buffer)
1415 // This will lead to infinite memory usage because or irrlicht.
1416 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1419 NOTE: If that is enabled, some kind of a queue to the main
1420 thread should be made which would call irrlicht to delete
1421 the hardware buffer and then delete the mesh
1427 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1436 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
1440 is_underground(false),
1441 m_lighting_expired(true),
1442 m_day_night_differs(false),
1443 //m_not_fully_generated(false),
1450 //m_spawn_timer = -10000;
1453 m_mesh_expired = false;
1456 m_temp_mods_mutex.Init();
1460 MapBlock::~MapBlock()
1464 JMutexAutoLock lock(mesh_mutex);
1478 bool MapBlock::isValidPositionParent(v3s16 p)
1480 if(isValidPosition(p))
1485 return m_parent->isValidPosition(getPosRelative() + p);
1489 MapNode MapBlock::getNodeParent(v3s16 p)
1491 if(isValidPosition(p) == false)
1493 return m_parent->getNode(getPosRelative() + p);
1498 throw InvalidPositionException();
1499 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1503 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
1505 if(isValidPosition(p) == false)
1507 m_parent->setNode(getPosRelative() + p, n);
1512 throw InvalidPositionException();
1513 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
1517 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
1519 if(isValidPosition(p) == false)
1522 return m_parent->getNode(getPosRelative() + p);
1524 catch(InvalidPositionException &e)
1526 return MapNode(CONTENT_IGNORE);
1533 return MapNode(CONTENT_IGNORE);
1535 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1542 void MapBlock::updateMesh(u32 daynight_ratio)
1546 DEBUG: If mesh has been generated, don't generate it again
1549 JMutexAutoLock meshlock(mesh_mutex);
1556 data.fill(daynight_ratio, this);
1558 scene::SMesh *mesh_new = makeMapBlockMesh(&data);
1564 replaceMesh(mesh_new);
1569 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
1573 //scene::SMesh *mesh_old = mesh[daynight_i];
1574 //mesh[daynight_i] = mesh_new;
1576 scene::SMesh *mesh_old = mesh;
1578 setMeshExpired(false);
1580 if(mesh_old != NULL)
1582 // Remove hardware buffers of meshbuffers of mesh
1583 // NOTE: No way, this runs in a different thread and everything
1584 /*u32 c = mesh_old->getMeshBufferCount();
1585 for(u32 i=0; i<c; i++)
1587 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1590 /*dstream<<"mesh_old->getReferenceCount()="
1591 <<mesh_old->getReferenceCount()<<std::endl;
1592 u32 c = mesh_old->getMeshBufferCount();
1593 for(u32 i=0; i<c; i++)
1595 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1596 dstream<<"buf->getReferenceCount()="
1597 <<buf->getReferenceCount()<<std::endl;
1606 mesh_mutex.Unlock();
1612 Propagates sunlight down through the block.
1613 Doesn't modify nodes that are not affected by sunlight.
1615 Returns false if sunlight at bottom block is invalid
1616 Returns true if bottom block doesn't exist.
1618 If there is a block above, continues from it.
1619 If there is no block above, assumes there is sunlight, unless
1620 is_underground is set or highest node is water.
1622 At the moment, all sunlighted nodes are added to light_sources.
1623 - SUGG: This could be optimized
1625 Turns sunglighted mud into grass.
1627 if remove_light==true, sets non-sunlighted nodes black.
1629 if black_air_left!=NULL, it is set to true if non-sunlighted
1630 air is left in block.
1632 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1633 bool remove_light, bool *black_air_left,
1636 // Whether the sunlight at the top of the bottom block is valid
1637 bool block_below_is_valid = true;
1639 v3s16 pos_relative = getPosRelative();
1641 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1643 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1646 bool no_sunlight = false;
1647 bool no_top_block = false;
1648 // Check if node above block has sunlight
1650 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1651 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1656 catch(InvalidPositionException &e)
1658 no_top_block = true;
1660 // NOTE: This makes over-ground roofed places sunlighted
1661 // Assume sunlight, unless is_underground==true
1668 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1669 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1674 // NOTE: As of now, this just would make everything dark.
1676 //no_sunlight = true;
1679 #if 0 // Doesn't work; nothing gets light.
1680 bool no_sunlight = true;
1681 bool no_top_block = false;
1682 // Check if node above block has sunlight
1684 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1685 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1687 no_sunlight = false;
1690 catch(InvalidPositionException &e)
1692 no_top_block = true;
1696 /*std::cout<<"("<<x<<","<<z<<"): "
1697 <<"no_top_block="<<no_top_block
1698 <<", is_underground="<<is_underground
1699 <<", no_sunlight="<<no_sunlight
1702 s16 y = MAP_BLOCKSIZE-1;
1704 // This makes difference to diminishing in water.
1705 bool stopped_to_solid_object = false;
1707 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1712 MapNode &n = getNodeRef(pos);
1714 if(current_light == 0)
1718 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1720 // Do nothing: Sunlight is continued
1722 else if(n.light_propagates() == false)
1726 bool upper_is_air = false;
1729 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1730 upper_is_air = true;
1732 catch(InvalidPositionException &e)
1735 // Turn mud into grass
1736 if(upper_is_air && n.d == CONTENT_MUD
1737 && current_light == LIGHT_SUN)
1739 n.d = CONTENT_GRASS;
1743 // A solid object is on the way.
1744 stopped_to_solid_object = true;
1752 current_light = diminish_light(current_light);
1755 u8 old_light = n.getLight(LIGHTBANK_DAY);
1757 if(current_light > old_light || remove_light)
1759 n.setLight(LIGHTBANK_DAY, current_light);
1762 if(diminish_light(current_light) != 0)
1764 light_sources.insert(pos_relative + pos, true);
1767 if(current_light == 0 && stopped_to_solid_object)
1771 *black_air_left = true;
1776 // Whether or not the block below should see LIGHT_SUN
1777 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1780 If the block below hasn't already been marked invalid:
1782 Check if the node below the block has proper sunlight at top.
1783 If not, the block below is invalid.
1785 Ignore non-transparent nodes as they always have no light
1789 if(block_below_is_valid)
1791 MapNode n = getNodeParent(v3s16(x, -1, z));
1792 if(n.light_propagates())
1794 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1795 && sunlight_should_go_down == false)
1796 block_below_is_valid = false;
1797 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1798 && sunlight_should_go_down == true)
1799 block_below_is_valid = false;
1803 catch(InvalidPositionException &e)
1805 /*std::cout<<"InvalidBlockException for bottom block node"
1807 // Just no block below, no need to panic.
1812 return block_below_is_valid;
1816 void MapBlock::copyTo(VoxelManipulator &dst)
1818 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1819 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1821 // Copy from data to VoxelManipulator
1822 dst.copyFrom(data, data_area, v3s16(0,0,0),
1823 getPosRelative(), data_size);
1826 void MapBlock::copyFrom(VoxelManipulator &dst)
1828 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1829 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1831 // Copy from VoxelManipulator to data
1832 dst.copyTo(data, data_area, v3s16(0,0,0),
1833 getPosRelative(), data_size);
1836 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1841 m_objects.step(dtime, server, daynight_ratio);
1845 Spawn some objects at random.
1847 Use dayNightDiffed() to approximate being near ground level
1849 if(m_spawn_timer < -999)
1853 if(dayNightDiffed() == true && getObjectCount() == 0)
1855 m_spawn_timer -= dtime;
1856 if(m_spawn_timer <= 0.0)
1858 m_spawn_timer += myrand() % 300;
1861 (myrand()%(MAP_BLOCKSIZE-1))+0,
1862 (myrand()%(MAP_BLOCKSIZE-1))+0
1865 s16 y = getGroundLevel(p2d);
1869 v3s16 p(p2d.X, y+1, p2d.Y);
1871 if(getNode(p).d == CONTENT_AIR
1872 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1874 RatObject *obj = new RatObject(NULL, -1, intToFloat(p, BS));
1886 void MapBlock::updateDayNightDiff()
1890 m_day_night_differs = false;
1894 bool differs = false;
1897 Check if any lighting value differs
1899 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1901 MapNode &n = data[i];
1902 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1910 If some lighting values differ, check if the whole thing is
1911 just air. If it is, differ = false
1915 bool only_air = true;
1916 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1918 MapNode &n = data[i];
1919 if(n.d != CONTENT_AIR)
1929 // Set member variable
1930 m_day_night_differs = differs;
1933 s16 MapBlock::getGroundLevel(v2s16 p2d)
1939 s16 y = MAP_BLOCKSIZE-1;
1942 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1943 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1945 if(y == MAP_BLOCKSIZE-1)
1953 catch(InvalidPositionException &e)
1963 void MapBlock::serialize(std::ostream &os, u8 version)
1965 if(!ser_ver_supported(version))
1966 throw VersionMismatchException("ERROR: MapBlock format not supported");
1970 throw SerializationError("ERROR: Not writing dummy block.");
1973 // These have no compression
1974 if(version <= 3 || version == 5 || version == 6)
1976 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1978 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1979 SharedBuffer<u8> dest(buflen);
1981 dest[0] = is_underground;
1982 for(u32 i=0; i<nodecount; i++)
1984 u32 s = 1 + i * MapNode::serializedLength(version);
1985 data[i].serialize(&dest[s], version);
1988 os.write((char*)*dest, dest.getSize());
1990 else if(version <= 10)
1994 Compress the materials and the params separately.
1998 os.write((char*)&is_underground, 1);
2000 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2002 // Get and compress materials
2003 SharedBuffer<u8> materialdata(nodecount);
2004 for(u32 i=0; i<nodecount; i++)
2006 materialdata[i] = data[i].d;
2008 compress(materialdata, os, version);
2010 // Get and compress lights
2011 SharedBuffer<u8> lightdata(nodecount);
2012 for(u32 i=0; i<nodecount; i++)
2014 lightdata[i] = data[i].param;
2016 compress(lightdata, os, version);
2020 // Get and compress param2
2021 SharedBuffer<u8> param2data(nodecount);
2022 for(u32 i=0; i<nodecount; i++)
2024 param2data[i] = data[i].param2;
2026 compress(param2data, os, version);
2029 // All other versions (newest)
2036 if(m_day_night_differs)
2038 if(m_lighting_expired)
2040 os.write((char*)&flags, 1);
2042 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2048 SharedBuffer<u8> databuf(nodecount*3);
2051 for(u32 i=0; i<nodecount; i++)
2053 databuf[i] = data[i].d;
2057 for(u32 i=0; i<nodecount; i++)
2059 databuf[i+nodecount] = data[i].param;
2063 for(u32 i=0; i<nodecount; i++)
2065 databuf[i+nodecount*2] = data[i].param2;
2069 Compress data to output stream
2072 compress(databuf, os, version);
2081 std::ostringstream oss(std::ios_base::binary);
2082 m_node_metadata.serialize(oss);
2083 os<<serializeString(oss.str());
2087 std::ostringstream oss(std::ios_base::binary);
2088 m_node_metadata.serialize(oss);
2089 compressZlib(oss.str(), os);
2090 //os<<serializeLongString(oss.str());
2096 void MapBlock::deSerialize(std::istream &is, u8 version)
2098 if(!ser_ver_supported(version))
2099 throw VersionMismatchException("ERROR: MapBlock format not supported");
2101 // These have no lighting info
2104 setLightingExpired(true);
2107 // These have no compression
2108 if(version <= 3 || version == 5 || version == 6)
2110 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2113 if(is.gcount() != 1)
2114 throw SerializationError
2115 ("MapBlock::deSerialize: no enough input data");
2116 is_underground = tmp;
2117 for(u32 i=0; i<nodecount; i++)
2119 s32 len = MapNode::serializedLength(version);
2120 SharedBuffer<u8> d(len);
2121 is.read((char*)*d, len);
2122 if(is.gcount() != len)
2123 throw SerializationError
2124 ("MapBlock::deSerialize: no enough input data");
2125 data[i].deSerialize(*d, version);
2128 else if(version <= 10)
2130 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2133 is.read((char*)&t8, 1);
2134 is_underground = t8;
2137 // Uncompress and set material data
2138 std::ostringstream os(std::ios_base::binary);
2139 decompress(is, os, version);
2140 std::string s = os.str();
2141 if(s.size() != nodecount)
2142 throw SerializationError
2143 ("MapBlock::deSerialize: invalid format");
2144 for(u32 i=0; i<s.size(); i++)
2150 // Uncompress and set param data
2151 std::ostringstream os(std::ios_base::binary);
2152 decompress(is, os, version);
2153 std::string s = os.str();
2154 if(s.size() != nodecount)
2155 throw SerializationError
2156 ("MapBlock::deSerialize: invalid format");
2157 for(u32 i=0; i<s.size(); i++)
2159 data[i].param = s[i];
2165 // Uncompress and set param2 data
2166 std::ostringstream os(std::ios_base::binary);
2167 decompress(is, os, version);
2168 std::string s = os.str();
2169 if(s.size() != nodecount)
2170 throw SerializationError
2171 ("MapBlock::deSerialize: invalid format");
2172 for(u32 i=0; i<s.size(); i++)
2174 data[i].param2 = s[i];
2178 // All other versions (newest)
2181 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2184 is.read((char*)&flags, 1);
2185 is_underground = (flags & 0x01) ? true : false;
2186 m_day_night_differs = (flags & 0x02) ? true : false;
2187 m_lighting_expired = (flags & 0x04) ? true : false;
2190 std::ostringstream os(std::ios_base::binary);
2191 decompress(is, os, version);
2192 std::string s = os.str();
2193 if(s.size() != nodecount*3)
2194 throw SerializationError
2195 ("MapBlock::deSerialize: invalid format");
2198 for(u32 i=0; i<nodecount; i++)
2203 for(u32 i=0; i<nodecount; i++)
2205 data[i].param = s[i+nodecount];
2208 for(u32 i=0; i<nodecount; i++)
2210 data[i].param2 = s[i+nodecount*2];
2222 std::string data = deSerializeString(is);
2223 std::istringstream iss(data, std::ios_base::binary);
2224 m_node_metadata.deSerialize(iss);
2228 //std::string data = deSerializeLongString(is);
2229 std::ostringstream oss(std::ios_base::binary);
2230 decompressZlib(is, oss);
2231 std::istringstream iss(oss.str(), std::ios_base::binary);
2232 m_node_metadata.deSerialize(iss);
2235 catch(SerializationError &e)
2237 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
2238 <<" while deserializing node metadata"<<std::endl;
2244 Translate nodes as specified in the translate_to fields of
2247 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2249 MapNode &n = data[i];
2251 MapNode *translate_to = content_features(n.d).translate_to;
2254 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2255 <<translate_to->d<<std::endl;