OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
KeyframeMotion.cs
Go to the documentation of this file.
1 /*
2  * Copyright (c) Contributors, http://opensimulator.org/
3  * See CONTRIBUTORS.TXT for a full list of copyright holders.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of the OpenSimulator Project nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 using System;
29 using System.Timers;
30 using System.Collections;
31 using System.Collections.Generic;
32 using System.IO;
33 using System.Diagnostics;
34 using System.Reflection;
35 using System.Threading;
36 using OpenMetaverse;
37 using OpenSim.Framework;
38 using OpenSim.Region.Framework.Interfaces;
39 using OpenSim.Region.PhysicsModules.SharedBase;
40 using OpenSim.Region.Framework.Scenes.Serialization;
41 using System.Runtime.Serialization.Formatters.Binary;
42 using System.Runtime.Serialization;
44 using log4net;
45 
46 namespace OpenSim.Region.Framework.Scenes
47 {
48  public class KeyframeTimer
49  {
50  private static Dictionary<Scene, KeyframeTimer> m_timers =
51  new Dictionary<Scene, KeyframeTimer>();
52 
53  private Timer m_timer;
54  private Dictionary<KeyframeMotion, object> m_motions = new Dictionary<KeyframeMotion, object>();
55  private object m_lockObject = new object();
56  private object m_timerLock = new object();
57  private const double m_tickDuration = 50.0;
58 
59  public double TickDuration
60  {
61  get { return m_tickDuration; }
62  }
63 
64  public KeyframeTimer(Scene scene)
65  {
66  m_timer = new Timer();
67  m_timer.Interval = TickDuration;
68  m_timer.AutoReset = true;
69  m_timer.Elapsed += OnTimer;
70  }
71 
72  public void Start()
73  {
74  lock (m_timer)
75  {
76  if (!m_timer.Enabled)
77  m_timer.Start();
78  }
79  }
80 
81  private void OnTimer(object sender, ElapsedEventArgs ea)
82  {
83  if (!Monitor.TryEnter(m_timerLock))
84  return;
85 
86  try
87  {
88  List<KeyframeMotion> motions;
89 
90  lock (m_lockObject)
91  {
92  motions = new List<KeyframeMotion>(m_motions.Keys);
93  }
94 
95  foreach (KeyframeMotion m in motions)
96  {
97  try
98  {
99  m.OnTimer(TickDuration);
100  }
101  catch (Exception)
102  {
103  // Don't stop processing
104  }
105  }
106  }
107  catch (Exception)
108  {
109  // Keep running no matter what
110  }
111  finally
112  {
113  Monitor.Exit(m_timerLock);
114  }
115  }
116 
117  public static void Add(KeyframeMotion motion)
118  {
120 
121  if (motion.Scene == null)
122  return;
123 
124  lock (m_timers)
125  {
126  if (!m_timers.TryGetValue(motion.Scene, out timer))
127  {
128  timer = new KeyframeTimer(motion.Scene);
129  m_timers[motion.Scene] = timer;
130 
132  {
133  // Start the timers only once all the regions are ready. This is required
134  // when using megaregions, because the megaregion is correctly configured
135  // only after all the regions have been loaded. (If we don't do this then
136  // when the prim moves it might think that it crossed into a region.)
137  SceneManager.Instance.OnRegionsReadyStatusChange += delegate(SceneManager sm)
138  {
139  if (sm.AllRegionsReady)
140  timer.Start();
141  };
142  }
143 
144  // Check again, in case the regions were started while we were adding the event handler
146  {
147  timer.Start();
148  }
149  }
150  }
151 
152  lock (timer.m_lockObject)
153  {
154  timer.m_motions[motion] = null;
155  }
156  }
157 
158  public static void Remove(KeyframeMotion motion)
159  {
161 
162  if (motion.Scene == null)
163  return;
164 
165  lock (m_timers)
166  {
167  if (!m_timers.TryGetValue(motion.Scene, out timer))
168  {
169  return;
170  }
171  }
172 
173  lock (timer.m_lockObject)
174  {
175  timer.m_motions.Remove(motion);
176  }
177  }
178  }
179 
180  [Serializable]
181  public class KeyframeMotion
182  {
183  //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
184 
185  public enum PlayMode : int
186  {
187  Forward = 0,
188  Reverse = 1,
189  Loop = 2,
190  PingPong = 3
191  };
192 
193  [Flags]
194  public enum DataFormat : int
195  {
196  Translation = 2,
197  Rotation = 1
198  }
199 
200  [Serializable]
201  public struct Keyframe
202  {
203  public Vector3? Position;
204  public Quaternion? Rotation;
205  public Quaternion StartRotation;
206  public int TimeMS;
207  public int TimeTotal;
208  public Vector3 AngularVelocity;
209  public Vector3 StartPosition;
210  };
211 
212  private Vector3 m_serializedPosition;
213  private Vector3 m_basePosition;
214  private Quaternion m_baseRotation;
215 
216  private Keyframe m_currentFrame;
217 
218  private List<Keyframe> m_frames = new List<Keyframe>();
219 
220  private Keyframe[] m_keyframes;
221 
222  // skip timer events.
223  //timer.stop doesn't assure there aren't event threads still being fired
224  [NonSerialized()]
225  private bool m_timerStopped;
226 
227  [NonSerialized()]
228  private bool m_isCrossing;
229 
230  [NonSerialized()]
231  private bool m_waitingCrossing;
232 
233  // retry position for cross fail
234  [NonSerialized()]
235  private Vector3 m_nextPosition;
236 
237  [NonSerialized()]
238  private SceneObjectGroup m_group;
239 
240  private PlayMode m_mode = PlayMode.Forward;
241  private DataFormat m_data = DataFormat.Translation | DataFormat.Rotation;
242 
243  private bool m_running = false;
244 
245  [NonSerialized()]
246  private bool m_selected = false;
247 
248  private int m_iterations = 0;
249 
250  private int m_skipLoops = 0;
251 
252  [NonSerialized()]
253  private Scene m_scene;
254 
255  public Scene Scene
256  {
257  get { return m_scene; }
258  }
259 
260  public DataFormat Data
261  {
262  get { return m_data; }
263  }
264 
265  public bool Selected
266  {
267  set
268  {
269  if (m_group != null)
270  {
271  if (!value)
272  {
273  // Once we're let go, recompute positions
274  if (m_selected)
275  UpdateSceneObject(m_group);
276  }
277  else
278  {
279  // Save selection position in case we get moved
280  if (!m_selected)
281  {
282  StopTimer();
283  m_serializedPosition = m_group.AbsolutePosition;
284  }
285  }
286  }
287  m_isCrossing = false;
288  m_waitingCrossing = false;
289  m_selected = value;
290  }
291  }
292 
293  private void StartTimer()
294  {
295  lock (m_frames)
296  {
297  KeyframeTimer.Add(this);
298  m_timerStopped = false;
299  }
300  }
301 
302  private void StopTimer()
303  {
304  lock (m_frames)
305  m_timerStopped = true;
306  }
307 
308  public static KeyframeMotion FromData(SceneObjectGroup grp, Byte[] data)
309  {
310  KeyframeMotion newMotion = null;
311 
312  try
313  {
314  using (MemoryStream ms = new MemoryStream(data))
315  {
316  BinaryFormatter fmt = new BinaryFormatter();
317  newMotion = (KeyframeMotion)fmt.Deserialize(ms);
318  }
319 
320  newMotion.m_group = grp;
321 
322  if (grp != null)
323  {
324  newMotion.m_scene = grp.Scene;
325  if (grp.IsSelected)
326  newMotion.m_selected = true;
327  }
328 
329  newMotion.m_timerStopped = false;
330  newMotion.m_running = true;
331  newMotion.m_isCrossing = false;
332  newMotion.m_waitingCrossing = false;
333  }
334  catch
335  {
336  newMotion = null;
337  }
338 
339  return newMotion;
340  }
341 
343  {
344  m_isCrossing = false;
345  m_waitingCrossing = false;
346  StopTimer();
347 
348  if (grp == null)
349  return;
350 
351  m_group = grp;
352  m_scene = grp.Scene;
353 
354 
355  lock (m_frames)
356  {
357  Vector3 grppos = grp.AbsolutePosition;
358  Vector3 offset = grppos - m_serializedPosition;
359  // avoid doing it more than once
360  // current this will happen draging a prim to other region
361  m_serializedPosition = grppos;
362 
363  m_basePosition += offset;
364  m_currentFrame.Position += offset;
365 
366  m_nextPosition += offset;
367 
368  for (int i = 0; i < m_frames.Count; i++)
369  {
370  Keyframe k = m_frames[i];
371  k.Position += offset;
372  m_frames[i] = k;
373  }
374  }
375 
376  if (m_running)
377  Start();
378  }
379 
381  {
382  m_mode = mode;
383  m_data = data;
384 
385  m_group = grp;
386  if (grp != null)
387  {
388  m_basePosition = grp.AbsolutePosition;
389  m_baseRotation = grp.GroupRotation;
390  m_scene = grp.Scene;
391  }
392 
393  m_timerStopped = true;
394  m_isCrossing = false;
395  m_waitingCrossing = false;
396  }
397 
398  public void SetKeyframes(Keyframe[] frames)
399  {
400  m_keyframes = frames;
401  }
402 
404  {
405  StopTimer();
406 
407  KeyframeMotion newmotion = new KeyframeMotion(null, m_mode, m_data);
408 
409  newmotion.m_group = newgrp;
410  newmotion.m_scene = newgrp.Scene;
411 
412  if (m_keyframes != null)
413  {
414  newmotion.m_keyframes = new Keyframe[m_keyframes.Length];
415  m_keyframes.CopyTo(newmotion.m_keyframes, 0);
416  }
417 
418  lock (m_frames)
419  {
420  newmotion.m_frames = new List<Keyframe>(m_frames);
421 
422  newmotion.m_basePosition = m_basePosition;
423  newmotion.m_baseRotation = m_baseRotation;
424 
425  if (m_selected)
426  newmotion.m_serializedPosition = m_serializedPosition;
427  else
428  {
429  if (m_group != null)
430  newmotion.m_serializedPosition = m_group.AbsolutePosition;
431  else
432  newmotion.m_serializedPosition = m_serializedPosition;
433  }
434 
435  newmotion.m_currentFrame = m_currentFrame;
436 
437  newmotion.m_iterations = m_iterations;
438  newmotion.m_running = m_running;
439  }
440 
441  if (m_running && !m_waitingCrossing)
442  StartTimer();
443 
444  return newmotion;
445  }
446 
447  public void Delete()
448  {
449  m_running = false;
450  StopTimer();
451  m_isCrossing = false;
452  m_waitingCrossing = false;
453  m_frames.Clear();
454  m_keyframes = null;
455  }
456 
457  public void Start()
458  {
459  m_isCrossing = false;
460  m_waitingCrossing = false;
461  if (m_keyframes != null && m_group != null && m_keyframes.Length > 0)
462  {
463  StartTimer();
464  m_running = true;
465  m_group.Scene.EventManager.TriggerMovingStartEvent(m_group.RootPart.LocalId);
466  }
467  else
468  {
469  StopTimer();
470  m_running = false;
471  }
472  }
473 
474  public void Stop()
475  {
476  StopTimer();
477  m_running = false;
478  m_isCrossing = false;
479  m_waitingCrossing = false;
480 
481  m_basePosition = m_group.AbsolutePosition;
482  m_baseRotation = m_group.GroupRotation;
483 
484  m_group.RootPart.Velocity = Vector3.Zero;
485  m_group.RootPart.AngularVelocity = Vector3.Zero;
486  m_group.SendGroupRootTerseUpdate();
487 // m_group.RootPart.ScheduleTerseUpdate();
488  m_frames.Clear();
489  }
490 
491  public void Pause()
492  {
493  StopTimer();
494  m_running = false;
495 
496  m_group.RootPart.Velocity = Vector3.Zero;
497  m_group.RootPart.AngularVelocity = Vector3.Zero;
498  m_group.SendGroupRootTerseUpdate();
499 // m_group.RootPart.ScheduleTerseUpdate();
500  }
501 
502  public void Suspend()
503  {
504  lock (m_frames)
505  {
506  if (m_timerStopped)
507  return;
508  m_timerStopped = true;
509  }
510  }
511 
512  public void Resume()
513  {
514  lock (m_frames)
515  {
516  if (!m_timerStopped)
517  return;
518  if (m_running && !m_waitingCrossing)
519  StartTimer();
520  }
521  }
522 
523  private void GetNextList()
524  {
525  m_frames.Clear();
526  Vector3 pos = m_basePosition;
527  Quaternion rot = m_baseRotation;
528 
529  if (m_mode == PlayMode.Loop || m_mode == PlayMode.PingPong || m_iterations == 0)
530  {
531  int direction = 1;
532  if (m_mode == PlayMode.Reverse || ((m_mode == PlayMode.PingPong) && ((m_iterations & 1) != 0)))
533  direction = -1;
534 
535  int start = 0;
536  int end = m_keyframes.Length;
537 
538  if (direction < 0)
539  {
540  start = m_keyframes.Length - 1;
541  end = -1;
542  }
543 
544  for (int i = start; i != end ; i += direction)
545  {
546  Keyframe k = m_keyframes[i];
547 
548  k.StartPosition = pos;
549  if (k.Position.HasValue)
550  {
551  k.Position = (k.Position * direction);
552 // k.Velocity = (Vector3)k.Position / (k.TimeMS / 1000.0f);
553  k.Position += pos;
554  }
555  else
556  {
557  k.Position = pos;
558 // k.Velocity = Vector3.Zero;
559  }
560 
561  k.StartRotation = rot;
562  if (k.Rotation.HasValue)
563  {
564  if (direction == -1)
565  k.Rotation = Quaternion.Conjugate((Quaternion)k.Rotation);
566  k.Rotation = rot * k.Rotation;
567  }
568  else
569  {
570  k.Rotation = rot;
571  }
572 
573 /* ang vel not in use for now
574 
575  float angle = 0;
576 
577  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;
578  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;
579  float aa_bb = aa * bb;
580 
581  if (aa_bb == 0)
582  {
583  angle = 0;
584  }
585  else
586  {
587  float ab = k.StartRotation.X * ((Quaternion)k.Rotation).X +
588  k.StartRotation.Y * ((Quaternion)k.Rotation).Y +
589  k.StartRotation.Z * ((Quaternion)k.Rotation).Z +
590  k.StartRotation.W * ((Quaternion)k.Rotation).W;
591  float q = (ab * ab) / aa_bb;
592 
593  if (q > 1.0f)
594  {
595  angle = 0;
596  }
597  else
598  {
599  angle = (float)Math.Acos(2 * q - 1);
600  }
601  }
602 
603  k.AngularVelocity = (new Vector3(0, 0, 1) * (Quaternion)k.Rotation) * (angle / (k.TimeMS / 1000));
604  */
605  k.TimeTotal = k.TimeMS;
606 
607  m_frames.Add(k);
608 
609  pos = (Vector3)k.Position;
610  rot = (Quaternion)k.Rotation;
611 
612  }
613 
614  m_basePosition = pos;
615  m_baseRotation = rot;
616 
617  m_iterations++;
618  }
619  }
620 
621  public void OnTimer(double tickDuration)
622  {
623  if (!Monitor.TryEnter(m_frames))
624  return;
625  if (m_timerStopped)
626  KeyframeTimer.Remove(this);
627  else
628  DoOnTimer(tickDuration);
629  Monitor.Exit(m_frames);
630  }
631 
632  private void Done()
633  {
634  KeyframeTimer.Remove(this);
635  m_timerStopped = true;
636  m_running = false;
637  m_isCrossing = false;
638  m_waitingCrossing = false;
639 
640  m_basePosition = m_group.AbsolutePosition;
641  m_baseRotation = m_group.GroupRotation;
642 
643  m_group.RootPart.Velocity = Vector3.Zero;
644  m_group.RootPart.AngularVelocity = Vector3.Zero;
645  m_group.SendGroupRootTerseUpdate();
646  // m_group.RootPart.ScheduleTerseUpdate();
647  m_frames.Clear();
648  }
649 
650  private void DoOnTimer(double tickDuration)
651  {
652  if (m_skipLoops > 0)
653  {
654  m_skipLoops--;
655  return;
656  }
657 
658  if (m_group == null)
659  return;
660 
661  bool update = false;
662 
663  if (m_selected)
664  {
665  if (m_group.RootPart.Velocity != Vector3.Zero)
666  {
667  m_group.RootPart.Velocity = Vector3.Zero;
668  m_group.SendGroupRootTerseUpdate();
669  }
670  return;
671  }
672 
673  if (m_isCrossing)
674  {
675  // if crossing and timer running then cross failed
676  // wait some time then
677  // retry to set the position that evtually caused the outbound
678  // if still outside region this will call startCrossing below
679  m_isCrossing = false;
680  m_group.AbsolutePosition = m_nextPosition;
681  if (!m_isCrossing)
682  {
683  StopTimer();
684  StartTimer();
685  }
686  return;
687  }
688 
689  if (m_frames.Count == 0)
690  {
691  lock (m_frames)
692  {
693  GetNextList();
694 
695  if (m_frames.Count == 0)
696  {
697  Done();
698  m_group.Scene.EventManager.TriggerMovingEndEvent(m_group.RootPart.LocalId);
699  return;
700  }
701 
702  m_currentFrame = m_frames[0];
703  m_currentFrame.TimeMS += (int)tickDuration;
704  }
705  //force a update on a keyframe transition
706  m_nextPosition = m_group.AbsolutePosition;
707  update = true;
708  }
709 
710  m_currentFrame.TimeMS -= (int)tickDuration;
711 
712  // Do the frame processing
713  double remainingSteps = (double)m_currentFrame.TimeMS / tickDuration;
714 
715  if (remainingSteps <= 0.0)
716  {
717  m_group.RootPart.Velocity = Vector3.Zero;
718  m_group.RootPart.AngularVelocity = Vector3.Zero;
719 
720  m_nextPosition = (Vector3)m_currentFrame.Position;
721  m_group.AbsolutePosition = m_nextPosition;
722 
723  // we are sending imediate updates, no doing force a extra terseUpdate
724  // m_group.UpdateGroupRotationR((Quaternion)m_currentFrame.Rotation);
725 
726  m_group.RootPart.RotationOffset = (Quaternion)m_currentFrame.Rotation;
727 
728  lock (m_frames)
729  {
730  m_frames.RemoveAt(0);
731  if (m_frames.Count > 0)
732  m_currentFrame = m_frames[0];
733  }
734 
735  update = true;
736  }
737  else
738  {
739  float completed = ((float)m_currentFrame.TimeTotal - (float)m_currentFrame.TimeMS) / (float)m_currentFrame.TimeTotal;
740  bool lastStep = m_currentFrame.TimeMS <= tickDuration;
741 
742  Vector3 v = (Vector3)m_currentFrame.Position - m_group.AbsolutePosition;
743  Vector3 motionThisFrame = v / (float)remainingSteps;
744  v = v * 1000 / m_currentFrame.TimeMS;
745 
746  m_nextPosition = m_group.AbsolutePosition + motionThisFrame;
747 
748  if (Vector3.Mag(motionThisFrame) >= 0.05f)
749  update = true;
750 
751  //int totalSteps = m_currentFrame.TimeTotal / (int)tickDuration;
752  //m_log.DebugFormat("KeyframeMotion.OnTimer: step {0}/{1}, curPosition={2}, finalPosition={3}, motionThisStep={4} (scene {5})",
753  // totalSteps - remainingSteps + 1, totalSteps, m_group.AbsolutePosition, m_currentFrame.Position, motionThisStep, m_scene.RegionInfo.RegionName);
754 
755  if ((Quaternion)m_currentFrame.Rotation != m_group.GroupRotation)
756  {
757  Quaternion current = m_group.GroupRotation;
758 
759  Quaternion step = Quaternion.Slerp(m_currentFrame.StartRotation, (Quaternion)m_currentFrame.Rotation, completed);
760  step.Normalize();
761  /* use simpler change detection
762  * float angle = 0;
763 
764  float aa = current.X * current.X + current.Y * current.Y + current.Z * current.Z + current.W * current.W;
765  float bb = step.X * step.X + step.Y * step.Y + step.Z * step.Z + step.W * step.W;
766  float aa_bb = aa * bb;
767 
768  if (aa_bb == 0)
769  {
770  angle = 0;
771  }
772  else
773  {
774  float ab = current.X * step.X +
775  current.Y * step.Y +
776  current.Z * step.Z +
777  current.W * step.W;
778  float q = (ab * ab) / aa_bb;
779 
780  if (q > 1.0f)
781  {
782  angle = 0;
783  }
784  else
785  {
786  angle = (float)Math.Acos(2 * q - 1);
787  }
788  }
789 
790  if (angle > 0.01f)
791  */
792  if (Math.Abs(step.X - current.X) > 0.001f
793  || Math.Abs(step.Y - current.Y) > 0.001f
794  || Math.Abs(step.Z - current.Z) > 0.001f)
795  // assuming w is a dependente var
796  {
797 // m_group.UpdateGroupRotationR(step);
798  m_group.RootPart.RotationOffset = step;
799 
800  //m_group.RootPart.UpdateAngularVelocity(m_currentFrame.AngularVelocity / 2);
801  update = true;
802  }
803  }
804  }
805 
806  if (update)
807  {
808  m_group.AbsolutePosition = m_nextPosition;
809  m_group.SendGroupRootTerseUpdate();
810  }
811  }
812 
813  public Byte[] Serialize()
814  {
815  bool timerWasStopped;
816  lock (m_frames)
817  {
818  timerWasStopped = m_timerStopped;
819  }
820  StopTimer();
821 
822  SceneObjectGroup tmp = m_group;
823  m_group = null;
824 
825  using (MemoryStream ms = new MemoryStream())
826  {
827  BinaryFormatter fmt = new BinaryFormatter();
828  if (!m_selected && tmp != null)
829  m_serializedPosition = tmp.AbsolutePosition;
830  fmt.Serialize(ms, this);
831  m_group = tmp;
832  if (!timerWasStopped && m_running && !m_waitingCrossing)
833  StartTimer();
834 
835  return ms.ToArray();
836  }
837  }
838 
839  public void StartCrossingCheck()
840  {
841  // timer will be restart by crossingFailure
842  // or never since crossing worked and this
843  // should be deleted
844  StopTimer();
845 
846  m_isCrossing = true;
847  m_waitingCrossing = true;
848 
849  // to remove / retune to smoth crossings
850  if (m_group.RootPart.Velocity != Vector3.Zero)
851  {
852  m_group.RootPart.Velocity = Vector3.Zero;
853  m_group.SendGroupRootTerseUpdate();
854 // m_group.RootPart.ScheduleTerseUpdate();
855  }
856  }
857 
858  public void CrossingFailure()
859  {
860  m_waitingCrossing = false;
861 
862  if (m_group != null)
863  {
864  m_group.RootPart.Velocity = Vector3.Zero;
865  m_group.SendGroupRootTerseUpdate();
866 // m_group.RootPart.ScheduleTerseUpdate();
867 
868  if (m_running)
869  {
870  StopTimer();
871  m_skipLoops = 1200; // 60 seconds
872  StartTimer();
873  }
874  }
875  }
876  }
877 }
static void Remove(KeyframeMotion motion)
bool AllRegionsReady
Are all regions ready for use?
Definition: SceneManager.cs:60
System.Timers.Timer Timer
A scene object group is conceptually an object in the scene. The object is constituted of SceneObject...
System.Timers.Timer Timer
Manager for adding, closing and restarting scenes.
Definition: SceneManager.cs:44
static void Add(KeyframeMotion motion)
KeyframeMotion(SceneObjectGroup grp, PlayMode mode, DataFormat data)
KeyframeMotion Copy(SceneObjectGroup newgrp)