aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSScene.cs948
1 files changed, 204 insertions, 744 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
index 27a78d1..8075b73 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
@@ -26,6 +26,7 @@
26 */ 26 */
27using System; 27using System;
28using System.Collections.Generic; 28using System.Collections.Generic;
29using System.Reflection;
29using System.Runtime.InteropServices; 30using System.Runtime.InteropServices;
30using System.Text; 31using System.Text;
31using System.Threading; 32using System.Threading;
@@ -38,40 +39,22 @@ using Nini.Config;
38using log4net; 39using log4net;
39using OpenMetaverse; 40using OpenMetaverse;
40 41
41// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim)
42// Test sculpties (verified that they don't work)
43// Compute physics FPS reasonably
44// Based on material, set density and friction
45// Don't use constraints in linksets of non-physical objects. Means having to move children manually.
46// Four states of prim: Physical, regular, phantom and selected. Are we modeling these correctly?
47// In SL one can set both physical and phantom (gravity, does not effect others, makes collisions with ground)
48// At the moment, physical and phantom causes object to drop through the terrain
49// Physical phantom objects and related typing (collision options )
50// Check out llVolumeDetect. Must do something for that.
51// Use collision masks for collision with terrain and phantom objects
52// More efficient memory usage when passing hull information from BSPrim to BulletSim
53// Should prim.link() and prim.delink() membership checking happen at taint time?
54// Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once.
55// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect
56// Implement LockAngularMotion
57// Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation)
58// Remove mesh and Hull stuff. Use mesh passed to bullet and use convexdecom from bullet.
59// Add PID movement operations. What does ScenePresence.MoveToTarget do?
60// Check terrain size. 128 or 127?
61// Raycast
62//
63namespace OpenSim.Region.Physics.BulletSPlugin 42namespace OpenSim.Region.Physics.BulletSPlugin
64{ 43{
65public sealed class BSScene : PhysicsScene, IPhysicsParameters 44public sealed class BSScene : PhysicsScene, IPhysicsParameters
66{ 45{
67 private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 46 internal static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
68 private static readonly string LogHeader = "[BULLETS SCENE]"; 47 internal static readonly string LogHeader = "[BULLETS SCENE]";
69 48
70 // The name of the region we're working for. 49 // The name of the region we're working for.
71 public string RegionName { get; private set; } 50 public string RegionName { get; private set; }
72 51
73 public string BulletSimVersion = "?"; 52 public string BulletSimVersion = "?";
74 53
54 // The handle to the underlying managed or unmanaged version of Bullet being used.
55 public string BulletEngineName { get; private set; }
56 public BSAPITemplate PE;
57
75 public Dictionary<uint, BSPhysObject> PhysObjects; 58 public Dictionary<uint, BSPhysObject> PhysObjects;
76 public BSShapeCollection Shapes; 59 public BSShapeCollection Shapes;
77 60
@@ -82,32 +65,29 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
82 // every tick so OpenSim will update its animation. 65 // every tick so OpenSim will update its animation.
83 private HashSet<BSPhysObject> m_avatars = new HashSet<BSPhysObject>(); 66 private HashSet<BSPhysObject> m_avatars = new HashSet<BSPhysObject>();
84 67
85 // List of all the objects that have vehicle properties and should be called
86 // to update each physics step.
87 private List<BSPhysObject> m_vehicles = new List<BSPhysObject>();
88
89 // let my minuions use my logger 68 // let my minuions use my logger
90 public ILog Logger { get { return m_log; } } 69 public ILog Logger { get { return m_log; } }
91 70
92 public IMesher mesher; 71 public IMesher mesher;
93 // Level of Detail values kept as float because that's what the Meshmerizer wants
94 public float MeshLOD { get; private set; }
95 public float MeshMegaPrimLOD { get; private set; }
96 public float MeshMegaPrimThreshold { get; private set; }
97 public float SculptLOD { get; private set; }
98
99 public uint WorldID { get; private set; } 72 public uint WorldID { get; private set; }
100 public BulletSim World { get; private set; } 73 public BulletWorld World { get; private set; }
101 74
102 // All the constraints that have been allocated in this instance. 75 // All the constraints that have been allocated in this instance.
103 public BSConstraintCollection Constraints { get; private set; } 76 public BSConstraintCollection Constraints { get; private set; }
104 77
105 // Simulation parameters 78 // Simulation parameters
106 private int m_maxSubSteps; 79 internal int m_maxSubSteps;
107 private float m_fixedTimeStep; 80 internal float m_fixedTimeStep;
108 private long m_simulationStep = 0; 81 internal long m_simulationStep = 0;
82 internal float NominalFrameRate { get; set; }
109 public long SimulationStep { get { return m_simulationStep; } } 83 public long SimulationStep { get { return m_simulationStep; } }
110 private int m_taintsToProcessPerStep; 84 internal float LastTimeStep { get; private set; }
85
86 // Physical objects can register for prestep or poststep events
87 public delegate void PreStepAction(float timeStep);
88 public delegate void PostStepAction(float timeStep);
89 public event PreStepAction BeforeStep;
90 public event PreStepAction AfterStep;
111 91
112 // A value of the time now so all the collision and update routines do not have to get their own 92 // A value of the time now so all the collision and update routines do not have to get their own
113 // Set to 'now' just before all the prims and actors are called for collisions and updates 93 // Set to 'now' just before all the prims and actors are called for collisions and updates
@@ -121,31 +101,22 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
121 public bool InTaintTime { get; private set; } 101 public bool InTaintTime { get; private set; }
122 102
123 // Pinned memory used to pass step information between managed and unmanaged 103 // Pinned memory used to pass step information between managed and unmanaged
124 private int m_maxCollisionsPerFrame; 104 internal int m_maxCollisionsPerFrame;
125 private CollisionDesc[] m_collisionArray; 105 internal CollisionDesc[] m_collisionArray;
126 private GCHandle m_collisionArrayPinnedHandle;
127 106
128 private int m_maxUpdatesPerFrame; 107 internal int m_maxUpdatesPerFrame;
129 private EntityProperties[] m_updateArray; 108 internal EntityProperties[] m_updateArray;
130 private GCHandle m_updateArrayPinnedHandle;
131
132 public bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed
133 public bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes
134 public bool ShouldUseHullsForPhysicalObjects { get; private set; } // 'true' if should create hulls for physical objects
135
136 public float PID_D { get; private set; } // derivative
137 public float PID_P { get; private set; } // proportional
138 109
139 public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero 110 public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero
140 public const uint GROUNDPLANE_ID = 1; 111 public const uint GROUNDPLANE_ID = 1;
141 public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here 112 public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here
142 113
143 private float m_waterLevel; 114 public float SimpleWaterLevel { get; set; }
144 public BSTerrainManager TerrainManager { get; private set; } 115 public BSTerrainManager TerrainManager { get; private set; }
145 116
146 public ConfigurationParameters Params 117 public ConfigurationParameters Params
147 { 118 {
148 get { return m_params[0]; } 119 get { return UnmanagedParams[0]; }
149 } 120 }
150 public Vector3 DefaultGravity 121 public Vector3 DefaultGravity
151 { 122 {
@@ -157,8 +128,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
157 get { return Params.gravity; } 128 get { return Params.gravity; }
158 } 129 }
159 130
160 public float MaximumObjectMass { get; private set; }
161
162 // When functions in the unmanaged code must be called, it is only 131 // When functions in the unmanaged code must be called, it is only
163 // done at a known time just before the simulation step. The taint 132 // done at a known time just before the simulation step. The taint
164 // system saves all these function calls and executes them in 133 // system saves all these function calls and executes them in
@@ -181,13 +150,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
181 150
182 // A pointer to an instance if this structure is passed to the C++ code 151 // A pointer to an instance if this structure is passed to the C++ code
183 // Used to pass basic configuration values to the unmanaged code. 152 // Used to pass basic configuration values to the unmanaged code.
184 ConfigurationParameters[] m_params; 153 internal ConfigurationParameters[] UnmanagedParams;
185 GCHandle m_paramsHandle;
186
187 // Handle to the callback used by the unmanaged code to call into the managed code.
188 // Used for debug logging.
189 // Need to store the handle in a persistant variable so it won't be freed.
190 private BulletSimAPI.DebugLogCallback m_DebugLogCallbackHandle;
191 154
192 // Sometimes you just have to log everything. 155 // Sometimes you just have to log everything.
193 public Logging.LogWriter PhysicsLogging; 156 public Logging.LogWriter PhysicsLogging;
@@ -195,15 +158,24 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
195 private string m_physicsLoggingDir; 158 private string m_physicsLoggingDir;
196 private string m_physicsLoggingPrefix; 159 private string m_physicsLoggingPrefix;
197 private int m_physicsLoggingFileMinutes; 160 private int m_physicsLoggingFileMinutes;
161 private bool m_physicsLoggingDoFlush;
162 private bool m_physicsPhysicalDumpEnabled;
163 public float PhysicsMetricDumpFrames { get; set; }
198 // 'true' of the vehicle code is to log lots of details 164 // 'true' of the vehicle code is to log lots of details
199 public bool VehicleLoggingEnabled { get; private set; } 165 public bool VehicleLoggingEnabled { get; private set; }
166 public bool VehiclePhysicalLoggingEnabled { get; private set; }
200 167
201 #region Construction and Initialization 168 #region Construction and Initialization
202 public BSScene(string identifier) 169 public BSScene(string engineType, string identifier)
203 { 170 {
204 m_initialized = false; 171 m_initialized = false;
205 // we are passed the name of the region we're working for. 172
173 // The name of the region we're working for is passed to us. Keep for identification.
206 RegionName = identifier; 174 RegionName = identifier;
175
176 // Set identifying variables in the PhysicsScene interface.
177 EngineType = engineType;
178 Name = EngineType + "/" + RegionName;
207 } 179 }
208 180
209 public override void Initialise(IMesher meshmerizer, IConfigSource config) 181 public override void Initialise(IMesher meshmerizer, IConfigSource config)
@@ -216,17 +188,13 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
216 Shapes = new BSShapeCollection(this); 188 Shapes = new BSShapeCollection(this);
217 189
218 // Allocate pinned memory to pass parameters. 190 // Allocate pinned memory to pass parameters.
219 m_params = new ConfigurationParameters[1]; 191 UnmanagedParams = new ConfigurationParameters[1];
220 m_paramsHandle = GCHandle.Alloc(m_params, GCHandleType.Pinned);
221 192
222 // Set default values for physics parameters plus any overrides from the ini file 193 // Set default values for physics parameters plus any overrides from the ini file
223 GetInitialParameterValues(config); 194 GetInitialParameterValues(config);
224 195
225 // allocate more pinned memory close to the above in an attempt to get the memory all together 196 // Get the connection to the physics engine (could be native or one of many DLLs)
226 m_collisionArray = new CollisionDesc[m_maxCollisionsPerFrame]; 197 PE = SelectUnderlyingBulletEngine(BulletEngineName);
227 m_collisionArrayPinnedHandle = GCHandle.Alloc(m_collisionArray, GCHandleType.Pinned);
228 m_updateArray = new EntityProperties[m_maxUpdatesPerFrame];
229 m_updateArrayPinnedHandle = GCHandle.Alloc(m_updateArray, GCHandleType.Pinned);
230 198
231 // Enable very detailed logging. 199 // Enable very detailed logging.
232 // By creating an empty logger when not logging, the log message invocation code 200 // By creating an empty logger when not logging, the log message invocation code
@@ -234,28 +202,16 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
234 if (m_physicsLoggingEnabled) 202 if (m_physicsLoggingEnabled)
235 { 203 {
236 PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes); 204 PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes);
205 PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output error messages.
237 } 206 }
238 else 207 else
239 { 208 {
240 PhysicsLogging = new Logging.LogWriter(); 209 PhysicsLogging = new Logging.LogWriter();
241 } 210 }
242 211
243 // If Debug logging level, enable logging from the unmanaged code 212 // Allocate memory for returning of the updates and collisions from the physics engine
244 m_DebugLogCallbackHandle = null; 213 m_collisionArray = new CollisionDesc[m_maxCollisionsPerFrame];
245 if (m_log.IsDebugEnabled || PhysicsLogging.Enabled) 214 m_updateArray = new EntityProperties[m_maxUpdatesPerFrame];
246 {
247 m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader);
248 if (PhysicsLogging.Enabled)
249 // The handle is saved in a variable to make sure it doesn't get freed after this call
250 m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLoggerPhysLog);
251 else
252 m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger);
253 }
254
255 // Get the version of the DLL
256 // TODO: this doesn't work yet. Something wrong with marshaling the returned string.
257 // BulletSimVersion = BulletSimAPI.GetVersion();
258 // m_log.WarnFormat("{0}: BulletSim.dll version='{1}'", LogHeader, BulletSimVersion);
259 215
260 // The bounding box for the simulated world. The origin is 0,0,0 unless we're 216 // The bounding box for the simulated world. The origin is 0,0,0 unless we're
261 // a child in a mega-region. 217 // a child in a mega-region.
@@ -263,18 +219,14 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
263 // area. It tracks active objects no matter where they are. 219 // area. It tracks active objects no matter where they are.
264 Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); 220 Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
265 221
266 // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader); 222 World = PE.Initialize(worldExtent, Params, m_maxCollisionsPerFrame, ref m_collisionArray, m_maxUpdatesPerFrame, ref m_updateArray);
267 World = new BulletSim(0, this, BulletSimAPI.Initialize2(worldExtent, m_paramsHandle.AddrOfPinnedObject(),
268 m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(),
269 m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject(),
270 m_DebugLogCallbackHandle));
271 223
272 Constraints = new BSConstraintCollection(World); 224 Constraints = new BSConstraintCollection(World);
273 225
274 TerrainManager = new BSTerrainManager(this); 226 TerrainManager = new BSTerrainManager(this);
275 TerrainManager.CreateInitialGroundPlaneAndTerrain(); 227 TerrainManager.CreateInitialGroundPlaneAndTerrain();
276 228
277 m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)Params.linksetImplementation); 229 m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation);
278 230
279 InTaintTime = false; 231 InTaintTime = false;
280 m_initialized = true; 232 m_initialized = true;
@@ -285,9 +237,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
285 private void GetInitialParameterValues(IConfigSource config) 237 private void GetInitialParameterValues(IConfigSource config)
286 { 238 {
287 ConfigurationParameters parms = new ConfigurationParameters(); 239 ConfigurationParameters parms = new ConfigurationParameters();
288 m_params[0] = parms; 240 UnmanagedParams[0] = parms;
289 241
290 SetParameterDefaultValues(); 242 BSParam.SetParameterDefaultValues(this);
291 243
292 if (config != null) 244 if (config != null)
293 { 245 {
@@ -295,19 +247,34 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
295 IConfig pConfig = config.Configs["BulletSim"]; 247 IConfig pConfig = config.Configs["BulletSim"];
296 if (pConfig != null) 248 if (pConfig != null)
297 { 249 {
298 SetParameterConfigurationValues(pConfig); 250 BSParam.SetParameterConfigurationValues(this, pConfig);
251
252 // There are two Bullet implementations to choose from
253 BulletEngineName = pConfig.GetString("BulletEngine", "BulletUnmanaged");
299 254
300 // Very detailed logging for physics debugging 255 // Very detailed logging for physics debugging
256 // TODO: the boolean values can be moved to the normal parameter processing.
301 m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false); 257 m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false);
302 m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", "."); 258 m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", ".");
303 m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-"); 259 m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-");
304 m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5); 260 m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5);
261 m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false);
262 m_physicsPhysicalDumpEnabled = pConfig.GetBoolean("PhysicsPhysicalDumpEnabled", false);
305 // Very detailed logging for vehicle debugging 263 // Very detailed logging for vehicle debugging
306 VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false); 264 VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
265 VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false);
307 266
308 // Do any replacements in the parameters 267 // Do any replacements in the parameters
309 m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName); 268 m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
310 } 269 }
270
271 // The material characteristics.
272 BSMaterials.InitializeFromDefaults(Params);
273 if (pConfig != null)
274 {
275 // Let the user add new and interesting material property values.
276 BSMaterials.InitializefromParameters(pConfig);
277 }
311 } 278 }
312 } 279 }
313 280
@@ -326,16 +293,41 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
326 return ret; 293 return ret;
327 } 294 }
328 295
329 // Called directly from unmanaged code so don't do much 296 // Select the connection to the actual Bullet implementation.
330 private void BulletLogger(string msg) 297 // The main engine selection is the engineName up to the first hypen.
298 // So "Bullet-2.80-OpenCL-Intel" specifies the 'bullet' class here and the whole name
299 // is passed to the engine to do its special selection, etc.
300 private BSAPITemplate SelectUnderlyingBulletEngine(string engineName)
331 { 301 {
332 m_log.Debug("[BULLETS UNMANAGED]:" + msg); 302 // For the moment, do a simple switch statement.
333 } 303 // Someday do fancyness with looking up the interfaces in the assembly.
304 BSAPITemplate ret = null;
334 305
335 // Called directly from unmanaged code so don't do much 306 string selectionName = engineName.ToLower();
336 private void BulletLoggerPhysLog(string msg) 307 int hyphenIndex = engineName.IndexOf("-");
337 { 308 if (hyphenIndex > 0)
338 DetailLog("[BULLETS UNMANAGED]:" + msg); 309 selectionName = engineName.ToLower().Substring(0, hyphenIndex - 1);
310
311 switch (selectionName)
312 {
313 case "bulletunmanaged":
314 ret = new BSAPIUnman(engineName, this);
315 break;
316 case "bulletxna":
317 ret = new BSAPIXNA(engineName, this);
318 break;
319 }
320
321 if (ret == null)
322 {
323 m_log.ErrorFormat("{0) COULD NOT SELECT BULLET ENGINE: '[BulletSim]PhysicsEngine' must be either 'BulletUnmanaged-*' or 'BulletXNA-*'", LogHeader);
324 }
325 else
326 {
327 m_log.WarnFormat("{0} Selected bullet engine {1} -> {2}/{3}", LogHeader, engineName, ret.BulletEngineName, ret.BulletEngineVersion);
328 }
329
330 return ret;
339 } 331 }
340 332
341 public override void Dispose() 333 public override void Dispose()
@@ -345,8 +337,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
345 // make sure no stepping happens while we're deleting stuff 337 // make sure no stepping happens while we're deleting stuff
346 m_initialized = false; 338 m_initialized = false;
347 339
348 TerrainManager.ReleaseGroundPlaneAndTerrain();
349
350 foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects) 340 foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
351 { 341 {
352 kvp.Value.Destroy(); 342 kvp.Value.Destroy();
@@ -366,8 +356,15 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
366 Shapes = null; 356 Shapes = null;
367 } 357 }
368 358
359 if (TerrainManager != null)
360 {
361 TerrainManager.ReleaseGroundPlaneAndTerrain();
362 TerrainManager.Dispose();
363 TerrainManager = null;
364 }
365
369 // Anything left in the unmanaged code should be cleaned out 366 // Anything left in the unmanaged code should be cleaned out
370 BulletSimAPI.Shutdown2(World.ptr); 367 PE.Shutdown(World);
371 368
372 // Not logging any more 369 // Not logging any more
373 PhysicsLogging.Close(); 370 PhysicsLogging.Close();
@@ -389,12 +386,14 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
389 if (!m_initialized) return null; 386 if (!m_initialized) return null;
390 387
391 BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying); 388 BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying);
392 lock (PhysObjects) PhysObjects.Add(localID, actor); 389 lock (PhysObjects)
390 PhysObjects.Add(localID, actor);
393 391
394 // TODO: Remove kludge someday. 392 // TODO: Remove kludge someday.
395 // We must generate a collision for avatars whether they collide or not. 393 // We must generate a collision for avatars whether they collide or not.
396 // This is required by OpenSim to update avatar animations, etc. 394 // This is required by OpenSim to update avatar animations, etc.
397 lock (m_avatars) m_avatars.Add(actor); 395 lock (m_avatars)
396 m_avatars.Add(actor);
398 397
399 return actor; 398 return actor;
400 } 399 }
@@ -410,9 +409,11 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
410 { 409 {
411 try 410 try
412 { 411 {
413 lock (PhysObjects) PhysObjects.Remove(actor.LocalID); 412 lock (PhysObjects)
413 PhysObjects.Remove(bsactor.LocalID);
414 // Remove kludge someday 414 // Remove kludge someday
415 lock (m_avatars) m_avatars.Remove(bsactor); 415 lock (m_avatars)
416 m_avatars.Remove(bsactor);
416 } 417 }
417 catch (Exception e) 418 catch (Exception e)
418 { 419 {
@@ -421,6 +422,11 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
421 bsactor.Destroy(); 422 bsactor.Destroy();
422 // bsactor.dispose(); 423 // bsactor.dispose();
423 } 424 }
425 else
426 {
427 m_log.ErrorFormat("{0}: Requested to remove avatar that is not a BSCharacter. ID={1}, type={2}",
428 LogHeader, actor.LocalID, actor.GetType().Name);
429 }
424 } 430 }
425 431
426 public override void RemovePrim(PhysicsActor prim) 432 public override void RemovePrim(PhysicsActor prim)
@@ -474,41 +480,56 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
474 // Simulate one timestep 480 // Simulate one timestep
475 public override float Simulate(float timeStep) 481 public override float Simulate(float timeStep)
476 { 482 {
483 // prevent simulation until we've been initialized
484 if (!m_initialized) return 5.0f;
485
486 LastTimeStep = timeStep;
487
477 int updatedEntityCount = 0; 488 int updatedEntityCount = 0;
478 IntPtr updatedEntitiesPtr;
479 int collidersCount = 0; 489 int collidersCount = 0;
480 IntPtr collidersPtr;
481 490
482 int beforeTime = 0; 491 int beforeTime = 0;
483 int simTime = 0; 492 int simTime = 0;
484 493
485 // prevent simulation until we've been initialized
486 if (!m_initialized) return 5.0f;
487
488 // update the prim states while we know the physics engine is not busy 494 // update the prim states while we know the physics engine is not busy
489 int numTaints = _taintOperations.Count; 495 int numTaints = _taintOperations.Count;
496
497 InTaintTime = true; // Only used for debugging so locking is not necessary.
498
499 ProcessTaints();
500
501 // Some of the physical objects requre individual, pre-step calls
502 // (vehicles and avatar movement, in particular)
503 TriggerPreStepEvent(timeStep);
504
505 // the prestep actions might have added taints
506 numTaints += _taintOperations.Count;
490 ProcessTaints(); 507 ProcessTaints();
491 508
492 // Some of the prims operate with special vehicle properties 509 InTaintTime = false; // Only used for debugging so locking is not necessary.
493 ProcessVehicles(timeStep); 510
494 ProcessTaints(); // the vehicles might have added taints 511 // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world.
512 // Only enable this in a limited test world with few objects.
513 if (m_physicsPhysicalDumpEnabled)
514 PE.DumpAllInfo(World);
495 515
496 // step the physical world one interval 516 // step the physical world one interval
497 m_simulationStep++; 517 m_simulationStep++;
498 int numSubSteps = 0; 518 int numSubSteps = 0;
499
500 try 519 try
501 { 520 {
502 if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG 521 if (PhysicsLogging.Enabled)
503 if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount(); 522 beforeTime = Util.EnvironmentTickCount();
504 523
505 numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep, 524 numSubSteps = PE.PhysicsStep(World, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out collidersCount);
506 out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr);
507 525
508 if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime); 526 if (PhysicsLogging.Enabled)
509 DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}", 527 {
510 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount); 528 simTime = Util.EnvironmentTickCountSubtract(beforeTime);
511 if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG 529 DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}",
530 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps,
531 updatedEntityCount, collidersCount, ObjectsWithCollisions.Count);
532 }
512 } 533 }
513 catch (Exception e) 534 catch (Exception e)
514 { 535 {
@@ -520,9 +541,10 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
520 collidersCount = 0; 541 collidersCount = 0;
521 } 542 }
522 543
523 // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in 544 if ((m_simulationStep % PhysicsMetricDumpFrames) == 0)
545 PE.DumpPhysicsStatistics(World);
524 546
525 // Get a value for 'now' so all the collision and update routines don't have to get their own 547 // Get a value for 'now' so all the collision and update routines don't have to get their own.
526 SimulationNowTime = Util.EnvironmentTickCount(); 548 SimulationNowTime = Util.EnvironmentTickCount();
527 549
528 // If there were collisions, process them by sending the event to the prim. 550 // If there were collisions, process them by sending the event to the prim.
@@ -562,12 +584,16 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
562 584
563 // Objects that are done colliding are removed from the ObjectsWithCollisions list. 585 // Objects that are done colliding are removed from the ObjectsWithCollisions list.
564 // Not done above because it is inside an iteration of ObjectWithCollisions. 586 // Not done above because it is inside an iteration of ObjectWithCollisions.
587 // This complex collision processing is required to create an empty collision
588 // event call after all real collisions have happened on an object. This enables
589 // the simulator to generate the 'collision end' event.
565 if (ObjectsWithNoMoreCollisions.Count > 0) 590 if (ObjectsWithNoMoreCollisions.Count > 0)
566 { 591 {
567 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions) 592 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
568 ObjectsWithCollisions.Remove(po); 593 ObjectsWithCollisions.Remove(po);
569 ObjectsWithNoMoreCollisions.Clear(); 594 ObjectsWithNoMoreCollisions.Clear();
570 } 595 }
596 // Done with collisions.
571 597
572 // If any of the objects had updated properties, tell the object it has been changed by the physics engine 598 // If any of the objects had updated properties, tell the object it has been changed by the physics engine
573 if (updatedEntityCount > 0) 599 if (updatedEntityCount > 0)
@@ -583,17 +609,17 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
583 } 609 }
584 } 610 }
585 611
586 ProcessPostStepTaints(); 612 TriggerPostStepEvent(timeStep);
587 613
588 // This causes the unmanaged code to output ALL the values found in ALL the objects in the world. 614 // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world.
589 // Only enable this in a limited test world with few objects. 615 // Only enable this in a limited test world with few objects.
590 // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG 616 if (m_physicsPhysicalDumpEnabled)
617 PE.DumpAllInfo(World);
591 618
592 // The physics engine returns the number of milliseconds it simulated this call. 619 // The physics engine returns the number of milliseconds it simulated this call.
593 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS. 620 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
594 // We multiply by 55 to give a recognizable running rate (55 or less). 621 // Multiply by a fixed nominal frame rate to give a rate similar to the simulator (usually 55).
595 return numSubSteps * m_fixedTimeStep * 1000 * 55; 622 return (float)numSubSteps * m_fixedTimeStep * 1000f * NominalFrameRate;
596 // return timeStep * 1000 * 55;
597 } 623 }
598 624
599 // Something has collided 625 // Something has collided
@@ -639,12 +665,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
639 665
640 public override void SetWaterLevel(float baseheight) 666 public override void SetWaterLevel(float baseheight)
641 { 667 {
642 m_waterLevel = baseheight; 668 SimpleWaterLevel = baseheight;
643 }
644 // Someday....
645 public float GetWaterLevelAtXYZ(Vector3 loc)
646 {
647 return m_waterLevel;
648 } 669 }
649 670
650 public override void DeleteTerrain() 671 public override void DeleteTerrain()
@@ -681,6 +702,15 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
681 public override bool IsThreaded { get { return false; } } 702 public override bool IsThreaded { get { return false; } }
682 703
683 #region Taints 704 #region Taints
705 // The simulation execution order is:
706 // Simulate()
707 // DoOneTimeTaints
708 // TriggerPreStepEvent
709 // DoOneTimeTaints
710 // Step()
711 // ProcessAndForwardCollisions
712 // ProcessAndForwardPropertyUpdates
713 // TriggerPostStepEvent
684 714
685 // Calls to the PhysicsActors can't directly call into the physics engine 715 // Calls to the PhysicsActors can't directly call into the physics engine
686 // because it might be busy. We delay changes to a known time. 716 // because it might be busy. We delay changes to a known time.
@@ -707,58 +737,35 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
707 TaintedObject(ident, callback); 737 TaintedObject(ident, callback);
708 } 738 }
709 739
740 private void TriggerPreStepEvent(float timeStep)
741 {
742 PreStepAction actions = BeforeStep;
743 if (actions != null)
744 actions(timeStep);
745
746 }
747
748 private void TriggerPostStepEvent(float timeStep)
749 {
750 PreStepAction actions = AfterStep;
751 if (actions != null)
752 actions(timeStep);
753
754 }
755
710 // When someone tries to change a property on a BSPrim or BSCharacter, the object queues 756 // When someone tries to change a property on a BSPrim or BSCharacter, the object queues
711 // a callback into itself to do the actual property change. That callback is called 757 // a callback into itself to do the actual property change. That callback is called
712 // here just before the physics engine is called to step the simulation. 758 // here just before the physics engine is called to step the simulation.
713 public void ProcessTaints() 759 public void ProcessTaints()
714 { 760 {
715 InTaintTime = true; // Only used for debugging so locking is not necessary.
716 ProcessRegularTaints(); 761 ProcessRegularTaints();
717 ProcessPostTaintTaints(); 762 ProcessPostTaintTaints();
718 InTaintTime = false;
719 } 763 }
720 764
721 private void ProcessRegularTaints() 765 private void ProcessRegularTaints()
722 { 766 {
723 if (_taintOperations.Count > 0) // save allocating new list if there is nothing to process 767 if (_taintOperations.Count > 0) // save allocating new list if there is nothing to process
724 { 768 {
725 /*
726 // Code to limit the number of taints processed per step. Meant to limit step time.
727 // Unsure if a good idea as code assumes that taints are done before the step.
728 int taintCount = m_taintsToProcessPerStep;
729 TaintCallbackEntry oneCallback = new TaintCallbackEntry();
730 while (_taintOperations.Count > 0 && taintCount-- > 0)
731 {
732 bool gotOne = false;
733 lock (_taintLock)
734 {
735 if (_taintOperations.Count > 0)
736 {
737 oneCallback = _taintOperations[0];
738 _taintOperations.RemoveAt(0);
739 gotOne = true;
740 }
741 }
742 if (gotOne)
743 {
744 try
745 {
746 DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, oneCallback.ident);
747 oneCallback.callback();
748 }
749 catch (Exception e)
750 {
751 DetailLog("{0},BSScene.ProcessTaints,doTaintException,id={1}", DetailLogZero, oneCallback.ident); // DEBUG DEBUG DEBUG
752 m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, oneCallback.ident, e);
753 }
754 }
755 }
756 if (_taintOperations.Count > 0)
757 {
758 DetailLog("{0},BSScene.ProcessTaints,leftTaintsOnList,numNotProcessed={1}", DetailLogZero, _taintOperations.Count);
759 }
760 */
761
762 // swizzle a new list into the list location so we can process what's there 769 // swizzle a new list into the list location so we can process what's there
763 List<TaintCallbackEntry> oldList; 770 List<TaintCallbackEntry> oldList;
764 lock (_taintLock) 771 lock (_taintLock)
@@ -797,6 +804,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
797 return; 804 return;
798 } 805 }
799 806
807 // Taints that happen after the normal taint processing but before the simulation step.
800 private void ProcessPostTaintTaints() 808 private void ProcessPostTaintTaints()
801 { 809 {
802 if (_postTaintOperations.Count > 0) 810 if (_postTaintOperations.Count > 0)
@@ -824,45 +832,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
824 } 832 }
825 } 833 }
826 834
827 public void PostStepTaintObject(String ident, TaintCallback callback)
828 {
829 if (!m_initialized) return;
830
831 lock (_taintLock)
832 {
833 _postStepOperations.Add(new TaintCallbackEntry(ident, callback));
834 }
835
836 return;
837 }
838
839 private void ProcessPostStepTaints()
840 {
841 if (_postStepOperations.Count > 0)
842 {
843 List<TaintCallbackEntry> oldList;
844 lock (_taintLock)
845 {
846 oldList = _postStepOperations;
847 _postStepOperations = new List<TaintCallbackEntry>();
848 }
849
850 foreach (TaintCallbackEntry tcbe in oldList)
851 {
852 try
853 {
854 DetailLog("{0},BSScene.ProcessPostStepTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG
855 tcbe.callback();
856 }
857 catch (Exception e)
858 {
859 m_log.ErrorFormat("{0}: ProcessPostStepTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e);
860 }
861 }
862 oldList.Clear();
863 }
864 }
865
866 // Only used for debugging. Does not change state of anything so locking is not necessary. 835 // Only used for debugging. Does not change state of anything so locking is not necessary.
867 public bool AssertInTaintTime(string whereFrom) 836 public bool AssertInTaintTime(string whereFrom)
868 { 837 {
@@ -870,517 +839,19 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
870 { 839 {
871 DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom); 840 DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom);
872 m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom); 841 m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom);
873 Util.PrintCallStack(); // Prints the stack into the DEBUG log file. 842 Util.PrintCallStack(DetailLog);
874 } 843 }
875 return InTaintTime; 844 return InTaintTime;
876 } 845 }
877 846
878 #endregion // Taints 847 #endregion // Taints
879 848
880 #region Vehicles
881
882 public void VehicleInSceneTypeChanged(BSPrim vehic, Vehicle newType)
883 {
884 RemoveVehiclePrim(vehic);
885 if (newType != Vehicle.TYPE_NONE)
886 {
887 // make it so the scene will call us each tick to do vehicle things
888 AddVehiclePrim(vehic);
889 }
890 }
891
892 // Make so the scene will call this prim for vehicle actions each tick.
893 // Safe to call if prim is already in the vehicle list.
894 public void AddVehiclePrim(BSPrim vehicle)
895 {
896 lock (m_vehicles)
897 {
898 if (!m_vehicles.Contains(vehicle))
899 {
900 m_vehicles.Add(vehicle);
901 }
902 }
903 }
904
905 // Remove a prim from our list of vehicles.
906 // Safe to call if the prim is not in the vehicle list.
907 public void RemoveVehiclePrim(BSPrim vehicle)
908 {
909 lock (m_vehicles)
910 {
911 if (m_vehicles.Contains(vehicle))
912 {
913 m_vehicles.Remove(vehicle);
914 }
915 }
916 }
917
918 // Some prims have extra vehicle actions
919 // Called at taint time!
920 private void ProcessVehicles(float timeStep)
921 {
922 foreach (BSPhysObject pobj in m_vehicles)
923 {
924 pobj.StepVehicle(timeStep);
925 }
926 }
927 #endregion Vehicles
928
929 #region INI and command line parameter processing
930
931 delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val);
932 delegate float ParamGet(BSScene scene);
933 delegate void ParamSet(BSScene scene, string paramName, uint localID, float val);
934 delegate void SetOnObject(BSScene scene, BSPhysObject obj, float val);
935
936 private struct ParameterDefn
937 {
938 public string name; // string name of the parameter
939 public string desc; // a short description of what the parameter means
940 public float defaultValue; // default value if not specified anywhere else
941 public ParamUser userParam; // get the value from the configuration file
942 public ParamGet getter; // return the current value stored for this parameter
943 public ParamSet setter; // set the current value for this parameter
944 public SetOnObject onObject; // set the value on an object in the physical domain
945 public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s)
946 {
947 name = n;
948 desc = d;
949 defaultValue = v;
950 userParam = u;
951 getter = g;
952 setter = s;
953 onObject = null;
954 }
955 public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s, SetOnObject o)
956 {
957 name = n;
958 desc = d;
959 defaultValue = v;
960 userParam = u;
961 getter = g;
962 setter = s;
963 onObject = o;
964 }
965 }
966
967 // List of all of the externally visible parameters.
968 // For each parameter, this table maps a text name to getter and setters.
969 // To add a new externally referencable/settable parameter, add the paramter storage
970 // location somewhere in the program and make an entry in this table with the
971 // getters and setters.
972 // It is easiest to find an existing definition and copy it.
973 // Parameter values are floats. Booleans are converted to a floating value.
974 //
975 // A ParameterDefn() takes the following parameters:
976 // -- the text name of the parameter. This is used for console input and ini file.
977 // -- a short text description of the parameter. This shows up in the console listing.
978 // -- a delegate for fetching the parameter from the ini file.
979 // Should handle fetching the right type from the ini file and converting it.
980 // -- a delegate for getting the value as a float
981 // -- a delegate for setting the value from a float
982 //
983 // The single letter parameters for the delegates are:
984 // s = BSScene
985 // o = BSPhysObject
986 // p = string parameter name
987 // l = localID of referenced object
988 // v = float value
989 // cf = parameter configuration class (for fetching values from ini file)
990 private ParameterDefn[] ParameterDefinitions =
991 {
992 new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties",
993 ConfigurationParameters.numericTrue,
994 (s,cf,p,v) => { s.ShouldMeshSculptedPrim = cf.GetBoolean(p, s.BoolNumeric(v)); },
995 (s) => { return s.NumericBool(s.ShouldMeshSculptedPrim); },
996 (s,p,l,v) => { s.ShouldMeshSculptedPrim = s.BoolNumeric(v); } ),
997 new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects",
998 ConfigurationParameters.numericFalse,
999 (s,cf,p,v) => { s.ShouldForceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); },
1000 (s) => { return s.NumericBool(s.ShouldForceSimplePrimMeshing); },
1001 (s,p,l,v) => { s.ShouldForceSimplePrimMeshing = s.BoolNumeric(v); } ),
1002 new ParameterDefn("UseHullsForPhysicalObjects", "If true, create hulls for physical objects",
1003 ConfigurationParameters.numericTrue,
1004 (s,cf,p,v) => { s.ShouldUseHullsForPhysicalObjects = cf.GetBoolean(p, s.BoolNumeric(v)); },
1005 (s) => { return s.NumericBool(s.ShouldUseHullsForPhysicalObjects); },
1006 (s,p,l,v) => { s.ShouldUseHullsForPhysicalObjects = s.BoolNumeric(v); } ),
1007
1008 new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)",
1009 8f,
1010 (s,cf,p,v) => { s.MeshLOD = (float)cf.GetInt(p, (int)v); },
1011 (s) => { return s.MeshLOD; },
1012 (s,p,l,v) => { s.MeshLOD = v; } ),
1013 new ParameterDefn("MeshLevelOfDetailMegaPrim", "Level of detail to render meshes larger than threshold meters",
1014 16f,
1015 (s,cf,p,v) => { s.MeshMegaPrimLOD = (float)cf.GetInt(p, (int)v); },
1016 (s) => { return s.MeshMegaPrimLOD; },
1017 (s,p,l,v) => { s.MeshMegaPrimLOD = v; } ),
1018 new ParameterDefn("MeshLevelOfDetailMegaPrimThreshold", "Size (in meters) of a mesh before using MeshMegaPrimLOD",
1019 10f,
1020 (s,cf,p,v) => { s.MeshMegaPrimThreshold = (float)cf.GetInt(p, (int)v); },
1021 (s) => { return s.MeshMegaPrimThreshold; },
1022 (s,p,l,v) => { s.MeshMegaPrimThreshold = v; } ),
1023 new ParameterDefn("SculptLevelOfDetail", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)",
1024 32f,
1025 (s,cf,p,v) => { s.SculptLOD = (float)cf.GetInt(p, (int)v); },
1026 (s) => { return s.SculptLOD; },
1027 (s,p,l,v) => { s.SculptLOD = v; } ),
1028
1029 new ParameterDefn("MaxSubStep", "In simulation step, maximum number of substeps",
1030 10f,
1031 (s,cf,p,v) => { s.m_maxSubSteps = cf.GetInt(p, (int)v); },
1032 (s) => { return (float)s.m_maxSubSteps; },
1033 (s,p,l,v) => { s.m_maxSubSteps = (int)v; } ),
1034 new ParameterDefn("FixedTimeStep", "In simulation step, seconds of one substep (1/60)",
1035 1f / 60f,
1036 (s,cf,p,v) => { s.m_fixedTimeStep = cf.GetFloat(p, v); },
1037 (s) => { return (float)s.m_fixedTimeStep; },
1038 (s,p,l,v) => { s.m_fixedTimeStep = v; } ),
1039 new ParameterDefn("MaxCollisionsPerFrame", "Max collisions returned at end of each frame",
1040 2048f,
1041 (s,cf,p,v) => { s.m_maxCollisionsPerFrame = cf.GetInt(p, (int)v); },
1042 (s) => { return (float)s.m_maxCollisionsPerFrame; },
1043 (s,p,l,v) => { s.m_maxCollisionsPerFrame = (int)v; } ),
1044 new ParameterDefn("MaxUpdatesPerFrame", "Max updates returned at end of each frame",
1045 8000f,
1046 (s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); },
1047 (s) => { return (float)s.m_maxUpdatesPerFrame; },
1048 (s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ),
1049 new ParameterDefn("MaxTaintsToProcessPerStep", "Number of update taints to process before each simulation step",
1050 500f,
1051 (s,cf,p,v) => { s.m_taintsToProcessPerStep = cf.GetInt(p, (int)v); },
1052 (s) => { return (float)s.m_taintsToProcessPerStep; },
1053 (s,p,l,v) => { s.m_taintsToProcessPerStep = (int)v; } ),
1054 new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)",
1055 10000.01f,
1056 (s,cf,p,v) => { s.MaximumObjectMass = cf.GetFloat(p, v); },
1057 (s) => { return (float)s.MaximumObjectMass; },
1058 (s,p,l,v) => { s.MaximumObjectMass = v; } ),
1059
1060 new ParameterDefn("PID_D", "Derivitive factor for motion smoothing",
1061 2200f,
1062 (s,cf,p,v) => { s.PID_D = cf.GetFloat(p, v); },
1063 (s) => { return (float)s.PID_D; },
1064 (s,p,l,v) => { s.PID_D = v; } ),
1065 new ParameterDefn("PID_P", "Parameteric factor for motion smoothing",
1066 900f,
1067 (s,cf,p,v) => { s.PID_P = cf.GetFloat(p, v); },
1068 (s) => { return (float)s.PID_P; },
1069 (s,p,l,v) => { s.PID_P = v; } ),
1070
1071 new ParameterDefn("DefaultFriction", "Friction factor used on new objects",
1072 0.5f,
1073 (s,cf,p,v) => { s.m_params[0].defaultFriction = cf.GetFloat(p, v); },
1074 (s) => { return s.m_params[0].defaultFriction; },
1075 (s,p,l,v) => { s.m_params[0].defaultFriction = v; } ),
1076 new ParameterDefn("DefaultDensity", "Density for new objects" ,
1077 10.000006836f, // Aluminum g/cm3
1078 (s,cf,p,v) => { s.m_params[0].defaultDensity = cf.GetFloat(p, v); },
1079 (s) => { return s.m_params[0].defaultDensity; },
1080 (s,p,l,v) => { s.m_params[0].defaultDensity = v; } ),
1081 new ParameterDefn("DefaultRestitution", "Bouncyness of an object" ,
1082 0f,
1083 (s,cf,p,v) => { s.m_params[0].defaultRestitution = cf.GetFloat(p, v); },
1084 (s) => { return s.m_params[0].defaultRestitution; },
1085 (s,p,l,v) => { s.m_params[0].defaultRestitution = v; } ),
1086 new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)",
1087 0f,
1088 (s,cf,p,v) => { s.m_params[0].collisionMargin = cf.GetFloat(p, v); },
1089 (s) => { return s.m_params[0].collisionMargin; },
1090 (s,p,l,v) => { s.m_params[0].collisionMargin = v; } ),
1091 new ParameterDefn("Gravity", "Vertical force of gravity (negative means down)",
1092 -9.80665f,
1093 (s,cf,p,v) => { s.m_params[0].gravity = cf.GetFloat(p, v); },
1094 (s) => { return s.m_params[0].gravity; },
1095 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].gravity, p, PhysParameterEntry.APPLY_TO_NONE, v); },
1096 (s,o,v) => { BulletSimAPI.SetGravity2(s.World.ptr, new Vector3(0f,0f,v)); } ),
1097
1098
1099 new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)",
1100 0f,
1101 (s,cf,p,v) => { s.m_params[0].linearDamping = cf.GetFloat(p, v); },
1102 (s) => { return s.m_params[0].linearDamping; },
1103 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearDamping, p, l, v); },
1104 (s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, v, s.m_params[0].angularDamping); } ),
1105 new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)",
1106 0f,
1107 (s,cf,p,v) => { s.m_params[0].angularDamping = cf.GetFloat(p, v); },
1108 (s) => { return s.m_params[0].angularDamping; },
1109 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularDamping, p, l, v); },
1110 (s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, s.m_params[0].linearDamping, v); } ),
1111 new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static",
1112 0.2f,
1113 (s,cf,p,v) => { s.m_params[0].deactivationTime = cf.GetFloat(p, v); },
1114 (s) => { return s.m_params[0].deactivationTime; },
1115 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].deactivationTime, p, l, v); },
1116 (s,o,v) => { BulletSimAPI.SetDeactivationTime2(o.PhysBody.ptr, v); } ),
1117 new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static",
1118 0.8f,
1119 (s,cf,p,v) => { s.m_params[0].linearSleepingThreshold = cf.GetFloat(p, v); },
1120 (s) => { return s.m_params[0].linearSleepingThreshold; },
1121 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearSleepingThreshold, p, l, v); },
1122 (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ),
1123 new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static",
1124 1.0f,
1125 (s,cf,p,v) => { s.m_params[0].angularSleepingThreshold = cf.GetFloat(p, v); },
1126 (s) => { return s.m_params[0].angularSleepingThreshold; },
1127 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularSleepingThreshold, p, l, v); },
1128 (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ),
1129 new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" ,
1130 0f, // set to zero to disable
1131 (s,cf,p,v) => { s.m_params[0].ccdMotionThreshold = cf.GetFloat(p, v); },
1132 (s) => { return s.m_params[0].ccdMotionThreshold; },
1133 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdMotionThreshold, p, l, v); },
1134 (s,o,v) => { BulletSimAPI.SetCcdMotionThreshold2(o.PhysBody.ptr, v); } ),
1135 new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" ,
1136 0f,
1137 (s,cf,p,v) => { s.m_params[0].ccdSweptSphereRadius = cf.GetFloat(p, v); },
1138 (s) => { return s.m_params[0].ccdSweptSphereRadius; },
1139 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); },
1140 (s,o,v) => { BulletSimAPI.SetCcdSweptSphereRadius2(o.PhysBody.ptr, v); } ),
1141 new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" ,
1142 0.1f,
1143 (s,cf,p,v) => { s.m_params[0].contactProcessingThreshold = cf.GetFloat(p, v); },
1144 (s) => { return s.m_params[0].contactProcessingThreshold; },
1145 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); },
1146 (s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.PhysBody.ptr, v); } ),
1147
1148 new ParameterDefn("TerrainImplementation", "Type of shape to use for terrain (0=heightmap, 1=mesh)",
1149 (float)BSTerrainPhys.TerrainImplementation.Mesh,
1150 (s,cf,p,v) => { s.m_params[0].terrainImplementation = cf.GetFloat(p,v); },
1151 (s) => { return s.m_params[0].terrainImplementation; },
1152 (s,p,l,v) => { s.m_params[0].terrainImplementation = v; } ),
1153 new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" ,
1154 0.5f,
1155 (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); },
1156 (s) => { return s.m_params[0].terrainFriction; },
1157 (s,p,l,v) => { s.m_params[0].terrainFriction = v; /* TODO: set on real terrain */} ),
1158 new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" ,
1159 0.8f,
1160 (s,cf,p,v) => { s.m_params[0].terrainHitFraction = cf.GetFloat(p, v); },
1161 (s) => { return s.m_params[0].terrainHitFraction; },
1162 (s,p,l,v) => { s.m_params[0].terrainHitFraction = v; /* TODO: set on real terrain */ } ),
1163 new ParameterDefn("TerrainRestitution", "Bouncyness" ,
1164 0f,
1165 (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); },
1166 (s) => { return s.m_params[0].terrainRestitution; },
1167 (s,p,l,v) => { s.m_params[0].terrainRestitution = v; /* TODO: set on real terrain */ } ),
1168 new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.",
1169 0.2f,
1170 (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); },
1171 (s) => { return s.m_params[0].avatarFriction; },
1172 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ),
1173 new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.",
1174 10f,
1175 (s,cf,p,v) => { s.m_params[0].avatarStandingFriction = cf.GetFloat(p, v); },
1176 (s) => { return s.m_params[0].avatarStandingFriction; },
1177 (s,p,l,v) => { s.m_params[0].avatarStandingFriction = v; } ),
1178 new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.",
1179 60f,
1180 (s,cf,p,v) => { s.m_params[0].avatarDensity = cf.GetFloat(p, v); },
1181 (s) => { return s.m_params[0].avatarDensity; },
1182 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarDensity, p, l, v); } ),
1183 new ParameterDefn("AvatarRestitution", "Bouncyness. Changed on avatar recreation.",
1184 0f,
1185 (s,cf,p,v) => { s.m_params[0].avatarRestitution = cf.GetFloat(p, v); },
1186 (s) => { return s.m_params[0].avatarRestitution; },
1187 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarRestitution, p, l, v); } ),
1188 new ParameterDefn("AvatarCapsuleWidth", "The distance between the sides of the avatar capsule",
1189 0.6f,
1190 (s,cf,p,v) => { s.m_params[0].avatarCapsuleWidth = cf.GetFloat(p, v); },
1191 (s) => { return s.m_params[0].avatarCapsuleWidth; },
1192 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleWidth, p, l, v); } ),
1193 new ParameterDefn("AvatarCapsuleDepth", "The distance between the front and back of the avatar capsule",
1194 0.45f,
1195 (s,cf,p,v) => { s.m_params[0].avatarCapsuleDepth = cf.GetFloat(p, v); },
1196 (s) => { return s.m_params[0].avatarCapsuleDepth; },
1197 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleDepth, p, l, v); } ),
1198 new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar",
1199 1.5f,
1200 (s,cf,p,v) => { s.m_params[0].avatarCapsuleHeight = cf.GetFloat(p, v); },
1201 (s) => { return s.m_params[0].avatarCapsuleHeight; },
1202 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleHeight, p, l, v); } ),
1203 new ParameterDefn("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions",
1204 0.1f,
1205 (s,cf,p,v) => { s.m_params[0].avatarContactProcessingThreshold = cf.GetFloat(p, v); },
1206 (s) => { return s.m_params[0].avatarContactProcessingThreshold; },
1207 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ),
1208
1209
1210 new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)",
1211 0f,
1212 (s,cf,p,v) => { s.m_params[0].maxPersistantManifoldPoolSize = cf.GetFloat(p, v); },
1213 (s) => { return s.m_params[0].maxPersistantManifoldPoolSize; },
1214 (s,p,l,v) => { s.m_params[0].maxPersistantManifoldPoolSize = v; } ),
1215 new ParameterDefn("MaxCollisionAlgorithmPoolSize", "Number of collisions pooled (0 means default of 4096)",
1216 0f,
1217 (s,cf,p,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = cf.GetFloat(p, v); },
1218 (s) => { return s.m_params[0].maxCollisionAlgorithmPoolSize; },
1219 (s,p,l,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = v; } ),
1220 new ParameterDefn("ShouldDisableContactPoolDynamicAllocation", "Enable to allow large changes in object count",
1221 ConfigurationParameters.numericFalse,
1222 (s,cf,p,v) => { s.m_params[0].shouldDisableContactPoolDynamicAllocation = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
1223 (s) => { return s.m_params[0].shouldDisableContactPoolDynamicAllocation; },
1224 (s,p,l,v) => { s.m_params[0].shouldDisableContactPoolDynamicAllocation = v; } ),
1225 new ParameterDefn("ShouldForceUpdateAllAabbs", "Enable to recomputer AABBs every simulator step",
1226 ConfigurationParameters.numericFalse,
1227 (s,cf,p,v) => { s.m_params[0].shouldForceUpdateAllAabbs = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
1228 (s) => { return s.m_params[0].shouldForceUpdateAllAabbs; },
1229 (s,p,l,v) => { s.m_params[0].shouldForceUpdateAllAabbs = v; } ),
1230 new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction",
1231 ConfigurationParameters.numericTrue,
1232 (s,cf,p,v) => { s.m_params[0].shouldRandomizeSolverOrder = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
1233 (s) => { return s.m_params[0].shouldRandomizeSolverOrder; },
1234 (s,p,l,v) => { s.m_params[0].shouldRandomizeSolverOrder = v; } ),
1235 new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands",
1236 ConfigurationParameters.numericTrue,
1237 (s,cf,p,v) => { s.m_params[0].shouldSplitSimulationIslands = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
1238 (s) => { return s.m_params[0].shouldSplitSimulationIslands; },
1239 (s,p,l,v) => { s.m_params[0].shouldSplitSimulationIslands = v; } ),
1240 new ParameterDefn("ShouldEnableFrictionCaching", "Enable friction computation caching",
1241 ConfigurationParameters.numericFalse,
1242 (s,cf,p,v) => { s.m_params[0].shouldEnableFrictionCaching = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
1243 (s) => { return s.m_params[0].shouldEnableFrictionCaching; },
1244 (s,p,l,v) => { s.m_params[0].shouldEnableFrictionCaching = v; } ),
1245 new ParameterDefn("NumberOfSolverIterations", "Number of internal iterations (0 means default)",
1246 0f, // zero says use Bullet default
1247 (s,cf,p,v) => { s.m_params[0].numberOfSolverIterations = cf.GetFloat(p, v); },
1248 (s) => { return s.m_params[0].numberOfSolverIterations; },
1249 (s,p,l,v) => { s.m_params[0].numberOfSolverIterations = v; } ),
1250
1251 new ParameterDefn("LinksetImplementation", "Type of linkset implementation (0=Constraint, 1=Compound, 2=Manual)",
1252 (float)BSLinkset.LinksetImplementation.Compound,
1253 (s,cf,p,v) => { s.m_params[0].linksetImplementation = cf.GetFloat(p,v); },
1254 (s) => { return s.m_params[0].linksetImplementation; },
1255 (s,p,l,v) => { s.m_params[0].linksetImplementation = v; } ),
1256 new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.",
1257 ConfigurationParameters.numericFalse,
1258 (s,cf,p,v) => { s.m_params[0].linkConstraintUseFrameOffset = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
1259 (s) => { return s.m_params[0].linkConstraintUseFrameOffset; },
1260 (s,p,l,v) => { s.m_params[0].linkConstraintUseFrameOffset = v; } ),
1261 new ParameterDefn("LinkConstraintEnableTransMotor", "Whether to enable translational motor on linkset constraints",
1262 ConfigurationParameters.numericTrue,
1263 (s,cf,p,v) => { s.m_params[0].linkConstraintEnableTransMotor = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
1264 (s) => { return s.m_params[0].linkConstraintEnableTransMotor; },
1265 (s,p,l,v) => { s.m_params[0].linkConstraintEnableTransMotor = v; } ),
1266 new ParameterDefn("LinkConstraintTransMotorMaxVel", "Maximum velocity to be applied by translational motor in linkset constraints",
1267 5.0f,
1268 (s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxVel = cf.GetFloat(p, v); },
1269 (s) => { return s.m_params[0].linkConstraintTransMotorMaxVel; },
1270 (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxVel = v; } ),
1271 new ParameterDefn("LinkConstraintTransMotorMaxForce", "Maximum force to be applied by translational motor in linkset constraints",
1272 0.1f,
1273 (s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = cf.GetFloat(p, v); },
1274 (s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; },
1275 (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ),
1276 new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1",
1277 0.1f,
1278 (s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); },
1279 (s) => { return s.m_params[0].linkConstraintCFM; },
1280 (s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ),
1281 new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2",
1282 0.1f,
1283 (s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); },
1284 (s) => { return s.m_params[0].linkConstraintERP; },
1285 (s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ),
1286 new ParameterDefn("LinkConstraintSolverIterations", "Number of solver iterations when computing constraint. (0 = Bullet default)",
1287 40,
1288 (s,cf,p,v) => { s.m_params[0].linkConstraintSolverIterations = cf.GetFloat(p, v); },
1289 (s) => { return s.m_params[0].linkConstraintSolverIterations; },
1290 (s,p,l,v) => { s.m_params[0].linkConstraintSolverIterations = v; } ),
1291
1292 new ParameterDefn("LogPhysicsStatisticsFrames", "Frames between outputting detailed phys stats. (0 is off)",
1293 0f,
1294 (s,cf,p,v) => { s.m_params[0].physicsLoggingFrames = cf.GetInt(p, (int)v); },
1295 (s) => { return (float)s.m_params[0].physicsLoggingFrames; },
1296 (s,p,l,v) => { s.m_params[0].physicsLoggingFrames = (int)v; } ),
1297 };
1298
1299 // Convert a boolean to our numeric true and false values
1300 public float NumericBool(bool b)
1301 {
1302 return (b ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse);
1303 }
1304
1305 // Convert numeric true and false values to a boolean
1306 public bool BoolNumeric(float b)
1307 {
1308 return (b == ConfigurationParameters.numericTrue ? true : false);
1309 }
1310
1311 // Search through the parameter definitions and return the matching
1312 // ParameterDefn structure.
1313 // Case does not matter as names are compared after converting to lower case.
1314 // Returns 'false' if the parameter is not found.
1315 private bool TryGetParameter(string paramName, out ParameterDefn defn)
1316 {
1317 bool ret = false;
1318 ParameterDefn foundDefn = new ParameterDefn();
1319 string pName = paramName.ToLower();
1320
1321 foreach (ParameterDefn parm in ParameterDefinitions)
1322 {
1323 if (pName == parm.name.ToLower())
1324 {
1325 foundDefn = parm;
1326 ret = true;
1327 break;
1328 }
1329 }
1330 defn = foundDefn;
1331 return ret;
1332 }
1333
1334 // Pass through the settable parameters and set the default values
1335 private void SetParameterDefaultValues()
1336 {
1337 foreach (ParameterDefn parm in ParameterDefinitions)
1338 {
1339 parm.setter(this, parm.name, PhysParameterEntry.APPLY_TO_NONE, parm.defaultValue);
1340 }
1341 }
1342
1343 // Get user set values out of the ini file.
1344 private void SetParameterConfigurationValues(IConfig cfg)
1345 {
1346 foreach (ParameterDefn parm in ParameterDefinitions)
1347 {
1348 parm.userParam(this, cfg, parm.name, parm.defaultValue);
1349 }
1350 }
1351
1352 private PhysParameterEntry[] SettableParameters = new PhysParameterEntry[1];
1353
1354 // This creates an array in the correct format for returning the list of
1355 // parameters. This is used by the 'list' option of the 'physics' command.
1356 private void BuildParameterTable()
1357 {
1358 if (SettableParameters.Length < ParameterDefinitions.Length)
1359 {
1360 List<PhysParameterEntry> entries = new List<PhysParameterEntry>();
1361 for (int ii = 0; ii < ParameterDefinitions.Length; ii++)
1362 {
1363 ParameterDefn pd = ParameterDefinitions[ii];
1364 entries.Add(new PhysParameterEntry(pd.name, pd.desc));
1365 }
1366
1367 // make the list in alphabetical order for estetic reasons
1368 entries.Sort(delegate(PhysParameterEntry ppe1, PhysParameterEntry ppe2)
1369 {
1370 return ppe1.name.CompareTo(ppe2.name);
1371 });
1372
1373 SettableParameters = entries.ToArray();
1374 }
1375 }
1376
1377
1378 #region IPhysicsParameters 849 #region IPhysicsParameters
1379 // Get the list of parameters this physics engine supports 850 // Get the list of parameters this physics engine supports
1380 public PhysParameterEntry[] GetParameterList() 851 public PhysParameterEntry[] GetParameterList()
1381 { 852 {
1382 BuildParameterTable(); 853 BSParam.BuildParameterTable();
1383 return SettableParameters; 854 return BSParam.SettableParameters;
1384 } 855 }
1385 856
1386 // Set parameter on a specific or all instances. 857 // Set parameter on a specific or all instances.
@@ -1392,8 +863,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1392 public bool SetPhysicsParameter(string parm, float val, uint localID) 863 public bool SetPhysicsParameter(string parm, float val, uint localID)
1393 { 864 {
1394 bool ret = false; 865 bool ret = false;
1395 ParameterDefn theParam; 866 BSParam.ParameterDefn theParam;
1396 if (TryGetParameter(parm, out theParam)) 867 if (BSParam.TryGetParameter(parm, out theParam))
1397 { 868 {
1398 theParam.setter(this, parm, localID, val); 869 theParam.setter(this, parm, localID, val);
1399 ret = true; 870 ret = true;
@@ -1405,19 +876,20 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1405 // If the local ID is APPLY_TO_NONE, just change the default value 876 // If the local ID is APPLY_TO_NONE, just change the default value
1406 // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs 877 // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs
1407 // If the localID is a specific object, apply the parameter change to only that object 878 // If the localID is a specific object, apply the parameter change to only that object
1408 private void UpdateParameterObject(ref float defaultLoc, string parm, uint localID, float val) 879 internal delegate void AssignVal(float x);
880 internal void UpdateParameterObject(AssignVal setDefault, string parm, uint localID, float val)
1409 { 881 {
1410 List<uint> objectIDs = new List<uint>(); 882 List<uint> objectIDs = new List<uint>();
1411 switch (localID) 883 switch (localID)
1412 { 884 {
1413 case PhysParameterEntry.APPLY_TO_NONE: 885 case PhysParameterEntry.APPLY_TO_NONE:
1414 defaultLoc = val; // setting only the default value 886 setDefault(val); // setting only the default value
1415 // This will cause a call into the physical world if some operation is specified (SetOnObject). 887 // This will cause a call into the physical world if some operation is specified (SetOnObject).
1416 objectIDs.Add(TERRAIN_ID); 888 objectIDs.Add(TERRAIN_ID);
1417 TaintedUpdateParameter(parm, objectIDs, val); 889 TaintedUpdateParameter(parm, objectIDs, val);
1418 break; 890 break;
1419 case PhysParameterEntry.APPLY_TO_ALL: 891 case PhysParameterEntry.APPLY_TO_ALL:
1420 defaultLoc = val; // setting ALL also sets the default value 892 setDefault(val); // setting ALL also sets the default value
1421 lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys); 893 lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys);
1422 TaintedUpdateParameter(parm, objectIDs, val); 894 TaintedUpdateParameter(parm, objectIDs, val);
1423 break; 895 break;
@@ -1436,8 +908,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1436 List<uint> xlIDs = lIDs; 908 List<uint> xlIDs = lIDs;
1437 string xparm = parm; 909 string xparm = parm;
1438 TaintedObject("BSScene.UpdateParameterSet", delegate() { 910 TaintedObject("BSScene.UpdateParameterSet", delegate() {
1439 ParameterDefn thisParam; 911 BSParam.ParameterDefn thisParam;
1440 if (TryGetParameter(xparm, out thisParam)) 912 if (BSParam.TryGetParameter(xparm, out thisParam))
1441 { 913 {
1442 if (thisParam.onObject != null) 914 if (thisParam.onObject != null)
1443 { 915 {
@@ -1458,8 +930,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1458 { 930 {
1459 float val = 0f; 931 float val = 0f;
1460 bool ret = false; 932 bool ret = false;
1461 ParameterDefn theParam; 933 BSParam.ParameterDefn theParam;
1462 if (TryGetParameter(parm, out theParam)) 934 if (BSParam.TryGetParameter(parm, out theParam))
1463 { 935 {
1464 val = theParam.getter(this); 936 val = theParam.getter(this);
1465 ret = true; 937 ret = true;
@@ -1470,24 +942,12 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1470 942
1471 #endregion IPhysicsParameters 943 #endregion IPhysicsParameters
1472 944
1473 #endregion Runtime settable parameters
1474
1475 // Debugging routine for dumping detailed physical information for vehicle prims
1476 private void DumpVehicles()
1477 {
1478 foreach (BSPrim prim in m_vehicles)
1479 {
1480 BulletSimAPI.DumpRigidBody2(World.ptr, prim.PhysBody.ptr);
1481 BulletSimAPI.DumpCollisionShape2(World.ptr, prim.PhysShape.ptr);
1482 }
1483 }
1484
1485 // Invoke the detailed logger and output something if it's enabled. 945 // Invoke the detailed logger and output something if it's enabled.
1486 public void DetailLog(string msg, params Object[] args) 946 public void DetailLog(string msg, params Object[] args)
1487 { 947 {
1488 PhysicsLogging.Write(msg, args); 948 PhysicsLogging.Write(msg, args);
1489 // Add the Flush() if debugging crashes. Gets all the messages written out. 949 // Add the Flush() if debugging crashes. Gets all the messages written out.
1490 // PhysicsLogging.Flush(); 950 if (m_physicsLoggingDoFlush) PhysicsLogging.Flush();
1491 } 951 }
1492 // Used to fill in the LocalID when there isn't one. It's the correct number of characters. 952 // Used to fill in the LocalID when there isn't one. It's the correct number of characters.
1493 public const string DetailLogZero = "0000000000"; 953 public const string DetailLogZero = "0000000000";