+ vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+ }
+
+ // Set material
+ video::SMaterial material;
+ material.setFlag(video::EMF_LIGHTING, false);
+ material.setFlag(video::EMF_BACK_FACE_CULLING, false);
+ material.setFlag(video::EMF_BILINEAR_FILTER, false);
+ //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+ material.MaterialType
+ = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+
+ if(dir == v3s16(0,-1,0))
+ material.setTexture(0,
+ g_texturesource->getTextureRaw("torch_on_floor.png"));
+ else if(dir == v3s16(0,1,0))
+ material.setTexture(0,
+ g_texturesource->getTextureRaw("torch_on_ceiling.png"));
+ // For backwards compatibility
+ else if(dir == v3s16(0,0,0))
+ material.setTexture(0,
+ g_texturesource->getTextureRaw("torch_on_floor.png"));
+ else
+ material.setTexture(0,
+ g_texturesource->getTextureRaw("torch.png"));
+
+ u16 indices[] = {0,1,2,2,3,0};
+ // Add to mesh collector
+ collector.append(material, vertices, 4, indices, 6);
+ }
+ /*
+ Signs on walls
+ */
+ if(n.d == CONTENT_SIGN_WALL)
+ {
+ u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
+ video::SColor c(255,l,l,l);
+
+ float d = (float)BS/16;
+ // Wall at X+ of node
+ video::S3DVertex vertices[4] =
+ {
+ video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
+ video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
+ video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
+ video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
+ };
+
+ v3s16 dir = unpackDir(n.dir);
+
+ for(s32 i=0; i<4; i++)
+ {
+ if(dir == v3s16(1,0,0))
+ vertices[i].Pos.rotateXZBy(0);
+ if(dir == v3s16(-1,0,0))
+ vertices[i].Pos.rotateXZBy(180);
+ if(dir == v3s16(0,0,1))
+ vertices[i].Pos.rotateXZBy(90);
+ if(dir == v3s16(0,0,-1))
+ vertices[i].Pos.rotateXZBy(-90);
+ if(dir == v3s16(0,-1,0))
+ vertices[i].Pos.rotateXYBy(-90);
+ if(dir == v3s16(0,1,0))
+ vertices[i].Pos.rotateXYBy(90);
+
+ vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+ }
+
+ // Set material
+ video::SMaterial material;
+ material.setFlag(video::EMF_LIGHTING, false);
+ material.setFlag(video::EMF_BACK_FACE_CULLING, false);
+ material.setFlag(video::EMF_BILINEAR_FILTER, false);
+ material.setFlag(video::EMF_FOG_ENABLE, true);
+ //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+ material.MaterialType
+ = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+
+ material.setTexture(0,
+ g_texturesource->getTextureRaw("sign_wall.png"));
+
+ u16 indices[] = {0,1,2,2,3,0};
+ // Add to mesh collector
+ collector.append(material, vertices, 4, indices, 6);
+ }
+ /*
+ Add flowing water to mesh
+ */
+ else if(n.d == CONTENT_WATER)
+ {
+ bool top_is_water = false;
+ MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
+ if(ntop.d == CONTENT_WATER || ntop.d == CONTENT_WATERSOURCE)
+ top_is_water = true;
+
+ u8 l = 0;
+ // Use the light of the node on top if possible
+ if(content_features(ntop.d).param_type == CPT_LIGHT)
+ l = decode_light(ntop.getLightBlend(data->m_daynight_ratio));
+ // Otherwise use the light of this node (the water)
+ else
+ l = decode_light(n.getLightBlend(data->m_daynight_ratio));
+ video::SColor c(WATER_ALPHA,l,l,l);
+
+ // Neighbor water levels (key = relative position)
+ // Includes current node
+ core::map<v3s16, f32> neighbor_levels;
+ core::map<v3s16, u8> neighbor_contents;
+ core::map<v3s16, u8> neighbor_flags;
+ const u8 neighborflag_top_is_water = 0x01;
+ v3s16 neighbor_dirs[9] = {
+ v3s16(0,0,0),
+ v3s16(0,0,1),
+ v3s16(0,0,-1),
+ v3s16(1,0,0),
+ v3s16(-1,0,0),
+ v3s16(1,0,1),
+ v3s16(-1,0,-1),
+ v3s16(1,0,-1),
+ v3s16(-1,0,1),
+ };
+ for(u32 i=0; i<9; i++)
+ {
+ u8 content = CONTENT_AIR;
+ float level = -0.5 * BS;
+ u8 flags = 0;
+ // Check neighbor
+ v3s16 p2 = p + neighbor_dirs[i];
+ MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
+ if(n2.d != CONTENT_IGNORE)
+ {
+ content = n2.d;
+
+ if(n2.d == CONTENT_WATERSOURCE)
+ level = (-0.5+node_water_level) * BS;
+ else if(n2.d == CONTENT_WATER)
+ level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
+ * node_water_level) * BS;
+
+ // Check node above neighbor.
+ // NOTE: This doesn't get executed if neighbor
+ // doesn't exist
+ p2.Y += 1;
+ n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
+ if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
+ flags |= neighborflag_top_is_water;
+ }
+
+ neighbor_levels.insert(neighbor_dirs[i], level);
+ neighbor_contents.insert(neighbor_dirs[i], content);
+ neighbor_flags.insert(neighbor_dirs[i], flags);
+ }
+
+ //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
+ //float water_level = neighbor_levels[v3s16(0,0,0)];
+
+ // Corner heights (average between four waters)
+ f32 corner_levels[4];
+
+ v3s16 halfdirs[4] = {
+ v3s16(0,0,0),
+ v3s16(1,0,0),
+ v3s16(1,0,1),
+ v3s16(0,0,1),
+ };
+ for(u32 i=0; i<4; i++)
+ {
+ v3s16 cornerdir = halfdirs[i];
+ float cornerlevel = 0;
+ u32 valid_count = 0;
+ for(u32 j=0; j<4; j++)
+ {
+ v3s16 neighbordir = cornerdir - halfdirs[j];
+ u8 content = neighbor_contents[neighbordir];
+ // Special case for source nodes
+ if(content == CONTENT_WATERSOURCE)
+ {
+ cornerlevel = (-0.5+node_water_level)*BS;
+ valid_count = 1;
+ break;
+ }
+ else if(content == CONTENT_WATER)
+ {
+ cornerlevel += neighbor_levels[neighbordir];
+ valid_count++;
+ }
+ else if(content == CONTENT_AIR)
+ {
+ cornerlevel += -0.5*BS;
+ valid_count++;
+ }
+ }
+ if(valid_count > 0)
+ cornerlevel /= valid_count;
+ corner_levels[i] = cornerlevel;
+ }
+
+ /*
+ Generate sides
+ */
+
+ v3s16 side_dirs[4] = {
+ v3s16(1,0,0),
+ v3s16(-1,0,0),
+ v3s16(0,0,1),
+ v3s16(0,0,-1),
+ };
+ s16 side_corners[4][2] = {
+ {1, 2},
+ {3, 0},
+ {2, 3},
+ {0, 1},
+ };
+ for(u32 i=0; i<4; i++)
+ {
+ v3s16 dir = side_dirs[i];
+
+ /*
+ If our topside is water and neighbor's topside
+ is water, don't draw side face
+ */
+ if(top_is_water &&
+ neighbor_flags[dir] & neighborflag_top_is_water)
+ continue;
+
+ u8 neighbor_content = neighbor_contents[dir];
+
+ // Don't draw face if neighbor is not air or water
+ if(neighbor_content != CONTENT_AIR
+ && neighbor_content != CONTENT_WATER)
+ continue;
+
+ bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
+
+ // Don't draw any faces if neighbor is water and top is water
+ if(neighbor_is_water == true && top_is_water == false)
+ continue;
+
+ video::S3DVertex vertices[4] =
+ {
+ /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
+ video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
+ video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
+ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
+ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
+ pa_water1.x0(), pa_water1.y1()),
+ video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
+ pa_water1.x1(), pa_water1.y1()),
+ video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
+ pa_water1.x1(), pa_water1.y0()),
+ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
+ pa_water1.x0(), pa_water1.y0()),
+ };
+
+ /*
+ If our topside is water, set upper border of face
+ at upper border of node
+ */
+ if(top_is_water)
+ {
+ vertices[2].Pos.Y = 0.5*BS;
+ vertices[3].Pos.Y = 0.5*BS;
+ }
+ /*
+ Otherwise upper position of face is corner levels
+ */
+ else
+ {
+ vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
+ vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
+ }
+
+ /*
+ If neighbor is water, lower border of face is corner
+ water levels
+ */
+ if(neighbor_is_water)
+ {
+ vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
+ vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
+ }
+ /*
+ If neighbor is not water, lower border of face is
+ lower border of node
+ */
+ else
+ {
+ vertices[0].Pos.Y = -0.5*BS;
+ vertices[1].Pos.Y = -0.5*BS;
+ }
+
+ for(s32 j=0; j<4; j++)
+ {
+ if(dir == v3s16(0,0,1))
+ vertices[j].Pos.rotateXZBy(0);
+ if(dir == v3s16(0,0,-1))
+ vertices[j].Pos.rotateXZBy(180);
+ if(dir == v3s16(-1,0,0))
+ vertices[j].Pos.rotateXZBy(90);
+ if(dir == v3s16(1,0,-0))
+ vertices[j].Pos.rotateXZBy(-90);
+
+ vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
+ }
+
+ u16 indices[] = {0,1,2,2,3,0};
+ // Add to mesh collector
+ collector.append(material_water1, vertices, 4, indices, 6);
+ }
+
+ /*
+ Generate top side, if appropriate
+ */
+
+ if(top_is_water == false)
+ {
+ video::S3DVertex vertices[4] =
+ {
+ /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
+ video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
+ video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
+ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
+ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
+ pa_water1.x0(), pa_water1.y1()),
+ video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
+ pa_water1.x1(), pa_water1.y1()),
+ video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
+ pa_water1.x1(), pa_water1.y0()),
+ video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
+ pa_water1.x0(), pa_water1.y0()),
+ };
+
+ // This fixes a strange bug
+ s32 corner_resolve[4] = {3,2,1,0};
+
+ for(s32 i=0; i<4; i++)
+ {
+ //vertices[i].Pos.Y += water_level;
+ //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
+ s32 j = corner_resolve[i];
+ vertices[i].Pos.Y += corner_levels[j];
+ vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+ }
+
+ u16 indices[] = {0,1,2,2,3,0};
+ // Add to mesh collector
+ collector.append(material_water1, vertices, 4, indices, 6);
+ }
+ }
+ /*
+ Add water sources to mesh if using new style
+ */
+ else if(n.d == CONTENT_WATERSOURCE && new_style_water)
+ {
+ //bool top_is_water = false;
+ bool top_is_air = false;
+ MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
+ /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
+ top_is_water = true;*/
+ if(n.d == CONTENT_AIR)
+ top_is_air = true;
+
+ /*if(top_is_water == true)
+ continue;*/
+ if(top_is_air == false)
+ continue;
+
+ u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
+ video::SColor c(WATER_ALPHA,l,l,l);
+
+ video::S3DVertex vertices[4] =
+ {
+ /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
+ video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
+ video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
+ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
+ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
+ pa_water1.x0(), pa_water1.y1()),
+ video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
+ pa_water1.x1(), pa_water1.y1()),
+ video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
+ pa_water1.x1(), pa_water1.y0()),
+ video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
+ pa_water1.x0(), pa_water1.y0()),
+ };
+
+ for(s32 i=0; i<4; i++)
+ {
+ vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
+ vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+ }
+
+ u16 indices[] = {0,1,2,2,3,0};
+ // Add to mesh collector
+ collector.append(material_water1, vertices, 4, indices, 6);
+ }
+ /*
+ Add leaves if using new style
+ */
+ else if(n.d == CONTENT_LEAVES && new_style_leaves)
+ {
+ /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
+ u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
+ video::SColor c(255,l,l,l);
+
+ for(u32 j=0; j<6; j++)
+ {
+ video::S3DVertex vertices[4] =
+ {
+ /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
+ video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
+ video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
+ video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
+ video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
+ pa_leaves1.x0(), pa_leaves1.y1()),
+ video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
+ pa_leaves1.x1(), pa_leaves1.y1()),
+ video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
+ pa_leaves1.x1(), pa_leaves1.y0()),
+ video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
+ pa_leaves1.x0(), pa_leaves1.y0()),
+ };
+
+ if(j == 0)
+ {
+ for(u16 i=0; i<4; i++)
+ vertices[i].Pos.rotateXZBy(0);
+ }
+ else if(j == 1)
+ {
+ for(u16 i=0; i<4; i++)
+ vertices[i].Pos.rotateXZBy(180);
+ }
+ else if(j == 2)
+ {
+ for(u16 i=0; i<4; i++)
+ vertices[i].Pos.rotateXZBy(-90);
+ }
+ else if(j == 3)
+ {
+ for(u16 i=0; i<4; i++)
+ vertices[i].Pos.rotateXZBy(90);
+ }
+ else if(j == 4)
+ {
+ for(u16 i=0; i<4; i++)
+ vertices[i].Pos.rotateYZBy(-90);
+ }
+ else if(j == 5)
+ {
+ for(u16 i=0; i<4; i++)
+ vertices[i].Pos.rotateYZBy(90);
+ }
+
+ for(u16 i=0; i<4; i++)
+ {
+ vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+ }
+
+ u16 indices[] = {0,1,2,2,3,0};
+ // Add to mesh collector
+ collector.append(material_leaves1, vertices, 4, indices, 6);
+ }
+ }
+ /*
+ Add glass
+ */
+ else if(n.d == CONTENT_GLASS)
+ {
+ u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
+ video::SColor c(255,l,l,l);
+
+ for(u32 j=0; j<6; j++)
+ {
+ video::S3DVertex vertices[4] =
+ {
+ video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
+ pa_glass.x0(), pa_glass.y1()),
+ video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
+ pa_glass.x1(), pa_glass.y1()),
+ video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
+ pa_glass.x1(), pa_glass.y0()),
+ video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
+ pa_glass.x0(), pa_glass.y0()),
+ };
+
+ if(j == 0)
+ {
+ for(u16 i=0; i<4; i++)
+ vertices[i].Pos.rotateXZBy(0);
+ }
+ else if(j == 1)
+ {
+ for(u16 i=0; i<4; i++)
+ vertices[i].Pos.rotateXZBy(180);
+ }
+ else if(j == 2)
+ {
+ for(u16 i=0; i<4; i++)
+ vertices[i].Pos.rotateXZBy(-90);
+ }
+ else if(j == 3)
+ {
+ for(u16 i=0; i<4; i++)
+ vertices[i].Pos.rotateXZBy(90);
+ }
+ else if(j == 4)
+ {
+ for(u16 i=0; i<4; i++)
+ vertices[i].Pos.rotateYZBy(-90);
+ }
+ else if(j == 5)
+ {
+ for(u16 i=0; i<4; i++)
+ vertices[i].Pos.rotateYZBy(90);
+ }
+
+ for(u16 i=0; i<4; i++)
+ {
+ vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+ }
+
+ u16 indices[] = {0,1,2,2,3,0};
+ // Add to mesh collector
+ collector.append(material_glass, vertices, 4, indices, 6);
+ }
+ }
+
+
+
+ }
+
+ /*
+ Add stuff from collector to mesh
+ */
+
+ scene::SMesh *mesh_new = NULL;
+ mesh_new = new scene::SMesh();
+
+ collector.fillMesh(mesh_new);
+
+ /*
+ Do some stuff to the mesh
+ */
+
+ mesh_new->recalculateBoundingBox();
+
+ /*
+ Delete new mesh if it is empty
+ */
+
+ if(mesh_new->getMeshBufferCount() == 0)
+ {
+ mesh_new->drop();
+ mesh_new = NULL;
+ }
+
+ if(mesh_new)
+ {
+#if 0
+ // Usually 1-700 faces and 1-7 materials
+ std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
+ <<"and uses "<<mesh_new->getMeshBufferCount()
+ <<" materials (meshbuffers)"<<std::endl;
+#endif
+
+ // Use VBO for mesh (this just would set this for ever buffer)
+ // This will lead to infinite memory usage because or irrlicht.
+ //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
+
+ /*
+ NOTE: If that is enabled, some kind of a queue to the main
+ thread should be made which would call irrlicht to delete
+ the hardware buffer and then delete the mesh
+ */
+ }
+
+ return mesh_new;
+
+ //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
+}
+
+#endif // !SERVER
+
+/*
+ MapBlock
+*/
+
+MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
+ m_parent(parent),
+ m_pos(pos),
+ changed(true),
+ is_underground(false),
+ m_lighting_expired(true),
+ m_day_night_differs(false),
+ //m_not_fully_generated(false),
+ m_objects(this)
+{
+ data = NULL;
+ if(dummy == false)
+ reallocate();
+
+ //m_spawn_timer = -10000;
+
+#ifndef SERVER
+ m_mesh_expired = false;
+ mesh_mutex.Init();
+ mesh = NULL;
+ m_temp_mods_mutex.Init();
+#endif
+}
+
+MapBlock::~MapBlock()
+{
+#ifndef SERVER
+ {
+ JMutexAutoLock lock(mesh_mutex);
+
+ if(mesh)
+ {
+ mesh->drop();
+ mesh = NULL;
+ }
+ }
+#endif
+
+ if(data)
+ delete[] data;
+}