/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the OpenSimulator Project nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Collections.Generic; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Physics.Manager; namespace OpenSim.Region.Framework.Scenes.Animation { /// /// Handle all animation duties for a scene presence /// public class ScenePresenceAnimator { public AnimationSet Animations { get { return m_animations; } } protected AnimationSet m_animations = new AnimationSet(); /// /// The current movement animation /// public string CurrentMovementAnimation { get { return m_movementAnimation; } } // protected string m_movementAnimation = "DEFAULT"; //KF: 'DEFAULT' does not exist! protected string m_movementAnimation = "CROUCH"; //KF: CROUCH ensures reliable Av Anim. init. private int m_animTickFall; // private int m_animTickJump; public int m_animTickJump; // ScenePresence has to see this to control +Z force /// /// The scene presence that this animator applies to /// protected ScenePresence m_scenePresence; public ScenePresenceAnimator(ScenePresence sp) { m_scenePresence = sp; } public void AddAnimation(UUID animID, UUID objectID) { if (m_scenePresence.IsChildAgent) return; if (m_animations.Add(animID, m_scenePresence.ControllingClient.NextAnimationSequenceNumber, objectID)) SendAnimPack(); } // Called from scripts public void AddAnimation(string name, UUID objectID) { if (m_scenePresence.IsChildAgent) return; UUID animID = m_scenePresence.ControllingClient.GetDefaultAnimation(name); if (animID == UUID.Zero) return; AddAnimation(animID, objectID); } public void RemoveAnimation(UUID animID) { if (m_scenePresence.IsChildAgent) return; if (m_animations.Remove(animID)) SendAnimPack(); } // Called from scripts public void RemoveAnimation(string name) { if (m_scenePresence.IsChildAgent) return; UUID animID = m_scenePresence.ControllingClient.GetDefaultAnimation(name); if (animID == UUID.Zero) return; RemoveAnimation(animID); } public void ResetAnimations() { m_animations.Clear(); } /// /// The movement animation is reserved for "main" animations /// that are mutually exclusive, e.g. flying and sitting. /// public void TrySetMovementAnimation(string anim) { if (!m_scenePresence.IsChildAgent) { if (m_animations.TrySetDefaultAnimation( anim, m_scenePresence.ControllingClient.NextAnimationSequenceNumber, m_scenePresence.UUID)) { // 16384 is CHANGED_ANIMATION m_scenePresence.SendScriptEventToAttachments("changed", new Object[] { 16384 }); SendAnimPack(); } } } /// /// This method determines the proper movement related animation /// public string GetMovementAnimation() { const float FALL_DELAY = 0.33f; const float PREJUMP_DELAY = 0.25f; #region Inputs AgentManager.ControlFlags controlFlags = (AgentManager.ControlFlags)m_scenePresence.AgentControlFlags; PhysicsActor actor = m_scenePresence.PhysicsActor; // Create forward and left vectors from the current avatar rotation Matrix4 rotMatrix = Matrix4.CreateFromQuaternion(m_scenePresence.Rotation); Vector3 fwd = Vector3.Transform(Vector3.UnitX, rotMatrix); Vector3 left = Vector3.Transform(Vector3.UnitY, rotMatrix); // Check control flags bool heldForward = ((controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_AT_POS || (controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_POS); bool heldBack = ((controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG || (controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_NEG); bool heldLeft = ((controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS || (controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_LEFT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_LEFT_POS); bool heldRight = ((controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG || (controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_LEFT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_LEFT_NEG); //bool heldTurnLeft = (controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT; //bool heldTurnRight = (controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT; bool heldUp = (controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_POS) == AgentManager.ControlFlags.AGENT_CONTROL_UP_POS; bool heldDown = (controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG; //bool flying = (controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) == AgentManager.ControlFlags.AGENT_CONTROL_FLY; //bool mouselook = (controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) == AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK; // Direction in which the avatar is trying to move Vector3 move = Vector3.Zero; if (heldForward) { move.X += fwd.X; move.Y += fwd.Y; } if (heldBack) { move.X -= fwd.X; move.Y -= fwd.Y; } if (heldLeft) { move.X += left.X; move.Y += left.Y; } if (heldRight) { move.X -= left.X; move.Y -= left.Y; } if (heldUp) { move.Z += 1; } if (heldDown) { move.Z -= 1; } // Is the avatar trying to move? // bool moving = (move != Vector3.Zero); bool jumping = m_animTickJump != 0; #endregion Inputs #region Flying if (actor != null && actor.Flying) { m_animTickFall = 0; m_animTickJump = 0; if (move.X != 0f || move.Y != 0f) { return (m_scenePresence.Scene.m_useFlySlow ? "FLYSLOW" : "FLY"); } else if (move.Z > 0f) { return "HOVER_UP"; } else if (move.Z < 0f) { if (actor != null && actor.IsColliding) return "LAND"; else return "HOVER_DOWN"; } else { return "HOVER"; } } #endregion Flying #region Falling/Floating/Landing if (actor == null || !actor.IsColliding) { float fallElapsed = (float)(Environment.TickCount - m_animTickFall) / 1000f; float fallVelocity = (actor != null) ? actor.Velocity.Z : 0.0f; if (m_animTickFall == 0 || (fallElapsed > FALL_DELAY && fallVelocity >= 0.0f)) { // Just started falling m_animTickFall = Environment.TickCount; } else if (!jumping && fallElapsed > FALL_DELAY) { // Falling long enough to trigger the animation return "FALLDOWN"; } return m_movementAnimation; } #endregion Falling/Floating/Landing #region Ground Movement if (m_movementAnimation == "FALLDOWN") { m_animTickFall = Environment.TickCount; // TODO: SOFT_LAND support return "LAND"; } else if (m_movementAnimation == "LAND") { float landElapsed = (float)(Environment.TickCount - m_animTickFall) / 1000f; if ((m_animTickFall != 0) && (landElapsed <= FALL_DELAY)) return "LAND"; } m_animTickFall = 0; if (move.Z > 0f) { // Jumping if (!jumping) { // Begin prejump m_animTickJump = Environment.TickCount; return "PREJUMP"; } else if (Environment.TickCount - m_animTickJump > PREJUMP_DELAY * 800.0f) { // Start actual jump if (m_animTickJump == -1) { // Already jumping! End the current jump m_animTickJump = 0; return "JUMP"; } m_animTickJump = -1; return "JUMP"; } } else { // Not jumping m_animTickJump = 0; if (move.X != 0f || move.Y != 0f) { // Walking / crouchwalking / running if (move.Z < 0f) return "CROUCHWALK"; else if (m_scenePresence.SetAlwaysRun) return "RUN"; else return "WALK"; } else { // Not walking if (move.Z < 0f) return "CROUCH"; else return "STAND"; } } #endregion Ground Movement return m_movementAnimation; } /// /// Update the movement animation of this avatar according to its current state /// public void UpdateMovementAnimations() { m_movementAnimation = GetMovementAnimation(); if (m_movementAnimation == "PREJUMP" && !m_scenePresence.Scene.m_usePreJump) { // This was the previous behavior before PREJUMP TrySetMovementAnimation("JUMP"); } else { TrySetMovementAnimation(m_movementAnimation); } } public UUID[] GetAnimationArray() { UUID[] animIDs; int[] sequenceNums; UUID[] objectIDs; m_animations.GetArrays(out animIDs, out sequenceNums, out objectIDs); return animIDs; } public BinBVHAnimation GenerateRandomAnimation() { int rnditerations = 3; BinBVHAnimation anim = new BinBVHAnimation(); List parts = new List(); parts.Add("mPelvis");parts.Add("mHead");parts.Add("mTorso"); parts.Add("mHipLeft");parts.Add("mHipRight");parts.Add("mHipLeft");parts.Add("mKneeLeft"); parts.Add("mKneeRight");parts.Add("mCollarLeft");parts.Add("mCollarRight");parts.Add("mNeck"); parts.Add("mElbowLeft");parts.Add("mElbowRight");parts.Add("mWristLeft");parts.Add("mWristRight"); parts.Add("mShoulderLeft");parts.Add("mShoulderRight");parts.Add("mAnkleLeft");parts.Add("mAnkleRight"); parts.Add("mEyeRight");parts.Add("mChest");parts.Add("mToeLeft");parts.Add("mToeRight"); parts.Add("mFootLeft");parts.Add("mFootRight");parts.Add("mEyeLeft"); anim.HandPose = 1; anim.InPoint = 0; anim.OutPoint = (rnditerations * .10f); anim.Priority = 7; anim.Loop = false; anim.Length = (rnditerations * .10f); anim.ExpressionName = "afraid"; anim.EaseInTime = 0; anim.EaseOutTime = 0; string[] strjoints = parts.ToArray(); anim.Joints = new binBVHJoint[strjoints.Length]; for (int j = 0; j < strjoints.Length; j++) { anim.Joints[j] = new binBVHJoint(); anim.Joints[j].Name = strjoints[j]; anim.Joints[j].Priority = 7; anim.Joints[j].positionkeys = new binBVHJointKey[rnditerations]; anim.Joints[j].rotationkeys = new binBVHJointKey[rnditerations]; Random rnd = new Random(); for (int i = 0; i < rnditerations; i++) { anim.Joints[j].rotationkeys[i] = new binBVHJointKey(); anim.Joints[j].rotationkeys[i].time = (i*.10f); anim.Joints[j].rotationkeys[i].key_element.X = ((float) rnd.NextDouble()*2 - 1); anim.Joints[j].rotationkeys[i].key_element.Y = ((float) rnd.NextDouble()*2 - 1); anim.Joints[j].rotationkeys[i].key_element.Z = ((float) rnd.NextDouble()*2 - 1); anim.Joints[j].positionkeys[i] = new binBVHJointKey(); anim.Joints[j].positionkeys[i].time = (i*.10f); anim.Joints[j].positionkeys[i].key_element.X = 0; anim.Joints[j].positionkeys[i].key_element.Y = 0; anim.Joints[j].positionkeys[i].key_element.Z = 0; } } AssetBase Animasset = new AssetBase(UUID.Random(), "Random Animation", (sbyte)AssetType.Animation); Animasset.Data = anim.ToBytes(); Animasset.Temporary = true; Animasset.Local = true; Animasset.Description = "dance"; //BinBVHAnimation bbvhanim = new BinBVHAnimation(Animasset.Data); m_scenePresence.Scene.AssetService.Store(Animasset); AddAnimation(Animasset.FullID, m_scenePresence.UUID); return anim; } /// /// /// /// /// /// public void SendAnimPack(UUID[] animations, int[] seqs, UUID[] objectIDs) { if (m_scenePresence.IsChildAgent) return; m_scenePresence.Scene.ForEachClient( delegate(IClientAPI client) { client.SendAnimations(animations, seqs, m_scenePresence.ControllingClient.AgentId, objectIDs); }); } public void SendAnimPackToClient(IClientAPI client) { if (m_scenePresence.IsChildAgent) return; m_scenePresence.Scene.ForEachScenePresence( delegate(ScenePresence SP) { SP.Animator.SendAnimPack(); }); } /// /// Send animation information about this avatar to all clients. /// public void SendAnimPack() { //m_log.Debug("Sending animation pack to all"); if (m_scenePresence.IsChildAgent) return; UUID[] animIDs; int[] sequenceNums; UUID[] objectIDs; m_animations.GetArrays(out animIDs, out sequenceNums, out objectIDs); SendAnimPack(animIDs, sequenceNums, objectIDs); } public void Close() { m_animations = null; m_scenePresence = null; } } }