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;
}
Exemple #7
0
// 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 );
}
Exemple #11
0
/* 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]);
    }
Exemple #13
0
/* 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);
}
Exemple #14
0
// 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 );
}
Exemple #16
0
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);
	}
}
Exemple #18
0
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();
    }
}
Exemple #20
0
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;
	}
}
Exemple #21
0
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;
			}
		}
	}
}