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.
32 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
36 is_underground(false),
37 m_day_night_differs(false),
44 m_spawn_timer = -10000;
47 m_mesh_expired = false;
50 m_temp_mods_mutex.Init();
58 JMutexAutoLock lock(mesh_mutex);
72 bool MapBlock::isValidPositionParent(v3s16 p)
74 if(isValidPosition(p))
79 return m_parent->isValidPosition(getPosRelative() + p);
83 MapNode MapBlock::getNodeParent(v3s16 p)
85 if(isValidPosition(p) == false)
87 return m_parent->getNode(getPosRelative() + p);
92 throw InvalidPositionException();
93 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
97 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
99 if(isValidPosition(p) == false)
101 m_parent->setNode(getPosRelative() + p, n);
106 throw InvalidPositionException();
107 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
111 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
113 if(isValidPosition(p) == false)
116 return m_parent->getNode(getPosRelative() + p);
118 catch(InvalidPositionException &e)
120 return MapNode(CONTENT_IGNORE);
127 return MapNode(CONTENT_IGNORE);
129 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
134 Parameters must consist of air and !air.
135 Order doesn't matter.
137 If either of the nodes doesn't exist, light is 0.
140 daynight_ratio: 0...1000
142 n2: getNodeParent(p + face_dir)
143 face_dir: axis oriented unit vector from p to p2
145 returns encoded light value.
147 u8 MapBlock::getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
153 if(n.d == CONTENT_WATER)
160 if(n2.d == CONTENT_WATER)
171 u8 l1 = n.getLightBlend(daynight_ratio);
172 u8 l2 = n2.getLightBlend(daynight_ratio);
178 // Make some nice difference to different sides
180 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
181 light = diminish_light(diminish_light(light));
182 else if(face_dir.X == -1 || face_dir.Z == -1)
183 light = diminish_light(light);*/
185 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
186 light = diminish_light(diminish_light(light));
187 else if(face_dir.Z == 1 || face_dir.Z == -1)
188 light = diminish_light(light);
192 catch(InvalidPositionException &e)
200 void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
201 v3s16 dir, v3f scale, v3f posRelative_f,
202 core::array<FastFace> &dest)
206 // Position is at the center of the cube.
211 // If looking towards z+, this is the face that is behind
212 // the center point, facing towards z+.
213 vertex_pos[0] = v3f( BS/2,-BS/2,BS/2);
214 vertex_pos[1] = v3f(-BS/2,-BS/2,BS/2);
215 vertex_pos[2] = v3f(-BS/2, BS/2,BS/2);
216 vertex_pos[3] = v3f( BS/2, BS/2,BS/2);
218 if(dir == v3s16(0,0,1))
220 for(u16 i=0; i<4; i++)
221 vertex_pos[i].rotateXZBy(0);
223 else if(dir == v3s16(0,0,-1))
225 for(u16 i=0; i<4; i++)
226 vertex_pos[i].rotateXZBy(180);
228 else if(dir == v3s16(1,0,0))
230 for(u16 i=0; i<4; i++)
231 vertex_pos[i].rotateXZBy(-90);
233 else if(dir == v3s16(-1,0,0))
235 for(u16 i=0; i<4; i++)
236 vertex_pos[i].rotateXZBy(90);
238 else if(dir == v3s16(0,1,0))
240 for(u16 i=0; i<4; i++)
241 vertex_pos[i].rotateYZBy(-90);
243 else if(dir == v3s16(0,-1,0))
245 for(u16 i=0; i<4; i++)
246 vertex_pos[i].rotateYZBy(90);
249 for(u16 i=0; i<4; i++)
251 vertex_pos[i].X *= scale.X;
252 vertex_pos[i].Y *= scale.Y;
253 vertex_pos[i].Z *= scale.Z;
254 vertex_pos[i] += pos + posRelative_f;
258 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
259 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
260 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
262 v3f zerovector = v3f(0,0,0);
264 //u8 li = decode_light(light);
267 u8 alpha = tile.alpha;
269 if(tile.id == TILE_WATER)
270 alpha = WATER_ALPHA;*/
272 video::SColor c = video::SColor(alpha,li,li,li);
274 face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
275 core::vector2d<f32>(0,1));
276 face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
277 core::vector2d<f32>(abs_scale,1));
278 face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
279 core::vector2d<f32>(abs_scale,0));
280 face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
281 core::vector2d<f32>(0,0));
285 //f->tile = TILE_STONE;
287 dest.push_back(face);
292 Gets node tile from any place relative to block.
293 Returns TILE_NODE if doesn't exist or should not be drawn.
295 TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir)
298 spec = mn.getTile(face_dir);
301 Check temporary modifications on this node
303 core::map<v3s16, NodeMod>::Node *n;
304 n = m_temp_mods.find(p);
309 struct NodeMod mod = n->getValue();
310 if(mod.type == NODEMOD_CHANGECONTENT)
312 //spec = content_tile(mod.param, face_dir);
313 MapNode mn2(mod.param);
314 spec = mn2.getTile(face_dir);
316 if(mod.type == NODEMOD_CRACK)
318 std::ostringstream os;
319 os<<"[[mod:crack"<<mod.param;
320 spec.name += os.str();
327 u8 MapBlock::getNodeContent(v3s16 p, MapNode mn)
330 Check temporary modifications on this node
332 core::map<v3s16, NodeMod>::Node *n;
333 n = m_temp_mods.find(p);
338 struct NodeMod mod = n->getValue();
339 if(mod.type == NODEMOD_CHANGECONTENT)
344 if(mod.type == NODEMOD_CRACK)
347 Content doesn't change.
349 face_contents works just like it should, because
350 there should not be faces between differently cracked
353 If a semi-transparent node is cracked in front an
354 another one, it really doesn't matter whether there
355 is a cracked face drawn in between or not.
365 translate_dir: unit vector with only one of x, y or z
366 face_dir: unit vector with only one of x, y or z
368 void MapBlock::updateFastFaceRow(
377 core::array<FastFace> &dest)
381 u16 continuous_tiles_count = 0;
383 MapNode n0 = getNodeParentNoEx(p);
384 MapNode n1 = getNodeParentNoEx(p + face_dir);
386 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
388 TileSpec tile0 = getNodeTile(n0, p, face_dir);
389 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir);
391 for(u16 j=0; j<length; j++)
393 bool next_is_different = true;
404 p_next = p + translate_dir;
405 n0_next = getNodeParentNoEx(p_next);
406 n1_next = getNodeParentNoEx(p_next + face_dir);
407 tile0_next = getNodeTile(n0_next, p_next, face_dir);
408 tile1_next = getNodeTile(n1_next, p_next + face_dir, -face_dir);
409 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
411 if(tile0_next == tile0
412 && tile1_next == tile1
413 && light_next == light)
415 next_is_different = false;
419 continuous_tiles_count++;
421 if(next_is_different)
424 Create a face if there should be one
426 //u8 mf = face_contents(tile0, tile1);
428 u8 content0 = getNodeContent(p, n0);
429 u8 content1 = getNodeContent(p + face_dir, n1);
430 u8 mf = face_contents(content0, content1);
434 // Floating point conversion of the position vector
435 v3f pf(p.X, p.Y, p.Z);
436 // Center point of face (kind of)
437 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
439 if(translate_dir.X != 0){
440 scale.X = continuous_tiles_count;
442 if(translate_dir.Y != 0){
443 scale.Y = continuous_tiles_count;
445 if(translate_dir.Z != 0){
446 scale.Z = continuous_tiles_count;
451 // If node at sp (tile0) is more solid
454 makeFastFace(tile0, decode_light(light),
456 posRelative_f, dest);
458 // If node at sp is less solid (mf == 2)
461 makeFastFace(tile1, decode_light(light),
462 sp+face_dir_f, -face_dir, scale,
463 posRelative_f, dest);
468 continuous_tiles_count = 0;
481 This is used because CMeshBuffer::append() is very slow
485 video::SMaterial material;
486 core::array<u16> indices;
487 core::array<video::S3DVertex> vertices;
494 video::SMaterial material,
495 const video::S3DVertex* const vertices,
497 const u16* const indices,
501 PreMeshBuffer *p = NULL;
502 for(u32 i=0; i<m_prebuffers.size(); i++)
504 PreMeshBuffer &pp = m_prebuffers[i];
505 if(pp.material != material)
515 pp.material = material;
516 m_prebuffers.push_back(pp);
517 p = &m_prebuffers[m_prebuffers.size()-1];
520 u32 vertex_count = p->vertices.size();
521 for(u32 i=0; i<numIndices; i++)
523 u32 j = indices[i] + vertex_count;
526 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
527 // NOTE: Fix is to just add an another MeshBuffer
529 p->indices.push_back(j);
531 for(u32 i=0; i<numVertices; i++)
533 p->vertices.push_back(vertices[i]);
537 void fillMesh(scene::SMesh *mesh)
539 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
540 <<" meshbuffers"<<std::endl;*/
541 for(u32 i=0; i<m_prebuffers.size(); i++)
543 PreMeshBuffer &p = m_prebuffers[i];
545 /*dstream<<"p.vertices.size()="<<p.vertices.size()
546 <<", p.indices.size()="<<p.indices.size()
551 // This is a "Standard MeshBuffer",
552 // it's a typedeffed CMeshBuffer<video::S3DVertex>
553 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
555 buf->Material = p.material;
556 //((scene::SMeshBuffer*)buf)->Material = p.material;
558 //buf->setHardwareMappingHint(scene::EHM_STATIC);
560 mesh->addMeshBuffer(buf);
564 buf->append(p.vertices.pointer(), p.vertices.size(),
565 p.indices.pointer(), p.indices.size());
570 core::array<PreMeshBuffer> m_prebuffers;
573 void MapBlock::updateMesh(u32 daynight_ratio)
577 DEBUG: If mesh has been generated, don't generate it again
580 JMutexAutoLock meshlock(mesh_mutex);
586 // 4-21ms for MAP_BLOCKSIZE=16
587 // 24-155ms for MAP_BLOCKSIZE=32
588 //TimeTaker timer1("updateMesh()");
590 core::array<FastFace> fastfaces_new;
592 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
593 getPosRelative().Z); // floating point conversion
596 We are including the faces of the trailing edges of the block.
597 This means that when something changes, the caller must
598 also update the meshes of the blocks at the leading edges.
600 NOTE: This is the slowest part of this method.
604 //TimeTaker timer2("updateMesh() collect");
606 // Lock this, as m_temp_mods will be used directly
607 JMutexAutoLock lock(m_temp_mods_mutex);
610 Go through every y,z and get top faces in rows of x+
612 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
613 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
614 updateFastFaceRow(daynight_ratio, posRelative_f,
615 v3s16(0,y,z), MAP_BLOCKSIZE,
618 v3s16(0,1,0), //face dir
624 Go through every x,y and get right faces in rows of z+
626 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
627 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
628 updateFastFaceRow(daynight_ratio, posRelative_f,
629 v3s16(x,y,0), MAP_BLOCKSIZE,
638 Go through every y,z and get back faces in rows of x+
640 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
641 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
642 updateFastFaceRow(daynight_ratio, posRelative_f,
643 v3s16(0,y,z), MAP_BLOCKSIZE,
656 Convert FastFaces to SMesh
659 scene::SMesh *mesh_new = NULL;
661 mesh_new = new scene::SMesh();
663 MeshCollector collector;
665 if(fastfaces_new.size() > 0)
667 // avg 0ms (100ms spikes when loading textures the first time)
668 //TimeTaker timer2("updateMesh() mesh building");
670 for(u32 i=0; i<fastfaces_new.size(); i++)
672 FastFace &f = fastfaces_new[i];
674 const u16 indices[] = {0,1,2,2,3,0};
676 video::ITexture *texture = g_irrlicht->getTexture(f.tile.name);
677 video::SMaterial material;
678 material.Lighting = false;
679 material.BackfaceCulling = false;
680 material.setFlag(video::EMF_BILINEAR_FILTER, false);
681 material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
682 material.setFlag(video::EMF_FOG_ENABLE, true);
683 material.setTexture(0, texture);
684 if(f.tile.alpha != 255)
685 material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
687 collector.append(material, f.vertices, 4, indices, 6);
692 Add special graphics:
695 TODO: Optimize by using same meshbuffer for same textures
699 //TimeTaker timer2("updateMesh() adding special stuff");
701 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
702 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
703 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
707 MapNode &n = getNodeRef(x,y,z);
712 if(n.d == CONTENT_TORCH)
714 video::SColor c(255,255,255,255);
716 video::S3DVertex vertices[4] =
718 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
719 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
720 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
721 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
724 v3s16 dir = unpackDir(n.dir);
726 for(s32 i=0; i<4; i++)
728 if(dir == v3s16(1,0,0))
729 vertices[i].Pos.rotateXZBy(0);
730 if(dir == v3s16(-1,0,0))
731 vertices[i].Pos.rotateXZBy(180);
732 if(dir == v3s16(0,0,1))
733 vertices[i].Pos.rotateXZBy(90);
734 if(dir == v3s16(0,0,-1))
735 vertices[i].Pos.rotateXZBy(-90);
736 if(dir == v3s16(0,-1,0))
737 vertices[i].Pos.rotateXZBy(45);
738 if(dir == v3s16(0,1,0))
739 vertices[i].Pos.rotateXZBy(-45);
741 vertices[i].Pos += intToFloat(p + getPosRelative());
745 video::SMaterial material;
746 material.setFlag(video::EMF_LIGHTING, false);
747 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
748 material.setFlag(video::EMF_BILINEAR_FILTER, false);
749 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
750 material.MaterialType
751 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
752 if(dir == v3s16(0,-1,0))
753 material.setTexture(0,
754 g_irrlicht->getTexture(porting::getDataPath("torch_on_floor.png").c_str()));
755 else if(dir == v3s16(0,1,0))
756 material.setTexture(0,
757 g_irrlicht->getTexture(porting::getDataPath("torch_on_ceiling.png").c_str()));
758 // For backwards compatibility
759 else if(dir == v3s16(0,0,0))
760 material.setTexture(0,
761 g_irrlicht->getTexture(porting::getDataPath("torch_on_floor.png").c_str()));
763 material.setTexture(0,
764 g_irrlicht->getTexture(porting::getDataPath("torch.png").c_str()));
766 u16 indices[] = {0,1,2,2,3,0};
767 // Add to mesh collector
768 collector.append(material, vertices, 4, indices, 6);
771 Add flowing water to mesh
773 else if(n.d == CONTENT_WATER)
775 bool top_is_water = false;
777 MapNode n = getNodeParent(v3s16(x,y+1,z));
778 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
780 }catch(InvalidPositionException &e){}
782 u8 l = decode_light(n.getLightBlend(daynight_ratio));
783 video::SColor c(WATER_ALPHA,l,l,l);
785 // Neighbor water levels (key = relative position)
786 // Includes current node
787 core::map<v3s16, f32> neighbor_levels;
788 core::map<v3s16, u8> neighbor_contents;
789 v3s16 neighbor_dirs[9] = {
800 for(u32 i=0; i<9; i++)
802 u8 content = CONTENT_AIR;
803 float level = -0.5 * BS;
805 v3s16 p2 = p + neighbor_dirs[i];
806 MapNode n2 = getNodeParent(p2);
810 if(n2.d == CONTENT_WATERSOURCE)
812 else if(n2.d == CONTENT_WATER)
813 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0) * BS;
815 catch(InvalidPositionException &e){}
817 neighbor_levels.insert(neighbor_dirs[i], level);
818 neighbor_contents.insert(neighbor_dirs[i], content);
821 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
822 //float water_level = neighbor_levels[v3s16(0,0,0)];
824 // Corner heights (average between four waters)
825 f32 corner_levels[4];
827 v3s16 halfdirs[4] = {
833 for(u32 i=0; i<4; i++)
835 v3s16 cornerdir = halfdirs[i];
836 float cornerlevel = 0;
838 for(u32 j=0; j<4; j++)
840 v3s16 neighbordir = cornerdir - halfdirs[j];
841 u8 content = neighbor_contents[neighbordir];
842 // Special case for source nodes
843 if(content == CONTENT_WATERSOURCE)
845 cornerlevel = 0.5*BS;
849 else if(content == CONTENT_WATER)
851 cornerlevel += neighbor_levels[neighbordir];
854 else if(content == CONTENT_AIR)
856 cornerlevel += -0.5*BS;
861 cornerlevel /= valid_count;
862 corner_levels[i] = cornerlevel;
869 v3s16 side_dirs[4] = {
875 s16 side_corners[4][2] = {
881 for(u32 i=0; i<4; i++)
883 v3s16 dir = side_dirs[i];
885 //float neighbor_level = neighbor_levels[dir];
886 /*if(neighbor_level > -0.5*BS + 0.001)
888 /*if(neighbor_level > water_level - 0.1*BS)
891 u8 neighbor_content = neighbor_contents[dir];
893 if(neighbor_content != CONTENT_AIR
894 && neighbor_content != CONTENT_WATER)
897 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
899 if(neighbor_is_water == true && top_is_water == false)
902 video::S3DVertex vertices[4] =
904 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
905 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
906 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
907 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
908 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
909 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
910 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
911 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
916 vertices[2].Pos.Y = 0.5*BS;
917 vertices[3].Pos.Y = 0.5*BS;
921 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
922 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
925 if(neighbor_is_water)
927 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
928 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
932 vertices[0].Pos.Y = -0.5*BS;
933 vertices[1].Pos.Y = -0.5*BS;
936 for(s32 j=0; j<4; j++)
938 if(dir == v3s16(0,0,1))
939 vertices[j].Pos.rotateXZBy(0);
940 if(dir == v3s16(0,0,-1))
941 vertices[j].Pos.rotateXZBy(180);
942 if(dir == v3s16(-1,0,0))
943 vertices[j].Pos.rotateXZBy(90);
944 if(dir == v3s16(1,0,-0))
945 vertices[j].Pos.rotateXZBy(-90);
947 vertices[j].Pos += intToFloat(p + getPosRelative());
951 video::SMaterial material;
952 material.setFlag(video::EMF_LIGHTING, false);
953 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
954 material.setFlag(video::EMF_BILINEAR_FILTER, false);
955 material.setFlag(video::EMF_FOG_ENABLE, true);
956 material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
957 material.setTexture(0,
958 g_irrlicht->getTexture(porting::getDataPath("water.png").c_str()));
960 u16 indices[] = {0,1,2,2,3,0};
961 // Add to mesh collector
962 collector.append(material, vertices, 4, indices, 6);
966 Generate top side, if appropriate
969 if(top_is_water == false)
971 video::S3DVertex vertices[4] =
973 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
974 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
975 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
976 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
979 for(s32 i=0; i<4; i++)
981 //vertices[i].Pos.Y += water_level;
982 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
983 vertices[i].Pos.Y += corner_levels[i];
984 vertices[i].Pos += intToFloat(p + getPosRelative());
988 video::SMaterial material;
989 material.setFlag(video::EMF_LIGHTING, false);
990 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
991 material.setFlag(video::EMF_BILINEAR_FILTER, false);
992 material.setFlag(video::EMF_FOG_ENABLE, true);
993 material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
994 material.setTexture(0,
995 g_irrlicht->getTexture(porting::getDataPath("water.png").c_str()));
997 u16 indices[] = {0,1,2,2,3,0};
998 // Add to mesh collector
999 collector.append(material, vertices, 4, indices, 6);
1005 Add stuff from collector to mesh
1008 collector.fillMesh(mesh_new);
1011 Do some stuff to the mesh
1014 mesh_new->recalculateBoundingBox();
1017 Delete new mesh if it is empty
1020 if(mesh_new->getMeshBufferCount() == 0)
1026 // Use VBO for mesh (this just would set this for ever buffer)
1027 // This will lead to infinite memory usage because or irrlicht.
1028 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1030 /*std::cout<<"MapBlock has "<<fastfaces_new.size()<<" faces "
1031 <<"and uses "<<mesh_new->getMeshBufferCount()
1032 <<" materials (meshbuffers)"<<std::endl;*/
1040 //scene::SMesh *mesh_old = mesh[daynight_i];
1041 //mesh[daynight_i] = mesh_new;
1043 scene::SMesh *mesh_old = mesh;
1045 setMeshExpired(false);
1047 if(mesh_old != NULL)
1049 // Remove hardware buffers of meshbuffers of mesh
1050 // NOTE: No way, this runs in a different thread and everything
1051 /*u32 c = mesh_old->getMeshBufferCount();
1052 for(u32 i=0; i<c; i++)
1054 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1057 /*dstream<<"mesh_old->getReferenceCount()="
1058 <<mesh_old->getReferenceCount()<<std::endl;
1059 u32 c = mesh_old->getMeshBufferCount();
1060 for(u32 i=0; i<c; i++)
1062 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1063 dstream<<"buf->getReferenceCount()="
1064 <<buf->getReferenceCount()<<std::endl;
1073 mesh_mutex.Unlock();
1075 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1078 /*void MapBlock::updateMeshes(s32 first_i)
1080 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
1081 updateMesh(first_i);
1082 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
1093 Propagates sunlight down through the block.
1094 Doesn't modify nodes that are not affected by sunlight.
1096 Returns false if sunlight at bottom block is invalid
1097 Returns true if bottom block doesn't exist.
1099 If there is a block above, continues from it.
1100 If there is no block above, assumes there is sunlight, unless
1101 is_underground is set or highest node is water.
1103 At the moment, all sunlighted nodes are added to light_sources.
1104 - SUGG: This could be optimized
1106 Turns sunglighted mud into grass.
1108 if remove_light==true, sets non-sunlighted nodes black.
1110 if black_air_left!=NULL, it is set to true if non-sunlighted
1111 air is left in block.
1113 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1114 bool remove_light, bool *black_air_left,
1117 // Whether the sunlight at the top of the bottom block is valid
1118 bool block_below_is_valid = true;
1120 v3s16 pos_relative = getPosRelative();
1122 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1124 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1127 bool no_sunlight = false;
1128 bool no_top_block = false;
1129 // Check if node above block has sunlight
1131 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1132 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1137 catch(InvalidPositionException &e)
1139 no_top_block = true;
1141 // NOTE: This makes over-ground roofed places sunlighted
1142 // Assume sunlight, unless is_underground==true
1149 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1150 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1155 // NOTE: As of now, this just would make everything dark.
1157 //no_sunlight = true;
1160 #if 0 // Doesn't work; nothing gets light.
1161 bool no_sunlight = true;
1162 bool no_top_block = false;
1163 // Check if node above block has sunlight
1165 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1166 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1168 no_sunlight = false;
1171 catch(InvalidPositionException &e)
1173 no_top_block = true;
1177 /*std::cout<<"("<<x<<","<<z<<"): "
1178 <<"no_top_block="<<no_top_block
1179 <<", is_underground="<<is_underground
1180 <<", no_sunlight="<<no_sunlight
1183 s16 y = MAP_BLOCKSIZE-1;
1185 // This makes difference to diminishing in water.
1186 bool stopped_to_solid_object = false;
1188 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1193 MapNode &n = getNodeRef(pos);
1195 if(current_light == 0)
1199 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1201 // Do nothing: Sunlight is continued
1203 else if(n.light_propagates() == false)
1207 bool upper_is_air = false;
1210 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1211 upper_is_air = true;
1213 catch(InvalidPositionException &e)
1216 // Turn mud into grass
1217 if(upper_is_air && n.d == CONTENT_MUD
1218 && current_light == LIGHT_SUN)
1220 n.d = CONTENT_GRASS;
1224 // A solid object is on the way.
1225 stopped_to_solid_object = true;
1233 current_light = diminish_light(current_light);
1236 u8 old_light = n.getLight(LIGHTBANK_DAY);
1238 if(current_light > old_light || remove_light)
1240 n.setLight(LIGHTBANK_DAY, current_light);
1243 if(diminish_light(current_light) != 0)
1245 light_sources.insert(pos_relative + pos, true);
1248 if(current_light == 0 && stopped_to_solid_object)
1252 *black_air_left = true;
1257 // Whether or not the block below should see LIGHT_SUN
1258 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1261 If the block below hasn't already been marked invalid:
1263 Check if the node below the block has proper sunlight at top.
1264 If not, the block below is invalid.
1266 Ignore non-transparent nodes as they always have no light
1270 if(block_below_is_valid)
1272 MapNode n = getNodeParent(v3s16(x, -1, z));
1273 if(n.light_propagates())
1275 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1276 && sunlight_should_go_down == false)
1277 block_below_is_valid = false;
1278 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1279 && sunlight_should_go_down == true)
1280 block_below_is_valid = false;
1284 catch(InvalidPositionException &e)
1286 /*std::cout<<"InvalidBlockException for bottom block node"
1288 // Just no block below, no need to panic.
1293 return block_below_is_valid;
1296 void MapBlock::copyTo(VoxelManipulator &dst)
1298 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1299 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1301 dst.copyFrom(data, data_area, v3s16(0,0,0),
1302 getPosRelative(), data_size);
1305 /*void getPseudoObjects(v3f origin, f32 max_d,
1306 core::array<DistanceSortedObject> &dest)
1309 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1314 m_objects.step(dtime, server, daynight_ratio);
1317 Spawn some objects at random.
1319 Use dayNightDiffed() to approximate being near ground level
1321 if(m_spawn_timer < -999)
1325 if(dayNightDiffed() == true && getObjectCount() == 0)
1327 m_spawn_timer -= dtime;
1328 if(m_spawn_timer <= 0.0)
1330 m_spawn_timer += myrand() % 300;
1333 (myrand()%(MAP_BLOCKSIZE-1))+0,
1334 (myrand()%(MAP_BLOCKSIZE-1))+0
1337 s16 y = getGroundLevel(p2d);
1341 v3s16 p(p2d.X, y+1, p2d.Y);
1343 if(getNode(p).d == CONTENT_AIR
1344 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1346 RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1357 void MapBlock::updateDayNightDiff()
1361 m_day_night_differs = false;
1365 bool differs = false;
1368 Check if any lighting value differs
1370 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1372 MapNode &n = data[i];
1373 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1381 If some lighting values differ, check if the whole thing is
1382 just air. If it is, differ = false
1386 bool only_air = true;
1387 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1389 MapNode &n = data[i];
1390 if(n.d != CONTENT_AIR)
1400 // Set member variable
1401 m_day_night_differs = differs;
1404 s16 MapBlock::getGroundLevel(v2s16 p2d)
1410 s16 y = MAP_BLOCKSIZE-1;
1413 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1414 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1416 if(y == MAP_BLOCKSIZE-1)
1424 catch(InvalidPositionException &e)
1434 void MapBlock::serialize(std::ostream &os, u8 version)
1436 if(!ser_ver_supported(version))
1437 throw VersionMismatchException("ERROR: MapBlock format not supported");
1441 throw SerializationError("ERROR: Not writing dummy block.");
1444 // These have no compression
1445 if(version <= 3 || version == 5 || version == 6)
1447 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1449 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1450 SharedBuffer<u8> dest(buflen);
1452 dest[0] = is_underground;
1453 for(u32 i=0; i<nodecount; i++)
1455 u32 s = 1 + i * MapNode::serializedLength(version);
1456 data[i].serialize(&dest[s], version);
1459 os.write((char*)*dest, dest.getSize());
1461 else if(version <= 10)
1465 Compress the materials and the params separately.
1469 os.write((char*)&is_underground, 1);
1471 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1473 // Get and compress materials
1474 SharedBuffer<u8> materialdata(nodecount);
1475 for(u32 i=0; i<nodecount; i++)
1477 materialdata[i] = data[i].d;
1479 compress(materialdata, os, version);
1481 // Get and compress lights
1482 SharedBuffer<u8> lightdata(nodecount);
1483 for(u32 i=0; i<nodecount; i++)
1485 lightdata[i] = data[i].param;
1487 compress(lightdata, os, version);
1491 // Get and compress param2
1492 SharedBuffer<u8> param2data(nodecount);
1493 for(u32 i=0; i<nodecount; i++)
1495 param2data[i] = data[i].param2;
1497 compress(param2data, os, version);
1500 // All other versions (newest)
1507 if(m_day_night_differs)
1509 os.write((char*)&flags, 1);
1511 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1517 SharedBuffer<u8> databuf(nodecount*3);
1520 for(u32 i=0; i<nodecount; i++)
1522 databuf[i] = data[i].d;
1526 for(u32 i=0; i<nodecount; i++)
1528 databuf[i+nodecount] = data[i].param;
1532 for(u32 i=0; i<nodecount; i++)
1534 databuf[i+nodecount*2] = data[i].param2;
1538 Compress data to output stream
1541 compress(databuf, os, version);
1545 void MapBlock::deSerialize(std::istream &is, u8 version)
1547 if(!ser_ver_supported(version))
1548 throw VersionMismatchException("ERROR: MapBlock format not supported");
1550 // These have no compression
1551 if(version <= 3 || version == 5 || version == 6)
1553 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1556 if(is.gcount() != 1)
1557 throw SerializationError
1558 ("MapBlock::deSerialize: no enough input data");
1559 is_underground = tmp;
1560 for(u32 i=0; i<nodecount; i++)
1562 s32 len = MapNode::serializedLength(version);
1563 SharedBuffer<u8> d(len);
1564 is.read((char*)*d, len);
1565 if(is.gcount() != len)
1566 throw SerializationError
1567 ("MapBlock::deSerialize: no enough input data");
1568 data[i].deSerialize(*d, version);
1571 else if(version <= 10)
1573 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1576 is.read((char*)&t8, 1);
1577 is_underground = t8;
1580 // Uncompress and set material data
1581 std::ostringstream os(std::ios_base::binary);
1582 decompress(is, os, version);
1583 std::string s = os.str();
1584 if(s.size() != nodecount)
1585 throw SerializationError
1586 ("MapBlock::deSerialize: invalid format");
1587 for(u32 i=0; i<s.size(); i++)
1593 // Uncompress and set param data
1594 std::ostringstream os(std::ios_base::binary);
1595 decompress(is, os, version);
1596 std::string s = os.str();
1597 if(s.size() != nodecount)
1598 throw SerializationError
1599 ("MapBlock::deSerialize: invalid format");
1600 for(u32 i=0; i<s.size(); i++)
1602 data[i].param = s[i];
1608 // Uncompress and set param2 data
1609 std::ostringstream os(std::ios_base::binary);
1610 decompress(is, os, version);
1611 std::string s = os.str();
1612 if(s.size() != nodecount)
1613 throw SerializationError
1614 ("MapBlock::deSerialize: invalid format");
1615 for(u32 i=0; i<s.size(); i++)
1617 data[i].param2 = s[i];
1621 // All other versions (newest)
1624 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1627 is.read((char*)&flags, 1);
1628 is_underground = (flags & 1) ? true : false;
1629 m_day_night_differs = (flags & 2) ? true : false;
1632 std::ostringstream os(std::ios_base::binary);
1633 decompress(is, os, version);
1634 std::string s = os.str();
1635 if(s.size() != nodecount*3)
1636 throw SerializationError
1637 ("MapBlock::deSerialize: invalid format");
1640 for(u32 i=0; i<nodecount; i++)
1645 for(u32 i=0; i<nodecount; i++)
1647 data[i].param = s[i+nodecount];
1650 for(u32 i=0; i<nodecount; i++)
1652 data[i].param2 = s[i+nodecount*2];