aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
diff options
context:
space:
mode:
authorDiva Canto2012-09-15 19:35:33 -0700
committerDiva Canto2012-09-15 19:35:33 -0700
commitdaa4745fb785d30de256f04c763bfd6478ea2238 (patch)
treea68b49adb21eb5902f0e3a9687878e6c6db5e014 /OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
parentMore on HG2.0: added the possibility of controlling the appearance that avies... (diff)
parentBulletSim: update DLLs and SOs and remove some debugging code. (diff)
downloadopensim-SC_OLD-daa4745fb785d30de256f04c763bfd6478ea2238.zip
opensim-SC_OLD-daa4745fb785d30de256f04c763bfd6478ea2238.tar.gz
opensim-SC_OLD-daa4745fb785d30de256f04c763bfd6478ea2238.tar.bz2
opensim-SC_OLD-daa4745fb785d30de256f04c763bfd6478ea2238.tar.xz
Merge branch 'master' of ssh://opensimulator.org/var/git/opensim
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSScene.cs')
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSScene.cs230
1 files changed, 53 insertions, 177 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
index 4a468af..52997dd 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
@@ -56,7 +56,6 @@ using OpenMetaverse;
56// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect 56// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect
57// Implement LockAngularMotion 57// Implement LockAngularMotion
58// Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation) 58// Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation)
59// Does NeedsMeshing() really need to exclude all the different shapes?
60// Remove mesh and Hull stuff. Use mesh passed to bullet and use convexdecom from bullet. 59// Remove mesh and Hull stuff. Use mesh passed to bullet and use convexdecom from bullet.
61// Add PID movement operations. What does ScenePresence.MoveToTarget do? 60// Add PID movement operations. What does ScenePresence.MoveToTarget do?
62// Check terrain size. 128 or 127? 61// Check terrain size. 128 or 127?
@@ -79,7 +78,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
79 private HashSet<BSPhysObject> m_objectsWithCollisions = new HashSet<BSPhysObject>(); 78 private HashSet<BSPhysObject> m_objectsWithCollisions = new HashSet<BSPhysObject>();
80 // Following is a kludge and can be removed when avatar animation updating is 79 // Following is a kludge and can be removed when avatar animation updating is
81 // moved to a better place. 80 // moved to a better place.
82 private HashSet<BSCharacter> m_avatarsWithCollisions = new HashSet<BSCharacter>(); 81 private HashSet<BSPhysObject> m_avatarsWithCollisions = new HashSet<BSPhysObject>();
83 82
84 // List of all the objects that have vehicle properties and should be called 83 // List of all the objects that have vehicle properties and should be called
85 // to update each physics step. 84 // to update each physics step.
@@ -111,11 +110,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
111 private long m_simulationStep = 0; 110 private long m_simulationStep = 0;
112 public long SimulationStep { get { return m_simulationStep; } } 111 public long SimulationStep { get { return m_simulationStep; } }
113 112
114 // The length of the last timestep we were asked to simulate.
115 // This is used by the vehicle code. Since the vehicle code is called
116 // once per simulation step, its constants need to be scaled by this.
117 public float LastSimulatedTimestep { get; private set; }
118
119 // A value of the time now so all the collision and update routines do not have to get their own 113 // A value of the time now so all the collision and update routines do not have to get their own
120 // Set to 'now' just before all the prims and actors are called for collisions and updates 114 // Set to 'now' just before all the prims and actors are called for collisions and updates
121 public int SimulationNowTime { get; private set; } 115 public int SimulationNowTime { get; private set; }
@@ -132,8 +126,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
132 private EntityProperties[] m_updateArray; 126 private EntityProperties[] m_updateArray;
133 private GCHandle m_updateArrayPinnedHandle; 127 private GCHandle m_updateArrayPinnedHandle;
134 128
135 private bool _meshSculptedPrim = true; // cause scuplted prims to get meshed 129 public bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed
136 private bool _forceSimplePrimMeshing = false; // if a cube or sphere, let Bullet do internal shapes 130 public bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes
137 131
138 public float PID_D { get; private set; } // derivative 132 public float PID_D { get; private set; } // derivative
139 public float PID_P { get; private set; } // proportional 133 public float PID_P { get; private set; } // proportional
@@ -153,6 +147,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
153 { 147 {
154 get { return new Vector3(0f, 0f, Params.gravity); } 148 get { return new Vector3(0f, 0f, Params.gravity); }
155 } 149 }
150 // Just the Z value of the gravity
151 public float DefaultGravityZ
152 {
153 get { return Params.gravity; }
154 }
156 155
157 public float MaximumObjectMass { get; private set; } 156 public float MaximumObjectMass { get; private set; }
158 157
@@ -171,8 +170,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
171 callback = c; 170 callback = c;
172 } 171 }
173 } 172 }
173 private Object _taintLock = new Object(); // lock for using the next object
174 private List<TaintCallbackEntry> _taintedObjects; 174 private List<TaintCallbackEntry> _taintedObjects;
175 private Object _taintLock = new Object();
176 175
177 // A pointer to an instance if this structure is passed to the C++ code 176 // A pointer to an instance if this structure is passed to the C++ code
178 // Used to pass basic configuration values to the unmanaged code. 177 // Used to pass basic configuration values to the unmanaged code.
@@ -465,12 +464,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
465 int collidersCount = 0; 464 int collidersCount = 0;
466 IntPtr collidersPtr; 465 IntPtr collidersPtr;
467 466
468 LastSimulatedTimestep = timeStep;
469
470 // prevent simulation until we've been initialized 467 // prevent simulation until we've been initialized
471 if (!m_initialized) return 10.0f; 468 if (!m_initialized) return 5.0f;
472
473 int simulateStartTime = Util.EnvironmentTickCount();
474 469
475 // update the prim states while we know the physics engine is not busy 470 // update the prim states while we know the physics engine is not busy
476 int numTaints = _taintedObjects.Count; 471 int numTaints = _taintedObjects.Count;
@@ -478,6 +473,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
478 473
479 // Some of the prims operate with special vehicle properties 474 // Some of the prims operate with special vehicle properties
480 ProcessVehicles(timeStep); 475 ProcessVehicles(timeStep);
476 numTaints += _taintedObjects.Count;
481 ProcessTaints(); // the vehicles might have added taints 477 ProcessTaints(); // the vehicles might have added taints
482 478
483 // step the physical world one interval 479 // step the physical world one interval
@@ -506,6 +502,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters
506 // Get a value for 'now' so all the collision and update routines don't have to get their own 502 // Get a value for 'now' so all the collision and update routines don't have to get their own
507 SimulationNowTime = Util.EnvironmentTickCount(); 503 SimulationNowTime = Util.EnvironmentTickCount();
508 504
505 // This is a kludge to get avatar movement updates.
506 // ODE sends collisions for avatars even if there are have been no collisions. This updates
507 // avatar animations and stuff.
508 // If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
509 m_objectsWithCollisions = new HashSet<BSPhysObject>(m_avatarsWithCollisions);
510
509 // If there were collisions, process them by sending the event to the prim. 511 // If there were collisions, process them by sending the event to the prim.
510 // Collisions must be processed before updates. 512 // Collisions must be processed before updates.
511 if (collidersCount > 0) 513 if (collidersCount > 0)
@@ -527,13 +529,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
527 bsp.SendCollisions(); 529 bsp.SendCollisions();
528 m_objectsWithCollisions.Clear(); 530 m_objectsWithCollisions.Clear();
529 531
530 // This is a kludge to get avatar movement updated.
531 // ODE sends collisions even if there are none and this is used to update
532 // avatar animations and stuff.
533 foreach (BSPhysObject bpo in m_avatarsWithCollisions)
534 bpo.SendCollisions();
535 // m_avatarsWithCollisions.Clear();
536
537 // If any of the objects had updated properties, tell the object it has been changed by the physics engine 532 // If any of the objects had updated properties, tell the object it has been changed by the physics engine
538 if (updatedEntityCount > 0) 533 if (updatedEntityCount > 0)
539 { 534 {
@@ -544,7 +539,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
544 if (PhysObjects.TryGetValue(entprop.ID, out pobj)) 539 if (PhysObjects.TryGetValue(entprop.ID, out pobj))
545 { 540 {
546 pobj.UpdateProperties(entprop); 541 pobj.UpdateProperties(entprop);
547 continue;
548 } 542 }
549 } 543 }
550 } 544 }
@@ -558,18 +552,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters
558 } 552 }
559 } 553 }
560 554
561 // this is a waste since the outside routine also calcuates the physics simulation 555 // The physics engine returns the number of milliseconds it simulated this call.
562 // period. TODO: There should be a way of computing physics frames from simulator computation. 556 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
563 // long simulateTotalTime = Util.EnvironmentTickCountSubtract(simulateStartTime); 557 // Since Bullet normally does 5 or 6 substeps, this will normally sum to about 60 FPS.
564 // return (timeStep * (float)simulateTotalTime); 558 return numSubSteps * m_fixedTimeStep;
565
566 // TODO: FIX THIS: fps calculation possibly wrong.
567 // This calculation says 1/timeStep is the ideal frame rate. Any time added to
568 // that by the physics simulation gives a slower frame rate.
569 long totalSimulationTime = Util.EnvironmentTickCountSubtract(simulateStartTime);
570 if (totalSimulationTime >= timeStep)
571 return 0;
572 return 1f / (timeStep + totalSimulationTime);
573 } 559 }
574 560
575 // Something has collided 561 // Something has collided
@@ -580,28 +566,25 @@ public class BSScene : PhysicsScene, IPhysicsParameters
580 return; // don't send collisions to the terrain 566 return; // don't send collisions to the terrain
581 } 567 }
582 568
583 BSPhysObject collider = PhysObjects[localID]; 569 BSPhysObject collider;
584 // TODO: as of this code, terrain was not in the physical object list. 570 if (!PhysObjects.TryGetValue(localID, out collider))
585 // When BSTerrain is created and it will be in the list, we can remove
586 // the possibility that it's not there and just fetch the collidee.
587 BSPhysObject collidee = null;
588
589 ActorTypes type = ActorTypes.Prim;
590 if (collidingWith <= TerrainManager.HighestTerrainID)
591 {
592 type = ActorTypes.Ground;
593 }
594 else
595 { 571 {
596 collidee = PhysObjects[collidingWith]; 572 // If the object that is colliding cannot be found, just ignore the collision.
597 if (collidee is BSCharacter) 573 return;
598 type = ActorTypes.Agent;
599 } 574 }
600 575
576 // The terrain is not in the physical object list so 'collidee'
577 // can be null when Collide() is called.
578 BSPhysObject collidee = null;
579 PhysObjects.TryGetValue(collidingWith, out collidee);
580
601 // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith); 581 // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
602 582
603 collider.Collide(collidingWith, collidee, type, collidePoint, collideNormal, penetration); 583 if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
604 m_objectsWithCollisions.Add(collider); 584 {
585 // If a collision was posted, remember to send it to the simulator
586 m_objectsWithCollisions.Add(collider);
587 }
605 588
606 return; 589 return;
607 } 590 }
@@ -619,9 +602,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters
619 public override void SetWaterLevel(float baseheight) 602 public override void SetWaterLevel(float baseheight)
620 { 603 {
621 m_waterLevel = baseheight; 604 m_waterLevel = baseheight;
622 // TODO: pass to physics engine so things will float?
623 } 605 }
624 public float GetWaterLevel() 606 // Someday....
607 public float GetWaterLevelAtXYZ(Vector3 loc)
625 { 608 {
626 return m_waterLevel; 609 return m_waterLevel;
627 } 610 }
@@ -659,121 +642,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
659 642
660 public override bool IsThreaded { get { return false; } } 643 public override bool IsThreaded { get { return false; } }
661 644
662 /// <summary>
663 /// Routine to figure out if we need to mesh this prim with our mesher
664 /// </summary>
665 /// <param name="pbs"></param>
666 /// <returns>true if the prim needs meshing</returns>
667 public bool NeedsMeshing(PrimitiveBaseShape pbs)
668 {
669 // most of this is redundant now as the mesher will return null if it cant mesh a prim
670 // but we still need to check for sculptie meshing being enabled so this is the most
671 // convenient place to do it for now...
672
673 // int iPropertiesNotSupportedDefault = 0;
674
675 if (pbs.SculptEntry && !_meshSculptedPrim)
676 {
677 // Render sculpties as boxes
678 return false;
679 }
680
681 // if it's a standard box or sphere with no cuts, hollows, twist or top shear, return false since Bullet
682 // can use an internal representation for the prim
683 if (!_forceSimplePrimMeshing)
684 {
685 if ((pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
686 || (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1
687 && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z))
688 {
689
690 if (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
691 && pbs.ProfileHollow == 0
692 && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0
693 && pbs.PathBegin == 0 && pbs.PathEnd == 0
694 && pbs.PathTaperX == 0 && pbs.PathTaperY == 0
695 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
696 && pbs.PathShearX == 0 && pbs.PathShearY == 0)
697 {
698 return false;
699 }
700 }
701 }
702
703 /* TODO: verify that the mesher will now do all these shapes
704 if (pbs.ProfileHollow != 0)
705 iPropertiesNotSupportedDefault++;
706
707 if ((pbs.PathBegin != 0) || pbs.PathEnd != 0)
708 iPropertiesNotSupportedDefault++;
709
710 if ((pbs.PathTwistBegin != 0) || (pbs.PathTwist != 0))
711 iPropertiesNotSupportedDefault++;
712
713 if ((pbs.ProfileBegin != 0) || pbs.ProfileEnd != 0)
714 iPropertiesNotSupportedDefault++;
715
716 if ((pbs.PathScaleX != 100) || (pbs.PathScaleY != 100))
717 iPropertiesNotSupportedDefault++;
718
719 if ((pbs.PathShearX != 0) || (pbs.PathShearY != 0))
720 iPropertiesNotSupportedDefault++;
721
722 if (pbs.ProfileShape == ProfileShape.Circle && pbs.PathCurve == (byte)Extrusion.Straight)
723 iPropertiesNotSupportedDefault++;
724
725 if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1 && (pbs.Scale.X != pbs.Scale.Y || pbs.Scale.Y != pbs.Scale.Z || pbs.Scale.Z != pbs.Scale.X))
726 iPropertiesNotSupportedDefault++;
727
728 if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte) Extrusion.Curve1)
729 iPropertiesNotSupportedDefault++;
730
731 // test for torus
732 if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Square)
733 {
734 if (pbs.PathCurve == (byte)Extrusion.Curve1)
735 {
736 iPropertiesNotSupportedDefault++;
737 }
738 }
739 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Circle)
740 {
741 if (pbs.PathCurve == (byte)Extrusion.Straight)
742 {
743 iPropertiesNotSupportedDefault++;
744 }
745 // ProfileCurve seems to combine hole shape and profile curve so we need to only compare against the lower 3 bits
746 else if (pbs.PathCurve == (byte)Extrusion.Curve1)
747 {
748 iPropertiesNotSupportedDefault++;
749 }
750 }
751 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle)
752 {
753 if (pbs.PathCurve == (byte)Extrusion.Curve1 || pbs.PathCurve == (byte)Extrusion.Curve2)
754 {
755 iPropertiesNotSupportedDefault++;
756 }
757 }
758 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle)
759 {
760 if (pbs.PathCurve == (byte)Extrusion.Straight)
761 {
762 iPropertiesNotSupportedDefault++;
763 }
764 else if (pbs.PathCurve == (byte)Extrusion.Curve1)
765 {
766 iPropertiesNotSupportedDefault++;
767 }
768 }
769 if (iPropertiesNotSupportedDefault == 0)
770 {
771 return false;
772 }
773 */
774 return true;
775 }
776
777 // Calls to the PhysicsActors can't directly call into the physics engine 645 // Calls to the PhysicsActors can't directly call into the physics engine
778 // because it might be busy. We delay changes to a known time. 646 // because it might be busy. We delay changes to a known time.
779 // We rely on C#'s closure to save and restore the context for the delegate. 647 // We rely on C#'s closure to save and restore the context for the delegate.
@@ -782,7 +650,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters
782 if (!m_initialized) return; 650 if (!m_initialized) return;
783 651
784 lock (_taintLock) 652 lock (_taintLock)
653 {
785 _taintedObjects.Add(new TaintCallbackEntry(ident, callback)); 654 _taintedObjects.Add(new TaintCallbackEntry(ident, callback));
655 }
656
786 return; 657 return;
787 } 658 }
788 659
@@ -919,14 +790,14 @@ public class BSScene : PhysicsScene, IPhysicsParameters
919 { 790 {
920 new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties", 791 new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties",
921 ConfigurationParameters.numericTrue, 792 ConfigurationParameters.numericTrue,
922 (s,cf,p,v) => { s._meshSculptedPrim = cf.GetBoolean(p, s.BoolNumeric(v)); }, 793 (s,cf,p,v) => { s.ShouldMeshSculptedPrim = cf.GetBoolean(p, s.BoolNumeric(v)); },
923 (s) => { return s.NumericBool(s._meshSculptedPrim); }, 794 (s) => { return s.NumericBool(s.ShouldMeshSculptedPrim); },
924 (s,p,l,v) => { s._meshSculptedPrim = s.BoolNumeric(v); } ), 795 (s,p,l,v) => { s.ShouldMeshSculptedPrim = s.BoolNumeric(v); } ),
925 new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects", 796 new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects",
926 ConfigurationParameters.numericFalse, 797 ConfigurationParameters.numericFalse,
927 (s,cf,p,v) => { s._forceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); }, 798 (s,cf,p,v) => { s.ShouldForceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); },
928 (s) => { return s.NumericBool(s._forceSimplePrimMeshing); }, 799 (s) => { return s.NumericBool(s.ShouldForceSimplePrimMeshing); },
929 (s,p,l,v) => { s._forceSimplePrimMeshing = s.BoolNumeric(v); } ), 800 (s,p,l,v) => { s.ShouldForceSimplePrimMeshing = s.BoolNumeric(v); } ),
930 801
931 new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)", 802 new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)",
932 8f, 803 8f,
@@ -1162,8 +1033,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
1162 (s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = cf.GetFloat(p, v); }, 1033 (s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = cf.GetFloat(p, v); },
1163 (s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; }, 1034 (s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; },
1164 (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ), 1035 (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ),
1165 new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=none, 1=all. Default=0", 1036 new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1",
1166 0.0f, 1037 0.1f,
1167 (s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); }, 1038 (s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); },
1168 (s) => { return s.m_params[0].linkConstraintCFM; }, 1039 (s) => { return s.m_params[0].linkConstraintCFM; },
1169 (s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ), 1040 (s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ),
@@ -1172,6 +1043,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
1172 (s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); }, 1043 (s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); },
1173 (s) => { return s.m_params[0].linkConstraintERP; }, 1044 (s) => { return s.m_params[0].linkConstraintERP; },
1174 (s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ), 1045 (s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ),
1046 new ParameterDefn("LinkConstraintSolverIterations", "Number of solver iterations when computing constraint. (0 = Bullet default)",
1047 40,
1048 (s,cf,p,v) => { s.m_params[0].linkConstraintSolverIterations = cf.GetFloat(p, v); },
1049 (s) => { return s.m_params[0].linkConstraintSolverIterations; },
1050 (s,p,l,v) => { s.m_params[0].linkConstraintSolverIterations = v; } ),
1175 1051
1176 new ParameterDefn("DetailedStats", "Frames between outputting detailed phys stats. (0 is off)", 1052 new ParameterDefn("DetailedStats", "Frames between outputting detailed phys stats. (0 is off)",
1177 0f, 1053 0f,