diff options
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSScene.cs')
-rw-r--r-- | OpenSim/Region/Physics/BulletSPlugin/BSScene.cs | 332 |
1 files changed, 248 insertions, 84 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index e6aefd5..c92c9b9 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 | ||
@@ -188,6 +209,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
188 | PhysObjects = new Dictionary<uint, BSPhysObject>(); | 209 | PhysObjects = new Dictionary<uint, BSPhysObject>(); |
189 | Shapes = new BSShapeCollection(this); | 210 | Shapes = new BSShapeCollection(this); |
190 | 211 | ||
212 | m_simulatedTime = 0f; | ||
213 | LastTimeStep = 0.1f; | ||
214 | |||
191 | // Allocate pinned memory to pass parameters. | 215 | // Allocate pinned memory to pass parameters. |
192 | UnmanagedParams = new ConfigurationParameters[1]; | 216 | UnmanagedParams = new ConfigurationParameters[1]; |
193 | 217 | ||
@@ -202,8 +226,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
202 | // can be left in and every call doesn't have to check for null. | 226 | // can be left in and every call doesn't have to check for null. |
203 | if (m_physicsLoggingEnabled) | 227 | if (m_physicsLoggingEnabled) |
204 | { | 228 | { |
205 | PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes); | 229 | 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. | 230 | PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output its own error messages. |
207 | } | 231 | } |
208 | else | 232 | else |
209 | { | 233 | { |
@@ -227,10 +251,20 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
227 | TerrainManager = new BSTerrainManager(this); | 251 | TerrainManager = new BSTerrainManager(this); |
228 | TerrainManager.CreateInitialGroundPlaneAndTerrain(); | 252 | TerrainManager.CreateInitialGroundPlaneAndTerrain(); |
229 | 253 | ||
230 | m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation); | 254 | // Put some informational messages into the log file. |
255 | m_log.InfoFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation); | ||
231 | 256 | ||
232 | InTaintTime = false; | 257 | InTaintTime = false; |
233 | m_initialized = true; | 258 | m_initialized = true; |
259 | |||
260 | // If the physics engine runs on its own thread, start same. | ||
261 | if (BSParam.UseSeparatePhysicsThread) | ||
262 | { | ||
263 | // The physics simulation should happen independently of the heartbeat loop | ||
264 | m_physicsThread = new Thread(BulletSPluginPhysicsThread); | ||
265 | m_physicsThread.Name = BulletEngineName; | ||
266 | m_physicsThread.Start(); | ||
267 | } | ||
234 | } | 268 | } |
235 | 269 | ||
236 | // All default parameter values are set here. There should be no values set in the | 270 | // All default parameter values are set here. There should be no values set in the |
@@ -268,6 +302,13 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
268 | // Do any replacements in the parameters | 302 | // Do any replacements in the parameters |
269 | m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName); | 303 | m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName); |
270 | } | 304 | } |
305 | else | ||
306 | { | ||
307 | // Nothing in the configuration INI file so assume unmanaged and other defaults. | ||
308 | BulletEngineName = "BulletUnmanaged"; | ||
309 | m_physicsLoggingEnabled = false; | ||
310 | VehicleLoggingEnabled = false; | ||
311 | } | ||
271 | 312 | ||
272 | // The material characteristics. | 313 | // The material characteristics. |
273 | BSMaterials.InitializeFromDefaults(Params); | 314 | BSMaterials.InitializeFromDefaults(Params); |
@@ -311,11 +352,22 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
311 | 352 | ||
312 | switch (selectionName) | 353 | switch (selectionName) |
313 | { | 354 | { |
355 | case "bullet": | ||
314 | case "bulletunmanaged": | 356 | case "bulletunmanaged": |
315 | ret = new BSAPIUnman(engineName, this); | 357 | ret = new BSAPIUnman(engineName, this); |
316 | break; | 358 | break; |
317 | case "bulletxna": | 359 | case "bulletxna": |
318 | ret = new BSAPIXNA(engineName, this); | 360 | ret = new BSAPIXNA(engineName, this); |
361 | // Disable some features that are not implemented in BulletXNA | ||
362 | m_log.InfoFormat("{0} Disabling some physics features not implemented by BulletXNA", LogHeader); | ||
363 | m_log.InfoFormat("{0} Disabling ShouldUseBulletHACD", LogHeader); | ||
364 | BSParam.ShouldUseBulletHACD = false; | ||
365 | m_log.InfoFormat("{0} Disabling ShouldUseSingleConvexHullForPrims", LogHeader); | ||
366 | BSParam.ShouldUseSingleConvexHullForPrims = false; | ||
367 | m_log.InfoFormat("{0} Disabling ShouldUseGImpactShapeForPrims", LogHeader); | ||
368 | BSParam.ShouldUseGImpactShapeForPrims = false; | ||
369 | m_log.InfoFormat("{0} Setting terrain implimentation to Heightmap", LogHeader); | ||
370 | BSParam.TerrainImplementation = (float)BSTerrainPhys.TerrainImplementation.Heightmap; | ||
319 | break; | 371 | break; |
320 | } | 372 | } |
321 | 373 | ||
@@ -325,7 +377,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
325 | } | 377 | } |
326 | else | 378 | else |
327 | { | 379 | { |
328 | m_log.WarnFormat("{0} Selected bullet engine {1} -> {2}/{3}", LogHeader, engineName, ret.BulletEngineName, ret.BulletEngineVersion); | 380 | m_log.InfoFormat("{0} Selected bullet engine {1} -> {2}/{3}", LogHeader, engineName, ret.BulletEngineName, ret.BulletEngineVersion); |
329 | } | 381 | } |
330 | 382 | ||
331 | return ret; | 383 | return ret; |
@@ -463,7 +515,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
463 | 515 | ||
464 | if (!m_initialized) return null; | 516 | if (!m_initialized) return null; |
465 | 517 | ||
466 | DetailLog("{0},BSScene.AddPrimShape,call", localID); | 518 | // DetailLog("{0},BSScene.AddPrimShape,call", localID); |
467 | 519 | ||
468 | BSPhysObject prim = new BSPrimLinkable(localID, primName, this, position, size, rotation, pbs, isPhysical); | 520 | BSPhysObject prim = new BSPrimLinkable(localID, primName, this, position, size, rotation, pbs, isPhysical); |
469 | lock (PhysObjects) PhysObjects.Add(localID, prim); | 521 | lock (PhysObjects) PhysObjects.Add(localID, prim); |
@@ -478,25 +530,41 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
478 | #endregion // Prim and Avatar addition and removal | 530 | #endregion // Prim and Avatar addition and removal |
479 | 531 | ||
480 | #region Simulation | 532 | #region Simulation |
481 | // Simulate one timestep | 533 | |
534 | // Call from the simulator to send physics information to the simulator objects. | ||
535 | // This pushes all the collision and property update events into the objects in | ||
536 | // the simulator and, since it is on the heartbeat thread, there is an implicit | ||
537 | // locking of those data structures from other heartbeat events. | ||
538 | // If the physics engine is running on a separate thread, the update information | ||
539 | // will be in the ObjectsWithCollions and ObjectsWithUpdates structures. | ||
482 | public override float Simulate(float timeStep) | 540 | public override float Simulate(float timeStep) |
483 | { | 541 | { |
542 | if (!BSParam.UseSeparatePhysicsThread) | ||
543 | { | ||
544 | DoPhysicsStep(timeStep); | ||
545 | } | ||
546 | return SendUpdatesToSimulator(timeStep); | ||
547 | } | ||
548 | |||
549 | // Call the physics engine to do one 'timeStep' and collect collisions and updates | ||
550 | // into ObjectsWithCollisions and ObjectsWithUpdates data structures. | ||
551 | private void DoPhysicsStep(float timeStep) | ||
552 | { | ||
484 | // prevent simulation until we've been initialized | 553 | // prevent simulation until we've been initialized |
485 | if (!m_initialized) return 5.0f; | 554 | if (!m_initialized) return; |
486 | 555 | ||
487 | LastTimeStep = timeStep; | 556 | LastTimeStep = timeStep; |
488 | 557 | ||
489 | int updatedEntityCount = 0; | 558 | int updatedEntityCount = 0; |
490 | int collidersCount = 0; | 559 | int collidersCount = 0; |
491 | 560 | ||
492 | int beforeTime = 0; | 561 | int beforeTime = Util.EnvironmentTickCount(); |
493 | int simTime = 0; | 562 | int simTime = 0; |
494 | 563 | ||
495 | // update the prim states while we know the physics engine is not busy | ||
496 | int numTaints = _taintOperations.Count; | 564 | int numTaints = _taintOperations.Count; |
497 | |||
498 | InTaintTime = true; // Only used for debugging so locking is not necessary. | 565 | InTaintTime = true; // Only used for debugging so locking is not necessary. |
499 | 566 | ||
567 | // update the prim states while we know the physics engine is not busy | ||
500 | ProcessTaints(); | 568 | ProcessTaints(); |
501 | 569 | ||
502 | // Some of the physical objects requre individual, pre-step calls | 570 | // Some of the physical objects requre individual, pre-step calls |
@@ -519,18 +587,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
519 | int numSubSteps = 0; | 587 | int numSubSteps = 0; |
520 | try | 588 | try |
521 | { | 589 | { |
522 | if (PhysicsLogging.Enabled) | ||
523 | beforeTime = Util.EnvironmentTickCount(); | ||
524 | |||
525 | numSubSteps = PE.PhysicsStep(World, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out collidersCount); | 590 | numSubSteps = PE.PhysicsStep(World, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out collidersCount); |
526 | 591 | ||
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 | } | 592 | } |
535 | catch (Exception e) | 593 | catch (Exception e) |
536 | { | 594 | { |
@@ -542,77 +600,63 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
542 | collidersCount = 0; | 600 | collidersCount = 0; |
543 | } | 601 | } |
544 | 602 | ||
603 | // Make the physics engine dump useful statistics periodically | ||
545 | if (PhysicsMetricDumpFrames != 0 && ((m_simulationStep % PhysicsMetricDumpFrames) == 0)) | 604 | if (PhysicsMetricDumpFrames != 0 && ((m_simulationStep % PhysicsMetricDumpFrames) == 0)) |
546 | PE.DumpPhysicsStatistics(World); | 605 | PE.DumpPhysicsStatistics(World); |
547 | 606 | ||
548 | // Get a value for 'now' so all the collision and update routines don't have to get their own. | 607 | // Get a value for 'now' so all the collision and update routines don't have to get their own. |
549 | SimulationNowTime = Util.EnvironmentTickCount(); | 608 | SimulationNowTime = Util.EnvironmentTickCount(); |
550 | 609 | ||
551 | // If there were collisions, process them by sending the event to the prim. | 610 | // Send collision information to the colliding objects. The objects decide if the collision |
552 | // Collisions must be processed before updates. | 611 | // is 'real' (like linksets don't collide with themselves) and the individual objects |
553 | if (collidersCount > 0) | 612 | // know if the simulator has subscribed to collisions. |
613 | lock (CollisionLock) | ||
554 | { | 614 | { |
555 | for (int ii = 0; ii < collidersCount; ii++) | 615 | if (collidersCount > 0) |
556 | { | 616 | { |
557 | uint cA = m_collisionArray[ii].aID; | 617 | 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 | { | 618 | { |
574 | // If the object is done colliding, see that it's removed from the colliding list | 619 | uint cA = m_collisionArray[ii].aID; |
575 | ObjectsWithNoMoreCollisions.Add(bsp); | 620 | uint cB = m_collisionArray[ii].bID; |
621 | Vector3 point = m_collisionArray[ii].point; | ||
622 | Vector3 normal = m_collisionArray[ii].normal; | ||
623 | float penetration = m_collisionArray[ii].penetration; | ||
624 | SendCollision(cA, cB, point, normal, penetration); | ||
625 | SendCollision(cB, cA, point, -normal, penetration); | ||
576 | } | 626 | } |
627 | } | ||
577 | } | 628 | } |
578 | 629 | ||
579 | // This is a kludge to get avatar movement updates. | 630 | // 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. | 631 | // and remember that there was a change so it will be passed to the simulator. |
581 | // The event updates avatar animations and stuff. | 632 | 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 | { | 633 | { |
603 | for (int ii = 0; ii < updatedEntityCount; ii++) | 634 | if (updatedEntityCount > 0) |
604 | { | 635 | { |
605 | EntityProperties entprop = m_updateArray[ii]; | 636 | for (int ii = 0; ii < updatedEntityCount; ii++) |
606 | BSPhysObject pobj; | ||
607 | if (PhysObjects.TryGetValue(entprop.ID, out pobj)) | ||
608 | { | 637 | { |
609 | pobj.UpdateProperties(entprop); | 638 | EntityProperties entprop = m_updateArray[ii]; |
639 | BSPhysObject pobj; | ||
640 | if (PhysObjects.TryGetValue(entprop.ID, out pobj)) | ||
641 | { | ||
642 | if (pobj.IsInitialized) | ||
643 | pobj.UpdateProperties(entprop); | ||
644 | } | ||
610 | } | 645 | } |
611 | } | 646 | } |
612 | } | 647 | } |
613 | 648 | ||
649 | // Some actors want to know when the simulation step is complete. | ||
614 | TriggerPostStepEvent(timeStep); | 650 | TriggerPostStepEvent(timeStep); |
615 | 651 | ||
652 | simTime = Util.EnvironmentTickCountSubtract(beforeTime); | ||
653 | if (PhysicsLogging.Enabled) | ||
654 | { | ||
655 | DetailLog("{0},DoPhysicsStep,complete,frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}", | ||
656 | DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, | ||
657 | updatedEntityCount, collidersCount, ObjectsWithCollisions.Count); | ||
658 | } | ||
659 | |||
616 | // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world. | 660 | // 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. | 661 | // Only enable this in a limited test world with few objects. |
618 | if (m_physicsPhysicalDumpEnabled) | 662 | if (m_physicsPhysicalDumpEnabled) |
@@ -621,7 +665,84 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
621 | // The physics engine returns the number of milliseconds it simulated this call. | 665 | // 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. | 666 | // 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). | 667 | // 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; | 668 | m_simulatedTime += (float)numSubSteps * m_fixedTimeStep * 1000f * NominalFrameRate; |
669 | } | ||
670 | |||
671 | // Called by a BSPhysObject to note that it has changed properties and this information | ||
672 | // should be passed up to the simulator at the proper time. | ||
673 | // Note: this is called by the BSPhysObject from invocation via DoPhysicsStep() above so | ||
674 | // this is is under UpdateLock. | ||
675 | public void PostUpdate(BSPhysObject updatee) | ||
676 | { | ||
677 | ObjectsWithUpdates.Add(updatee); | ||
678 | } | ||
679 | |||
680 | // The simulator thinks it is physics time so return all the collisions and position | ||
681 | // updates that were collected in actual physics simulation. | ||
682 | private float SendUpdatesToSimulator(float timeStep) | ||
683 | { | ||
684 | if (!m_initialized) return 5.0f; | ||
685 | |||
686 | DetailLog("{0},SendUpdatesToSimulator,collisions={1},updates={2},simedTime={3}", | ||
687 | BSScene.DetailLogZero, ObjectsWithCollisions.Count, ObjectsWithUpdates.Count, m_simulatedTime); | ||
688 | // Push the collisions into the simulator. | ||
689 | lock (CollisionLock) | ||
690 | { | ||
691 | if (ObjectsWithCollisions.Count > 0) | ||
692 | { | ||
693 | foreach (BSPhysObject bsp in ObjectsWithCollisions) | ||
694 | if (!bsp.SendCollisions()) | ||
695 | { | ||
696 | // If the object is done colliding, see that it's removed from the colliding list | ||
697 | ObjectsWithNoMoreCollisions.Add(bsp); | ||
698 | } | ||
699 | } | ||
700 | |||
701 | // This is a kludge to get avatar movement updates. | ||
702 | // The simulator expects collisions for avatars even if there are have been no collisions. | ||
703 | // The event updates avatar animations and stuff. | ||
704 | // If you fix avatar animation updates, remove this overhead and let normal collision processing happen. | ||
705 | foreach (BSPhysObject bsp in m_avatars) | ||
706 | if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice | ||
707 | bsp.SendCollisions(); | ||
708 | |||
709 | // Objects that are done colliding are removed from the ObjectsWithCollisions list. | ||
710 | // Not done above because it is inside an iteration of ObjectWithCollisions. | ||
711 | // This complex collision processing is required to create an empty collision | ||
712 | // event call after all real collisions have happened on an object. This allows | ||
713 | // the simulator to generate the 'collision end' event. | ||
714 | if (ObjectsWithNoMoreCollisions.Count > 0) | ||
715 | { | ||
716 | foreach (BSPhysObject po in ObjectsWithNoMoreCollisions) | ||
717 | ObjectsWithCollisions.Remove(po); | ||
718 | ObjectsWithNoMoreCollisions.Clear(); | ||
719 | } | ||
720 | } | ||
721 | |||
722 | // Call the simulator for each object that has physics property updates. | ||
723 | HashSet<BSPhysObject> updatedObjects = null; | ||
724 | lock (UpdateLock) | ||
725 | { | ||
726 | if (ObjectsWithUpdates.Count > 0) | ||
727 | { | ||
728 | updatedObjects = ObjectsWithUpdates; | ||
729 | ObjectsWithUpdates = new HashSet<BSPhysObject>(); | ||
730 | } | ||
731 | } | ||
732 | if (updatedObjects != null) | ||
733 | { | ||
734 | foreach (BSPhysObject obj in updatedObjects) | ||
735 | { | ||
736 | obj.RequestPhysicsterseUpdate(); | ||
737 | } | ||
738 | updatedObjects.Clear(); | ||
739 | } | ||
740 | |||
741 | // Return the framerate simulated to give the above returned results. | ||
742 | // (Race condition here but this is just bookkeeping so rare mistakes do not merit a lock). | ||
743 | float simTime = m_simulatedTime; | ||
744 | m_simulatedTime = 0f; | ||
745 | return simTime; | ||
625 | } | 746 | } |
626 | 747 | ||
627 | // Something has collided | 748 | // Something has collided |
@@ -640,21 +761,49 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
640 | return; | 761 | return; |
641 | } | 762 | } |
642 | 763 | ||
643 | // The terrain is not in the physical object list so 'collidee' can be null when Collide() is called. | 764 | // Note: the terrain is not in the physical object list so 'collidee' can be null when Collide() is called. |
644 | BSPhysObject collidee = null; | 765 | BSPhysObject collidee = null; |
645 | PhysObjects.TryGetValue(collidingWith, out collidee); | 766 | PhysObjects.TryGetValue(collidingWith, out collidee); |
646 | 767 | ||
647 | // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith); | 768 | // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith); |
648 | 769 | ||
649 | if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration)) | 770 | if (collider.IsInitialized) |
650 | { | 771 | { |
651 | // If a collision was posted, remember to send it to the simulator | 772 | if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration)) |
652 | ObjectsWithCollisions.Add(collider); | 773 | { |
774 | // If a collision was 'good', remember to send it to the simulator | ||
775 | ObjectsWithCollisions.Add(collider); | ||
776 | } | ||
653 | } | 777 | } |
654 | 778 | ||
655 | return; | 779 | return; |
656 | } | 780 | } |
657 | 781 | ||
782 | public void BulletSPluginPhysicsThread() | ||
783 | { | ||
784 | while (m_initialized) | ||
785 | { | ||
786 | int beginSimulationRealtimeMS = Util.EnvironmentTickCount(); | ||
787 | DoPhysicsStep(BSParam.PhysicsTimeStep); | ||
788 | int simulationRealtimeMS = Util.EnvironmentTickCountSubtract(beginSimulationRealtimeMS); | ||
789 | int simulationTimeVsRealtimeDifferenceMS = ((int)(BSParam.PhysicsTimeStep*1000f)) - simulationRealtimeMS; | ||
790 | |||
791 | if (simulationTimeVsRealtimeDifferenceMS > 0) | ||
792 | { | ||
793 | // The simulation of the time interval took less than realtime. | ||
794 | // Do a sleep for the rest of realtime. | ||
795 | Thread.Sleep(simulationTimeVsRealtimeDifferenceMS); | ||
796 | } | ||
797 | else | ||
798 | { | ||
799 | // The simulation took longer than realtime. | ||
800 | // Do some scaling of simulation time. | ||
801 | // TODO. | ||
802 | DetailLog("{0},BulletSPluginPhysicsThread,longerThanRealtime={1}", BSScene.DetailLogZero, simulationTimeVsRealtimeDifferenceMS); | ||
803 | } | ||
804 | } | ||
805 | } | ||
806 | |||
658 | #endregion // Simulation | 807 | #endregion // Simulation |
659 | 808 | ||
660 | public override void GetResults() { } | 809 | public override void GetResults() { } |
@@ -717,6 +866,23 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
717 | 866 | ||
718 | public override bool IsThreaded { get { return false; } } | 867 | public override bool IsThreaded { get { return false; } } |
719 | 868 | ||
869 | #region Extensions | ||
870 | // ============================================================= | ||
871 | // Per scene functions. See below. | ||
872 | |||
873 | // Per avatar functions. See BSCharacter. | ||
874 | |||
875 | // Per prim functions. See BSPrim. | ||
876 | public const string PhysFunctGetLinksetType = "BulletSim.GetLinksetType"; | ||
877 | public const string PhysFunctSetLinksetType = "BulletSim.SetLinksetType"; | ||
878 | // ============================================================= | ||
879 | |||
880 | public override object Extension(string pFunct, params object[] pParams) | ||
881 | { | ||
882 | return base.Extension(pFunct, pParams); | ||
883 | } | ||
884 | #endregion // Extensions | ||
885 | |||
720 | #region Taints | 886 | #region Taints |
721 | // The simulation execution order is: | 887 | // The simulation execution order is: |
722 | // Simulate() | 888 | // Simulate() |
@@ -780,7 +946,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
780 | 946 | ||
781 | private void ProcessRegularTaints() | 947 | private void ProcessRegularTaints() |
782 | { | 948 | { |
783 | if (_taintOperations.Count > 0) // save allocating new list if there is nothing to process | 949 | if (m_initialized && _taintOperations.Count > 0) // save allocating new list if there is nothing to process |
784 | { | 950 | { |
785 | // swizzle a new list into the list location so we can process what's there | 951 | // swizzle a new list into the list location so we can process what's there |
786 | List<TaintCallbackEntry> oldList; | 952 | List<TaintCallbackEntry> oldList; |
@@ -823,7 +989,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
823 | // Taints that happen after the normal taint processing but before the simulation step. | 989 | // Taints that happen after the normal taint processing but before the simulation step. |
824 | private void ProcessPostTaintTaints() | 990 | private void ProcessPostTaintTaints() |
825 | { | 991 | { |
826 | if (_postTaintOperations.Count > 0) | 992 | if (m_initialized && _postTaintOperations.Count > 0) |
827 | { | 993 | { |
828 | Dictionary<string, TaintCallbackEntry> oldList; | 994 | Dictionary<string, TaintCallbackEntry> oldList; |
829 | lock (_taintLock) | 995 | lock (_taintLock) |
@@ -963,8 +1129,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
963 | public void DetailLog(string msg, params Object[] args) | 1129 | public void DetailLog(string msg, params Object[] args) |
964 | { | 1130 | { |
965 | PhysicsLogging.Write(msg, args); | 1131 | PhysicsLogging.Write(msg, args); |
966 | // Add the Flush() if debugging crashes. Gets all the messages written out. | ||
967 | if (m_physicsLoggingDoFlush) PhysicsLogging.Flush(); | ||
968 | } | 1132 | } |
969 | // Used to fill in the LocalID when there isn't one. It's the correct number of characters. | 1133 | // Used to fill in the LocalID when there isn't one. It's the correct number of characters. |
970 | public const string DetailLogZero = "0000000000"; | 1134 | public const string DetailLogZero = "0000000000"; |