bool Pathfinder::updateAllCosts(v3s16 ipos, v3s16 srcdir, int current_cost, int level) { PathGridnode &g_pos = getIndexElement(ipos); g_pos.totalcost = current_cost; g_pos.sourcedir = srcdir; level ++; //check if target has been found if (g_pos.target) { m_min_target_distance = current_cost; DEBUG_OUT(LVL " Pathfinder: target found!" << std::endl); return true; } bool retval = false; std::vector<v3s16> directions; directions.push_back(v3s16( 1,0, 0)); directions.push_back(v3s16(-1,0, 0)); directions.push_back(v3s16( 0,0, 1)); directions.push_back(v3s16( 0,0,-1)); for (unsigned int i=0; i < directions.size(); i++) { if (directions[i] != srcdir) { PathCost cost = g_pos.getCost(directions[i]); if (cost.valid) { directions[i].Y = cost.direction; v3s16 ipos2 = ipos + directions[i]; if (!isValidIndex(ipos2)) { DEBUG_OUT(LVL " Pathfinder: " << PP(ipos2) << " out of range, max=" << PP(m_limits.MaxEdge) << std::endl); continue; } PathGridnode &g_pos2 = getIndexElement(ipos2); if (!g_pos2.valid) { VERBOSE_TARGET << LVL "Pathfinder: no data for new position: " << PP(ipos2) << std::endl; continue; } assert(cost.value > 0); int new_cost = current_cost + cost.value; // check if there already is a smaller path if ((m_min_target_distance > 0) && (m_min_target_distance < new_cost)) { return false; } if ((g_pos2.totalcost < 0) || (g_pos2.totalcost > new_cost)) { DEBUG_OUT(LVL "Pathfinder: updating path at: "<< PP(ipos2) << " from: " << g_pos2.totalcost << " to "<< new_cost << std::endl); if (updateAllCosts(ipos2, invert(directions[i]), new_cost, level)) { retval = true; } } else { DEBUG_OUT(LVL "Pathfinder:" " already found shorter path to: " << PP(ipos2) << std::endl); } } else { DEBUG_OUT(LVL "Pathfinder:" " not moving to invalid direction: " << PP(directions[i]) << std::endl); } } } return retval; }
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 Schematic::placeOnMap(Map *map, v3s16 p, u32 flags, Rotation rot, bool force_place) { concurrent_map<v3POS, MapBlock *> lighting_modified_blocks; /* std::map<v3s16, MapBlock *> lighting_modified_blocks; */ std::map<v3s16, MapBlock *> modified_blocks; std::map<v3s16, MapBlock *>::iterator it; if(!map || !schemdata || !m_ndef) return; /* assert(map != NULL); assert(schemdata != NULL); sanity_check(m_ndef != NULL); */ //// Determine effective rotation and effective schematic dimensions if (rot == ROTATE_RAND) rot = (Rotation)myrand_range(ROTATE_0, ROTATE_270); v3s16 s = (rot == ROTATE_90 || rot == ROTATE_270) ? v3s16(size.Z, size.Y, size.X) : size; //// Adjust placement position if necessary if (flags & DECO_PLACE_CENTER_X) p.X -= (s.X + 1) / 2; if (flags & DECO_PLACE_CENTER_Y) p.Y -= (s.Y + 1) / 2; if (flags & DECO_PLACE_CENTER_Z) p.Z -= (s.Z + 1) / 2; //// Create VManip for effected area, emerge our area, modify area //// inside VManip, then blit back. v3s16 bp1 = getNodeBlockPos(p); v3s16 bp2 = getNodeBlockPos(p + s - v3s16(1,1,1)); MMVManip vm(map); vm.initialEmerge(bp1, bp2); blitToVManip(&vm, p, rot, force_place); vm.blitBackAll(&modified_blocks); //// Carry out post-map-modification actions //// Update lighting // TODO: Optimize this by using Mapgen::calcLighting() instead lighting_modified_blocks.insert(modified_blocks.begin(), modified_blocks.end()); map->updateLighting(lighting_modified_blocks, modified_blocks); //// Create & dispatch map modification events to observers MapEditEvent event; event.type = MEET_OTHER; /* for (it = modified_blocks.begin(); it != modified_blocks.end(); ++it) event.modified_blocks.insert(it->first); */ map->dispatchEvent(&event); }
void MapgenV6::placeTreesAndJungleGrass() { //TimeTaker t("placeTrees"); PseudoRandom grassrandom(blockseed + 53); content_t c_sand = ndef->getId("mapgen_sand"); content_t c_junglegrass = ndef->getId("mapgen_junglegrass"); // if we don't have junglegrass, don't place cignore... that's bad if (c_junglegrass == CONTENT_IGNORE) c_junglegrass = CONTENT_AIR; MapNode n_junglegrass(c_junglegrass); v3s16 em = vm->m_area.getExtent(); // Divide area into parts s16 div = 8; s16 sidelen = central_area_size.X / div; double area = sidelen * sidelen; // N.B. We must add jungle grass first, since tree leaves will // obstruct the ground, giving us a false ground level for (s16 z0 = 0; z0 < div; z0++) for (s16 x0 = 0; x0 < div; x0++) { // Center position of part of division v2s16 p2d_center( node_min.X + sidelen / 2 + sidelen * x0, node_min.Z + sidelen / 2 + sidelen * z0 ); // Minimum edge of part of division v2s16 p2d_min( node_min.X + sidelen * x0, node_min.Z + sidelen * z0 ); // Maximum edge of part of division v2s16 p2d_max( node_min.X + sidelen + sidelen * x0 - 1, node_min.Z + sidelen + sidelen * z0 - 1 ); // Get biome at center position of part of division BiomeV6Type bt = getBiome(v3POS(p2d_center.X, node_min.Y, p2d_center.Y)); // Amount of trees float humidity = getHumidity(v3POS(p2d_center.X, node_max.Y, p2d_center.Y)); s32 tree_count; if (bt == BT_JUNGLE || bt == BT_TAIGA || bt == BT_NORMAL) { tree_count = area * getTreeAmount(p2d_center) * ((humidity + 1)/2.0); if (bt == BT_JUNGLE) tree_count *= 4; } else { tree_count = 0; } if (node_max.Y < water_level) tree_count /= 2; // Add jungle grass if (bt == BT_JUNGLE) { u32 grass_count = 5 * humidity * tree_count; for (u32 i = 0; i < grass_count; i++) { s16 x = grassrandom.range(p2d_min.X, p2d_max.X); s16 z = grassrandom.range(p2d_min.Y, p2d_max.Y); /* wtf int mapindex = central_area_size.X * (z - node_min.Z) + (x - node_min.X); s16 y = heightmap[mapindex]; */ s16 y = findGroundLevelFull(v2s16(x, z)); if (y < water_level) continue; u32 vi = vm->m_area.index(x, y, z); // place on dirt_with_grass, since we know it is exposed to sunlight if (vm->m_data[vi].getContent() == c_dirt_with_grass) { vm->m_area.add_y(em, vi, 1); vm->m_data[vi] = n_junglegrass; } } } // Put trees in random places on part of division for (s32 i = 0; i < tree_count; i++) { s16 x = myrand_range(p2d_min.X, p2d_max.X); s16 z = myrand_range(p2d_min.Y, p2d_max.Y); /* wtf int mapindex = central_area_size.X * (z - node_min.Z) + (x - node_min.X); s16 y = heightmap[mapindex]; */ s16 y = findGroundLevelFull(v2s16(x, z)); // Don't make a tree under water level // Don't make a tree so high that it doesn't fit if (y > node_max.Y - 6) continue; v3s16 p(x, y, z); // Trees grow only on mud and grass and snowblock { u32 i = vm->m_area.index(p); content_t c = vm->m_data[i].getContent(); if (c != c_dirt && c != c_dirt_with_grass && c != c_dirt_with_snow && c != c_snowblock && (y >= water_level || c != c_sand)) continue; } p.Y++; // Make a tree if (y < water_level) { if (y < water_level - 20) // do not spawn trees in lakes treegen::make_cavetree(*vm, p, bt == BT_JUNGLE, ndef, myrand()); } else if (bt == BT_JUNGLE) { treegen::make_jungletree(*vm, p, ndef, myrand()); } else if (bt == BT_TAIGA) { treegen::make_pine_tree(*vm, p - v3s16(0, 1, 0), ndef, myrand()); } else if (bt == BT_NORMAL) { bool is_apple_tree = (myrand_range(0, 3) == 0) && getHaveAppleTree(v2s16(x, z)); treegen::make_tree(*vm, p, is_apple_tree, ndef, myrand()); } } } //printf("placeTreesAndJungleGrass: %dms\n", t.stop()); }
/* vertex_dirs: v3s16[4] */ static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs) { /* If looked from outside the node towards the face, the corners are: 0: bottom-right 1: bottom-left 2: top-left 3: top-right */ if(dir == v3s16(0,0,1)) { // If looking towards z+, this is the face that is behind // the center point, facing towards z+. vertex_dirs[0] = v3s16(-1,-1, 1); vertex_dirs[1] = v3s16( 1,-1, 1); vertex_dirs[2] = v3s16( 1, 1, 1); vertex_dirs[3] = v3s16(-1, 1, 1); } else if(dir == v3s16(0,0,-1)) { // faces towards Z- vertex_dirs[0] = v3s16( 1,-1,-1); vertex_dirs[1] = v3s16(-1,-1,-1); vertex_dirs[2] = v3s16(-1, 1,-1); vertex_dirs[3] = v3s16( 1, 1,-1); } else if(dir == v3s16(1,0,0)) { // faces towards X+ vertex_dirs[0] = v3s16( 1,-1, 1); vertex_dirs[1] = v3s16( 1,-1,-1); vertex_dirs[2] = v3s16( 1, 1,-1); vertex_dirs[3] = v3s16( 1, 1, 1); } else if(dir == v3s16(-1,0,0)) { // faces towards X- vertex_dirs[0] = v3s16(-1,-1,-1); vertex_dirs[1] = v3s16(-1,-1, 1); vertex_dirs[2] = v3s16(-1, 1, 1); vertex_dirs[3] = v3s16(-1, 1,-1); } else if(dir == v3s16(0,1,0)) { // faces towards Y+ (assume Z- as "down" in texture) vertex_dirs[0] = v3s16( 1, 1,-1); vertex_dirs[1] = v3s16(-1, 1,-1); vertex_dirs[2] = v3s16(-1, 1, 1); vertex_dirs[3] = v3s16( 1, 1, 1); } else if(dir == v3s16(0,-1,0)) { // faces towards Y- (assume Z+ as "down" in texture) vertex_dirs[0] = v3s16( 1,-1, 1); vertex_dirs[1] = v3s16(-1,-1, 1); vertex_dirs[2] = v3s16(-1,-1,-1); vertex_dirs[3] = v3s16( 1,-1,-1); } }
collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, f32 pos_max_d, const aabb3f &box_0, f32 stepheight, f32 dtime, v3f *pos_f, v3f *speed_f, v3f accel_f, ActiveObject *self, bool collideWithObjects) { static bool time_notification_done = false; Map *map = &env->getMap(); //TimeTaker tt("collisionMoveSimple"); ScopeProfiler sp(g_profiler, "collisionMoveSimple avg", SPT_AVG); collisionMoveResult result; /* Calculate new velocity */ if (dtime > 0.5) { if (!time_notification_done) { time_notification_done = true; infostream << "collisionMoveSimple: maximum step interval exceeded," " lost movement details!"<<std::endl; } dtime = 0.5; } else { time_notification_done = false; } *speed_f += accel_f * dtime; // If there is no speed, there are no collisions if (speed_f->getLength() == 0) return result; // Limit speed for avoiding hangs speed_f->Y = rangelim(speed_f->Y, -5000, 5000); speed_f->X = rangelim(speed_f->X, -5000, 5000); speed_f->Z = rangelim(speed_f->Z, -5000, 5000); /* Collect node boxes in movement range */ std::vector<aabb3f> cboxes; std::vector<bool> is_unloaded; std::vector<bool> is_step_up; std::vector<bool> is_object; std::vector<int> bouncy_values; std::vector<v3s16> node_positions; { //TimeTaker tt2("collisionMoveSimple collect boxes"); ScopeProfiler sp(g_profiler, "collisionMoveSimple collect boxes avg", SPT_AVG); v3s16 oldpos_i = floatToInt(*pos_f, BS); v3s16 newpos_i = floatToInt(*pos_f + *speed_f * dtime, BS); s16 min_x = MYMIN(oldpos_i.X, newpos_i.X) + (box_0.MinEdge.X / BS) - 1; s16 min_y = MYMIN(oldpos_i.Y, newpos_i.Y) + (box_0.MinEdge.Y / BS) - 1; s16 min_z = MYMIN(oldpos_i.Z, newpos_i.Z) + (box_0.MinEdge.Z / BS) - 1; s16 max_x = MYMAX(oldpos_i.X, newpos_i.X) + (box_0.MaxEdge.X / BS) + 1; s16 max_y = MYMAX(oldpos_i.Y, newpos_i.Y) + (box_0.MaxEdge.Y / BS) + 1; s16 max_z = MYMAX(oldpos_i.Z, newpos_i.Z) + (box_0.MaxEdge.Z / BS) + 1; bool any_position_valid = false; for(s16 x = min_x; x <= max_x; x++) for(s16 y = min_y; y <= max_y; y++) for(s16 z = min_z; z <= max_z; z++) { v3s16 p(x,y,z); bool is_position_valid; MapNode n = map->getNodeNoEx(p, &is_position_valid); if (is_position_valid) { // Object collides into walkable nodes any_position_valid = true; const ContentFeatures &f = gamedef->getNodeDefManager()->get(n); if(f.walkable == false) continue; int n_bouncy_value = itemgroup_get(f.groups, "bouncy"); std::vector<aabb3f> nodeboxes = n.getCollisionBoxes(gamedef->ndef()); for(std::vector<aabb3f>::iterator i = nodeboxes.begin(); i != nodeboxes.end(); ++i) { aabb3f box = *i; box.MinEdge += v3f(x, y, z)*BS; box.MaxEdge += v3f(x, y, z)*BS; cboxes.push_back(box); is_unloaded.push_back(false); is_step_up.push_back(false); bouncy_values.push_back(n_bouncy_value); node_positions.push_back(p); is_object.push_back(false); } } else { // Collide with unloaded nodes aabb3f box = getNodeBox(p, BS); cboxes.push_back(box); is_unloaded.push_back(true); is_step_up.push_back(false); bouncy_values.push_back(0); node_positions.push_back(p); is_object.push_back(false); } } // Do not move if world has not loaded yet, since custom node boxes // are not available for collision detection. if (!any_position_valid) return result; } // tt2 if(collideWithObjects) { ScopeProfiler sp(g_profiler, "collisionMoveSimple objects avg", SPT_AVG); //TimeTaker tt3("collisionMoveSimple collect object boxes"); /* add object boxes to cboxes */ std::vector<ActiveObject*> objects; #ifndef SERVER ClientEnvironment *c_env = dynamic_cast<ClientEnvironment*>(env); if (c_env != 0) { f32 distance = speed_f->getLength(); std::vector<DistanceSortedActiveObject> clientobjects; c_env->getActiveObjects(*pos_f, distance * 1.5, clientobjects); for (size_t i=0; i < clientobjects.size(); i++) { if ((self == 0) || (self != clientobjects[i].obj)) { objects.push_back((ActiveObject*)clientobjects[i].obj); } } } else #endif { ServerEnvironment *s_env = dynamic_cast<ServerEnvironment*>(env); if (s_env != 0) { f32 distance = speed_f->getLength(); std::vector<u16> s_objects; s_env->getObjectsInsideRadius(s_objects, *pos_f, distance * 1.5); for (std::vector<u16>::iterator iter = s_objects.begin(); iter != s_objects.end(); ++iter) { ServerActiveObject *current = s_env->getActiveObject(*iter); if ((self == 0) || (self != current)) { objects.push_back((ActiveObject*)current); } } } } for (std::vector<ActiveObject*>::const_iterator iter = objects.begin(); iter != objects.end(); ++iter) { ActiveObject *object = *iter; if (object != NULL) { aabb3f object_collisionbox; if (object->getCollisionBox(&object_collisionbox) && object->collideWithObjects()) { cboxes.push_back(object_collisionbox); is_unloaded.push_back(false); is_step_up.push_back(false); bouncy_values.push_back(0); node_positions.push_back(v3s16(0,0,0)); is_object.push_back(true); } } } } //tt3 assert(cboxes.size() == is_unloaded.size()); // post-condition assert(cboxes.size() == is_step_up.size()); // post-condition assert(cboxes.size() == bouncy_values.size()); // post-condition assert(cboxes.size() == node_positions.size()); // post-condition assert(cboxes.size() == is_object.size()); // post-condition /* Collision detection */ /* Collision uncertainty radius Make it a bit larger than the maximum distance of movement */ f32 d = pos_max_d * 1.1; // A fairly large value in here makes moving smoother //f32 d = 0.15*BS; // This should always apply, otherwise there are glitches assert(d > pos_max_d); // invariant int loopcount = 0; while(dtime > BS * 1e-10) { //TimeTaker tt3("collisionMoveSimple dtime loop"); ScopeProfiler sp(g_profiler, "collisionMoveSimple dtime loop avg", SPT_AVG); // Avoid infinite loop loopcount++; if (loopcount >= 100) { warningstream << "collisionMoveSimple: Loop count exceeded, aborting to avoid infiniite loop" << std::endl; dtime = 0; break; } aabb3f movingbox = box_0; movingbox.MinEdge += *pos_f; movingbox.MaxEdge += *pos_f; int nearest_collided = -1; f32 nearest_dtime = dtime; u32 nearest_boxindex = -1; /* Go through every nodebox, find nearest collision */ for (u32 boxindex = 0; boxindex < cboxes.size(); boxindex++) { // Ignore if already stepped up this nodebox. if(is_step_up[boxindex]) continue; // Find nearest collision of the two boxes (raytracing-like) f32 dtime_tmp; int collided = axisAlignedCollision( cboxes[boxindex], movingbox, *speed_f, d, &dtime_tmp); if (collided == -1 || dtime_tmp >= nearest_dtime) continue; nearest_dtime = dtime_tmp; nearest_collided = collided; nearest_boxindex = boxindex; } if (nearest_collided == -1) { // No collision with any collision box. *pos_f += *speed_f * dtime; dtime = 0; // Set to 0 to avoid "infinite" loop due to small FP numbers } else { // Otherwise, a collision occurred. const aabb3f& cbox = cboxes[nearest_boxindex]; // Check for stairs. bool step_up = (nearest_collided != 1) && // must not be Y direction (movingbox.MinEdge.Y < cbox.MaxEdge.Y) && (movingbox.MinEdge.Y + stepheight > cbox.MaxEdge.Y) && (!wouldCollideWithCeiling(cboxes, movingbox, cbox.MaxEdge.Y - movingbox.MinEdge.Y, d)); // Get bounce multiplier bool bouncy = (bouncy_values[nearest_boxindex] >= 1); float bounce = -(float)bouncy_values[nearest_boxindex] / 100.0; // Move to the point of collision and reduce dtime by nearest_dtime if (nearest_dtime < 0) { // Handle negative nearest_dtime (can be caused by the d allowance) if (!step_up) { if (nearest_collided == 0) pos_f->X += speed_f->X * nearest_dtime; if (nearest_collided == 1) pos_f->Y += speed_f->Y * nearest_dtime; if (nearest_collided == 2) pos_f->Z += speed_f->Z * nearest_dtime; } } else { *pos_f += *speed_f * nearest_dtime; dtime -= nearest_dtime; } bool is_collision = true; if (is_unloaded[nearest_boxindex]) is_collision = false; CollisionInfo info; if (is_object[nearest_boxindex]) info.type = COLLISION_OBJECT; else info.type = COLLISION_NODE; info.node_p = node_positions[nearest_boxindex]; info.bouncy = bouncy; info.old_speed = *speed_f; // Set the speed component that caused the collision to zero if (step_up) { // Special case: Handle stairs is_step_up[nearest_boxindex] = true; is_collision = false; } else if(nearest_collided == 0) { // X if (fabs(speed_f->X) > BS * 3) speed_f->X *= bounce; else speed_f->X = 0; result.collides = true; result.collides_xz = true; } else if(nearest_collided == 1) { // Y if (fabs(speed_f->Y) > BS * 3) speed_f->Y *= bounce; else speed_f->Y = 0; result.collides = true; } else if(nearest_collided == 2) { // Z if (fabs(speed_f->Z) > BS * 3) speed_f->Z *= bounce; else speed_f->Z = 0; result.collides = true; result.collides_xz = true; } info.new_speed = *speed_f; if (info.new_speed.getDistanceFrom(info.old_speed) < 0.1 * BS) is_collision = false; if (is_collision) { result.collisions.push_back(info); } } } /* Final touches: Check if standing on ground, step up stairs. */ aabb3f box = box_0; box.MinEdge += *pos_f; box.MaxEdge += *pos_f; for (u32 boxindex = 0; boxindex < cboxes.size(); boxindex++) { const aabb3f& cbox = cboxes[boxindex]; /* See if the object is touching ground. Object touches ground if object's minimum Y is near node's maximum Y and object's X-Z-area overlaps with the node's X-Z-area. Use 0.15*BS so that it is easier to get on a node. */ if (cbox.MaxEdge.X - d > box.MinEdge.X && cbox.MinEdge.X + d < box.MaxEdge.X && cbox.MaxEdge.Z - d > box.MinEdge.Z && cbox.MinEdge.Z + d < box.MaxEdge.Z) { if (is_step_up[boxindex]) { pos_f->Y += (cbox.MaxEdge.Y - box.MinEdge.Y); box = box_0; box.MinEdge += *pos_f; box.MaxEdge += *pos_f; } if (fabs(cbox.MaxEdge.Y - box.MinEdge.Y) < 0.15 * BS) { result.touching_ground = true; if (is_object[boxindex]) result.standing_on_object = true; if (is_unloaded[boxindex]) result.standing_on_unloaded = true; } } } return result; }
void MapgenV6::makeChunk(BlockMakeData *data) { // Pre-conditions assert(data->vmanip); assert(data->nodedef); assert(data->blockpos_requested.X >= data->blockpos_min.X && data->blockpos_requested.Y >= data->blockpos_min.Y && data->blockpos_requested.Z >= data->blockpos_min.Z); assert(data->blockpos_requested.X <= data->blockpos_max.X && data->blockpos_requested.Y <= data->blockpos_max.Y && data->blockpos_requested.Z <= data->blockpos_max.Z); this->generating = true; this->vm = data->vmanip; this->ndef = data->nodedef; // Hack: use minimum block coords for old code that assumes a single block v3s16 blockpos_min = data->blockpos_min; v3s16 blockpos_max = data->blockpos_max; // Area of central chunk node_min = blockpos_min * MAP_BLOCKSIZE; node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1); // Full allocated area full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE; full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1); central_area_size = node_max - node_min + v3s16(1, 1, 1); assert(central_area_size.X == central_area_size.Z); int volume_blocks = (blockpos_max.X - blockpos_min.X + 1) * (blockpos_max.Y - blockpos_min.Y + 1) * (blockpos_max.Z - blockpos_max.Z + 1); volume_nodes = volume_blocks * MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE; // Create a block-specific seed blockseed = get_blockseed(data->seed, full_node_min); // Make some noise calculateNoise(); // Maximum height of the stone surface and obstacles. // This is used to guide the cave generation s16 stone_surface_max_y; // Generate general ground level to full area stone_surface_max_y = generateGround(); generateExperimental(); // Create initial heightmap to limit caves updateHeightmap(node_min, node_max); const s16 max_spread_amount = MAP_BLOCKSIZE; // Limit dirt flow area by 1 because mud is flown into neighbors. s16 mudflow_minpos = -max_spread_amount + 1; s16 mudflow_maxpos = central_area_size.X + max_spread_amount - 2; // Loop this part, it will make stuff look older and newer nicely const u32 age_loops = 2; for (u32 i_age = 0; i_age < age_loops; i_age++) { // Aging loop // Make caves (this code is relatively horrible) if (flags & MG_CAVES) generateCaves(stone_surface_max_y); // Add mud to the central chunk addMud(); // Flow mud away from steep edges if (spflags & MGV6_MUDFLOW) flowMud(mudflow_minpos, mudflow_maxpos); } // Update heightmap after mudflow updateHeightmap(node_min, node_max); // Add dungeons if ((flags & MG_DUNGEONS) && (stone_surface_max_y >= node_min.Y)) { DungeonParams dp; dp.seed = seed; dp.c_water = c_water_source; dp.c_river_water = c_water_source; dp.rooms_min = 2; dp.rooms_max = 16; dp.y_min = -MAX_MAP_GENERATION_LIMIT; dp.y_max = MAX_MAP_GENERATION_LIMIT; dp.np_density = NoiseParams(0.9, 0.5, v3f(500.0, 500.0, 500.0), 0, 2, 0.8, 2.0); dp.np_alt_wall = NoiseParams(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0); if (getBiome(0, node_min) == BT_DESERT) { dp.c_wall = c_desert_stone; dp.c_alt_wall = CONTENT_IGNORE; dp.c_stair = c_desert_stone; dp.diagonal_dirs = true; dp.holesize = v3s16(2, 3, 2); dp.roomsize = v3s16(2, 5, 2); dp.notifytype = GENNOTIFY_TEMPLE; } else { dp.c_wall = c_cobble; dp.c_alt_wall = c_mossycobble; dp.c_stair = c_stair_cobble; dp.diagonal_dirs = false; dp.holesize = v3s16(1, 2, 1); dp.roomsize = v3s16(0, 0, 0); dp.notifytype = GENNOTIFY_DUNGEON; } DungeonGen dgen(ndef, &gennotify, &dp); dgen.generate(vm, blockseed, full_node_min, full_node_max); } // Add top and bottom side of water to transforming_liquid queue updateLiquid(full_node_min, full_node_max); // Add surface nodes growGrass(); // Generate some trees, and add grass, if a jungle if (spflags & MGV6_TREES) placeTreesAndJungleGrass(); // Generate the registered decorations if (flags & MG_DECORATIONS) m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max); // Generate the registered ores m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max); // Calculate lighting if (flags & MG_LIGHT) calcLighting(node_min - v3s16(1, 1, 1) * MAP_BLOCKSIZE, node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE, full_node_min, full_node_max); this->generating = false; }
/* Lights neighbors of from_nodes, collects all them and then goes on recursively. */ void VoxelManipulator::spreadLight(enum LightBank bank, core::map<v3s16, bool> & from_nodes, INodeDefManager *nodemgr) { const v3s16 dirs[6] = { v3s16(0,0,1), // back v3s16(0,1,0), // top v3s16(1,0,0), // right v3s16(0,0,-1), // front v3s16(0,-1,0), // bottom v3s16(-1,0,0), // left }; if(from_nodes.size() == 0) return; core::map<v3s16, bool> lighted_nodes; core::map<v3s16, bool>::Iterator j; j = from_nodes.getIterator(); for(; j.atEnd() == false; j++) { v3s16 pos = j.getNode()->getKey(); emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1))); u32 i = m_area.index(pos); if(m_flags[i] & VOXELFLAG_INEXISTENT) continue; MapNode &n = m_data[i]; u8 oldlight = n.getLight(bank, nodemgr); u8 newlight = diminish_light(oldlight); // Loop through 6 neighbors for(u16 i=0; i<6; i++) { // Get the position of the neighbor node v3s16 n2pos = pos + dirs[i]; try { u32 n2i = m_area.index(n2pos); if(m_flags[n2i] & VOXELFLAG_INEXISTENT) continue; MapNode &n2 = m_data[n2i]; u8 light2 = n2.getLight(bank, nodemgr); /* If the neighbor is brighter than the current node, add to list (it will light up this node on its turn) */ if(light2 > undiminish_light(oldlight)) { lighted_nodes.insert(n2pos, true); } /* If the neighbor is dimmer than how much light this node would spread on it, add to list */ if(light2 < newlight) { if(nodemgr->get(n2).light_propagates) { n2.setLight(bank, newlight, nodemgr); lighted_nodes.insert(n2pos, true); } } } catch(InvalidPositionException &e) { continue; } } } /*dstream<<"spreadLight(): Changed block " <<blockchangecount<<" times" <<" for "<<from_nodes.size()<<" nodes" <<std::endl;*/ if(lighted_nodes.size() > 0) spreadLight(bank, lighted_nodes, nodemgr); }
/* Propagates sunlight down through the block. Doesn't modify nodes that are not affected by sunlight. Returns false if sunlight at bottom block is invalid. Returns true if sunlight at bottom block is valid. Returns true if bottom block doesn't exist. If there is a block above, continues from it. If there is no block above, assumes there is sunlight, unless is_underground is set or highest node is water. All sunlighted nodes are added to light_sources. if remove_light==true, sets non-sunlighted nodes black. if black_air_left!=NULL, it is set to true if non-sunlighted air is left in block. */ bool MapBlock::propagateSunlight(std::set<v3s16> & light_sources, bool remove_light, bool *black_air_left) { INodeDefManager *nodemgr = m_gamedef->ndef(); // Whether the sunlight at the top of the bottom block is valid bool block_below_is_valid = true; v3s16 pos_relative = getPosRelative(); for(s16 x=0; x<MAP_BLOCKSIZE; x++) { for(s16 z=0; z<MAP_BLOCKSIZE; z++) { #if 1 bool no_sunlight = false; //bool no_top_block = false; // Check if node above block has sunlight bool is_valid_position; MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z), &is_valid_position); if (is_valid_position) { if(n.getContent() == CONTENT_IGNORE) { // Trust heuristics no_sunlight = is_underground; } else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN) { no_sunlight = true; } } else { //no_top_block = true; // NOTE: This makes over-ground roofed places sunlighted // Assume sunlight, unless is_underground==true if(is_underground) { no_sunlight = true; } else { MapNode n = getNodeNoEx(v3s16(x, MAP_BLOCKSIZE-1, z)); if(m_gamedef->ndef()->get(n).sunlight_propagates == false) { no_sunlight = true; } } // NOTE: As of now, this just would make everything dark. // No sunlight here //no_sunlight = true; } #endif #if 0 // Doesn't work; nothing gets light. bool no_sunlight = true; bool no_top_block = false; // Check if node above block has sunlight try { MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z)); if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN) { no_sunlight = false; } } catch(InvalidPositionException &e) { no_top_block = true; } #endif /*std::cout<<"("<<x<<","<<z<<"): " <<"no_top_block="<<no_top_block <<", is_underground="<<is_underground <<", no_sunlight="<<no_sunlight <<std::endl;*/ s16 y = MAP_BLOCKSIZE-1; // This makes difference to diminishing in water. bool stopped_to_solid_object = false; u8 current_light = no_sunlight ? 0 : LIGHT_SUN; for(; y >= 0; y--) { v3s16 pos(x, y, z); MapNode &n = getNodeRef(pos); if(current_light == 0) { // Do nothing } else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates) { // Do nothing: Sunlight is continued } else if(nodemgr->get(n).light_propagates == false) { // A solid object is on the way. stopped_to_solid_object = true; // Light stops. current_light = 0; } else { // Diminish light current_light = diminish_light(current_light); } u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr); if(current_light > old_light || remove_light) { n.setLight(LIGHTBANK_DAY, current_light, nodemgr); } if(diminish_light(current_light) != 0) { light_sources.insert(pos_relative + pos); } if(current_light == 0 && stopped_to_solid_object) { if(black_air_left) { *black_air_left = true; } } } // Whether or not the block below should see LIGHT_SUN bool sunlight_should_go_down = (current_light == LIGHT_SUN); /* If the block below hasn't already been marked invalid: Check if the node below the block has proper sunlight at top. If not, the block below is invalid. Ignore non-transparent nodes as they always have no light */ if(block_below_is_valid) { MapNode n = getNodeParent(v3s16(x, -1, z), &is_valid_position); if (is_valid_position) { if(nodemgr->get(n).light_propagates) { if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN && sunlight_should_go_down == false) block_below_is_valid = false; else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN && sunlight_should_go_down == true) block_below_is_valid = false; } } else { /*std::cout<<"InvalidBlockException for bottom block node" <<std::endl;*/ // Just no block below, no need to panic. } } } } return block_below_is_valid; }
/* Goes recursively through the neighbours of the node. Alters only transparent nodes. If the lighting of the neighbour is lower than the lighting of the node was (before changing it to 0 at the step before), the lighting of the neighbour is set to 0 and then the same stuff repeats for the neighbour. The ending nodes of the routine are stored in light_sources. This is useful when a light is removed. In such case, this routine can be called for the light node and then again for light_sources to re-light the area without the removed light. values of from_nodes are lighting values. */ void VoxelManipulator::unspreadLight(enum LightBank bank, core::map<v3s16, u8> & from_nodes, core::map<v3s16, bool> & light_sources) { v3s16 dirs[6] = { v3s16(0,0,1), // back v3s16(0,1,0), // top v3s16(1,0,0), // right v3s16(0,0,-1), // front v3s16(0,-1,0), // bottom v3s16(-1,0,0), // left }; if(from_nodes.size() == 0) return; core::map<v3s16, u8> unlighted_nodes; core::map<v3s16, u8>::Iterator j; j = from_nodes.getIterator(); for(; j.atEnd() == false; j++) { v3s16 pos = j.getNode()->getKey(); emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1))); //MapNode &n = m_data[m_area.index(pos)]; u8 oldlight = j.getNode()->getValue(); // Loop through 6 neighbors for(u16 i=0; i<6; i++) { // Get the position of the neighbor node v3s16 n2pos = pos + dirs[i]; u32 n2i = m_area.index(n2pos); if(m_flags[n2i] & VOXELFLAG_INEXISTENT) continue; MapNode &n2 = m_data[n2i]; /* If the neighbor is dimmer than what was specified as oldlight (the light of the previous node) */ if(n2.getLight(bank, nodemgr) < oldlight) { /* And the neighbor is transparent and it has some light */ if(nodemgr->get(n2).light_propagates && n2.getLight(bank, nodemgr) != 0) { /* Set light to 0 and add to queue */ u8 current_light = n2.getLight(bank, nodemgr); n2.setLight(bank, 0); unlighted_nodes.insert(n2pos, current_light); /* Remove from light_sources if it is there NOTE: This doesn't happen nearly at all */ /*if(light_sources.find(n2pos)) { std::cout<<"Removed from light_sources"<<std::endl; light_sources.remove(n2pos); }*/ } } else{ light_sources.insert(n2pos, true); } } } /*dstream<<"unspreadLight(): Changed block " <<blockchangecount<<" times" <<" for "<<from_nodes.size()<<" nodes" <<std::endl;*/ if(unlighted_nodes.size() > 0) unspreadLight(bank, unlighted_nodes, light_sources); }
void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p, INodeDefManager *nodemgr) { const v3s16 dirs[6] = { v3s16(0,0,1), // back v3s16(0,1,0), // top v3s16(1,0,0), // right v3s16(0,0,-1), // front v3s16(0,-1,0), // bottom v3s16(-1,0,0), // left }; emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1))); u32 i = m_area.index(p); if(m_flags[i] & VOXELFLAG_INEXISTENT) return; MapNode &n = m_data[i]; u8 oldlight = n.getLight(bank, nodemgr); u8 newlight = diminish_light(oldlight); // Loop through 6 neighbors for(u16 i=0; i<6; i++) { // Get the position of the neighbor node v3s16 n2pos = p + dirs[i]; u32 n2i = m_area.index(n2pos); if(m_flags[n2i] & VOXELFLAG_INEXISTENT) continue; MapNode &n2 = m_data[n2i]; u8 light2 = n2.getLight(bank, nodemgr); /* If the neighbor is brighter than the current node, add to list (it will light up this node on its turn) */ if(light2 > undiminish_light(oldlight)) { spreadLight(bank, n2pos, nodemgr); } /* If the neighbor is dimmer than how much light this node would spread on it, add to list */ if(light2 < newlight) { if(nodemgr->get(n2).light_propagates) { n2.setLight(bank, newlight, nodemgr); spreadLight(bank, n2pos, nodemgr); } } } }
void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight, core::map<v3s16, bool> & light_sources, INodeDefManager *nodemgr) { v3s16 dirs[6] = { v3s16(0,0,1), // back v3s16(0,1,0), // top v3s16(1,0,0), // right v3s16(0,0,-1), // front v3s16(0,-1,0), // bottom v3s16(-1,0,0), // left }; emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1))); // Loop through 6 neighbors for(u16 i=0; i<6; i++) { // Get the position of the neighbor node v3s16 n2pos = p + dirs[i]; u32 n2i = m_area.index(n2pos); if(m_flags[n2i] & VOXELFLAG_INEXISTENT) continue; MapNode &n2 = m_data[n2i]; /* If the neighbor is dimmer than what was specified as oldlight (the light of the previous node) */ u8 light2 = n2.getLight(bank, nodemgr); if(light2 < oldlight) { /* And the neighbor is transparent and it has some light */ if(nodemgr->get(n2).light_propagates && light2 != 0) { /* Set light to 0 and add to queue */ n2.setLight(bank, 0, nodemgr); unspreadLight(bank, n2pos, light2, light_sources, nodemgr); /* Remove from light_sources if it is there NOTE: This doesn't happen nearly at all */ /*if(light_sources.find(n2pos)) { std::cout<<"Removed from light_sources"<<std::endl; light_sources.remove(n2pos); }*/ } } else{ light_sources.insert(n2pos, true); } } }
void VoxelManipulator::addArea(VoxelArea area) { // Cancel if requested area has zero volume if(area.getExtent() == v3s16(0,0,0)) return; // Cancel if m_area already contains the requested area if(m_area.contains(area)) return; TimeTaker timer("addArea", &addarea_time); // Calculate new area VoxelArea new_area; // New area is the requested area if m_area has zero volume if(m_area.getExtent() == v3s16(0,0,0)) { new_area = area; } // Else add requested area to m_area else { new_area = m_area; new_area.addArea(area); } s32 new_size = new_area.getVolume(); /*dstream<<"adding area "; area.print(dstream); dstream<<", old area "; m_area.print(dstream); dstream<<", new area "; new_area.print(dstream); dstream<<", new_size="<<new_size; dstream<<std::endl;*/ // Allocate and clear new data MapNode *new_data = new MapNode[new_size]; u8 *new_flags = new u8[new_size]; for(s32 i=0; i<new_size; i++) { new_flags[i] = VOXELFLAG_NOT_LOADED; } // Copy old data for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++) for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++) for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++) { // If loaded, copy data and flags if((m_flags[m_area.index(x,y,z)] & VOXELFLAG_NOT_LOADED) == false) { new_data[new_area.index(x,y,z)] = m_data[m_area.index(x,y,z)]; new_flags[new_area.index(x,y,z)] = m_flags[m_area.index(x,y,z)]; } } // Replace area, data and flags m_area = new_area; MapNode *old_data = m_data; u8 *old_flags = m_flags; /*dstream<<"old_data="<<(int)old_data<<", new_data="<<(int)new_data <<", old_flags="<<(int)m_flags<<", new_flags="<<(int)new_flags<<std::endl;*/ m_data = new_data; m_flags = new_flags; if(old_data) delete[] old_data; if(old_flags) delete[] old_flags; //dstream<<"addArea done"<<std::endl; }
void MapgenV6::makeChunk(BlockMakeData *data) { assert(data->vmanip); assert(data->nodedef); assert(data->blockpos_requested.X >= data->blockpos_min.X && data->blockpos_requested.Y >= data->blockpos_min.Y && data->blockpos_requested.Z >= data->blockpos_min.Z); assert(data->blockpos_requested.X <= data->blockpos_max.X && data->blockpos_requested.Y <= data->blockpos_max.Y && data->blockpos_requested.Z <= data->blockpos_max.Z); this->generating = true; this->vm = data->vmanip; this->ndef = data->nodedef; // Hack: use minimum block coords for old code that assumes a single block v3s16 blockpos = data->blockpos_requested; v3s16 blockpos_min = data->blockpos_min; v3s16 blockpos_max = data->blockpos_max; // Area of central chunk node_min = blockpos_min*MAP_BLOCKSIZE; node_max = (blockpos_max+v3s16(1,1,1))*MAP_BLOCKSIZE-v3s16(1,1,1); // Full allocated area full_node_min = (blockpos_min-1)*MAP_BLOCKSIZE; full_node_max = (blockpos_max+2)*MAP_BLOCKSIZE-v3s16(1,1,1); central_area_size = node_max - node_min + v3s16(1,1,1); assert(central_area_size.X == central_area_size.Z); int volume_blocks = (blockpos_max.X - blockpos_min.X + 1) * (blockpos_max.Y - blockpos_min.Y + 1) * (blockpos_max.Z - blockpos_max.Z + 1); volume_nodes = volume_blocks * MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE; // Create a block-specific seed blockseed = get_blockseed(data->seed, full_node_min); // Make some noise calculateNoise(); c_stone = ndef->getId("mapgen_stone"); c_dirt = ndef->getId("mapgen_dirt"); c_dirt_with_grass = ndef->getId("mapgen_dirt_with_grass"); c_sand = ndef->getId("mapgen_sand"); c_water_source = ndef->getId("mapgen_water_source"); c_lava_source = ndef->getId("mapgen_lava_source"); c_gravel = ndef->getId("mapgen_gravel"); c_cobble = ndef->getId("mapgen_cobble"); c_desert_sand = ndef->getId("mapgen_desert_sand"); c_desert_stone = ndef->getId("mapgen_desert_stone"); c_mossycobble = ndef->getId("mapgen_mossycobble"); c_sandbrick = ndef->getId("mapgen_sandstonebrick"); c_stair_cobble = ndef->getId("mapgen_stair_cobble"); c_stair_sandstone = ndef->getId("mapgen_stair_sandstone"); if (c_desert_sand == CONTENT_IGNORE) c_desert_sand = c_sand; if (c_desert_stone == CONTENT_IGNORE) c_desert_stone = c_stone; if (c_mossycobble == CONTENT_IGNORE) c_mossycobble = c_cobble; if (c_sandbrick == CONTENT_IGNORE) c_sandbrick = c_desert_stone; if (c_stair_cobble == CONTENT_IGNORE) c_stair_cobble = c_cobble; if (c_stair_sandstone == CONTENT_IGNORE) c_stair_sandstone = c_sandbrick; // Maximum height of the stone surface and obstacles. // This is used to guide the cave generation s16 stone_surface_max_y; // Generate general ground level to full area stone_surface_max_y = generateGround(); generateExperimental(); const s16 max_spread_amount = MAP_BLOCKSIZE; // Limit dirt flow area by 1 because mud is flown into neighbors. s16 mudflow_minpos = -max_spread_amount + 1; s16 mudflow_maxpos = central_area_size.X + max_spread_amount - 2; // Loop this part, it will make stuff look older and newer nicely const u32 age_loops = 2; for (u32 i_age = 0; i_age < age_loops; i_age++) { // Aging loop // Make caves (this code is relatively horrible) if (flags & MG_CAVES) generateCaves(stone_surface_max_y); // Add mud to the central chunk addMud(); // Add blobs of dirt and gravel underground addDirtGravelBlobs(); // Flow mud away from steep edges flowMud(mudflow_minpos, mudflow_maxpos); } // Add dungeons if (flags & MG_DUNGEONS) { DungeonParams dp; dp.np_rarity = nparams_dungeon_rarity; dp.np_density = nparams_dungeon_density; dp.np_wetness = nparams_dungeon_wetness; dp.c_water = c_water_source; if (getBiome(0, v2s16(node_min.X, node_min.Z)) == BT_NORMAL) { dp.c_cobble = c_cobble; dp.c_moss = c_mossycobble; dp.c_stair = c_stair_cobble; dp.diagonal_dirs = false; dp.mossratio = 3.0; dp.holesize = v3s16(1, 2, 1); dp.roomsize = v3s16(0, 0, 0); dp.notifytype = GENNOTIFY_DUNGEON; } else { dp.c_cobble = c_sandbrick; dp.c_moss = c_sandbrick; // should make this 'cracked sandstone' later dp.c_stair = c_stair_sandstone; dp.diagonal_dirs = true; dp.mossratio = 0.0; dp.holesize = v3s16(2, 3, 2); dp.roomsize = v3s16(2, 5, 2); dp.notifytype = GENNOTIFY_TEMPLE; } DungeonGen dgen(this, &dp); dgen.generate(blockseed, full_node_min, full_node_max); } // Add top and bottom side of water to transforming_liquid queue updateLiquid(&data->transforming_liquid, full_node_min, full_node_max); // Grow grass growGrass(); // Generate some trees, and add grass, if a jungle if (flags & MG_TREES) placeTreesAndJungleGrass(); // Generate the registered decorations for (unsigned int i = 0; i != emerge->decorations.size(); i++) { Decoration *deco = emerge->decorations[i]; deco->placeDeco(this, blockseed + i, node_min, node_max); } // Generate the registered ores for (unsigned int i = 0; i != emerge->ores.size(); i++) { Ore *ore = emerge->ores[i]; ore->placeOre(this, blockseed + i, node_min, node_max); } // Calculate lighting if (!(flags & MG_NOLIGHT)) calcLighting(node_min - v3s16(1, 1, 1) * MAP_BLOCKSIZE, node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE); this->generating = false; }
bool PathFinder::findPathHeuristic(v3s16 pos, std::vector <v3s16>& directions, unsigned int (*heuristicFunction)(v3s16, v3s16)) { std::multiset <OpenElement> q; used.clear(); q.insert(OpenElement(heuristicFunction(pos, m_destination), 0, pos, v3s16(0, 0, 0))); while(!q.empty()) { v3s16 current_pos = q.begin()->pos; v3s16 prev_pos = q.begin()->prev_pos; unsigned int current_cost = q.begin()->start_cost; q.erase(q.begin()); for(unsigned int i = 0; i < directions.size(); ++i) { v3s16 next_pos = current_pos + directions[i]; unsigned int next_cost = current_cost + getDirectionCost(i); // Check limits or already processed if((next_pos.X < m_limits.X.min) || (next_pos.X >= m_limits.X.max) || (next_pos.Z < m_limits.Z.min) || (next_pos.Z >= m_limits.Z.max)) { continue; } MapNode node_at_next_pos = m_env->getMap().getNodeNoEx(next_pos); if(node_at_next_pos.param0 == CONTENT_IGNORE) { continue; } if(node_at_next_pos.param0 == CONTENT_AIR) { MapNode node_below_next_pos = m_env->getMap().getNodeNoEx(next_pos + v3s16(0, -1, 0)); if(node_below_next_pos.param0 == CONTENT_IGNORE) { continue; } if(node_below_next_pos.param0 == CONTENT_AIR) { // Try jump down v3s16 test_pos = next_pos - v3s16(0, -1, 0); MapNode node_at_test_pos = m_env->getMap().getNodeNoEx(test_pos); while((node_at_test_pos.param0 == CONTENT_AIR) && (test_pos.Y > m_limits.Y.min)) { --test_pos.Y; node_at_test_pos = m_env->getMap().getNodeNoEx(test_pos); } ++test_pos.Y; if((test_pos.Y >= m_limits.Y.min) && (node_at_test_pos.param0 != CONTENT_IGNORE) && (node_at_test_pos.param0 != CONTENT_AIR) && ((next_pos.Y - test_pos.Y) <= m_maxdrop)) { next_pos.Y = test_pos.Y; next_cost = current_cost + getDirectionCost(i) * 2; } else { continue; } } } else { // Try jump up v3s16 test_pos = next_pos; MapNode node_at_test_pos = m_env->getMap().getNodeNoEx(test_pos); while((node_at_test_pos.param0 != CONTENT_IGNORE) && (node_at_test_pos.param0 != CONTENT_AIR) && (test_pos.Y < m_limits.Y.max)) { ++test_pos.Y; node_at_test_pos = m_env->getMap().getNodeNoEx(test_pos); } // Did we find surface? if((test_pos.Y <= m_limits.Y.max) && (node_at_test_pos.param0 == CONTENT_AIR) && (test_pos.Y - next_pos.Y <= m_maxjump)) { next_pos.Y = test_pos.Y; next_cost = current_cost + getDirectionCost(i) * 2; } else { continue; } } if((used.find(next_pos) == used.end()) || (used[next_pos].second > next_cost)) { used[next_pos].first = current_pos; used[next_pos].second = next_cost; q.insert(OpenElement(next_cost + heuristicFunction(next_pos, m_destination), next_cost, next_pos, current_pos)); } } if(current_pos == m_destination) { return true; } } return (used.find(m_destination) != used.end()); }
/* 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]); if (n.getContent() == CONTENT_IGNORE) { ambient_occlusion++; 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 += decode_light(n.getLight(bank, ndef)); light_count++; } } 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 LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, std::vector<CollisionInfo> *collision_info) { Map *map = &env->getMap(); INodeDefManager *nodemgr = m_gamedef->ndef(); v3f position = getPosition(); // Copy parent position if local player is attached if(isAttached) { setPosition(overridePosition); m_sneak_node_exists = false; return; } // Skip collision detection if noclip mode is used bool fly_allowed = m_gamedef->checkLocalPrivilege("fly"); bool noclip = m_gamedef->checkLocalPrivilege("noclip") && g_settings->getBool("noclip"); bool free_move = noclip && fly_allowed && g_settings->getBool("free_move"); if (free_move) { position += m_speed * dtime; setPosition(position); m_sneak_node_exists = false; return; } /* Collision detection */ bool is_valid_position; MapNode node; v3s16 pp; /* Check if player is in liquid (the oscillating value) */ // If in liquid, the threshold of coming out is at higher y if (in_liquid) { pp = floatToInt(position + v3f(0,BS*0.1,0), BS); node = map->getNodeNoEx(pp, &is_valid_position); if (is_valid_position) { in_liquid = nodemgr->get(node.getContent()).isLiquid(); liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity; } else { in_liquid = false; } } // If not in liquid, the threshold of going in is at lower y else { pp = floatToInt(position + v3f(0,BS*0.5,0), BS); node = map->getNodeNoEx(pp, &is_valid_position); if (is_valid_position) { in_liquid = nodemgr->get(node.getContent()).isLiquid(); liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity; } else { in_liquid = false; } } /* Check if player is in liquid (the stable value) */ pp = floatToInt(position + v3f(0,0,0), BS); node = map->getNodeNoEx(pp, &is_valid_position); if (is_valid_position) { in_liquid_stable = nodemgr->get(node.getContent()).isLiquid(); } else { in_liquid_stable = false; } /* Check if player is climbing */ pp = floatToInt(position + v3f(0,0.5*BS,0), BS); v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS); node = map->getNodeNoEx(pp, &is_valid_position); bool is_valid_position2; MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2); if (!(is_valid_position && is_valid_position2)) { is_climbing = false; } else { is_climbing = (nodemgr->get(node.getContent()).climbable || nodemgr->get(node2.getContent()).climbable) && !free_move; } /* Collision uncertainty radius Make it a bit larger than the maximum distance of movement */ //f32 d = pos_max_d * 1.1; // A fairly large value in here makes moving smoother f32 d = 0.15*BS; // This should always apply, otherwise there are glitches sanity_check(d > pos_max_d); // Maximum distance over border for sneaking f32 sneak_max = BS*0.4; /* If sneaking, keep in range from the last walked node and don't fall off from it */ if(control.sneak && m_sneak_node_exists && !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid && physics_override_sneak) { f32 maxd = 0.5*BS + sneak_max; v3f lwn_f = intToFloat(m_sneak_node, BS); position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd); position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd); if(!is_climbing) { f32 min_y = lwn_f.Y + 0.5*BS; if(position.Y < min_y) { position.Y = min_y; if(m_speed.Y < 0) m_speed.Y = 0; } } } // this shouldn't be hardcoded but transmitted from server float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2); #ifdef __ANDROID__ player_stepheight += (0.5 * BS); #endif v3f accel_f = v3f(0,0,0); collisionMoveResult result = collisionMoveSimple(env, m_gamedef, pos_max_d, m_collisionbox, player_stepheight, dtime, position, m_speed, accel_f); /* If the player's feet touch the topside of any node, this is set to true. Player is allowed to jump when this is true. */ bool touching_ground_was = touching_ground; touching_ground = result.touching_ground; //bool standing_on_unloaded = result.standing_on_unloaded; /* Check the nodes under the player to see from which node the player is sneaking from, if any. If the node from under the player has been removed, the player falls. */ v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS); if(m_sneak_node_exists && nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" && m_old_node_below_type != "air") { // Old node appears to have been removed; that is, // it wasn't air before but now it is m_need_to_get_new_sneak_node = false; m_sneak_node_exists = false; } else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air") { // We are on something, so make sure to recalculate the sneak // node. m_need_to_get_new_sneak_node = true; } if(m_need_to_get_new_sneak_node && physics_override_sneak) { v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS); v2f player_p2df(position.X, position.Z); f32 min_distance_f = 100000.0*BS; // If already seeking from some node, compare to it. /*if(m_sneak_node_exists) { v3f sneaknode_pf = intToFloat(m_sneak_node, BS); v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z); f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df); f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y); // Ignore if player is not on the same level (likely dropped) if(d_vert_f < 0.15*BS) min_distance_f = d_horiz_f; }*/ v3s16 new_sneak_node = m_sneak_node; for(s16 x=-1; x<=1; x++) for(s16 z=-1; z<=1; z++) { v3s16 p = pos_i_bottom + v3s16(x,0,z); v3f pf = intToFloat(p, BS); v2f node_p2df(pf.X, pf.Z); f32 distance_f = player_p2df.getDistanceFrom(node_p2df); f32 max_axis_distance_f = MYMAX( fabs(player_p2df.X-node_p2df.X), fabs(player_p2df.Y-node_p2df.Y)); if(distance_f > min_distance_f || max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS) continue; // The node to be sneaked on has to be walkable node = map->getNodeNoEx(p, &is_valid_position); if (!is_valid_position || nodemgr->get(node).walkable == false) continue; // And the node above it has to be nonwalkable node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position); if (!is_valid_position || nodemgr->get(node).walkable) { continue; } if (!physics_override_sneak_glitch) { node =map->getNodeNoEx(p + v3s16(0,2,0), &is_valid_position); if (!is_valid_position || nodemgr->get(node).walkable) continue; } min_distance_f = distance_f; new_sneak_node = p; } bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9); m_sneak_node = new_sneak_node; m_sneak_node_exists = sneak_node_found; /* If sneaking, the player's collision box can be in air, so this has to be set explicitly */ if(sneak_node_found && control.sneak) touching_ground = true; } /* Set new position */ setPosition(position); /* Report collisions */ bool bouncy_jump = false; // Dont report if flying if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) { for(size_t i=0; i<result.collisions.size(); i++) { const CollisionInfo &info = result.collisions[i]; collision_info->push_back(info); if(info.new_speed.Y - info.old_speed.Y > 0.1*BS && info.bouncy) bouncy_jump = true; } } if(bouncy_jump && control.jump){ m_speed.Y += movement_speed_jump*BS; touching_ground = false; MtEvent *e = new SimpleTriggerEvent("PlayerJump"); m_gamedef->event()->put(e); } if(!touching_ground_was && touching_ground){ MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround"); m_gamedef->event()->put(e); // Set camera impact value to be used for view bobbing camera_impact = getSpeed().Y * -1; } { camera_barely_in_ceiling = false; v3s16 camera_np = floatToInt(getEyePosition(), BS); MapNode n = map->getNodeNoEx(camera_np); if(n.getContent() != CONTENT_IGNORE){ if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){ camera_barely_in_ceiling = true; } } } /* Update the node last under the player */ m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS); m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name; /* Check properties of the node on which the player is standing */ const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos())); // Determine if jumping is possible m_can_jump = touching_ground && !in_liquid; if(itemgroup_get(f.groups, "disable_jump")) m_can_jump = false; }
void WieldMeshSceneNode::setItem(const ItemStack &item, IGameDef *gamedef) { ITextureSource *tsrc = gamedef->getTextureSource(); IItemDefManager *idef = gamedef->getItemDefManager(); //IShaderSource *shdrsrc = gamedef->getShaderSource(); INodeDefManager *ndef = gamedef->getNodeDefManager(); const ItemDefinition &def = item.getDefinition(idef); const ContentFeatures &f = ndef->get(def.name); content_t id = ndef->getId(def.name); #if 0 //// TODO(RealBadAngel): Reactivate when shader is added for wield items if (m_enable_shaders) { u32 shader_id = shdrsrc->getShader("nodes_shader", TILE_MATERIAL_BASIC, NDT_NORMAL); m_material_type = shdrsrc->getShaderInfo(shader_id).material; } #endif // If wield_image is defined, it overrides everything else if (def.wield_image != "") { setExtruded(def.wield_image, def.wield_scale, tsrc, 1); return; } // Handle nodes // See also CItemDefManager::createClientCached() else if (def.type == ITEM_NODE) { if (f.mesh_ptr[0]) { // e.g. mesh nodes and nodeboxes changeToMesh(f.mesh_ptr[0]); // mesh_ptr[0] is pre-scaled by BS * f->visual_scale m_meshnode->setScale( def.wield_scale * WIELD_SCALE_FACTOR / (BS * f.visual_scale)); } else if (f.drawtype == NDT_AIRLIKE) { changeToMesh(NULL); } else if (f.drawtype == NDT_PLANTLIKE) { setExtruded(tsrc->getTextureName(f.tiles[0].texture_id), def.wield_scale, tsrc, f.tiles[0].animation_frame_count); } else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES) { setCube(f.tiles, def.wield_scale, tsrc); } else { //// TODO: Change false in the following constructor args to //// appropriate value when shader is added for wield items (if applicable) MeshMakeData mesh_make_data(gamedef, false); MapNode mesh_make_node(id, 255, 0); mesh_make_data.fillSingleNode(&mesh_make_node); MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0)); changeToMesh(mapblock_mesh.getMesh()); translateMesh(m_meshnode->getMesh(), v3f(-BS, -BS, -BS)); m_meshnode->setScale( def.wield_scale * WIELD_SCALE_FACTOR / (BS * f.visual_scale)); } u32 material_count = m_meshnode->getMaterialCount(); if (material_count > 6) { errorstream << "WieldMeshSceneNode::setItem: Invalid material " "count " << material_count << ", truncating to 6" << std::endl; material_count = 6; } for (u32 i = 0; i < material_count; ++i) { video::SMaterial &material = m_meshnode->getMaterial(i); material.setFlag(video::EMF_BACK_FACE_CULLING, true); material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter); material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_filter); bool animated = (f.tiles[i].animation_frame_count > 1); if (animated) { FrameSpec animation_frame = f.tiles[i].frames[0]; material.setTexture(0, animation_frame.texture); } else { material.setTexture(0, f.tiles[i].texture); } material.MaterialType = m_material_type; #if 0 //// TODO(RealBadAngel): Reactivate when shader is added for wield items if (m_enable_shaders) { if (f.tiles[i].normal_texture) { if (animated) { FrameSpec animation_frame = f.tiles[i].frames[0]; material.setTexture(1, animation_frame.normal_texture); } else { material.setTexture(1, f.tiles[i].normal_texture); } material.setTexture(2, tsrc->getTexture("enable_img.png")); } else { material.setTexture(2, tsrc->getTexture("disable_img.png")); } } #endif } return; } else if (def.inventory_image != "") { setExtruded(def.inventory_image, def.wield_scale, tsrc, 1); return; } // no wield mesh found changeToMesh(NULL); }
void MapgenValleys::makeChunk(BlockMakeData *data) { // Pre-conditions assert(data->vmanip); assert(data->nodedef); assert(data->blockpos_requested.X >= data->blockpos_min.X && data->blockpos_requested.Y >= data->blockpos_min.Y && data->blockpos_requested.Z >= data->blockpos_min.Z); assert(data->blockpos_requested.X <= data->blockpos_max.X && data->blockpos_requested.Y <= data->blockpos_max.Y && data->blockpos_requested.Z <= data->blockpos_max.Z); this->generating = true; this->vm = data->vmanip; this->ndef = data->nodedef; //TimeTaker t("makeChunk"); v3s16 blockpos_min = data->blockpos_min; v3s16 blockpos_max = data->blockpos_max; node_min = blockpos_min * MAP_BLOCKSIZE; node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1); full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE; full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1); blockseed = getBlockSeed2(full_node_min, seed); // Generate noise maps and base terrain height. calculateNoise(); // Generate base terrain with initial heightmaps s16 stone_surface_max_y = generateTerrain(); // Create biomemap at heightmap surface bmgr->calcBiomes(csize.X, csize.Z, heatmap, humidmap, heightmap, biomemap); // Actually place the biome-specific nodes MgStoneType stone_type = generateBiomes(heatmap, humidmap); // Cave creation. if (flags & MG_CAVES) generateCaves(stone_surface_max_y); // Dungeon creation if ((flags & MG_DUNGEONS) && node_max.Y < 50 && (stone_surface_max_y >= node_min.Y)) { DungeonParams dp; dp.np_rarity = nparams_dungeon_rarity; dp.np_density = nparams_dungeon_density; dp.np_wetness = nparams_dungeon_wetness; dp.c_water = c_water_source; if (stone_type == STONE) { dp.c_cobble = c_cobble; dp.c_moss = c_mossycobble; dp.c_stair = c_stair_cobble; dp.diagonal_dirs = false; dp.mossratio = 3.f; dp.holesize = v3s16(1, 2, 1); dp.roomsize = v3s16(0, 0, 0); dp.notifytype = GENNOTIFY_DUNGEON; } else if (stone_type == DESERT_STONE) { dp.c_cobble = c_desert_stone; dp.c_moss = c_desert_stone; dp.c_stair = c_desert_stone; dp.diagonal_dirs = true; dp.mossratio = 0.f; dp.holesize = v3s16(2, 3, 2); dp.roomsize = v3s16(2, 5, 2); dp.notifytype = GENNOTIFY_TEMPLE; } else if (stone_type == SANDSTONE) { dp.c_cobble = c_sandstonebrick; dp.c_moss = c_sandstonebrick; dp.c_stair = c_sandstonebrick; dp.diagonal_dirs = false; dp.mossratio = 0.f; dp.holesize = v3s16(2, 2, 2); dp.roomsize = v3s16(2, 0, 2); dp.notifytype = GENNOTIFY_DUNGEON; } DungeonGen dgen(this, &dp); dgen.generate(blockseed, full_node_min, full_node_max); } // Generate the registered decorations if (flags & MG_DECORATIONS) m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max); // Generate the registered ores m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max); // Sprinkle some dust on top after everything else was generated dustTopNodes(); //TimeTaker tll("liquid_lighting"); updateLiquid(&data->transforming_liquid, full_node_min, full_node_max); if (flags & MG_LIGHT) calcLighting( node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0), full_node_min, full_node_max); //mapgen_profiler->avg("liquid_lighting", tll.stop() / 1000.f); //mapgen_profiler->avg("makeChunk", t.stop() / 1000.f); this->generating = false; }
void DungeonGen::makeDungeon(v3s16 start_padding) { v3s16 areasize = vm->m_area.getExtent(); v3s16 roomsize; v3s16 roomplace; float far_multi = farscale(5, vm->m_area.MinEdge.X, vm->m_area.MinEdge.Y, vm->m_area.MinEdge.Z); /* Find place for first room */ bool fits = false; for (u32 i = 0; i < 100 && !fits; i++) { bool is_large_room = ((random.next() & 3) == 1); roomsize = is_large_room ? v3s16(random.range(8, 16 * far_multi), random.range(8, 16 * far_multi), random.range(8, 16 * far_multi)) : v3s16(random.range(4, 8 * far_multi), random.range(4, 6 * far_multi), random.range(4, 8 * far_multi)); roomsize += dp.roomsize; // start_padding is used to disallow starting the generation of // a dungeon in a neighboring generation chunk roomplace = vm->m_area.MinEdge + start_padding + v3s16( random.range(0, areasize.X - roomsize.X - start_padding.X), random.range(0, areasize.Y - roomsize.Y - start_padding.Y), random.range(0, areasize.Z - roomsize.Z - start_padding.Z)); /* Check that we're not putting the room to an unknown place, otherwise it might end up floating in the air */ fits = true; for (s16 z = 0; z < roomsize.Z; z++) for (s16 y = 0; y < roomsize.Y; y++) for (s16 x = 0; x < roomsize.X; x++) { v3s16 p = roomplace + v3s16(x, y, z); u32 vi = vm->m_area.index(p); if ((vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) || vm->m_data[vi].getContent() == CONTENT_IGNORE) { fits = false; break; } } } // No place found if (fits == false) return; /* Stores the center position of the last room made, so that a new corridor can be started from the last room instead of the new room, if chosen so. */ v3s16 last_room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2); u32 room_count = random.range(2, random.range(8, 16 * far_multi)); for (u32 i = 0; i < room_count; i++) { // Make a room to the determined place makeRoom(roomsize, roomplace); v3s16 room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2); mg->gennotify.addEvent(dp.notifytype, room_center); #ifdef DGEN_USE_TORCHES // Place torch at room center (for testing) vm->m_data[vm->m_area.index(room_center)] = MapNode(c_torch); #endif // Quit if last room if (i == room_count - 1) break; // Determine walker start position bool start_in_last_room = (random.range(0, 2) != 0); v3s16 walker_start_place; if (start_in_last_room) { walker_start_place = last_room_center; } else { walker_start_place = room_center; // Store center of current room as the last one last_room_center = room_center; } // Create walker and find a place for a door v3s16 doorplace; v3s16 doordir; m_pos = walker_start_place; if (!findPlaceForDoor(doorplace, doordir)) return; if (random.range(0, 1) == 0) // Make the door makeDoor(doorplace, doordir); else // Don't actually make a door doorplace -= doordir; // Make a random corridor starting from the door v3s16 corridor_end; v3s16 corridor_end_dir; makeCorridor(doorplace, doordir, corridor_end, corridor_end_dir); // Find a place for a random sized room roomsize = v3s16(random.range(4, 8 * far_multi), random.range(4, 6 * far_multi), random.range(4, 8 * far_multi)); roomsize += dp.roomsize; m_pos = corridor_end; m_dir = corridor_end_dir; if (!findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace)) return; if (random.range(0, 1) == 0) // Make the door makeDoor(doorplace, doordir); else // Don't actually make a door roomplace -= doordir; } }
void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos) { // 340ms @cs=8 //TimeTaker timer1("flow mud"); // Iterate a few times for (s16 k = 0; k < 3; k++) { for (s16 z = mudflow_minpos; z <= mudflow_maxpos; z++) for (s16 x = mudflow_minpos; x <= mudflow_maxpos; x++) { // Invert coordinates every 2nd iteration if (k % 2 == 0) { x = mudflow_maxpos - (x - mudflow_minpos); z = mudflow_maxpos - (z - mudflow_minpos); } // Node position in 2d v2s16 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x, z); v3s16 em = vm->m_area.getExtent(); u32 i = vm->m_area.index(p2d.X, node_max.Y, p2d.Y); s16 y = node_max.Y; while (y >= node_min.Y) { for (;; y--) { MapNode *n = NULL; // Find mud for (; y >= node_min.Y; y--) { n = &vm->m_data[i]; if (n->getContent() == c_dirt || n->getContent() == c_dirt_with_grass || n->getContent() == c_gravel) break; vm->m_area.add_y(em, i, -1); } // Stop if out of area //if(vmanip.m_area.contains(i) == false) if (y < node_min.Y) break; if (n->getContent() == c_dirt || n->getContent() == c_dirt_with_grass) { // Make it exactly mud n->setContent(c_dirt); // Don't flow it if the stuff under it is not mud { u32 i2 = i; vm->m_area.add_y(em, i2, -1); // Cancel if out of area if (vm->m_area.contains(i2) == false) continue; MapNode *n2 = &vm->m_data[i2]; if (n2->getContent() != c_dirt && n2->getContent() != c_dirt_with_grass) continue; } } v3s16 dirs4[4] = { v3s16(0, 0, 1), // back v3s16(1, 0, 0), // right v3s16(0, 0, -1), // front v3s16(-1, 0, 0), // left }; // Check that upper is air or doesn't exist. // Cancel dropping if upper keeps it in place u32 i3 = i; vm->m_area.add_y(em, i3, 1); if (vm->m_area.contains(i3) == true && ndef->get(vm->m_data[i3]).walkable) continue; // Drop mud on side for (u32 di = 0; di < 4; di++) { v3s16 dirp = dirs4[di]; u32 i2 = i; // Move to side vm->m_area.add_p(em, i2, dirp); // Fail if out of area if (vm->m_area.contains(i2) == false) continue; // Check that side is air MapNode *n2 = &vm->m_data[i2]; if (ndef->get(*n2).walkable) continue; // Check that under side is air vm->m_area.add_y(em, i2, -1); if (vm->m_area.contains(i2) == false) continue; n2 = &vm->m_data[i2]; if (ndef->get(*n2).walkable) continue; // Loop further down until not air bool dropped_to_unknown = false; do { vm->m_area.add_y(em, i2, -1); n2 = &vm->m_data[i2]; // if out of known area if (vm->m_area.contains(i2) == false || n2->getContent() == CONTENT_IGNORE) { dropped_to_unknown = true; break; } } while (ndef->get(*n2).walkable == false); // Loop one up so that we're in air vm->m_area.add_y(em, i2, 1); n2 = &vm->m_data[i2]; bool old_is_water = (n->getContent() == c_water_source); // Move mud to new place if (!dropped_to_unknown) { *n2 = *n; // Set old place to be air (or water) if (old_is_water) *n = MapNode(c_water_source); else *n = MapNode(CONTENT_AIR); } // Done break; } } } } } }
void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace) { MapNode n_cobble(dp.c_cobble); MapNode n_air(CONTENT_AIR); // Make +-X walls for (s16 z = 0; z < roomsize.Z; z++) for (s16 y = 0; y < roomsize.Y; y++) { { v3s16 p = roomplace + v3s16(0, y, z); if (!vm->m_area.contains(p)) continue; u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; vm->m_data[vi] = n_cobble; } { v3s16 p = roomplace + v3s16(roomsize.X - 1, y, z); if (!vm->m_area.contains(p)) continue; u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; vm->m_data[vi] = n_cobble; } } // Make +-Z walls for (s16 x = 0; x < roomsize.X; x++) for (s16 y = 0; y < roomsize.Y; y++) { { v3s16 p = roomplace + v3s16(x, y, 0); if (!vm->m_area.contains(p)) continue; u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; vm->m_data[vi] = n_cobble; } { v3s16 p = roomplace + v3s16(x, y, roomsize.Z - 1); if (!vm->m_area.contains(p)) continue; u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; vm->m_data[vi] = n_cobble; } } // Make +-Y walls (floor and ceiling) for (s16 z = 0; z < roomsize.Z; z++) for (s16 x = 0; x < roomsize.X; x++) { { v3s16 p = roomplace + v3s16(x, 0, z); if (!vm->m_area.contains(p)) continue; u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; vm->m_data[vi] = n_cobble; } { v3s16 p = roomplace + v3s16(x,roomsize. Y - 1, z); if (!vm->m_area.contains(p)) continue; u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; vm->m_data[vi] = n_cobble; } } // Fill with air for (s16 z = 1; z < roomsize.Z - 1; z++) for (s16 y = 1; y < roomsize.Y - 1; y++) for (s16 x = 1; x < roomsize.X - 1; x++) { v3s16 p = roomplace + v3s16(x, y, z); if (!vm->m_area.contains(p)) continue; u32 vi = vm->m_area.index(p); vm->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE; vm->m_data[vi] = n_air; } }
/* 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 DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir, v3s16 &result_place, v3s16 &result_dir) { makeHole(doorplace); v3s16 p0 = doorplace; v3s16 dir = doordir; u32 length; /*if (random.next() % 2) length = random.range(1, 13); else length = random.range(1, 6);*/ length = random.range(1, 13); u32 partlength = random.range(1, 13); u32 partcount = 0; s16 make_stairs = 0; if (random.next() % 2 == 0 && partlength >= 3) make_stairs = random.next() % 2 ? 1 : -1; for (u32 i = 0; i < length; i++) { v3s16 p = p0 + dir; if (partcount != 0) p.Y += make_stairs; if (vm->m_area.contains(p) && vm->m_area.contains(p + v3s16(0, 1, 0)) && vm->m_area.contains(v3s16(p.X - dir.X, p.Y - 1, p.Z - dir.Z))) { if (make_stairs) { makeFill(p + v3s16(-1, -1, -1), dp.holesize + v3s16(2, 3, 2), VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(dp.c_cobble), 0); makeHole(p); makeHole(p - dir); // TODO: fix stairs code so it works 100% // (quite difficult) // exclude stairs from the bottom step // exclude stairs from diagonal steps if (((dir.X ^ dir.Z) & 1) && (((make_stairs == 1) && i != 0) || ((make_stairs == -1) && i != length - 1))) { // rotate face 180 deg if // making stairs backwards int facedir = dir_to_facedir(dir * make_stairs); u32 vi = vm->m_area.index(p.X - dir.X, p.Y - 1, p.Z - dir.Z); if (vm->m_data[vi].getContent() == dp.c_cobble) vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir); vi = vm->m_area.index(p.X, p.Y, p.Z); if (vm->m_data[vi].getContent() == dp.c_cobble) vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir); } } else { makeFill(p + v3s16(-1, -1, -1), dp.holesize + v3s16(2, 2, 2), VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(dp.c_cobble), 0); makeHole(p); } p0 = p; } else { // Can't go here, turn away dir = turn_xz(dir, random.range(0, 1)); make_stairs = -make_stairs; partcount = 0; partlength = random.range(1, length); continue; } partcount++; if (partcount >= partlength) { partcount = 0; dir = random_turn(random, dir); partlength = random.range(1, length); make_stairs = 0; if (random.next() % 2 == 0 && partlength >= 3) make_stairs = random.next() % 2 ? 1 : -1; } } result_place = p0; result_dir = dir; }
void MapgenValleys::makeChunk(BlockMakeData *data) { // Pre-conditions assert(data->vmanip); assert(data->nodedef); assert(data->blockpos_requested.X >= data->blockpos_min.X && data->blockpos_requested.Y >= data->blockpos_min.Y && data->blockpos_requested.Z >= data->blockpos_min.Z); assert(data->blockpos_requested.X <= data->blockpos_max.X && data->blockpos_requested.Y <= data->blockpos_max.Y && data->blockpos_requested.Z <= data->blockpos_max.Z); this->generating = true; this->vm = data->vmanip; this->ndef = data->nodedef; //TimeTaker t("makeChunk"); v3s16 blockpos_min = data->blockpos_min; v3s16 blockpos_max = data->blockpos_max; node_min = blockpos_min * MAP_BLOCKSIZE; node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1); full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE; full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1); blockseed = getBlockSeed2(full_node_min, seed); // Generate noise maps and base terrain height. calculateNoise(); // Generate biome noises. Note this must be executed strictly before // generateTerrain, because generateTerrain depends on intermediate // biome-related noises. m_bgen->calcBiomeNoise(node_min); // Generate base terrain with initial heightmaps s16 stone_surface_max_y = generateTerrain(); // Build biomemap m_bgen->getBiomes(heightmap); // Place biome-specific nodes MgStoneType stone_type = generateBiomes(); // Cave creation. if (flags & MG_CAVES) generateCaves(stone_surface_max_y, large_cave_depth); // Dungeon creation if ((flags & MG_DUNGEONS) && node_max.Y < 50) generateDungeons(stone_surface_max_y, stone_type); // Generate the registered decorations if (flags & MG_DECORATIONS) m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max); // Generate the registered ores m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max); // Sprinkle some dust on top after everything else was generated dustTopNodes(); //TimeTaker tll("liquid_lighting"); updateLiquid(&data->transforming_liquid, full_node_min, full_node_max); if (flags & MG_LIGHT) calcLighting( node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0), full_node_min, full_node_max); //mapgen_profiler->avg("liquid_lighting", tll.stop() / 1000.f); //mapgen_profiler->avg("makeChunk", t.stop() / 1000.f); this->generating = false; }
bool DungeonGen::findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace, v3s16 &result_doordir, v3s16 &result_roomplace) { for (s16 trycount = 0; trycount < 30; trycount++) { v3s16 doorplace; v3s16 doordir; bool r = findPlaceForDoor(doorplace, doordir); if (r == false) continue; v3s16 roomplace; // X east, Z north, Y up #if 1 if (doordir == v3s16(1, 0, 0)) // X+ roomplace = doorplace + v3s16(0, -1, random.range(-roomsize.Z + 2, -2)); if (doordir == v3s16(-1, 0, 0)) // X- roomplace = doorplace + v3s16(-roomsize.X + 1, -1, random.range(-roomsize.Z + 2, -2)); if (doordir == v3s16(0, 0, 1)) // Z+ roomplace = doorplace + v3s16(random.range(-roomsize.X + 2, -2), -1, 0); if (doordir == v3s16(0, 0, -1)) // Z- roomplace = doorplace + v3s16(random.range(-roomsize.X + 2, -2), -1, -roomsize.Z + 1); #endif #if 0 if (doordir == v3s16(1, 0, 0)) // X+ roomplace = doorplace + v3s16(0, -1, -roomsize.Z / 2); if (doordir == v3s16(-1, 0, 0)) // X- roomplace = doorplace + v3s16(-roomsize.X+1,-1,-roomsize.Z / 2); if (doordir == v3s16(0, 0, 1)) // Z+ roomplace = doorplace + v3s16(-roomsize.X / 2, -1, 0); if (doordir == v3s16(0, 0, -1)) // Z- roomplace = doorplace + v3s16(-roomsize.X / 2, -1, -roomsize.Z + 1); #endif // Check fit bool fits = true; for (s16 z = 1; z < roomsize.Z - 1; z++) for (s16 y = 1; y < roomsize.Y - 1; y++) for (s16 x = 1; x < roomsize.X - 1; x++) { v3s16 p = roomplace + v3s16(x, y, z); if (!vm->m_area.contains(p)) { fits = false; break; } if (vm->m_flags[vm->m_area.index(p)] & VMANIP_FLAG_DUNGEON_INSIDE) { fits = false; break; } } if (fits == false) { // Find new place continue; } result_doorplace = doorplace; result_doordir = doordir; result_roomplace = roomplace; return true; } return false; }
void ClientMap::updateDrawList(video::IVideoDriver* driver) { ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG); g_profiler->add("CM::updateDrawList() count", 1); INodeDefManager *nodemgr = m_gamedef->ndef(); for (std::map<v3s16, MapBlock*>::iterator i = m_drawlist.begin(); i != m_drawlist.end(); ++i) { MapBlock *block = i->second; block->refDrop(); } m_drawlist.clear(); v3f camera_position = m_camera_position; v3f camera_direction = m_camera_direction; f32 camera_fov = m_camera_fov; // Use a higher fov to accomodate faster camera movements. // Blocks are cropped better when they are drawn. // Or maybe they aren't? Well whatever. camera_fov *= 1.2; v3s16 cam_pos_nodes = floatToInt(camera_position, BS); v3s16 p_blocks_min; v3s16 p_blocks_max; getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max); // Number of blocks in rendering range u32 blocks_in_range = 0; // Number of blocks occlusion culled u32 blocks_occlusion_culled = 0; // Number of blocks in rendering range but don't have a mesh u32 blocks_in_range_without_mesh = 0; // Blocks that had mesh that would have been drawn according to // rendering range (if max blocks limit didn't kick in) u32 blocks_would_have_drawn = 0; // Blocks that were drawn and had a mesh u32 blocks_drawn = 0; // Blocks which had a corresponding meshbuffer for this pass //u32 blocks_had_pass_meshbuf = 0; // Blocks from which stuff was actually drawn //u32 blocks_without_stuff = 0; // Distance to farthest drawn block float farthest_drawn = 0; for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin(); si != m_sectors.end(); ++si) { MapSector *sector = si->second; v2s16 sp = sector->getPos(); if (m_control.range_all == false) { if (sp.X < p_blocks_min.X || sp.X > p_blocks_max.X || sp.Y < p_blocks_min.Z || sp.Y > p_blocks_max.Z) continue; } MapBlockVect sectorblocks; sector->getBlocks(sectorblocks); /* Loop through blocks in sector */ u32 sector_blocks_drawn = 0; for (MapBlockVect::iterator i = sectorblocks.begin(); i != sectorblocks.end(); ++i) { MapBlock *block = *i; /* Compare block position to camera position, skip if not seen on display */ if (block->mesh != NULL) block->mesh->updateCameraOffset(m_camera_offset); float range = 100000 * BS; if (m_control.range_all == false) range = m_control.wanted_range * BS; float d = 0.0; if (!isBlockInSight(block->getPos(), camera_position, camera_direction, camera_fov, range, &d)) continue; // This is ugly (spherical distance limit?) /*if(m_control.range_all == false && d - 0.5*BS*MAP_BLOCKSIZE > range) continue;*/ blocks_in_range++; /* Ignore if mesh doesn't exist */ { //MutexAutoLock lock(block->mesh_mutex); if (block->mesh == NULL) { blocks_in_range_without_mesh++; continue; } } /* Occlusion culling */ // No occlusion culling when free_move is on and camera is // inside ground bool occlusion_culling_enabled = true; if (g_settings->getBool("free_move")) { MapNode n = getNodeNoEx(cam_pos_nodes); if (n.getContent() == CONTENT_IGNORE || nodemgr->get(n).solidness == 2) occlusion_culling_enabled = false; } v3s16 cpn = block->getPos() * MAP_BLOCKSIZE; cpn += v3s16(MAP_BLOCKSIZE / 2, MAP_BLOCKSIZE / 2, MAP_BLOCKSIZE / 2); float step = BS * 1; float stepfac = 1.1; float startoff = BS * 1; // The occlusion search of 'isOccluded()' must stop short of the target // point by distance 'endoff' (end offset) to not enter the target mapblock. // For the 8 mapblock corners 'endoff' must therefore be the maximum diagonal // of a mapblock, because we must consider all view angles. // sqrt(1^2 + 1^2 + 1^2) = 1.732 float endoff = -BS * MAP_BLOCKSIZE * 1.732050807569; v3s16 spn = cam_pos_nodes; s16 bs2 = MAP_BLOCKSIZE / 2 + 1; // to reduce the likelihood of falsely occluded blocks // require at least two solid blocks // this is a HACK, we should think of a more precise algorithm u32 needed_count = 2; if (occlusion_culling_enabled && // For the central point of the mapblock 'endoff' can be halved isOccluded(this, spn, cpn, step, stepfac, startoff, endoff / 2.0f, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr)) { blocks_occlusion_culled++; continue; } // This block is in range. Reset usage timer. block->resetUsageTimer(); // Limit block count in case of a sudden increase blocks_would_have_drawn++; if (blocks_drawn >= m_control.wanted_max_blocks && !m_control.range_all && d > m_control.wanted_range * BS) continue; // Add to set block->refGrab(); m_drawlist[block->getPos()] = block; sector_blocks_drawn++; blocks_drawn++; if (d / BS > farthest_drawn) farthest_drawn = d / BS; } // foreach sectorblocks if (sector_blocks_drawn != 0) m_last_drawn_sectors.insert(sp); } m_control.blocks_would_have_drawn = blocks_would_have_drawn; m_control.blocks_drawn = blocks_drawn; m_control.farthest_drawn = farthest_drawn; g_profiler->avg("CM: blocks in range", blocks_in_range); g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled); if (blocks_in_range != 0) g_profiler->avg("CM: blocks in range without mesh (frac)", (float)blocks_in_range_without_mesh / blocks_in_range); g_profiler->avg("CM: blocks drawn", blocks_drawn); g_profiler->avg("CM: farthest drawn", farthest_drawn); g_profiler->avg("CM: wanted max blocks", m_control.wanted_max_blocks); }
PathFinder::PathFinder() : m_searchdistance(0), m_maxdrop(0), m_maxjump(0), m_start(0,0,0), m_destination(0,0,0), m_limits(), m_env(0) { m_adjacency_4.push_back(v3s16(-1, 0, 0)); m_adjacency_4.push_back(v3s16(1, 0, 0)); m_adjacency_4.push_back(v3s16(0, 0, 1)); m_adjacency_4.push_back(v3s16(0, 0, -1)); m_adjacency_4_cost.push_back(1); m_adjacency_4_cost.push_back(1); m_adjacency_4_cost.push_back(1); m_adjacency_4_cost.push_back(1); m_adjacency_8.push_back(v3s16(-1, 0, 0)); m_adjacency_8.push_back(v3s16(1, 0, 0)); m_adjacency_8.push_back(v3s16(0, 0, 1)); m_adjacency_8.push_back(v3s16(0, 0, -1)); m_adjacency_8.push_back(v3s16(-1, 0, -1)); m_adjacency_8.push_back(v3s16(1, 0, -1)); m_adjacency_8.push_back(v3s16(-1, 0, 1)); m_adjacency_8.push_back(v3s16(1, 0, 1)); m_adjacency_8_cost.push_back(1); m_adjacency_8_cost.push_back(1); m_adjacency_8_cost.push_back(1); m_adjacency_8_cost.push_back(1); m_adjacency_8_cost.push_back(1); m_adjacency_8_cost.push_back(1); m_adjacency_8_cost.push_back(1); m_adjacency_8_cost.push_back(1); }
collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, f32 pos_max_d, const core::aabbox3d<f32> &box_0, f32 dtime, v3f &pos_f, v3f &speed_f) { collisionMoveResult result; v3f oldpos_f = pos_f; v3s16 oldpos_i = floatToInt(oldpos_f, BS); /* Calculate new position */ pos_f += speed_f * dtime; /* Collision detection */ // position in nodes v3s16 pos_i = floatToInt(pos_f, BS); /* Collision uncertainty radius Make it a bit larger than the maximum distance of movement */ f32 d = pos_max_d * 1.1; // A fairly large value in here makes moving smoother //f32 d = 0.15*BS; // This should always apply, otherwise there are glitches assert(d > pos_max_d); /* Calculate collision box */ core::aabbox3d<f32> box = box_0; box.MaxEdge += pos_f; box.MinEdge += pos_f; core::aabbox3d<f32> oldbox = box_0; oldbox.MaxEdge += oldpos_f; oldbox.MinEdge += oldpos_f; /* If the object lies on a walkable node, this is set to true. */ result.touching_ground = false; /* Go through every node around the object */ s16 min_x = (box_0.MinEdge.X / BS) - 2; s16 min_y = (box_0.MinEdge.Y / BS) - 2; s16 min_z = (box_0.MinEdge.Z / BS) - 2; s16 max_x = (box_0.MaxEdge.X / BS) + 1; s16 max_y = (box_0.MaxEdge.Y / BS) + 1; s16 max_z = (box_0.MaxEdge.Z / BS) + 1; for(s16 y = oldpos_i.Y + min_y; y <= oldpos_i.Y + max_y; y++) for(s16 z = oldpos_i.Z + min_z; z <= oldpos_i.Z + max_z; z++) for(s16 x = oldpos_i.X + min_x; x <= oldpos_i.X + max_x; x++) { try{ // Object collides into walkable nodes MapNode n = map->getNode(v3s16(x,y,z)); if(gamedef->getNodeDefManager()->get(n).walkable == false) continue; } catch(InvalidPositionException &e) { // Doing nothing here will block the object from // walking over map borders } core::aabbox3d<f32> nodebox = getNodeBox(v3s16(x,y,z), BS); /* See if the object is touching ground. Object touches ground if object's minimum Y is near node's maximum Y and object's X-Z-area overlaps with the node's X-Z-area. Use 0.15*BS so that it is easier to get on a node. */ if( //fabs(nodebox.MaxEdge.Y-box.MinEdge.Y) < d fabs(nodebox.MaxEdge.Y-box.MinEdge.Y) < 0.15*BS && nodebox.MaxEdge.X-d > box.MinEdge.X && nodebox.MinEdge.X+d < box.MaxEdge.X && nodebox.MaxEdge.Z-d > box.MinEdge.Z && nodebox.MinEdge.Z+d < box.MaxEdge.Z ){ result.touching_ground = true; } // If object doesn't intersect with node, ignore node. if(box.intersectsWithBox(nodebox) == false) continue; /* Go through every axis */ v3f dirs[3] = { v3f(0,0,1), // back-front v3f(0,1,0), // top-bottom v3f(1,0,0), // right-left }; for(u16 i=0; i<3; i++) { /* Calculate values along the axis */ f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[i]); f32 nodemin = nodebox.MinEdge.dotProduct(dirs[i]); f32 objectmax = box.MaxEdge.dotProduct(dirs[i]); f32 objectmin = box.MinEdge.dotProduct(dirs[i]); f32 objectmax_old = oldbox.MaxEdge.dotProduct(dirs[i]); f32 objectmin_old = oldbox.MinEdge.dotProduct(dirs[i]); /* Check collision for the axis. Collision happens when object is going through a surface. */ bool negative_axis_collides = (nodemax > objectmin && nodemax <= objectmin_old + d && speed_f.dotProduct(dirs[i]) < 0); bool positive_axis_collides = (nodemin < objectmax && nodemin >= objectmax_old - d && speed_f.dotProduct(dirs[i]) > 0); bool main_axis_collides = negative_axis_collides || positive_axis_collides; /* Check overlap of object and node in other axes */ bool other_axes_overlap = true; for(u16 j=0; j<3; j++) { if(j == i) continue; f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[j]); f32 nodemin = nodebox.MinEdge.dotProduct(dirs[j]); f32 objectmax = box.MaxEdge.dotProduct(dirs[j]); f32 objectmin = box.MinEdge.dotProduct(dirs[j]); if(!(nodemax - d > objectmin && nodemin + d < objectmax)) { other_axes_overlap = false; break; } } /* If this is a collision, revert the pos_f in the main direction. */ if(other_axes_overlap && main_axis_collides) { speed_f -= speed_f.dotProduct(dirs[i]) * dirs[i]; pos_f -= pos_f.dotProduct(dirs[i]) * dirs[i]; pos_f += oldpos_f.dotProduct(dirs[i]) * dirs[i]; result.collides = true; } } } // xyz return result; }
inline PathGridnode &Pathfinder::getIdxElem(s16 x, s16 y, s16 z) { return m_nodes_container->access(v3s16(x,y,z)); }