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);
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
605 Avoid interlocks by copying m_temp_mods
607 NodeModMap temp_mods;
609 JMutexAutoLock lock(m_temp_mods_mutex);
610 m_temp_mods.copy(temp_mods);
614 We are including the faces of the trailing edges of the block.
615 This means that when something changes, the caller must
616 also update the meshes of the blocks at the leading edges.
618 NOTE: This is the slowest part of this method.
622 // 4-23ms for MAP_BLOCKSIZE=16
623 //TimeTaker timer2("updateMesh() collect");
626 Go through every y,z and get top faces in rows of x+
628 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
629 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
630 updateFastFaceRow(daynight_ratio, posRelative_f,
631 v3s16(0,y,z), MAP_BLOCKSIZE,
634 v3s16(0,1,0), //face dir
641 Go through every x,y and get right faces in rows of z+
643 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
644 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
645 updateFastFaceRow(daynight_ratio, posRelative_f,
646 v3s16(x,y,0), MAP_BLOCKSIZE,
656 Go through every y,z and get back faces in rows of x+
658 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
659 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
660 updateFastFaceRow(daynight_ratio, posRelative_f,
661 v3s16(0,y,z), MAP_BLOCKSIZE,
675 Convert FastFaces to SMesh
678 scene::SMesh *mesh_new = NULL;
680 mesh_new = new scene::SMesh();
682 MeshCollector collector;
684 if(fastfaces_new.size() > 0)
686 // avg 0ms (100ms spikes when loading textures the first time)
687 //TimeTaker timer2("updateMesh() mesh building");
689 video::SMaterial material;
690 material.Lighting = false;
691 material.BackfaceCulling = false;
692 material.setFlag(video::EMF_BILINEAR_FILTER, false);
693 material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
694 material.setFlag(video::EMF_FOG_ENABLE, true);
696 for(u32 i=0; i<fastfaces_new.size(); i++)
698 FastFace &f = fastfaces_new[i];
700 const u16 indices[] = {0,1,2,2,3,0};
702 video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
706 material.setTexture(0, texture);
707 if(f.tile.alpha != 255)
708 material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
710 material.MaterialType = video::EMT_SOLID;
712 collector.append(material, f.vertices, 4, indices, 6);
717 Add special graphics:
723 //TimeTaker timer2("updateMesh() adding special stuff");
725 // Flowing water material
726 video::SMaterial material_w1;
727 material_w1.setFlag(video::EMF_LIGHTING, false);
728 material_w1.setFlag(video::EMF_BACK_FACE_CULLING, false);
729 material_w1.setFlag(video::EMF_BILINEAR_FILTER, false);
730 material_w1.setFlag(video::EMF_FOG_ENABLE, true);
731 material_w1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
732 material_w1.setTexture(0,
733 g_irrlicht->getTexture("water.png"));
735 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
736 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
737 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
741 MapNode &n = getNodeRef(x,y,z);
746 if(n.d == CONTENT_TORCH)
748 video::SColor c(255,255,255,255);
750 video::S3DVertex vertices[4] =
752 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
753 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
754 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
755 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
758 v3s16 dir = unpackDir(n.dir);
760 for(s32 i=0; i<4; i++)
762 if(dir == v3s16(1,0,0))
763 vertices[i].Pos.rotateXZBy(0);
764 if(dir == v3s16(-1,0,0))
765 vertices[i].Pos.rotateXZBy(180);
766 if(dir == v3s16(0,0,1))
767 vertices[i].Pos.rotateXZBy(90);
768 if(dir == v3s16(0,0,-1))
769 vertices[i].Pos.rotateXZBy(-90);
770 if(dir == v3s16(0,-1,0))
771 vertices[i].Pos.rotateXZBy(45);
772 if(dir == v3s16(0,1,0))
773 vertices[i].Pos.rotateXZBy(-45);
775 vertices[i].Pos += intToFloat(p + getPosRelative());
779 video::SMaterial material;
780 material.setFlag(video::EMF_LIGHTING, false);
781 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
782 material.setFlag(video::EMF_BILINEAR_FILTER, false);
783 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
784 material.MaterialType
785 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
786 if(dir == v3s16(0,-1,0))
787 material.setTexture(0,
788 g_irrlicht->getTexture("torch_on_floor.png"));
789 else if(dir == v3s16(0,1,0))
790 material.setTexture(0,
791 g_irrlicht->getTexture("torch_on_ceiling.png"));
792 // For backwards compatibility
793 else if(dir == v3s16(0,0,0))
794 material.setTexture(0,
795 g_irrlicht->getTexture("torch_on_floor.png"));
797 material.setTexture(0,
798 g_irrlicht->getTexture("torch.png"));
800 u16 indices[] = {0,1,2,2,3,0};
801 // Add to mesh collector
802 collector.append(material, vertices, 4, indices, 6);
805 Add flowing water to mesh
807 else if(n.d == CONTENT_WATER)
809 bool top_is_water = false;
811 MapNode n = getNodeParent(v3s16(x,y+1,z));
812 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
814 }catch(InvalidPositionException &e){}
816 u8 l = decode_light(n.getLightBlend(daynight_ratio));
817 video::SColor c(WATER_ALPHA,l,l,l);
819 // Neighbor water levels (key = relative position)
820 // Includes current node
821 core::map<v3s16, f32> neighbor_levels;
822 core::map<v3s16, u8> neighbor_contents;
823 v3s16 neighbor_dirs[9] = {
834 for(u32 i=0; i<9; i++)
836 u8 content = CONTENT_AIR;
837 float level = -0.5 * BS;
839 v3s16 p2 = p + neighbor_dirs[i];
840 MapNode n2 = getNodeParent(p2);
844 if(n2.d == CONTENT_WATERSOURCE)
846 else if(n2.d == CONTENT_WATER)
847 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0) * BS;
849 catch(InvalidPositionException &e){}
851 neighbor_levels.insert(neighbor_dirs[i], level);
852 neighbor_contents.insert(neighbor_dirs[i], content);
855 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
856 //float water_level = neighbor_levels[v3s16(0,0,0)];
858 // Corner heights (average between four waters)
859 f32 corner_levels[4];
861 v3s16 halfdirs[4] = {
867 for(u32 i=0; i<4; i++)
869 v3s16 cornerdir = halfdirs[i];
870 float cornerlevel = 0;
872 for(u32 j=0; j<4; j++)
874 v3s16 neighbordir = cornerdir - halfdirs[j];
875 u8 content = neighbor_contents[neighbordir];
876 // Special case for source nodes
877 if(content == CONTENT_WATERSOURCE)
879 cornerlevel = 0.5*BS;
883 else if(content == CONTENT_WATER)
885 cornerlevel += neighbor_levels[neighbordir];
888 else if(content == CONTENT_AIR)
890 cornerlevel += -0.5*BS;
895 cornerlevel /= valid_count;
896 corner_levels[i] = cornerlevel;
903 v3s16 side_dirs[4] = {
909 s16 side_corners[4][2] = {
915 for(u32 i=0; i<4; i++)
917 v3s16 dir = side_dirs[i];
919 //float neighbor_level = neighbor_levels[dir];
920 /*if(neighbor_level > -0.5*BS + 0.001)
922 /*if(neighbor_level > water_level - 0.1*BS)
925 u8 neighbor_content = neighbor_contents[dir];
927 if(neighbor_content != CONTENT_AIR
928 && neighbor_content != CONTENT_WATER)
931 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
933 if(neighbor_is_water == true && top_is_water == false)
936 video::S3DVertex vertices[4] =
938 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
939 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
940 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
941 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
942 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
943 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
944 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
945 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
950 vertices[2].Pos.Y = 0.5*BS;
951 vertices[3].Pos.Y = 0.5*BS;
955 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
956 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
959 if(neighbor_is_water)
961 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
962 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
966 vertices[0].Pos.Y = -0.5*BS;
967 vertices[1].Pos.Y = -0.5*BS;
970 for(s32 j=0; j<4; j++)
972 if(dir == v3s16(0,0,1))
973 vertices[j].Pos.rotateXZBy(0);
974 if(dir == v3s16(0,0,-1))
975 vertices[j].Pos.rotateXZBy(180);
976 if(dir == v3s16(-1,0,0))
977 vertices[j].Pos.rotateXZBy(90);
978 if(dir == v3s16(1,0,-0))
979 vertices[j].Pos.rotateXZBy(-90);
981 vertices[j].Pos += intToFloat(p + getPosRelative());
984 u16 indices[] = {0,1,2,2,3,0};
985 // Add to mesh collector
986 collector.append(material_w1, vertices, 4, indices, 6);
990 Generate top side, if appropriate
993 if(top_is_water == false)
995 video::S3DVertex vertices[4] =
997 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
998 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
999 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1000 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
1003 for(s32 i=0; i<4; i++)
1005 //vertices[i].Pos.Y += water_level;
1006 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1007 vertices[i].Pos.Y += corner_levels[i];
1008 vertices[i].Pos += intToFloat(p + getPosRelative());
1011 u16 indices[] = {0,1,2,2,3,0};
1012 // Add to mesh collector
1013 collector.append(material_w1, vertices, 4, indices, 6);
1019 Add stuff from collector to mesh
1022 collector.fillMesh(mesh_new);
1025 Do some stuff to the mesh
1028 mesh_new->recalculateBoundingBox();
1031 Delete new mesh if it is empty
1034 if(mesh_new->getMeshBufferCount() == 0)
1040 // Use VBO for mesh (this just would set this for ever buffer)
1041 // This will lead to infinite memory usage because or irrlicht.
1042 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1044 /*std::cout<<"MapBlock has "<<fastfaces_new.size()<<" faces "
1045 <<"and uses "<<mesh_new->getMeshBufferCount()
1046 <<" materials (meshbuffers)"<<std::endl;*/
1054 //scene::SMesh *mesh_old = mesh[daynight_i];
1055 //mesh[daynight_i] = mesh_new;
1057 scene::SMesh *mesh_old = mesh;
1059 setMeshExpired(false);
1061 if(mesh_old != NULL)
1063 // Remove hardware buffers of meshbuffers of mesh
1064 // NOTE: No way, this runs in a different thread and everything
1065 /*u32 c = mesh_old->getMeshBufferCount();
1066 for(u32 i=0; i<c; i++)
1068 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1071 /*dstream<<"mesh_old->getReferenceCount()="
1072 <<mesh_old->getReferenceCount()<<std::endl;
1073 u32 c = mesh_old->getMeshBufferCount();
1074 for(u32 i=0; i<c; i++)
1076 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1077 dstream<<"buf->getReferenceCount()="
1078 <<buf->getReferenceCount()<<std::endl;
1087 mesh_mutex.Unlock();
1089 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1092 /*void MapBlock::updateMeshes(s32 first_i)
1094 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
1095 updateMesh(first_i);
1096 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
1107 Propagates sunlight down through the block.
1108 Doesn't modify nodes that are not affected by sunlight.
1110 Returns false if sunlight at bottom block is invalid
1111 Returns true if bottom block doesn't exist.
1113 If there is a block above, continues from it.
1114 If there is no block above, assumes there is sunlight, unless
1115 is_underground is set or highest node is water.
1117 At the moment, all sunlighted nodes are added to light_sources.
1118 - SUGG: This could be optimized
1120 Turns sunglighted mud into grass.
1122 if remove_light==true, sets non-sunlighted nodes black.
1124 if black_air_left!=NULL, it is set to true if non-sunlighted
1125 air is left in block.
1127 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1128 bool remove_light, bool *black_air_left,
1131 // Whether the sunlight at the top of the bottom block is valid
1132 bool block_below_is_valid = true;
1134 v3s16 pos_relative = getPosRelative();
1136 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1138 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1141 bool no_sunlight = false;
1142 bool no_top_block = false;
1143 // Check if node above block has sunlight
1145 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1146 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1151 catch(InvalidPositionException &e)
1153 no_top_block = true;
1155 // NOTE: This makes over-ground roofed places sunlighted
1156 // Assume sunlight, unless is_underground==true
1163 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1164 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1169 // NOTE: As of now, this just would make everything dark.
1171 //no_sunlight = true;
1174 #if 0 // Doesn't work; nothing gets light.
1175 bool no_sunlight = true;
1176 bool no_top_block = false;
1177 // Check if node above block has sunlight
1179 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1180 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1182 no_sunlight = false;
1185 catch(InvalidPositionException &e)
1187 no_top_block = true;
1191 /*std::cout<<"("<<x<<","<<z<<"): "
1192 <<"no_top_block="<<no_top_block
1193 <<", is_underground="<<is_underground
1194 <<", no_sunlight="<<no_sunlight
1197 s16 y = MAP_BLOCKSIZE-1;
1199 // This makes difference to diminishing in water.
1200 bool stopped_to_solid_object = false;
1202 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1207 MapNode &n = getNodeRef(pos);
1209 if(current_light == 0)
1213 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1215 // Do nothing: Sunlight is continued
1217 else if(n.light_propagates() == false)
1221 bool upper_is_air = false;
1224 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1225 upper_is_air = true;
1227 catch(InvalidPositionException &e)
1230 // Turn mud into grass
1231 if(upper_is_air && n.d == CONTENT_MUD
1232 && current_light == LIGHT_SUN)
1234 n.d = CONTENT_GRASS;
1238 // A solid object is on the way.
1239 stopped_to_solid_object = true;
1247 current_light = diminish_light(current_light);
1250 u8 old_light = n.getLight(LIGHTBANK_DAY);
1252 if(current_light > old_light || remove_light)
1254 n.setLight(LIGHTBANK_DAY, current_light);
1257 if(diminish_light(current_light) != 0)
1259 light_sources.insert(pos_relative + pos, true);
1262 if(current_light == 0 && stopped_to_solid_object)
1266 *black_air_left = true;
1271 // Whether or not the block below should see LIGHT_SUN
1272 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1275 If the block below hasn't already been marked invalid:
1277 Check if the node below the block has proper sunlight at top.
1278 If not, the block below is invalid.
1280 Ignore non-transparent nodes as they always have no light
1284 if(block_below_is_valid)
1286 MapNode n = getNodeParent(v3s16(x, -1, z));
1287 if(n.light_propagates())
1289 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1290 && sunlight_should_go_down == false)
1291 block_below_is_valid = false;
1292 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1293 && sunlight_should_go_down == true)
1294 block_below_is_valid = false;
1298 catch(InvalidPositionException &e)
1300 /*std::cout<<"InvalidBlockException for bottom block node"
1302 // Just no block below, no need to panic.
1307 return block_below_is_valid;
1310 void MapBlock::copyTo(VoxelManipulator &dst)
1312 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1313 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1315 // Copy from data to VoxelManipulator
1316 dst.copyFrom(data, data_area, v3s16(0,0,0),
1317 getPosRelative(), data_size);
1320 void MapBlock::copyFrom(VoxelManipulator &dst)
1322 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1323 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1325 // Copy from VoxelManipulator to data
1326 dst.copyTo(data, data_area, v3s16(0,0,0),
1327 getPosRelative(), data_size);
1330 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1335 m_objects.step(dtime, server, daynight_ratio);
1338 Spawn some objects at random.
1340 Use dayNightDiffed() to approximate being near ground level
1342 if(m_spawn_timer < -999)
1346 if(dayNightDiffed() == true && getObjectCount() == 0)
1348 m_spawn_timer -= dtime;
1349 if(m_spawn_timer <= 0.0)
1351 m_spawn_timer += myrand() % 300;
1354 (myrand()%(MAP_BLOCKSIZE-1))+0,
1355 (myrand()%(MAP_BLOCKSIZE-1))+0
1358 s16 y = getGroundLevel(p2d);
1362 v3s16 p(p2d.X, y+1, p2d.Y);
1364 if(getNode(p).d == CONTENT_AIR
1365 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1367 RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1378 void MapBlock::updateDayNightDiff()
1382 m_day_night_differs = false;
1386 bool differs = false;
1389 Check if any lighting value differs
1391 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1393 MapNode &n = data[i];
1394 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1402 If some lighting values differ, check if the whole thing is
1403 just air. If it is, differ = false
1407 bool only_air = true;
1408 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1410 MapNode &n = data[i];
1411 if(n.d != CONTENT_AIR)
1421 // Set member variable
1422 m_day_night_differs = differs;
1425 s16 MapBlock::getGroundLevel(v2s16 p2d)
1431 s16 y = MAP_BLOCKSIZE-1;
1434 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1435 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1437 if(y == MAP_BLOCKSIZE-1)
1445 catch(InvalidPositionException &e)
1455 void MapBlock::serialize(std::ostream &os, u8 version)
1457 if(!ser_ver_supported(version))
1458 throw VersionMismatchException("ERROR: MapBlock format not supported");
1462 throw SerializationError("ERROR: Not writing dummy block.");
1465 // These have no compression
1466 if(version <= 3 || version == 5 || version == 6)
1468 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1470 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1471 SharedBuffer<u8> dest(buflen);
1473 dest[0] = is_underground;
1474 for(u32 i=0; i<nodecount; i++)
1476 u32 s = 1 + i * MapNode::serializedLength(version);
1477 data[i].serialize(&dest[s], version);
1480 os.write((char*)*dest, dest.getSize());
1482 else if(version <= 10)
1486 Compress the materials and the params separately.
1490 os.write((char*)&is_underground, 1);
1492 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1494 // Get and compress materials
1495 SharedBuffer<u8> materialdata(nodecount);
1496 for(u32 i=0; i<nodecount; i++)
1498 materialdata[i] = data[i].d;
1500 compress(materialdata, os, version);
1502 // Get and compress lights
1503 SharedBuffer<u8> lightdata(nodecount);
1504 for(u32 i=0; i<nodecount; i++)
1506 lightdata[i] = data[i].param;
1508 compress(lightdata, os, version);
1512 // Get and compress param2
1513 SharedBuffer<u8> param2data(nodecount);
1514 for(u32 i=0; i<nodecount; i++)
1516 param2data[i] = data[i].param2;
1518 compress(param2data, os, version);
1521 // All other versions (newest)
1528 if(m_day_night_differs)
1530 if(m_lighting_expired)
1532 os.write((char*)&flags, 1);
1534 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1540 SharedBuffer<u8> databuf(nodecount*3);
1543 for(u32 i=0; i<nodecount; i++)
1545 databuf[i] = data[i].d;
1549 for(u32 i=0; i<nodecount; i++)
1551 databuf[i+nodecount] = data[i].param;
1555 for(u32 i=0; i<nodecount; i++)
1557 databuf[i+nodecount*2] = data[i].param2;
1561 Compress data to output stream
1564 compress(databuf, os, version);
1568 void MapBlock::deSerialize(std::istream &is, u8 version)
1570 if(!ser_ver_supported(version))
1571 throw VersionMismatchException("ERROR: MapBlock format not supported");
1573 // These have no compression
1574 if(version <= 3 || version == 5 || version == 6)
1576 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1579 if(is.gcount() != 1)
1580 throw SerializationError
1581 ("MapBlock::deSerialize: no enough input data");
1582 is_underground = tmp;
1583 for(u32 i=0; i<nodecount; i++)
1585 s32 len = MapNode::serializedLength(version);
1586 SharedBuffer<u8> d(len);
1587 is.read((char*)*d, len);
1588 if(is.gcount() != len)
1589 throw SerializationError
1590 ("MapBlock::deSerialize: no enough input data");
1591 data[i].deSerialize(*d, version);
1594 else if(version <= 10)
1596 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1599 is.read((char*)&t8, 1);
1600 is_underground = t8;
1603 // Uncompress and set material data
1604 std::ostringstream os(std::ios_base::binary);
1605 decompress(is, os, version);
1606 std::string s = os.str();
1607 if(s.size() != nodecount)
1608 throw SerializationError
1609 ("MapBlock::deSerialize: invalid format");
1610 for(u32 i=0; i<s.size(); i++)
1616 // Uncompress and set param data
1617 std::ostringstream os(std::ios_base::binary);
1618 decompress(is, os, version);
1619 std::string s = os.str();
1620 if(s.size() != nodecount)
1621 throw SerializationError
1622 ("MapBlock::deSerialize: invalid format");
1623 for(u32 i=0; i<s.size(); i++)
1625 data[i].param = s[i];
1631 // Uncompress and set param2 data
1632 std::ostringstream os(std::ios_base::binary);
1633 decompress(is, os, version);
1634 std::string s = os.str();
1635 if(s.size() != nodecount)
1636 throw SerializationError
1637 ("MapBlock::deSerialize: invalid format");
1638 for(u32 i=0; i<s.size(); i++)
1640 data[i].param2 = s[i];
1644 // All other versions (newest)
1647 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1650 is.read((char*)&flags, 1);
1651 is_underground = (flags & 1) ? true : false;
1652 m_day_night_differs = (flags & 2) ? true : false;
1653 m_lighting_expired = (flags & 3) ? true : false;
1656 std::ostringstream os(std::ios_base::binary);
1657 decompress(is, os, version);
1658 std::string s = os.str();
1659 if(s.size() != nodecount*3)
1660 throw SerializationError
1661 ("MapBlock::deSerialize: invalid format");
1664 for(u32 i=0; i<nodecount; i++)
1669 for(u32 i=0; i<nodecount; i++)
1671 data[i].param = s[i+nodecount];
1674 for(u32 i=0; i<nodecount; i++)
1676 data[i].param2 = s[i+nodecount*2];
1681 Translate nodes as specified in the translate_to fields of
1684 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1686 MapNode &n = data[i];
1688 MapNode *translate_to = content_features(n.d).translate_to;
1691 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
1692 <<translate_to->d<<std::endl;