123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824 |
- /*
- * 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.Timers;
- using System.Collections;
- 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 System.Runtime.Serialization.Formatters.Binary;
- using System.Runtime.Serialization;
- using Timer = System.Timers.Timer;
- using log4net;
- namespace OpenSim.Region.Framework.Scenes
- {
- public class KeyframeTimer
- {
- private static Dictionary<Scene, KeyframeTimer> m_timers =
- new Dictionary<Scene, KeyframeTimer>();
- private Timer m_timer;
- private Dictionary<KeyframeMotion, object> m_motions = new Dictionary<KeyframeMotion, object>();
- private object m_lockObject = new object();
- private object m_timerLock = new object();
- private const double m_tickDuration = 50.0;
- public double TickDuration
- {
- get { return m_tickDuration; }
- }
- public KeyframeTimer(Scene scene)
- {
- m_timer = new Timer();
- m_timer.Interval = TickDuration;
- m_timer.AutoReset = true;
- m_timer.Elapsed += OnTimer;
- }
- public void Start()
- {
- lock (m_timer)
- {
- if (!m_timer.Enabled)
- m_timer.Start();
- }
- }
- private void OnTimer(object sender, ElapsedEventArgs ea)
- {
- if (!Monitor.TryEnter(m_timerLock))
- return;
- try
- {
- List<KeyframeMotion> motions;
- lock (m_lockObject)
- {
- motions = new List<KeyframeMotion>(m_motions.Keys);
- }
- foreach (KeyframeMotion m in motions)
- {
- try
- {
- m.OnTimer(TickDuration);
- }
- catch (Exception)
- {
- // Don't stop processing
- }
- }
- }
- catch (Exception)
- {
- // Keep running no matter what
- }
- finally
- {
- Monitor.Exit(m_timerLock);
- }
- }
- public static void Add(KeyframeMotion motion)
- {
- KeyframeTimer timer;
- if (motion.Scene == null)
- return;
- lock (m_timers)
- {
- if (!m_timers.TryGetValue(motion.Scene, out timer))
- {
- timer = new KeyframeTimer(motion.Scene);
- m_timers[motion.Scene] = timer;
- if (!SceneManager.Instance.AllRegionsReady)
- {
- // Start the timers only once all the regions are ready. This is required
- // when using megaregions, because the megaregion is correctly configured
- // only after all the regions have been loaded. (If we don't do this then
- // when the prim moves it might think that it crossed into a region.)
- SceneManager.Instance.OnRegionsReadyStatusChange += delegate(SceneManager sm)
- {
- if (sm.AllRegionsReady)
- timer.Start();
- };
- }
-
- // Check again, in case the regions were started while we were adding the event handler
- if (SceneManager.Instance.AllRegionsReady)
- {
- timer.Start();
- }
- }
- }
- lock (timer.m_lockObject)
- {
- timer.m_motions[motion] = null;
- }
- }
- public static void Remove(KeyframeMotion motion)
- {
- KeyframeTimer timer;
- if (motion.Scene == null)
- return;
- lock (m_timers)
- {
- if (!m_timers.TryGetValue(motion.Scene, out timer))
- {
- return;
- }
- }
- lock (timer.m_lockObject)
- {
- timer.m_motions.Remove(motion);
- }
- }
- }
- [Serializable]
- 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 = 2,
- Rotation = 1
- }
- [Serializable]
- public struct Keyframe
- {
- public Vector3? Position;
- public Quaternion? Rotation;
- public Quaternion StartRotation;
- public int TimeMS;
- public int TimeTotal;
- public Vector3 AngularVelocity;
- public Vector3 StartPosition;
- };
- private Vector3 m_serializedPosition;
- private Vector3 m_basePosition;
- private Quaternion m_baseRotation;
- private Keyframe m_currentFrame;
- private List<Keyframe> m_frames = new List<Keyframe>();
- private Keyframe[] m_keyframes;
- // skip timer events.
- //timer.stop doesn't assure there aren't event threads still being fired
- [NonSerialized()]
- private bool m_timerStopped;
- [NonSerialized()]
- private bool m_isCrossing;
- [NonSerialized()]
- private bool m_waitingCrossing;
- // retry position for cross fail
- [NonSerialized()]
- private Vector3 m_nextPosition;
- [NonSerialized()]
- private SceneObjectGroup m_group;
- private PlayMode m_mode = PlayMode.Forward;
- private DataFormat m_data = DataFormat.Translation | DataFormat.Rotation;
- private bool m_running = false;
- [NonSerialized()]
- private bool m_selected = false;
- private int m_iterations = 0;
- private int m_skipLoops = 0;
- [NonSerialized()]
- private Scene m_scene;
- public Scene Scene
- {
- get { return m_scene; }
- }
- public DataFormat Data
- {
- get { return m_data; }
- }
- public bool Selected
- {
- set
- {
- if (m_group != null)
- {
- if (!value)
- {
- // Once we're let go, recompute positions
- if (m_selected)
- UpdateSceneObject(m_group);
- }
- else
- {
- // Save selection position in case we get moved
- if (!m_selected)
- {
- StopTimer();
- m_serializedPosition = m_group.AbsolutePosition;
- }
- }
- }
- m_isCrossing = false;
- m_waitingCrossing = false;
- m_selected = value;
- }
- }
- private void StartTimer()
- {
- KeyframeTimer.Add(this);
- m_timerStopped = false;
- }
- private void StopTimer()
- {
- m_timerStopped = true;
- KeyframeTimer.Remove(this);
- }
- public static KeyframeMotion FromData(SceneObjectGroup grp, Byte[] data)
- {
- KeyframeMotion newMotion = null;
- try
- {
- using (MemoryStream ms = new MemoryStream(data))
- {
- BinaryFormatter fmt = new BinaryFormatter();
- newMotion = (KeyframeMotion)fmt.Deserialize(ms);
- }
- newMotion.m_group = grp;
- if (grp != null)
- {
- newMotion.m_scene = grp.Scene;
- if (grp.IsSelected)
- newMotion.m_selected = true;
- }
- newMotion.m_timerStopped = false;
- newMotion.m_running = true;
- newMotion.m_isCrossing = false;
- newMotion.m_waitingCrossing = false;
- }
- catch
- {
- newMotion = null;
- }
- return newMotion;
- }
- public void UpdateSceneObject(SceneObjectGroup grp)
- {
- m_isCrossing = false;
- m_waitingCrossing = false;
- StopTimer();
- if (grp == null)
- return;
- m_group = grp;
- m_scene = grp.Scene;
- Vector3 grppos = grp.AbsolutePosition;
- Vector3 offset = grppos - m_serializedPosition;
- // avoid doing it more than once
- // current this will happen dragging a prim to other region
- m_serializedPosition = grppos;
- m_basePosition += offset;
- m_nextPosition += offset;
-
- m_currentFrame.StartPosition += offset;
- m_currentFrame.Position += offset;
- for (int i = 0; i < m_frames.Count; i++)
- {
- Keyframe k = m_frames[i];
- k.StartPosition += offset;
- k.Position += offset;
- m_frames[i]=k;
- }
- if (m_running)
- Start();
- }
- public KeyframeMotion(SceneObjectGroup grp, PlayMode mode, DataFormat data)
- {
- m_mode = mode;
- m_data = data;
- m_group = grp;
- if (grp != null)
- {
- m_basePosition = grp.AbsolutePosition;
- m_baseRotation = grp.GroupRotation;
- m_scene = grp.Scene;
- }
- m_timerStopped = true;
- m_isCrossing = false;
- m_waitingCrossing = false;
- }
- public void SetKeyframes(Keyframe[] frames)
- {
- m_keyframes = frames;
- }
- public KeyframeMotion Copy(SceneObjectGroup newgrp)
- {
- StopTimer();
- KeyframeMotion newmotion = new KeyframeMotion(null, m_mode, m_data);
- newmotion.m_group = newgrp;
- newmotion.m_scene = newgrp.Scene;
- if (m_keyframes != null)
- {
- newmotion.m_keyframes = new Keyframe[m_keyframes.Length];
- m_keyframes.CopyTo(newmotion.m_keyframes, 0);
- }
- newmotion.m_frames = new List<Keyframe>(m_frames);
- newmotion.m_basePosition = m_basePosition;
- newmotion.m_baseRotation = m_baseRotation;
- if (m_selected)
- newmotion.m_serializedPosition = m_serializedPosition;
- else
- {
- if (m_group != null)
- newmotion.m_serializedPosition = m_group.AbsolutePosition;
- else
- newmotion.m_serializedPosition = m_serializedPosition;
- }
- newmotion.m_currentFrame = m_currentFrame;
- newmotion.m_iterations = m_iterations;
- newmotion.m_running = m_running;
- if (m_running && !m_waitingCrossing)
- StartTimer();
- return newmotion;
- }
- public void Delete()
- {
- m_running = false;
- StopTimer();
- m_isCrossing = false;
- m_waitingCrossing = false;
- m_frames.Clear();
- m_keyframes = null;
- }
- public void Start()
- {
- m_isCrossing = false;
- m_waitingCrossing = false;
- if (m_keyframes != null && m_group != null && m_keyframes.Length > 0)
- {
- StartTimer();
- m_running = true;
- }
- else
- {
- m_running = false;
- StopTimer();
- }
- }
- public void Stop()
- {
- m_running = false;
- m_isCrossing = false;
- m_waitingCrossing = false;
- StopTimer();
- m_basePosition = m_group.AbsolutePosition;
- m_baseRotation = m_group.GroupRotation;
- m_group.RootPart.Velocity = Vector3.Zero;
- m_group.RootPart.AngularVelocity = Vector3.Zero;
- m_group.SendGroupRootTerseUpdate();
- // m_group.RootPart.ScheduleTerseUpdate();
- m_frames.Clear();
- }
- public void Pause()
- {
- m_running = false;
- StopTimer();
- m_group.RootPart.Velocity = Vector3.Zero;
- m_group.RootPart.AngularVelocity = Vector3.Zero;
- m_group.SendGroupRootTerseUpdate();
- // m_group.RootPart.ScheduleTerseUpdate();
- }
- 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 (direction < 0)
- {
- start = m_keyframes.Length - 1;
- end = -1;
- }
- for (int i = start; i != end ; i += direction)
- {
- Keyframe k = m_keyframes[i];
- k.StartPosition = pos;
- if (k.Position.HasValue)
- {
- k.Position = (k.Position * direction);
- // k.Velocity = (Vector3)k.Position / (k.TimeMS / 1000.0f);
- k.Position += pos;
- }
- else
- {
- k.Position = pos;
- // k.Velocity = Vector3.Zero;
- }
- 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;
- }
- /* ang vel not in use for now
- 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);
- }
- }
- 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++;
- }
- }
- public void OnTimer(double tickDuration)
- {
- if (m_skipLoops > 0)
- {
- m_skipLoops--;
- return;
- }
- if (m_timerStopped) // trap events still in air even after a timer.stop
- return;
- if (m_group == null)
- return;
- bool update = false;
- if (m_selected)
- {
- if (m_group.RootPart.Velocity != Vector3.Zero)
- {
- m_group.RootPart.Velocity = Vector3.Zero;
- m_group.SendGroupRootTerseUpdate();
- }
- return;
- }
- if (m_isCrossing)
- {
- // if crossing and timer running then cross failed
- // wait some time then
- // retry to set the position that evtually caused the outbound
- // if still outside region this will call startCrossing below
- m_isCrossing = false;
- m_group.AbsolutePosition = m_nextPosition;
- if (!m_isCrossing)
- {
- StopTimer();
- StartTimer();
- }
- return;
- }
- if (m_frames.Count == 0)
- {
- GetNextList();
- if (m_frames.Count == 0)
- {
- Stop();
- Scene scene = m_group.Scene;
- IScriptModule[] scriptModules = scene.RequestModuleInterfaces<IScriptModule>();
- foreach (IScriptModule m in scriptModules)
- {
- if (m == null)
- continue;
- m.PostObjectEvent(m_group.RootPart.UUID, "moving_end", new object[0]);
- }
- return;
- }
- m_currentFrame = m_frames[0];
- m_currentFrame.TimeMS += (int)tickDuration;
- //force a update on a keyframe transition
- update = true;
- }
- m_currentFrame.TimeMS -= (int)tickDuration;
- // Do the frame processing
- double remainingSteps = (double)m_currentFrame.TimeMS / tickDuration;
- if (remainingSteps <= 0.0)
- {
- m_group.RootPart.Velocity = Vector3.Zero;
- m_group.RootPart.AngularVelocity = Vector3.Zero;
- m_nextPosition = (Vector3)m_currentFrame.Position;
- m_group.AbsolutePosition = m_nextPosition;
- // we are sending imediate updates, no doing force a extra terseUpdate
- // m_group.UpdateGroupRotationR((Quaternion)m_currentFrame.Rotation);
- m_group.RootPart.RotationOffset = (Quaternion)m_currentFrame.Rotation;
- m_frames.RemoveAt(0);
- if (m_frames.Count > 0)
- m_currentFrame = m_frames[0];
- update = true;
- }
- else
- {
- float completed = ((float)m_currentFrame.TimeTotal - (float)m_currentFrame.TimeMS) / (float)m_currentFrame.TimeTotal;
- bool lastStep = m_currentFrame.TimeMS <= tickDuration;
- Vector3 positionThisStep = m_currentFrame.StartPosition + (m_currentFrame.Position.Value - m_currentFrame.StartPosition) * completed;
- Vector3 motionThisStep = positionThisStep - m_group.AbsolutePosition;
- float mag = Vector3.Mag(motionThisStep);
- if ((mag >= 0.02f) || lastStep)
- {
- m_nextPosition = m_group.AbsolutePosition + motionThisStep;
- m_group.AbsolutePosition = m_nextPosition;
- update = true;
- }
- //int totalSteps = m_currentFrame.TimeTotal / (int)tickDuration;
- //m_log.DebugFormat("KeyframeMotion.OnTimer: step {0}/{1}, curPosition={2}, finalPosition={3}, motionThisStep={4} (scene {5})",
- // totalSteps - remainingSteps + 1, totalSteps, m_group.AbsolutePosition, m_currentFrame.Position, motionThisStep, m_scene.RegionInfo.RegionName);
- if ((Quaternion)m_currentFrame.Rotation != m_group.GroupRotation)
- {
- Quaternion current = m_group.GroupRotation;
- Quaternion step = Quaternion.Slerp(m_currentFrame.StartRotation, (Quaternion)m_currentFrame.Rotation, completed);
- step.Normalize();
- /* use simpler change detection
- * 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)
- */
- if(Math.Abs(step.X - current.X) > 0.001f
- || Math.Abs(step.Y - current.Y) > 0.001f
- || Math.Abs(step.Z - current.Z) > 0.001f
- || lastStep)
- // assuming w is a dependente var
- {
- // m_group.UpdateGroupRotationR(step);
- m_group.RootPart.RotationOffset = step;
- //m_group.RootPart.UpdateAngularVelocity(m_currentFrame.AngularVelocity / 2);
- update = true;
- }
- }
- }
- if (update)
- {
- m_group.SendGroupRootTerseUpdate();
- }
- }
- public Byte[] Serialize()
- {
- StopTimer();
- SceneObjectGroup tmp = m_group;
- m_group = null;
- if (!m_selected && tmp != null)
- m_serializedPosition = tmp.AbsolutePosition;
- using (MemoryStream ms = new MemoryStream())
- {
- BinaryFormatter fmt = new BinaryFormatter();
- fmt.Serialize(ms, this);
- m_group = tmp;
- if (m_running && !m_waitingCrossing)
- StartTimer();
- return ms.ToArray();
- }
- }
- public void StartCrossingCheck()
- {
- // timer will be restart by crossingFailure
- // or never since crossing worked and this
- // should be deleted
- StopTimer();
-
- m_isCrossing = true;
- m_waitingCrossing = true;
- // to remove / retune to smoth crossings
- if (m_group.RootPart.Velocity != Vector3.Zero)
- {
- m_group.RootPart.Velocity = Vector3.Zero;
- m_group.SendGroupRootTerseUpdate();
- // m_group.RootPart.ScheduleTerseUpdate();
- }
- }
- public void CrossingFailure()
- {
- m_waitingCrossing = false;
- if (m_group != null)
- {
- m_group.RootPart.Velocity = Vector3.Zero;
- m_group.SendGroupRootTerseUpdate();
- // m_group.RootPart.ScheduleTerseUpdate();
- if (m_running)
- {
- StopTimer();
- m_skipLoops = 1200; // 60 seconds
- StartTimer();
- }
- }
- }
- }
- }
|