/* Calculate non-smooth lighting at face of node. Single light bank. */ static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef) { u8 light; u8 l1 = n.getLight(bank, ndef); u8 l2 = n2.getLight(bank, ndef); if(l1 > l2) light = l1; else light = l2; // Boost light level for light sources u8 light_source = MYMAX(ndef->get(n).light_source, ndef->get(n2).light_source); if(light_source > light) light = light_source; 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); }
void Particle::updateLight() { u8 light = 0; bool pos_ok; v3s16 p = v3s16( floor(m_pos.X+0.5), floor(m_pos.Y+0.5), floor(m_pos.Z+0.5) ); MapNode n = m_env->getClientMap().getNodeNoEx(p, &pos_ok); if (pos_ok) light = n.getLightBlend(m_env->getDayNightRatio(), m_gamedef->ndef()); else light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0); u8 m_light = decode_light(light + m_glow); m_color.set(255, m_light * m_base_color.getRed() / 255, m_light * m_base_color.getGreen() / 255, m_light * m_base_color.getBlue() / 255); }
/* Calculate non-smooth lighting at face of node. Single light bank. */ static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2, v3s16 face_dir, MeshMakeData *data) { INodeDefManager *ndef = data->m_gamedef->ndef(); u8 light; u8 l1 = n.getLight(bank, ndef); u8 l2 = n2.getLight(bank, ndef); if(l1 > l2) light = l1; else light = l2; // Boost light level for light sources u8 light_source = MYMAX(ndef->get(n).light_source, ndef->get(n2).light_source); //if(light_source >= light) //return decode_light(undiminish_light(light_source)); if(light_source > light) //return decode_light(light_source); light = light_source; // Make some nice difference to different sides // This makes light come from a corner /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1) light = diminish_light(diminish_light(light)); else if(face_dir.X == -1 || face_dir.Z == -1) light = diminish_light(light);*/ // All neighboring faces have different shade (like in minecraft) if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1) light = diminish_light(diminish_light(light)); else if(face_dir.Z == 1 || face_dir.Z == -1) light = diminish_light(light); return decode_light(light); }
/* Calculate smooth lighting at the XYZ- corner of p. Single light bank. */ static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data) { static v3s16 dirs8[8] = { v3s16(0,0,0), v3s16(0,0,1), v3s16(0,1,0), v3s16(0,1,1), v3s16(1,0,0), v3s16(1,1,0), v3s16(1,0,1), v3s16(1,1,1), }; INodeDefManager *ndef = data->m_gamedef->ndef(); u16 ambient_occlusion = 0; u16 light = 0; u16 light_count = 0; u8 light_source_max = 0; for(u32 i=0; i<8; i++) { MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]); const ContentFeatures &f = ndef->get(n); if(f.light_source > light_source_max) light_source_max = f.light_source; // Check f.solidness because fast-style leaves look // better this way if(f.param_type == CPT_LIGHT && f.solidness != 2) { light += decode_light(n.getLight(bank, ndef)); light_count++; } else if(n.getContent() != CONTENT_IGNORE) { ambient_occlusion++; } } if(light_count == 0) return 255; light /= light_count; // Boost brightness around light sources if(decode_light(light_source_max) >= light) //return decode_light(undiminish_light(light_source_max)); return decode_light(light_source_max); if(ambient_occlusion > 4) { //ambient_occlusion -= 4; //light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0); float light_amount = (8 - ambient_occlusion) / 4.0; float light_f = (float)light / 255.0; light_f = pow(light_f, 2.2f); // gamma -> linear space light_f = light_f * light_amount; light_f = pow(light_f, 1.0f/2.2f); // linear -> gamma space if(light_f > 1.0) light_f = 1.0; light = 255.0 * light_f + 0.5; } return light; }
void mapblock_mesh_generate_special(MeshMakeData *data, MeshCollector &collector) { INodeDefManager *nodedef = data->m_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; 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 */ TileSpec tile_liquid = f.special_tiles[0]; AtlasPointer &pa_liquid = tile_liquid.texture; 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; u16 l = getInteriorLight(n, 0, data); video::SColor c = MapBlock_LightColor(f.alpha, l); video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, pa_liquid.x0(), pa_liquid.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, pa_liquid.x1(), pa_liquid.y1()), video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, pa_liquid.x1(), pa_liquid.y0()), video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, pa_liquid.x0(), pa_liquid.y0()), }; v3f offset(p.X, p.Y + (-0.5+node_liquid_level)*BS, p.Z); for(s32 i=0; i<4; i++) { vertices[i].Pos += offset; } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(tile_liquid, vertices, 4, indices, 6); break;} case NDT_FLOWINGLIQUID: { /* Add flowing liquid to mesh */ TileSpec tile_liquid = f.special_tiles[0]; TileSpec tile_liquid_bfculled = f.special_tiles[1]; AtlasPointer &pa_liquid = tile_liquid.texture; 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(f.liquid_alternative_flowing); content_t c_source = nodedef->getId(f.liquid_alternative_source); if(ntop.getContent() == c_flowing || ntop.getContent() == c_source) top_is_same_liquid = true; u16 l = 0; // If this liquid emits light and doesn't contain light, draw // it at what it emits, for an increased effect u8 light_source = nodedef->get(n).light_source; if(light_source != 0){ //l = decode_light(undiminish_light(light_source)); l = decode_light(light_source); l = l | (l<<8); } // Use the light of the node on top if possible else if(nodedef->get(ntop).param_type == CPT_LIGHT) l = getInteriorLight(ntop, 0, data); // Otherwise use the light of this node (the liquid) else l = getInteriorLight(n, 0, data); video::SColor c = MapBlock_LightColor(f.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 const TileSpec *current_tile = &tile_liquid; if(n_feat.solidness != 0 || n_feat.visual_solidness != 0) current_tile = &tile_liquid_bfculled; video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, pa_liquid.x0(), pa_liquid.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, pa_liquid.x1(), pa_liquid.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, pa_liquid.x1(), pa_liquid.y0()), video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, pa_liquid.x0(), pa_liquid.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, BS); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(*current_tile, 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_liquid.x0(), pa_liquid.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, pa_liquid.x1(), pa_liquid.y1()), video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, pa_liquid.x1(), pa_liquid.y0()), video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, pa_liquid.x0(), pa_liquid.y0()), }; // To get backface culling right, the vertices need to go // clockwise around the front of the face. And we happened to // calculate corner levels in exact reverse order. 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, BS); } // Default downwards-flowing texture animation goes from // -Z towards +Z, thus the direction is +Z. // Rotate texture to make animation go in flow direction // Positive if liquid moves towards +Z int dz = (corner_levels[side_corners[2][0]] + corner_levels[side_corners[2][1]] < corner_levels[side_corners[3][0]] + corner_levels[side_corners[3][1]]); // Positive if liquid moves towards +X int dx = (corner_levels[side_corners[0][0]] + corner_levels[side_corners[0][1]] < corner_levels[side_corners[1][0]] + corner_levels[side_corners[1][1]]); // -X if(-dx >= abs(dz)) { v2f t = vertices[0].TCoords; vertices[0].TCoords = vertices[1].TCoords; vertices[1].TCoords = vertices[2].TCoords; vertices[2].TCoords = vertices[3].TCoords; vertices[3].TCoords = t; } // +X if(dx >= abs(dz)) { v2f t = vertices[0].TCoords; vertices[0].TCoords = vertices[3].TCoords; vertices[3].TCoords = vertices[2].TCoords; vertices[2].TCoords = vertices[1].TCoords; vertices[1].TCoords = t; } // -Z if(-dz >= abs(dx)) { v2f t = vertices[0].TCoords; vertices[0].TCoords = vertices[3].TCoords; vertices[3].TCoords = vertices[2].TCoords; vertices[2].TCoords = vertices[1].TCoords; vertices[1].TCoords = t; t = vertices[0].TCoords; vertices[0].TCoords = vertices[3].TCoords; vertices[3].TCoords = vertices[2].TCoords; vertices[2].TCoords = vertices[1].TCoords; vertices[1].TCoords = t; } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(tile_liquid, vertices, 4, indices, 6); } break;} case NDT_GLASSLIKE: { TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data); AtlasPointer ap = tile.texture; u16 l = getInteriorLight(n, 1, data); 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, 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()), }; // 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, BS); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(tile, vertices, 4, indices, 6); } break;} case NDT_ALLFACES: { TileSpec tile_leaves = getNodeTile(n, p, v3s16(0,0,0), data); AtlasPointer pa_leaves = tile_leaves.texture; u16 l = getInteriorLight(n, 1, data); video::SColor c = MapBlock_LightColor(255, l); v3f pos = intToFloat(p, BS); aabb3f box(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2); box.MinEdge += pos; box.MaxEdge += pos; makeCuboid(&collector, box, &tile_leaves, 1, c, NULL); break;} case NDT_ALLFACES_OPTIONAL: // This is always pre-converted to something else assert(0); break; case NDT_TORCHLIKE: { v3s16 dir = n.getWallMountedDir(nodedef); u8 tileindex = 0; if(dir == v3s16(0,-1,0)){ tileindex = 0; // floor } else if(dir == v3s16(0,1,0)){ tileindex = 1; // ceiling // For backwards compatibility } else if(dir == v3s16(0,0,0)){ tileindex = 0; // floor } else { tileindex = 2; // side } TileSpec tile = getNodeTileN(n, p, tileindex, data); tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING; tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; AtlasPointer ap = tile.texture; 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, BS); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(tile, vertices, 4, indices, 6); break;} case NDT_SIGNLIKE: { TileSpec tile = getNodeTileN(n, p, 0, data); tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING; tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; AtlasPointer ap = tile.texture; u16 l = getInteriorLight(n, 0, data); 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.y0()), 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.x1(), ap.y1()), video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, ap.x0(), ap.y1()), }; v3s16 dir = n.getWallMountedDir(nodedef); 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, BS); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(tile, vertices, 4, indices, 6); break;} case NDT_PLANTLIKE: { TileSpec tile = getNodeTileN(n, p, 0, data); tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; AtlasPointer ap = tile.texture; u16 l = getInteriorLight(n, 1, data); 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, ap.x0(), ap.y1()), video::S3DVertex( BS/2*f.visual_scale,-BS/2,0, 0,0,0, c, ap.x1(), ap.y1()), video::S3DVertex( BS/2*f.visual_scale, -BS/2 + f.visual_scale*BS,0, 0,0,0, c, ap.x1(), ap.y0()), video::S3DVertex(-BS/2*f.visual_scale, -BS/2 + f.visual_scale*BS,0, 0,0,0, c, ap.x0(), ap.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, BS); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(tile, vertices, 4, indices, 6); } break;} case NDT_FENCELIKE: { TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data); TileSpec tile_nocrack = tile; tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK; // A hack to put wood the right way around in the posts ITextureSource *tsrc = data->m_gamedef->tsrc(); TileSpec tile_rot = tile; tile_rot.texture = tsrc->getTexture(tsrc->getTextureName( tile.texture.id) + "^[transformR90"); u16 l = getInteriorLight(n, 1, data); video::SColor c = MapBlock_LightColor(255, l); const f32 post_rad=(f32)BS/8; const f32 bar_rad=(f32)BS/16; const f32 bar_len=(f32)(BS/2)-post_rad; v3f pos = intToFloat(p, BS); // The post - always present aabb3f post(-post_rad,-BS/2,-post_rad,post_rad,BS/2,post_rad); post.MinEdge += pos; post.MaxEdge += pos; f32 postuv[24]={ 6/16.,6/16.,10/16.,10/16., 6/16.,6/16.,10/16.,10/16., 0/16.,0,4/16.,1, 4/16.,0,8/16.,1, 8/16.,0,12/16.,1, 12/16.,0,16/16.,1}; makeCuboid(&collector, post, &tile_rot, 1, c, 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) { aabb3f bar(-bar_len+BS/2,-bar_rad+BS/4,-bar_rad, bar_len+BS/2,bar_rad+BS/4,bar_rad); bar.MinEdge += pos; bar.MaxEdge += pos; f32 xrailuv[24]={ 0/16.,2/16.,16/16.,4/16., 0/16.,4/16.,16/16.,6/16., 6/16.,6/16.,8/16.,8/16., 10/16.,10/16.,12/16.,12/16., 0/16.,8/16.,16/16.,10/16., 0/16.,14/16.,16/16.,16/16.}; makeCuboid(&collector, bar, &tile_nocrack, 1, c, xrailuv); bar.MinEdge.Y -= BS/2; bar.MaxEdge.Y -= BS/2; makeCuboid(&collector, bar, &tile_nocrack, 1, c, 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) { aabb3f bar(-bar_rad,-bar_rad+BS/4,-bar_len+BS/2, bar_rad,bar_rad+BS/4,bar_len+BS/2); bar.MinEdge += pos; bar.MaxEdge += pos; f32 zrailuv[24]={ 3/16.,1/16.,5/16.,5/16., // cannot rotate; stretch 4/16.,1/16.,6/16.,5/16., // for wood texture instead 0/16.,9/16.,16/16.,11/16., 0/16.,6/16.,16/16.,8/16., 6/16.,6/16.,8/16.,8/16., 10/16.,10/16.,12/16.,12/16.}; makeCuboid(&collector, bar, &tile_nocrack, 1, c, zrailuv); bar.MinEdge.Y -= BS/2; bar.MaxEdge.Y -= BS/2; makeCuboid(&collector, bar, &tile_nocrack, 1, c, 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 */ bool is_rail_z_minus_y [] = { false, false }; /* z-1, z+1; y-1 */ bool is_rail_x_minus_y [] = { false, false }; /* x-1, z+1; y-1 */ bool is_rail_z_plus_y [] = { false, false }; /* z-1, z+1; y+1 */ bool is_rail_x_plus_y [] = { false, false }; /* x-1, x+1; y+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)); MapNode n_plus_x_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1, y+1, z)); MapNode n_plus_x_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1, y-1, z)); MapNode n_minus_x_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1, y+1, z)); MapNode n_minus_x_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1, y-1, z)); MapNode n_plus_z_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y+1, z+1)); MapNode n_minus_z_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y+1, z-1)); MapNode n_plus_z_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y-1, z+1)); MapNode n_minus_z_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y-1, z-1)); content_t thiscontent = n.getContent(); if(n_minus_x.getContent() == thiscontent) is_rail_x[0] = true; if (n_minus_x_minus_y.getContent() == thiscontent) is_rail_x_minus_y[0] = true; if(n_minus_x_plus_y.getContent() == thiscontent) is_rail_x_plus_y[0] = true; if(n_plus_x.getContent() == thiscontent) is_rail_x[1] = true; if (n_plus_x_minus_y.getContent() == thiscontent) is_rail_x_minus_y[1] = true; if(n_plus_x_plus_y.getContent() == thiscontent) is_rail_x_plus_y[1] = true; if(n_minus_z.getContent() == thiscontent) is_rail_z[0] = true; if (n_minus_z_minus_y.getContent() == thiscontent) is_rail_z_minus_y[0] = true; if(n_minus_z_plus_y.getContent() == thiscontent) is_rail_z_plus_y[0] = true; if(n_plus_z.getContent() == thiscontent) is_rail_z[1] = true; if (n_plus_z_minus_y.getContent() == thiscontent) is_rail_z_minus_y[1] = true; if(n_plus_z_plus_y.getContent() == thiscontent) is_rail_z_plus_y[1] = true; bool is_rail_x_all[] = {false, false}; bool is_rail_z_all[] = {false, false}; is_rail_x_all[0]=is_rail_x[0] || is_rail_x_minus_y[0] || is_rail_x_plus_y[0]; is_rail_x_all[1]=is_rail_x[1] || is_rail_x_minus_y[1] || is_rail_x_plus_y[1]; is_rail_z_all[0]=is_rail_z[0] || is_rail_z_minus_y[0] || is_rail_z_plus_y[0]; is_rail_z_all[1]=is_rail_z[1] || is_rail_z_minus_y[1] || is_rail_z_plus_y[1]; // reasonable default, flat straight unrotated rail bool is_straight = true; int adjacencies = 0; int angle = 0; u8 tileindex = 0; // check for sloped rail if (is_rail_x_plus_y[0] || is_rail_x_plus_y[1] || is_rail_z_plus_y[0] || is_rail_z_plus_y[1]) { adjacencies = 5; //5 means sloped is_straight = true; // sloped is always straight } else { // is really straight, rails on both sides is_straight = (is_rail_x_all[0] && is_rail_x_all[1]) || (is_rail_z_all[0] && is_rail_z_all[1]); adjacencies = is_rail_x_all[0] + is_rail_x_all[1] + is_rail_z_all[0] + is_rail_z_all[1]; } switch (adjacencies) { case 1: if(is_rail_x_all[0] || is_rail_x_all[1]) angle = 90; break; case 2: if(!is_straight) tileindex = 1; // curved if(is_rail_x_all[0] && is_rail_x_all[1]) angle = 90; if(is_rail_z_all[0] && is_rail_z_all[1]){ if (n_minus_z_plus_y.getContent() == thiscontent) angle = 180; } else if(is_rail_x_all[0] && is_rail_z_all[0]) angle = 270; else if(is_rail_x_all[0] && is_rail_z_all[1]) angle = 180; else if(is_rail_x_all[1] && is_rail_z_all[1]) angle = 90; break; case 3: // here is where the potential to 'switch' a junction is, but not implemented at present tileindex = 2; // t-junction if(!is_rail_x_all[1]) angle=180; if(!is_rail_z_all[0]) angle=90; if(!is_rail_z_all[1]) angle=270; break; case 4: tileindex = 3; // crossing break; case 5: //sloped if(is_rail_z_plus_y[0]) angle = 180; if(is_rail_x_plus_y[0]) angle = 90; if(is_rail_x_plus_y[1]) angle = -90; break; default: break; } TileSpec tile = getNodeTileN(n, p, tileindex, data); tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING; tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; AtlasPointer ap = tile.texture; u16 l = getInteriorLight(n, 0, data); video::SColor c = MapBlock_LightColor(255, l); float d = (float)BS/64; char g=-1; if (is_rail_x_plus_y[0] || is_rail_x_plus_y[1] || is_rail_z_plus_y[0] || is_rail_z_plus_y[1]) g=1; //Object is at a slope 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,g*BS/2+d,BS/2, 0,0,0, c, ap.x1(), ap.y0()), video::S3DVertex(-BS/2,g*BS/2+d,BS/2, 0,0,0, c, ap.x0(), ap.y0()), }; for(s32 i=0; i<4; i++) { if(angle != 0) vertices[i].Pos.rotateXZBy(angle); vertices[i].Pos += intToFloat(p, BS); } u16 indices[] = {0,1,2,2,3,0}; collector.append(tile, vertices, 4, indices, 6); break;} case NDT_NODEBOX: { TileSpec tiles[6]; for(int i = 0; i < 6; i++) { tiles[i] = getNodeTileN(n, p, i, data); } u16 l = getInteriorLight(n, 0, data); video::SColor c = MapBlock_LightColor(255, l); v3f pos = intToFloat(p, BS); std::vector<aabb3f> boxes = n.getNodeBoxes(nodedef); for(std::vector<aabb3f>::iterator i = boxes.begin(); i != boxes.end(); i++) { aabb3f box = *i; box.MinEdge += pos; box.MaxEdge += pos; // Compute texture coords f32 tx1 = (i->MinEdge.X/BS)+0.5; f32 ty1 = (i->MinEdge.Y/BS)+0.5; f32 tz1 = (i->MinEdge.Z/BS)+0.5; f32 tx2 = (i->MaxEdge.X/BS)+0.5; f32 ty2 = (i->MaxEdge.Y/BS)+0.5; f32 tz2 = (i->MaxEdge.Z/BS)+0.5; f32 txc[24] = { // up tx1, 1-tz2, tx2, 1-tz1, // down tx1, tz1, tx2, tz2, // right tz1, 1-ty2, tz2, 1-ty1, // left 1-tz2, 1-ty2, 1-tz1, 1-ty1, // back 1-tx2, 1-ty2, 1-tx1, 1-ty1, // front tx1, 1-ty2, tx2, 1-ty1, }; makeCuboid(&collector, box, tiles, 6, c, txc); } break;} } } }
int ClientMap::getBackgroundBrightness(float max_d, u32 daylight_factor, int oldvalue, bool *sunlight_seen_result) { const bool debugprint = false; INodeDefManager *ndef = m_gamedef->ndef(); static v3f z_directions[50] = { v3f(-100, 0, 0) }; static f32 z_offsets[sizeof(z_directions)/sizeof(*z_directions)] = { -1000, }; if(z_directions[0].X < -99){ for(u32 i=0; i<sizeof(z_directions)/sizeof(*z_directions); i++){ z_directions[i] = v3f( 0.01 * myrand_range(-100, 100), 1.0, 0.01 * myrand_range(-100, 100) ); z_offsets[i] = 0.01 * myrand_range(0,100); } } if(debugprint) std::cerr<<"In goes "<<PP(m_camera_direction)<<", out comes "; int sunlight_seen_count = 0; float sunlight_min_d = max_d*0.8; if(sunlight_min_d > 35*BS) sunlight_min_d = 35*BS; std::vector<int> values; for(u32 i=0; i<sizeof(z_directions)/sizeof(*z_directions); i++){ v3f z_dir = z_directions[i]; z_dir.normalize(); core::CMatrix4<f32> a; a.buildRotateFromTo(v3f(0,1,0), z_dir); v3f dir = m_camera_direction; a.rotateVect(dir); int br = 0; float step = BS*1.5; if(max_d > 35*BS) step = max_d / 35 * 1.5; float off = step * z_offsets[i]; bool sunlight_seen_now = false; bool ok = getVisibleBrightness(this, m_camera_position, dir, step, 1.0, max_d*0.6+off, max_d, ndef, daylight_factor, sunlight_min_d, &br, &sunlight_seen_now); if(sunlight_seen_now) sunlight_seen_count++; if(!ok) continue; values.push_back(br); // Don't try too much if being in the sun is clear if(sunlight_seen_count >= 20) break; } int brightness_sum = 0; int brightness_count = 0; std::sort(values.begin(), values.end()); u32 num_values_to_use = values.size(); if(num_values_to_use >= 10) num_values_to_use -= num_values_to_use/2; else if(num_values_to_use >= 7) num_values_to_use -= num_values_to_use/3; u32 first_value_i = (values.size() - num_values_to_use) / 2; if(debugprint){ for(u32 i=0; i < first_value_i; i++) std::cerr<<values[i]<<" "; std::cerr<<"["; } for(u32 i=first_value_i; i < first_value_i+num_values_to_use; i++){ if(debugprint) std::cerr<<values[i]<<" "; brightness_sum += values[i]; brightness_count++; } if(debugprint){ std::cerr<<"]"; for(u32 i=first_value_i+num_values_to_use; i < values.size(); i++) std::cerr<<values[i]<<" "; } int ret = 0; if(brightness_count == 0){ MapNode n = getNodeNoEx(floatToInt(m_camera_position, BS)); if(ndef->get(n).param_type == CPT_LIGHT){ ret = decode_light(n.getLightBlend(daylight_factor, ndef)); } else { ret = oldvalue; //ret = blend_light(255, 0, daylight_factor); } } else { /*float pre = (float)brightness_sum / (float)brightness_count; float tmp = pre; const float d = 0.2; pre *= 1.0 + d*2; pre -= tmp * d; int preint = pre; ret = MYMAX(0, MYMIN(255, preint));*/ ret = brightness_sum / brightness_count; } if(debugprint) std::cerr<<"Result: "<<ret<<" sunlight_seen_count=" <<sunlight_seen_count<<std::endl; *sunlight_seen_result = (sunlight_seen_count > 0); return ret; }
static bool getVisibleBrightness(Map *map, v3f p0, v3f dir, float step, float step_multiplier, float start_distance, float end_distance, INodeDefManager *ndef, u32 daylight_factor, float sunlight_min_d, int *result, bool *sunlight_seen) { int brightness_sum = 0; int brightness_count = 0; float distance = start_distance; dir.normalize(); v3f pf = p0; pf += dir * distance; int noncount = 0; bool nonlight_seen = false; bool allow_allowing_non_sunlight_propagates = false; bool allow_non_sunlight_propagates = false; // Check content nearly at camera position { v3s16 p = floatToInt(p0 /*+ dir * 3*BS*/, BS); MapNode n = map->getNodeNoEx(p); if(ndef->get(n).param_type == CPT_LIGHT && !ndef->get(n).sunlight_propagates) allow_allowing_non_sunlight_propagates = true; } // If would start at CONTENT_IGNORE, start closer { v3s16 p = floatToInt(pf, BS); MapNode n = map->getNodeNoEx(p); if(n.getContent() == CONTENT_IGNORE){ float newd = 2*BS; pf = p0 + dir * 2*newd; distance = newd; sunlight_min_d = 0; } } for(int i=0; distance < end_distance; i++){ pf += dir * step; distance += step; step *= step_multiplier; v3s16 p = floatToInt(pf, BS); MapNode n = map->getNodeNoEx(p); if(allow_allowing_non_sunlight_propagates && i == 0 && ndef->get(n).param_type == CPT_LIGHT && !ndef->get(n).sunlight_propagates){ allow_non_sunlight_propagates = true; } if(ndef->get(n).param_type != CPT_LIGHT || (!ndef->get(n).sunlight_propagates && !allow_non_sunlight_propagates)){ nonlight_seen = true; noncount++; if(noncount >= 4) break; continue; } if(distance >= sunlight_min_d && *sunlight_seen == false && nonlight_seen == false) if(n.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN) *sunlight_seen = true; noncount = 0; brightness_sum += decode_light(n.getLightBlend(daylight_factor, ndef)); brightness_count++; } *result = 0; if(brightness_count == 0) return false; *result = brightness_sum / brightness_count; /*std::cerr<<"Sampled "<<brightness_count<<" points; result=" <<(*result)<<std::endl;*/ return true; }
void getTileInfo( // Input: v3s16 blockpos_nodes, v3s16 p, v3s16 face_dir, u32 daynight_ratio, VoxelManipulator &vmanip, NodeModMap &temp_mods, bool smooth_lighting, // Output: bool &makes_face, v3s16 &p_corrected, v3s16 &face_dir_corrected, u8 *lights, TileSpec &tile ) { MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p); MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir); TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods); TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods); // This is hackish u8 content0 = getNodeContent(p, n0, temp_mods); u8 content1 = getNodeContent(p + face_dir, n1, temp_mods); u8 mf = face_contents(content0, content1); if(mf == 0) { makes_face = false; return; } makes_face = true; if(mf == 1) { tile = tile0; p_corrected = p; face_dir_corrected = face_dir; } else { tile = tile1; p_corrected = p + face_dir; face_dir_corrected = -face_dir; } if(smooth_lighting == false) { lights[0] = lights[1] = lights[2] = lights[3] = decode_light(getFaceLight(daynight_ratio, n0, n1, face_dir)); } else { v3s16 vertex_dirs[4]; getNodeVertexDirs(face_dir_corrected, vertex_dirs); for(u16 i=0; i<4; i++) { lights[i] = getSmoothLight(blockpos_nodes + p_corrected, vertex_dirs[i], vmanip, daynight_ratio); } } return; }
/* Calculate smooth lighting at the XYZ- corner of p. Both light banks */ static u16 getSmoothLightCombined(v3s16 p, MeshMakeData *data) { static const v3s16 dirs8[8] = { v3s16(0,0,0), v3s16(0,0,1), v3s16(0,1,0), v3s16(0,1,1), v3s16(1,0,0), v3s16(1,1,0), v3s16(1,0,1), v3s16(1,1,1), }; INodeDefManager *ndef = data->m_gamedef->ndef(); u16 ambient_occlusion = 0; u16 light_count = 0; u8 light_source_max = 0; u16 light_day = 0; u16 light_night = 0; for (u32 i = 0; i < 8; i++) { MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]); // if it's CONTENT_IGNORE we can't do any light calculations if (n.getContent() == CONTENT_IGNORE) { continue; } const ContentFeatures &f = ndef->get(n); if (f.light_source > light_source_max) light_source_max = f.light_source; // Check f.solidness because fast-style leaves look better this way if (f.param_type == CPT_LIGHT && f.solidness != 2) { light_day += decode_light(n.getLight(LIGHTBANK_DAY, ndef)); light_night += decode_light(n.getLight(LIGHTBANK_NIGHT, ndef)); light_count++; } else { ambient_occlusion++; } } if(light_count == 0) return 0xffff; light_day /= light_count; light_night /= light_count; // Boost brightness around light sources bool skip_ambient_occlusion_day = false; if(decode_light(light_source_max) >= light_day) { light_day = decode_light(light_source_max); skip_ambient_occlusion_day = true; } bool skip_ambient_occlusion_night = false; if(decode_light(light_source_max) >= light_night) { light_night = decode_light(light_source_max); skip_ambient_occlusion_night = true; } if (ambient_occlusion > 4) { //table of precalculated gamma space multiply factors //light^2.2 * factor (0.75, 0.5, 0.25, 0.0), so table holds factor ^ (1 / 2.2) static const float light_amount[4] = { 0.877424315, 0.729740053, 0.532520545, 0.0 }; //calculate table index for gamma space multiplier ambient_occlusion -= 5; if (!skip_ambient_occlusion_day) light_day = rangelim(core::round32(light_day*light_amount[ambient_occlusion]), 0, 255); if (!skip_ambient_occlusion_night) light_night = rangelim(core::round32(light_night*light_amount[ambient_occlusion]), 0, 255); } return light_day | (light_night << 8); }
/* Calculate smooth lighting at the XYZ- corner of p. Both light banks */ static u16 getSmoothLightCombined(v3s16 p, MeshMakeData *data) { static const v3s16 dirs8[8] = { v3s16(0,0,0), v3s16(0,0,1), v3s16(0,1,0), v3s16(0,1,1), v3s16(1,0,0), v3s16(1,1,0), v3s16(1,0,1), v3s16(1,1,1), }; INodeDefManager *ndef = data->m_gamedef->ndef(); u16 ambient_occlusion = 0; u16 light_count = 0; u8 light_source_max = 0; u16 light_day = 0; u16 light_night = 0; for (u32 i = 0; i < 8; i++) { const MapNode &n = data->m_vmanip.getNodeRefUnsafeCheckFlags(p - dirs8[i]); // if it's CONTENT_IGNORE we can't do any light calculations if (n.getContent() == CONTENT_IGNORE) { continue; } const ContentFeatures &f = ndef->get(n); if (f.light_source > light_source_max) light_source_max = f.light_source; // Check f.solidness because fast-style leaves look better this way if (f.param_type == CPT_LIGHT && f.solidness != 2) { light_day += decode_light(n.getLightNoChecks(LIGHTBANK_DAY, &f)); light_night += decode_light(n.getLightNoChecks(LIGHTBANK_NIGHT, &f)); light_count++; } else { ambient_occlusion++; } } if(light_count == 0) return 0xffff; light_day /= light_count; light_night /= light_count; // Boost brightness around light sources bool skip_ambient_occlusion_day = false; if(decode_light(light_source_max) >= light_day) { light_day = decode_light(light_source_max); skip_ambient_occlusion_day = true; } bool skip_ambient_occlusion_night = false; if(decode_light(light_source_max) >= light_night) { light_night = decode_light(light_source_max); skip_ambient_occlusion_night = true; } if (ambient_occlusion > 4) { static const float ao_gamma = rangelim( g_settings->getFloat("ambient_occlusion_gamma"), 0.25, 4.0); // Table of gamma space multiply factors. static const float light_amount[3] = { powf(0.75, 1.0 / ao_gamma), powf(0.5, 1.0 / ao_gamma), powf(0.25, 1.0 / ao_gamma) }; //calculate table index for gamma space multiplier ambient_occlusion -= 5; if (!skip_ambient_occlusion_day) light_day = rangelim(core::round32(light_day*light_amount[ambient_occlusion]), 0, 255); if (!skip_ambient_occlusion_night) light_night = rangelim(core::round32(light_night*light_amount[ambient_occlusion]), 0, 255); } return light_day | (light_night << 8); }
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;} } } }
void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize) { // Set player node transformation m_playernode->setPosition(player->getPosition()); m_playernode->setRotation(v3f(0, -1 * player->getYaw(), 0)); m_playernode->updateAbsolutePosition(); // Set head node transformation m_headnode->setPosition(player->getEyeOffset()); m_headnode->setRotation(v3f(player->getPitch(), 0, 0)); m_headnode->updateAbsolutePosition(); // Compute relative camera position and target v3f rel_cam_pos = v3f(0,0,0); v3f rel_cam_target = v3f(0,0,1); v3f rel_cam_up = v3f(0,1,0); if (m_view_bobbing_state != 0 && m_view_bobbing_anim != 0) { f32 bobfrac = my_modf(m_view_bobbing_anim * 2); f32 bobdir = (m_view_bobbing_anim < 0.5) ? 1.0 : -1.0; f32 bobknob = 1.2; f32 bobtmp = sin(pow(bobfrac, bobknob) * PI); v3f bobvec = v3f( 0.3 * bobdir * sin(bobfrac * PI), -0.28 * bobtmp * bobtmp, 0.); float f = 1.0; f *= m_view_bobbing_amount; rel_cam_pos += bobvec * f; rel_cam_target += bobvec * f; rel_cam_target.Z -= 0.005 * bobvec.Z * f; rel_cam_up.rotateXYBy(-0.03 * bobdir * bobtmp * PI * f); } // Compute absolute camera position and target m_headnode->getAbsoluteTransformation().transformVect(m_camera_position, rel_cam_pos); m_headnode->getAbsoluteTransformation().rotateVect(m_camera_direction, rel_cam_target - rel_cam_pos); v3f abs_cam_up; m_headnode->getAbsoluteTransformation().rotateVect(abs_cam_up, rel_cam_up); // Update offset if too far away from the center of the map m_camera_offset.X += CAMERA_OFFSET_STEP* (((s16)(m_camera_position.X/BS) - m_camera_offset.X)/CAMERA_OFFSET_STEP); m_camera_offset.Y += CAMERA_OFFSET_STEP* (((s16)(m_camera_position.Y/BS) - m_camera_offset.Y)/CAMERA_OFFSET_STEP); m_camera_offset.Z += CAMERA_OFFSET_STEP* (((s16)(m_camera_position.Z/BS) - m_camera_offset.Z)/CAMERA_OFFSET_STEP); // Set camera node transformation m_cameranode->setPosition(m_camera_position-intToFloat(m_camera_offset, BS)); m_cameranode->setUpVector(abs_cam_up); // *100.0 helps in large map coordinates m_cameranode->setTarget(m_camera_position-intToFloat(m_camera_offset, BS) + 100 * m_camera_direction); // FOV and aspect ratio m_aspect = (f32)screensize.X / (f32) screensize.Y; m_fov_y = m_fov * PI / 180.0; m_fov_x = 2 * atan(0.5 * m_aspect * tan(m_fov_y)); m_cameranode->setAspectRatio(m_aspect); m_cameranode->setFOV(m_fov_y); // Position the wielded item v3f wield_position = m_wieldnode_baseposition; v3f wield_rotation = m_wieldnode_baserotation; if (m_digging_button != -1) { f32 digfrac = m_digging_anim; wield_position.X -= 30 * sin(pow(digfrac, 0.8f) * PI); wield_position.Y += 15 * sin(digfrac * 2 * PI); wield_position.Z += 5 * digfrac; // Euler angles are PURE EVIL, so why not use quaternions? core::quaternion quat_begin(wield_rotation * core::DEGTORAD); core::quaternion quat_end(v3f(90, -10, -130) * core::DEGTORAD); core::quaternion quat_slerp; quat_slerp.slerp(quat_begin, quat_end, sin(digfrac * PI)); quat_slerp.toEuler(wield_rotation); wield_rotation *= core::RADTODEG; }else{ f32 bobfrac = my_modf(m_view_bobbing_anim); wield_position.X -= sin(bobfrac*PI*2.0) * 3.0; wield_position.Y += sin(my_modf(bobfrac*2.0)*PI) * 3.0; } m_wieldnode->setPosition(wield_position); m_wieldnode->setRotation(wield_rotation); u8 li = decode_light(player->light); // Set brightness one lower than incoming light diminish_light(li); m_wieldnode->updateLight(li); // Render distance feedback loop updateViewingRange(frametime); // If the player seems to be walking on solid ground, // view bobbing is enabled and free_move is off, // start (or continue) the view bobbing animation. v3f speed = player->getSpeed(); if ( (hypot(speed.X, speed.Z) > BS) && (player->touching_ground) && m_view_bobbing == true && player->control.free == false ) { // Start animation m_view_bobbing_state = 1; m_view_bobbing_speed = MYMIN(speed.getLength(), 60)*1.2; }else if (m_view_bobbing_state == 1) { // Stop animation m_view_bobbing_state = 2; m_view_bobbing_speed = 60; } }
ClientCached* createClientCachedDirect(const std::string &name, IGameDef *gamedef) const { infostream<<"Lazily creating item texture and mesh for \"" <<name<<"\""<<std::endl; // This is not thread-safe assert(get_current_thread_id() == m_main_thread); // Skip if already in cache ClientCached *cc = NULL; m_clientcached.get(name, &cc); if(cc) return cc; ITextureSource *tsrc = gamedef->getTextureSource(); INodeDefManager *nodedef = gamedef->getNodeDefManager(); IrrlichtDevice *device = tsrc->getDevice(); video::IVideoDriver *driver = device->getVideoDriver(); const ItemDefinition *def = &get(name); // Create new ClientCached cc = new ClientCached(); bool need_node_mesh = false; // Create an inventory texture cc->inventory_texture = NULL; if(def->inventory_image != "") { cc->inventory_texture = tsrc->getTextureRaw(def->inventory_image); } else if(def->type == ITEM_NODE) { need_node_mesh = true; } // Create a wield mesh assert(cc->wield_mesh == NULL); if(def->type == ITEM_NODE && def->wield_image == "") { need_node_mesh = true; } else if(def->wield_image != "" || def->inventory_image != "") { // Extrude the wield image into a mesh std::string imagename; if(def->wield_image != "") imagename = def->wield_image; else imagename = def->inventory_image; cc->wield_mesh = createExtrudedMesh( tsrc->getTextureRaw(imagename), driver, def->wield_scale * v3f(40.0, 40.0, 4.0)); if(cc->wield_mesh == NULL) { infostream<<"ItemDefManager: WARNING: " <<"updateTexturesAndMeshes(): " <<"Unable to create extruded mesh for item " <<def->name<<std::endl; } } if(need_node_mesh) { /* Get node properties */ content_t id = nodedef->getId(def->name); const ContentFeatures &f = nodedef->get(id); u8 param1 = 0; if(f.param_type == CPT_LIGHT) param1 = 0xee; /* Make a mesh from the node */ MeshMakeData mesh_make_data(gamedef); MapNode mesh_make_node(id, param1, 0); mesh_make_data.fillSingleNode(&mesh_make_node); MapBlockMesh mapblock_mesh(&mesh_make_data); scene::IMesh *node_mesh = mapblock_mesh.getMesh(); assert(node_mesh); video::SColor c(255, 255, 255, 255); if(g_settings->getS32("enable_shaders") != 0) c = MapBlock_LightColor(255, 0xffff, decode_light(f.light_source)); setMeshColor(node_mesh, c); /* Scale and translate the mesh so it's a unit cube centered on the origin */ scaleMesh(node_mesh, v3f(1.0/BS, 1.0/BS, 1.0/BS)); translateMesh(node_mesh, v3f(-1.0, -1.0, -1.0)); /* Draw node mesh into a render target texture */ if(cc->inventory_texture == NULL) { core::dimension2d<u32> dim(64,64); std::string rtt_texture_name = "INVENTORY_" + def->name + "_RTT"; v3f camera_position(0, 1.0, -1.5); camera_position.rotateXZBy(45); v3f camera_lookat(0, 0, 0); core::CMatrix4<f32> camera_projection_matrix; // Set orthogonal projection camera_projection_matrix.buildProjectionMatrixOrthoLH( 1.65, 1.65, 0, 100); video::SColorf ambient_light(0.2,0.2,0.2); v3f light_position(10, 100, -50); video::SColorf light_color(0.5,0.5,0.5); f32 light_radius = 1000; cc->inventory_texture = generateTextureFromMesh( node_mesh, device, dim, rtt_texture_name, camera_position, camera_lookat, camera_projection_matrix, ambient_light, light_position, light_color, light_radius); // render-to-target didn't work if(cc->inventory_texture == NULL) { cc->inventory_texture = tsrc->getTextureRaw(f.tiledef[0].name); } } else { if (m_driver == 0) m_driver = driver; m_extruded_textures.push_back(cc->inventory_texture); } /* Use the node mesh as the wield mesh */ // Scale to proper wield mesh proportions scaleMesh(node_mesh, v3f(30.0, 30.0, 30.0) * def->wield_scale); cc->wield_mesh = node_mesh; cc->wield_mesh->grab(); //no way reference count can be smaller than 2 in this place! assert(cc->wield_mesh->getReferenceCount() >= 2); } // Put in cache m_clientcached.set(name, cc); return cc; }