/* Calculate non-smooth lighting at interior of node. Single light bank. */ static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment, INodeDefManager *ndef) { u8 light = n.getLight(bank, ndef); while(increment > 0) { light = undiminish_light(light); --increment; } while(increment < 0) { light = diminish_light(light); ++increment; } return decode_light(light); }
/* Calculate non-smooth lighting at interior of node. Single light bank. */ static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment, MeshMakeData *data) { INodeDefManager *ndef = data->m_gamedef->ndef(); u8 light = n.getLight(bank, ndef); while(increment > 0) { light = undiminish_light(light); --increment; } while(increment < 0) { light = diminish_light(light); ++increment; } return decode_light(light); }
/* Lights neighbors of from_nodes, collects all them and then goes on recursively. */ void VoxelManipulator::spreadLight(enum LightBank bank, std::set<v3s16> & from_nodes, INodeDefManager *nodemgr) { const v3s16 dirs[6] = { v3s16(0,0,1), // back v3s16(0,1,0), // top v3s16(1,0,0), // right v3s16(0,0,-1), // front v3s16(0,-1,0), // bottom v3s16(-1,0,0), // left }; if(from_nodes.empty()) return; std::set<v3s16> lighted_nodes; for(std::set<v3s16>::iterator j = from_nodes.begin(); j != from_nodes.end(); ++j) { v3s16 pos = *j; VoxelArea voxel_area(pos - v3s16(1,1,1), pos + v3s16(1,1,1)); addArea(voxel_area); u32 i = m_area.index(pos); if(m_flags[i] & VOXELFLAG_NO_DATA) continue; MapNode &n = m_data[i]; u8 oldlight = n.getLight(bank, nodemgr); u8 newlight = diminish_light(oldlight); // Loop through 6 neighbors for(u16 i=0; i<6; i++) { // Get the position of the neighbor node v3s16 n2pos = pos + dirs[i]; try { u32 n2i = m_area.index(n2pos); if(m_flags[n2i] & VOXELFLAG_NO_DATA) continue; MapNode &n2 = m_data[n2i]; u8 light2 = n2.getLight(bank, nodemgr); /* If the neighbor is brighter than the current node, add to list (it will light up this node on its turn) */ if(light2 > undiminish_light(oldlight)) { lighted_nodes.insert(n2pos); } /* If the neighbor is dimmer than how much light this node would spread on it, add to list */ if(light2 < newlight) { if(nodemgr->get(n2).light_propagates) { n2.setLight(bank, newlight, nodemgr); lighted_nodes.insert(n2pos); } } } catch(InvalidPositionException &e) { continue; } } } /*dstream<<"spreadLight(): Changed block " <<blockchangecount<<" times" <<" for "<<from_nodes.size()<<" nodes" <<std::endl;*/ if(!lighted_nodes.empty()) spreadLight(bank, lighted_nodes, nodemgr); }
void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p, INodeDefManager *nodemgr) { const v3s16 dirs[6] = { v3s16(0,0,1), // back v3s16(0,1,0), // top v3s16(1,0,0), // right v3s16(0,0,-1), // front v3s16(0,-1,0), // bottom v3s16(-1,0,0), // left }; VoxelArea voxel_area(p - v3s16(1,1,1), p + v3s16(1,1,1)); addArea(voxel_area); u32 i = m_area.index(p); if(m_flags[i] & VOXELFLAG_NO_DATA) return; MapNode &n = m_data[i]; u8 oldlight = n.getLight(bank, nodemgr); u8 newlight = diminish_light(oldlight); // Loop through 6 neighbors for(u16 i=0; i<6; i++) { // Get the position of the neighbor node v3s16 n2pos = p + dirs[i]; u32 n2i = m_area.index(n2pos); if(m_flags[n2i] & VOXELFLAG_NO_DATA) continue; MapNode &n2 = m_data[n2i]; u8 light2 = n2.getLight(bank, nodemgr); /* If the neighbor is brighter than the current node, add to list (it will light up this node on its turn) */ if(light2 > undiminish_light(oldlight)) { spreadLight(bank, n2pos, nodemgr); } /* If the neighbor is dimmer than how much light this node would spread on it, add to list */ if(light2 < newlight) { if(nodemgr->get(n2).light_propagates) { n2.setLight(bank, newlight, nodemgr); spreadLight(bank, n2pos, nodemgr); } } } }
void mapblock_mesh_generate_special(MeshMakeData *data, MeshCollector &collector) { // 0ms //TimeTaker timer("mapblock_mesh_generate_special()"); /* Some settings */ bool new_style_water = g_settings.getBool("new_style_water"); bool new_style_leaves = g_settings.getBool("new_style_leaves"); //bool smooth_lighting = g_settings.getBool("smooth_lighting"); bool invisible_stone = g_settings.getBool("invisible_stone"); float node_water_level = 1.0; if(new_style_water) node_water_level = 0.85; v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE; // Flowing water material video::SMaterial material_water1; material_water1.setFlag(video::EMF_LIGHTING, false); material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false); material_water1.setFlag(video::EMF_BILINEAR_FILTER, false); material_water1.setFlag(video::EMF_FOG_ENABLE, true); material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA; AtlasPointer pa_water1 = g_texturesource->getTexture( g_texturesource->getTextureId("water.png")); material_water1.setTexture(0, pa_water1.atlas); // New-style leaves material video::SMaterial material_leaves1; material_leaves1.setFlag(video::EMF_LIGHTING, false); //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false); material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false); material_leaves1.setFlag(video::EMF_FOG_ENABLE, true); material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; AtlasPointer pa_leaves1 = g_texturesource->getTexture( g_texturesource->getTextureId("leaves.png")); material_leaves1.setTexture(0, pa_leaves1.atlas); // Glass material video::SMaterial material_glass; material_glass.setFlag(video::EMF_LIGHTING, false); material_glass.setFlag(video::EMF_BILINEAR_FILTER, false); material_glass.setFlag(video::EMF_FOG_ENABLE, true); material_glass.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; AtlasPointer pa_glass = g_texturesource->getTexture( g_texturesource->getTextureId("glass.png")); material_glass.setTexture(0, pa_glass.atlas); // Wood material video::SMaterial material_wood; material_wood.setFlag(video::EMF_LIGHTING, false); material_wood.setFlag(video::EMF_BILINEAR_FILTER, false); material_wood.setFlag(video::EMF_FOG_ENABLE, true); material_wood.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; AtlasPointer pa_wood = g_texturesource->getTexture( g_texturesource->getTextureId("wood.png")); material_wood.setTexture(0, pa_wood.atlas); // General ground material for special output // Texture is modified just before usage video::SMaterial material_general; material_general.setFlag(video::EMF_LIGHTING, false); material_general.setFlag(video::EMF_BILINEAR_FILTER, false); material_general.setFlag(video::EMF_FOG_ENABLE, true); material_general.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; // Papyrus material video::SMaterial material_papyrus; material_papyrus.setFlag(video::EMF_LIGHTING, false); material_papyrus.setFlag(video::EMF_BILINEAR_FILTER, false); material_papyrus.setFlag(video::EMF_FOG_ENABLE, true); material_papyrus.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; AtlasPointer pa_papyrus = g_texturesource->getTexture( g_texturesource->getTextureId("papyrus.png")); material_papyrus.setTexture(0, pa_papyrus.atlas); for(s16 z=0; z<MAP_BLOCKSIZE; z++) for(s16 y=0; y<MAP_BLOCKSIZE; y++) for(s16 x=0; x<MAP_BLOCKSIZE; x++) { v3s16 p(x,y,z); MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p); /* Add torches to mesh */ if(n.d == CONTENT_TORCH) { video::SColor c(255,255,255,255); // Wall at X+ of node video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1), video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1), video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0), video::S3DVertex(-BS/2,BS/2,0, 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.rotateXZBy(45); if(dir == v3s16(0,1,0)) vertices[i].Pos.rotateXZBy(-45); 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 */ else 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 fence */ else if(n.d == CONTENT_FENCE) { u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio))); video::SColor c(255,l,l,l); const f32 post_rad=(f32)BS/10; const f32 bar_rad=(f32)BS/20; const f32 bar_len=(f32)(BS/2)-post_rad; // The post - always present v3f pos = intToFloat(p+blockpos_nodes, BS); f32 postuv[24]={ 0.4,0.4,0.6,0.6, 0.35,0,0.65,1, 0.35,0,0.65,1, 0.35,0,0.65,1, 0.35,0,0.65,1, 0.4,0.4,0.6,0.6}; makeCuboid(material_wood, &collector, &pa_wood, c, pos, post_rad,BS/2,post_rad, postuv); // Now a section of fence, +X, if there's a post there v3s16 p2 = p; p2.X++; MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); if(n2.d == CONTENT_FENCE) { pos = intToFloat(p+blockpos_nodes, BS); pos.X += BS/2; pos.Y += BS/4; f32 xrailuv[24]={ 0,0.4,1,0.6, 0,0.4,1,0.6, 0,0.4,1,0.6, 0,0.4,1,0.6, 0,0.4,1,0.6, 0,0.4,1,0.6}; makeCuboid(material_wood, &collector, &pa_wood, c, pos, bar_len,bar_rad,bar_rad, xrailuv); pos.Y -= BS/2; makeCuboid(material_wood, &collector, &pa_wood, c, pos, bar_len,bar_rad,bar_rad, xrailuv); } // Now a section of fence, +Z, if there's a post there p2 = p; p2.Z++; n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); if(n2.d == CONTENT_FENCE) { pos = intToFloat(p+blockpos_nodes, BS); pos.Z += BS/2; pos.Y += BS/4; f32 zrailuv[24]={ 0,0.4,1,0.6, 0,0.4,1,0.6, 0,0.4,1,0.6, 0,0.4,1,0.6, 0,0.4,1,0.6, 0,0.4,1,0.6}; makeCuboid(material_wood, &collector, &pa_wood, c, pos, bar_rad,bar_rad,bar_len, zrailuv); pos.Y -= BS/2; makeCuboid(material_wood, &collector, &pa_wood, c, pos, bar_rad,bar_rad,bar_len, zrailuv); } } #if 1 /* Add stones with minerals if stone is invisible */ else if(n.d == CONTENT_STONE && invisible_stone && n.getMineral() != MINERAL_NONE) { for(u32 j=0; j<6; j++) { // NOTE: Hopefully g_6dirs[j] is the right direction... v3s16 dir = g_6dirs[j]; /*u8 l = 0; MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + dir); if(content_features(n2.d).param_type == CPT_LIGHT) l = decode_light(n2.getLightBlend(data->m_daynight_ratio)); else l = 255;*/ u8 l = 255; video::SColor c(255,l,l,l); // Get the right texture TileSpec ts = n.getTile(dir); AtlasPointer ap = ts.texture; material_general.setTexture(0, ap.atlas); 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, ap.x0(), ap.y1()), video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, ap.x1(), ap.y1()), video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, ap.x1(), ap.y0()), video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, ap.x0(), ap.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 += intToFloat(p + blockpos_nodes, BS); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(material_general, vertices, 4, indices, 6); } } #endif else if(n.d == CONTENT_PAPYRUS) { u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio))); video::SColor c(255,l,l,l); for(u32 j=0; j<4; j++) { video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, pa_papyrus.x0(), pa_papyrus.y1()), video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, pa_papyrus.x1(), pa_papyrus.y1()), video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, pa_papyrus.x1(), pa_papyrus.y0()), video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, pa_papyrus.x0(), pa_papyrus.y0()), }; if(j == 0) { for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(45); } else if(j == 1) { for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(-45); } else if(j == 2) { for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(135); } else if(j == 3) { for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(-135); } 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_papyrus, vertices, 4, indices, 6); } } else if(n.d == CONTENT_RAIL) { u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio)); video::SColor c(255,l,l,l); bool is_rail_x [] = { false, false }; /* x-1, x+1 */ bool is_rail_z [] = { false, false }; /* z-1, z+1 */ MapNode n_minus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1,y,z)); MapNode n_plus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1,y,z)); MapNode n_minus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z-1)); MapNode n_plus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z+1)); if(n_minus_x.d == CONTENT_RAIL) is_rail_x[0] = true; if(n_plus_x.d == CONTENT_RAIL) is_rail_x[1] = true; if(n_minus_z.d == CONTENT_RAIL) is_rail_z[0] = true; if(n_plus_z.d == CONTENT_RAIL) is_rail_z[1] = true; float d = (float)BS/16; video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,-BS/2+d,-BS/2, 0,0,0, c, 0, 1), video::S3DVertex(BS/2,-BS/2+d,-BS/2, 0,0,0, c, 1, 1), video::S3DVertex(BS/2,-BS/2+d,BS/2, 0,0,0, c, 1, 0), video::S3DVertex(-BS/2,-BS/2+d,BS/2, 0,0,0, c, 0, 0), }; video::SMaterial material_rail; material_rail.setFlag(video::EMF_LIGHTING, false); material_rail.setFlag(video::EMF_BACK_FACE_CULLING, false); material_rail.setFlag(video::EMF_BILINEAR_FILTER, false); material_rail.setFlag(video::EMF_FOG_ENABLE, true); material_rail.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; int adjacencies = is_rail_x[0] + is_rail_x[1] + is_rail_z[0] + is_rail_z[1]; // Assign textures if(adjacencies < 2) material_rail.setTexture(0, g_texturesource->getTextureRaw("rail.png")); else if(adjacencies == 2) { if((is_rail_x[0] && is_rail_x[1]) || (is_rail_z[0] && is_rail_z[1])) material_rail.setTexture(0, g_texturesource->getTextureRaw("rail.png")); else material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_curved.png")); } else if(adjacencies == 3) material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_t_junction.png")); else if(adjacencies == 4) material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_crossing.png")); // Rotate textures int angle = 0; if(adjacencies == 1) { if(is_rail_x[0] || is_rail_x[1]) angle = 90; } else if(adjacencies == 2) { if(is_rail_x[0] && is_rail_x[1]) angle = 90; else if(is_rail_x[0] && is_rail_z[0]) angle = 270; else if(is_rail_x[0] && is_rail_z[1]) angle = 180; else if(is_rail_x[1] && is_rail_z[1]) angle = 90; } else if(adjacencies == 3) { if(!is_rail_x[0]) angle=0; if(!is_rail_x[1]) angle=180; if(!is_rail_z[0]) angle=90; if(!is_rail_z[1]) angle=270; } if(angle != 0) { for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(angle); } for(s32 i=0; i<4; i++) { vertices[i].Pos += intToFloat(p + blockpos_nodes, BS); } u16 indices[] = {0,1,2,2,3,0}; collector.append(material_rail, vertices, 4, indices, 6); } } }
void mapblock_mesh_generate_special(MeshMakeData *data, MeshCollector &collector, IGameDef *gamedef) { INodeDefManager *nodedef = gamedef->ndef(); // 0ms //TimeTaker timer("mapblock_mesh_generate_special()"); /* Some settings */ bool new_style_water = g_settings->getBool("new_style_water"); float node_liquid_level = 1.0; if(new_style_water) node_liquid_level = 0.85; v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE; /*// General ground material for special output // Texture is modified just before usage video::SMaterial material_general; material_general.setFlag(video::EMF_LIGHTING, false); material_general.setFlag(video::EMF_BILINEAR_FILTER, false); material_general.setFlag(video::EMF_FOG_ENABLE, true); material_general.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;*/ for(s16 z=0; z<MAP_BLOCKSIZE; z++) for(s16 y=0; y<MAP_BLOCKSIZE; y++) for(s16 x=0; x<MAP_BLOCKSIZE; x++) { v3s16 p(x,y,z); MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p); const ContentFeatures &f = nodedef->get(n); // Only solidness=0 stuff is drawn here if(f.solidness != 0) continue; switch(f.drawtype){ default: infostream<<"Got "<<f.drawtype<<std::endl; assert(0); break; case NDT_AIRLIKE: break; case NDT_LIQUID: { /* Add water sources to mesh if using new style */ assert(nodedef->get(n).special_materials[0]); //assert(nodedef->get(n).special_materials[1]); assert(nodedef->get(n).special_aps[0]); video::SMaterial &liquid_material = *nodedef->get(n).special_materials[0]; /*video::SMaterial &liquid_material_bfculled = *nodedef->get(n).special_materials[1];*/ AtlasPointer &pa_liquid1 = *nodedef->get(n).special_aps[0]; bool top_is_air = false; MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z)); if(n.getContent() == CONTENT_AIR) top_is_air = true; if(top_is_air == false) continue; u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef)); video::SColor c = MapBlock_LightColor( nodedef->get(n).alpha, l); video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, pa_liquid1.x0(), pa_liquid1.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, pa_liquid1.x1(), pa_liquid1.y1()), video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, pa_liquid1.x1(), pa_liquid1.y0()), video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, pa_liquid1.x0(), pa_liquid1.y0()), }; for(s32 i=0; i<4; i++) { vertices[i].Pos.Y += (-0.5+node_liquid_level)*BS; vertices[i].Pos += intToFloat(p + blockpos_nodes, BS); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(liquid_material, vertices, 4, indices, 6); break;} case NDT_FLOWINGLIQUID: { /* Add flowing liquid to mesh */ assert(nodedef->get(n).special_materials[0]); assert(nodedef->get(n).special_materials[1]); assert(nodedef->get(n).special_aps[0]); video::SMaterial &liquid_material = *nodedef->get(n).special_materials[0]; video::SMaterial &liquid_material_bfculled = *nodedef->get(n).special_materials[1]; AtlasPointer &pa_liquid1 = *nodedef->get(n).special_aps[0]; bool top_is_same_liquid = false; MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z)); content_t c_flowing = nodedef->getId(nodedef->get(n).liquid_alternative_flowing); content_t c_source = nodedef->getId(nodedef->get(n).liquid_alternative_source); if(ntop.getContent() == c_flowing || ntop.getContent() == c_source) top_is_same_liquid = true; u8 l = 0; // Use the light of the node on top if possible if(nodedef->get(ntop).param_type == CPT_LIGHT) l = decode_light(ntop.getLightBlend(data->m_daynight_ratio, nodedef)); // Otherwise use the light of this node (the liquid) else l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef)); video::SColor c = MapBlock_LightColor( nodedef->get(n).alpha, l); // Neighbor liquid levels (key = relative position) // Includes current node core::map<v3s16, f32> neighbor_levels; core::map<v3s16, content_t> neighbor_contents; core::map<v3s16, u8> neighbor_flags; const u8 neighborflag_top_is_same_liquid = 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++) { content_t 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.getContent() != CONTENT_IGNORE) { content = n2.getContent(); if(n2.getContent() == c_source) level = (-0.5+node_liquid_level) * BS; else if(n2.getContent() == c_flowing) level = (-0.5 + ((float)(n2.param2&LIQUID_LEVEL_MASK) + 0.5) / 8.0 * node_liquid_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.getContent() == c_source || n2.getContent() == c_flowing) flags |= neighborflag_top_is_same_liquid; } neighbor_levels.insert(neighbor_dirs[i], level); neighbor_contents.insert(neighbor_dirs[i], content); neighbor_flags.insert(neighbor_dirs[i], flags); } // Corner heights (average between four liquids) 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; u32 air_count = 0; for(u32 j=0; j<4; j++) { v3s16 neighbordir = cornerdir - halfdirs[j]; content_t content = neighbor_contents[neighbordir]; // If top is liquid, draw starting from top of node if(neighbor_flags[neighbordir] & neighborflag_top_is_same_liquid) { cornerlevel = 0.5*BS; valid_count = 1; break; } // Source is always the same height else if(content == c_source) { cornerlevel = (-0.5+node_liquid_level)*BS; valid_count = 1; break; } // Flowing liquid has level information else if(content == c_flowing) { cornerlevel += neighbor_levels[neighbordir]; valid_count++; } else if(content == CONTENT_AIR) { air_count++; } } if(air_count >= 2) cornerlevel = -0.5*BS; else 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 liquid and neighbor's topside is liquid, don't draw side face */ if(top_is_same_liquid && neighbor_flags[dir] & neighborflag_top_is_same_liquid) continue; content_t neighbor_content = neighbor_contents[dir]; const ContentFeatures &n_feat = nodedef->get(neighbor_content); // Don't draw face if neighbor is blocking the view if(n_feat.solidness == 2) continue; bool neighbor_is_same_liquid = (neighbor_content == c_source || neighbor_content == c_flowing); // Don't draw any faces if neighbor same is liquid and top is // same liquid if(neighbor_is_same_liquid == true && top_is_same_liquid == false) continue; // Use backface culled material if neighbor doesn't have a // solidness of 0 video::SMaterial *current_material = &liquid_material; if(n_feat.solidness != 0 || n_feat.visual_solidness != 0) current_material = &liquid_material_bfculled; video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, pa_liquid1.x0(), pa_liquid1.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, pa_liquid1.x1(), pa_liquid1.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, pa_liquid1.x1(), pa_liquid1.y0()), video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, pa_liquid1.x0(), pa_liquid1.y0()), }; /* If our topside is liquid, set upper border of face at upper border of node */ if(top_is_same_liquid) { 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 liquid, lower border of face is corner liquid levels */ if(neighbor_is_same_liquid) { vertices[0].Pos.Y = corner_levels[side_corners[i][1]]; vertices[1].Pos.Y = corner_levels[side_corners[i][0]]; } /* If neighbor is not liquid, 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); // Do this to not cause glitches when two liquids are // side-by-side /*if(neighbor_is_same_liquid == false){ vertices[j].Pos.X *= 0.98; vertices[j].Pos.Z *= 0.98; }*/ vertices[j].Pos += intToFloat(p + blockpos_nodes, BS); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(*current_material, vertices, 4, indices, 6); } /* Generate top side, if appropriate */ if(top_is_same_liquid == false) { video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, pa_liquid1.x0(), pa_liquid1.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, pa_liquid1.x1(), pa_liquid1.y1()), video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, pa_liquid1.x1(), pa_liquid1.y0()), video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, pa_liquid1.x0(), pa_liquid1.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 += liquid_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(liquid_material, vertices, 4, indices, 6); } break;} case NDT_GLASSLIKE: { video::SMaterial material_glass; material_glass.setFlag(video::EMF_LIGHTING, false); material_glass.setFlag(video::EMF_BILINEAR_FILTER, false); material_glass.setFlag(video::EMF_FOG_ENABLE, true); material_glass.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; AtlasPointer pa_glass = f.tiles[0].texture; material_glass.setTexture(0, pa_glass.atlas); u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef))); video::SColor c = MapBlock_LightColor(255, l); for(u32 j=0; j<6; j++) { // Check this neighbor v3s16 n2p = blockpos_nodes + p + g_6dirs[j]; MapNode n2 = data->m_vmanip.getNodeNoEx(n2p); // Don't make face if neighbor is of same type if(n2.getContent() == n.getContent()) continue; // The face at Z+ 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()), }; // Rotations in the g_6dirs format if(j == 0) // Z+ for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(0); else if(j == 1) // Y+ for(u16 i=0; i<4; i++) vertices[i].Pos.rotateYZBy(-90); else if(j == 2) // X+ for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(-90); else if(j == 3) // Z- for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(180); else if(j == 4) // Y- for(u16 i=0; i<4; i++) vertices[i].Pos.rotateYZBy(90); else if(j == 5) // X- for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(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); } break;} case NDT_ALLFACES: { video::SMaterial material_leaves1; material_leaves1.setFlag(video::EMF_LIGHTING, false); material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false); material_leaves1.setFlag(video::EMF_FOG_ENABLE, true); material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; AtlasPointer pa_leaves1 = f.tiles[0].texture; material_leaves1.setTexture(0, pa_leaves1.atlas); u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef))); video::SColor c = MapBlock_LightColor(255, 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_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()), }; // Rotations in the g_6dirs format if(j == 0) // Z+ for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(0); else if(j == 1) // Y+ for(u16 i=0; i<4; i++) vertices[i].Pos.rotateYZBy(-90); else if(j == 2) // X+ for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(-90); else if(j == 3) // Z- for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(180); else if(j == 4) // Y- for(u16 i=0; i<4; i++) vertices[i].Pos.rotateYZBy(90); else if(j == 5) // X- for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(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); } break;} case NDT_ALLFACES_OPTIONAL: // This is always pre-converted to something else assert(0); break; case NDT_TORCHLIKE: { v3s16 dir = unpackDir(n.param2); AtlasPointer ap(0); if(dir == v3s16(0,-1,0)){ ap = f.tiles[0].texture; // floor } else if(dir == v3s16(0,1,0)){ ap = f.tiles[1].texture; // ceiling // For backwards compatibility } else if(dir == v3s16(0,0,0)){ ap = f.tiles[0].texture; // floor } else { ap = f.tiles[2].texture; // side } // 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, ap.atlas); video::SColor c(255,255,255,255); // Wall at X+ of node video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, ap.x0(), ap.y1()), video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, ap.x1(), ap.y1()), video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, ap.x1(), ap.y0()), video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, ap.x0(), ap.y0()), }; 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.rotateXZBy(45); if(dir == v3s16(0,1,0)) vertices[i].Pos.rotateXZBy(-45); vertices[i].Pos += intToFloat(p + blockpos_nodes, BS); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(material, vertices, 4, indices, 6); break;} case NDT_SIGNLIKE: { // 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_REF; AtlasPointer ap = f.tiles[0].texture; material.setTexture(0, ap.atlas); u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef)); video::SColor c = MapBlock_LightColor(255, 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, ap.x0(), ap.y1()), video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, ap.x1(), ap.y1()), video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, ap.x1(), ap.y0()), video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, ap.x0(), ap.y0()), }; v3s16 dir = unpackDir(n.param2); 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); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(material, vertices, 4, indices, 6); break;} case NDT_PLANTLIKE: { video::SMaterial material_papyrus; material_papyrus.setFlag(video::EMF_LIGHTING, false); material_papyrus.setFlag(video::EMF_BILINEAR_FILTER, false); material_papyrus.setFlag(video::EMF_FOG_ENABLE, true); material_papyrus.MaterialType=video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; AtlasPointer pa_papyrus = f.tiles[0].texture; material_papyrus.setTexture(0, pa_papyrus.atlas); u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef))); video::SColor c = MapBlock_LightColor(255, l); for(u32 j=0; j<4; j++) { video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2*f.visual_scale,-BS/2,0, 0,0,0, c, pa_papyrus.x0(), pa_papyrus.y1()), video::S3DVertex( BS/2*f.visual_scale,-BS/2,0, 0,0,0, c, pa_papyrus.x1(), pa_papyrus.y1()), video::S3DVertex( BS/2*f.visual_scale, -BS/2 + f.visual_scale*BS,0, 0,0,0, c, pa_papyrus.x1(), pa_papyrus.y0()), video::S3DVertex(-BS/2*f.visual_scale, -BS/2 + f.visual_scale*BS,0, 0,0,0, c, pa_papyrus.x0(), pa_papyrus.y0()), }; if(j == 0) { for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(45); } else if(j == 1) { for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(-45); } else if(j == 2) { for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(135); } else if(j == 3) { for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(-135); } for(u16 i=0; i<4; i++) { vertices[i].Pos *= f.visual_scale; vertices[i].Pos += intToFloat(p + blockpos_nodes, BS); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(material_papyrus, vertices, 4, indices, 6); } break;} case NDT_FENCELIKE: { video::SMaterial material_wood; material_wood.setFlag(video::EMF_LIGHTING, false); material_wood.setFlag(video::EMF_BILINEAR_FILTER, false); material_wood.setFlag(video::EMF_FOG_ENABLE, true); material_wood.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; AtlasPointer pa_wood = f.tiles[0].texture; material_wood.setTexture(0, pa_wood.atlas); u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef))); video::SColor c = MapBlock_LightColor(255, l); const f32 post_rad=(f32)BS/10; const f32 bar_rad=(f32)BS/20; const f32 bar_len=(f32)(BS/2)-post_rad; // The post - always present v3f pos = intToFloat(p+blockpos_nodes, BS); f32 postuv[24]={ 0.4,0.4,0.6,0.6, 0.35,0,0.65,1, 0.35,0,0.65,1, 0.35,0,0.65,1, 0.35,0,0.65,1, 0.4,0.4,0.6,0.6}; makeCuboid(material_wood, &collector, &pa_wood, c, pos, post_rad,BS/2,post_rad, postuv); // Now a section of fence, +X, if there's a post there v3s16 p2 = p; p2.X++; MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); const ContentFeatures *f2 = &nodedef->get(n2); if(f2->drawtype == NDT_FENCELIKE) { pos = intToFloat(p+blockpos_nodes, BS); pos.X += BS/2; pos.Y += BS/4; f32 xrailuv[24]={ 0,0.4,1,0.6, 0,0.4,1,0.6, 0,0.4,1,0.6, 0,0.4,1,0.6, 0,0.4,1,0.6, 0,0.4,1,0.6}; makeCuboid(material_wood, &collector, &pa_wood, c, pos, bar_len,bar_rad,bar_rad, xrailuv); pos.Y -= BS/2; makeCuboid(material_wood, &collector, &pa_wood, c, pos, bar_len,bar_rad,bar_rad, xrailuv); } // Now a section of fence, +Z, if there's a post there p2 = p; p2.Z++; n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); f2 = &nodedef->get(n2); if(f2->drawtype == NDT_FENCELIKE) { pos = intToFloat(p+blockpos_nodes, BS); pos.Z += BS/2; pos.Y += BS/4; f32 zrailuv[24]={ 0,0.4,1,0.6, 0,0.4,1,0.6, 0,0.4,1,0.6, 0,0.4,1,0.6, 0,0.4,1,0.6, 0,0.4,1,0.6}; makeCuboid(material_wood, &collector, &pa_wood, c, pos, bar_rad,bar_rad,bar_len, zrailuv); pos.Y -= BS/2; makeCuboid(material_wood, &collector, &pa_wood, c, pos, bar_rad,bar_rad,bar_len, zrailuv); } break;} case NDT_RAILLIKE: { bool is_rail_x [] = { false, false }; /* x-1, x+1 */ bool is_rail_z [] = { false, false }; /* z-1, z+1 */ MapNode n_minus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1,y,z)); MapNode n_plus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1,y,z)); MapNode n_minus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z-1)); MapNode n_plus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z+1)); content_t thiscontent = n.getContent(); if(n_minus_x.getContent() == thiscontent) is_rail_x[0] = true; if(n_plus_x.getContent() == thiscontent) is_rail_x[1] = true; if(n_minus_z.getContent() == thiscontent) is_rail_z[0] = true; if(n_plus_z.getContent() == thiscontent) is_rail_z[1] = true; int adjacencies = is_rail_x[0] + is_rail_x[1] + is_rail_z[0] + is_rail_z[1]; // Assign textures AtlasPointer ap = f.tiles[0].texture; // straight if(adjacencies < 2) ap = f.tiles[0].texture; // straight else if(adjacencies == 2) { if((is_rail_x[0] && is_rail_x[1]) || (is_rail_z[0] && is_rail_z[1])) ap = f.tiles[0].texture; // straight else ap = f.tiles[1].texture; // curved } else if(adjacencies == 3) ap = f.tiles[2].texture; // t-junction else if(adjacencies == 4) ap = f.tiles[3].texture; // crossing video::SMaterial material_rail; material_rail.setFlag(video::EMF_LIGHTING, false); material_rail.setFlag(video::EMF_BACK_FACE_CULLING, false); material_rail.setFlag(video::EMF_BILINEAR_FILTER, false); material_rail.setFlag(video::EMF_FOG_ENABLE, true); material_rail.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; material_rail.setTexture(0, ap.atlas); u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef)); video::SColor c = MapBlock_LightColor(255, l); float d = (float)BS/16; video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,-BS/2+d,-BS/2, 0,0,0, c, ap.x0(), ap.y1()), video::S3DVertex(BS/2,-BS/2+d,-BS/2, 0,0,0, c, ap.x1(), ap.y1()), video::S3DVertex(BS/2,-BS/2+d,BS/2, 0,0,0, c, ap.x1(), ap.y0()), video::S3DVertex(-BS/2,-BS/2+d,BS/2, 0,0,0, c, ap.x0(), ap.y0()), }; // Rotate textures int angle = 0; if(adjacencies == 1) { if(is_rail_x[0] || is_rail_x[1]) angle = 90; } else if(adjacencies == 2) { if(is_rail_x[0] && is_rail_x[1]) angle = 90; else if(is_rail_x[0] && is_rail_z[0]) angle = 270; else if(is_rail_x[0] && is_rail_z[1]) angle = 180; else if(is_rail_x[1] && is_rail_z[1]) angle = 90; } else if(adjacencies == 3) { if(!is_rail_x[0]) angle=0; if(!is_rail_x[1]) angle=180; if(!is_rail_z[0]) angle=90; if(!is_rail_z[1]) angle=270; } if(angle != 0) { for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(angle); } for(s32 i=0; i<4; i++) { vertices[i].Pos += intToFloat(p + blockpos_nodes, BS); } u16 indices[] = {0,1,2,2,3,0}; collector.append(material_rail, vertices, 4, indices, 6); break;} } } }
/* Lights neighbors of from_nodes, collects all them and then goes on recursively. */ void VoxelManipulator::spreadLight(enum LightBank bank, core::map<v3s16, bool> & from_nodes, INodeDefManager *nodemgr) { const v3s16 dirs[6] = { v3s16(0,0,1), // back v3s16(0,1,0), // top v3s16(1,0,0), // right v3s16(0,0,-1), // front v3s16(0,-1,0), // bottom v3s16(-1,0,0), // left }; if(from_nodes.size() == 0) return; core::map<v3s16, bool> lighted_nodes; core::map<v3s16, bool>::Iterator j; j = from_nodes.getIterator(); for(; j.atEnd() == false; j++) { v3s16 pos = j.getNode()->getKey(); emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1))); u32 i = m_area.index(pos); if(m_flags[i] & VOXELFLAG_INEXISTENT) continue; MapNode &n = m_data[i]; u8 oldlight = n.getLight(bank, nodemgr); u8 newlight = diminish_light(oldlight); // Loop through 6 neighbors for(u16 i=0; i<6; i++) { // Get the position of the neighbor node v3s16 n2pos = pos + dirs[i]; try { u32 n2i = m_area.index(n2pos); if(m_flags[n2i] & VOXELFLAG_INEXISTENT) continue; MapNode &n2 = m_data[n2i]; u8 light2 = n2.getLight(bank, nodemgr); /* If the neighbor is brighter than the current node, add to list (it will light up this node on its turn) */ if(light2 > undiminish_light(oldlight)) { lighted_nodes.insert(n2pos, true); } /* If the neighbor is dimmer than how much light this node would spread on it, add to list */ if(light2 < newlight) { if(nodemgr->get(n2).light_propagates) { n2.setLight(bank, newlight, nodemgr); lighted_nodes.insert(n2pos, true); } } } catch(InvalidPositionException &e) { continue; } } } /*dstream<<"spreadLight(): Changed block " <<blockchangecount<<" times" <<" for "<<from_nodes.size()<<" nodes" <<std::endl;*/ if(lighted_nodes.size() > 0) spreadLight(bank, lighted_nodes, nodemgr); }