aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Framework/Scenes/KeyframeMotion.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Framework/Scenes/KeyframeMotion.cs')
-rw-r--r--OpenSim/Region/Framework/Scenes/KeyframeMotion.cs513
1 files changed, 286 insertions, 227 deletions
diff --git a/OpenSim/Region/Framework/Scenes/KeyframeMotion.cs b/OpenSim/Region/Framework/Scenes/KeyframeMotion.cs
index 5cfba39..b102e48 100644
--- a/OpenSim/Region/Framework/Scenes/KeyframeMotion.cs
+++ b/OpenSim/Region/Framework/Scenes/KeyframeMotion.cs
@@ -22,6 +22,115 @@ using log4net;
22 22
23namespace OpenSim.Region.Framework.Scenes 23namespace OpenSim.Region.Framework.Scenes
24{ 24{
25 public class KeyframeTimer
26 {
27 private static Dictionary<Scene, KeyframeTimer>m_timers =
28 new Dictionary<Scene, KeyframeTimer>();
29
30 private Timer m_timer;
31 private Dictionary<KeyframeMotion, object> m_motions = new Dictionary<KeyframeMotion, object>();
32 private object m_lockObject = new object();
33 private object m_timerLock = new object();
34 private const double m_tickDuration = 50.0;
35 private Scene m_scene;
36
37 public double TickDuration
38 {
39 get { return m_tickDuration; }
40 }
41
42 public KeyframeTimer(Scene scene)
43 {
44 m_timer = new Timer();
45 m_timer.Interval = TickDuration;
46 m_timer.AutoReset = true;
47 m_timer.Elapsed += OnTimer;
48
49 m_scene = scene;
50
51 m_timer.Start();
52 }
53
54 private void OnTimer(object sender, ElapsedEventArgs ea)
55 {
56 if (!Monitor.TryEnter(m_timerLock))
57 return;
58
59 try
60 {
61 List<KeyframeMotion> motions;
62
63 lock (m_lockObject)
64 {
65 motions = new List<KeyframeMotion>(m_motions.Keys);
66 }
67
68 foreach (KeyframeMotion m in motions)
69 {
70 try
71 {
72 m.OnTimer(TickDuration);
73 }
74 catch (Exception inner)
75 {
76 // Don't stop processing
77 }
78 }
79 }
80 catch (Exception e)
81 {
82 // Keep running no matter what
83 }
84 finally
85 {
86 Monitor.Exit(m_timerLock);
87 }
88 }
89
90 public static void Add(KeyframeMotion motion)
91 {
92 KeyframeTimer timer;
93
94 if (motion.Scene == null)
95 return;
96
97 lock (m_timers)
98 {
99 if (!m_timers.TryGetValue(motion.Scene, out timer))
100 {
101 timer = new KeyframeTimer(motion.Scene);
102 m_timers[motion.Scene] = timer;
103 }
104 }
105
106 lock (timer.m_lockObject)
107 {
108 timer.m_motions[motion] = null;
109 }
110 }
111
112 public static void Remove(KeyframeMotion motion)
113 {
114 KeyframeTimer timer;
115
116 if (motion.Scene == null)
117 return;
118
119 lock (m_timers)
120 {
121 if (!m_timers.TryGetValue(motion.Scene, out timer))
122 {
123 return;
124 }
125 }
126
127 lock (timer.m_lockObject)
128 {
129 timer.m_motions.Remove(motion);
130 }
131 }
132 }
133
25 [Serializable] 134 [Serializable]
26 public class KeyframeMotion 135 public class KeyframeMotion
27 { 136 {
@@ -63,18 +172,6 @@ namespace OpenSim.Region.Framework.Scenes
63 172
64 private Keyframe[] m_keyframes; 173 private Keyframe[] m_keyframes;
65 174
66 [NonSerialized()]
67 protected Timer m_timer = null;
68
69 // timer lock
70 [NonSerialized()]
71 private object m_onTimerLock;
72
73 // timer overrun detect
74 // prevents overlap or timer events threads frozen on the lock
75 [NonSerialized()]
76 private bool m_inOnTimer;
77
78 // skip timer events. 175 // skip timer events.
79 //timer.stop doesn't assure there aren't event threads still being fired 176 //timer.stop doesn't assure there aren't event threads still being fired
80 [NonSerialized()] 177 [NonSerialized()]
@@ -97,12 +194,21 @@ namespace OpenSim.Region.Framework.Scenes
97 private DataFormat m_data = DataFormat.Translation | DataFormat.Rotation; 194 private DataFormat m_data = DataFormat.Translation | DataFormat.Rotation;
98 195
99 private bool m_running = false; 196 private bool m_running = false;
197
100 [NonSerialized()] 198 [NonSerialized()]
101 private bool m_selected = false; 199 private bool m_selected = false;
102 200
103 private int m_iterations = 0; 201 private int m_iterations = 0;
104 202
105 private const double timerInterval = 50.0; 203 private int m_skipLoops = 0;
204
205 [NonSerialized()]
206 private Scene m_scene;
207
208 public Scene Scene
209 {
210 get { return m_scene; }
211 }
106 212
107 public DataFormat Data 213 public DataFormat Data
108 { 214 {
@@ -139,31 +245,16 @@ namespace OpenSim.Region.Framework.Scenes
139 245
140 private void StartTimer() 246 private void StartTimer()
141 { 247 {
142 if (m_timer == null) 248 KeyframeTimer.Add(this);
143 return;
144 m_timerStopped = false; 249 m_timerStopped = false;
145 m_timer.Start();
146 } 250 }
147 251
148 private void StopTimer() 252 private void StopTimer()
149 { 253 {
150 if (m_timer == null || m_timerStopped)
151 return;
152 m_timerStopped = true;
153 m_timer.Stop();
154 }
155
156 private void RemoveTimer()
157 {
158 if (m_timer == null)
159 return;
160 m_timerStopped = true; 254 m_timerStopped = true;
161 m_timer.Stop(); 255 KeyframeTimer.Remove(this);
162 m_timer.Elapsed -= OnTimer;
163 m_timer = null;
164 } 256 }
165 257
166
167 public static KeyframeMotion FromData(SceneObjectGroup grp, Byte[] data) 258 public static KeyframeMotion FromData(SceneObjectGroup grp, Byte[] data)
168 { 259 {
169 KeyframeMotion newMotion = null; 260 KeyframeMotion newMotion = null;
@@ -177,12 +268,15 @@ namespace OpenSim.Region.Framework.Scenes
177 268
178 newMotion.m_group = grp; 269 newMotion.m_group = grp;
179 270
180 if (grp != null && grp.IsSelected) 271 if (grp != null)
181 newMotion.m_selected = true; 272 {
273 newMotion.m_scene = grp.Scene;
274 if (grp.IsSelected)
275 newMotion.m_selected = true;
276 }
182 277
183 newMotion.m_onTimerLock = new object();
184 newMotion.m_timerStopped = false; 278 newMotion.m_timerStopped = false;
185 newMotion.m_inOnTimer = false; 279 newMotion.m_running = true;
186 newMotion.m_isCrossing = false; 280 newMotion.m_isCrossing = false;
187 newMotion.m_waitingCrossing = false; 281 newMotion.m_waitingCrossing = false;
188 } 282 }
@@ -196,37 +290,36 @@ namespace OpenSim.Region.Framework.Scenes
196 290
197 public void UpdateSceneObject(SceneObjectGroup grp) 291 public void UpdateSceneObject(SceneObjectGroup grp)
198 { 292 {
199// lock (m_onTimerLock) 293 m_isCrossing = false;
200 { 294 m_waitingCrossing = false;
201 m_isCrossing = false; 295 StopTimer();
202 m_waitingCrossing = false;
203 StopTimer();
204 296
205 if (grp == null) 297 if (grp == null)
206 return; 298 return;
207 299
208 m_group = grp; 300 m_group = grp;
209 Vector3 grppos = grp.AbsolutePosition; 301 m_scene = grp.Scene;
210 Vector3 offset = grppos - m_serializedPosition;
211 // avoid doing it more than once
212 // current this will happen draging a prim to other region
213 m_serializedPosition = grppos;
214 302
215 m_basePosition += offset; 303 Vector3 grppos = grp.AbsolutePosition;
216 m_currentFrame.Position += offset; 304 Vector3 offset = grppos - m_serializedPosition;
305 // avoid doing it more than once
306 // current this will happen draging a prim to other region
307 m_serializedPosition = grppos;
217 308
218 m_nextPosition += offset; 309 m_basePosition += offset;
310 m_currentFrame.Position += offset;
219 311
220 for (int i = 0; i < m_frames.Count; i++) 312 m_nextPosition += offset;
221 {
222 Keyframe k = m_frames[i];
223 k.Position += offset;
224 m_frames[i]=k;
225 }
226 313
227 if (m_running) 314 for (int i = 0; i < m_frames.Count; i++)
228 Start(); 315 {
316 Keyframe k = m_frames[i];
317 k.Position += offset;
318 m_frames[i]=k;
229 } 319 }
320
321 if (m_running)
322 Start();
230 } 323 }
231 324
232 public KeyframeMotion(SceneObjectGroup grp, PlayMode mode, DataFormat data) 325 public KeyframeMotion(SceneObjectGroup grp, PlayMode mode, DataFormat data)
@@ -239,11 +332,10 @@ namespace OpenSim.Region.Framework.Scenes
239 { 332 {
240 m_basePosition = grp.AbsolutePosition; 333 m_basePosition = grp.AbsolutePosition;
241 m_baseRotation = grp.GroupRotation; 334 m_baseRotation = grp.GroupRotation;
335 m_scene = grp.Scene;
242 } 336 }
243 337
244 m_onTimerLock = new object();
245 m_timerStopped = true; 338 m_timerStopped = true;
246 m_inOnTimer = false;
247 m_isCrossing = false; 339 m_isCrossing = false;
248 m_waitingCrossing = false; 340 m_waitingCrossing = false;
249 } 341 }
@@ -260,6 +352,7 @@ namespace OpenSim.Region.Framework.Scenes
260 KeyframeMotion newmotion = new KeyframeMotion(null, m_mode, m_data); 352 KeyframeMotion newmotion = new KeyframeMotion(null, m_mode, m_data);
261 353
262 newmotion.m_group = newgrp; 354 newmotion.m_group = newgrp;
355 newmotion.m_scene = newgrp.Scene;
263 356
264 if (m_keyframes != null) 357 if (m_keyframes != null)
265 { 358 {
@@ -296,7 +389,7 @@ namespace OpenSim.Region.Framework.Scenes
296 public void Delete() 389 public void Delete()
297 { 390 {
298 m_running = false; 391 m_running = false;
299 RemoveTimer(); 392 StopTimer();
300 m_isCrossing = false; 393 m_isCrossing = false;
301 m_waitingCrossing = false; 394 m_waitingCrossing = false;
302 m_frames.Clear(); 395 m_frames.Clear();
@@ -309,27 +402,13 @@ namespace OpenSim.Region.Framework.Scenes
309 m_waitingCrossing = false; 402 m_waitingCrossing = false;
310 if (m_keyframes != null && m_group != null && m_keyframes.Length > 0) 403 if (m_keyframes != null && m_group != null && m_keyframes.Length > 0)
311 { 404 {
312 if (m_timer == null)
313 {
314 m_timer = new Timer();
315 m_timer.Interval = timerInterval;
316 m_timer.AutoReset = true;
317 m_timer.Elapsed += OnTimer;
318 }
319 else
320 {
321 StopTimer();
322 m_timer.Interval = timerInterval;
323 }
324
325 m_inOnTimer = false;
326 StartTimer(); 405 StartTimer();
327 m_running = true; 406 m_running = true;
328 } 407 }
329 else 408 else
330 { 409 {
331 m_running = false; 410 m_running = false;
332 RemoveTimer(); 411 StopTimer();
333 } 412 }
334 } 413 }
335 414
@@ -339,7 +418,7 @@ namespace OpenSim.Region.Framework.Scenes
339 m_isCrossing = false; 418 m_isCrossing = false;
340 m_waitingCrossing = false; 419 m_waitingCrossing = false;
341 420
342 RemoveTimer(); 421 StopTimer();
343 422
344 m_basePosition = m_group.AbsolutePosition; 423 m_basePosition = m_group.AbsolutePosition;
345 m_baseRotation = m_group.GroupRotation; 424 m_baseRotation = m_group.GroupRotation;
@@ -354,7 +433,7 @@ namespace OpenSim.Region.Framework.Scenes
354 public void Pause() 433 public void Pause()
355 { 434 {
356 m_running = false; 435 m_running = false;
357 RemoveTimer(); 436 StopTimer();
358 437
359 m_group.RootPart.Velocity = Vector3.Zero; 438 m_group.RootPart.Velocity = Vector3.Zero;
360 m_group.RootPart.AngularVelocity = Vector3.Zero; 439 m_group.RootPart.AngularVelocity = Vector3.Zero;
@@ -377,15 +456,11 @@ namespace OpenSim.Region.Framework.Scenes
377 456
378 int start = 0; 457 int start = 0;
379 int end = m_keyframes.Length; 458 int end = m_keyframes.Length;
380// if (m_mode == PlayMode.PingPong && m_keyframes.Length > 1)
381// end = m_keyframes.Length - 1;
382 459
383 if (direction < 0) 460 if (direction < 0)
384 { 461 {
385 start = m_keyframes.Length - 1; 462 start = m_keyframes.Length - 1;
386 end = -1; 463 end = -1;
387// if (m_mode == PlayMode.PingPong && m_keyframes.Length > 1)
388// end = 0;
389 } 464 }
390 465
391 for (int i = start; i != end ; i += direction) 466 for (int i = start; i != end ; i += direction)
@@ -463,189 +538,172 @@ namespace OpenSim.Region.Framework.Scenes
463 } 538 }
464 } 539 }
465 540
466 protected void OnTimer(object sender, ElapsedEventArgs e) 541 public void OnTimer(double tickDuration)
467 { 542 {
468 if (m_timerStopped) // trap events still in air even after a timer.stop 543 if (m_skipLoops > 0)
469 return;
470
471 if (m_inOnTimer) // don't let overruns to happen
472 { 544 {
473 m_log.Warn("[KeyFrame]: timer overrun"); 545 m_skipLoops--;
474 return; 546 return;
475 } 547 }
476 548
549 if (m_timerStopped) // trap events still in air even after a timer.stop
550 return;
551
477 if (m_group == null) 552 if (m_group == null)
478 return; 553 return;
479 554
480 lock (m_onTimerLock) 555 bool update = false;
481 {
482 556
483 m_inOnTimer = true; 557 if (m_selected)
558 {
559 if (m_group.RootPart.Velocity != Vector3.Zero)
560 {
561 m_group.RootPart.Velocity = Vector3.Zero;
562 m_group.SendGroupRootTerseUpdate();
484 563
485 bool update = false; 564 }
565 return;
566 }
486 567
487 try 568 if (m_isCrossing)
569 {
570 // if crossing and timer running then cross failed
571 // wait some time then
572 // retry to set the position that evtually caused the outbound
573 // if still outside region this will call startCrossing below
574 m_isCrossing = false;
575 m_group.AbsolutePosition = m_nextPosition;
576 if (!m_isCrossing)
488 { 577 {
489 if (m_selected) 578 StopTimer();
490 { 579 StartTimer();
491 if (m_group.RootPart.Velocity != Vector3.Zero) 580 }
492 { 581 return;
493 m_group.RootPart.Velocity = Vector3.Zero; 582 }
494 m_group.SendGroupRootTerseUpdate();
495// m_group.RootPart.ScheduleTerseUpdate();
496 583
497 } 584 if (m_frames.Count == 0)
498 m_inOnTimer = false; 585 {
499 return; 586 GetNextList();
500 }
501 587
502 if (m_isCrossing) 588 if (m_frames.Count == 0)
503 { 589 {
504 // if crossing and timer running then cross failed 590 Stop();
505 // wait some time then 591 Scene scene = m_group.Scene;
506 // retry to set the position that evtually caused the outbound
507 // if still outside region this will call startCrossing below
508 m_isCrossing = false;
509 m_group.AbsolutePosition = m_nextPosition;
510 if (!m_isCrossing)
511 {
512 StopTimer();
513 m_timer.Interval = timerInterval;
514 StartTimer();
515 }
516 m_inOnTimer = false;
517 return;
518 }
519 592
520 if (m_frames.Count == 0) 593 IScriptModule[] scriptModules = scene.RequestModuleInterfaces<IScriptModule>();
594 foreach (IScriptModule m in scriptModules)
521 { 595 {
522 GetNextList(); 596 if (m == null)
597 continue;
598 m.PostObjectEvent(m_group.RootPart.UUID, "moving_end", new object[0]);
599 }
523 600
524 if (m_frames.Count == 0) 601 return;
525 { 602 }
526 Stop();
527 m_inOnTimer = false;
528 return;
529 }
530 603
531 m_currentFrame = m_frames[0]; 604 m_currentFrame = m_frames[0];
532 m_currentFrame.TimeMS += (int)timerInterval; 605 m_currentFrame.TimeMS += (int)tickDuration;
533 606
534 //force a update on a keyframe transition 607 //force a update on a keyframe transition
535 update = true; 608 update = true;
536 } 609 }
537 610
538 m_currentFrame.TimeMS -= (int)timerInterval; 611 m_currentFrame.TimeMS -= (int)tickDuration;
539 612
540 // Do the frame processing 613 // Do the frame processing
541 double steps = (double)m_currentFrame.TimeMS / timerInterval; 614 double steps = (double)m_currentFrame.TimeMS / tickDuration;
542 615
543 if (steps <= 0.0) 616 if (steps <= 0.0)
544 { 617 {
545 m_group.RootPart.Velocity = Vector3.Zero; 618 m_group.RootPart.Velocity = Vector3.Zero;
546 m_group.RootPart.AngularVelocity = Vector3.Zero; 619 m_group.RootPart.AngularVelocity = Vector3.Zero;
547 620
548 m_nextPosition = (Vector3)m_currentFrame.Position; 621 m_nextPosition = (Vector3)m_currentFrame.Position;
549 m_group.AbsolutePosition = m_nextPosition; 622 m_group.AbsolutePosition = m_nextPosition;
550 623
551 // we are sending imediate updates, no doing force a extra terseUpdate 624 // we are sending imediate updates, no doing force a extra terseUpdate
552// m_group.UpdateGroupRotationR((Quaternion)m_currentFrame.Rotation); 625 // m_group.UpdateGroupRotationR((Quaternion)m_currentFrame.Rotation);
553 626
554 m_group.RootPart.RotationOffset = (Quaternion)m_currentFrame.Rotation; 627 m_group.RootPart.RotationOffset = (Quaternion)m_currentFrame.Rotation;
555 m_frames.RemoveAt(0); 628 m_frames.RemoveAt(0);
556 if (m_frames.Count > 0) 629 if (m_frames.Count > 0)
557 m_currentFrame = m_frames[0]; 630 m_currentFrame = m_frames[0];
558 631
559 update = true; 632 update = true;
560 } 633 }
561 else 634 else
562 { 635 {
563 float complete = ((float)m_currentFrame.TimeTotal - (float)m_currentFrame.TimeMS) / (float)m_currentFrame.TimeTotal; 636 float complete = ((float)m_currentFrame.TimeTotal - (float)m_currentFrame.TimeMS) / (float)m_currentFrame.TimeTotal;
564 637
565 Vector3 v = (Vector3)m_currentFrame.Position - m_group.AbsolutePosition; 638 Vector3 v = (Vector3)m_currentFrame.Position - m_group.AbsolutePosition;
566 Vector3 motionThisFrame = v / (float)steps; 639 Vector3 motionThisFrame = v / (float)steps;
567 v = v * 1000 / m_currentFrame.TimeMS; 640 v = v * 1000 / m_currentFrame.TimeMS;
568 641
569 if (Vector3.Mag(motionThisFrame) >= 0.05f) 642 if (Vector3.Mag(motionThisFrame) >= 0.05f)
570 { 643 {
571 // m_group.AbsolutePosition += motionThisFrame; 644 // m_group.AbsolutePosition += motionThisFrame;
572 m_nextPosition = m_group.AbsolutePosition + motionThisFrame; 645 m_nextPosition = m_group.AbsolutePosition + motionThisFrame;
573 m_group.AbsolutePosition = m_nextPosition; 646 m_group.AbsolutePosition = m_nextPosition;
574 647
575 m_group.RootPart.Velocity = v; 648 m_group.RootPart.Velocity = v;
576 update = true; 649 update = true;
577 } 650 }
578 651
579 if ((Quaternion)m_currentFrame.Rotation != m_group.GroupRotation) 652 if ((Quaternion)m_currentFrame.Rotation != m_group.GroupRotation)
580 { 653 {
581 Quaternion current = m_group.GroupRotation; 654 Quaternion current = m_group.GroupRotation;
582 655
583 Quaternion step = Quaternion.Slerp(m_currentFrame.StartRotation, (Quaternion)m_currentFrame.Rotation, complete); 656 Quaternion step = Quaternion.Slerp(m_currentFrame.StartRotation, (Quaternion)m_currentFrame.Rotation, complete);
584 step.Normalize(); 657 step.Normalize();
585/* use simpler change detection 658/* use simpler change detection
586 * float angle = 0; 659* float angle = 0;
587
588 float aa = current.X * current.X + current.Y * current.Y + current.Z * current.Z + current.W * current.W;
589 float bb = step.X * step.X + step.Y * step.Y + step.Z * step.Z + step.W * step.W;
590 float aa_bb = aa * bb;
591
592 if (aa_bb == 0)
593 {
594 angle = 0;
595 }
596 else
597 {
598 float ab = current.X * step.X +
599 current.Y * step.Y +
600 current.Z * step.Z +
601 current.W * step.W;
602 float q = (ab * ab) / aa_bb;
603
604 if (q > 1.0f)
605 {
606 angle = 0;
607 }
608 else
609 {
610 angle = (float)Math.Acos(2 * q - 1);
611 }
612 }
613
614 if (angle > 0.01f)
615 */
616 if(Math.Abs(step.X - current.X) > 0.001f
617 || Math.Abs(step.Y - current.Y) > 0.001f
618 || Math.Abs(step.Z - current.Z) > 0.001f)
619 // assuming w is a dependente var
620 660
621 { 661 float aa = current.X * current.X + current.Y * current.Y + current.Z * current.Z + current.W * current.W;
622// m_group.UpdateGroupRotationR(step); 662 float bb = step.X * step.X + step.Y * step.Y + step.Z * step.Z + step.W * step.W;
623 m_group.RootPart.RotationOffset = step; 663 float aa_bb = aa * bb;
624 664
625 //m_group.RootPart.UpdateAngularVelocity(m_currentFrame.AngularVelocity / 2); 665 if (aa_bb == 0)
626 update = true; 666 {
627 } 667 angle = 0;
668 }
669 else
670 {
671 float ab = current.X * step.X +
672 current.Y * step.Y +
673 current.Z * step.Z +
674 current.W * step.W;
675 float q = (ab * ab) / aa_bb;
676
677 if (q > 1.0f)
678 {
679 angle = 0;
680 }
681 else
682 {
683 angle = (float)Math.Acos(2 * q - 1);
628 } 684 }
629 } 685 }
630 686
631 if (update) 687 if (angle > 0.01f)
632 m_group.SendGroupRootTerseUpdate(); 688*/
633// m_group.RootPart.ScheduleTerseUpdate(); 689 if(Math.Abs(step.X - current.X) > 0.001f
690 || Math.Abs(step.Y - current.Y) > 0.001f
691 || Math.Abs(step.Z - current.Z) > 0.001f)
692 // assuming w is a dependente var
634 693
694 {
695// m_group.UpdateGroupRotationR(step);
696 m_group.RootPart.RotationOffset = step;
635 697
698 //m_group.RootPart.UpdateAngularVelocity(m_currentFrame.AngularVelocity / 2);
699 update = true;
700 }
636 } 701 }
637 catch ( Exception ex) 702 }
638 {
639 // still happening sometimes
640 // lets try to see where
641 m_log.Warn("[KeyFrame]: timer overrun" + ex.Message);
642 }
643 703
644 finally 704 if (update)
645 { 705 {
646 // make sure we do not let this frozen 706 m_group.SendGroupRootTerseUpdate();
647 m_inOnTimer = false;
648 }
649 } 707 }
650 } 708 }
651 709
@@ -677,7 +735,7 @@ namespace OpenSim.Region.Framework.Scenes
677 m_isCrossing = true; 735 m_isCrossing = true;
678 m_waitingCrossing = true; 736 m_waitingCrossing = true;
679 737
680// to remove / retune to smoth crossings 738 // to remove / retune to smoth crossings
681 if (m_group.RootPart.Velocity != Vector3.Zero) 739 if (m_group.RootPart.Velocity != Vector3.Zero)
682 { 740 {
683 m_group.RootPart.Velocity = Vector3.Zero; 741 m_group.RootPart.Velocity = Vector3.Zero;
@@ -696,9 +754,10 @@ namespace OpenSim.Region.Framework.Scenes
696 m_group.SendGroupRootTerseUpdate(); 754 m_group.SendGroupRootTerseUpdate();
697// m_group.RootPart.ScheduleTerseUpdate(); 755// m_group.RootPart.ScheduleTerseUpdate();
698 756
699 if (m_running && m_timer != null) 757 if (m_running)
700 { 758 {
701 m_timer.Interval = 60000; 759 StopTimer();
760 m_skipLoops = 1200; // 60 seconds
702 StartTimer(); 761 StartTimer();
703 } 762 }
704 } 763 }