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);
615 bool new_style_water = g_settings.getBool("new_style_water");
616 bool new_style_leaves = g_settings.getBool("new_style_leaves");
618 float node_water_level = 1.0;
620 node_water_level = 0.9;
623 We are including the faces of the trailing edges of the block.
624 This means that when something changes, the caller must
625 also update the meshes of the blocks at the leading edges.
627 NOTE: This is the slowest part of this method.
631 // 4-23ms for MAP_BLOCKSIZE=16
632 //TimeTaker timer2("updateMesh() collect");
635 Go through every y,z and get top faces in rows of x+
637 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
638 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
639 updateFastFaceRow(daynight_ratio, posRelative_f,
640 v3s16(0,y,z), MAP_BLOCKSIZE,
643 v3s16(0,1,0), //face dir
650 Go through every x,y and get right faces in rows of z+
652 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
653 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
654 updateFastFaceRow(daynight_ratio, posRelative_f,
655 v3s16(x,y,0), MAP_BLOCKSIZE,
665 Go through every y,z and get back faces in rows of x+
667 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
668 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
669 updateFastFaceRow(daynight_ratio, posRelative_f,
670 v3s16(0,y,z), MAP_BLOCKSIZE,
684 Convert FastFaces to SMesh
687 scene::SMesh *mesh_new = NULL;
689 mesh_new = new scene::SMesh();
691 MeshCollector collector;
693 if(fastfaces_new.size() > 0)
695 // avg 0ms (100ms spikes when loading textures the first time)
696 //TimeTaker timer2("updateMesh() mesh building");
698 video::SMaterial material;
699 material.Lighting = false;
700 material.BackfaceCulling = false;
701 material.setFlag(video::EMF_BILINEAR_FILTER, false);
702 material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
703 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
704 material.setFlag(video::EMF_FOG_ENABLE, true);
706 for(u32 i=0; i<fastfaces_new.size(); i++)
708 FastFace &f = fastfaces_new[i];
710 const u16 indices[] = {0,1,2,2,3,0};
712 video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
716 material.setTexture(0, texture);
718 f.tile.applyMaterialOptions(material);
720 /*if(f.tile.alpha != 255)
721 material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
723 material.MaterialType = video::EMT_SOLID;*/
725 collector.append(material, f.vertices, 4, indices, 6);
730 Add special graphics:
736 //TimeTaker timer2("updateMesh() adding special stuff");
738 // Flowing water material
739 video::SMaterial material_water1;
740 material_water1.setFlag(video::EMF_LIGHTING, false);
741 material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
742 material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
743 material_water1.setFlag(video::EMF_FOG_ENABLE, true);
744 material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
745 material_water1.setTexture(0, g_irrlicht->getTexture("water.png"));
747 // New-style leaves material
748 video::SMaterial material_leaves1;
749 material_leaves1.setFlag(video::EMF_LIGHTING, false);
750 //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
751 material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
752 material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
753 material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
754 material_leaves1.setTexture(0, g_irrlicht->getTexture("leaves.png"));
756 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
757 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
758 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
762 MapNode &n = getNodeRef(x,y,z);
767 if(n.d == CONTENT_TORCH)
769 video::SColor c(255,255,255,255);
771 video::S3DVertex vertices[4] =
773 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
774 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
775 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
776 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
779 v3s16 dir = unpackDir(n.dir);
781 for(s32 i=0; i<4; i++)
783 if(dir == v3s16(1,0,0))
784 vertices[i].Pos.rotateXZBy(0);
785 if(dir == v3s16(-1,0,0))
786 vertices[i].Pos.rotateXZBy(180);
787 if(dir == v3s16(0,0,1))
788 vertices[i].Pos.rotateXZBy(90);
789 if(dir == v3s16(0,0,-1))
790 vertices[i].Pos.rotateXZBy(-90);
791 if(dir == v3s16(0,-1,0))
792 vertices[i].Pos.rotateXZBy(45);
793 if(dir == v3s16(0,1,0))
794 vertices[i].Pos.rotateXZBy(-45);
796 vertices[i].Pos += intToFloat(p + getPosRelative());
800 video::SMaterial material;
801 material.setFlag(video::EMF_LIGHTING, false);
802 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
803 material.setFlag(video::EMF_BILINEAR_FILTER, false);
804 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
805 material.MaterialType
806 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
807 if(dir == v3s16(0,-1,0))
808 material.setTexture(0,
809 g_irrlicht->getTexture("torch_on_floor.png"));
810 else if(dir == v3s16(0,1,0))
811 material.setTexture(0,
812 g_irrlicht->getTexture("torch_on_ceiling.png"));
813 // For backwards compatibility
814 else if(dir == v3s16(0,0,0))
815 material.setTexture(0,
816 g_irrlicht->getTexture("torch_on_floor.png"));
818 material.setTexture(0,
819 g_irrlicht->getTexture("torch.png"));
821 u16 indices[] = {0,1,2,2,3,0};
822 // Add to mesh collector
823 collector.append(material, vertices, 4, indices, 6);
826 Add flowing water to mesh
828 else if(n.d == CONTENT_WATER)
830 bool top_is_water = false;
832 MapNode n = getNodeParent(v3s16(x,y+1,z));
833 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
835 }catch(InvalidPositionException &e){}
837 u8 l = decode_light(n.getLightBlend(daynight_ratio));
838 video::SColor c(WATER_ALPHA,l,l,l);
840 // Neighbor water levels (key = relative position)
841 // Includes current node
842 core::map<v3s16, f32> neighbor_levels;
843 core::map<v3s16, u8> neighbor_contents;
844 core::map<v3s16, u8> neighbor_flags;
845 const u8 neighborflag_top_is_water = 0x01;
846 v3s16 neighbor_dirs[9] = {
857 for(u32 i=0; i<9; i++)
859 u8 content = CONTENT_AIR;
860 float level = -0.5 * BS;
864 v3s16 p2 = p + neighbor_dirs[i];
865 MapNode n2 = getNodeParent(p2);
869 if(n2.d == CONTENT_WATERSOURCE)
870 level = (-0.5+node_water_level) * BS;
871 else if(n2.d == CONTENT_WATER)
872 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
873 * node_water_level) * BS;
875 // Check node above neighbor.
876 // NOTE: This doesn't get executed if neighbor
879 n2 = getNodeParent(p2);
880 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
881 flags |= neighborflag_top_is_water;
883 catch(InvalidPositionException &e){}
885 neighbor_levels.insert(neighbor_dirs[i], level);
886 neighbor_contents.insert(neighbor_dirs[i], content);
887 neighbor_flags.insert(neighbor_dirs[i], flags);
890 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
891 //float water_level = neighbor_levels[v3s16(0,0,0)];
893 // Corner heights (average between four waters)
894 f32 corner_levels[4];
896 v3s16 halfdirs[4] = {
902 for(u32 i=0; i<4; i++)
904 v3s16 cornerdir = halfdirs[i];
905 float cornerlevel = 0;
907 for(u32 j=0; j<4; j++)
909 v3s16 neighbordir = cornerdir - halfdirs[j];
910 u8 content = neighbor_contents[neighbordir];
911 // Special case for source nodes
912 if(content == CONTENT_WATERSOURCE)
914 cornerlevel = (-0.5+node_water_level)*BS;
918 else if(content == CONTENT_WATER)
920 cornerlevel += neighbor_levels[neighbordir];
923 else if(content == CONTENT_AIR)
925 cornerlevel += -0.5*BS;
930 cornerlevel /= valid_count;
931 corner_levels[i] = cornerlevel;
938 v3s16 side_dirs[4] = {
944 s16 side_corners[4][2] = {
950 for(u32 i=0; i<4; i++)
952 v3s16 dir = side_dirs[i];
955 If our topside is water and neighbor's topside
956 is water, don't draw side face
959 neighbor_flags[dir] & neighborflag_top_is_water)
962 u8 neighbor_content = neighbor_contents[dir];
964 // Don't draw face if neighbor is not air or water
965 if(neighbor_content != CONTENT_AIR
966 && neighbor_content != CONTENT_WATER)
969 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
971 // Don't draw any faces if neighbor is water and top is water
972 if(neighbor_is_water == true && top_is_water == false)
975 video::S3DVertex vertices[4] =
977 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
978 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
979 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
980 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
981 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
982 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
983 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
984 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
988 If our topside is water, set upper border of face
989 at upper border of node
993 vertices[2].Pos.Y = 0.5*BS;
994 vertices[3].Pos.Y = 0.5*BS;
997 Otherwise upper position of face is corner levels
1001 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1002 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1006 If neighbor is water, lower border of face is corner
1009 if(neighbor_is_water)
1011 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1012 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1015 If neighbor is not water, lower border of face is
1016 lower border of node
1020 vertices[0].Pos.Y = -0.5*BS;
1021 vertices[1].Pos.Y = -0.5*BS;
1024 for(s32 j=0; j<4; j++)
1026 if(dir == v3s16(0,0,1))
1027 vertices[j].Pos.rotateXZBy(0);
1028 if(dir == v3s16(0,0,-1))
1029 vertices[j].Pos.rotateXZBy(180);
1030 if(dir == v3s16(-1,0,0))
1031 vertices[j].Pos.rotateXZBy(90);
1032 if(dir == v3s16(1,0,-0))
1033 vertices[j].Pos.rotateXZBy(-90);
1035 vertices[j].Pos += intToFloat(p + getPosRelative());
1038 u16 indices[] = {0,1,2,2,3,0};
1039 // Add to mesh collector
1040 collector.append(material_water1, vertices, 4, indices, 6);
1044 Generate top side, if appropriate
1047 if(top_is_water == false)
1049 video::S3DVertex vertices[4] =
1051 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1052 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1053 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1054 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
1057 for(s32 i=0; i<4; i++)
1059 //vertices[i].Pos.Y += water_level;
1060 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1061 vertices[i].Pos.Y += corner_levels[i];
1062 vertices[i].Pos += intToFloat(p + getPosRelative());
1065 u16 indices[] = {0,1,2,2,3,0};
1066 // Add to mesh collector
1067 collector.append(material_water1, vertices, 4, indices, 6);
1071 Add water sources to mesh if using new style
1073 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1075 //bool top_is_water = false;
1076 bool top_is_air = false;
1078 MapNode n = getNodeParent(v3s16(x,y+1,z));
1079 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1080 top_is_water = true;*/
1081 if(n.d == CONTENT_AIR)
1083 }catch(InvalidPositionException &e){}
1085 /*if(top_is_water == true)
1087 if(top_is_air == false)
1090 u8 l = decode_light(n.getLightBlend(daynight_ratio));
1091 video::SColor c(WATER_ALPHA,l,l,l);
1093 video::S3DVertex vertices[4] =
1095 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1096 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1097 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1098 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
1101 for(s32 i=0; i<4; i++)
1103 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1104 vertices[i].Pos += intToFloat(p + getPosRelative());
1107 u16 indices[] = {0,1,2,2,3,0};
1108 // Add to mesh collector
1109 collector.append(material_water1, vertices, 4, indices, 6);
1112 Add leaves if using new style
1114 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1116 u8 l = decode_light(n.getLightBlend(daynight_ratio));
1117 video::SColor c(255,l,l,l);
1119 for(u32 j=0; j<6; j++)
1121 video::S3DVertex vertices[4] =
1123 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1124 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1125 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1126 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),
1131 for(u16 i=0; i<4; i++)
1132 vertices[i].Pos.rotateXZBy(0);
1136 for(u16 i=0; i<4; i++)
1137 vertices[i].Pos.rotateXZBy(180);
1141 for(u16 i=0; i<4; i++)
1142 vertices[i].Pos.rotateXZBy(-90);
1146 for(u16 i=0; i<4; i++)
1147 vertices[i].Pos.rotateXZBy(90);
1151 for(u16 i=0; i<4; i++)
1152 vertices[i].Pos.rotateYZBy(-90);
1156 for(u16 i=0; i<4; i++)
1157 vertices[i].Pos.rotateYZBy(90);
1160 for(u16 i=0; i<4; i++)
1162 vertices[i].Pos += intToFloat(p + getPosRelative());
1165 u16 indices[] = {0,1,2,2,3,0};
1166 // Add to mesh collector
1167 collector.append(material_leaves1, vertices, 4, indices, 6);
1173 Add stuff from collector to mesh
1176 collector.fillMesh(mesh_new);
1179 Do some stuff to the mesh
1182 mesh_new->recalculateBoundingBox();
1185 Delete new mesh if it is empty
1188 if(mesh_new->getMeshBufferCount() == 0)
1194 // Use VBO for mesh (this just would set this for ever buffer)
1195 // This will lead to infinite memory usage because or irrlicht.
1196 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1198 /*std::cout<<"MapBlock has "<<fastfaces_new.size()<<" faces "
1199 <<"and uses "<<mesh_new->getMeshBufferCount()
1200 <<" materials (meshbuffers)"<<std::endl;*/
1208 //scene::SMesh *mesh_old = mesh[daynight_i];
1209 //mesh[daynight_i] = mesh_new;
1211 scene::SMesh *mesh_old = mesh;
1213 setMeshExpired(false);
1215 if(mesh_old != NULL)
1217 // Remove hardware buffers of meshbuffers of mesh
1218 // NOTE: No way, this runs in a different thread and everything
1219 /*u32 c = mesh_old->getMeshBufferCount();
1220 for(u32 i=0; i<c; i++)
1222 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1225 /*dstream<<"mesh_old->getReferenceCount()="
1226 <<mesh_old->getReferenceCount()<<std::endl;
1227 u32 c = mesh_old->getMeshBufferCount();
1228 for(u32 i=0; i<c; i++)
1230 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1231 dstream<<"buf->getReferenceCount()="
1232 <<buf->getReferenceCount()<<std::endl;
1241 mesh_mutex.Unlock();
1243 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1246 /*void MapBlock::updateMeshes(s32 first_i)
1248 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
1249 updateMesh(first_i);
1250 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
1261 Propagates sunlight down through the block.
1262 Doesn't modify nodes that are not affected by sunlight.
1264 Returns false if sunlight at bottom block is invalid
1265 Returns true if bottom block doesn't exist.
1267 If there is a block above, continues from it.
1268 If there is no block above, assumes there is sunlight, unless
1269 is_underground is set or highest node is water.
1271 At the moment, all sunlighted nodes are added to light_sources.
1272 - SUGG: This could be optimized
1274 Turns sunglighted mud into grass.
1276 if remove_light==true, sets non-sunlighted nodes black.
1278 if black_air_left!=NULL, it is set to true if non-sunlighted
1279 air is left in block.
1281 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1282 bool remove_light, bool *black_air_left,
1285 // Whether the sunlight at the top of the bottom block is valid
1286 bool block_below_is_valid = true;
1288 v3s16 pos_relative = getPosRelative();
1290 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1292 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1295 bool no_sunlight = false;
1296 bool no_top_block = false;
1297 // Check if node above block has sunlight
1299 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1300 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1305 catch(InvalidPositionException &e)
1307 no_top_block = true;
1309 // NOTE: This makes over-ground roofed places sunlighted
1310 // Assume sunlight, unless is_underground==true
1317 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1318 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1323 // NOTE: As of now, this just would make everything dark.
1325 //no_sunlight = true;
1328 #if 0 // Doesn't work; nothing gets light.
1329 bool no_sunlight = true;
1330 bool no_top_block = false;
1331 // Check if node above block has sunlight
1333 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1334 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1336 no_sunlight = false;
1339 catch(InvalidPositionException &e)
1341 no_top_block = true;
1345 /*std::cout<<"("<<x<<","<<z<<"): "
1346 <<"no_top_block="<<no_top_block
1347 <<", is_underground="<<is_underground
1348 <<", no_sunlight="<<no_sunlight
1351 s16 y = MAP_BLOCKSIZE-1;
1353 // This makes difference to diminishing in water.
1354 bool stopped_to_solid_object = false;
1356 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1361 MapNode &n = getNodeRef(pos);
1363 if(current_light == 0)
1367 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1369 // Do nothing: Sunlight is continued
1371 else if(n.light_propagates() == false)
1375 bool upper_is_air = false;
1378 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1379 upper_is_air = true;
1381 catch(InvalidPositionException &e)
1384 // Turn mud into grass
1385 if(upper_is_air && n.d == CONTENT_MUD
1386 && current_light == LIGHT_SUN)
1388 n.d = CONTENT_GRASS;
1392 // A solid object is on the way.
1393 stopped_to_solid_object = true;
1401 current_light = diminish_light(current_light);
1404 u8 old_light = n.getLight(LIGHTBANK_DAY);
1406 if(current_light > old_light || remove_light)
1408 n.setLight(LIGHTBANK_DAY, current_light);
1411 if(diminish_light(current_light) != 0)
1413 light_sources.insert(pos_relative + pos, true);
1416 if(current_light == 0 && stopped_to_solid_object)
1420 *black_air_left = true;
1425 // Whether or not the block below should see LIGHT_SUN
1426 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1429 If the block below hasn't already been marked invalid:
1431 Check if the node below the block has proper sunlight at top.
1432 If not, the block below is invalid.
1434 Ignore non-transparent nodes as they always have no light
1438 if(block_below_is_valid)
1440 MapNode n = getNodeParent(v3s16(x, -1, z));
1441 if(n.light_propagates())
1443 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1444 && sunlight_should_go_down == false)
1445 block_below_is_valid = false;
1446 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1447 && sunlight_should_go_down == true)
1448 block_below_is_valid = false;
1452 catch(InvalidPositionException &e)
1454 /*std::cout<<"InvalidBlockException for bottom block node"
1456 // Just no block below, no need to panic.
1461 return block_below_is_valid;
1464 void MapBlock::copyTo(VoxelManipulator &dst)
1466 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1467 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1469 // Copy from data to VoxelManipulator
1470 dst.copyFrom(data, data_area, v3s16(0,0,0),
1471 getPosRelative(), data_size);
1474 void MapBlock::copyFrom(VoxelManipulator &dst)
1476 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1477 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1479 // Copy from VoxelManipulator to data
1480 dst.copyTo(data, data_area, v3s16(0,0,0),
1481 getPosRelative(), data_size);
1484 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1489 m_objects.step(dtime, server, daynight_ratio);
1492 Spawn some objects at random.
1494 Use dayNightDiffed() to approximate being near ground level
1496 if(m_spawn_timer < -999)
1500 if(dayNightDiffed() == true && getObjectCount() == 0)
1502 m_spawn_timer -= dtime;
1503 if(m_spawn_timer <= 0.0)
1505 m_spawn_timer += myrand() % 300;
1508 (myrand()%(MAP_BLOCKSIZE-1))+0,
1509 (myrand()%(MAP_BLOCKSIZE-1))+0
1512 s16 y = getGroundLevel(p2d);
1516 v3s16 p(p2d.X, y+1, p2d.Y);
1518 if(getNode(p).d == CONTENT_AIR
1519 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1521 RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1532 void MapBlock::updateDayNightDiff()
1536 m_day_night_differs = false;
1540 bool differs = false;
1543 Check if any lighting value differs
1545 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1547 MapNode &n = data[i];
1548 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1556 If some lighting values differ, check if the whole thing is
1557 just air. If it is, differ = false
1561 bool only_air = true;
1562 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1564 MapNode &n = data[i];
1565 if(n.d != CONTENT_AIR)
1575 // Set member variable
1576 m_day_night_differs = differs;
1579 s16 MapBlock::getGroundLevel(v2s16 p2d)
1585 s16 y = MAP_BLOCKSIZE-1;
1588 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1589 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1591 if(y == MAP_BLOCKSIZE-1)
1599 catch(InvalidPositionException &e)
1609 void MapBlock::serialize(std::ostream &os, u8 version)
1611 if(!ser_ver_supported(version))
1612 throw VersionMismatchException("ERROR: MapBlock format not supported");
1616 throw SerializationError("ERROR: Not writing dummy block.");
1619 // These have no compression
1620 if(version <= 3 || version == 5 || version == 6)
1622 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1624 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1625 SharedBuffer<u8> dest(buflen);
1627 dest[0] = is_underground;
1628 for(u32 i=0; i<nodecount; i++)
1630 u32 s = 1 + i * MapNode::serializedLength(version);
1631 data[i].serialize(&dest[s], version);
1634 os.write((char*)*dest, dest.getSize());
1636 else if(version <= 10)
1640 Compress the materials and the params separately.
1644 os.write((char*)&is_underground, 1);
1646 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1648 // Get and compress materials
1649 SharedBuffer<u8> materialdata(nodecount);
1650 for(u32 i=0; i<nodecount; i++)
1652 materialdata[i] = data[i].d;
1654 compress(materialdata, os, version);
1656 // Get and compress lights
1657 SharedBuffer<u8> lightdata(nodecount);
1658 for(u32 i=0; i<nodecount; i++)
1660 lightdata[i] = data[i].param;
1662 compress(lightdata, os, version);
1666 // Get and compress param2
1667 SharedBuffer<u8> param2data(nodecount);
1668 for(u32 i=0; i<nodecount; i++)
1670 param2data[i] = data[i].param2;
1672 compress(param2data, os, version);
1675 // All other versions (newest)
1682 if(m_day_night_differs)
1684 if(m_lighting_expired)
1686 os.write((char*)&flags, 1);
1688 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1694 SharedBuffer<u8> databuf(nodecount*3);
1697 for(u32 i=0; i<nodecount; i++)
1699 databuf[i] = data[i].d;
1703 for(u32 i=0; i<nodecount; i++)
1705 databuf[i+nodecount] = data[i].param;
1709 for(u32 i=0; i<nodecount; i++)
1711 databuf[i+nodecount*2] = data[i].param2;
1715 Compress data to output stream
1718 compress(databuf, os, version);
1722 void MapBlock::deSerialize(std::istream &is, u8 version)
1724 if(!ser_ver_supported(version))
1725 throw VersionMismatchException("ERROR: MapBlock format not supported");
1727 // These have no compression
1728 if(version <= 3 || version == 5 || version == 6)
1730 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1733 if(is.gcount() != 1)
1734 throw SerializationError
1735 ("MapBlock::deSerialize: no enough input data");
1736 is_underground = tmp;
1737 for(u32 i=0; i<nodecount; i++)
1739 s32 len = MapNode::serializedLength(version);
1740 SharedBuffer<u8> d(len);
1741 is.read((char*)*d, len);
1742 if(is.gcount() != len)
1743 throw SerializationError
1744 ("MapBlock::deSerialize: no enough input data");
1745 data[i].deSerialize(*d, version);
1748 else if(version <= 10)
1750 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1753 is.read((char*)&t8, 1);
1754 is_underground = t8;
1757 // Uncompress and set material data
1758 std::ostringstream os(std::ios_base::binary);
1759 decompress(is, os, version);
1760 std::string s = os.str();
1761 if(s.size() != nodecount)
1762 throw SerializationError
1763 ("MapBlock::deSerialize: invalid format");
1764 for(u32 i=0; i<s.size(); i++)
1770 // Uncompress and set param data
1771 std::ostringstream os(std::ios_base::binary);
1772 decompress(is, os, version);
1773 std::string s = os.str();
1774 if(s.size() != nodecount)
1775 throw SerializationError
1776 ("MapBlock::deSerialize: invalid format");
1777 for(u32 i=0; i<s.size(); i++)
1779 data[i].param = s[i];
1785 // Uncompress and set param2 data
1786 std::ostringstream os(std::ios_base::binary);
1787 decompress(is, os, version);
1788 std::string s = os.str();
1789 if(s.size() != nodecount)
1790 throw SerializationError
1791 ("MapBlock::deSerialize: invalid format");
1792 for(u32 i=0; i<s.size(); i++)
1794 data[i].param2 = s[i];
1798 // All other versions (newest)
1801 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1804 is.read((char*)&flags, 1);
1805 is_underground = (flags & 1) ? true : false;
1806 m_day_night_differs = (flags & 2) ? true : false;
1807 m_lighting_expired = (flags & 3) ? true : false;
1810 std::ostringstream os(std::ios_base::binary);
1811 decompress(is, os, version);
1812 std::string s = os.str();
1813 if(s.size() != nodecount*3)
1814 throw SerializationError
1815 ("MapBlock::deSerialize: invalid format");
1818 for(u32 i=0; i<nodecount; i++)
1823 for(u32 i=0; i<nodecount; i++)
1825 data[i].param = s[i+nodecount];
1828 for(u32 i=0; i<nodecount; i++)
1830 data[i].param2 = s[i+nodecount*2];
1835 Translate nodes as specified in the translate_to fields of
1838 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1840 MapNode &n = data[i];
1842 MapNode *translate_to = content_features(n.d).translate_to;
1845 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
1846 <<translate_to->d<<std::endl;