private void UpdateEntity(ISceneEntity entity) { if (!(entity is IPhysical)) { return; } if (entity is IScenePresence && ((IScenePresence)entity).IsChildPresence) { return; } float elapsedTime = m_elapsedTime; if (elapsedTime <= 0f) { return; } IPhysical physical = (IPhysical)entity; if (!physical.DynamicsEnabled) { return; } IPhysicalPresence presence = (physical is IPhysicalPresence) ? (IPhysicalPresence)physical : null; Vector3 velocity = physical.Velocity * elapsedTime; Vector3 position = physical.RelativePosition; Vector3 move = (presence != null) ? presence.InputVelocity : Vector3.Zero; bool jumping = (presence != null) ? presence.JumpStart != 0 : false; float gravity = 0f; float waterHeight = 0.0f; if (m_terrain != null) { waterHeight = m_terrain.WaterHeight; } float waterChestHeight = waterHeight - (physical.Scale.Z * .33f); float speed = elapsedTime; float fallElapsed = (float)(Util.TickCount() - physical.FallStart) / 1000f; UUID anim = UUID.Zero; List <ISceneEntity> colliders = new List <ISceneEntity>(); if (presence != null && presence.StunMS > 0) { move = Vector3.Zero; presence.StunMS -= (int)((float)1000 * elapsedTime); } ISceneEntity collider; float collisionDist; //if (presence != null && presence.InputDirection != Vector3.Zero) //{ // // Raycast in the direction the avatar wishes to move // Ray avRay = new Ray(physical.ScenePosition, Vector3.Normalize(presence.InputDirection)); // if (FullSceneCollisionTest(false, avRay, presence, out collider, out collisionDist)) // { // speed = Math.Min(speed, collisionDist - COLLISION_MARGIN); // m_log.Debug("Raycasted to " + collider.Name + " (" + collider.LocalID + ")"); // } //} //HACK: detect both X and Y ray collisions, since we are not calculating a collision normal Vector3 normVel = Vector3.Normalize(velocity); if ((normVel.X != 0f || move.X != 0f) && FullSceneCollisionTest(false, new Ray(position, normVel.X > 0 || move.X > 0 ? Vector3.UnitX : -Vector3.UnitX), physical, out collider, out collisionDist) && collisionDist <= COLLISION_MARGIN) { move.X = 0f; velocity.X = 0f; if (!colliders.Contains(collider)) { colliders.Add(collider); } } if ((normVel.Y != 0f || move.Y != 0f) && FullSceneCollisionTest(false, new Ray(position, normVel.Y > 0 || move.Y > 0 ? Vector3.UnitY : -Vector3.UnitY), physical, out collider, out collisionDist) && collisionDist <= COLLISION_MARGIN) { move.Y = 0f; velocity.Y = 0f; if (!colliders.Contains(collider)) { colliders.Add(collider); } } #region Terrain Movement float oldFloor = 0.0f, newFloor = 0.0f; if (m_terrain != null) { oldFloor = m_terrain.GetTerrainHeightAt(position.X, position.Y); } position += (move * speed); if (m_terrain != null) { newFloor = m_terrain.GetTerrainHeightAt(position.X, position.Y); } if (presence != null) { if (presence.MovementState != MovementState.Flying && newFloor != oldFloor) { speed /= (1f + (SQRT_TWO * Math.Abs(newFloor - oldFloor))); } } //HACK: distance from avatar center to the bottom of its feet float distanceFromFloor = physical.Scale.Z * .5f; // Raycast for gravity Ray ray = new Ray(position, -Vector3.UnitZ); if (FullSceneCollisionTest(false, ray, physical, out collider, out collisionDist)) { if (position.Z - collisionDist > newFloor) { newFloor = position.Z - collisionDist; //FIXME //if (!colliders.Contains(collider)) //colliders.Add(collider); } } float lowerLimit = newFloor + distanceFromFloor; #endregion Terrain Movement if (presence != null && presence.MovementState == MovementState.Flying) { #region Flying physical.FallStart = 0; presence.JumpStart = 0; //velocity falloff while flying velocity.X *= 0.66f; velocity.Y *= 0.66f; velocity.Z *= 0.33f; if (position.Z == lowerLimit) { velocity.Z += INITIAL_HOVER_IMPULSE; } if (move.X != 0f || move.Y != 0f) { anim = Animations.FLY; } else if (move.Z > 0f) { anim = Animations.HOVER_UP; } else if (move.Z < 0f) { anim = Animations.HOVER_DOWN; } else { anim = Animations.HOVER; } #endregion Flying } else if (position.Z > lowerLimit + FALL_FORGIVENESS || position.Z <= waterChestHeight) { #region Falling/Floating/Landing if (position.Z > waterHeight) { //above water //override controls while drifting move = Vector3.Zero; //keep most of our horizontal inertia velocity.X *= 0.975f; velocity.Y *= 0.975f; if (physical.FallStart == 0) //|| (fallElapsed > FALL_DELAY && velocity.Z >= 0f)) { //just started falling physical.FallStart = Util.TickCount(); } else { gravity = GRAVITY * fallElapsed * elapsedTime; //normal gravity if (!jumping) { //falling if (fallElapsed > FALL_DELAY) { //falling long enough to trigger the animation anim = Animations.FALLDOWN; } } } } else if (position.Z >= waterChestHeight) { //at the water line velocity *= 0.1f; velocity.Z = 0f; physical.FallStart = 0; if (move.Z < 1f) { position.Z = waterChestHeight; } if (move.Z > 0f) { anim = Animations.HOVER_UP; } else if (move.X != 0f || move.Y != 0f) { anim = Animations.SWIM_FORWARD; } else { anim = Animations.HOVER; } } else { //underwater velocity *= 0.1f; velocity.Z += 0.75f * elapsedTime; // buoyant if (move.Z < 0f) { anim = Animations.SWIM_DOWN; } else { anim = Animations.SWIM_FORWARD; } } #endregion Falling/Floating/Landing } else { #region Ground Movement if (presence != null) { if (presence.JumpStart == 0 && physical.FallStart > 0) { if (fallElapsed >= FALL_DELAY * 4) { anim = Animations.STANDUP; presence.StunMS = 2000; } else if (fallElapsed >= FALL_DELAY * 3) { anim = Animations.MEDIUM_LAND; presence.StunMS = 1000; } else if (fallElapsed >= FALL_DELAY * 2) { anim = Animations.LAND; presence.StunMS = 500; } if (presence.Animations.SetDefaultAnimation(anim, 1)) { m_scene.SendPresenceAnimations(this, presence); } } } physical.FallStart = 0; //friction velocity *= 0.2f; velocity.Z = 0f; position.Z = lowerLimit; if (presence != null) { if (move.Z > 0f) { //jumping if (!jumping) { //begin prejump move.Z = 0f; //override Z control anim = Animations.PRE_JUMP; presence.JumpStart = Util.TickCount(); } else if (Util.TickCount() - presence.JumpStart > PREJUMP_DELAY * 1000) { //start actual jump if (presence.JumpStart == -1) { //already jumping! end current jump presence.JumpStart = 0; return; } anim = Animations.JUMP; Vector3 normalVel = Vector3.Normalize(velocity); velocity.X += normalVel.X * JUMP_IMPULSE_HORIZONTAL * elapsedTime; velocity.Y += normalVel.Y * JUMP_IMPULSE_HORIZONTAL * elapsedTime; velocity.Z = JUMP_IMPULSE_VERTICAL * elapsedTime; presence.JumpStart = -1; //flag that we are currently jumping } else { move.Z = 0; //override Z control } } else { //not jumping presence.JumpStart = 0; if (move.X != 0 || move.Y != 0) { if (move.Z < 0) { anim = Animations.CROUCHWALK; } else if (presence.MovementState == MovementState.Running) { anim = Animations.RUN; } else { anim = Animations.WALK; } } else { if (move.Z < 0) { anim = Animations.CROUCH; } else { anim = Animations.STAND; } } } } #endregion Ground Movement } if (presence != null) { if (anim != UUID.Zero && presence.StunMS <= 0 && presence.Animations.SetDefaultAnimation(anim, 1)) { m_scene.SendPresenceAnimations(this, presence); } } float maxVel = AVATAR_TERMINAL_VELOCITY * elapsedTime; #region Update Physical State // Calculate how far we moved this frame Vector3 moved = position - physical.RelativePosition; if (moved.Z < -maxVel) { moved.Z = -maxVel; } else if (moved.Z > maxVel) { moved.Z = maxVel; } position += velocity; moved.Z = moved.Z - gravity; velocity += moved; if (velocity.Z < -maxVel) { velocity.Z = -maxVel; } else if (velocity.Z > maxVel) { velocity.Z = maxVel; } if (position.Z < lowerLimit) { position.Z = lowerLimit; } if (position.Z < lowerLimit + COLLISION_MARGIN) { velocity.Z = 0f; } physical.Velocity = velocity / elapsedTime; physical.RelativePosition = position; #endregion Update Physical State EventHandler <EntityCollisionArgs> callback = OnEntityCollision; if (callback != null) { for (int i = 0, len = colliders.Count; i < len; i++) { callback(this, new EntityCollisionArgs { First = physical, Second = colliders[i] }); callback(this, new EntityCollisionArgs { First = colliders[i], Second = physical }); } } m_scene.EntityAddOrUpdate(this, physical, UpdateFlags.Position | UpdateFlags.Velocity, 0); }