aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSScene.cs383
1 files changed, 279 insertions, 104 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
index e6aefd5..b3dfa41 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
@@ -1,4 +1,4 @@
1/* 1/*
2 * Copyright (c) Contributors, http://opensimulator.org/ 2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders. 3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 * 4 *
@@ -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,22 @@ 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 // A number to use for SimulationStep that is probably not any step value
101 // Used by the collision code (which remembers the step when a collision happens) to remember not any simulation step.
102 public static long NotASimulationStep = -1234;
103
104 internal float LastTimeStep { get; private set; } // The simulation time from the last invocation of Simulate()
105
106 internal float NominalFrameRate { get; set; } // Parameterized ideal frame rate that simulation is scaled to
86 107
87 // Physical objects can register for prestep or poststep events 108 // Physical objects can register for prestep or poststep events
88 public delegate void PreStepAction(float timeStep); 109 public delegate void PreStepAction(float timeStep);
@@ -90,7 +111,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
90 public event PreStepAction BeforeStep; 111 public event PreStepAction BeforeStep;
91 public event PostStepAction AfterStep; 112 public event PostStepAction AfterStep;
92 113
93 // A value of the time now so all the collision and update routines do not have to get their own 114 // 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 115 // Set to 'now' just before all the prims and actors are called for collisions and updates
95 public int SimulationNowTime { get; private set; } 116 public int SimulationNowTime { get; private set; }
96 117
@@ -136,12 +157,20 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
136 public delegate void TaintCallback(); 157 public delegate void TaintCallback();
137 private struct TaintCallbackEntry 158 private struct TaintCallbackEntry
138 { 159 {
160 public String originator;
139 public String ident; 161 public String ident;
140 public TaintCallback callback; 162 public TaintCallback callback;
141 public TaintCallbackEntry(string i, TaintCallback c) 163 public TaintCallbackEntry(string pIdent, TaintCallback pCallBack)
164 {
165 originator = BSScene.DetailLogZero;
166 ident = pIdent;
167 callback = pCallBack;
168 }
169 public TaintCallbackEntry(string pOrigin, string pIdent, TaintCallback pCallBack)
142 { 170 {
143 ident = i; 171 originator = pOrigin;
144 callback = c; 172 ident = pIdent;
173 callback = pCallBack;
145 } 174 }
146 } 175 }
147 private Object _taintLock = new Object(); // lock for using the next object 176 private Object _taintLock = new Object(); // lock for using the next object
@@ -188,6 +217,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
188 PhysObjects = new Dictionary<uint, BSPhysObject>(); 217 PhysObjects = new Dictionary<uint, BSPhysObject>();
189 Shapes = new BSShapeCollection(this); 218 Shapes = new BSShapeCollection(this);
190 219
220 m_simulatedTime = 0f;
221 LastTimeStep = 0.1f;
222
191 // Allocate pinned memory to pass parameters. 223 // Allocate pinned memory to pass parameters.
192 UnmanagedParams = new ConfigurationParameters[1]; 224 UnmanagedParams = new ConfigurationParameters[1];
193 225
@@ -202,8 +234,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
202 // can be left in and every call doesn't have to check for null. 234 // can be left in and every call doesn't have to check for null.
203 if (m_physicsLoggingEnabled) 235 if (m_physicsLoggingEnabled)
204 { 236 {
205 PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes); 237 PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes, m_physicsLoggingDoFlush);
206 PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output error messages. 238 PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output its own error messages.
207 } 239 }
208 else 240 else
209 { 241 {
@@ -227,10 +259,20 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
227 TerrainManager = new BSTerrainManager(this); 259 TerrainManager = new BSTerrainManager(this);
228 TerrainManager.CreateInitialGroundPlaneAndTerrain(); 260 TerrainManager.CreateInitialGroundPlaneAndTerrain();
229 261
230 m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation); 262 // Put some informational messages into the log file.
263 m_log.InfoFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation);
231 264
232 InTaintTime = false; 265 InTaintTime = false;
233 m_initialized = true; 266 m_initialized = true;
267
268 // If the physics engine runs on its own thread, start same.
269 if (BSParam.UseSeparatePhysicsThread)
270 {
271 // The physics simulation should happen independently of the heartbeat loop
272 m_physicsThread = new Thread(BulletSPluginPhysicsThread);
273 m_physicsThread.Name = BulletEngineName;
274 m_physicsThread.Start();
275 }
234 } 276 }
235 277
236 // All default parameter values are set here. There should be no values set in the 278 // All default parameter values are set here. There should be no values set in the
@@ -268,6 +310,13 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
268 // Do any replacements in the parameters 310 // Do any replacements in the parameters
269 m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName); 311 m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
270 } 312 }
313 else
314 {
315 // Nothing in the configuration INI file so assume unmanaged and other defaults.
316 BulletEngineName = "BulletUnmanaged";
317 m_physicsLoggingEnabled = false;
318 VehicleLoggingEnabled = false;
319 }
271 320
272 // The material characteristics. 321 // The material characteristics.
273 BSMaterials.InitializeFromDefaults(Params); 322 BSMaterials.InitializeFromDefaults(Params);
@@ -311,11 +360,22 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
311 360
312 switch (selectionName) 361 switch (selectionName)
313 { 362 {
363 case "bullet":
314 case "bulletunmanaged": 364 case "bulletunmanaged":
315 ret = new BSAPIUnman(engineName, this); 365 ret = new BSAPIUnman(engineName, this);
316 break; 366 break;
317 case "bulletxna": 367 case "bulletxna":
318 ret = new BSAPIXNA(engineName, this); 368 ret = new BSAPIXNA(engineName, this);
369 // Disable some features that are not implemented in BulletXNA
370 m_log.InfoFormat("{0} Disabling some physics features not implemented by BulletXNA", LogHeader);
371 m_log.InfoFormat("{0} Disabling ShouldUseBulletHACD", LogHeader);
372 BSParam.ShouldUseBulletHACD = false;
373 m_log.InfoFormat("{0} Disabling ShouldUseSingleConvexHullForPrims", LogHeader);
374 BSParam.ShouldUseSingleConvexHullForPrims = false;
375 m_log.InfoFormat("{0} Disabling ShouldUseGImpactShapeForPrims", LogHeader);
376 BSParam.ShouldUseGImpactShapeForPrims = false;
377 m_log.InfoFormat("{0} Setting terrain implimentation to Heightmap", LogHeader);
378 BSParam.TerrainImplementation = (float)BSTerrainPhys.TerrainImplementation.Heightmap;
319 break; 379 break;
320 } 380 }
321 381
@@ -325,7 +385,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
325 } 385 }
326 else 386 else
327 { 387 {
328 m_log.WarnFormat("{0} Selected bullet engine {1} -> {2}/{3}", LogHeader, engineName, ret.BulletEngineName, ret.BulletEngineVersion); 388 m_log.InfoFormat("{0} Selected bullet engine {1} -> {2}/{3}", LogHeader, engineName, ret.BulletEngineName, ret.BulletEngineVersion);
329 } 389 }
330 390
331 return ret; 391 return ret;
@@ -463,7 +523,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
463 523
464 if (!m_initialized) return null; 524 if (!m_initialized) return null;
465 525
466 DetailLog("{0},BSScene.AddPrimShape,call", localID); 526 // DetailLog("{0},BSScene.AddPrimShape,call", localID);
467 527
468 BSPhysObject prim = new BSPrimLinkable(localID, primName, this, position, size, rotation, pbs, isPhysical); 528 BSPhysObject prim = new BSPrimLinkable(localID, primName, this, position, size, rotation, pbs, isPhysical);
469 lock (PhysObjects) PhysObjects.Add(localID, prim); 529 lock (PhysObjects) PhysObjects.Add(localID, prim);
@@ -478,25 +538,41 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
478 #endregion // Prim and Avatar addition and removal 538 #endregion // Prim and Avatar addition and removal
479 539
480 #region Simulation 540 #region Simulation
481 // Simulate one timestep 541
542 // Call from the simulator to send physics information to the simulator objects.
543 // This pushes all the collision and property update events into the objects in
544 // the simulator and, since it is on the heartbeat thread, there is an implicit
545 // locking of those data structures from other heartbeat events.
546 // If the physics engine is running on a separate thread, the update information
547 // will be in the ObjectsWithCollions and ObjectsWithUpdates structures.
482 public override float Simulate(float timeStep) 548 public override float Simulate(float timeStep)
483 { 549 {
550 if (!BSParam.UseSeparatePhysicsThread)
551 {
552 DoPhysicsStep(timeStep);
553 }
554 return SendUpdatesToSimulator(timeStep);
555 }
556
557 // Call the physics engine to do one 'timeStep' and collect collisions and updates
558 // into ObjectsWithCollisions and ObjectsWithUpdates data structures.
559 private void DoPhysicsStep(float timeStep)
560 {
484 // prevent simulation until we've been initialized 561 // prevent simulation until we've been initialized
485 if (!m_initialized) return 5.0f; 562 if (!m_initialized) return;
486 563
487 LastTimeStep = timeStep; 564 LastTimeStep = timeStep;
488 565
489 int updatedEntityCount = 0; 566 int updatedEntityCount = 0;
490 int collidersCount = 0; 567 int collidersCount = 0;
491 568
492 int beforeTime = 0; 569 int beforeTime = Util.EnvironmentTickCount();
493 int simTime = 0; 570 int simTime = 0;
494 571
495 // update the prim states while we know the physics engine is not busy
496 int numTaints = _taintOperations.Count; 572 int numTaints = _taintOperations.Count;
497
498 InTaintTime = true; // Only used for debugging so locking is not necessary. 573 InTaintTime = true; // Only used for debugging so locking is not necessary.
499 574
575 // update the prim states while we know the physics engine is not busy
500 ProcessTaints(); 576 ProcessTaints();
501 577
502 // Some of the physical objects requre individual, pre-step calls 578 // Some of the physical objects requre individual, pre-step calls
@@ -519,18 +595,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
519 int numSubSteps = 0; 595 int numSubSteps = 0;
520 try 596 try
521 { 597 {
522 if (PhysicsLogging.Enabled)
523 beforeTime = Util.EnvironmentTickCount();
524
525 numSubSteps = PE.PhysicsStep(World, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out collidersCount); 598 numSubSteps = PE.PhysicsStep(World, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out collidersCount);
526 599
527 if (PhysicsLogging.Enabled)
528 {
529 simTime = Util.EnvironmentTickCountSubtract(beforeTime);
530 DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}",
531 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps,
532 updatedEntityCount, collidersCount, ObjectsWithCollisions.Count);
533 }
534 } 600 }
535 catch (Exception e) 601 catch (Exception e)
536 { 602 {
@@ -542,77 +608,63 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
542 collidersCount = 0; 608 collidersCount = 0;
543 } 609 }
544 610
611 // Make the physics engine dump useful statistics periodically
545 if (PhysicsMetricDumpFrames != 0 && ((m_simulationStep % PhysicsMetricDumpFrames) == 0)) 612 if (PhysicsMetricDumpFrames != 0 && ((m_simulationStep % PhysicsMetricDumpFrames) == 0))
546 PE.DumpPhysicsStatistics(World); 613 PE.DumpPhysicsStatistics(World);
547 614
548 // Get a value for 'now' so all the collision and update routines don't have to get their own. 615 // Get a value for 'now' so all the collision and update routines don't have to get their own.
549 SimulationNowTime = Util.EnvironmentTickCount(); 616 SimulationNowTime = Util.EnvironmentTickCount();
550 617
551 // If there were collisions, process them by sending the event to the prim. 618 // Send collision information to the colliding objects. The objects decide if the collision
552 // Collisions must be processed before updates. 619 // is 'real' (like linksets don't collide with themselves) and the individual objects
553 if (collidersCount > 0) 620 // know if the simulator has subscribed to collisions.
621 lock (CollisionLock)
554 { 622 {
555 for (int ii = 0; ii < collidersCount; ii++) 623 if (collidersCount > 0)
556 { 624 {
557 uint cA = m_collisionArray[ii].aID; 625 for (int ii = 0; ii < collidersCount; ii++)
558 uint cB = m_collisionArray[ii].bID;
559 Vector3 point = m_collisionArray[ii].point;
560 Vector3 normal = m_collisionArray[ii].normal;
561 float penetration = m_collisionArray[ii].penetration;
562 SendCollision(cA, cB, point, normal, penetration);
563 SendCollision(cB, cA, point, -normal, penetration);
564 }
565 }
566
567 // The above SendCollision's batch up the collisions on the objects.
568 // Now push the collisions into the simulator.
569 if (ObjectsWithCollisions.Count > 0)
570 {
571 foreach (BSPhysObject bsp in ObjectsWithCollisions)
572 if (!bsp.SendCollisions())
573 { 626 {
574 // If the object is done colliding, see that it's removed from the colliding list 627 uint cA = m_collisionArray[ii].aID;
575 ObjectsWithNoMoreCollisions.Add(bsp); 628 uint cB = m_collisionArray[ii].bID;
629 Vector3 point = m_collisionArray[ii].point;
630 Vector3 normal = m_collisionArray[ii].normal;
631 float penetration = m_collisionArray[ii].penetration;
632 SendCollision(cA, cB, point, normal, penetration);
633 SendCollision(cB, cA, point, -normal, penetration);
576 } 634 }
635 }
577 } 636 }
578 637
579 // This is a kludge to get avatar movement updates. 638 // If any of the objects had updated properties, tell the managed objects about the update
580 // The simulator expects collisions for avatars even if there are have been no collisions. 639 // and remember that there was a change so it will be passed to the simulator.
581 // The event updates avatar animations and stuff. 640 lock (UpdateLock)
582 // If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
583 foreach (BSPhysObject bsp in m_avatars)
584 if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice
585 bsp.SendCollisions();
586
587 // Objects that are done colliding are removed from the ObjectsWithCollisions list.
588 // Not done above because it is inside an iteration of ObjectWithCollisions.
589 // This complex collision processing is required to create an empty collision
590 // event call after all real collisions have happened on an object. This enables
591 // the simulator to generate the 'collision end' event.
592 if (ObjectsWithNoMoreCollisions.Count > 0)
593 {
594 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
595 ObjectsWithCollisions.Remove(po);
596 ObjectsWithNoMoreCollisions.Clear();
597 }
598 // Done with collisions.
599
600 // If any of the objects had updated properties, tell the object it has been changed by the physics engine
601 if (updatedEntityCount > 0)
602 { 641 {
603 for (int ii = 0; ii < updatedEntityCount; ii++) 642 if (updatedEntityCount > 0)
604 { 643 {
605 EntityProperties entprop = m_updateArray[ii]; 644 for (int ii = 0; ii < updatedEntityCount; ii++)
606 BSPhysObject pobj;
607 if (PhysObjects.TryGetValue(entprop.ID, out pobj))
608 { 645 {
609 pobj.UpdateProperties(entprop); 646 EntityProperties entprop = m_updateArray[ii];
647 BSPhysObject pobj;
648 if (PhysObjects.TryGetValue(entprop.ID, out pobj))
649 {
650 if (pobj.IsInitialized)
651 pobj.UpdateProperties(entprop);
652 }
610 } 653 }
611 } 654 }
612 } 655 }
613 656
657 // Some actors want to know when the simulation step is complete.
614 TriggerPostStepEvent(timeStep); 658 TriggerPostStepEvent(timeStep);
615 659
660 simTime = Util.EnvironmentTickCountSubtract(beforeTime);
661 if (PhysicsLogging.Enabled)
662 {
663 DetailLog("{0},DoPhysicsStep,complete,frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}",
664 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps,
665 updatedEntityCount, collidersCount, ObjectsWithCollisions.Count);
666 }
667
616 // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world. 668 // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world.
617 // Only enable this in a limited test world with few objects. 669 // Only enable this in a limited test world with few objects.
618 if (m_physicsPhysicalDumpEnabled) 670 if (m_physicsPhysicalDumpEnabled)
@@ -621,7 +673,84 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
621 // The physics engine returns the number of milliseconds it simulated this call. 673 // The physics engine returns the number of milliseconds it simulated this call.
622 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS. 674 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
623 // Multiply by a fixed nominal frame rate to give a rate similar to the simulator (usually 55). 675 // Multiply by a fixed nominal frame rate to give a rate similar to the simulator (usually 55).
624 return (float)numSubSteps * m_fixedTimeStep * 1000f * NominalFrameRate; 676 m_simulatedTime += (float)numSubSteps * m_fixedTimeStep * 1000f * NominalFrameRate;
677 }
678
679 // Called by a BSPhysObject to note that it has changed properties and this information
680 // should be passed up to the simulator at the proper time.
681 // Note: this is called by the BSPhysObject from invocation via DoPhysicsStep() above so
682 // this is is under UpdateLock.
683 public void PostUpdate(BSPhysObject updatee)
684 {
685 ObjectsWithUpdates.Add(updatee);
686 }
687
688 // The simulator thinks it is physics time so return all the collisions and position
689 // updates that were collected in actual physics simulation.
690 private float SendUpdatesToSimulator(float timeStep)
691 {
692 if (!m_initialized) return 5.0f;
693
694 DetailLog("{0},SendUpdatesToSimulator,collisions={1},updates={2},simedTime={3}",
695 BSScene.DetailLogZero, ObjectsWithCollisions.Count, ObjectsWithUpdates.Count, m_simulatedTime);
696 // Push the collisions into the simulator.
697 lock (CollisionLock)
698 {
699 if (ObjectsWithCollisions.Count > 0)
700 {
701 foreach (BSPhysObject bsp in ObjectsWithCollisions)
702 if (!bsp.SendCollisions())
703 {
704 // If the object is done colliding, see that it's removed from the colliding list
705 ObjectsWithNoMoreCollisions.Add(bsp);
706 }
707 }
708
709 // This is a kludge to get avatar movement updates.
710 // The simulator expects collisions for avatars even if there are have been no collisions.
711 // The event updates avatar animations and stuff.
712 // If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
713 foreach (BSPhysObject bsp in m_avatars)
714 if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice
715 bsp.SendCollisions();
716
717 // Objects that are done colliding are removed from the ObjectsWithCollisions list.
718 // Not done above because it is inside an iteration of ObjectWithCollisions.
719 // This complex collision processing is required to create an empty collision
720 // event call after all real collisions have happened on an object. This allows
721 // the simulator to generate the 'collision end' event.
722 if (ObjectsWithNoMoreCollisions.Count > 0)
723 {
724 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
725 ObjectsWithCollisions.Remove(po);
726 ObjectsWithNoMoreCollisions.Clear();
727 }
728 }
729
730 // Call the simulator for each object that has physics property updates.
731 HashSet<BSPhysObject> updatedObjects = null;
732 lock (UpdateLock)
733 {
734 if (ObjectsWithUpdates.Count > 0)
735 {
736 updatedObjects = ObjectsWithUpdates;
737 ObjectsWithUpdates = new HashSet<BSPhysObject>();
738 }
739 }
740 if (updatedObjects != null)
741 {
742 foreach (BSPhysObject obj in updatedObjects)
743 {
744 obj.RequestPhysicsterseUpdate();
745 }
746 updatedObjects.Clear();
747 }
748
749 // Return the framerate simulated to give the above returned results.
750 // (Race condition here but this is just bookkeeping so rare mistakes do not merit a lock).
751 float simTime = m_simulatedTime;
752 m_simulatedTime = 0f;
753 return simTime;
625 } 754 }
626 755
627 // Something has collided 756 // Something has collided
@@ -640,21 +769,49 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
640 return; 769 return;
641 } 770 }
642 771
643 // The terrain is not in the physical object list so 'collidee' can be null when Collide() is called. 772 // Note: the terrain is not in the physical object list so 'collidee' can be null when Collide() is called.
644 BSPhysObject collidee = null; 773 BSPhysObject collidee = null;
645 PhysObjects.TryGetValue(collidingWith, out collidee); 774 PhysObjects.TryGetValue(collidingWith, out collidee);
646 775
647 // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith); 776 // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
648 777
649 if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration)) 778 if (collider.IsInitialized)
650 { 779 {
651 // If a collision was posted, remember to send it to the simulator 780 if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
652 ObjectsWithCollisions.Add(collider); 781 {
782 // If a collision was 'good', remember to send it to the simulator
783 ObjectsWithCollisions.Add(collider);
784 }
653 } 785 }
654 786
655 return; 787 return;
656 } 788 }
657 789
790 public void BulletSPluginPhysicsThread()
791 {
792 while (m_initialized)
793 {
794 int beginSimulationRealtimeMS = Util.EnvironmentTickCount();
795 DoPhysicsStep(BSParam.PhysicsTimeStep);
796 int simulationRealtimeMS = Util.EnvironmentTickCountSubtract(beginSimulationRealtimeMS);
797 int simulationTimeVsRealtimeDifferenceMS = ((int)(BSParam.PhysicsTimeStep*1000f)) - simulationRealtimeMS;
798
799 if (simulationTimeVsRealtimeDifferenceMS > 0)
800 {
801 // The simulation of the time interval took less than realtime.
802 // Do a sleep for the rest of realtime.
803 Thread.Sleep(simulationTimeVsRealtimeDifferenceMS);
804 }
805 else
806 {
807 // The simulation took longer than realtime.
808 // Do some scaling of simulation time.
809 // TODO.
810 DetailLog("{0},BulletSPluginPhysicsThread,longerThanRealtime={1}", BSScene.DetailLogZero, simulationTimeVsRealtimeDifferenceMS);
811 }
812 }
813 }
814
658 #endregion // Simulation 815 #endregion // Simulation
659 816
660 public override void GetResults() { } 817 public override void GetResults() { }
@@ -717,6 +874,14 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
717 874
718 public override bool IsThreaded { get { return false; } } 875 public override bool IsThreaded { get { return false; } }
719 876
877 #region Extensions
878 public override object Extension(string pFunct, params object[] pParams)
879 {
880 DetailLog("{0} BSScene.Extension,op={1}", DetailLogZero, pFunct);
881 return base.Extension(pFunct, pParams);
882 }
883 #endregion // Extensions
884
720 #region Taints 885 #region Taints
721 // The simulation execution order is: 886 // The simulation execution order is:
722 // Simulate() 887 // Simulate()
@@ -731,26 +896,37 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
731 // Calls to the PhysicsActors can't directly call into the physics engine 896 // Calls to the PhysicsActors can't directly call into the physics engine
732 // because it might be busy. We delay changes to a known time. 897 // because it might be busy. We delay changes to a known time.
733 // We rely on C#'s closure to save and restore the context for the delegate. 898 // We rely on C#'s closure to save and restore the context for the delegate.
734 public void TaintedObject(String ident, TaintCallback callback) 899 public void TaintedObject(string pOriginator, string pIdent, TaintCallback pCallback)
735 { 900 {
736 if (!m_initialized) return; 901 TaintedObject(false /*inTaintTime*/, pOriginator, pIdent, pCallback);
737 902 }
738 lock (_taintLock) 903 public void TaintedObject(uint pOriginator, String pIdent, TaintCallback pCallback)
739 { 904 {
740 _taintOperations.Add(new TaintCallbackEntry(ident, callback)); 905 TaintedObject(false /*inTaintTime*/, m_physicsLoggingEnabled ? pOriginator.ToString() : BSScene.DetailLogZero, pIdent, pCallback);
741 } 906 }
742 907 public void TaintedObject(bool inTaintTime, String pIdent, TaintCallback pCallback)
743 return; 908 {
909 TaintedObject(inTaintTime, BSScene.DetailLogZero, pIdent, pCallback);
910 }
911 public void TaintedObject(bool inTaintTime, uint pOriginator, String pIdent, TaintCallback pCallback)
912 {
913 TaintedObject(inTaintTime, m_physicsLoggingEnabled ? pOriginator.ToString() : BSScene.DetailLogZero, pIdent, pCallback);
744 } 914 }
745
746 // Sometimes a potentially tainted operation can be used in and out of taint time. 915 // Sometimes a potentially tainted operation can be used in and out of taint time.
747 // This routine executes the command immediately if in taint-time otherwise it is queued. 916 // This routine executes the command immediately if in taint-time otherwise it is queued.
748 public void TaintedObject(bool inTaintTime, string ident, TaintCallback callback) 917 public void TaintedObject(bool inTaintTime, string pOriginator, string pIdent, TaintCallback pCallback)
749 { 918 {
919 if (!m_initialized) return;
920
750 if (inTaintTime) 921 if (inTaintTime)
751 callback(); 922 pCallback();
752 else 923 else
753 TaintedObject(ident, callback); 924 {
925 lock (_taintLock)
926 {
927 _taintOperations.Add(new TaintCallbackEntry(pOriginator, pIdent, pCallback));
928 }
929 }
754 } 930 }
755 931
756 private void TriggerPreStepEvent(float timeStep) 932 private void TriggerPreStepEvent(float timeStep)
@@ -780,7 +956,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
780 956
781 private void ProcessRegularTaints() 957 private void ProcessRegularTaints()
782 { 958 {
783 if (_taintOperations.Count > 0) // save allocating new list if there is nothing to process 959 if (m_initialized && _taintOperations.Count > 0) // save allocating new list if there is nothing to process
784 { 960 {
785 // swizzle a new list into the list location so we can process what's there 961 // swizzle a new list into the list location so we can process what's there
786 List<TaintCallbackEntry> oldList; 962 List<TaintCallbackEntry> oldList;
@@ -794,7 +970,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
794 { 970 {
795 try 971 try
796 { 972 {
797 DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG 973 DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", tcbe.originator, tcbe.ident); // DEBUG DEBUG DEBUG
798 tcbe.callback(); 974 tcbe.callback();
799 } 975 }
800 catch (Exception e) 976 catch (Exception e)
@@ -811,10 +987,11 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
811 // will replace any previous operation by the same object. 987 // will replace any previous operation by the same object.
812 public void PostTaintObject(String ident, uint ID, TaintCallback callback) 988 public void PostTaintObject(String ident, uint ID, TaintCallback callback)
813 { 989 {
814 string uniqueIdent = ident + "-" + ID.ToString(); 990 string IDAsString = ID.ToString();
991 string uniqueIdent = ident + "-" + IDAsString;
815 lock (_taintLock) 992 lock (_taintLock)
816 { 993 {
817 _postTaintOperations[uniqueIdent] = new TaintCallbackEntry(uniqueIdent, callback); 994 _postTaintOperations[uniqueIdent] = new TaintCallbackEntry(IDAsString, uniqueIdent, callback);
818 } 995 }
819 996
820 return; 997 return;
@@ -823,7 +1000,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
823 // Taints that happen after the normal taint processing but before the simulation step. 1000 // Taints that happen after the normal taint processing but before the simulation step.
824 private void ProcessPostTaintTaints() 1001 private void ProcessPostTaintTaints()
825 { 1002 {
826 if (_postTaintOperations.Count > 0) 1003 if (m_initialized && _postTaintOperations.Count > 0)
827 { 1004 {
828 Dictionary<string, TaintCallbackEntry> oldList; 1005 Dictionary<string, TaintCallbackEntry> oldList;
829 lock (_taintLock) 1006 lock (_taintLock)
@@ -924,7 +1101,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
924 string xval = val; 1101 string xval = val;
925 List<uint> xlIDs = lIDs; 1102 List<uint> xlIDs = lIDs;
926 string xparm = parm; 1103 string xparm = parm;
927 TaintedObject("BSScene.UpdateParameterSet", delegate() { 1104 TaintedObject(DetailLogZero, "BSScene.UpdateParameterSet", delegate() {
928 BSParam.ParameterDefnBase thisParam; 1105 BSParam.ParameterDefnBase thisParam;
929 if (BSParam.TryGetParameter(xparm, out thisParam)) 1106 if (BSParam.TryGetParameter(xparm, out thisParam))
930 { 1107 {
@@ -963,8 +1140,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
963 public void DetailLog(string msg, params Object[] args) 1140 public void DetailLog(string msg, params Object[] args)
964 { 1141 {
965 PhysicsLogging.Write(msg, args); 1142 PhysicsLogging.Write(msg, args);
966 // Add the Flush() if debugging crashes. Gets all the messages written out.
967 if (m_physicsLoggingDoFlush) PhysicsLogging.Flush();
968 } 1143 }
969 // Used to fill in the LocalID when there isn't one. It's the correct number of characters. 1144 // Used to fill in the LocalID when there isn't one. It's the correct number of characters.
970 public const string DetailLogZero = "0000000000"; 1145 public const string DetailLogZero = "0000000000";