aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSScene.cs')
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSScene.cs1047
1 files changed, 262 insertions, 785 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
index 27a78d1..e6aefd5 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
@@ -26,6 +26,8 @@
26 */ 26 */
27using System; 27using System;
28using System.Collections.Generic; 28using System.Collections.Generic;
29using System.Linq;
30using System.Reflection;
29using System.Runtime.InteropServices; 31using System.Runtime.InteropServices;
30using System.Text; 32using System.Text;
31using System.Threading; 33using System.Threading;
@@ -38,40 +40,22 @@ using Nini.Config;
38using log4net; 40using log4net;
39using OpenMetaverse; 41using OpenMetaverse;
40 42
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 43namespace OpenSim.Region.Physics.BulletSPlugin
64{ 44{
65public sealed class BSScene : PhysicsScene, IPhysicsParameters 45public sealed class BSScene : PhysicsScene, IPhysicsParameters
66{ 46{
67 private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 47 internal static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
68 private static readonly string LogHeader = "[BULLETS SCENE]"; 48 internal static readonly string LogHeader = "[BULLETS SCENE]";
69 49
70 // The name of the region we're working for. 50 // The name of the region we're working for.
71 public string RegionName { get; private set; } 51 public string RegionName { get; private set; }
72 52
73 public string BulletSimVersion = "?"; 53 public string BulletSimVersion = "?";
74 54
55 // The handle to the underlying managed or unmanaged version of Bullet being used.
56 public string BulletEngineName { get; private set; }
57 public BSAPITemplate PE;
58
75 public Dictionary<uint, BSPhysObject> PhysObjects; 59 public Dictionary<uint, BSPhysObject> PhysObjects;
76 public BSShapeCollection Shapes; 60 public BSShapeCollection Shapes;
77 61
@@ -82,32 +66,29 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
82 // every tick so OpenSim will update its animation. 66 // every tick so OpenSim will update its animation.
83 private HashSet<BSPhysObject> m_avatars = new HashSet<BSPhysObject>(); 67 private HashSet<BSPhysObject> m_avatars = new HashSet<BSPhysObject>();
84 68
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 69 // let my minuions use my logger
90 public ILog Logger { get { return m_log; } } 70 public ILog Logger { get { return m_log; } }
91 71
92 public IMesher mesher; 72 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; } 73 public uint WorldID { get; private set; }
100 public BulletSim World { get; private set; } 74 public BulletWorld World { get; private set; }
101 75
102 // All the constraints that have been allocated in this instance. 76 // All the constraints that have been allocated in this instance.
103 public BSConstraintCollection Constraints { get; private set; } 77 public BSConstraintCollection Constraints { get; private set; }
104 78
105 // Simulation parameters 79 // Simulation parameters
106 private int m_maxSubSteps; 80 internal int m_maxSubSteps;
107 private float m_fixedTimeStep; 81 internal float m_fixedTimeStep;
108 private long m_simulationStep = 0; 82 internal long m_simulationStep = 0;
83 internal float NominalFrameRate { get; set; }
109 public long SimulationStep { get { return m_simulationStep; } } 84 public long SimulationStep { get { return m_simulationStep; } }
110 private int m_taintsToProcessPerStep; 85 internal float LastTimeStep { get; private set; }
86
87 // Physical objects can register for prestep or poststep events
88 public delegate void PreStepAction(float timeStep);
89 public delegate void PostStepAction(float timeStep);
90 public event PreStepAction BeforeStep;
91 public event PostStepAction AfterStep;
111 92
112 // A value of the time now so all the collision and update routines do not have to get their own 93 // 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 94 // Set to 'now' just before all the prims and actors are called for collisions and updates
@@ -121,31 +102,22 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
121 public bool InTaintTime { get; private set; } 102 public bool InTaintTime { get; private set; }
122 103
123 // Pinned memory used to pass step information between managed and unmanaged 104 // Pinned memory used to pass step information between managed and unmanaged
124 private int m_maxCollisionsPerFrame; 105 internal int m_maxCollisionsPerFrame;
125 private CollisionDesc[] m_collisionArray; 106 internal CollisionDesc[] m_collisionArray;
126 private GCHandle m_collisionArrayPinnedHandle;
127 107
128 private int m_maxUpdatesPerFrame; 108 internal int m_maxUpdatesPerFrame;
129 private EntityProperties[] m_updateArray; 109 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 110
139 public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero 111 public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero
140 public const uint GROUNDPLANE_ID = 1; 112 public const uint GROUNDPLANE_ID = 1;
141 public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here 113 public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here
142 114
143 private float m_waterLevel; 115 public float SimpleWaterLevel { get; set; }
144 public BSTerrainManager TerrainManager { get; private set; } 116 public BSTerrainManager TerrainManager { get; private set; }
145 117
146 public ConfigurationParameters Params 118 public ConfigurationParameters Params
147 { 119 {
148 get { return m_params[0]; } 120 get { return UnmanagedParams[0]; }
149 } 121 }
150 public Vector3 DefaultGravity 122 public Vector3 DefaultGravity
151 { 123 {
@@ -157,8 +129,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
157 get { return Params.gravity; } 129 get { return Params.gravity; }
158 } 130 }
159 131
160 public float MaximumObjectMass { get; private set; }
161
162 // When functions in the unmanaged code must be called, it is only 132 // 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 133 // done at a known time just before the simulation step. The taint
164 // system saves all these function calls and executes them in 134 // system saves all these function calls and executes them in
@@ -181,13 +151,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
181 151
182 // A pointer to an instance if this structure is passed to the C++ code 152 // 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. 153 // Used to pass basic configuration values to the unmanaged code.
184 ConfigurationParameters[] m_params; 154 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 155
192 // Sometimes you just have to log everything. 156 // Sometimes you just have to log everything.
193 public Logging.LogWriter PhysicsLogging; 157 public Logging.LogWriter PhysicsLogging;
@@ -195,15 +159,24 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
195 private string m_physicsLoggingDir; 159 private string m_physicsLoggingDir;
196 private string m_physicsLoggingPrefix; 160 private string m_physicsLoggingPrefix;
197 private int m_physicsLoggingFileMinutes; 161 private int m_physicsLoggingFileMinutes;
162 private bool m_physicsLoggingDoFlush;
163 private bool m_physicsPhysicalDumpEnabled;
164 public int PhysicsMetricDumpFrames { get; set; }
198 // 'true' of the vehicle code is to log lots of details 165 // 'true' of the vehicle code is to log lots of details
199 public bool VehicleLoggingEnabled { get; private set; } 166 public bool VehicleLoggingEnabled { get; private set; }
167 public bool VehiclePhysicalLoggingEnabled { get; private set; }
200 168
201 #region Construction and Initialization 169 #region Construction and Initialization
202 public BSScene(string identifier) 170 public BSScene(string engineType, string identifier)
203 { 171 {
204 m_initialized = false; 172 m_initialized = false;
205 // we are passed the name of the region we're working for. 173
174 // The name of the region we're working for is passed to us. Keep for identification.
206 RegionName = identifier; 175 RegionName = identifier;
176
177 // Set identifying variables in the PhysicsScene interface.
178 EngineType = engineType;
179 Name = EngineType + "/" + RegionName;
207 } 180 }
208 181
209 public override void Initialise(IMesher meshmerizer, IConfigSource config) 182 public override void Initialise(IMesher meshmerizer, IConfigSource config)
@@ -216,17 +189,13 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
216 Shapes = new BSShapeCollection(this); 189 Shapes = new BSShapeCollection(this);
217 190
218 // Allocate pinned memory to pass parameters. 191 // Allocate pinned memory to pass parameters.
219 m_params = new ConfigurationParameters[1]; 192 UnmanagedParams = new ConfigurationParameters[1];
220 m_paramsHandle = GCHandle.Alloc(m_params, GCHandleType.Pinned);
221 193
222 // Set default values for physics parameters plus any overrides from the ini file 194 // Set default values for physics parameters plus any overrides from the ini file
223 GetInitialParameterValues(config); 195 GetInitialParameterValues(config);
224 196
225 // allocate more pinned memory close to the above in an attempt to get the memory all together 197 // Get the connection to the physics engine (could be native or one of many DLLs)
226 m_collisionArray = new CollisionDesc[m_maxCollisionsPerFrame]; 198 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 199
231 // Enable very detailed logging. 200 // Enable very detailed logging.
232 // By creating an empty logger when not logging, the log message invocation code 201 // By creating an empty logger when not logging, the log message invocation code
@@ -234,28 +203,16 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
234 if (m_physicsLoggingEnabled) 203 if (m_physicsLoggingEnabled)
235 { 204 {
236 PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes); 205 PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes);
206 PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output error messages.
237 } 207 }
238 else 208 else
239 { 209 {
240 PhysicsLogging = new Logging.LogWriter(); 210 PhysicsLogging = new Logging.LogWriter();
241 } 211 }
242 212
243 // If Debug logging level, enable logging from the unmanaged code 213 // Allocate memory for returning of the updates and collisions from the physics engine
244 m_DebugLogCallbackHandle = null; 214 m_collisionArray = new CollisionDesc[m_maxCollisionsPerFrame];
245 if (m_log.IsDebugEnabled || PhysicsLogging.Enabled) 215 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 216
260 // The bounding box for the simulated world. The origin is 0,0,0 unless we're 217 // The bounding box for the simulated world. The origin is 0,0,0 unless we're
261 // a child in a mega-region. 218 // a child in a mega-region.
@@ -263,18 +220,14 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
263 // area. It tracks active objects no matter where they are. 220 // area. It tracks active objects no matter where they are.
264 Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); 221 Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
265 222
266 // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader); 223 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 224
272 Constraints = new BSConstraintCollection(World); 225 Constraints = new BSConstraintCollection(World);
273 226
274 TerrainManager = new BSTerrainManager(this); 227 TerrainManager = new BSTerrainManager(this);
275 TerrainManager.CreateInitialGroundPlaneAndTerrain(); 228 TerrainManager.CreateInitialGroundPlaneAndTerrain();
276 229
277 m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)Params.linksetImplementation); 230 m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation);
278 231
279 InTaintTime = false; 232 InTaintTime = false;
280 m_initialized = true; 233 m_initialized = true;
@@ -285,9 +238,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
285 private void GetInitialParameterValues(IConfigSource config) 238 private void GetInitialParameterValues(IConfigSource config)
286 { 239 {
287 ConfigurationParameters parms = new ConfigurationParameters(); 240 ConfigurationParameters parms = new ConfigurationParameters();
288 m_params[0] = parms; 241 UnmanagedParams[0] = parms;
289 242
290 SetParameterDefaultValues(); 243 BSParam.SetParameterDefaultValues(this);
291 244
292 if (config != null) 245 if (config != null)
293 { 246 {
@@ -295,19 +248,34 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
295 IConfig pConfig = config.Configs["BulletSim"]; 248 IConfig pConfig = config.Configs["BulletSim"];
296 if (pConfig != null) 249 if (pConfig != null)
297 { 250 {
298 SetParameterConfigurationValues(pConfig); 251 BSParam.SetParameterConfigurationValues(this, pConfig);
252
253 // There are two Bullet implementations to choose from
254 BulletEngineName = pConfig.GetString("BulletEngine", "BulletUnmanaged");
299 255
300 // Very detailed logging for physics debugging 256 // Very detailed logging for physics debugging
257 // TODO: the boolean values can be moved to the normal parameter processing.
301 m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false); 258 m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false);
302 m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", "."); 259 m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", ".");
303 m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-"); 260 m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-");
304 m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5); 261 m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5);
262 m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false);
263 m_physicsPhysicalDumpEnabled = pConfig.GetBoolean("PhysicsPhysicalDumpEnabled", false);
305 // Very detailed logging for vehicle debugging 264 // Very detailed logging for vehicle debugging
306 VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false); 265 VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
266 VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false);
307 267
308 // Do any replacements in the parameters 268 // Do any replacements in the parameters
309 m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName); 269 m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
310 } 270 }
271
272 // The material characteristics.
273 BSMaterials.InitializeFromDefaults(Params);
274 if (pConfig != null)
275 {
276 // Let the user add new and interesting material property values.
277 BSMaterials.InitializefromParameters(pConfig);
278 }
311 } 279 }
312 } 280 }
313 281
@@ -326,16 +294,41 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
326 return ret; 294 return ret;
327 } 295 }
328 296
329 // Called directly from unmanaged code so don't do much 297 // Select the connection to the actual Bullet implementation.
330 private void BulletLogger(string msg) 298 // The main engine selection is the engineName up to the first hypen.
299 // So "Bullet-2.80-OpenCL-Intel" specifies the 'bullet' class here and the whole name
300 // is passed to the engine to do its special selection, etc.
301 private BSAPITemplate SelectUnderlyingBulletEngine(string engineName)
331 { 302 {
332 m_log.Debug("[BULLETS UNMANAGED]:" + msg); 303 // For the moment, do a simple switch statement.
333 } 304 // Someday do fancyness with looking up the interfaces in the assembly.
305 BSAPITemplate ret = null;
334 306
335 // Called directly from unmanaged code so don't do much 307 string selectionName = engineName.ToLower();
336 private void BulletLoggerPhysLog(string msg) 308 int hyphenIndex = engineName.IndexOf("-");
337 { 309 if (hyphenIndex > 0)
338 DetailLog("[BULLETS UNMANAGED]:" + msg); 310 selectionName = engineName.ToLower().Substring(0, hyphenIndex - 1);
311
312 switch (selectionName)
313 {
314 case "bulletunmanaged":
315 ret = new BSAPIUnman(engineName, this);
316 break;
317 case "bulletxna":
318 ret = new BSAPIXNA(engineName, this);
319 break;
320 }
321
322 if (ret == null)
323 {
324 m_log.ErrorFormat("{0) COULD NOT SELECT BULLET ENGINE: '[BulletSim]PhysicsEngine' must be either 'BulletUnmanaged-*' or 'BulletXNA-*'", LogHeader);
325 }
326 else
327 {
328 m_log.WarnFormat("{0} Selected bullet engine {1} -> {2}/{3}", LogHeader, engineName, ret.BulletEngineName, ret.BulletEngineVersion);
329 }
330
331 return ret;
339 } 332 }
340 333
341 public override void Dispose() 334 public override void Dispose()
@@ -345,8 +338,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
345 // make sure no stepping happens while we're deleting stuff 338 // make sure no stepping happens while we're deleting stuff
346 m_initialized = false; 339 m_initialized = false;
347 340
348 TerrainManager.ReleaseGroundPlaneAndTerrain();
349
350 foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects) 341 foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
351 { 342 {
352 kvp.Value.Destroy(); 343 kvp.Value.Destroy();
@@ -366,8 +357,15 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
366 Shapes = null; 357 Shapes = null;
367 } 358 }
368 359
360 if (TerrainManager != null)
361 {
362 TerrainManager.ReleaseGroundPlaneAndTerrain();
363 TerrainManager.Dispose();
364 TerrainManager = null;
365 }
366
369 // Anything left in the unmanaged code should be cleaned out 367 // Anything left in the unmanaged code should be cleaned out
370 BulletSimAPI.Shutdown2(World.ptr); 368 PE.Shutdown(World);
371 369
372 // Not logging any more 370 // Not logging any more
373 PhysicsLogging.Close(); 371 PhysicsLogging.Close();
@@ -389,12 +387,14 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
389 if (!m_initialized) return null; 387 if (!m_initialized) return null;
390 388
391 BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying); 389 BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying);
392 lock (PhysObjects) PhysObjects.Add(localID, actor); 390 lock (PhysObjects)
391 PhysObjects.Add(localID, actor);
393 392
394 // TODO: Remove kludge someday. 393 // TODO: Remove kludge someday.
395 // We must generate a collision for avatars whether they collide or not. 394 // We must generate a collision for avatars whether they collide or not.
396 // This is required by OpenSim to update avatar animations, etc. 395 // This is required by OpenSim to update avatar animations, etc.
397 lock (m_avatars) m_avatars.Add(actor); 396 lock (m_avatars)
397 m_avatars.Add(actor);
398 398
399 return actor; 399 return actor;
400 } 400 }
@@ -410,9 +410,11 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
410 { 410 {
411 try 411 try
412 { 412 {
413 lock (PhysObjects) PhysObjects.Remove(actor.LocalID); 413 lock (PhysObjects)
414 PhysObjects.Remove(bsactor.LocalID);
414 // Remove kludge someday 415 // Remove kludge someday
415 lock (m_avatars) m_avatars.Remove(bsactor); 416 lock (m_avatars)
417 m_avatars.Remove(bsactor);
416 } 418 }
417 catch (Exception e) 419 catch (Exception e)
418 { 420 {
@@ -421,13 +423,18 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
421 bsactor.Destroy(); 423 bsactor.Destroy();
422 // bsactor.dispose(); 424 // bsactor.dispose();
423 } 425 }
426 else
427 {
428 m_log.ErrorFormat("{0}: Requested to remove avatar that is not a BSCharacter. ID={1}, type={2}",
429 LogHeader, actor.LocalID, actor.GetType().Name);
430 }
424 } 431 }
425 432
426 public override void RemovePrim(PhysicsActor prim) 433 public override void RemovePrim(PhysicsActor prim)
427 { 434 {
428 if (!m_initialized) return; 435 if (!m_initialized) return;
429 436
430 BSPrim bsprim = prim as BSPrim; 437 BSPhysObject bsprim = prim as BSPhysObject;
431 if (bsprim != null) 438 if (bsprim != null)
432 { 439 {
433 DetailLog("{0},RemovePrim,call", bsprim.LocalID); 440 DetailLog("{0},RemovePrim,call", bsprim.LocalID);
@@ -456,9 +463,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
456 463
457 if (!m_initialized) return null; 464 if (!m_initialized) return null;
458 465
459 DetailLog("{0},AddPrimShape,call", localID); 466 DetailLog("{0},BSScene.AddPrimShape,call", localID);
460 467
461 BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, pbs, isPhysical); 468 BSPhysObject prim = new BSPrimLinkable(localID, primName, this, position, size, rotation, pbs, isPhysical);
462 lock (PhysObjects) PhysObjects.Add(localID, prim); 469 lock (PhysObjects) PhysObjects.Add(localID, prim);
463 return prim; 470 return prim;
464 } 471 }
@@ -474,41 +481,56 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
474 // Simulate one timestep 481 // Simulate one timestep
475 public override float Simulate(float timeStep) 482 public override float Simulate(float timeStep)
476 { 483 {
484 // prevent simulation until we've been initialized
485 if (!m_initialized) return 5.0f;
486
487 LastTimeStep = timeStep;
488
477 int updatedEntityCount = 0; 489 int updatedEntityCount = 0;
478 IntPtr updatedEntitiesPtr;
479 int collidersCount = 0; 490 int collidersCount = 0;
480 IntPtr collidersPtr;
481 491
482 int beforeTime = 0; 492 int beforeTime = 0;
483 int simTime = 0; 493 int simTime = 0;
484 494
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 495 // update the prim states while we know the physics engine is not busy
489 int numTaints = _taintOperations.Count; 496 int numTaints = _taintOperations.Count;
497
498 InTaintTime = true; // Only used for debugging so locking is not necessary.
499
490 ProcessTaints(); 500 ProcessTaints();
491 501
492 // Some of the prims operate with special vehicle properties 502 // Some of the physical objects requre individual, pre-step calls
493 ProcessVehicles(timeStep); 503 // (vehicles and avatar movement, in particular)
494 ProcessTaints(); // the vehicles might have added taints 504 TriggerPreStepEvent(timeStep);
505
506 // the prestep actions might have added taints
507 numTaints += _taintOperations.Count;
508 ProcessTaints();
509
510 InTaintTime = false; // Only used for debugging so locking is not necessary.
511
512 // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world.
513 // Only enable this in a limited test world with few objects.
514 if (m_physicsPhysicalDumpEnabled)
515 PE.DumpAllInfo(World);
495 516
496 // step the physical world one interval 517 // step the physical world one interval
497 m_simulationStep++; 518 m_simulationStep++;
498 int numSubSteps = 0; 519 int numSubSteps = 0;
499
500 try 520 try
501 { 521 {
502 if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG 522 if (PhysicsLogging.Enabled)
503 if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount(); 523 beforeTime = Util.EnvironmentTickCount();
504 524
505 numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep, 525 numSubSteps = PE.PhysicsStep(World, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out collidersCount);
506 out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr);
507 526
508 if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime); 527 if (PhysicsLogging.Enabled)
509 DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}", 528 {
510 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount); 529 simTime = Util.EnvironmentTickCountSubtract(beforeTime);
511 if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG 530 DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}",
531 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps,
532 updatedEntityCount, collidersCount, ObjectsWithCollisions.Count);
533 }
512 } 534 }
513 catch (Exception e) 535 catch (Exception e)
514 { 536 {
@@ -520,9 +542,10 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
520 collidersCount = 0; 542 collidersCount = 0;
521 } 543 }
522 544
523 // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in 545 if (PhysicsMetricDumpFrames != 0 && ((m_simulationStep % PhysicsMetricDumpFrames) == 0))
546 PE.DumpPhysicsStatistics(World);
524 547
525 // Get a value for 'now' so all the collision and update routines don't have to get their own 548 // Get a value for 'now' so all the collision and update routines don't have to get their own.
526 SimulationNowTime = Util.EnvironmentTickCount(); 549 SimulationNowTime = Util.EnvironmentTickCount();
527 550
528 // If there were collisions, process them by sending the event to the prim. 551 // If there were collisions, process them by sending the event to the prim.
@@ -535,8 +558,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
535 uint cB = m_collisionArray[ii].bID; 558 uint cB = m_collisionArray[ii].bID;
536 Vector3 point = m_collisionArray[ii].point; 559 Vector3 point = m_collisionArray[ii].point;
537 Vector3 normal = m_collisionArray[ii].normal; 560 Vector3 normal = m_collisionArray[ii].normal;
538 SendCollision(cA, cB, point, normal, 0.01f); 561 float penetration = m_collisionArray[ii].penetration;
539 SendCollision(cB, cA, point, -normal, 0.01f); 562 SendCollision(cA, cB, point, normal, penetration);
563 SendCollision(cB, cA, point, -normal, penetration);
540 } 564 }
541 } 565 }
542 566
@@ -562,12 +586,16 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
562 586
563 // Objects that are done colliding are removed from the ObjectsWithCollisions list. 587 // Objects that are done colliding are removed from the ObjectsWithCollisions list.
564 // Not done above because it is inside an iteration of ObjectWithCollisions. 588 // Not done above because it is inside an iteration of ObjectWithCollisions.
589 // This complex collision processing is required to create an empty collision
590 // event call after all real collisions have happened on an object. This enables
591 // the simulator to generate the 'collision end' event.
565 if (ObjectsWithNoMoreCollisions.Count > 0) 592 if (ObjectsWithNoMoreCollisions.Count > 0)
566 { 593 {
567 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions) 594 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
568 ObjectsWithCollisions.Remove(po); 595 ObjectsWithCollisions.Remove(po);
569 ObjectsWithNoMoreCollisions.Clear(); 596 ObjectsWithNoMoreCollisions.Clear();
570 } 597 }
598 // Done with collisions.
571 599
572 // If any of the objects had updated properties, tell the object it has been changed by the physics engine 600 // If any of the objects had updated properties, tell the object it has been changed by the physics engine
573 if (updatedEntityCount > 0) 601 if (updatedEntityCount > 0)
@@ -583,17 +611,17 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
583 } 611 }
584 } 612 }
585 613
586 ProcessPostStepTaints(); 614 TriggerPostStepEvent(timeStep);
587 615
588 // This causes the unmanaged code to output ALL the values found in ALL the objects in the world. 616 // 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. 617 // Only enable this in a limited test world with few objects.
590 // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG 618 if (m_physicsPhysicalDumpEnabled)
619 PE.DumpAllInfo(World);
591 620
592 // The physics engine returns the number of milliseconds it simulated this call. 621 // 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. 622 // 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). 623 // Multiply by a fixed nominal frame rate to give a rate similar to the simulator (usually 55).
595 return numSubSteps * m_fixedTimeStep * 1000 * 55; 624 return (float)numSubSteps * m_fixedTimeStep * 1000f * NominalFrameRate;
596 // return timeStep * 1000 * 55;
597 } 625 }
598 626
599 // Something has collided 627 // Something has collided
@@ -639,12 +667,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
639 667
640 public override void SetWaterLevel(float baseheight) 668 public override void SetWaterLevel(float baseheight)
641 { 669 {
642 m_waterLevel = baseheight; 670 SimpleWaterLevel = baseheight;
643 }
644 // Someday....
645 public float GetWaterLevelAtXYZ(Vector3 loc)
646 {
647 return m_waterLevel;
648 } 671 }
649 672
650 public override void DeleteTerrain() 673 public override void DeleteTerrain()
@@ -675,12 +698,35 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
675 698
676 public override Dictionary<uint, float> GetTopColliders() 699 public override Dictionary<uint, float> GetTopColliders()
677 { 700 {
678 return new Dictionary<uint, float>(); 701 Dictionary<uint, float> topColliders;
702
703 lock (PhysObjects)
704 {
705 foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
706 {
707 kvp.Value.ComputeCollisionScore();
708 }
709
710 List<BSPhysObject> orderedPrims = new List<BSPhysObject>(PhysObjects.Values);
711 orderedPrims.OrderByDescending(p => p.CollisionScore);
712 topColliders = orderedPrims.Take(25).ToDictionary(p => p.LocalID, p => p.CollisionScore);
713 }
714
715 return topColliders;
679 } 716 }
680 717
681 public override bool IsThreaded { get { return false; } } 718 public override bool IsThreaded { get { return false; } }
682 719
683 #region Taints 720 #region Taints
721 // The simulation execution order is:
722 // Simulate()
723 // DoOneTimeTaints
724 // TriggerPreStepEvent
725 // DoOneTimeTaints
726 // Step()
727 // ProcessAndSendToSimulatorCollisions
728 // ProcessAndSendToSimulatorPropertyUpdates
729 // TriggerPostStepEvent
684 730
685 // Calls to the PhysicsActors can't directly call into the physics engine 731 // 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. 732 // because it might be busy. We delay changes to a known time.
@@ -707,58 +753,35 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
707 TaintedObject(ident, callback); 753 TaintedObject(ident, callback);
708 } 754 }
709 755
756 private void TriggerPreStepEvent(float timeStep)
757 {
758 PreStepAction actions = BeforeStep;
759 if (actions != null)
760 actions(timeStep);
761
762 }
763
764 private void TriggerPostStepEvent(float timeStep)
765 {
766 PostStepAction actions = AfterStep;
767 if (actions != null)
768 actions(timeStep);
769
770 }
771
710 // When someone tries to change a property on a BSPrim or BSCharacter, the object queues 772 // 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 773 // 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. 774 // here just before the physics engine is called to step the simulation.
713 public void ProcessTaints() 775 public void ProcessTaints()
714 { 776 {
715 InTaintTime = true; // Only used for debugging so locking is not necessary.
716 ProcessRegularTaints(); 777 ProcessRegularTaints();
717 ProcessPostTaintTaints(); 778 ProcessPostTaintTaints();
718 InTaintTime = false;
719 } 779 }
720 780
721 private void ProcessRegularTaints() 781 private void ProcessRegularTaints()
722 { 782 {
723 if (_taintOperations.Count > 0) // save allocating new list if there is nothing to process 783 if (_taintOperations.Count > 0) // save allocating new list if there is nothing to process
724 { 784 {
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 785 // swizzle a new list into the list location so we can process what's there
763 List<TaintCallbackEntry> oldList; 786 List<TaintCallbackEntry> oldList;
764 lock (_taintLock) 787 lock (_taintLock)
@@ -797,6 +820,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
797 return; 820 return;
798 } 821 }
799 822
823 // Taints that happen after the normal taint processing but before the simulation step.
800 private void ProcessPostTaintTaints() 824 private void ProcessPostTaintTaints()
801 { 825 {
802 if (_postTaintOperations.Count > 0) 826 if (_postTaintOperations.Count > 0)
@@ -824,45 +848,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
824 } 848 }
825 } 849 }
826 850
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. 851 // Only used for debugging. Does not change state of anything so locking is not necessary.
867 public bool AssertInTaintTime(string whereFrom) 852 public bool AssertInTaintTime(string whereFrom)
868 { 853 {
@@ -870,517 +855,19 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
870 { 855 {
871 DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom); 856 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); 857 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. 858 // Util.PrintCallStack(DetailLog);
874 } 859 }
875 return InTaintTime; 860 return InTaintTime;
876 } 861 }
877 862
878 #endregion // Taints 863 #endregion // Taints
879 864
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 865 #region IPhysicsParameters
1379 // Get the list of parameters this physics engine supports 866 // Get the list of parameters this physics engine supports
1380 public PhysParameterEntry[] GetParameterList() 867 public PhysParameterEntry[] GetParameterList()
1381 { 868 {
1382 BuildParameterTable(); 869 BSParam.BuildParameterTable();
1383 return SettableParameters; 870 return BSParam.SettableParameters;
1384 } 871 }
1385 872
1386 // Set parameter on a specific or all instances. 873 // Set parameter on a specific or all instances.
@@ -1389,63 +876,65 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1389 // will use the next time since it's pinned and shared memory. 876 // will use the next time since it's pinned and shared memory.
1390 // Some of the values require calling into the physics engine to get the new 877 // Some of the values require calling into the physics engine to get the new
1391 // value activated ('terrainFriction' for instance). 878 // value activated ('terrainFriction' for instance).
1392 public bool SetPhysicsParameter(string parm, float val, uint localID) 879 public bool SetPhysicsParameter(string parm, string val, uint localID)
1393 { 880 {
1394 bool ret = false; 881 bool ret = false;
1395 ParameterDefn theParam; 882
1396 if (TryGetParameter(parm, out theParam)) 883 BSParam.ParameterDefnBase theParam;
884 if (BSParam.TryGetParameter(parm, out theParam))
1397 { 885 {
1398 theParam.setter(this, parm, localID, val); 886 // Set the value in the C# code
887 theParam.SetValue(this, val);
888
889 // Optionally set the parameter in the unmanaged code
890 if (theParam.HasSetOnObject)
891 {
892 // update all the localIDs specified
893 // If the local ID is APPLY_TO_NONE, just change the default value
894 // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs
895 // If the localID is a specific object, apply the parameter change to only that object
896 List<uint> objectIDs = new List<uint>();
897 switch (localID)
898 {
899 case PhysParameterEntry.APPLY_TO_NONE:
900 // This will cause a call into the physical world if some operation is specified (SetOnObject).
901 objectIDs.Add(TERRAIN_ID);
902 TaintedUpdateParameter(parm, objectIDs, val);
903 break;
904 case PhysParameterEntry.APPLY_TO_ALL:
905 lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys);
906 TaintedUpdateParameter(parm, objectIDs, val);
907 break;
908 default:
909 // setting only one localID
910 objectIDs.Add(localID);
911 TaintedUpdateParameter(parm, objectIDs, val);
912 break;
913 }
914 }
915
1399 ret = true; 916 ret = true;
1400 } 917 }
1401 return ret; 918 return ret;
1402 } 919 }
1403 920
1404 // update all the localIDs specified
1405 // 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
1407 // 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)
1409 {
1410 List<uint> objectIDs = new List<uint>();
1411 switch (localID)
1412 {
1413 case PhysParameterEntry.APPLY_TO_NONE:
1414 defaultLoc = val; // setting only the default value
1415 // This will cause a call into the physical world if some operation is specified (SetOnObject).
1416 objectIDs.Add(TERRAIN_ID);
1417 TaintedUpdateParameter(parm, objectIDs, val);
1418 break;
1419 case PhysParameterEntry.APPLY_TO_ALL:
1420 defaultLoc = val; // setting ALL also sets the default value
1421 lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys);
1422 TaintedUpdateParameter(parm, objectIDs, val);
1423 break;
1424 default:
1425 // setting only one localID
1426 objectIDs.Add(localID);
1427 TaintedUpdateParameter(parm, objectIDs, val);
1428 break;
1429 }
1430 }
1431
1432 // schedule the actual updating of the paramter to when the phys engine is not busy 921 // schedule the actual updating of the paramter to when the phys engine is not busy
1433 private void TaintedUpdateParameter(string parm, List<uint> lIDs, float val) 922 private void TaintedUpdateParameter(string parm, List<uint> lIDs, string val)
1434 { 923 {
1435 float xval = val; 924 string xval = val;
1436 List<uint> xlIDs = lIDs; 925 List<uint> xlIDs = lIDs;
1437 string xparm = parm; 926 string xparm = parm;
1438 TaintedObject("BSScene.UpdateParameterSet", delegate() { 927 TaintedObject("BSScene.UpdateParameterSet", delegate() {
1439 ParameterDefn thisParam; 928 BSParam.ParameterDefnBase thisParam;
1440 if (TryGetParameter(xparm, out thisParam)) 929 if (BSParam.TryGetParameter(xparm, out thisParam))
1441 { 930 {
1442 if (thisParam.onObject != null) 931 if (thisParam.HasSetOnObject)
1443 { 932 {
1444 foreach (uint lID in xlIDs) 933 foreach (uint lID in xlIDs)
1445 { 934 {
1446 BSPhysObject theObject = null; 935 BSPhysObject theObject = null;
1447 PhysObjects.TryGetValue(lID, out theObject); 936 if (PhysObjects.TryGetValue(lID, out theObject))
1448 thisParam.onObject(this, theObject, xval); 937 thisParam.SetOnObject(this, theObject);
1449 } 938 }
1450 } 939 }
1451 } 940 }
@@ -1454,14 +943,14 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1454 943
1455 // Get parameter. 944 // Get parameter.
1456 // Return 'false' if not able to get the parameter. 945 // Return 'false' if not able to get the parameter.
1457 public bool GetPhysicsParameter(string parm, out float value) 946 public bool GetPhysicsParameter(string parm, out string value)
1458 { 947 {
1459 float val = 0f; 948 string val = String.Empty;
1460 bool ret = false; 949 bool ret = false;
1461 ParameterDefn theParam; 950 BSParam.ParameterDefnBase theParam;
1462 if (TryGetParameter(parm, out theParam)) 951 if (BSParam.TryGetParameter(parm, out theParam))
1463 { 952 {
1464 val = theParam.getter(this); 953 val = theParam.GetValue(this);
1465 ret = true; 954 ret = true;
1466 } 955 }
1467 value = val; 956 value = val;
@@ -1470,24 +959,12 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1470 959
1471 #endregion IPhysicsParameters 960 #endregion IPhysicsParameters
1472 961
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. 962 // Invoke the detailed logger and output something if it's enabled.
1486 public void DetailLog(string msg, params Object[] args) 963 public void DetailLog(string msg, params Object[] args)
1487 { 964 {
1488 PhysicsLogging.Write(msg, args); 965 PhysicsLogging.Write(msg, args);
1489 // Add the Flush() if debugging crashes. Gets all the messages written out. 966 // Add the Flush() if debugging crashes. Gets all the messages written out.
1490 // PhysicsLogging.Flush(); 967 if (m_physicsLoggingDoFlush) PhysicsLogging.Flush();
1491 } 968 }
1492 // Used to fill in the LocalID when there isn't one. It's the correct number of characters. 969 // Used to fill in the LocalID when there isn't one. It's the correct number of characters.
1493 public const string DetailLogZero = "0000000000"; 970 public const string DetailLogZero = "0000000000";