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 u8 MapBlock::getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
151 if(n.d == CONTENT_WATER)
158 if(n2.d == CONTENT_WATER)
169 u8 l1 = n.getLightBlend(daynight_ratio);
170 u8 l2 = n2.getLightBlend(daynight_ratio);
176 // Make some nice difference to different sides
178 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
179 light = diminish_light(diminish_light(light));
180 else if(face_dir.X == -1 || face_dir.Z == -1)
181 light = diminish_light(light);*/
183 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
184 light = diminish_light(diminish_light(light));
185 else if(face_dir.Z == 1 || face_dir.Z == -1)
186 light = diminish_light(light);
190 catch(InvalidPositionException &e)
198 void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
199 v3s16 dir, v3f scale, v3f posRelative_f,
200 core::array<FastFace> &dest)
204 // Position is at the center of the cube.
209 // If looking towards z+, this is the face that is behind
210 // the center point, facing towards z+.
211 vertex_pos[0] = v3f( BS/2,-BS/2,BS/2);
212 vertex_pos[1] = v3f(-BS/2,-BS/2,BS/2);
213 vertex_pos[2] = v3f(-BS/2, BS/2,BS/2);
214 vertex_pos[3] = v3f( BS/2, BS/2,BS/2);
216 if(dir == v3s16(0,0,1))
218 for(u16 i=0; i<4; i++)
219 vertex_pos[i].rotateXZBy(0);
221 else if(dir == v3s16(0,0,-1))
223 for(u16 i=0; i<4; i++)
224 vertex_pos[i].rotateXZBy(180);
226 else if(dir == v3s16(1,0,0))
228 for(u16 i=0; i<4; i++)
229 vertex_pos[i].rotateXZBy(-90);
231 else if(dir == v3s16(-1,0,0))
233 for(u16 i=0; i<4; i++)
234 vertex_pos[i].rotateXZBy(90);
236 else if(dir == v3s16(0,1,0))
238 for(u16 i=0; i<4; i++)
239 vertex_pos[i].rotateYZBy(-90);
241 else if(dir == v3s16(0,-1,0))
243 for(u16 i=0; i<4; i++)
244 vertex_pos[i].rotateYZBy(90);
247 for(u16 i=0; i<4; i++)
249 vertex_pos[i].X *= scale.X;
250 vertex_pos[i].Y *= scale.Y;
251 vertex_pos[i].Z *= scale.Z;
252 vertex_pos[i] += pos + posRelative_f;
256 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
257 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
258 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
260 v3f zerovector = v3f(0,0,0);
262 //u8 li = decode_light(light);
267 if(tile.id == TILE_WATER)
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)
301 spec.id = TILE_STONE;
305 spec.feature = TILEFEAT_NONE;
306 //spec.id = TILE_STONE;
307 spec.id = mn.getTile(face_dir);
310 Check temporary modifications on this node
312 core::map<v3s16, NodeMod>::Node *n;
313 n = m_temp_mods.find(p);
318 struct NodeMod mod = n->getValue();
319 if(mod.type == NODEMOD_CHANGECONTENT)
321 spec.id = content_tile(mod.param, face_dir);
323 if(mod.type == NODEMOD_CRACK)
325 spec.feature = TILEFEAT_CRACK;
326 spec.param.crack.progression = mod.param;
333 u8 MapBlock::getNodeContent(v3s16 p, MapNode mn)
336 Check temporary modifications on this node
338 core::map<v3s16, NodeMod>::Node *n;
339 n = m_temp_mods.find(p);
344 struct NodeMod mod = n->getValue();
345 if(mod.type == NODEMOD_CHANGECONTENT)
350 if(mod.type == NODEMOD_CRACK)
353 Content doesn't change.
355 face_contents works just like it should, because
356 there should not be faces between differently cracked
359 If a semi-transparent node is cracked in front an
360 another one, it really doesn't matter whether there
361 is a cracked face drawn in between or not.
371 translate_dir: unit vector with only one of x, y or z
372 face_dir: unit vector with only one of x, y or z
374 void MapBlock::updateFastFaceRow(
383 core::array<FastFace> &dest)
387 u16 continuous_tiles_count = 0;
389 MapNode n0 = getNodeParentNoEx(p);
390 MapNode n1 = getNodeParentNoEx(p + face_dir);
392 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
394 TileSpec tile0 = getNodeTile(n0, p, face_dir);
395 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir);
397 for(u16 j=0; j<length; j++)
399 bool next_is_different = true;
410 p_next = p + translate_dir;
411 n0_next = getNodeParentNoEx(p_next);
412 n1_next = getNodeParentNoEx(p_next + face_dir);
413 tile0_next = getNodeTile(n0_next, p_next, face_dir);
414 tile1_next = getNodeTile(n1_next, p_next + face_dir, -face_dir);
415 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
417 if(tile0_next == tile0
418 && tile1_next == tile1
419 && light_next == light)
421 next_is_different = false;
425 continuous_tiles_count++;
427 if(next_is_different)
430 Create a face if there should be one
432 //u8 mf = face_contents(tile0, tile1);
434 u8 content0 = getNodeContent(p, n0);
435 u8 content1 = getNodeContent(p + face_dir, n1);
436 u8 mf = face_contents(content0, content1);
440 // Floating point conversion of the position vector
441 v3f pf(p.X, p.Y, p.Z);
442 // Center point of face (kind of)
443 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
445 if(translate_dir.X != 0){
446 scale.X = continuous_tiles_count;
448 if(translate_dir.Y != 0){
449 scale.Y = continuous_tiles_count;
451 if(translate_dir.Z != 0){
452 scale.Z = continuous_tiles_count;
457 // If node at sp (tile0) is more solid
460 makeFastFace(tile0, decode_light(light),
462 posRelative_f, dest);
464 // If node at sp is less solid (mf == 2)
467 makeFastFace(tile1, decode_light(light),
468 sp+face_dir_f, -face_dir, scale,
469 posRelative_f, dest);
474 continuous_tiles_count = 0;
487 This is used because CMeshBuffer::append() is very slow
491 video::SMaterial material;
492 core::array<u16> indices;
493 core::array<video::S3DVertex> vertices;
500 video::SMaterial material,
501 const video::S3DVertex* const vertices,
503 const u16* const indices,
507 PreMeshBuffer *p = NULL;
508 for(u32 i=0; i<m_prebuffers.size(); i++)
510 PreMeshBuffer &pp = m_prebuffers[i];
511 if(pp.material != material)
521 pp.material = material;
522 m_prebuffers.push_back(pp);
523 p = &m_prebuffers[m_prebuffers.size()-1];
526 u32 vertex_count = p->vertices.size();
527 for(u32 i=0; i<numIndices; i++)
529 u32 j = indices[i] + vertex_count;
532 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
533 // NOTE: Fix is to just add an another MeshBuffer
535 p->indices.push_back(j);
537 for(u32 i=0; i<numVertices; i++)
539 p->vertices.push_back(vertices[i]);
543 void fillMesh(scene::SMesh *mesh)
545 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
546 <<" meshbuffers"<<std::endl;*/
547 for(u32 i=0; i<m_prebuffers.size(); i++)
549 PreMeshBuffer &p = m_prebuffers[i];
551 /*dstream<<"p.vertices.size()="<<p.vertices.size()
552 <<", p.indices.size()="<<p.indices.size()
557 // This is a "Standard MeshBuffer",
558 // it's a typedeffed CMeshBuffer<video::S3DVertex>
559 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
561 buf->Material = p.material;
562 //((scene::SMeshBuffer*)buf)->Material = p.material;
564 //buf->setHardwareMappingHint(scene::EHM_STATIC);
566 mesh->addMeshBuffer(buf);
570 buf->append(p.vertices.pointer(), p.vertices.size(),
571 p.indices.pointer(), p.indices.size());
576 core::array<PreMeshBuffer> m_prebuffers;
579 void MapBlock::updateMesh(u32 daynight_ratio)
583 DEBUG: If mesh has been generated, don't generate it again
586 JMutexAutoLock meshlock(mesh_mutex);
592 // 4-21ms for MAP_BLOCKSIZE=16
593 // 24-155ms for MAP_BLOCKSIZE=32
594 //TimeTaker timer1("updateMesh()");
596 core::array<FastFace> fastfaces_new;
598 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
599 getPosRelative().Z); // floating point conversion
602 We are including the faces of the trailing edges of the block.
603 This means that when something changes, the caller must
604 also update the meshes of the blocks at the leading edges.
606 NOTE: This is the slowest part of this method.
610 // Lock this, as m_temp_mods will be used directly
611 JMutexAutoLock lock(m_temp_mods_mutex);
614 Go through every y,z and get top faces in rows of x+
616 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
617 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
618 updateFastFaceRow(daynight_ratio, posRelative_f,
619 v3s16(0,y,z), MAP_BLOCKSIZE,
622 v3s16(0,1,0), //face dir
628 Go through every x,y and get right faces in rows of z+
630 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
631 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
632 updateFastFaceRow(daynight_ratio, posRelative_f,
633 v3s16(x,y,0), MAP_BLOCKSIZE,
642 Go through every y,z and get back faces in rows of x+
644 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
645 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
646 updateFastFaceRow(daynight_ratio, posRelative_f,
647 v3s16(0,y,z), MAP_BLOCKSIZE,
660 Convert FastFaces to SMesh
663 scene::SMesh *mesh_new = NULL;
665 mesh_new = new scene::SMesh();
667 MeshCollector collector;
669 if(fastfaces_new.size() > 0)
671 for(u32 i=0; i<fastfaces_new.size(); i++)
673 FastFace &f = fastfaces_new[i];
675 const u16 indices[] = {0,1,2,2,3,0};
677 if(f.tile.feature == TILEFEAT_NONE)
679 collector.append(tile_material_get(f.tile.id), f.vertices, 4,
682 else if(f.tile.feature == TILEFEAT_CRACK)
684 const char *path = tile_texture_path_get(f.tile.id);
686 u16 progression = f.tile.param.crack.progression;
688 std::string name = (std::string)path + "_cracked_"
689 + (char)('0' + progression);
691 TextureMod *mod = new CrackTextureMod(progression);
693 video::ITexture *texture = g_irrlicht->getTexture(
694 TextureSpec(name, path, mod));
696 video::SMaterial material = tile_material_get(f.tile.id);
697 material.setTexture(0, texture);
699 collector.append(material, f.vertices, 4, indices, 6);
710 Add special graphics:
713 TODO: Optimize by using same meshbuffer for same textures
716 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
717 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
718 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
722 MapNode &n = getNodeRef(x,y,z);
724 if(n.d == CONTENT_TORCH)
726 video::SColor c(255,255,255,255);
728 video::S3DVertex vertices[4] =
730 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
731 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
732 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
733 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
736 v3s16 dir = unpackDir(n.dir);
738 for(s32 i=0; i<4; i++)
740 if(dir == v3s16(1,0,0))
741 vertices[i].Pos.rotateXZBy(0);
742 if(dir == v3s16(-1,0,0))
743 vertices[i].Pos.rotateXZBy(180);
744 if(dir == v3s16(0,0,1))
745 vertices[i].Pos.rotateXZBy(90);
746 if(dir == v3s16(0,0,-1))
747 vertices[i].Pos.rotateXZBy(-90);
748 if(dir == v3s16(0,-1,0))
749 vertices[i].Pos.rotateXZBy(45);
750 if(dir == v3s16(0,1,0))
751 vertices[i].Pos.rotateXZBy(-45);
753 vertices[i].Pos += intToFloat(p + getPosRelative());
757 video::SMaterial material;
758 material.setFlag(video::EMF_LIGHTING, false);
759 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
760 material.setFlag(video::EMF_BILINEAR_FILTER, false);
761 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
762 material.MaterialType
763 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
764 if(dir == v3s16(0,-1,0))
765 material.setTexture(0,
766 g_irrlicht->getTexture(porting::getDataPath("torch_on_floor.png").c_str()));
767 else if(dir == v3s16(0,1,0))
768 material.setTexture(0,
769 g_irrlicht->getTexture(porting::getDataPath("torch_on_ceiling.png").c_str()));
770 // For backwards compatibility
771 else if(dir == v3s16(0,0,0))
772 material.setTexture(0,
773 g_irrlicht->getTexture(porting::getDataPath("torch_on_floor.png").c_str()));
775 material.setTexture(0,
776 g_irrlicht->getTexture(porting::getDataPath("torch.png").c_str()));
778 u16 indices[] = {0,1,2,2,3,0};
779 // Add to mesh collector
780 collector.append(material, vertices, 4, indices, 6);
782 else if(n.d == CONTENT_WATER)
784 bool top_is_water = false;
786 MapNode n = getNodeParent(v3s16(x,y+1,z));
787 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
789 }catch(InvalidPositionException &e){}
791 video::SColor c(128,255,255,255);
793 // Neighbor water levels (key = relative position)
794 // Includes current node
795 core::map<v3s16, f32> neighbor_levels;
796 core::map<v3s16, u8> neighbor_contents;
797 v3s16 neighbor_dirs[9] = {
808 for(u32 i=0; i<9; i++)
810 u8 content = CONTENT_AIR;
811 float level = -0.5 * BS;
813 v3s16 p2 = p + neighbor_dirs[i];
814 MapNode n2 = getNodeParent(p2);
818 if(n2.d == CONTENT_WATERSOURCE)
820 else if(n2.d == CONTENT_WATER)
821 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0) * BS;
823 catch(InvalidPositionException &e){}
825 neighbor_levels.insert(neighbor_dirs[i], level);
826 neighbor_contents.insert(neighbor_dirs[i], content);
829 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
830 //float water_level = neighbor_levels[v3s16(0,0,0)];
832 // Corner heights (average between four waters)
833 f32 corner_levels[4];
835 v3s16 halfdirs[4] = {
841 for(u32 i=0; i<4; i++)
843 v3s16 cornerdir = halfdirs[i];
844 float cornerlevel = 0;
846 for(u32 j=0; j<4; j++)
848 v3s16 neighbordir = cornerdir - halfdirs[j];
849 u8 content = neighbor_contents[neighbordir];
850 // Special case for source nodes
851 if(content == CONTENT_WATERSOURCE)
853 cornerlevel = 0.5*BS;
857 else if(content == CONTENT_WATER)
859 cornerlevel += neighbor_levels[neighbordir];
862 else if(content == CONTENT_AIR)
864 cornerlevel += -0.5*BS;
869 cornerlevel /= valid_count;
870 corner_levels[i] = cornerlevel;
877 v3s16 side_dirs[4] = {
883 s16 side_corners[4][2] = {
889 for(u32 i=0; i<4; i++)
891 v3s16 dir = side_dirs[i];
893 //float neighbor_level = neighbor_levels[dir];
894 /*if(neighbor_level > -0.5*BS + 0.001)
896 /*if(neighbor_level > water_level - 0.1*BS)
899 u8 neighbor_content = neighbor_contents[dir];
901 if(neighbor_content != CONTENT_AIR
902 && neighbor_content != CONTENT_WATER)
905 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
907 if(neighbor_is_water == true && top_is_water == false)
910 video::S3DVertex vertices[4] =
912 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
913 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
914 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
915 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
916 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
917 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
918 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
919 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
924 vertices[2].Pos.Y = 0.5*BS;
925 vertices[3].Pos.Y = 0.5*BS;
929 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
930 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
933 if(neighbor_is_water)
935 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
936 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
940 vertices[0].Pos.Y = -0.5*BS;
941 vertices[1].Pos.Y = -0.5*BS;
944 for(s32 j=0; j<4; j++)
946 if(dir == v3s16(0,0,1))
947 vertices[j].Pos.rotateXZBy(0);
948 if(dir == v3s16(0,0,-1))
949 vertices[j].Pos.rotateXZBy(180);
950 if(dir == v3s16(-1,0,0))
951 vertices[j].Pos.rotateXZBy(90);
952 if(dir == v3s16(1,0,-0))
953 vertices[j].Pos.rotateXZBy(-90);
955 vertices[j].Pos += intToFloat(p + getPosRelative());
959 video::SMaterial material;
960 material.setFlag(video::EMF_LIGHTING, false);
961 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
962 material.setFlag(video::EMF_BILINEAR_FILTER, false);
963 material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
964 material.setTexture(0,
965 g_irrlicht->getTexture(porting::getDataPath("water.png").c_str()));
967 u16 indices[] = {0,1,2,2,3,0};
968 // Add to mesh collector
969 collector.append(material, vertices, 4, indices, 6);
973 Generate top side, if appropriate
976 if(top_is_water == false)
978 video::S3DVertex vertices[4] =
980 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
981 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
982 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
983 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
986 for(s32 i=0; i<4; i++)
988 //vertices[i].Pos.Y += water_level;
989 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
990 vertices[i].Pos.Y += corner_levels[i];
991 vertices[i].Pos += intToFloat(p + getPosRelative());
995 video::SMaterial material;
996 material.setFlag(video::EMF_LIGHTING, false);
997 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
998 material.setFlag(video::EMF_BILINEAR_FILTER, false);
999 material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
1000 material.setTexture(0,
1001 g_irrlicht->getTexture(porting::getDataPath("water.png").c_str()));
1003 u16 indices[] = {0,1,2,2,3,0};
1004 // Add to mesh collector
1005 collector.append(material, vertices, 4, indices, 6);
1011 Add stuff from collector to mesh
1014 collector.fillMesh(mesh_new);
1017 Do some stuff to the mesh
1020 mesh_new->recalculateBoundingBox();
1023 Delete new mesh if it is empty
1026 if(mesh_new->getMeshBufferCount() == 0)
1032 // Use VBO for mesh (this just would set this for ever buffer)
1033 // This will lead to infinite memory usage because or irrlicht.
1034 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1036 /*std::cout<<"MapBlock has "<<fastfaces_new.size()<<" faces "
1037 <<"and uses "<<mesh_new->getMeshBufferCount()
1038 <<" materials (meshbuffers)"<<std::endl;*/
1046 //scene::SMesh *mesh_old = mesh[daynight_i];
1047 //mesh[daynight_i] = mesh_new;
1049 scene::SMesh *mesh_old = mesh;
1051 setMeshExpired(false);
1053 if(mesh_old != NULL)
1055 // Remove hardware buffers of meshbuffers of mesh
1056 // NOTE: No way, this runs in a different thread and everything
1057 /*u32 c = mesh_old->getMeshBufferCount();
1058 for(u32 i=0; i<c; i++)
1060 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1063 /*dstream<<"mesh_old->getReferenceCount()="
1064 <<mesh_old->getReferenceCount()<<std::endl;
1065 u32 c = mesh_old->getMeshBufferCount();
1066 for(u32 i=0; i<c; i++)
1068 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1069 dstream<<"buf->getReferenceCount()="
1070 <<buf->getReferenceCount()<<std::endl;
1079 mesh_mutex.Unlock();
1081 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1084 /*void MapBlock::updateMeshes(s32 first_i)
1086 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
1087 updateMesh(first_i);
1088 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
1099 Propagates sunlight down through the block.
1100 Doesn't modify nodes that are not affected by sunlight.
1102 Returns false if sunlight at bottom block is invalid
1103 Returns true if bottom block doesn't exist.
1105 If there is a block above, continues from it.
1106 If there is no block above, assumes there is sunlight, unless
1107 is_underground is set or highest node is water.
1109 At the moment, all sunlighted nodes are added to light_sources.
1110 - SUGG: This could be optimized
1112 Turns sunglighted mud into grass.
1114 if remove_light==true, sets non-sunlighted nodes black.
1116 if black_air_left!=NULL, it is set to true if non-sunlighted
1117 air is left in block.
1119 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1120 bool remove_light, bool *black_air_left,
1123 // Whether the sunlight at the top of the bottom block is valid
1124 bool block_below_is_valid = true;
1126 v3s16 pos_relative = getPosRelative();
1128 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1130 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1133 bool no_sunlight = false;
1134 bool no_top_block = false;
1135 // Check if node above block has sunlight
1137 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1138 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1143 catch(InvalidPositionException &e)
1145 no_top_block = true;
1147 // NOTE: This makes over-ground roofed places sunlighted
1148 // Assume sunlight, unless is_underground==true
1155 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1156 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1161 // NOTE: As of now, this just would make everything dark.
1163 //no_sunlight = true;
1166 #if 0 // Doesn't work; nothing gets light.
1167 bool no_sunlight = true;
1168 bool no_top_block = false;
1169 // Check if node above block has sunlight
1171 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1172 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1174 no_sunlight = false;
1177 catch(InvalidPositionException &e)
1179 no_top_block = true;
1183 /*std::cout<<"("<<x<<","<<z<<"): "
1184 <<"no_top_block="<<no_top_block
1185 <<", is_underground="<<is_underground
1186 <<", no_sunlight="<<no_sunlight
1189 s16 y = MAP_BLOCKSIZE-1;
1191 // This makes difference to diminishing in water.
1192 bool stopped_to_solid_object = false;
1194 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1199 MapNode &n = getNodeRef(pos);
1201 if(current_light == 0)
1205 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1207 // Do nothing: Sunlight is continued
1209 else if(n.light_propagates() == false)
1213 bool upper_is_air = false;
1216 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1217 upper_is_air = true;
1219 catch(InvalidPositionException &e)
1222 // Turn mud into grass
1223 if(upper_is_air && n.d == CONTENT_MUD
1224 && current_light == LIGHT_SUN)
1226 n.d = CONTENT_GRASS;
1230 // A solid object is on the way.
1231 stopped_to_solid_object = true;
1239 current_light = diminish_light(current_light);
1242 u8 old_light = n.getLight(LIGHTBANK_DAY);
1244 if(current_light > old_light || remove_light)
1246 n.setLight(LIGHTBANK_DAY, current_light);
1249 if(diminish_light(current_light) != 0)
1251 light_sources.insert(pos_relative + pos, true);
1254 if(current_light == 0 && stopped_to_solid_object)
1258 *black_air_left = true;
1263 // Whether or not the block below should see LIGHT_SUN
1264 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1267 If the block below hasn't already been marked invalid:
1269 Check if the node below the block has proper sunlight at top.
1270 If not, the block below is invalid.
1272 Ignore non-transparent nodes as they always have no light
1276 if(block_below_is_valid)
1278 MapNode n = getNodeParent(v3s16(x, -1, z));
1279 if(n.light_propagates())
1281 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1282 && sunlight_should_go_down == false)
1283 block_below_is_valid = false;
1284 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1285 && sunlight_should_go_down == true)
1286 block_below_is_valid = false;
1290 catch(InvalidPositionException &e)
1292 /*std::cout<<"InvalidBlockException for bottom block node"
1294 // Just no block below, no need to panic.
1299 return block_below_is_valid;
1302 void MapBlock::copyTo(VoxelManipulator &dst)
1304 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1305 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1307 dst.copyFrom(data, data_area, v3s16(0,0,0),
1308 getPosRelative(), data_size);
1311 /*void getPseudoObjects(v3f origin, f32 max_d,
1312 core::array<DistanceSortedObject> &dest)
1315 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1320 m_objects.step(dtime, server, daynight_ratio);
1323 Spawn some objects at random.
1325 Use dayNightDiffed() to approximate being near ground level
1327 if(m_spawn_timer < -999)
1331 if(dayNightDiffed() == true && getObjectCount() == 0)
1333 m_spawn_timer -= dtime;
1334 if(m_spawn_timer <= 0.0)
1336 m_spawn_timer += myrand() % 300;
1339 (myrand()%(MAP_BLOCKSIZE-1))+0,
1340 (myrand()%(MAP_BLOCKSIZE-1))+0
1343 s16 y = getGroundLevel(p2d);
1347 v3s16 p(p2d.X, y+1, p2d.Y);
1349 if(getNode(p).d == CONTENT_AIR
1350 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1352 RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1363 void MapBlock::updateDayNightDiff()
1367 m_day_night_differs = false;
1371 bool differs = false;
1374 Check if any lighting value differs
1376 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1378 MapNode &n = data[i];
1379 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1387 If some lighting values differ, check if the whole thing is
1388 just air. If it is, differ = false
1392 bool only_air = true;
1393 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1395 MapNode &n = data[i];
1396 if(n.d != CONTENT_AIR)
1406 // Set member variable
1407 m_day_night_differs = differs;
1410 s16 MapBlock::getGroundLevel(v2s16 p2d)
1416 s16 y = MAP_BLOCKSIZE-1;
1419 if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1421 if(y == MAP_BLOCKSIZE-1)
1429 catch(InvalidPositionException &e)
1439 void MapBlock::serialize(std::ostream &os, u8 version)
1441 if(!ser_ver_supported(version))
1442 throw VersionMismatchException("ERROR: MapBlock format not supported");
1446 throw SerializationError("ERROR: Not writing dummy block.");
1449 // These have no compression
1450 if(version <= 3 || version == 5 || version == 6)
1452 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1454 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1455 SharedBuffer<u8> dest(buflen);
1457 dest[0] = is_underground;
1458 for(u32 i=0; i<nodecount; i++)
1460 u32 s = 1 + i * MapNode::serializedLength(version);
1461 data[i].serialize(&dest[s], version);
1464 os.write((char*)*dest, dest.getSize());
1466 else if(version <= 10)
1470 Compress the materials and the params separately.
1474 os.write((char*)&is_underground, 1);
1476 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1478 // Get and compress materials
1479 SharedBuffer<u8> materialdata(nodecount);
1480 for(u32 i=0; i<nodecount; i++)
1482 materialdata[i] = data[i].d;
1484 compress(materialdata, os, version);
1486 // Get and compress lights
1487 SharedBuffer<u8> lightdata(nodecount);
1488 for(u32 i=0; i<nodecount; i++)
1490 lightdata[i] = data[i].param;
1492 compress(lightdata, os, version);
1496 // Get and compress pressure
1497 SharedBuffer<u8> pressuredata(nodecount);
1498 for(u32 i=0; i<nodecount; i++)
1500 pressuredata[i] = data[i].pressure;
1502 compress(pressuredata, os, version);
1505 // All other versions (newest)
1512 if(m_day_night_differs)
1514 os.write((char*)&flags, 1);
1516 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1522 SharedBuffer<u8> databuf(nodecount*3);
1525 for(u32 i=0; i<nodecount; i++)
1527 databuf[i] = data[i].d;
1531 for(u32 i=0; i<nodecount; i++)
1533 databuf[i+nodecount] = data[i].param;
1537 for(u32 i=0; i<nodecount; i++)
1539 databuf[i+nodecount*2] = data[i].pressure;
1543 Compress data to output stream
1546 compress(databuf, os, version);
1550 void MapBlock::deSerialize(std::istream &is, u8 version)
1552 if(!ser_ver_supported(version))
1553 throw VersionMismatchException("ERROR: MapBlock format not supported");
1555 // These have no compression
1556 if(version <= 3 || version == 5 || version == 6)
1558 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1561 if(is.gcount() != 1)
1562 throw SerializationError
1563 ("MapBlock::deSerialize: no enough input data");
1564 is_underground = tmp;
1565 for(u32 i=0; i<nodecount; i++)
1567 s32 len = MapNode::serializedLength(version);
1568 SharedBuffer<u8> d(len);
1569 is.read((char*)*d, len);
1570 if(is.gcount() != len)
1571 throw SerializationError
1572 ("MapBlock::deSerialize: no enough input data");
1573 data[i].deSerialize(*d, version);
1576 else if(version <= 10)
1578 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1581 is.read((char*)&t8, 1);
1582 is_underground = t8;
1585 // Uncompress and set material data
1586 std::ostringstream os(std::ios_base::binary);
1587 decompress(is, os, version);
1588 std::string s = os.str();
1589 if(s.size() != nodecount)
1590 throw SerializationError
1591 ("MapBlock::deSerialize: invalid format");
1592 for(u32 i=0; i<s.size(); i++)
1598 // Uncompress and set param data
1599 std::ostringstream os(std::ios_base::binary);
1600 decompress(is, os, version);
1601 std::string s = os.str();
1602 if(s.size() != nodecount)
1603 throw SerializationError
1604 ("MapBlock::deSerialize: invalid format");
1605 for(u32 i=0; i<s.size(); i++)
1607 data[i].param = s[i];
1613 // Uncompress and set pressure data
1614 std::ostringstream os(std::ios_base::binary);
1615 decompress(is, os, version);
1616 std::string s = os.str();
1617 if(s.size() != nodecount)
1618 throw SerializationError
1619 ("MapBlock::deSerialize: invalid format");
1620 for(u32 i=0; i<s.size(); i++)
1622 data[i].pressure = s[i];
1626 // All other versions (newest)
1629 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1632 is.read((char*)&flags, 1);
1633 is_underground = (flags & 1) ? true : false;
1634 m_day_night_differs = (flags & 2) ? true : false;
1637 std::ostringstream os(std::ios_base::binary);
1638 decompress(is, os, version);
1639 std::string s = os.str();
1640 if(s.size() != nodecount*3)
1641 throw SerializationError
1642 ("MapBlock::deSerialize: invalid format");
1645 for(u32 i=0; i<nodecount; i++)
1650 for(u32 i=0; i<nodecount; i++)
1652 data[i].param = s[i+nodecount];
1655 for(u32 i=0; i<nodecount; i++)
1657 data[i].pressure = s[i+nodecount*2];