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.
22 // For g_settings and g_irrlicht
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);
266 //u8 li = 255; //DEBUG
268 u8 alpha = tile.alpha;
270 if(tile.id == TILE_WATER)
271 alpha = WATER_ALPHA;*/
273 video::SColor c = video::SColor(alpha,li,li,li);
275 face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
276 core::vector2d<f32>(0,1));
277 face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
278 core::vector2d<f32>(abs_scale,1));
279 face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
280 core::vector2d<f32>(abs_scale,0));
281 face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
282 core::vector2d<f32>(0,0));
286 //f->tile = TILE_STONE;
288 dest.push_back(face);
293 Gets node tile from any place relative to block.
294 Returns TILE_NODE if doesn't exist or should not be drawn.
296 TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
297 NodeModMap &temp_mods)
300 spec = mn.getTile(face_dir);
303 Check temporary modifications on this node
305 /*core::map<v3s16, NodeMod>::Node *n;
306 n = m_temp_mods.find(p);
310 struct NodeMod mod = n->getValue();*/
312 if(temp_mods.get(p, &mod))
314 if(mod.type == NODEMOD_CHANGECONTENT)
316 MapNode mn2(mod.param);
317 spec = mn2.getTile(face_dir);
319 if(mod.type == NODEMOD_CRACK)
321 std::ostringstream os;
322 os<<"[crack"<<mod.param;
324 textureid_t tid = g_irrlicht->getTextureId(os.str());
325 spec.spec.addTid(tid);
332 u8 MapBlock::getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
335 Check temporary modifications on this node
337 /*core::map<v3s16, NodeMod>::Node *n;
338 n = m_temp_mods.find(p);
342 struct NodeMod mod = n->getValue();*/
344 if(temp_mods.get(p, &mod))
346 if(mod.type == NODEMOD_CHANGECONTENT)
351 if(mod.type == NODEMOD_CRACK)
354 Content doesn't change.
356 face_contents works just like it should, because
357 there should not be faces between differently cracked
360 If a semi-transparent node is cracked in front an
361 another one, it really doesn't matter whether there
362 is a cracked face drawn in between or not.
372 translate_dir: unit vector with only one of x, y or z
373 face_dir: unit vector with only one of x, y or z
375 void MapBlock::updateFastFaceRow(
384 core::array<FastFace> &dest,
385 NodeModMap &temp_mods)
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, temp_mods);
397 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
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, temp_mods);
416 tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
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, temp_mods);
437 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
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 Avoid interlocks by copying m_temp_mods
606 NodeModMap temp_mods;
608 JMutexAutoLock lock(m_temp_mods_mutex);
609 m_temp_mods.copy(temp_mods);
612 bool new_style_water = g_settings.getBool("new_style_water");
613 float node_water_level = 1.0;
615 node_water_level = 0.9;
618 We are including the faces of the trailing edges of the block.
619 This means that when something changes, the caller must
620 also update the meshes of the blocks at the leading edges.
622 NOTE: This is the slowest part of this method.
626 // 4-23ms for MAP_BLOCKSIZE=16
627 //TimeTaker timer2("updateMesh() collect");
630 Go through every y,z and get top faces in rows of x+
632 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
633 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
634 updateFastFaceRow(daynight_ratio, posRelative_f,
635 v3s16(0,y,z), MAP_BLOCKSIZE,
638 v3s16(0,1,0), //face dir
645 Go through every x,y and get right faces in rows of z+
647 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
648 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
649 updateFastFaceRow(daynight_ratio, posRelative_f,
650 v3s16(x,y,0), MAP_BLOCKSIZE,
660 Go through every y,z and get back faces in rows of x+
662 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
663 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
664 updateFastFaceRow(daynight_ratio, posRelative_f,
665 v3s16(0,y,z), MAP_BLOCKSIZE,
679 Convert FastFaces to SMesh
682 scene::SMesh *mesh_new = NULL;
684 mesh_new = new scene::SMesh();
686 MeshCollector collector;
688 if(fastfaces_new.size() > 0)
690 // avg 0ms (100ms spikes when loading textures the first time)
691 //TimeTaker timer2("updateMesh() mesh building");
693 video::SMaterial material;
694 material.Lighting = false;
695 material.BackfaceCulling = false;
696 material.setFlag(video::EMF_BILINEAR_FILTER, false);
697 material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
698 material.setFlag(video::EMF_FOG_ENABLE, true);
700 for(u32 i=0; i<fastfaces_new.size(); i++)
702 FastFace &f = fastfaces_new[i];
704 const u16 indices[] = {0,1,2,2,3,0};
706 video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
710 material.setTexture(0, texture);
711 if(f.tile.alpha != 255)
712 material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
714 material.MaterialType = video::EMT_SOLID;
716 collector.append(material, f.vertices, 4, indices, 6);
721 Add special graphics:
727 //TimeTaker timer2("updateMesh() adding special stuff");
729 // Flowing water material
730 video::SMaterial material_w1;
731 material_w1.setFlag(video::EMF_LIGHTING, false);
732 material_w1.setFlag(video::EMF_BACK_FACE_CULLING, false);
733 material_w1.setFlag(video::EMF_BILINEAR_FILTER, false);
734 material_w1.setFlag(video::EMF_FOG_ENABLE, true);
735 material_w1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
736 material_w1.setTexture(0,
737 g_irrlicht->getTexture("water.png"));
739 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
740 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
741 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
745 MapNode &n = getNodeRef(x,y,z);
750 if(n.d == CONTENT_TORCH)
752 video::SColor c(255,255,255,255);
754 video::S3DVertex vertices[4] =
756 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
757 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
758 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
759 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
762 v3s16 dir = unpackDir(n.dir);
764 for(s32 i=0; i<4; i++)
766 if(dir == v3s16(1,0,0))
767 vertices[i].Pos.rotateXZBy(0);
768 if(dir == v3s16(-1,0,0))
769 vertices[i].Pos.rotateXZBy(180);
770 if(dir == v3s16(0,0,1))
771 vertices[i].Pos.rotateXZBy(90);
772 if(dir == v3s16(0,0,-1))
773 vertices[i].Pos.rotateXZBy(-90);
774 if(dir == v3s16(0,-1,0))
775 vertices[i].Pos.rotateXZBy(45);
776 if(dir == v3s16(0,1,0))
777 vertices[i].Pos.rotateXZBy(-45);
779 vertices[i].Pos += intToFloat(p + getPosRelative());
783 video::SMaterial material;
784 material.setFlag(video::EMF_LIGHTING, false);
785 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
786 material.setFlag(video::EMF_BILINEAR_FILTER, false);
787 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
788 material.MaterialType
789 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
790 if(dir == v3s16(0,-1,0))
791 material.setTexture(0,
792 g_irrlicht->getTexture("torch_on_floor.png"));
793 else if(dir == v3s16(0,1,0))
794 material.setTexture(0,
795 g_irrlicht->getTexture("torch_on_ceiling.png"));
796 // For backwards compatibility
797 else if(dir == v3s16(0,0,0))
798 material.setTexture(0,
799 g_irrlicht->getTexture("torch_on_floor.png"));
801 material.setTexture(0,
802 g_irrlicht->getTexture("torch.png"));
804 u16 indices[] = {0,1,2,2,3,0};
805 // Add to mesh collector
806 collector.append(material, vertices, 4, indices, 6);
809 Add flowing water to mesh
811 else if(n.d == CONTENT_WATER)
813 bool top_is_water = false;
815 MapNode n = getNodeParent(v3s16(x,y+1,z));
816 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
818 }catch(InvalidPositionException &e){}
820 u8 l = decode_light(n.getLightBlend(daynight_ratio));
821 video::SColor c(WATER_ALPHA,l,l,l);
823 // Neighbor water levels (key = relative position)
824 // Includes current node
825 core::map<v3s16, f32> neighbor_levels;
826 core::map<v3s16, u8> neighbor_contents;
827 core::map<v3s16, u8> neighbor_flags;
828 const u8 neighborflag_top_is_water = 0x01;
829 v3s16 neighbor_dirs[9] = {
840 for(u32 i=0; i<9; i++)
842 u8 content = CONTENT_AIR;
843 float level = -0.5 * BS;
847 v3s16 p2 = p + neighbor_dirs[i];
848 MapNode n2 = getNodeParent(p2);
852 if(n2.d == CONTENT_WATERSOURCE)
853 level = (-0.5+node_water_level) * BS;
854 else if(n2.d == CONTENT_WATER)
855 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
856 * node_water_level) * BS;
858 // Check node above neighbor.
859 // NOTE: This doesn't get executed if neighbor
862 n2 = getNodeParent(p2);
863 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
864 flags |= neighborflag_top_is_water;
866 catch(InvalidPositionException &e){}
868 neighbor_levels.insert(neighbor_dirs[i], level);
869 neighbor_contents.insert(neighbor_dirs[i], content);
870 neighbor_flags.insert(neighbor_dirs[i], flags);
873 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
874 //float water_level = neighbor_levels[v3s16(0,0,0)];
876 // Corner heights (average between four waters)
877 f32 corner_levels[4];
879 v3s16 halfdirs[4] = {
885 for(u32 i=0; i<4; i++)
887 v3s16 cornerdir = halfdirs[i];
888 float cornerlevel = 0;
890 for(u32 j=0; j<4; j++)
892 v3s16 neighbordir = cornerdir - halfdirs[j];
893 u8 content = neighbor_contents[neighbordir];
894 // Special case for source nodes
895 if(content == CONTENT_WATERSOURCE)
897 cornerlevel = (-0.5+node_water_level)*BS;
901 else if(content == CONTENT_WATER)
903 cornerlevel += neighbor_levels[neighbordir];
906 else if(content == CONTENT_AIR)
908 cornerlevel += -0.5*BS;
913 cornerlevel /= valid_count;
914 corner_levels[i] = cornerlevel;
921 v3s16 side_dirs[4] = {
927 s16 side_corners[4][2] = {
933 for(u32 i=0; i<4; i++)
935 v3s16 dir = side_dirs[i];
938 If our topside is water and neighbor's topside
939 is water, don't draw side face
942 neighbor_flags[dir] & neighborflag_top_is_water)
945 u8 neighbor_content = neighbor_contents[dir];
947 // Don't draw face if neighbor is not air or water
948 if(neighbor_content != CONTENT_AIR
949 && neighbor_content != CONTENT_WATER)
952 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
954 // Don't draw any faces if neighbor is water and top is water
955 if(neighbor_is_water == true && top_is_water == false)
958 video::S3DVertex vertices[4] =
960 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
961 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
962 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
963 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
964 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
965 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
966 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
967 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
971 If our topside is water, set upper border of face
972 at upper border of node
976 vertices[2].Pos.Y = 0.5*BS;
977 vertices[3].Pos.Y = 0.5*BS;
980 Otherwise upper position of face is corner levels
984 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
985 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
989 If neighbor is water, lower border of face is corner
992 if(neighbor_is_water)
994 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
995 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
998 If neighbor is not water, lower border of face is
1003 vertices[0].Pos.Y = -0.5*BS;
1004 vertices[1].Pos.Y = -0.5*BS;
1007 for(s32 j=0; j<4; j++)
1009 if(dir == v3s16(0,0,1))
1010 vertices[j].Pos.rotateXZBy(0);
1011 if(dir == v3s16(0,0,-1))
1012 vertices[j].Pos.rotateXZBy(180);
1013 if(dir == v3s16(-1,0,0))
1014 vertices[j].Pos.rotateXZBy(90);
1015 if(dir == v3s16(1,0,-0))
1016 vertices[j].Pos.rotateXZBy(-90);
1018 vertices[j].Pos += intToFloat(p + getPosRelative());
1021 u16 indices[] = {0,1,2,2,3,0};
1022 // Add to mesh collector
1023 collector.append(material_w1, vertices, 4, indices, 6);
1027 Generate top side, if appropriate
1030 if(top_is_water == false)
1032 video::S3DVertex vertices[4] =
1034 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1035 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1036 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1037 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
1040 for(s32 i=0; i<4; i++)
1042 //vertices[i].Pos.Y += water_level;
1043 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1044 vertices[i].Pos.Y += corner_levels[i];
1045 vertices[i].Pos += intToFloat(p + getPosRelative());
1048 u16 indices[] = {0,1,2,2,3,0};
1049 // Add to mesh collector
1050 collector.append(material_w1, vertices, 4, indices, 6);
1054 Add water sources to mesh
1056 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1058 //bool top_is_water = false;
1059 bool top_is_air = false;
1061 MapNode n = getNodeParent(v3s16(x,y+1,z));
1062 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1063 top_is_water = true;*/
1064 if(n.d == CONTENT_AIR)
1066 }catch(InvalidPositionException &e){}
1068 /*if(top_is_water == true)
1070 if(top_is_air == false)
1073 u8 l = decode_light(n.getLightBlend(daynight_ratio));
1074 video::SColor c(WATER_ALPHA,l,l,l);
1076 video::S3DVertex vertices[4] =
1078 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1079 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1080 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1081 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
1084 for(s32 i=0; i<4; i++)
1086 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1087 vertices[i].Pos += intToFloat(p + getPosRelative());
1090 u16 indices[] = {0,1,2,2,3,0};
1091 // Add to mesh collector
1092 collector.append(material_w1, vertices, 4, indices, 6);
1097 Add stuff from collector to mesh
1100 collector.fillMesh(mesh_new);
1103 Do some stuff to the mesh
1106 mesh_new->recalculateBoundingBox();
1109 Delete new mesh if it is empty
1112 if(mesh_new->getMeshBufferCount() == 0)
1118 // Use VBO for mesh (this just would set this for ever buffer)
1119 // This will lead to infinite memory usage because or irrlicht.
1120 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1122 /*std::cout<<"MapBlock has "<<fastfaces_new.size()<<" faces "
1123 <<"and uses "<<mesh_new->getMeshBufferCount()
1124 <<" materials (meshbuffers)"<<std::endl;*/
1132 //scene::SMesh *mesh_old = mesh[daynight_i];
1133 //mesh[daynight_i] = mesh_new;
1135 scene::SMesh *mesh_old = mesh;
1137 setMeshExpired(false);
1139 if(mesh_old != NULL)
1141 // Remove hardware buffers of meshbuffers of mesh
1142 // NOTE: No way, this runs in a different thread and everything
1143 /*u32 c = mesh_old->getMeshBufferCount();
1144 for(u32 i=0; i<c; i++)
1146 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1149 /*dstream<<"mesh_old->getReferenceCount()="
1150 <<mesh_old->getReferenceCount()<<std::endl;
1151 u32 c = mesh_old->getMeshBufferCount();
1152 for(u32 i=0; i<c; i++)
1154 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1155 dstream<<"buf->getReferenceCount()="
1156 <<buf->getReferenceCount()<<std::endl;
1165 mesh_mutex.Unlock();
1167 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1170 /*void MapBlock::updateMeshes(s32 first_i)
1172 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
1173 updateMesh(first_i);
1174 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
1185 Propagates sunlight down through the block.
1186 Doesn't modify nodes that are not affected by sunlight.
1188 Returns false if sunlight at bottom block is invalid
1189 Returns true if bottom block doesn't exist.
1191 If there is a block above, continues from it.
1192 If there is no block above, assumes there is sunlight, unless
1193 is_underground is set or highest node is water.
1195 At the moment, all sunlighted nodes are added to light_sources.
1196 - SUGG: This could be optimized
1198 Turns sunglighted mud into grass.
1200 if remove_light==true, sets non-sunlighted nodes black.
1202 if black_air_left!=NULL, it is set to true if non-sunlighted
1203 air is left in block.
1205 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1206 bool remove_light, bool *black_air_left,
1209 // Whether the sunlight at the top of the bottom block is valid
1210 bool block_below_is_valid = true;
1212 v3s16 pos_relative = getPosRelative();
1214 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1216 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1219 bool no_sunlight = false;
1220 bool no_top_block = false;
1221 // Check if node above block has sunlight
1223 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1224 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1229 catch(InvalidPositionException &e)
1231 no_top_block = true;
1233 // NOTE: This makes over-ground roofed places sunlighted
1234 // Assume sunlight, unless is_underground==true
1241 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1242 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1247 // NOTE: As of now, this just would make everything dark.
1249 //no_sunlight = true;
1252 #if 0 // Doesn't work; nothing gets light.
1253 bool no_sunlight = true;
1254 bool no_top_block = false;
1255 // Check if node above block has sunlight
1257 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1258 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1260 no_sunlight = false;
1263 catch(InvalidPositionException &e)
1265 no_top_block = true;
1269 /*std::cout<<"("<<x<<","<<z<<"): "
1270 <<"no_top_block="<<no_top_block
1271 <<", is_underground="<<is_underground
1272 <<", no_sunlight="<<no_sunlight
1275 s16 y = MAP_BLOCKSIZE-1;
1277 // This makes difference to diminishing in water.
1278 bool stopped_to_solid_object = false;
1280 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1285 MapNode &n = getNodeRef(pos);
1287 if(current_light == 0)
1291 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1293 // Do nothing: Sunlight is continued
1295 else if(n.light_propagates() == false)
1299 bool upper_is_air = false;
1302 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1303 upper_is_air = true;
1305 catch(InvalidPositionException &e)
1308 // Turn mud into grass
1309 if(upper_is_air && n.d == CONTENT_MUD
1310 && current_light == LIGHT_SUN)
1312 n.d = CONTENT_GRASS;
1316 // A solid object is on the way.
1317 stopped_to_solid_object = true;
1325 current_light = diminish_light(current_light);
1328 u8 old_light = n.getLight(LIGHTBANK_DAY);
1330 if(current_light > old_light || remove_light)
1332 n.setLight(LIGHTBANK_DAY, current_light);
1335 if(diminish_light(current_light) != 0)
1337 light_sources.insert(pos_relative + pos, true);
1340 if(current_light == 0 && stopped_to_solid_object)
1344 *black_air_left = true;
1349 // Whether or not the block below should see LIGHT_SUN
1350 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1353 If the block below hasn't already been marked invalid:
1355 Check if the node below the block has proper sunlight at top.
1356 If not, the block below is invalid.
1358 Ignore non-transparent nodes as they always have no light
1362 if(block_below_is_valid)
1364 MapNode n = getNodeParent(v3s16(x, -1, z));
1365 if(n.light_propagates())
1367 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1368 && sunlight_should_go_down == false)
1369 block_below_is_valid = false;
1370 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1371 && sunlight_should_go_down == true)
1372 block_below_is_valid = false;
1376 catch(InvalidPositionException &e)
1378 /*std::cout<<"InvalidBlockException for bottom block node"
1380 // Just no block below, no need to panic.
1385 return block_below_is_valid;
1388 void MapBlock::copyTo(VoxelManipulator &dst)
1390 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1391 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1393 // Copy from data to VoxelManipulator
1394 dst.copyFrom(data, data_area, v3s16(0,0,0),
1395 getPosRelative(), data_size);
1398 void MapBlock::copyFrom(VoxelManipulator &dst)
1400 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1401 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1403 // Copy from VoxelManipulator to data
1404 dst.copyTo(data, data_area, v3s16(0,0,0),
1405 getPosRelative(), data_size);
1408 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1413 m_objects.step(dtime, server, daynight_ratio);
1416 Spawn some objects at random.
1418 Use dayNightDiffed() to approximate being near ground level
1420 if(m_spawn_timer < -999)
1424 if(dayNightDiffed() == true && getObjectCount() == 0)
1426 m_spawn_timer -= dtime;
1427 if(m_spawn_timer <= 0.0)
1429 m_spawn_timer += myrand() % 300;
1432 (myrand()%(MAP_BLOCKSIZE-1))+0,
1433 (myrand()%(MAP_BLOCKSIZE-1))+0
1436 s16 y = getGroundLevel(p2d);
1440 v3s16 p(p2d.X, y+1, p2d.Y);
1442 if(getNode(p).d == CONTENT_AIR
1443 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1445 RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1456 void MapBlock::updateDayNightDiff()
1460 m_day_night_differs = false;
1464 bool differs = false;
1467 Check if any lighting value differs
1469 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1471 MapNode &n = data[i];
1472 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1480 If some lighting values differ, check if the whole thing is
1481 just air. If it is, differ = false
1485 bool only_air = true;
1486 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1488 MapNode &n = data[i];
1489 if(n.d != CONTENT_AIR)
1499 // Set member variable
1500 m_day_night_differs = differs;
1503 s16 MapBlock::getGroundLevel(v2s16 p2d)
1509 s16 y = MAP_BLOCKSIZE-1;
1512 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1513 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1515 if(y == MAP_BLOCKSIZE-1)
1523 catch(InvalidPositionException &e)
1533 void MapBlock::serialize(std::ostream &os, u8 version)
1535 if(!ser_ver_supported(version))
1536 throw VersionMismatchException("ERROR: MapBlock format not supported");
1540 throw SerializationError("ERROR: Not writing dummy block.");
1543 // These have no compression
1544 if(version <= 3 || version == 5 || version == 6)
1546 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1548 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1549 SharedBuffer<u8> dest(buflen);
1551 dest[0] = is_underground;
1552 for(u32 i=0; i<nodecount; i++)
1554 u32 s = 1 + i * MapNode::serializedLength(version);
1555 data[i].serialize(&dest[s], version);
1558 os.write((char*)*dest, dest.getSize());
1560 else if(version <= 10)
1564 Compress the materials and the params separately.
1568 os.write((char*)&is_underground, 1);
1570 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1572 // Get and compress materials
1573 SharedBuffer<u8> materialdata(nodecount);
1574 for(u32 i=0; i<nodecount; i++)
1576 materialdata[i] = data[i].d;
1578 compress(materialdata, os, version);
1580 // Get and compress lights
1581 SharedBuffer<u8> lightdata(nodecount);
1582 for(u32 i=0; i<nodecount; i++)
1584 lightdata[i] = data[i].param;
1586 compress(lightdata, os, version);
1590 // Get and compress param2
1591 SharedBuffer<u8> param2data(nodecount);
1592 for(u32 i=0; i<nodecount; i++)
1594 param2data[i] = data[i].param2;
1596 compress(param2data, os, version);
1599 // All other versions (newest)
1606 if(m_day_night_differs)
1608 if(m_lighting_expired)
1610 os.write((char*)&flags, 1);
1612 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1618 SharedBuffer<u8> databuf(nodecount*3);
1621 for(u32 i=0; i<nodecount; i++)
1623 databuf[i] = data[i].d;
1627 for(u32 i=0; i<nodecount; i++)
1629 databuf[i+nodecount] = data[i].param;
1633 for(u32 i=0; i<nodecount; i++)
1635 databuf[i+nodecount*2] = data[i].param2;
1639 Compress data to output stream
1642 compress(databuf, os, version);
1646 void MapBlock::deSerialize(std::istream &is, u8 version)
1648 if(!ser_ver_supported(version))
1649 throw VersionMismatchException("ERROR: MapBlock format not supported");
1651 // These have no compression
1652 if(version <= 3 || version == 5 || version == 6)
1654 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1657 if(is.gcount() != 1)
1658 throw SerializationError
1659 ("MapBlock::deSerialize: no enough input data");
1660 is_underground = tmp;
1661 for(u32 i=0; i<nodecount; i++)
1663 s32 len = MapNode::serializedLength(version);
1664 SharedBuffer<u8> d(len);
1665 is.read((char*)*d, len);
1666 if(is.gcount() != len)
1667 throw SerializationError
1668 ("MapBlock::deSerialize: no enough input data");
1669 data[i].deSerialize(*d, version);
1672 else if(version <= 10)
1674 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1677 is.read((char*)&t8, 1);
1678 is_underground = t8;
1681 // Uncompress and set material data
1682 std::ostringstream os(std::ios_base::binary);
1683 decompress(is, os, version);
1684 std::string s = os.str();
1685 if(s.size() != nodecount)
1686 throw SerializationError
1687 ("MapBlock::deSerialize: invalid format");
1688 for(u32 i=0; i<s.size(); i++)
1694 // Uncompress and set param data
1695 std::ostringstream os(std::ios_base::binary);
1696 decompress(is, os, version);
1697 std::string s = os.str();
1698 if(s.size() != nodecount)
1699 throw SerializationError
1700 ("MapBlock::deSerialize: invalid format");
1701 for(u32 i=0; i<s.size(); i++)
1703 data[i].param = s[i];
1709 // Uncompress and set param2 data
1710 std::ostringstream os(std::ios_base::binary);
1711 decompress(is, os, version);
1712 std::string s = os.str();
1713 if(s.size() != nodecount)
1714 throw SerializationError
1715 ("MapBlock::deSerialize: invalid format");
1716 for(u32 i=0; i<s.size(); i++)
1718 data[i].param2 = s[i];
1722 // All other versions (newest)
1725 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1728 is.read((char*)&flags, 1);
1729 is_underground = (flags & 1) ? true : false;
1730 m_day_night_differs = (flags & 2) ? true : false;
1731 m_lighting_expired = (flags & 3) ? true : false;
1734 std::ostringstream os(std::ios_base::binary);
1735 decompress(is, os, version);
1736 std::string s = os.str();
1737 if(s.size() != nodecount*3)
1738 throw SerializationError
1739 ("MapBlock::deSerialize: invalid format");
1742 for(u32 i=0; i<nodecount; i++)
1747 for(u32 i=0; i<nodecount; i++)
1749 data[i].param = s[i+nodecount];
1752 for(u32 i=0; i<nodecount; i++)
1754 data[i].param2 = s[i+nodecount*2];
1759 Translate nodes as specified in the translate_to fields of
1762 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1764 MapNode &n = data[i];
1766 MapNode *translate_to = content_features(n.d).translate_to;
1769 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
1770 <<translate_to->d<<std::endl;