void ViewingCore::pickCenter( const double ndcX, const double ndcY ) { // Preserve the view direction. const osg::Vec3d lastPosition = getEyePosition(); osg::Matrixd p = computeProjection(); osg::Vec4d ccFarPoint( ndcX, ndcY, 1., 1. ); if( !getOrtho() ) { // Not ortho, so w != 1.0. Multiply by the far plane distance. // This yields a value in clip coords. double fovy, aspect, zNear, zFar; p.getPerspective( fovy, aspect, zNear, zFar ); ccFarPoint *= zFar; } // Get inverse view & proj matrices to back-transform the clip coord point. osg::Matrixd v = getMatrix(); p.invert( p ); osg::Vec4d wc = ccFarPoint * p * v; osg::Vec3d farPoint( wc.x(), wc.y(), wc.z() ); if( !( intersect( _viewCenter, farPoint ) ) ) osg::notify( osg::WARN ) << "ViewingCore::pickCenter: No intersections." << std::endl; _viewDistance = ( lastPosition - _viewCenter ).length(); }
void LookAtTransformationElement::calcMatrix(Matrix &result) const { MatrixLookAt(result, getEyePosition(), getLookAtPosition(), getUpDirection() ); }
osg::Matrixd ViewingCore::computeProjection() const { if( !( _scene.valid() ) ) { osg::notify( osg::WARN ) << "ViewingCore::computeProjection: _scene == NULL." << std::endl; return( osg::Matrixd::identity() ); } // TBD do we really want eyeToCenter to be a vector // to the *bound* center, or to the *view* center? const osg::BoundingSphere& bs = _scene->getBound(); const osg::Vec3d eyeToCenter( bs._center - getEyePosition() ); if( _ortho ) { double zNear = eyeToCenter.length() - bs._radius; double zFar = eyeToCenter.length() + bs._radius; const double xRange = _aspect * ( _orthoTop - _orthoBottom ); const double right = xRange * .5; return( osg::Matrixd::ortho( -right, right, _orthoBottom, _orthoTop, zNear, zFar ) ); } else { double zNear = eyeToCenter.length() - bs._radius; double zFar = zNear + ( bs._radius * 2. ); if( zNear < 0. ) { zNear = zFar / 2000.; // Default z ratio. } return( osg::Matrixd::perspective( _fovy, _aspect, zNear, zFar ) ); } }
osg::Vec3d ViewingCore::findDeltaOnPanPlane(double ndcX1, double ndcY1, double ndcX2, double ndcY2) { // Get the view volume far plane value, and the distance from // the near to far plane. double zNear, zFar; osg::Matrixd p = computeProjection(); if( getOrtho() ) { double l, r, b, t; p.getOrtho( l, r, b, t, zNear, zFar ); } else { double fovy, aspect; p.getPerspective( fovy, aspect, zNear, zFar ); } const double distance = zFar - zNear; // Create two points, both in NDC space, and lying on the far plane at the back // of the view volume. One is the xy origin, the other with the passed xy parameters. osg::Vec4d farPoint0 = osg::Vec4d( ndcX1, ndcY1, 1., 1. ); osg::Vec4d farPoint1 = osg::Vec4d( ndcX2, ndcY2, 1., 1. ); if( !getOrtho() ) { // Not ortho, so w != 1.0. Multiply by the far plane distance. // This yields values in clip coordinates. farPoint0 *= zFar; farPoint1 *= zFar; } // Get inverse view & proj matrices to back-transform the // two clip coord far points into world space. osg::Matrixd v = getMatrix(); p.invert( p ); osg::Vec4d wc0 = farPoint0 * p * v; osg::Vec4d wc1 = farPoint1 * p * v; // Intersect the two world coord points with the pan plane. osg::Vec3d result0, result1; osg::Vec3d p1( wc0.x(), wc0.y(), wc0.z() ); osg::Vec3d p0 = getOrtho() ? p1 - ( _viewDir * distance ) : getEyePosition(); intersectPlaneRay( result0, _panPlane, p0, p1 ); p1 = osg::Vec3d( wc1.x(), wc1.y(), wc1.z() ); p0 = getOrtho() ? p1 - ( _viewDir * distance ) : getEyePosition(); intersectPlaneRay( result1, _panPlane, p0, p1 ); // Subtract the two plane intersection points to get the delta world coord // motion return return result1 - result0; }
void Observer::_updateEyes() { const Matrix4f& head = getHeadMatrix(); for( size_t i = 0; i < NUM_EYES; ++i ) _eyeWorld[ i ] = head * getEyePosition( Eye( 1 << i )); LBVERB << "Eye position: " << _eyeWorld[ fabric::EYE_CYCLOP_BIT ] << std::endl; }
void ViewingCore::pan( const double ndcX, const double ndcY ) { // Get the view volume far plane value, and the distance from // the near to far plane. double zNear, zFar; osg::Matrixd p; getZNearZFarProj(zNear, zFar, p); const double distance = zFar - zNear; // Create two points, both in NDC space, and lying on the far plane at the back // of the view volume. One is the xy origin, the other with the passed xy parameters. osg::Vec4d farPoint0 = osg::Vec4d( 0., 0., 1., 1. ); osg::Vec4d farPoint1 = osg::Vec4d( ndcX, ndcY, 1., 1. ); if( !getOrtho() ) { // Not ortho, so w != 1.0. Multiply by the far plane distance. // This yields values in clip coordinates. farPoint0 *= zFar; farPoint1 *= zFar; } // Get inverse view & proj matrices to back-transform the // two clip coord far points into world space. osg::Matrixd v = getMatrix(); p.invert( p ); osg::Vec4d wc0 = farPoint0 * p * v; osg::Vec4d wc1 = farPoint1 * p * v; // Intersect the two world coord points with the pan plane. osg::Vec3d result0, result1; osg::Vec3d p1( wc0.x(), wc0.y(), wc0.z() ); osg::Vec3d p0 = getOrtho() ? p1 - ( _viewDir * distance ) : getEyePosition(); intersectPlaneRay( result0, _panPlane, p0, p1 ); p1 = osg::Vec3d( wc1.x(), wc1.y(), wc1.z() ); p0 = getOrtho() ? p1 - ( _viewDir * distance ) : getEyePosition(); intersectPlaneRay( result1, _panPlane, p0, p1 ); // Subtract the two plane intersection points to get the delta world coord // motion and move the view center accordingly. osg::Vec3d delta = result1 - result0; osg::notify( osg::DEBUG_FP ) << " delta " << delta << std::endl; _viewCenter -= delta; }
// the bot killed pVictim void CHLDMBot :: killed ( edict_t *pVictim, char *weapon ) { extern ConVar bot_beliefmulti; CBot::killed(pVictim,weapon); // update belief around this waypoint if ( pVictim && CBotGlobals::entityIsValid(pVictim) ) m_pNavigator->belief(CBotGlobals::entityOrigin(pVictim),getEyePosition(),bot_beliefmulti.GetFloat(),distanceFrom(pVictim),BELIEF_SAFETY); }
bool ViewingCore::getStartPoint(osg::Vec3d & startPoint, const osg::Vec3d farPoint, const double ndcX, const double ndcY) { if( !( _scene.valid() ) ) { osg::notify( osg::WARN ) << "ViewingCore::intersect: _scene == NULL." << std::endl; return( false ); } const osg::BoundingSphere& bs = _scene->getBound(); const double distance = _viewDistance + bs._radius; startPoint = getOrtho() ? farPoint - ( _viewDir * distance * 2. ) : getEyePosition(); return true; }
void FirstPersonCamera::use() { // Obtain the reference point determined by the angles float eyeX, eyeY, eyeZ; getEyePosition(eyeX, eyeY, eyeZ); float centerX = eyeX + cos(degreesToRadians(xzAngle))*cos(degreesToRadians(yAngle)); float centerY = eyeY + sin(degreesToRadians(yAngle)); float centerZ = eyeZ + sin(degreesToRadians(xzAngle))*cos(degreesToRadians(yAngle)); setReferencePoint(centerX, centerY, centerZ); // Use the camera Camera::use(); }
osg::Matrixd ViewingCore::getMatrix() const { const osg::Vec3d& d = _viewDir; const osg::Vec3d& u = _viewUp; osg::Vec3d r = d ^ u; const osg::Vec3d p = getEyePosition(); osg::Matrixd m = osg::Matrixd( r[0], r[1], r[2], 0.0, u[0], u[1], u[2], 0.0, -d[0], -d[1], -d[2], 0.0, p[0], p[1], p[2], 1.0 ); return( m ); }
/* Rotating */ void FreeCamera::rotate(float angle) { float eyeX, eyeY, eyeZ; float centerX, centerY, centerZ; getEyePosition(eyeX, eyeY, eyeZ); getReferencePoint(centerX, centerY, centerZ); float distance = distance2D(eyeX, eyeZ, centerX, centerZ); centerX += distance*(cos(degreesToRadians(xzAngle + angle)) - cos(degreesToRadians(xzAngle))); centerZ += distance*(sin(degreesToRadians(xzAngle + angle)) - sin(degreesToRadians(xzAngle))); xzAngle += angle; setReferencePoint(centerX, centerY, centerZ); }
void OpenGLCamera::doFocus() { float m[16]; Vector3d up_unit(getUp()[0], getUp()[1], getUp()[2]); Vector3d eyePosition(getEyePosition()[0], getEyePosition()[1], getEyePosition()[2]); Vector3d center(getCenter()[0], getCenter()[1], getCenter()[2]); Vector3d f_unit = center - eyePosition; f_unit.Normalize(); up_unit.Normalize(); Vector3d s = CrossProduct(f_unit, up_unit); Vector3d u = CrossProduct(s, f_unit); m[0] = s[0]; m[1] = u[0]; m[2] = -f_unit[0]; m[3] = 0.f; m[4] = s[1]; m[5] = u[1]; m[6] = -f_unit[1]; m[7] = 0.f; m[8] = s[2]; m[9] = u[2]; m[10] = -f_unit[2]; m[11] = 0.f; m[12] = 0.f; m[13] = 0.f; m[14] = 0.f; m[15] = 1.f; glMultMatrixf(m); glTranslatef(-eyePosition[0], -eyePosition[1], -eyePosition[2]); }
/* Panning */ void FreeCamera::pan(float panX, float panZ) { float eyeX, eyeY, eyeZ; float centerX, centerY, centerZ; getEyePosition(eyeX, eyeY, eyeZ); getReferencePoint(centerX, centerY, centerZ); float deltaX = panX*cos(degreesToRadians(xzAngle)) + panZ*cos(degreesToRadians(xzAngle + 90.0f)); float deltaZ = panX*sin(degreesToRadians(xzAngle)) + panZ*sin(degreesToRadians(xzAngle + 90.0f)); eyeX += deltaX; centerX += deltaX; eyeZ += deltaZ; centerZ += deltaZ; setEyePosition(eyeX, eyeY, eyeZ); setReferencePoint(centerX, centerY, centerZ); }
// the bot was killed by pKiller void CHLDMBot :: died ( edict_t *pKiller, const char *pszWeapon ) { extern ConVar bot_beliefmulti; // re-initialize stuff per life CBot::died(pKiller, pszWeapon); //if ( randomInt(0,1) ) m_pButtons->attack(); // respawn if ( pKiller ) { if ( CBotGlobals::entityIsValid(pKiller) ) { m_pNavigator->belief(CBotGlobals::entityOrigin(pKiller),getEyePosition(),bot_beliefmulti.GetFloat(),distanceFrom(pKiller),BELIEF_DANGER); } } }
bool ViewingCore::intersect( osg::Vec3d& result, const osg::Vec3d& farPoint ) { if( !( _scene.valid() ) ) { osg::notify( osg::WARN ) << "ViewingCore::intersect: _scene == NULL." << std::endl; return( false ); } const osg::BoundingSphere& bs = _scene->getBound(); const double distance = _viewDistance + bs._radius; osg::Vec3d startPoint = getOrtho() ? farPoint - ( _viewDir * distance * 2. ) : getEyePosition(); osgUtil::LineSegmentIntersector* intersector = new osgUtil::LineSegmentIntersector( startPoint, farPoint ); osgUtil::IntersectionVisitor intersectVisitor( intersector, NULL ); _scene->accept( intersectVisitor ); osgUtil::LineSegmentIntersector::Intersections& intersections = intersector->getIntersections(); if( intersections.empty() ) return( false ); const osgUtil::LineSegmentIntersector::Intersection& intersection = *( intersections.begin() ); result = intersection.getWorldIntersectPoint(); return( true ); }
void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, std::vector<CollisionInfo> *collision_info) { if (!collision_info || collision_info->empty()) { // Node below the feet, update each ClientEnvironment::step() m_standing_node = floatToInt(m_position, BS) - v3s16(0, 1, 0); } // Temporary option for old move code if (!physics_override_new_move) { old_move(dtime, env, pos_max_d, collision_info); return; } Map *map = &env->getMap(); INodeDefManager *nodemgr = m_client->ndef(); v3f position = getPosition(); // Copy parent position if local player is attached if (isAttached) { setPosition(overridePosition); return; } // Skip collision detection if noclip mode is used bool fly_allowed = m_client->checkLocalPrivilege("fly"); bool noclip = m_client->checkLocalPrivilege("noclip") && g_settings->getBool("noclip"); bool free_move = g_settings->getBool("free_move") && fly_allowed; if (noclip && free_move) { position += m_speed * dtime; setPosition(position); 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); // Player object property step height is multiplied by BS in // /src/script/common/c_content.cpp and /src/content_sao.cpp float player_stepheight = (m_cao == nullptr) ? 0.0f : (touching_ground ? m_cao->getStepHeight() : (0.2f * BS)); // TODO this is a problematic hack. // Use a better implementation for autojump, or apply a custom stepheight // to all players, as this currently creates unintended special movement // abilities and advantages for Android players on a server. #ifdef __ANDROID__ if (touching_ground) player_stepheight += (0.6f * BS); #endif v3f accel_f = v3f(0,0,0); collisionMoveResult result = collisionMoveSimple(env, m_client, pos_max_d, m_collisionbox, player_stepheight, dtime, &position, &m_speed, accel_f); bool could_sneak = control.sneak && !free_move && !in_liquid && !is_climbing && physics_override_sneak; // Add new collisions to the vector if (collision_info && !free_move) { v3f diff = intToFloat(m_standing_node, BS) - position; f32 distance = diff.getLength(); // Force update each ClientEnvironment::step() bool is_first = collision_info->empty(); for (const auto &colinfo : result.collisions) { collision_info->push_back(colinfo); if (colinfo.type != COLLISION_NODE || colinfo.new_speed.Y != 0 || (could_sneak && m_sneak_node_exists)) continue; diff = intToFloat(colinfo.node_p, BS) - position; // Find nearest colliding node f32 len = diff.getLength(); if (is_first || len < distance) { m_standing_node = colinfo.node_p; distance = len; } } } /* 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 sneak_can_jump = false; // Max. distance (X, Z) over border for sneaking determined by collision box // * 0.49 to keep the center just barely on the node v3f sneak_max = m_collisionbox.getExtent() * 0.49; if (m_sneak_ladder_detected) { // restore legacy behaviour (this makes the m_speed.Y hack necessary) sneak_max = v3f(0.4 * BS, 0, 0.4 * BS); } /* If sneaking, keep on top of last walked node and don't fall off */ if (could_sneak && m_sneak_node_exists) { const v3f sn_f = intToFloat(m_sneak_node, BS); const v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge; const v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge; const v3f old_pos = position; const v3f old_speed = m_speed; f32 y_diff = bmax.Y - position.Y; m_standing_node = m_sneak_node; // (BS * 0.6f) is the basic stepheight while standing on ground if (y_diff < BS * 0.6f) { // Only center player when they're on the node position.X = rangelim(position.X, bmin.X - sneak_max.X, bmax.X + sneak_max.X); position.Z = rangelim(position.Z, bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z); if (position.X != old_pos.X) m_speed.X = 0; if (position.Z != old_pos.Z) m_speed.Z = 0; } if (y_diff > 0 && m_speed.Y < 0 && (physics_override_sneak_glitch || y_diff < BS * 0.6f)) { // Move player to the maximal height when falling or when // the ledge is climbed on the next step. position.Y = bmax.Y; m_speed.Y = 0; } // Allow jumping on node edges while sneaking if (m_speed.Y == 0 || m_sneak_ladder_detected) sneak_can_jump = true; if (collision_info && m_speed.Y - old_speed.Y > BS) { // Collide with sneak node, report fall damage CollisionInfo sn_info; sn_info.node_p = m_sneak_node; sn_info.old_speed = old_speed; sn_info.new_speed = m_speed; collision_info->push_back(sn_info); } } /* Find the next sneak node if necessary */ bool new_sneak_node_exists = false; if (could_sneak) new_sneak_node_exists = updateSneakNode(map, position, sneak_max); /* Set new position but keep sneak node set */ setPosition(position); m_sneak_node_exists = new_sneak_node_exists; /* Report collisions */ if(!result.standing_on_object && !touching_ground_was && touching_ground) { MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround"); m_client->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; } } } /* Check properties of the node on which the player is standing */ const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(m_standing_node)); // Determine if jumping is possible m_can_jump = (touching_ground && !in_liquid && !is_climbing) || sneak_can_jump; if (itemgroup_get(f.groups, "disable_jump")) m_can_jump = false; // Jump key pressed while jumping off from a bouncy block if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") && m_speed.Y >= -0.5 * BS) { float jumpspeed = movement_speed_jump * physics_override_jump; if (m_speed.Y > 1) { // Reduce boost when speed already is high m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 )); } else { m_speed.Y += jumpspeed; } setSpeed(m_speed); m_can_jump = false; } }
void PlayerSAO::step(float dtime, bool send_recommended) { if (m_drowning_interval.step(dtime, 2.0f)) { // Get nose/mouth position, approximate with eye position v3s16 p = floatToInt(getEyePosition(), BS); MapNode n = m_env->getMap().getNodeNoEx(p); const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); // If node generates drown if (c.drowning > 0 && m_hp > 0) { if (m_breath > 0) setBreath(m_breath - 1); // No more breath, damage player if (m_breath == 0) { PlayerHPChangeReason reason(PlayerHPChangeReason::DROWNING); setHP(m_hp - c.drowning, reason); m_env->getGameDef()->SendPlayerHPOrDie(this, reason); } } } if (m_breathing_interval.step(dtime, 0.5f)) { // Get nose/mouth position, approximate with eye position v3s16 p = floatToInt(getEyePosition(), BS); MapNode n = m_env->getMap().getNodeNoEx(p); const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); // If player is alive & no drowning, breathe if (m_hp > 0 && m_breath < m_prop.breath_max && c.drowning == 0) setBreath(m_breath + 1); } if (m_node_hurt_interval.step(dtime, 1.0f)) { u32 damage_per_second = 0; // Lowest and highest damage points are 0.1 within collisionbox float dam_top = m_prop.collisionbox.MaxEdge.Y - 0.1f; // Sequence of damage points, starting 0.1 above feet and progressing // upwards in 1 node intervals, stopping below top damage point. for (float dam_height = 0.1f; dam_height < dam_top; dam_height++) { v3s16 p = floatToInt(m_base_position + v3f(0.0f, dam_height * BS, 0.0f), BS); MapNode n = m_env->getMap().getNodeNoEx(p); damage_per_second = std::max(damage_per_second, m_env->getGameDef()->ndef()->get(n).damage_per_second); } // Top damage point v3s16 ptop = floatToInt(m_base_position + v3f(0.0f, dam_top * BS, 0.0f), BS); MapNode ntop = m_env->getMap().getNodeNoEx(ptop); damage_per_second = std::max(damage_per_second, m_env->getGameDef()->ndef()->get(ntop).damage_per_second); if (damage_per_second != 0 && m_hp > 0) { s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second); PlayerHPChangeReason reason(PlayerHPChangeReason::NODE_DAMAGE); setHP(newhp, reason); m_env->getGameDef()->SendPlayerHPOrDie(this, reason); } } if (!m_properties_sent) { m_properties_sent = true; std::string str = getPropertyPacket(); // create message and add to list ActiveObjectMessage aom(getId(), true, str); m_messages_out.push(aom); } // If attached, check that our parent is still there. If it isn't, detach. if (m_attachment_parent_id && !isAttached()) { m_attachment_parent_id = 0; m_attachment_bone = ""; m_attachment_position = v3f(0.0f, 0.0f, 0.0f); m_attachment_rotation = v3f(0.0f, 0.0f, 0.0f); setBasePosition(m_last_good_position); m_env->getGameDef()->SendMovePlayer(m_peer_id); } //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl; // Set lag pool maximums based on estimated lag const float LAG_POOL_MIN = 5.0f; float lag_pool_max = m_env->getMaxLagEstimate() * 2.0f; if(lag_pool_max < LAG_POOL_MIN) lag_pool_max = LAG_POOL_MIN; m_dig_pool.setMax(lag_pool_max); m_move_pool.setMax(lag_pool_max); // Increment cheat prevention timers m_dig_pool.add(dtime); m_move_pool.add(dtime); m_time_from_last_teleport += dtime; m_time_from_last_punch += dtime; m_nocheat_dig_time += dtime; // Each frame, parent position is copied if the object is attached, // otherwise it's calculated normally. // If the object gets detached this comes into effect automatically from // the last known origin. if (isAttached()) { v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition(); m_last_good_position = pos; setBasePosition(pos); } if (!send_recommended) return; // If the object is attached client-side, don't waste bandwidth sending its // position to clients. if (m_position_not_sent && !isAttached()) { m_position_not_sent = false; float update_interval = m_env->getSendRecommendedInterval(); v3f pos; if (isAttached()) // Just in case we ever do send attachment position too pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition(); else pos = m_base_position; std::string str = gob_cmd_update_position( pos, v3f(0.0f, 0.0f, 0.0f), v3f(0.0f, 0.0f, 0.0f), m_yaw, true, false, update_interval ); // create message and add to list ActiveObjectMessage aom(getId(), false, str); m_messages_out.push(aom); } if (!m_armor_groups_sent) { m_armor_groups_sent = true; std::string str = gob_cmd_update_armor_groups( m_armor_groups); // create message and add to list ActiveObjectMessage aom(getId(), true, str); m_messages_out.push(aom); } if (!m_physics_override_sent) { m_physics_override_sent = true; std::string str = gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak, m_physics_override_sneak_glitch, m_physics_override_new_move); // create message and add to list ActiveObjectMessage aom(getId(), true, str); m_messages_out.push(aom); } if (!m_animation_sent) { m_animation_sent = true; std::string str = gob_cmd_update_animation( m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop); // create message and add to list ActiveObjectMessage aom(getId(), true, str); m_messages_out.push(aom); } if (!m_bone_position_sent) { m_bone_position_sent = true; for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y); // create message and add to list ActiveObjectMessage aom(getId(), true, str); m_messages_out.push(aom); } } if (!m_attachment_sent) { m_attachment_sent = true; std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation); // create message and add to list ActiveObjectMessage aom(getId(), true, str); m_messages_out.push(aom); } }
void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, std::list<CollisionInfo> *collision_info) { INodeDefManager *nodemgr = m_gamedef->ndef(); v3f position = getPosition(); v3f old_speed = m_speed; // Copy parent position if local player is attached if(isAttached) { setPosition(overridePosition); 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); return; } /* Collision detection */ /* Check if player is in liquid (the oscillating value) */ try{ // If in liquid, the threshold of coming out is at higher y if(in_liquid) { v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS); in_liquid = nodemgr->get(map.getNode(pp).getContent()).isLiquid(); liquid_viscosity = nodemgr->get(map.getNode(pp).getContent()).liquid_viscosity; } // If not in liquid, the threshold of going in is at lower y else { v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS); in_liquid = nodemgr->get(map.getNode(pp).getContent()).isLiquid(); liquid_viscosity = nodemgr->get(map.getNode(pp).getContent()).liquid_viscosity; } } catch(InvalidPositionException &e) { in_liquid = false; } /* Check if player is in liquid (the stable value) */ try{ v3s16 pp = floatToInt(position + v3f(0,0,0), BS); in_liquid_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid(); } catch(InvalidPositionException &e) { in_liquid_stable = false; } /* Check if player is climbing */ try { v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS); v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS); is_climbing = ((nodemgr->get(map.getNode(pp).getContent()).climbable || nodemgr->get(map.getNode(pp2).getContent()).climbable) && !free_move); } catch(InvalidPositionException &e) { is_climbing = false; } /* 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); float player_radius = BS*0.30; float player_height = BS*1.55; // 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) { 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; } } } /* Calculate player collision box (new and old) */ core::aabbox3d<f32> playerbox( -player_radius, 0.0, -player_radius, player_radius, player_height, player_radius ); float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2); v3f accel_f = v3f(0,0,0); collisionMoveResult result = collisionMoveSimple(&map, m_gamedef, pos_max_d, playerbox, 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) { 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; try{ // The node to be sneaked on has to be walkable if(nodemgr->get(map.getNode(p)).walkable == false) continue; // And the node above it has to be nonwalkable if(nodemgr->get(map.getNode(p+v3s16(0,1,0))).walkable == true) continue; } catch(InvalidPositionException &e) { 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); } { 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 ViewingCore::rotate( osg::Vec2d start, osg::Vec2d dir ) { if( dir.length2() == 0. ) // No motion return; if( _mode == FIRST_PERSON ) { // Position is constant in 1st person view. Obtain it (for later use) // *before* we alter the _viewDir. const osg::Vec3d position = getEyePosition(); // Compute rotation matrix. osg::Vec3d cross = _viewDir ^ _viewUp; osg::Matrix m = osg::Matrix::rotate( dir[ 0 ], _viewUp ) * osg::Matrix::rotate( -dir[ 1 ], cross ); // Re-orient the basis. _viewDir = _viewDir * m; _viewUp = _viewUp * m; // Orthonormalize. cross = _viewDir ^ _viewUp; _viewUp = cross ^ _viewDir; _viewDir.normalize(); _viewUp.normalize(); // Compute the new view center. _viewCenter = position + ( _viewDir * _viewDistance ); } else { // THIRD_PERSON const osg::Matrixd orientMat = getOrientationMatrix(); // Take the spin direction 'dir' and rotate it 90 degrees // to get our base axis (still in the window plane). // Simultaneously convert to current view space. osg::Vec2d screenAxis( -dir[ 1 ], dir[ 0 ] ); const osg::Vec3d baseAxis = osg::Vec3d( screenAxis[ 0 ], screenAxis[ 1 ], 0. ) * orientMat; osg::Vec3d dir3 = osg::Vec3d( dir[ 0 ], dir[ 1 ], 0. ) * orientMat; dir3.normalize(); // The distance from center, along with the roll sensitivity, // tells us how much to rotate the baseAxis (ballTouchAngle) to get // the actual ballAxis. const double distance = start.length(); const double rotationDir( ( screenAxis * start > 0. ) ? -1. : 1. ); const double ballTouchAngle = rotationDir * _trackballRollSensitivity * distance; osg::Vec3d ballAxis = baseAxis * osg::Matrixd::rotate( ballTouchAngle, dir3 ); ballAxis.normalize(); osg::Matrixd m = osg::Matrixd::rotate( -( dir.length() ), ballAxis ); // Re-orient the basis. _viewDir = _viewDir * m; _viewUp = _viewUp * m; // Orthonormalize. osg::Vec3d cross = _viewDir ^ _viewUp; _viewUp = cross ^ _viewDir; _viewDir.normalize(); _viewUp.normalize(); } }
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) { // Move up if necessary f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax; if (position.Y < new_y) position.Y = new_y; /* Collision seems broken, since player is sinking when sneaking over the edges of current sneaking_node. TODO (when fixed): Set Y-speed only to 0 when position.Y < new_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. */ f32 position_y_mod = 0.05 * BS; if (m_sneak_node_bb_ymax > 0) position_y_mod = m_sneak_node_bb_ymax - position_y_mod; v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 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) { m_sneak_node_bb_ymax = 0; v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 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 (sneak_node_found) { f32 cb_max = 0; MapNode n = map->getNodeNoEx(m_sneak_node); std::vector<aabb3f> nodeboxes = n.getCollisionBoxes(nodemgr); for (std::vector<aabb3f>::iterator it = nodeboxes.begin(); it != nodeboxes.end(); ++it) { aabb3f box = *it; if (box.MaxEdge.Y > cb_max) cb_max = box.MaxEdge.Y; } m_sneak_node_bb_ymax = cb_max; } /* 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 */ // 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(!result.standing_on_object && !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; // Jump key pressed while jumping off from a bouncy block if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") && m_speed.Y >= -0.5 * BS) { float jumpspeed = movement_speed_jump * physics_override_jump; if (m_speed.Y > 1) { // Reduce boost when speed already is high m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 )); } else { m_speed.Y += jumpspeed; } setSpeed(m_speed); m_can_jump = false; } }
void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, core::list<CollisionInfo> *collision_info) { INodeDefManager *nodemgr = m_gamedef->ndef(); v3f position = getPosition(); v3f oldpos = position; v3s16 oldpos_i = floatToInt(oldpos, BS); v3f old_speed = m_speed; /*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<"," <<oldpos_i.Z<<")"<<std::endl;*/ /* Calculate new position */ position += m_speed * dtime; // Skip collision detection if a special movement mode is used bool fly_allowed = m_gamedef->checkLocalPrivilege("fly"); bool free_move = fly_allowed && g_settings->getBool("free_move"); if(free_move) { setPosition(position); return; } /* Collision detection */ // Player position in nodes v3s16 pos_i = floatToInt(position, BS); /* Check if player is in water (the oscillating value) */ try{ // If in water, the threshold of coming out is at higher y if(in_water) { v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS); in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid(); } // If not in water, the threshold of going in is at lower y else { v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS); in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid(); } } catch(InvalidPositionException &e) { in_water = false; } /* Check if player is in water (the stable value) */ try{ v3s16 pp = floatToInt(position + v3f(0,0,0), BS); in_water_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid(); } catch(InvalidPositionException &e) { in_water_stable = false; } /* Check if player is climbing */ try { v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS); v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS); is_climbing = ((nodemgr->get(map.getNode(pp).getContent()).climbable || nodemgr->get(map.getNode(pp2).getContent()).climbable) && !free_move); } catch(InvalidPositionException &e) { is_climbing = false; } /* 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); float player_radius = BS*0.30; float player_height = BS*1.55; // Maximum distance over border for sneaking f32 sneak_max = BS*0.4; /* If sneaking, player has larger collision radius to keep from falling */ /*if(control.sneak) player_radius = sneak_max + d*1.1;*/ /* If sneaking, keep in range from the last walked node and don't fall off from it */ if(control.sneak && m_sneak_node_exists) { 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); f32 min_y = lwn_f.Y + 0.5*BS; if(position.Y < min_y) { position.Y = min_y; //v3f old_speed = m_speed; if(m_speed.Y < 0) m_speed.Y = 0; /*if(collision_info) { // Report fall collision if(old_speed.Y < m_speed.Y - 0.1) { CollisionInfo info; info.t = COLLISION_FALL; info.speed = m_speed.Y - old_speed.Y; collision_info->push_back(info); } }*/ } } /* Calculate player collision box (new and old) */ core::aabbox3d<f32> playerbox( position.X - player_radius, position.Y - 0.0, position.Z - player_radius, position.X + player_radius, position.Y + player_height, position.Z + player_radius ); core::aabbox3d<f32> playerbox_old( oldpos.X - player_radius, oldpos.Y - 0.0, oldpos.Z - player_radius, oldpos.X + player_radius, oldpos.Y + player_height, oldpos.Z + player_radius ); /* 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 = false; /*std::cout<<"Checking collisions for (" <<oldpos_i.X<<","<<oldpos_i.Y<<","<<oldpos_i.Z <<") -> (" <<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z <<"):"<<std::endl;*/ bool standing_on_unloaded = false; /* Go through every node around the player */ for(s16 y = oldpos_i.Y - 1; y <= oldpos_i.Y + 2; y++) for(s16 z = oldpos_i.Z - 1; z <= oldpos_i.Z + 1; z++) for(s16 x = oldpos_i.X - 1; x <= oldpos_i.X + 1; x++) { bool is_unloaded = false; try{ // Player collides into walkable nodes if(nodemgr->get(map.getNode(v3s16(x,y,z))).walkable == false) continue; } catch(InvalidPositionException &e) { is_unloaded = true; // Doing nothing here will block the player from // walking over map borders } core::aabbox3d<f32> nodebox = getNodeBox(v3s16(x,y,z), BS); /* See if the player is touching ground. Player touches ground if player's minimum Y is near node's maximum Y and player'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-playerbox.MinEdge.Y) < d fabs(nodebox.MaxEdge.Y-playerbox.MinEdge.Y) < 0.15*BS && nodebox.MaxEdge.X-d > playerbox.MinEdge.X && nodebox.MinEdge.X+d < playerbox.MaxEdge.X && nodebox.MaxEdge.Z-d > playerbox.MinEdge.Z && nodebox.MinEdge.Z+d < playerbox.MaxEdge.Z ){ touching_ground = true; if(is_unloaded) standing_on_unloaded = true; } // If player doesn't intersect with node, ignore node. if(playerbox.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 playermax = playerbox.MaxEdge.dotProduct(dirs[i]); f32 playermin = playerbox.MinEdge.dotProduct(dirs[i]); f32 playermax_old = playerbox_old.MaxEdge.dotProduct(dirs[i]); f32 playermin_old = playerbox_old.MinEdge.dotProduct(dirs[i]); /* Check collision for the axis. Collision happens when player is going through a surface. */ /*f32 neg_d = d; f32 pos_d = d; // Make it easier to get on top of a node if(i == 1) neg_d = 0.15*BS; bool negative_axis_collides = (nodemax > playermin && nodemax <= playermin_old + neg_d && m_speed.dotProduct(dirs[i]) < 0); bool positive_axis_collides = (nodemin < playermax && nodemin >= playermax_old - pos_d && m_speed.dotProduct(dirs[i]) > 0);*/ bool negative_axis_collides = (nodemax > playermin && nodemax <= playermin_old + d && m_speed.dotProduct(dirs[i]) < 0); bool positive_axis_collides = (nodemin < playermax && nodemin >= playermax_old - d && m_speed.dotProduct(dirs[i]) > 0); bool main_axis_collides = negative_axis_collides || positive_axis_collides; /* Check overlap of player 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 playermax = playerbox.MaxEdge.dotProduct(dirs[j]); f32 playermin = playerbox.MinEdge.dotProduct(dirs[j]); if(!(nodemax - d > playermin && nodemin + d < playermax)) { other_axes_overlap = false; break; } } /* If this is a collision, revert the position in the main direction. */ if(other_axes_overlap && main_axis_collides) { //v3f old_speed = m_speed; m_speed -= m_speed.dotProduct(dirs[i]) * dirs[i]; position -= position.dotProduct(dirs[i]) * dirs[i]; position += oldpos.dotProduct(dirs[i]) * dirs[i]; /*if(collision_info) { // Report fall collision if(old_speed.Y < m_speed.Y - 0.1) { CollisionInfo info; info.t = COLLISION_FALL; info.speed = m_speed.Y - old_speed.Y; collision_info->push_back(info); } }*/ } } } // xyz /* Check the nodes under the player to see from which node the player is sneaking from, if any. */ { 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; try{ // The node to be sneaked on has to be walkable if(nodemgr->get(map.getNode(p)).walkable == false) continue; // And the node above it has to be nonwalkable if(nodemgr->get(map.getNode(p+v3s16(0,1,0))).walkable == true) continue; } catch(InvalidPositionException &e) { continue; } min_distance_f = distance_f; new_sneak_node = p; } bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9); if(control.sneak && m_sneak_node_exists) { if(sneak_node_found) m_sneak_node = new_sneak_node; } else { 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 */ if(collision_info) { // Report fall collision if(old_speed.Y < m_speed.Y - 0.1 && !standing_on_unloaded) { CollisionInfo info; info.t = COLLISION_FALL; info.speed = m_speed.Y - old_speed.Y; collision_info->push_back(info); } } if(!touching_ground_was && touching_ground){ MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround"); m_gamedef->event()->put(e); } { 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){ camera_barely_in_ceiling = true; } } } }