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);
269 if(tile.id == TILE_WATER)
274 video::SColor c = video::SColor(alpha,li,li,li);
276 face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
277 core::vector2d<f32>(0,1));
278 face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
279 core::vector2d<f32>(abs_scale,1));
280 face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
281 core::vector2d<f32>(abs_scale,0));
282 face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
283 core::vector2d<f32>(0,0));
287 //f->tile = TILE_STONE;
289 dest.push_back(face);
294 Gets node tile from any place relative to block.
295 Returns TILE_NODE if doesn't exist or should not be drawn.
297 TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir)
303 spec.id = TILE_STONE;
307 spec.feature = TILEFEAT_NONE;
308 //spec.id = TILE_STONE;
309 spec.id = mn.getTile(face_dir);
312 Check temporary modifications on this node
314 core::map<v3s16, NodeMod>::Node *n;
315 n = m_temp_mods.find(p);
320 struct NodeMod mod = n->getValue();
321 if(mod.type == NODEMOD_CHANGECONTENT)
323 spec.id = content_tile(mod.param, face_dir);
325 if(mod.type == NODEMOD_CRACK)
327 spec.feature = TILEFEAT_CRACK;
328 spec.param.crack.progression = mod.param;
335 u8 MapBlock::getNodeContent(v3s16 p, MapNode mn)
338 Check temporary modifications on this node
340 core::map<v3s16, NodeMod>::Node *n;
341 n = m_temp_mods.find(p);
346 struct NodeMod mod = n->getValue();
347 if(mod.type == NODEMOD_CHANGECONTENT)
352 if(mod.type == NODEMOD_CRACK)
355 Content doesn't change.
357 face_contents works just like it should, because
358 there should not be faces between differently cracked
361 If a semi-transparent node is cracked in front an
362 another one, it really doesn't matter whether there
363 is a cracked face drawn in between or not.
373 translate_dir: unit vector with only one of x, y or z
374 face_dir: unit vector with only one of x, y or z
376 void MapBlock::updateFastFaceRow(
385 core::array<FastFace> &dest)
389 u16 continuous_tiles_count = 0;
391 MapNode n0 = getNodeParentNoEx(p);
392 MapNode n1 = getNodeParentNoEx(p + face_dir);
394 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
396 TileSpec tile0 = getNodeTile(n0, p, face_dir);
397 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir);
399 for(u16 j=0; j<length; j++)
401 bool next_is_different = true;
412 p_next = p + translate_dir;
413 n0_next = getNodeParentNoEx(p_next);
414 n1_next = getNodeParentNoEx(p_next + face_dir);
415 tile0_next = getNodeTile(n0_next, p_next, face_dir);
416 tile1_next = getNodeTile(n1_next, p_next + face_dir, -face_dir);
417 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
419 if(tile0_next == tile0
420 && tile1_next == tile1
421 && light_next == light)
423 next_is_different = false;
427 continuous_tiles_count++;
429 if(next_is_different)
432 Create a face if there should be one
434 //u8 mf = face_contents(tile0, tile1);
436 u8 content0 = getNodeContent(p, n0);
437 u8 content1 = getNodeContent(p + face_dir, n1);
438 u8 mf = face_contents(content0, content1);
442 // Floating point conversion of the position vector
443 v3f pf(p.X, p.Y, p.Z);
444 // Center point of face (kind of)
445 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
447 if(translate_dir.X != 0){
448 scale.X = continuous_tiles_count;
450 if(translate_dir.Y != 0){
451 scale.Y = continuous_tiles_count;
453 if(translate_dir.Z != 0){
454 scale.Z = continuous_tiles_count;
459 // If node at sp (tile0) is more solid
462 makeFastFace(tile0, decode_light(light),
464 posRelative_f, dest);
466 // If node at sp is less solid (mf == 2)
469 makeFastFace(tile1, decode_light(light),
470 sp+face_dir_f, -face_dir, scale,
471 posRelative_f, dest);
476 continuous_tiles_count = 0;
489 This is used because CMeshBuffer::append() is very slow
493 video::SMaterial material;
494 core::array<u16> indices;
495 core::array<video::S3DVertex> vertices;
502 video::SMaterial material,
503 const video::S3DVertex* const vertices,
505 const u16* const indices,
509 PreMeshBuffer *p = NULL;
510 for(u32 i=0; i<m_prebuffers.size(); i++)
512 PreMeshBuffer &pp = m_prebuffers[i];
513 if(pp.material != material)
523 pp.material = material;
524 m_prebuffers.push_back(pp);
525 p = &m_prebuffers[m_prebuffers.size()-1];
528 u32 vertex_count = p->vertices.size();
529 for(u32 i=0; i<numIndices; i++)
531 u32 j = indices[i] + vertex_count;
534 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
535 // NOTE: Fix is to just add an another MeshBuffer
537 p->indices.push_back(j);
539 for(u32 i=0; i<numVertices; i++)
541 p->vertices.push_back(vertices[i]);
545 void fillMesh(scene::SMesh *mesh)
547 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
548 <<" meshbuffers"<<std::endl;*/
549 for(u32 i=0; i<m_prebuffers.size(); i++)
551 PreMeshBuffer &p = m_prebuffers[i];
553 /*dstream<<"p.vertices.size()="<<p.vertices.size()
554 <<", p.indices.size()="<<p.indices.size()
559 // This is a "Standard MeshBuffer",
560 // it's a typedeffed CMeshBuffer<video::S3DVertex>
561 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
563 buf->Material = p.material;
564 //((scene::SMeshBuffer*)buf)->Material = p.material;
566 //buf->setHardwareMappingHint(scene::EHM_STATIC);
568 mesh->addMeshBuffer(buf);
572 buf->append(p.vertices.pointer(), p.vertices.size(),
573 p.indices.pointer(), p.indices.size());
578 core::array<PreMeshBuffer> m_prebuffers;
581 void MapBlock::updateMesh(u32 daynight_ratio)
585 DEBUG: If mesh has been generated, don't generate it again
588 JMutexAutoLock meshlock(mesh_mutex);
594 // 4-21ms for MAP_BLOCKSIZE=16
595 // 24-155ms for MAP_BLOCKSIZE=32
596 //TimeTaker timer1("updateMesh()");
598 core::array<FastFace> fastfaces_new;
600 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
601 getPosRelative().Z); // floating point conversion
604 We are including the faces of the trailing edges of the block.
605 This means that when something changes, the caller must
606 also update the meshes of the blocks at the leading edges.
608 NOTE: This is the slowest part of this method.
612 // Lock this, as m_temp_mods will be used directly
613 JMutexAutoLock lock(m_temp_mods_mutex);
616 Go through every y,z and get top faces in rows of x+
618 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
619 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
620 updateFastFaceRow(daynight_ratio, posRelative_f,
621 v3s16(0,y,z), MAP_BLOCKSIZE,
624 v3s16(0,1,0), //face dir
630 Go through every x,y and get right faces in rows of z+
632 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
633 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
634 updateFastFaceRow(daynight_ratio, posRelative_f,
635 v3s16(x,y,0), MAP_BLOCKSIZE,
644 Go through every y,z and get back faces in rows of x+
646 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
647 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
648 updateFastFaceRow(daynight_ratio, posRelative_f,
649 v3s16(0,y,z), MAP_BLOCKSIZE,
662 Convert FastFaces to SMesh
665 scene::SMesh *mesh_new = NULL;
667 mesh_new = new scene::SMesh();
669 MeshCollector collector;
671 if(fastfaces_new.size() > 0)
673 for(u32 i=0; i<fastfaces_new.size(); i++)
675 FastFace &f = fastfaces_new[i];
677 const u16 indices[] = {0,1,2,2,3,0};
679 if(f.tile.feature == TILEFEAT_NONE)
681 collector.append(tile_material_get(f.tile.id), f.vertices, 4,
684 else if(f.tile.feature == TILEFEAT_CRACK)
686 const char *path = tile_texture_path_get(f.tile.id);
688 u16 progression = f.tile.param.crack.progression;
690 std::string name = (std::string)path + "_cracked_"
691 + (char)('0' + progression);
693 TextureMod *mod = new CrackTextureMod(progression);
695 video::ITexture *texture = g_irrlicht->getTexture(
696 TextureSpec(name, path, mod));
698 video::SMaterial material = tile_material_get(f.tile.id);
699 material.setTexture(0, texture);
701 collector.append(material, f.vertices, 4, indices, 6);
712 Add special graphics:
715 TODO: Optimize by using same meshbuffer for same textures
718 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
719 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
720 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
724 MapNode &n = getNodeRef(x,y,z);
729 if(n.d == CONTENT_TORCH)
731 video::SColor c(255,255,255,255);
733 video::S3DVertex vertices[4] =
735 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
736 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
737 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
738 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
741 v3s16 dir = unpackDir(n.dir);
743 for(s32 i=0; i<4; i++)
745 if(dir == v3s16(1,0,0))
746 vertices[i].Pos.rotateXZBy(0);
747 if(dir == v3s16(-1,0,0))
748 vertices[i].Pos.rotateXZBy(180);
749 if(dir == v3s16(0,0,1))
750 vertices[i].Pos.rotateXZBy(90);
751 if(dir == v3s16(0,0,-1))
752 vertices[i].Pos.rotateXZBy(-90);
753 if(dir == v3s16(0,-1,0))
754 vertices[i].Pos.rotateXZBy(45);
755 if(dir == v3s16(0,1,0))
756 vertices[i].Pos.rotateXZBy(-45);
758 vertices[i].Pos += intToFloat(p + getPosRelative());
762 video::SMaterial material;
763 material.setFlag(video::EMF_LIGHTING, false);
764 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
765 material.setFlag(video::EMF_BILINEAR_FILTER, false);
766 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
767 material.MaterialType
768 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
769 if(dir == v3s16(0,-1,0))
770 material.setTexture(0,
771 g_irrlicht->getTexture(porting::getDataPath("torch_on_floor.png").c_str()));
772 else if(dir == v3s16(0,1,0))
773 material.setTexture(0,
774 g_irrlicht->getTexture(porting::getDataPath("torch_on_ceiling.png").c_str()));
775 // For backwards compatibility
776 else if(dir == v3s16(0,0,0))
777 material.setTexture(0,
778 g_irrlicht->getTexture(porting::getDataPath("torch_on_floor.png").c_str()));
780 material.setTexture(0,
781 g_irrlicht->getTexture(porting::getDataPath("torch.png").c_str()));
783 u16 indices[] = {0,1,2,2,3,0};
784 // Add to mesh collector
785 collector.append(material, vertices, 4, indices, 6);
788 Add flowing water to mesh
790 else if(n.d == CONTENT_WATER)
792 bool top_is_water = false;
794 MapNode n = getNodeParent(v3s16(x,y+1,z));
795 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
797 }catch(InvalidPositionException &e){}
799 u8 l = decode_light(n.getLightBlend(daynight_ratio));
800 video::SColor c(128,l,l,l);
802 // Neighbor water levels (key = relative position)
803 // Includes current node
804 core::map<v3s16, f32> neighbor_levels;
805 core::map<v3s16, u8> neighbor_contents;
806 v3s16 neighbor_dirs[9] = {
817 for(u32 i=0; i<9; i++)
819 u8 content = CONTENT_AIR;
820 float level = -0.5 * BS;
822 v3s16 p2 = p + neighbor_dirs[i];
823 MapNode n2 = getNodeParent(p2);
827 if(n2.d == CONTENT_WATERSOURCE)
829 else if(n2.d == CONTENT_WATER)
830 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0) * BS;
832 catch(InvalidPositionException &e){}
834 neighbor_levels.insert(neighbor_dirs[i], level);
835 neighbor_contents.insert(neighbor_dirs[i], content);
838 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
839 //float water_level = neighbor_levels[v3s16(0,0,0)];
841 // Corner heights (average between four waters)
842 f32 corner_levels[4];
844 v3s16 halfdirs[4] = {
850 for(u32 i=0; i<4; i++)
852 v3s16 cornerdir = halfdirs[i];
853 float cornerlevel = 0;
855 for(u32 j=0; j<4; j++)
857 v3s16 neighbordir = cornerdir - halfdirs[j];
858 u8 content = neighbor_contents[neighbordir];
859 // Special case for source nodes
860 if(content == CONTENT_WATERSOURCE)
862 cornerlevel = 0.5*BS;
866 else if(content == CONTENT_WATER)
868 cornerlevel += neighbor_levels[neighbordir];
871 else if(content == CONTENT_AIR)
873 cornerlevel += -0.5*BS;
878 cornerlevel /= valid_count;
879 corner_levels[i] = cornerlevel;
886 v3s16 side_dirs[4] = {
892 s16 side_corners[4][2] = {
898 for(u32 i=0; i<4; i++)
900 v3s16 dir = side_dirs[i];
902 //float neighbor_level = neighbor_levels[dir];
903 /*if(neighbor_level > -0.5*BS + 0.001)
905 /*if(neighbor_level > water_level - 0.1*BS)
908 u8 neighbor_content = neighbor_contents[dir];
910 if(neighbor_content != CONTENT_AIR
911 && neighbor_content != CONTENT_WATER)
914 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
916 if(neighbor_is_water == true && top_is_water == false)
919 video::S3DVertex vertices[4] =
921 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
922 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
923 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
924 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
925 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
926 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
927 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
928 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
933 vertices[2].Pos.Y = 0.5*BS;
934 vertices[3].Pos.Y = 0.5*BS;
938 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
939 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
942 if(neighbor_is_water)
944 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
945 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
949 vertices[0].Pos.Y = -0.5*BS;
950 vertices[1].Pos.Y = -0.5*BS;
953 for(s32 j=0; j<4; j++)
955 if(dir == v3s16(0,0,1))
956 vertices[j].Pos.rotateXZBy(0);
957 if(dir == v3s16(0,0,-1))
958 vertices[j].Pos.rotateXZBy(180);
959 if(dir == v3s16(-1,0,0))
960 vertices[j].Pos.rotateXZBy(90);
961 if(dir == v3s16(1,0,-0))
962 vertices[j].Pos.rotateXZBy(-90);
964 vertices[j].Pos += intToFloat(p + getPosRelative());
968 video::SMaterial material;
969 material.setFlag(video::EMF_LIGHTING, false);
970 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
971 material.setFlag(video::EMF_BILINEAR_FILTER, false);
972 material.setFlag(video::EMF_FOG_ENABLE, true);
973 material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
974 material.setTexture(0,
975 g_irrlicht->getTexture(porting::getDataPath("water.png").c_str()));
977 u16 indices[] = {0,1,2,2,3,0};
978 // Add to mesh collector
979 collector.append(material, vertices, 4, indices, 6);
983 Generate top side, if appropriate
986 if(top_is_water == false)
988 video::S3DVertex vertices[4] =
990 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
991 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
992 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
993 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
996 for(s32 i=0; i<4; i++)
998 //vertices[i].Pos.Y += water_level;
999 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1000 vertices[i].Pos.Y += corner_levels[i];
1001 vertices[i].Pos += intToFloat(p + getPosRelative());
1005 video::SMaterial material;
1006 material.setFlag(video::EMF_LIGHTING, false);
1007 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
1008 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1009 material.setFlag(video::EMF_FOG_ENABLE, true);
1010 material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
1011 material.setTexture(0,
1012 g_irrlicht->getTexture(porting::getDataPath("water.png").c_str()));
1014 u16 indices[] = {0,1,2,2,3,0};
1015 // Add to mesh collector
1016 collector.append(material, vertices, 4, indices, 6);
1022 Add stuff from collector to mesh
1025 collector.fillMesh(mesh_new);
1028 Do some stuff to the mesh
1031 mesh_new->recalculateBoundingBox();
1034 Delete new mesh if it is empty
1037 if(mesh_new->getMeshBufferCount() == 0)
1043 // Use VBO for mesh (this just would set this for ever buffer)
1044 // This will lead to infinite memory usage because or irrlicht.
1045 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1047 /*std::cout<<"MapBlock has "<<fastfaces_new.size()<<" faces "
1048 <<"and uses "<<mesh_new->getMeshBufferCount()
1049 <<" materials (meshbuffers)"<<std::endl;*/
1057 //scene::SMesh *mesh_old = mesh[daynight_i];
1058 //mesh[daynight_i] = mesh_new;
1060 scene::SMesh *mesh_old = mesh;
1062 setMeshExpired(false);
1064 if(mesh_old != NULL)
1066 // Remove hardware buffers of meshbuffers of mesh
1067 // NOTE: No way, this runs in a different thread and everything
1068 /*u32 c = mesh_old->getMeshBufferCount();
1069 for(u32 i=0; i<c; i++)
1071 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1074 /*dstream<<"mesh_old->getReferenceCount()="
1075 <<mesh_old->getReferenceCount()<<std::endl;
1076 u32 c = mesh_old->getMeshBufferCount();
1077 for(u32 i=0; i<c; i++)
1079 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1080 dstream<<"buf->getReferenceCount()="
1081 <<buf->getReferenceCount()<<std::endl;
1090 mesh_mutex.Unlock();
1092 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1095 /*void MapBlock::updateMeshes(s32 first_i)
1097 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
1098 updateMesh(first_i);
1099 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
1110 Propagates sunlight down through the block.
1111 Doesn't modify nodes that are not affected by sunlight.
1113 Returns false if sunlight at bottom block is invalid
1114 Returns true if bottom block doesn't exist.
1116 If there is a block above, continues from it.
1117 If there is no block above, assumes there is sunlight, unless
1118 is_underground is set or highest node is water.
1120 At the moment, all sunlighted nodes are added to light_sources.
1121 - SUGG: This could be optimized
1123 Turns sunglighted mud into grass.
1125 if remove_light==true, sets non-sunlighted nodes black.
1127 if black_air_left!=NULL, it is set to true if non-sunlighted
1128 air is left in block.
1130 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1131 bool remove_light, bool *black_air_left,
1134 // Whether the sunlight at the top of the bottom block is valid
1135 bool block_below_is_valid = true;
1137 v3s16 pos_relative = getPosRelative();
1139 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1141 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1144 bool no_sunlight = false;
1145 bool no_top_block = false;
1146 // Check if node above block has sunlight
1148 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1149 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1154 catch(InvalidPositionException &e)
1156 no_top_block = true;
1158 // NOTE: This makes over-ground roofed places sunlighted
1159 // Assume sunlight, unless is_underground==true
1166 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1167 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1172 // NOTE: As of now, this just would make everything dark.
1174 //no_sunlight = true;
1177 #if 0 // Doesn't work; nothing gets light.
1178 bool no_sunlight = true;
1179 bool no_top_block = false;
1180 // Check if node above block has sunlight
1182 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1183 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1185 no_sunlight = false;
1188 catch(InvalidPositionException &e)
1190 no_top_block = true;
1194 /*std::cout<<"("<<x<<","<<z<<"): "
1195 <<"no_top_block="<<no_top_block
1196 <<", is_underground="<<is_underground
1197 <<", no_sunlight="<<no_sunlight
1200 s16 y = MAP_BLOCKSIZE-1;
1202 // This makes difference to diminishing in water.
1203 bool stopped_to_solid_object = false;
1205 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1210 MapNode &n = getNodeRef(pos);
1212 if(current_light == 0)
1216 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1218 // Do nothing: Sunlight is continued
1220 else if(n.light_propagates() == false)
1224 bool upper_is_air = false;
1227 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1228 upper_is_air = true;
1230 catch(InvalidPositionException &e)
1233 // Turn mud into grass
1234 if(upper_is_air && n.d == CONTENT_MUD
1235 && current_light == LIGHT_SUN)
1237 n.d = CONTENT_GRASS;
1241 // A solid object is on the way.
1242 stopped_to_solid_object = true;
1250 current_light = diminish_light(current_light);
1253 u8 old_light = n.getLight(LIGHTBANK_DAY);
1255 if(current_light > old_light || remove_light)
1257 n.setLight(LIGHTBANK_DAY, current_light);
1260 if(diminish_light(current_light) != 0)
1262 light_sources.insert(pos_relative + pos, true);
1265 if(current_light == 0 && stopped_to_solid_object)
1269 *black_air_left = true;
1274 // Whether or not the block below should see LIGHT_SUN
1275 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1278 If the block below hasn't already been marked invalid:
1280 Check if the node below the block has proper sunlight at top.
1281 If not, the block below is invalid.
1283 Ignore non-transparent nodes as they always have no light
1287 if(block_below_is_valid)
1289 MapNode n = getNodeParent(v3s16(x, -1, z));
1290 if(n.light_propagates())
1292 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1293 && sunlight_should_go_down == false)
1294 block_below_is_valid = false;
1295 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1296 && sunlight_should_go_down == true)
1297 block_below_is_valid = false;
1301 catch(InvalidPositionException &e)
1303 /*std::cout<<"InvalidBlockException for bottom block node"
1305 // Just no block below, no need to panic.
1310 return block_below_is_valid;
1313 void MapBlock::copyTo(VoxelManipulator &dst)
1315 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1316 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1318 dst.copyFrom(data, data_area, v3s16(0,0,0),
1319 getPosRelative(), data_size);
1322 /*void getPseudoObjects(v3f origin, f32 max_d,
1323 core::array<DistanceSortedObject> &dest)
1326 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1331 m_objects.step(dtime, server, daynight_ratio);
1334 Spawn some objects at random.
1336 Use dayNightDiffed() to approximate being near ground level
1338 if(m_spawn_timer < -999)
1342 if(dayNightDiffed() == true && getObjectCount() == 0)
1344 m_spawn_timer -= dtime;
1345 if(m_spawn_timer <= 0.0)
1347 m_spawn_timer += myrand() % 300;
1350 (myrand()%(MAP_BLOCKSIZE-1))+0,
1351 (myrand()%(MAP_BLOCKSIZE-1))+0
1354 s16 y = getGroundLevel(p2d);
1358 v3s16 p(p2d.X, y+1, p2d.Y);
1360 if(getNode(p).d == CONTENT_AIR
1361 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1363 RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1374 void MapBlock::updateDayNightDiff()
1378 m_day_night_differs = false;
1382 bool differs = false;
1385 Check if any lighting value differs
1387 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1389 MapNode &n = data[i];
1390 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1398 If some lighting values differ, check if the whole thing is
1399 just air. If it is, differ = false
1403 bool only_air = true;
1404 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1406 MapNode &n = data[i];
1407 if(n.d != CONTENT_AIR)
1417 // Set member variable
1418 m_day_night_differs = differs;
1421 s16 MapBlock::getGroundLevel(v2s16 p2d)
1427 s16 y = MAP_BLOCKSIZE-1;
1430 if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1432 if(y == MAP_BLOCKSIZE-1)
1440 catch(InvalidPositionException &e)
1450 void MapBlock::serialize(std::ostream &os, u8 version)
1452 if(!ser_ver_supported(version))
1453 throw VersionMismatchException("ERROR: MapBlock format not supported");
1457 throw SerializationError("ERROR: Not writing dummy block.");
1460 // These have no compression
1461 if(version <= 3 || version == 5 || version == 6)
1463 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1465 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1466 SharedBuffer<u8> dest(buflen);
1468 dest[0] = is_underground;
1469 for(u32 i=0; i<nodecount; i++)
1471 u32 s = 1 + i * MapNode::serializedLength(version);
1472 data[i].serialize(&dest[s], version);
1475 os.write((char*)*dest, dest.getSize());
1477 else if(version <= 10)
1481 Compress the materials and the params separately.
1485 os.write((char*)&is_underground, 1);
1487 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1489 // Get and compress materials
1490 SharedBuffer<u8> materialdata(nodecount);
1491 for(u32 i=0; i<nodecount; i++)
1493 materialdata[i] = data[i].d;
1495 compress(materialdata, os, version);
1497 // Get and compress lights
1498 SharedBuffer<u8> lightdata(nodecount);
1499 for(u32 i=0; i<nodecount; i++)
1501 lightdata[i] = data[i].param;
1503 compress(lightdata, os, version);
1507 // Get and compress pressure
1508 SharedBuffer<u8> pressuredata(nodecount);
1509 for(u32 i=0; i<nodecount; i++)
1511 pressuredata[i] = data[i].pressure;
1513 compress(pressuredata, os, version);
1516 // All other versions (newest)
1523 if(m_day_night_differs)
1525 os.write((char*)&flags, 1);
1527 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1533 SharedBuffer<u8> databuf(nodecount*3);
1536 for(u32 i=0; i<nodecount; i++)
1538 databuf[i] = data[i].d;
1542 for(u32 i=0; i<nodecount; i++)
1544 databuf[i+nodecount] = data[i].param;
1548 for(u32 i=0; i<nodecount; i++)
1550 databuf[i+nodecount*2] = data[i].pressure;
1554 Compress data to output stream
1557 compress(databuf, os, version);
1561 void MapBlock::deSerialize(std::istream &is, u8 version)
1563 if(!ser_ver_supported(version))
1564 throw VersionMismatchException("ERROR: MapBlock format not supported");
1566 // These have no compression
1567 if(version <= 3 || version == 5 || version == 6)
1569 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1572 if(is.gcount() != 1)
1573 throw SerializationError
1574 ("MapBlock::deSerialize: no enough input data");
1575 is_underground = tmp;
1576 for(u32 i=0; i<nodecount; i++)
1578 s32 len = MapNode::serializedLength(version);
1579 SharedBuffer<u8> d(len);
1580 is.read((char*)*d, len);
1581 if(is.gcount() != len)
1582 throw SerializationError
1583 ("MapBlock::deSerialize: no enough input data");
1584 data[i].deSerialize(*d, version);
1587 else if(version <= 10)
1589 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1592 is.read((char*)&t8, 1);
1593 is_underground = t8;
1596 // Uncompress and set material data
1597 std::ostringstream os(std::ios_base::binary);
1598 decompress(is, os, version);
1599 std::string s = os.str();
1600 if(s.size() != nodecount)
1601 throw SerializationError
1602 ("MapBlock::deSerialize: invalid format");
1603 for(u32 i=0; i<s.size(); i++)
1609 // Uncompress and set param data
1610 std::ostringstream os(std::ios_base::binary);
1611 decompress(is, os, version);
1612 std::string s = os.str();
1613 if(s.size() != nodecount)
1614 throw SerializationError
1615 ("MapBlock::deSerialize: invalid format");
1616 for(u32 i=0; i<s.size(); i++)
1618 data[i].param = s[i];
1624 // Uncompress and set pressure data
1625 std::ostringstream os(std::ios_base::binary);
1626 decompress(is, os, version);
1627 std::string s = os.str();
1628 if(s.size() != nodecount)
1629 throw SerializationError
1630 ("MapBlock::deSerialize: invalid format");
1631 for(u32 i=0; i<s.size(); i++)
1633 data[i].pressure = s[i];
1637 // All other versions (newest)
1640 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1643 is.read((char*)&flags, 1);
1644 is_underground = (flags & 1) ? true : false;
1645 m_day_night_differs = (flags & 2) ? true : false;
1648 std::ostringstream os(std::ios_base::binary);
1649 decompress(is, os, version);
1650 std::string s = os.str();
1651 if(s.size() != nodecount*3)
1652 throw SerializationError
1653 ("MapBlock::deSerialize: invalid format");
1656 for(u32 i=0; i<nodecount; i++)
1661 for(u32 i=0; i<nodecount; i++)
1663 data[i].param = s[i+nodecount];
1666 for(u32 i=0; i<nodecount; i++)
1668 data[i].pressure = s[i+nodecount*2];