clockwerk-opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 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.Physics.Manager;
40 using OpenSim.Region.Framework.Scenes.Serialization;
41 using System.Runtime.Serialization.Formatters.Binary;
42 using System.Runtime.Serialization;
43 using Timer = System.Timers.Timer;
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 {
119 KeyframeTimer timer;
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  
131 if (!SceneManager.Instance.AllRegionsReady)
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
145 if (SceneManager.Instance.AllRegionsReady)
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 {
160 KeyframeTimer timer;
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 KeyframeTimer.Add(this);
296 m_timerStopped = false;
297 }
298  
299 private void StopTimer()
300 {
301 m_timerStopped = true;
302 KeyframeTimer.Remove(this);
303 }
304  
305 public static KeyframeMotion FromData(SceneObjectGroup grp, Byte[] data)
306 {
307 KeyframeMotion newMotion = null;
308  
309 try
310 {
311 using (MemoryStream ms = new MemoryStream(data))
312 {
313 BinaryFormatter fmt = new BinaryFormatter();
314 newMotion = (KeyframeMotion)fmt.Deserialize(ms);
315 }
316  
317 newMotion.m_group = grp;
318  
319 if (grp != null)
320 {
321 newMotion.m_scene = grp.Scene;
322 if (grp.IsSelected)
323 newMotion.m_selected = true;
324 }
325  
326 newMotion.m_timerStopped = false;
327 newMotion.m_running = true;
328 newMotion.m_isCrossing = false;
329 newMotion.m_waitingCrossing = false;
330 }
331 catch
332 {
333 newMotion = null;
334 }
335  
336 return newMotion;
337 }
338  
339 public void UpdateSceneObject(SceneObjectGroup grp)
340 {
341 m_isCrossing = false;
342 m_waitingCrossing = false;
343 StopTimer();
344  
345 if (grp == null)
346 return;
347  
348 m_group = grp;
349 m_scene = grp.Scene;
350  
351 Vector3 grppos = grp.AbsolutePosition;
352 Vector3 offset = grppos - m_serializedPosition;
353 // avoid doing it more than once
354 // current this will happen dragging a prim to other region
355 m_serializedPosition = grppos;
356  
357 m_basePosition += offset;
358 m_nextPosition += offset;
359  
360 m_currentFrame.StartPosition += offset;
361 m_currentFrame.Position += offset;
362  
363 for (int i = 0; i < m_frames.Count; i++)
364 {
365 Keyframe k = m_frames[i];
366 k.StartPosition += offset;
367 k.Position += offset;
368 m_frames[i]=k;
369 }
370  
371 if (m_running)
372 Start();
373 }
374  
375 public KeyframeMotion(SceneObjectGroup grp, PlayMode mode, DataFormat data)
376 {
377 m_mode = mode;
378 m_data = data;
379  
380 m_group = grp;
381 if (grp != null)
382 {
383 m_basePosition = grp.AbsolutePosition;
384 m_baseRotation = grp.GroupRotation;
385 m_scene = grp.Scene;
386 }
387  
388 m_timerStopped = true;
389 m_isCrossing = false;
390 m_waitingCrossing = false;
391 }
392  
393 public void SetKeyframes(Keyframe[] frames)
394 {
395 m_keyframes = frames;
396 }
397  
398 public KeyframeMotion Copy(SceneObjectGroup newgrp)
399 {
400 StopTimer();
401  
402 KeyframeMotion newmotion = new KeyframeMotion(null, m_mode, m_data);
403  
404 newmotion.m_group = newgrp;
405 newmotion.m_scene = newgrp.Scene;
406  
407 if (m_keyframes != null)
408 {
409 newmotion.m_keyframes = new Keyframe[m_keyframes.Length];
410 m_keyframes.CopyTo(newmotion.m_keyframes, 0);
411 }
412  
413 newmotion.m_frames = new List<Keyframe>(m_frames);
414  
415 newmotion.m_basePosition = m_basePosition;
416 newmotion.m_baseRotation = m_baseRotation;
417  
418 if (m_selected)
419 newmotion.m_serializedPosition = m_serializedPosition;
420 else
421 {
422 if (m_group != null)
423 newmotion.m_serializedPosition = m_group.AbsolutePosition;
424 else
425 newmotion.m_serializedPosition = m_serializedPosition;
426 }
427  
428 newmotion.m_currentFrame = m_currentFrame;
429  
430 newmotion.m_iterations = m_iterations;
431 newmotion.m_running = m_running;
432  
433 if (m_running && !m_waitingCrossing)
434 StartTimer();
435  
436 return newmotion;
437 }
438  
439 public void Delete()
440 {
441 m_running = false;
442 StopTimer();
443 m_isCrossing = false;
444 m_waitingCrossing = false;
445 m_frames.Clear();
446 m_keyframes = null;
447 }
448  
449 public void Start()
450 {
451 m_isCrossing = false;
452 m_waitingCrossing = false;
453 if (m_keyframes != null && m_group != null && m_keyframes.Length > 0)
454 {
455 StartTimer();
456 m_running = true;
457 }
458 else
459 {
460 m_running = false;
461 StopTimer();
462 }
463 }
464  
465 public void Stop()
466 {
467 m_running = false;
468 m_isCrossing = false;
469 m_waitingCrossing = false;
470  
471 StopTimer();
472  
473 m_basePosition = m_group.AbsolutePosition;
474 m_baseRotation = m_group.GroupRotation;
475  
476 m_group.RootPart.Velocity = Vector3.Zero;
477 m_group.RootPart.AngularVelocity = Vector3.Zero;
478 m_group.SendGroupRootTerseUpdate();
479 // m_group.RootPart.ScheduleTerseUpdate();
480 m_frames.Clear();
481 }
482  
483 public void Pause()
484 {
485 m_running = false;
486 StopTimer();
487  
488 m_group.RootPart.Velocity = Vector3.Zero;
489 m_group.RootPart.AngularVelocity = Vector3.Zero;
490 m_group.SendGroupRootTerseUpdate();
491 // m_group.RootPart.ScheduleTerseUpdate();
492  
493 }
494  
495 private void GetNextList()
496 {
497 m_frames.Clear();
498 Vector3 pos = m_basePosition;
499 Quaternion rot = m_baseRotation;
500  
501 if (m_mode == PlayMode.Loop || m_mode == PlayMode.PingPong || m_iterations == 0)
502 {
503 int direction = 1;
504 if (m_mode == PlayMode.Reverse || ((m_mode == PlayMode.PingPong) && ((m_iterations & 1) != 0)))
505 direction = -1;
506  
507 int start = 0;
508 int end = m_keyframes.Length;
509  
510 if (direction < 0)
511 {
512 start = m_keyframes.Length - 1;
513 end = -1;
514 }
515  
516 for (int i = start; i != end ; i += direction)
517 {
518 Keyframe k = m_keyframes[i];
519  
520 k.StartPosition = pos;
521 if (k.Position.HasValue)
522 {
523 k.Position = (k.Position * direction);
524 // k.Velocity = (Vector3)k.Position / (k.TimeMS / 1000.0f);
525 k.Position += pos;
526 }
527 else
528 {
529 k.Position = pos;
530 // k.Velocity = Vector3.Zero;
531 }
532  
533 k.StartRotation = rot;
534 if (k.Rotation.HasValue)
535 {
536 if (direction == -1)
537 k.Rotation = Quaternion.Conjugate((Quaternion)k.Rotation);
538 k.Rotation = rot * k.Rotation;
539 }
540 else
541 {
542 k.Rotation = rot;
543 }
544  
545 /* ang vel not in use for now
546  
547 float angle = 0;
548  
549 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;
550 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;
551 float aa_bb = aa * bb;
552  
553 if (aa_bb == 0)
554 {
555 angle = 0;
556 }
557 else
558 {
559 float ab = k.StartRotation.X * ((Quaternion)k.Rotation).X +
560 k.StartRotation.Y * ((Quaternion)k.Rotation).Y +
561 k.StartRotation.Z * ((Quaternion)k.Rotation).Z +
562 k.StartRotation.W * ((Quaternion)k.Rotation).W;
563 float q = (ab * ab) / aa_bb;
564  
565 if (q > 1.0f)
566 {
567 angle = 0;
568 }
569 else
570 {
571 angle = (float)Math.Acos(2 * q - 1);
572 }
573 }
574  
575 k.AngularVelocity = (new Vector3(0, 0, 1) * (Quaternion)k.Rotation) * (angle / (k.TimeMS / 1000));
576 */
577 k.TimeTotal = k.TimeMS;
578  
579 m_frames.Add(k);
580  
581 pos = (Vector3)k.Position;
582 rot = (Quaternion)k.Rotation;
583 }
584  
585 m_basePosition = pos;
586 m_baseRotation = rot;
587  
588 m_iterations++;
589 }
590 }
591  
592 public void OnTimer(double tickDuration)
593 {
594 if (m_skipLoops > 0)
595 {
596 m_skipLoops--;
597 return;
598 }
599  
600 if (m_timerStopped) // trap events still in air even after a timer.stop
601 return;
602  
603 if (m_group == null)
604 return;
605  
606 bool update = false;
607  
608 if (m_selected)
609 {
610 if (m_group.RootPart.Velocity != Vector3.Zero)
611 {
612 m_group.RootPart.Velocity = Vector3.Zero;
613 m_group.SendGroupRootTerseUpdate();
614  
615 }
616 return;
617 }
618  
619 if (m_isCrossing)
620 {
621 // if crossing and timer running then cross failed
622 // wait some time then
623 // retry to set the position that evtually caused the outbound
624 // if still outside region this will call startCrossing below
625 m_isCrossing = false;
626 m_group.AbsolutePosition = m_nextPosition;
627 if (!m_isCrossing)
628 {
629 StopTimer();
630 StartTimer();
631 }
632 return;
633 }
634  
635 if (m_frames.Count == 0)
636 {
637 GetNextList();
638  
639 if (m_frames.Count == 0)
640 {
641 Stop();
642 Scene scene = m_group.Scene;
643  
644 IScriptModule[] scriptModules = scene.RequestModuleInterfaces<IScriptModule>();
645 foreach (IScriptModule m in scriptModules)
646 {
647 if (m == null)
648 continue;
649 m.PostObjectEvent(m_group.RootPart.UUID, "moving_end", new object[0]);
650 }
651  
652 return;
653 }
654  
655 m_currentFrame = m_frames[0];
656 m_currentFrame.TimeMS += (int)tickDuration;
657  
658 //force a update on a keyframe transition
659 update = true;
660 }
661  
662 m_currentFrame.TimeMS -= (int)tickDuration;
663  
664 // Do the frame processing
665 double remainingSteps = (double)m_currentFrame.TimeMS / tickDuration;
666  
667 if (remainingSteps <= 0.0)
668 {
669 m_group.RootPart.Velocity = Vector3.Zero;
670 m_group.RootPart.AngularVelocity = Vector3.Zero;
671  
672 m_nextPosition = (Vector3)m_currentFrame.Position;
673 m_group.AbsolutePosition = m_nextPosition;
674  
675 // we are sending imediate updates, no doing force a extra terseUpdate
676 // m_group.UpdateGroupRotationR((Quaternion)m_currentFrame.Rotation);
677  
678 m_group.RootPart.RotationOffset = (Quaternion)m_currentFrame.Rotation;
679 m_frames.RemoveAt(0);
680 if (m_frames.Count > 0)
681 m_currentFrame = m_frames[0];
682  
683 update = true;
684 }
685 else
686 {
687 float completed = ((float)m_currentFrame.TimeTotal - (float)m_currentFrame.TimeMS) / (float)m_currentFrame.TimeTotal;
688 bool lastStep = m_currentFrame.TimeMS <= tickDuration;
689  
690 Vector3 positionThisStep = m_currentFrame.StartPosition + (m_currentFrame.Position.Value - m_currentFrame.StartPosition) * completed;
691 Vector3 motionThisStep = positionThisStep - m_group.AbsolutePosition;
692  
693 float mag = Vector3.Mag(motionThisStep);
694  
695 if ((mag >= 0.02f) || lastStep)
696 {
697 m_nextPosition = m_group.AbsolutePosition + motionThisStep;
698 m_group.AbsolutePosition = m_nextPosition;
699 update = true;
700 }
701  
702 //int totalSteps = m_currentFrame.TimeTotal / (int)tickDuration;
703 //m_log.DebugFormat("KeyframeMotion.OnTimer: step {0}/{1}, curPosition={2}, finalPosition={3}, motionThisStep={4} (scene {5})",
704 // totalSteps - remainingSteps + 1, totalSteps, m_group.AbsolutePosition, m_currentFrame.Position, motionThisStep, m_scene.RegionInfo.RegionName);
705  
706 if ((Quaternion)m_currentFrame.Rotation != m_group.GroupRotation)
707 {
708 Quaternion current = m_group.GroupRotation;
709  
710 Quaternion step = Quaternion.Slerp(m_currentFrame.StartRotation, (Quaternion)m_currentFrame.Rotation, completed);
711 step.Normalize();
712 /* use simpler change detection
713 * float angle = 0;
714  
715 float aa = current.X * current.X + current.Y * current.Y + current.Z * current.Z + current.W * current.W;
716 float bb = step.X * step.X + step.Y * step.Y + step.Z * step.Z + step.W * step.W;
717 float aa_bb = aa * bb;
718  
719 if (aa_bb == 0)
720 {
721 angle = 0;
722 }
723 else
724 {
725 float ab = current.X * step.X +
726 current.Y * step.Y +
727 current.Z * step.Z +
728 current.W * step.W;
729 float q = (ab * ab) / aa_bb;
730  
731 if (q > 1.0f)
732 {
733 angle = 0;
734 }
735 else
736 {
737 angle = (float)Math.Acos(2 * q - 1);
738 }
739 }
740  
741 if (angle > 0.01f)
742 */
743 if(Math.Abs(step.X - current.X) > 0.001f
744 || Math.Abs(step.Y - current.Y) > 0.001f
745 || Math.Abs(step.Z - current.Z) > 0.001f
746 || lastStep)
747 // assuming w is a dependente var
748  
749 {
750 // m_group.UpdateGroupRotationR(step);
751 m_group.RootPart.RotationOffset = step;
752  
753 //m_group.RootPart.UpdateAngularVelocity(m_currentFrame.AngularVelocity / 2);
754 update = true;
755 }
756 }
757 }
758  
759 if (update)
760 {
761 m_group.SendGroupRootTerseUpdate();
762 }
763 }
764  
765 public Byte[] Serialize()
766 {
767 StopTimer();
768  
769 SceneObjectGroup tmp = m_group;
770 m_group = null;
771 if (!m_selected && tmp != null)
772 m_serializedPosition = tmp.AbsolutePosition;
773  
774 using (MemoryStream ms = new MemoryStream())
775 {
776 BinaryFormatter fmt = new BinaryFormatter();
777 fmt.Serialize(ms, this);
778 m_group = tmp;
779 if (m_running && !m_waitingCrossing)
780 StartTimer();
781  
782 return ms.ToArray();
783 }
784 }
785  
786 public void StartCrossingCheck()
787 {
788 // timer will be restart by crossingFailure
789 // or never since crossing worked and this
790 // should be deleted
791 StopTimer();
792  
793 m_isCrossing = true;
794 m_waitingCrossing = true;
795  
796 // to remove / retune to smoth crossings
797 if (m_group.RootPart.Velocity != Vector3.Zero)
798 {
799 m_group.RootPart.Velocity = Vector3.Zero;
800 m_group.SendGroupRootTerseUpdate();
801 // m_group.RootPart.ScheduleTerseUpdate();
802 }
803 }
804  
805 public void CrossingFailure()
806 {
807 m_waitingCrossing = false;
808  
809 if (m_group != null)
810 {
811 m_group.RootPart.Velocity = Vector3.Zero;
812 m_group.SendGroupRootTerseUpdate();
813 // m_group.RootPart.ScheduleTerseUpdate();
814  
815 if (m_running)
816 {
817 StopTimer();
818 m_skipLoops = 1200; // 60 seconds
819 StartTimer();
820 }
821 }
822 }
823 }
824 }