From c82709c0d6c72852d8614651f9cb31df09fff883 Mon Sep 17 00:00:00 2001 From: Melanie Date: Sun, 26 Feb 2012 02:36:34 +0100 Subject: Implement llSetKeyframedMotion. No persistence, no region crossing. Yet. --- .../Avatar/AvatarFactory/AvatarFactoryModule.cs | 2 +- .../InventoryAccess/InventoryAccessModule.cs | 4 + OpenSim/Region/Framework/Scenes/KeyframeMotion.cs | 315 +++++++++++++++++++++ .../Region/Framework/Scenes/SceneObjectGroup.cs | 12 + OpenSim/Region/Framework/Scenes/SceneObjectPart.cs | 3 + .../Shared/Api/Implementation/LSL_Api.cs | 138 +++++++++ .../ScriptEngine/Shared/Api/Interface/ILSL_Api.cs | 1 + .../Shared/Api/Runtime/LSL_Constants.cs | 13 + .../ScriptEngine/Shared/Api/Runtime/LSL_Stub.cs | 7 +- 9 files changed, 493 insertions(+), 2 deletions(-) create mode 100644 OpenSim/Region/Framework/Scenes/KeyframeMotion.cs (limited to 'OpenSim') diff --git a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs index 93e22e0..2bebd30 100644 --- a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs @@ -553,7 +553,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory { Util.FireAndForget(delegate(object x) { - Thread.Sleep(2000); + Thread.Sleep(4000); // m_log.DebugFormat("[AVFACTORY]: Client_OnRequestWearables called for {0} ({1})", client.Name, client.AgentId); ScenePresence sp = m_scene.GetScenePresence(client.AgentId); diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index 6c4c63f..26650a5 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -355,6 +355,10 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess foreach (SceneObjectGroup objectGroup in objlist) { + if (objectGroup.KeyframeMotion != null) + objectGroup.KeyframeMotion.Stop(); + objectGroup.KeyframeMotion = null; + Vector3 inventoryStoredPosition = new Vector3 (((objectGroup.AbsolutePosition.X > (int)Constants.RegionSize) ? 250 diff --git a/OpenSim/Region/Framework/Scenes/KeyframeMotion.cs b/OpenSim/Region/Framework/Scenes/KeyframeMotion.cs new file mode 100644 index 0000000..239f7ca --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/KeyframeMotion.cs @@ -0,0 +1,315 @@ +// Proprietary code of Avination Virtual Limited +// (c) 2012 Melanie Thielker +// + +using System; +using System.Timers; +using System.Collections.Generic; +using System.IO; +using System.Diagnostics; +using System.Reflection; +using System.Threading; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Physics.Manager; +using OpenSim.Region.Framework.Scenes.Serialization; +using Timer = System.Timers.Timer; +using log4net; + +namespace OpenSim.Region.Framework.Scenes +{ + public class KeyframeMotion + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public enum PlayMode : int + { + Forward = 0, + Reverse = 1, + Loop = 2, + PingPong = 3 + }; + + [Flags] + public enum DataFormat : int + { + Translation = 1, + Rotation = 2 + } + + public struct Keyframe + { + public Vector3? Position; + public Quaternion? Rotation; + public Quaternion StartRotation; + public int TimeMS; + public int TimeTotal; + public Vector3 AngularVelocity; + }; + + private Vector3 m_basePosition; + private Quaternion m_baseRotation; + + private Keyframe m_currentFrame; + private List m_frames = new List(); + + private Keyframe[] m_keyframes; + + private Timer m_timer = new Timer(); + + private readonly SceneObjectGroup m_group; + + private PlayMode m_mode = PlayMode.Forward; + private DataFormat m_data = DataFormat.Translation | DataFormat.Rotation; + + private int m_iterations = 0; + + private const double timerInterval = 50.0; + + public DataFormat Data + { + get { return m_data; } + } + + public KeyframeMotion(SceneObjectGroup grp, PlayMode mode, DataFormat data) + { + m_mode = mode; + m_data = data; + + m_group = grp; + m_basePosition = grp.AbsolutePosition; + m_baseRotation = grp.GroupRotation; + + m_timer.Interval = (int)timerInterval; + m_timer.AutoReset = true; + m_timer.Elapsed += OnTimer; + } + + public void SetKeyframes(Keyframe[] frames) + { + m_keyframes = frames; + } + + public void Start() + { + if (m_keyframes.Length > 0) + m_timer.Start(); + } + + public void Stop() + { + m_timer.Stop(); + + m_basePosition = m_group.AbsolutePosition; + m_baseRotation = m_group.GroupRotation; + + m_group.RootPart.Velocity = Vector3.Zero; + m_group.RootPart.UpdateAngularVelocity(Vector3.Zero); + m_group.SendGroupRootTerseUpdate(); + + m_frames.Clear(); + } + + public void Pause() + { + m_group.RootPart.Velocity = Vector3.Zero; + m_group.RootPart.UpdateAngularVelocity(Vector3.Zero); + m_group.SendGroupRootTerseUpdate(); + + m_timer.Stop(); + } + + private void GetNextList() + { + m_frames.Clear(); + Vector3 pos = m_basePosition; + Quaternion rot = m_baseRotation; + + if (m_mode == PlayMode.Loop || m_mode == PlayMode.PingPong || m_iterations == 0) + { + int direction = 1; + if (m_mode == PlayMode.Reverse || ((m_mode == PlayMode.PingPong) && ((m_iterations & 1) != 0))) + direction = -1; + + int start = 0; + int end = m_keyframes.Length; +// if (m_mode == PlayMode.PingPong && m_keyframes.Length > 1) +// end = m_keyframes.Length - 1; + + if (direction < 0) + { + start = m_keyframes.Length - 1; + end = -1; +// if (m_mode == PlayMode.PingPong && m_keyframes.Length > 1) +// end = 0; + } + + for (int i = start; i != end ; i += direction) + { + Keyframe k = m_keyframes[i]; + + if (k.Position.HasValue) + k.Position = (k.Position * direction) + pos; + else + k.Position = pos; + + k.StartRotation = rot; + if (k.Rotation.HasValue) + { + if (direction == -1) + k.Rotation = Quaternion.Conjugate((Quaternion)k.Rotation); + k.Rotation = rot * k.Rotation; + } + else + { + k.Rotation = rot; + } + + float angle = 0; + + float aa = k.StartRotation.X * k.StartRotation.X + k.StartRotation.Y * k.StartRotation.Y + k.StartRotation.Z * k.StartRotation.Z + k.StartRotation.W * k.StartRotation.W; + float bb = ((Quaternion)k.Rotation).X * ((Quaternion)k.Rotation).X + ((Quaternion)k.Rotation).Y * ((Quaternion)k.Rotation).Y + ((Quaternion)k.Rotation).Z * ((Quaternion)k.Rotation).Z + ((Quaternion)k.Rotation).W * ((Quaternion)k.Rotation).W; + float aa_bb = aa * bb; + + if (aa_bb == 0) + { + angle = 0; + } + else + { + float ab = k.StartRotation.X * ((Quaternion)k.Rotation).X + + k.StartRotation.Y * ((Quaternion)k.Rotation).Y + + k.StartRotation.Z * ((Quaternion)k.Rotation).Z + + k.StartRotation.W * ((Quaternion)k.Rotation).W; + float q = (ab * ab) / aa_bb; + + if (q > 1.0f) + { + angle = 0; + } + else + { + angle = (float)Math.Acos(2 * q - 1); + } + } + + m_log.DebugFormat("[KEYFRAME]: Angle {0} aabb {1}", angle, aa_bb); + k.AngularVelocity = (new Vector3(0, 0, 1) * (Quaternion)k.Rotation) * (angle / (k.TimeMS / 1000)); + k.TimeTotal = k.TimeMS; + + m_frames.Add(k); + + pos = (Vector3)k.Position; + rot = (Quaternion)k.Rotation; + } + + m_basePosition = pos; + m_baseRotation = rot; + + m_iterations++; + } + } + + private void OnTimer(object sender, ElapsedEventArgs e) + { + if (m_frames.Count == 0) + { + GetNextList(); + + if (m_frames.Count == 0) + { + Stop(); + return; + } + + m_currentFrame = m_frames[0]; + } + + // Do the frame processing + double steps = (double)m_currentFrame.TimeMS / timerInterval; + float complete = ((float)m_currentFrame.TimeTotal - (float)m_currentFrame.TimeMS) / (float)m_currentFrame.TimeTotal; + + if (steps <= 1.0) + { + m_currentFrame.TimeMS = 0; + + m_group.AbsolutePosition = (Vector3)m_currentFrame.Position; + m_group.UpdateGroupRotationR((Quaternion)m_currentFrame.Rotation); + } + else + { + Vector3 v = (Vector3)m_currentFrame.Position - m_group.AbsolutePosition; + Vector3 motionThisFrame = v / (float)steps; + v = v * 1000 / m_currentFrame.TimeMS; + + bool update = false; + + if (Vector3.Mag(motionThisFrame) >= 0.05f) + { + m_group.AbsolutePosition += motionThisFrame; + m_group.RootPart.Velocity = v; + update = true; + } + + if ((Quaternion)m_currentFrame.Rotation != m_group.GroupRotation) + { + Quaternion current = m_group.GroupRotation; + + Quaternion step = Quaternion.Slerp(m_currentFrame.StartRotation, (Quaternion)m_currentFrame.Rotation, complete); + + float angle = 0; + + float aa = current.X * current.X + current.Y * current.Y + current.Z * current.Z + current.W * current.W; + float bb = step.X * step.X + step.Y * step.Y + step.Z * step.Z + step.W * step.W; + float aa_bb = aa * bb; + + if (aa_bb == 0) + { + angle = 0; + } + else + { + float ab = current.X * step.X + + current.Y * step.Y + + current.Z * step.Z + + current.W * step.W; + float q = (ab * ab) / aa_bb; + + if (q > 1.0f) + { + angle = 0; + } + else + { + angle = (float)Math.Acos(2 * q - 1); + } + } + + if (angle > 0.01f) + { + m_group.UpdateGroupRotationR(step); + //m_group.RootPart.UpdateAngularVelocity(m_currentFrame.AngularVelocity / 2); + update = true; + } + } + + if (update) + m_group.SendGroupRootTerseUpdate(); + } + + m_currentFrame.TimeMS -= (int)timerInterval; + + if (m_currentFrame.TimeMS <= 0) + { + m_group.RootPart.Velocity = Vector3.Zero; + m_group.RootPart.UpdateAngularVelocity(Vector3.Zero); + m_group.SendGroupRootTerseUpdate(); + + m_frames.RemoveAt(0); + if (m_frames.Count > 0) + m_currentFrame = m_frames[0]; + } + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index 248d4c6..5ccea13 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -114,6 +114,12 @@ namespace OpenSim.Region.Framework.Scenes private Random m_rand; private bool m_suspendUpdates; private List m_linkedAvatars = new List(); + private KeyframeMotion m_keyframeMotion = null; + + public KeyframeMotion KeyframeMotion + { + get; set; + } public bool areUpdatesSuspended { @@ -1982,6 +1988,12 @@ namespace OpenSim.Region.Framework.Scenes public void ScriptSetPhysicsStatus(bool usePhysics) { + if (usePhysics) + { + if (KeyframeMotion != null) + KeyframeMotion.Stop(); + KeyframeMotion = null; + } UpdatePrimFlags(RootPart.LocalId, usePhysics, IsTemporary, IsPhantom, IsVolumeDetect); } diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index dd9431b..b6d5c4b 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -1855,6 +1855,9 @@ namespace OpenSim.Region.Framework.Scenes { if (UsePhysics) { + if (ParentGroup.KeyframeMotion != null) + ParentGroup.KeyframeMotion.Stop(); + ParentGroup.KeyframeMotion = null; ParentGroup.Scene.AddPhysicalPrim(1); PhysActor.OnRequestTerseUpdate += PhysicsRequestingTerseUpdate; diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index d0430f4..9090311 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -11896,6 +11896,144 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } #endregion + + public void llSetKeyframedMotion(LSL_List frames, LSL_List options) + { + SceneObjectGroup group = m_host.ParentGroup; + + if (group.RootPart.PhysActor != null && group.RootPart.PhysActor.IsPhysical) + return; + if (group.IsAttachment) + return; + + if (frames.Data.Length > 0) // We are getting a new motion + { + if (group.KeyframeMotion != null) + group.KeyframeMotion.Stop(); + group.KeyframeMotion = null; + + int idx = 0; + + KeyframeMotion.PlayMode mode = KeyframeMotion.PlayMode.Forward; + KeyframeMotion.DataFormat data = KeyframeMotion.DataFormat.Translation | KeyframeMotion.DataFormat.Rotation; + + while (idx < options.Data.Length) + { + int option = (int)options.GetLSLIntegerItem(idx++); + int remain = options.Data.Length - idx; + + switch (option) + { + case ScriptBaseClass.KFM_MODE: + if (remain < 1) + break; + int modeval = (int)options.GetLSLIntegerItem(idx++); + switch(modeval) + { + case ScriptBaseClass.KFM_FORWARD: + mode = KeyframeMotion.PlayMode.Forward; + break; + case ScriptBaseClass.KFM_REVERSE: + mode = KeyframeMotion.PlayMode.Reverse; + break; + case ScriptBaseClass.KFM_LOOP: + mode = KeyframeMotion.PlayMode.Loop; + break; + case ScriptBaseClass.KFM_PING_PONG: + mode = KeyframeMotion.PlayMode.PingPong; + break; + } + break; + case ScriptBaseClass.KFM_DATA: + if (remain < 1) + break; + int dataval = (int)options.GetLSLIntegerItem(idx++); + data = (KeyframeMotion.DataFormat)dataval; + break; + } + } + + group.KeyframeMotion = new KeyframeMotion(group, mode, data); + + idx = 0; + + int elemLength = 2; + if (data == (KeyframeMotion.DataFormat.Translation | KeyframeMotion.DataFormat.Rotation)) + elemLength = 3; + + List keyframes = new List(); + while (idx < frames.Data.Length) + { + int remain = frames.Data.Length - idx; + + if (remain < elemLength) + break; + + KeyframeMotion.Keyframe frame = new KeyframeMotion.Keyframe(); + frame.Position = null; + frame.Rotation = null; + + if ((data & KeyframeMotion.DataFormat.Translation) != 0) + { + LSL_Types.Vector3 tempv = frames.GetVector3Item(idx++); + frame.Position = new Vector3((float)tempv.x, (float)tempv.y, (float)tempv.z); + } + if ((data & KeyframeMotion.DataFormat.Rotation) != 0) + { + LSL_Types.Quaternion tempq = frames.GetQuaternionItem(idx++); + frame.Rotation = new Quaternion((float)tempq.x, (float)tempq.y, (float)tempq.z, (float)tempq.s); + } + + float tempf = (float)frames.GetLSLFloatItem(idx++); + frame.TimeMS = (int)(tempf * 1000.0f); + + keyframes.Add(frame); + } + + group.KeyframeMotion.SetKeyframes(keyframes.ToArray()); + group.KeyframeMotion.Start(); + } + else + { + if (group.KeyframeMotion == null) + return; + + if (options.Data.Length == 0) + { + group.KeyframeMotion.Stop(); + return; + } + + int code = (int)options.GetLSLIntegerItem(0); + + int idx = 0; + + while (idx < options.Data.Length) + { + int option = (int)options.GetLSLIntegerItem(idx++); + int remain = options.Data.Length - idx; + + switch (option) + { + case ScriptBaseClass.KFM_COMMAND: + int cmd = (int)options.GetLSLIntegerItem(idx++); + switch (cmd) + { + case ScriptBaseClass.KFM_CMD_PLAY: + group.KeyframeMotion.Start(); + break; + case ScriptBaseClass.KFM_CMD_STOP: + group.KeyframeMotion.Stop(); + break; + case ScriptBaseClass.KFM_CMD_PAUSE: + group.KeyframeMotion.Pause(); + break; + } + break; + } + } + } + } } public class NotecardCache diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/ILSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/ILSL_Api.cs index 9679798..99bfc97 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/ILSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/ILSL_Api.cs @@ -415,5 +415,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Interfaces void SetPrimitiveParamsEx(LSL_Key prim, LSL_List rules); LSL_List GetLinkPrimitiveParamsEx(LSL_Key prim, LSL_List rules); + void llSetKeyframedMotion(LSL_List frames, LSL_List options); } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs index 3d0e5cb..a5e160d 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs @@ -641,5 +641,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase public static readonly LSLInteger RCERR_UNKNOWN = -1; public static readonly LSLInteger RCERR_SIM_PERF_LOW = -2; public static readonly LSLInteger RCERR_CAST_TIME_EXCEEDED = 3; + + public const int KFM_MODE = 1; + public const int KFM_LOOP = 1; + public const int KFM_REVERSE = 3; + public const int KFM_FORWARD = 0; + public const int KFM_PING_PONG = 2; + public const int KFM_DATA = 2; + public const int KFM_TRANSLATION = 2; + public const int KFM_ROTATION = 1; + public const int KFM_COMMAND = 0; + public const int KFM_CMD_PLAY = 0; + public const int KFM_CMD_STOP = 1; + public const int KFM_CMD_PAUSE = 2; } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Stub.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Stub.cs index dcaa3b4..9c4437d 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Stub.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Stub.cs @@ -1917,7 +1917,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase public LSL_Integer llGetLinkNumberOfSides(LSL_Integer link) { - return m_LSL_Functions.llGetLinkNumberOfSides(link); + return m_LSL_Functions.llGetLinkNumberOfSides(link); + } + + public void llSetKeyframedMotion(LSL_List frames, LSL_List options) + { + m_LSL_Functions.llSetKeyframedMotion(frames, options); } } } -- cgit v1.1