diff options
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSScene.cs')
-rw-r--r-- | OpenSim/Region/Physics/BulletSPlugin/BSScene.cs | 383 |
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"; |