aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSScene.cs')
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSScene.cs271
1 files changed, 199 insertions, 72 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
index 39f5b0a..423c389 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
@@ -56,12 +56,23 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
56 public string BulletEngineName { get; private set; } 56 public string BulletEngineName { get; private set; }
57 public BSAPITemplate PE; 57 public BSAPITemplate PE;
58 58
59 // If the physics engine is running on a separate thread
60 public Thread m_physicsThread;
61
59 public Dictionary<uint, BSPhysObject> PhysObjects; 62 public Dictionary<uint, BSPhysObject> PhysObjects;
60 public BSShapeCollection Shapes; 63 public BSShapeCollection Shapes;
61 64
62 // Keeping track of the objects with collisions so we can report begin and end of a collision 65 // Keeping track of the objects with collisions so we can report begin and end of a collision
63 public HashSet<BSPhysObject> ObjectsWithCollisions = new HashSet<BSPhysObject>(); 66 public HashSet<BSPhysObject> ObjectsWithCollisions = new HashSet<BSPhysObject>();
64 public HashSet<BSPhysObject> ObjectsWithNoMoreCollisions = new HashSet<BSPhysObject>(); 67 public HashSet<BSPhysObject> ObjectsWithNoMoreCollisions = new HashSet<BSPhysObject>();
68
69 // All the collision processing is protected with this lock object
70 public Object CollisionLock = new Object();
71
72 // Properties are updated here
73 public Object UpdateLock = new Object();
74 public HashSet<BSPhysObject> ObjectsWithUpdates = new HashSet<BSPhysObject>();
75
65 // Keep track of all the avatars so we can send them a collision event 76 // Keep track of all the avatars so we can send them a collision event
66 // every tick so OpenSim will update its animation. 77 // every tick so OpenSim will update its animation.
67 private HashSet<BSPhysObject> m_avatars = new HashSet<BSPhysObject>(); 78 private HashSet<BSPhysObject> m_avatars = new HashSet<BSPhysObject>();
@@ -77,12 +88,19 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
77 public BSConstraintCollection Constraints { get; private set; } 88 public BSConstraintCollection Constraints { get; private set; }
78 89
79 // Simulation parameters 90 // Simulation parameters
91 internal float m_physicsStepTime; // if running independently, the interval simulated by default
92
80 internal int m_maxSubSteps; 93 internal int m_maxSubSteps;
81 internal float m_fixedTimeStep; 94 internal float m_fixedTimeStep;
82 internal long m_simulationStep = 0; 95
83 internal float NominalFrameRate { get; set; } 96 internal float m_simulatedTime; // the time simulated previously. Used for physics framerate calc.
97
98 internal long m_simulationStep = 0; // The current simulation step.
84 public long SimulationStep { get { return m_simulationStep; } } 99 public long SimulationStep { get { return m_simulationStep; } }
85 internal float LastTimeStep { get; private set; } 100
101 internal float LastTimeStep { get; private set; } // The simulation time from the last invocation of Simulate()
102
103 internal float NominalFrameRate { get; set; } // Parameterized ideal frame rate that simulation is scaled to
86 104
87 // Physical objects can register for prestep or poststep events 105 // Physical objects can register for prestep or poststep events
88 public delegate void PreStepAction(float timeStep); 106 public delegate void PreStepAction(float timeStep);
@@ -90,7 +108,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
90 public event PreStepAction BeforeStep; 108 public event PreStepAction BeforeStep;
91 public event PostStepAction AfterStep; 109 public event PostStepAction AfterStep;
92 110
93 // A value of the time now so all the collision and update routines do not have to get their own 111 // A value of the time 'now' so all the collision and update routines do not have to get their own
94 // Set to 'now' just before all the prims and actors are called for collisions and updates 112 // Set to 'now' just before all the prims and actors are called for collisions and updates
95 public int SimulationNowTime { get; private set; } 113 public int SimulationNowTime { get; private set; }
96 114
@@ -188,6 +206,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
188 PhysObjects = new Dictionary<uint, BSPhysObject>(); 206 PhysObjects = new Dictionary<uint, BSPhysObject>();
189 Shapes = new BSShapeCollection(this); 207 Shapes = new BSShapeCollection(this);
190 208
209 m_simulatedTime = 0f;
210 LastTimeStep = 0.1f;
211
191 // Allocate pinned memory to pass parameters. 212 // Allocate pinned memory to pass parameters.
192 UnmanagedParams = new ConfigurationParameters[1]; 213 UnmanagedParams = new ConfigurationParameters[1];
193 214
@@ -227,10 +248,20 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
227 TerrainManager = new BSTerrainManager(this); 248 TerrainManager = new BSTerrainManager(this);
228 TerrainManager.CreateInitialGroundPlaneAndTerrain(); 249 TerrainManager.CreateInitialGroundPlaneAndTerrain();
229 250
251 // Put some informational messages into the log file.
230 m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation); 252 m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation);
231 253
232 InTaintTime = false; 254 InTaintTime = false;
233 m_initialized = true; 255 m_initialized = true;
256
257 // If the physics engine runs on its own thread, start same.
258 if (BSParam.UseSeparatePhysicsThread)
259 {
260 // The physics simulation should happen independently of the heartbeat loop
261 m_physicsThread = new Thread(BulletSPluginPhysicsThread);
262 m_physicsThread.Name = BulletEngineName;
263 m_physicsThread.Start();
264 }
234 } 265 }
235 266
236 // All default parameter values are set here. There should be no values set in the 267 // All default parameter values are set here. There should be no values set in the
@@ -270,6 +301,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
270 } 301 }
271 else 302 else
272 { 303 {
304 // Nothing in the configuration INI file so assume unmanaged and other defaults.
273 BulletEngineName = "BulletUnmanaged"; 305 BulletEngineName = "BulletUnmanaged";
274 m_physicsLoggingEnabled = false; 306 m_physicsLoggingEnabled = false;
275 VehicleLoggingEnabled = false; 307 VehicleLoggingEnabled = false;
@@ -317,6 +349,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
317 349
318 switch (selectionName) 350 switch (selectionName)
319 { 351 {
352 case "bullet":
320 case "bulletunmanaged": 353 case "bulletunmanaged":
321 ret = new BSAPIUnman(engineName, this); 354 ret = new BSAPIUnman(engineName, this);
322 break; 355 break;
@@ -494,25 +527,41 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
494 #endregion // Prim and Avatar addition and removal 527 #endregion // Prim and Avatar addition and removal
495 528
496 #region Simulation 529 #region Simulation
497 // Simulate one timestep 530
531 // Call from the simulator to send physics information to the simulator objects.
532 // This pushes all the collision and property update events into the objects in
533 // the simulator and, since it is on the heartbeat thread, there is an implicit
534 // locking of those data structures from other heartbeat events.
535 // If the physics engine is running on a separate thread, the update information
536 // will be in the ObjectsWithCollions and ObjectsWithUpdates structures.
498 public override float Simulate(float timeStep) 537 public override float Simulate(float timeStep)
499 { 538 {
539 if (!BSParam.UseSeparatePhysicsThread)
540 {
541 DoPhysicsStep(timeStep);
542 }
543 return SendUpdatesToSimulator(timeStep);
544 }
545
546 // Call the physics engine to do one 'timeStep' and collect collisions and updates
547 // into ObjectsWithCollisions and ObjectsWithUpdates data structures.
548 private void DoPhysicsStep(float timeStep)
549 {
500 // prevent simulation until we've been initialized 550 // prevent simulation until we've been initialized
501 if (!m_initialized) return 5.0f; 551 if (!m_initialized) return;
502 552
503 LastTimeStep = timeStep; 553 LastTimeStep = timeStep;
504 554
505 int updatedEntityCount = 0; 555 int updatedEntityCount = 0;
506 int collidersCount = 0; 556 int collidersCount = 0;
507 557
508 int beforeTime = 0; 558 int beforeTime = Util.EnvironmentTickCount();
509 int simTime = 0; 559 int simTime = 0;
510 560
511 // update the prim states while we know the physics engine is not busy
512 int numTaints = _taintOperations.Count; 561 int numTaints = _taintOperations.Count;
513
514 InTaintTime = true; // Only used for debugging so locking is not necessary. 562 InTaintTime = true; // Only used for debugging so locking is not necessary.
515 563
564 // update the prim states while we know the physics engine is not busy
516 ProcessTaints(); 565 ProcessTaints();
517 566
518 // Some of the physical objects requre individual, pre-step calls 567 // Some of the physical objects requre individual, pre-step calls
@@ -535,18 +584,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
535 int numSubSteps = 0; 584 int numSubSteps = 0;
536 try 585 try
537 { 586 {
538 if (PhysicsLogging.Enabled)
539 beforeTime = Util.EnvironmentTickCount();
540
541 numSubSteps = PE.PhysicsStep(World, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out collidersCount); 587 numSubSteps = PE.PhysicsStep(World, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out collidersCount);
542 588
543 if (PhysicsLogging.Enabled)
544 {
545 simTime = Util.EnvironmentTickCountSubtract(beforeTime);
546 DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}",
547 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps,
548 updatedEntityCount, collidersCount, ObjectsWithCollisions.Count);
549 }
550 } 589 }
551 catch (Exception e) 590 catch (Exception e)
552 { 591 {
@@ -558,77 +597,62 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
558 collidersCount = 0; 597 collidersCount = 0;
559 } 598 }
560 599
600 // Make the physics engine dump useful statistics periodically
561 if (PhysicsMetricDumpFrames != 0 && ((m_simulationStep % PhysicsMetricDumpFrames) == 0)) 601 if (PhysicsMetricDumpFrames != 0 && ((m_simulationStep % PhysicsMetricDumpFrames) == 0))
562 PE.DumpPhysicsStatistics(World); 602 PE.DumpPhysicsStatistics(World);
563 603
564 // Get a value for 'now' so all the collision and update routines don't have to get their own. 604 // Get a value for 'now' so all the collision and update routines don't have to get their own.
565 SimulationNowTime = Util.EnvironmentTickCount(); 605 SimulationNowTime = Util.EnvironmentTickCount();
566 606
567 // If there were collisions, process them by sending the event to the prim. 607 // Send collision information to the colliding objects. The objects decide if the collision
568 // Collisions must be processed before updates. 608 // is 'real' (like linksets don't collide with themselves) and the individual objects
569 if (collidersCount > 0) 609 // know if the simulator has subscribed to collisions.
610 lock (CollisionLock)
570 { 611 {
571 for (int ii = 0; ii < collidersCount; ii++) 612 if (collidersCount > 0)
572 { 613 {
573 uint cA = m_collisionArray[ii].aID; 614 for (int ii = 0; ii < collidersCount; ii++)
574 uint cB = m_collisionArray[ii].bID;
575 Vector3 point = m_collisionArray[ii].point;
576 Vector3 normal = m_collisionArray[ii].normal;
577 float penetration = m_collisionArray[ii].penetration;
578 SendCollision(cA, cB, point, normal, penetration);
579 SendCollision(cB, cA, point, -normal, penetration);
580 }
581 }
582
583 // The above SendCollision's batch up the collisions on the objects.
584 // Now push the collisions into the simulator.
585 if (ObjectsWithCollisions.Count > 0)
586 {
587 foreach (BSPhysObject bsp in ObjectsWithCollisions)
588 if (!bsp.SendCollisions())
589 { 615 {
590 // If the object is done colliding, see that it's removed from the colliding list 616 uint cA = m_collisionArray[ii].aID;
591 ObjectsWithNoMoreCollisions.Add(bsp); 617 uint cB = m_collisionArray[ii].bID;
618 Vector3 point = m_collisionArray[ii].point;
619 Vector3 normal = m_collisionArray[ii].normal;
620 float penetration = m_collisionArray[ii].penetration;
621 SendCollision(cA, cB, point, normal, penetration);
622 SendCollision(cB, cA, point, -normal, penetration);
592 } 623 }
624 }
593 } 625 }
594 626
595 // This is a kludge to get avatar movement updates. 627 // If any of the objects had updated properties, tell the managed objects about the update
596 // The simulator expects collisions for avatars even if there are have been no collisions. 628 // and remember that there was a change so it will be passed to the simulator.
597 // The event updates avatar animations and stuff. 629 lock (UpdateLock)
598 // If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
599 foreach (BSPhysObject bsp in m_avatars)
600 if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice
601 bsp.SendCollisions();
602
603 // Objects that are done colliding are removed from the ObjectsWithCollisions list.
604 // Not done above because it is inside an iteration of ObjectWithCollisions.
605 // This complex collision processing is required to create an empty collision
606 // event call after all real collisions have happened on an object. This enables
607 // the simulator to generate the 'collision end' event.
608 if (ObjectsWithNoMoreCollisions.Count > 0)
609 {
610 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
611 ObjectsWithCollisions.Remove(po);
612 ObjectsWithNoMoreCollisions.Clear();
613 }
614 // Done with collisions.
615
616 // If any of the objects had updated properties, tell the object it has been changed by the physics engine
617 if (updatedEntityCount > 0)
618 { 630 {
619 for (int ii = 0; ii < updatedEntityCount; ii++) 631 if (updatedEntityCount > 0)
620 { 632 {
621 EntityProperties entprop = m_updateArray[ii]; 633 for (int ii = 0; ii < updatedEntityCount; ii++)
622 BSPhysObject pobj;
623 if (PhysObjects.TryGetValue(entprop.ID, out pobj))
624 { 634 {
625 pobj.UpdateProperties(entprop); 635 EntityProperties entprop = m_updateArray[ii];
636 BSPhysObject pobj;
637 if (PhysObjects.TryGetValue(entprop.ID, out pobj))
638 {
639 pobj.UpdateProperties(entprop);
640 }
626 } 641 }
627 } 642 }
628 } 643 }
629 644
645 // Some actors want to know when the simulation step is complete.
630 TriggerPostStepEvent(timeStep); 646 TriggerPostStepEvent(timeStep);
631 647
648 simTime = Util.EnvironmentTickCountSubtract(beforeTime);
649 if (PhysicsLogging.Enabled)
650 {
651 DetailLog("{0},DoPhysicsStep,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}",
652 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps,
653 updatedEntityCount, collidersCount, ObjectsWithCollisions.Count);
654 }
655
632 // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world. 656 // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world.
633 // Only enable this in a limited test world with few objects. 657 // Only enable this in a limited test world with few objects.
634 if (m_physicsPhysicalDumpEnabled) 658 if (m_physicsPhysicalDumpEnabled)
@@ -637,7 +661,84 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
637 // The physics engine returns the number of milliseconds it simulated this call. 661 // The physics engine returns the number of milliseconds it simulated this call.
638 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS. 662 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
639 // Multiply by a fixed nominal frame rate to give a rate similar to the simulator (usually 55). 663 // Multiply by a fixed nominal frame rate to give a rate similar to the simulator (usually 55).
640 return (float)numSubSteps * m_fixedTimeStep * 1000f * NominalFrameRate; 664 m_simulatedTime += (float)numSubSteps * m_fixedTimeStep * 1000f * NominalFrameRate;
665 }
666
667 // Called by a BSPhysObject to note that it has changed properties and this information
668 // should be passed up to the simulator at the proper time.
669 // Note: this is called by the BSPhysObject from invocation via DoPhysicsStep() above so
670 // this is is under UpdateLock.
671 public void PostUpdate(BSPhysObject updatee)
672 {
673 ObjectsWithUpdates.Add(updatee);
674 }
675
676 // The simulator thinks it is physics time so return all the collisions and position
677 // updates that were collected in actual physics simulation.
678 private float SendUpdatesToSimulator(float timeStep)
679 {
680 if (!m_initialized) return 5.0f;
681
682 DetailLog("{0},SendUpdatesToSimulator,collisions={1},updates={2},simedTime={3}",
683 BSScene.DetailLogZero, ObjectsWithCollisions.Count, ObjectsWithUpdates.Count, m_simulatedTime);
684 // Push the collisions into the simulator.
685 lock (CollisionLock)
686 {
687 if (ObjectsWithCollisions.Count > 0)
688 {
689 foreach (BSPhysObject bsp in ObjectsWithCollisions)
690 if (!bsp.SendCollisions())
691 {
692 // If the object is done colliding, see that it's removed from the colliding list
693 ObjectsWithNoMoreCollisions.Add(bsp);
694 }
695 }
696
697 // This is a kludge to get avatar movement updates.
698 // The simulator expects collisions for avatars even if there are have been no collisions.
699 // The event updates avatar animations and stuff.
700 // If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
701 foreach (BSPhysObject bsp in m_avatars)
702 if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice
703 bsp.SendCollisions();
704
705 // Objects that are done colliding are removed from the ObjectsWithCollisions list.
706 // Not done above because it is inside an iteration of ObjectWithCollisions.
707 // This complex collision processing is required to create an empty collision
708 // event call after all real collisions have happened on an object. This allows
709 // the simulator to generate the 'collision end' event.
710 if (ObjectsWithNoMoreCollisions.Count > 0)
711 {
712 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
713 ObjectsWithCollisions.Remove(po);
714 ObjectsWithNoMoreCollisions.Clear();
715 }
716 }
717
718 // Call the simulator for each object that has physics property updates.
719 HashSet<BSPhysObject> updatedObjects = null;
720 lock (UpdateLock)
721 {
722 if (ObjectsWithUpdates.Count > 0)
723 {
724 updatedObjects = ObjectsWithUpdates;
725 ObjectsWithUpdates = new HashSet<BSPhysObject>();
726 }
727 }
728 if (updatedObjects != null)
729 {
730 foreach (BSPhysObject obj in updatedObjects)
731 {
732 obj.RequestPhysicsterseUpdate();
733 }
734 updatedObjects.Clear();
735 }
736
737 // Return the framerate simulated to give the above returned results.
738 // (Race condition here but this is just bookkeeping so rare mistakes do not merit a lock).
739 float simTime = m_simulatedTime;
740 m_simulatedTime = 0f;
741 return simTime;
641 } 742 }
642 743
643 // Something has collided 744 // Something has collided
@@ -656,7 +757,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
656 return; 757 return;
657 } 758 }
658 759
659 // The terrain is not in the physical object list so 'collidee' can be null when Collide() is called. 760 // Note: the terrain is not in the physical object list so 'collidee' can be null when Collide() is called.
660 BSPhysObject collidee = null; 761 BSPhysObject collidee = null;
661 PhysObjects.TryGetValue(collidingWith, out collidee); 762 PhysObjects.TryGetValue(collidingWith, out collidee);
662 763
@@ -664,13 +765,39 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
664 765
665 if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration)) 766 if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
666 { 767 {
667 // If a collision was posted, remember to send it to the simulator 768 // If a collision was 'good', remember to send it to the simulator
668 ObjectsWithCollisions.Add(collider); 769 ObjectsWithCollisions.Add(collider);
669 } 770 }
670 771
671 return; 772 return;
672 } 773 }
673 774
775 public void BulletSPluginPhysicsThread()
776 {
777 while (m_initialized)
778 {
779 int beginSimulationRealtimeMS = Util.EnvironmentTickCount();
780 DoPhysicsStep(BSParam.PhysicsTimeStep);
781 int simulationRealtimeMS = Util.EnvironmentTickCountSubtract(beginSimulationRealtimeMS);
782 int simulationTimeVsRealtimeDifferenceMS = ((int)(BSParam.PhysicsTimeStep*1000f)) - simulationRealtimeMS;
783
784 if (simulationTimeVsRealtimeDifferenceMS > 0)
785 {
786 // The simulation of the time interval took less than realtime.
787 // Do a sleep for the rest of realtime.
788 DetailLog("{0},BulletSPluginPhysicsThread,sleeping={1}", BSScene.DetailLogZero, simulationTimeVsRealtimeDifferenceMS);
789 Thread.Sleep(simulationTimeVsRealtimeDifferenceMS);
790 }
791 else
792 {
793 // The simulation took longer than realtime.
794 // Do some scaling of simulation time.
795 // TODO.
796 DetailLog("{0},BulletSPluginPhysicsThread,longerThanRealtime={1}", BSScene.DetailLogZero, simulationTimeVsRealtimeDifferenceMS);
797 }
798 }
799 }
800
674 #endregion // Simulation 801 #endregion // Simulation
675 802
676 public override void GetResults() { } 803 public override void GetResults() { }