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.cs978
1 files changed, 417 insertions, 561 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
index 740f339..a31c578 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
@@ -39,88 +39,96 @@ using log4net;
39using OpenMetaverse; 39using OpenMetaverse;
40 40
41// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim) 41// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim)
42// Test sculpties (verified that they don't work) 42// Debug linkset
43// Test with multiple regions in one simulator
44// Adjust character capsule size when height is adjusted (ScenePresence.SetHeight)
45// Test sculpties
43// Compute physics FPS reasonably 46// Compute physics FPS reasonably
44// Based on material, set density and friction 47// Based on material, set density and friction
45// Don't use constraints in linksets of non-physical objects. Means having to move children manually. 48// More efficient memory usage when passing hull information from BSPrim to BulletSim
49// Move all logic out of the C++ code and into the C# code for easier future modifications.
46// Four states of prim: Physical, regular, phantom and selected. Are we modeling these correctly? 50// 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) 51// 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 52// At the moment, physical and phantom causes object to drop through the terrain
49// Physical phantom objects and related typing (collision options ) 53// Physical phantom objects and related typing (collision options )
54// Use collision masks for collision with terrain and phantom objects
50// Check out llVolumeDetect. Must do something for that. 55// 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? 56// 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. 57// changing the position and orientation of a linked prim must rebuild the constraint with the root.
58// 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 59// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect
60// Implement the genCollisions feature in BulletSim::SetObjectProperties (don't pass up unneeded collisions)
56// Implement LockAngularMotion 61// Implement LockAngularMotion
57// Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation) 62// Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation)
63// Does NeedsMeshing() really need to exclude all the different shapes?
58// Remove mesh and Hull stuff. Use mesh passed to bullet and use convexdecom from bullet. 64// 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? 65// Add PID movement operations. What does ScenePresence.MoveToTarget do?
60// Check terrain size. 128 or 127? 66// Check terrain size. 128 or 127?
61// Raycast 67// Raycast
62// 68//
63namespace OpenSim.Region.Physics.BulletSPlugin 69namespace OpenSim.Region.Physics.BulletSPlugin
64{ 70{
65public sealed class BSScene : PhysicsScene, IPhysicsParameters 71public class BSScene : PhysicsScene, IPhysicsParameters
66{ 72{
67 private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 73 private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
68 private static readonly string LogHeader = "[BULLETS SCENE]"; 74 private static readonly string LogHeader = "[BULLETS SCENE]";
69 75
70 // The name of the region we're working for. 76 public void DebugLog(string mm, params Object[] xx) { if (ShouldDebugLog) m_log.DebugFormat(mm, xx); }
71 public string RegionName { get; private set; }
72 77
73 public string BulletSimVersion = "?"; 78 public string BulletSimVersion = "?";
74 79
75 public Dictionary<uint, BSPhysObject> PhysObjects; 80 private Dictionary<uint, BSCharacter> m_avatars = new Dictionary<uint, BSCharacter>();
76 public BSShapeCollection Shapes; 81 private Dictionary<uint, BSPrim> m_prims = new Dictionary<uint, BSPrim>();
77 82 private HashSet<BSCharacter> m_avatarsWithCollisions = new HashSet<BSCharacter>();
78 // Keeping track of the objects with collisions so we can report begin and end of a collision 83 private HashSet<BSPrim> m_primsWithCollisions = new HashSet<BSPrim>();
79 public HashSet<BSPhysObject> ObjectsWithCollisions = new HashSet<BSPhysObject>(); 84 private List<BSPrim> m_vehicles = new List<BSPrim>();
80 public HashSet<BSPhysObject> ObjectsWithNoMoreCollisions = new HashSet<BSPhysObject>(); 85 private float[] m_heightMap;
81 // Keep track of all the avatars so we can send them a collision event 86 private float m_waterLevel;
82 // every tick so OpenSim will update its animation. 87 private uint m_worldID;
83 private HashSet<BSPhysObject> m_avatars = new HashSet<BSPhysObject>(); 88 public uint WorldID { get { return m_worldID; } }
84
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
89 // let my minuions use my logger 90 // let my minuions use my logger
90 public ILog Logger { get { return m_log; } } 91 public ILog Logger { get { return m_log; } }
91 92
92 public IMesher mesher; 93 private bool m_initialized = false;
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 94
99 public uint WorldID { get; private set; } 95 private int m_detailedStatsStep = 0;
100 public BulletSim World { get; private set; } 96
97 public IMesher mesher;
98 private float m_meshLOD;
99 public float MeshLOD
100 {
101 get { return m_meshLOD; }
102 }
103 private float m_sculptLOD;
104 public float SculptLOD
105 {
106 get { return m_sculptLOD; }
107 }
101 108
102 // All the constraints that have been allocated in this instance. 109 private BulletSim m_worldSim;
103 public BSConstraintCollection Constraints { get; private set; } 110 public BulletSim World
111 {
112 get { return m_worldSim; }
113 }
114 private BSConstraintCollection m_constraintCollection;
115 public BSConstraintCollection Constraints
116 {
117 get { return m_constraintCollection; }
118 }
104 119
105 // Simulation parameters
106 private int m_maxSubSteps; 120 private int m_maxSubSteps;
107 private float m_fixedTimeStep; 121 private float m_fixedTimeStep;
108 private long m_simulationStep = 0; 122 private long m_simulationStep = 0;
109 public long SimulationStep { get { return m_simulationStep; } } 123 public long SimulationStep { get { return m_simulationStep; } }
110 private int m_taintsToProcessPerStep; 124
125 public float LastSimulatedTimestep { get; private set; }
111 126
112 // A value of the time now so all the collision and update routines do not have to get their own 127 // 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 128 // Set to 'now' just before all the prims and actors are called for collisions and updates
114 public int SimulationNowTime { get; private set; } 129 private int m_simulationNowTime;
115 130 public int SimulationNowTime { get { return m_simulationNowTime; } }
116 // True if initialized and ready to do simulation steps
117 private bool m_initialized = false;
118
119 // Flag which is true when processing taints.
120 // Not guaranteed to be correct all the time (don't depend on this) but good for debugging.
121 public bool InTaintTime { get; private set; }
122 131
123 // Pinned memory used to pass step information between managed and unmanaged
124 private int m_maxCollisionsPerFrame; 132 private int m_maxCollisionsPerFrame;
125 private CollisionDesc[] m_collisionArray; 133 private CollisionDesc[] m_collisionArray;
126 private GCHandle m_collisionArrayPinnedHandle; 134 private GCHandle m_collisionArrayPinnedHandle;
@@ -129,19 +137,14 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
129 private EntityProperties[] m_updateArray; 137 private EntityProperties[] m_updateArray;
130 private GCHandle m_updateArrayPinnedHandle; 138 private GCHandle m_updateArrayPinnedHandle;
131 139
132 public bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed 140 private bool _meshSculptedPrim = true; // cause scuplted prims to get meshed
133 public bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes 141 private bool _forceSimplePrimMeshing = false; // 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 142
136 public float PID_D { get; private set; } // derivative 143 public float PID_D { get; private set; } // derivative
137 public float PID_P { get; private set; } // proportional 144 public float PID_P { get; private set; } // proportional
138 145
139 public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero 146 public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero
140 public const uint GROUNDPLANE_ID = 1; 147 public const uint GROUNDPLANE_ID = 1;
141 public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here
142
143 private float m_waterLevel;
144 public BSTerrainManager TerrainManager { get; private set; }
145 148
146 public ConfigurationParameters Params 149 public ConfigurationParameters Params
147 { 150 {
@@ -151,18 +154,13 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
151 { 154 {
152 get { return new Vector3(0f, 0f, Params.gravity); } 155 get { return new Vector3(0f, 0f, Params.gravity); }
153 } 156 }
154 // Just the Z value of the gravity 157
155 public float DefaultGravityZ 158 private float m_maximumObjectMass;
159 public float MaximumObjectMass
156 { 160 {
157 get { return Params.gravity; } 161 get { return m_maximumObjectMass; }
158 } 162 }
159 163
160 public float MaximumObjectMass { get; private set; }
161
162 // 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
164 // system saves all these function calls and executes them in
165 // order before the simulation.
166 public delegate void TaintCallback(); 164 public delegate void TaintCallback();
167 private struct TaintCallbackEntry 165 private struct TaintCallbackEntry
168 { 166 {
@@ -174,19 +172,15 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
174 callback = c; 172 callback = c;
175 } 173 }
176 } 174 }
177 private Object _taintLock = new Object(); // lock for using the next object 175 private List<TaintCallbackEntry> _taintedObjects;
178 private List<TaintCallbackEntry> _taintOperations; 176 private Object _taintLock = new Object();
179 private Dictionary<string, TaintCallbackEntry> _postTaintOperations;
180 private List<TaintCallbackEntry> _postStepOperations;
181 177
182 // A pointer to an instance if this structure is passed to the C++ code 178 // 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.
184 ConfigurationParameters[] m_params; 179 ConfigurationParameters[] m_params;
185 GCHandle m_paramsHandle; 180 GCHandle m_paramsHandle;
186 181
187 // Handle to the callback used by the unmanaged code to call into the managed code. 182 public bool ShouldDebugLog { get; private set; }
188 // Used for debug logging. 183
189 // Need to store the handle in a persistant variable so it won't be freed.
190 private BulletSimAPI.DebugLogCallback m_DebugLogCallbackHandle; 184 private BulletSimAPI.DebugLogCallback m_DebugLogCallbackHandle;
191 185
192 // Sometimes you just have to log everything. 186 // Sometimes you just have to log everything.
@@ -195,26 +189,17 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
195 private string m_physicsLoggingDir; 189 private string m_physicsLoggingDir;
196 private string m_physicsLoggingPrefix; 190 private string m_physicsLoggingPrefix;
197 private int m_physicsLoggingFileMinutes; 191 private int m_physicsLoggingFileMinutes;
198 // 'true' of the vehicle code is to log lots of details
199 public bool VehicleLoggingEnabled { get; private set; }
200 192
201 #region Construction and Initialization 193 private bool m_vehicleLoggingEnabled;
194 public bool VehicleLoggingEnabled { get { return m_vehicleLoggingEnabled; } }
195
202 public BSScene(string identifier) 196 public BSScene(string identifier)
203 { 197 {
204 m_initialized = false; 198 m_initialized = false;
205 // we are passed the name of the region we're working for.
206 RegionName = identifier;
207 } 199 }
208 200
209 public override void Initialise(IMesher meshmerizer, IConfigSource config) 201 public override void Initialise(IMesher meshmerizer, IConfigSource config)
210 { 202 {
211 mesher = meshmerizer;
212 _taintOperations = new List<TaintCallbackEntry>();
213 _postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
214 _postStepOperations = new List<TaintCallbackEntry>();
215 PhysObjects = new Dictionary<uint, BSPhysObject>();
216 Shapes = new BSShapeCollection(this);
217
218 // Allocate pinned memory to pass parameters. 203 // Allocate pinned memory to pass parameters.
219 m_params = new ConfigurationParameters[1]; 204 m_params = new ConfigurationParameters[1];
220 m_paramsHandle = GCHandle.Alloc(m_params, GCHandleType.Pinned); 205 m_paramsHandle = GCHandle.Alloc(m_params, GCHandleType.Pinned);
@@ -230,7 +215,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
230 215
231 // Enable very detailed logging. 216 // Enable very detailed logging.
232 // By creating an empty logger when not logging, the log message invocation code 217 // By creating an empty logger when not logging, the log message invocation code
233 // can be left in and every call doesn't have to check for null. 218 // can be left in and every call doesn't have to check for null.
234 if (m_physicsLoggingEnabled) 219 if (m_physicsLoggingEnabled)
235 { 220 {
236 PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes); 221 PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes);
@@ -240,43 +225,39 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
240 PhysicsLogging = new Logging.LogWriter(); 225 PhysicsLogging = new Logging.LogWriter();
241 } 226 }
242 227
243 // If Debug logging level, enable logging from the unmanaged code 228 // Get the version of the DLL
244 m_DebugLogCallbackHandle = null; 229 // TODO: this doesn't work yet. Something wrong with marshaling the returned string.
230 // BulletSimVersion = BulletSimAPI.GetVersion();
231 // m_log.WarnFormat("{0}: BulletSim.dll version='{1}'", LogHeader, BulletSimVersion);
232
233 // if Debug, enable logging from the unmanaged code
245 if (m_log.IsDebugEnabled || PhysicsLogging.Enabled) 234 if (m_log.IsDebugEnabled || PhysicsLogging.Enabled)
246 { 235 {
247 m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader); 236 m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader);
248 if (PhysicsLogging.Enabled) 237 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); 238 m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLoggerPhysLog);
251 else 239 else
252 m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger); 240 m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger);
241 // the handle is saved in a variable to make sure it doesn't get freed after this call
242 BulletSimAPI.SetDebugLogCallback(m_DebugLogCallbackHandle);
253 } 243 }
254 244
255 // Get the version of the DLL 245 _taintedObjects = new List<TaintCallbackEntry>();
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 246
260 // The bounding box for the simulated world. The origin is 0,0,0 unless we're 247 mesher = meshmerizer;
261 // a child in a mega-region. 248 // The bounding box for the simulated world
262 // Bullet actually doesn't care about the extents of the simulated 249 Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, 8192f);
263 // area. It tracks active objects no matter where they are.
264 Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
265 250
266 // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader); 251 // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader);
267 World = new BulletSim(0, this, BulletSimAPI.Initialize2(worldExtent, m_paramsHandle.AddrOfPinnedObject(), 252 m_worldID = BulletSimAPI.Initialize(worldExtent, m_paramsHandle.AddrOfPinnedObject(),
268 m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(), 253 m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(),
269 m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject(), 254 m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject());
270 m_DebugLogCallbackHandle));
271
272 Constraints = new BSConstraintCollection(World);
273 255
274 TerrainManager = new BSTerrainManager(this); 256 // Initialization to support the transition to a new API which puts most of the logic
275 TerrainManager.CreateInitialGroundPlaneAndTerrain(); 257 // into the C# code so it is easier to modify and add to.
258 m_worldSim = new BulletSim(m_worldID, this, BulletSimAPI.GetSimHandle2(m_worldID));
259 m_constraintCollection = new BSConstraintCollection(World);
276 260
277 m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)Params.linksetImplementation);
278
279 InTaintTime = false;
280 m_initialized = true; 261 m_initialized = true;
281 } 262 }
282 263
@@ -300,13 +281,10 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
300 // Very detailed logging for physics debugging 281 // Very detailed logging for physics debugging
301 m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false); 282 m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false);
302 m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", "."); 283 m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", ".");
303 m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-"); 284 m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-");
304 m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5); 285 m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5);
305 // Very detailed logging for vehicle debugging 286 // Very detailed logging for vehicle debugging
306 VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false); 287 m_vehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
307
308 // Do any replacements in the parameters
309 m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
310 } 288 }
311 } 289 }
312 } 290 }
@@ -331,50 +309,12 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
331 { 309 {
332 m_log.Debug("[BULLETS UNMANAGED]:" + msg); 310 m_log.Debug("[BULLETS UNMANAGED]:" + msg);
333 } 311 }
334 312
335 // Called directly from unmanaged code so don't do much 313 // Called directly from unmanaged code so don't do much
336 private void BulletLoggerPhysLog(string msg) 314 private void BulletLoggerPhysLog(string msg)
337 { 315 {
338 DetailLog("[BULLETS UNMANAGED]:" + msg); 316 PhysicsLogging.Write("[BULLETS UNMANAGED]:" + msg);
339 }
340
341 public override void Dispose()
342 {
343 // m_log.DebugFormat("{0}: Dispose()", LogHeader);
344
345 // make sure no stepping happens while we're deleting stuff
346 m_initialized = false;
347
348 TerrainManager.ReleaseGroundPlaneAndTerrain();
349
350 foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
351 {
352 kvp.Value.Destroy();
353 }
354 PhysObjects.Clear();
355
356 // Now that the prims are all cleaned up, there should be no constraints left
357 if (Constraints != null)
358 {
359 Constraints.Dispose();
360 Constraints = null;
361 }
362
363 if (Shapes != null)
364 {
365 Shapes.Dispose();
366 Shapes = null;
367 }
368
369 // Anything left in the unmanaged code should be cleaned out
370 BulletSimAPI.Shutdown2(World.ptr);
371
372 // Not logging any more
373 PhysicsLogging.Close();
374 } 317 }
375 #endregion // Construction and Initialization
376
377 #region Prim and Avatar addition and removal
378 318
379 public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying) 319 public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying)
380 { 320 {
@@ -389,13 +329,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
389 if (!m_initialized) return null; 329 if (!m_initialized) return null;
390 330
391 BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying); 331 BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying);
392 lock (PhysObjects) PhysObjects.Add(localID, actor); 332 lock (m_avatars) m_avatars.Add(localID, actor);
393
394 // TODO: Remove kludge someday.
395 // We must generate a collision for avatars whether they collide or not.
396 // This is required by OpenSim to update avatar animations, etc.
397 lock (m_avatars) m_avatars.Add(actor);
398
399 return actor; 333 return actor;
400 } 334 }
401 335
@@ -410,9 +344,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
410 { 344 {
411 try 345 try
412 { 346 {
413 lock (PhysObjects) PhysObjects.Remove(actor.LocalID); 347 lock (m_avatars) m_avatars.Remove(actor.LocalID);
414 // Remove kludge someday
415 lock (m_avatars) m_avatars.Remove(bsactor);
416 } 348 }
417 catch (Exception e) 349 catch (Exception e)
418 { 350 {
@@ -430,11 +362,11 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
430 BSPrim bsprim = prim as BSPrim; 362 BSPrim bsprim = prim as BSPrim;
431 if (bsprim != null) 363 if (bsprim != null)
432 { 364 {
433 DetailLog("{0},RemovePrim,call", bsprim.LocalID); 365 // DetailLog("{0},RemovePrim,call", bsprim.LocalID);
434 // m_log.DebugFormat("{0}: RemovePrim. id={1}/{2}", LogHeader, bsprim.Name, bsprim.LocalID); 366 // m_log.DebugFormat("{0}: RemovePrim. id={1}/{2}", LogHeader, bsprim.Name, bsprim.LocalID);
435 try 367 try
436 { 368 {
437 lock (PhysObjects) PhysObjects.Remove(bsprim.LocalID); 369 lock (m_prims) m_prims.Remove(bsprim.LocalID);
438 } 370 }
439 catch (Exception e) 371 catch (Exception e)
440 { 372 {
@@ -456,21 +388,18 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
456 388
457 if (!m_initialized) return null; 389 if (!m_initialized) return null;
458 390
459 DetailLog("{0},AddPrimShape,call", localID); 391 // DetailLog("{0},AddPrimShape,call", localID);
460 392
461 BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, pbs, isPhysical); 393 BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, pbs, isPhysical);
462 lock (PhysObjects) PhysObjects.Add(localID, prim); 394 lock (m_prims) m_prims.Add(localID, prim);
463 return prim; 395 return prim;
464 } 396 }
465 397
466 // This is a call from the simulator saying that some physical property has been updated. 398 // This is a call from the simulator saying that some physical property has been updated.
467 // The BulletSim driver senses the changing of relevant properties so this taint 399 // The BulletSim driver senses the changing of relevant properties so this taint
468 // information call is not needed. 400 // information call is not needed.
469 public override void AddPhysicsActorTaint(PhysicsActor prim) { } 401 public override void AddPhysicsActorTaint(PhysicsActor prim) { }
470 402
471 #endregion // Prim and Avatar addition and removal
472
473 #region Simulation
474 // Simulate one timestep 403 // Simulate one timestep
475 public override float Simulate(float timeStep) 404 public override float Simulate(float timeStep)
476 { 405 {
@@ -479,45 +408,34 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
479 int collidersCount = 0; 408 int collidersCount = 0;
480 IntPtr collidersPtr; 409 IntPtr collidersPtr;
481 410
482 int beforeTime = 0; 411 LastSimulatedTimestep = timeStep;
483 int simTime = 0;
484 412
485 // prevent simulation until we've been initialized 413 // prevent simulation until we've been initialized
486 if (!m_initialized) return 5.0f; 414 if (!m_initialized) return 10.0f;
415
416 int simulateStartTime = Util.EnvironmentTickCount();
487 417
488 // update the prim states while we know the physics engine is not busy 418 // update the prim states while we know the physics engine is not busy
489 int numTaints = _taintOperations.Count;
490 ProcessTaints(); 419 ProcessTaints();
491 420
492 // Some of the prims operate with special vehicle properties 421 // Some of the prims operate with special vehicle properties
493 ProcessVehicles(timeStep); 422 ProcessVehicles(timeStep);
494 numTaints += _taintOperations.Count;
495 ProcessTaints(); // the vehicles might have added taints 423 ProcessTaints(); // the vehicles might have added taints
496 424
497 // step the physical world one interval 425 // step the physical world one interval
498 m_simulationStep++; 426 m_simulationStep++;
499 int numSubSteps = 0; 427 int numSubSteps = 0;
500
501 try 428 try
502 { 429 {
503 // DumpVehicles(); // DEBUG 430 numSubSteps = BulletSimAPI.PhysicsStep(m_worldID, timeStep, m_maxSubSteps, m_fixedTimeStep,
504 if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount();
505
506 numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep,
507 out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr); 431 out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr);
508 432 // DetailLog("{0},Simulate,call, substeps={1}, updates={2}, colliders={3}", DetailLogZero, numSubSteps, updatedEntityCount, collidersCount);
509 if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime);
510 DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}",
511 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount);
512 // DumpVehicles(); // DEBUG
513 } 433 }
514 catch (Exception e) 434 catch (Exception e)
515 { 435 {
516 m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}", 436 m_log.WarnFormat("{0},PhysicsStep Exception: substeps={1}, updates={2}, colliders={3}, e={4}", LogHeader, numSubSteps, updatedEntityCount, collidersCount, e);
517 LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e); 437 // DetailLog("{0},PhysicsStepException,call, substeps={1}, updates={2}, colliders={3}", DetailLogZero, numSubSteps, updatedEntityCount, collidersCount);
518 DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}", 438 // updatedEntityCount = 0;
519 DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount);
520 updatedEntityCount = 0;
521 collidersCount = 0; 439 collidersCount = 0;
522 } 440 }
523 441
@@ -525,7 +443,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
525 // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in 443 // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in
526 444
527 // Get a value for 'now' so all the collision and update routines don't have to get their own 445 // Get a value for 'now' so all the collision and update routines don't have to get their own
528 SimulationNowTime = Util.EnvironmentTickCount(); 446 m_simulationNowTime = Util.EnvironmentTickCount();
529 447
530 // If there were collisions, process them by sending the event to the prim. 448 // If there were collisions, process them by sending the event to the prim.
531 // Collisions must be processed before updates. 449 // Collisions must be processed before updates.
@@ -544,32 +462,19 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
544 462
545 // The above SendCollision's batch up the collisions on the objects. 463 // The above SendCollision's batch up the collisions on the objects.
546 // Now push the collisions into the simulator. 464 // Now push the collisions into the simulator.
547 if (ObjectsWithCollisions.Count > 0) 465 foreach (BSPrim bsp in m_primsWithCollisions)
548 { 466 bsp.SendCollisions();
549 foreach (BSPhysObject bsp in ObjectsWithCollisions) 467 m_primsWithCollisions.Clear();
550 if (!bsp.SendCollisions()) 468
551 { 469 // This is a kludge to get avatar movement updated.
552 // If the object is done colliding, see that it's removed from the colliding list 470 // Don't send collisions only if there were collisions -- send everytime.
553 ObjectsWithNoMoreCollisions.Add(bsp); 471 // ODE sends collisions even if there are none and this is used to update
554 } 472 // avatar animations and stuff.
555 } 473 // foreach (BSCharacter bsc in m_avatarsWithCollisions)
556 474 // bsc.SendCollisions();
557 // This is a kludge to get avatar movement updates. 475 foreach (KeyValuePair<uint, BSCharacter> kvp in m_avatars)
558 // The simulator expects collisions for avatars even if there are have been no collisions. 476 kvp.Value.SendCollisions();
559 // The event updates avatar animations and stuff. 477 m_avatarsWithCollisions.Clear();
560 // If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
561 foreach (BSPhysObject bsp in m_avatars)
562 if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice
563 bsp.SendCollisions();
564
565 // Objects that are done colliding are removed from the ObjectsWithCollisions list.
566 // Not done above because it is inside an iteration of ObjectWithCollisions.
567 if (ObjectsWithNoMoreCollisions.Count > 0)
568 {
569 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
570 ObjectsWithCollisions.Remove(po);
571 ObjectsWithNoMoreCollisions.Clear();
572 }
573 478
574 // If any of the objects had updated properties, tell the object it has been changed by the physics engine 479 // If any of the objects had updated properties, tell the object it has been changed by the physics engine
575 if (updatedEntityCount > 0) 480 if (updatedEntityCount > 0)
@@ -577,310 +482,320 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
577 for (int ii = 0; ii < updatedEntityCount; ii++) 482 for (int ii = 0; ii < updatedEntityCount; ii++)
578 { 483 {
579 EntityProperties entprop = m_updateArray[ii]; 484 EntityProperties entprop = m_updateArray[ii];
580 BSPhysObject pobj; 485 BSPrim prim;
581 if (PhysObjects.TryGetValue(entprop.ID, out pobj)) 486 if (m_prims.TryGetValue(entprop.ID, out prim))
487 {
488 prim.UpdateProperties(entprop);
489 continue;
490 }
491 BSCharacter actor;
492 if (m_avatars.TryGetValue(entprop.ID, out actor))
582 { 493 {
583 pobj.UpdateProperties(entprop); 494 actor.UpdateProperties(entprop);
495 continue;
584 } 496 }
585 } 497 }
586 } 498 }
587 499
588 ProcessPostStepTaints(); 500 // If enabled, call into the physics engine to dump statistics
589 501 if (m_detailedStatsStep > 0)
590 // This causes the unmanaged code to output ALL the values found in ALL the objects in the world. 502 {
591 // Only enable this in a limited test world with few objects. 503 if ((m_simulationStep % m_detailedStatsStep) == 0)
592 // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG 504 {
505 BulletSimAPI.DumpBulletStatistics();
506 }
507 }
593 508
594 // The physics engine returns the number of milliseconds it simulated this call. 509 // this is a waste since the outside routine also calcuates the physics simulation
595 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS. 510 // period. TODO: There should be a way of computing physics frames from simulator computation.
596 // We multiply by 55 to give a recognizable running rate (55 or less). 511 // long simulateTotalTime = Util.EnvironmentTickCountSubtract(simulateStartTime);
597 return numSubSteps * m_fixedTimeStep * 1000 * 55; 512 // return (timeStep * (float)simulateTotalTime);
598 // return timeStep * 1000 * 55; 513
514 // TODO: FIX THIS: fps calculation possibly wrong.
515 // This calculation says 1/timeStep is the ideal frame rate. Any time added to
516 // that by the physics simulation gives a slower frame rate.
517 long totalSimulationTime = Util.EnvironmentTickCountSubtract(simulateStartTime);
518 if (totalSimulationTime >= timeStep)
519 return 0;
520 return 1f / (timeStep + totalSimulationTime);
599 } 521 }
600 522
601 // Something has collided 523 // Something has collided
602 private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration) 524 private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penitration)
603 { 525 {
604 if (localID <= TerrainManager.HighestTerrainID) 526 if (localID == TERRAIN_ID || localID == GROUNDPLANE_ID)
605 { 527 {
606 return; // don't send collisions to the terrain 528 return; // don't send collisions to the terrain
607 } 529 }
608 530
609 BSPhysObject collider; 531 ActorTypes type = ActorTypes.Prim;
610 if (!PhysObjects.TryGetValue(localID, out collider)) 532 if (collidingWith == TERRAIN_ID || collidingWith == GROUNDPLANE_ID)
611 { 533 type = ActorTypes.Ground;
612 // If the object that is colliding cannot be found, just ignore the collision. 534 else if (m_avatars.ContainsKey(collidingWith))
613 DetailLog("{0},BSScene.SendCollision,colliderNotInObjectList,id={1},with={2}", DetailLogZero, localID, collidingWith); 535 type = ActorTypes.Agent;
536
537 BSPrim prim;
538 if (m_prims.TryGetValue(localID, out prim)) {
539 prim.Collide(collidingWith, type, collidePoint, collideNormal, penitration);
540 m_primsWithCollisions.Add(prim);
614 return; 541 return;
615 } 542 }
616 543 BSCharacter actor;
617 // The terrain is not in the physical object list so 'collidee' can be null when Collide() is called. 544 if (m_avatars.TryGetValue(localID, out actor)) {
618 BSPhysObject collidee = null; 545 actor.Collide(collidingWith, type, collidePoint, collideNormal, penitration);
619 PhysObjects.TryGetValue(collidingWith, out collidee); 546 m_avatarsWithCollisions.Add(actor);
620 547 return;
621 // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
622
623 if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
624 {
625 // If a collision was posted, remember to send it to the simulator
626 ObjectsWithCollisions.Add(collider);
627 } 548 }
628
629 return; 549 return;
630 } 550 }
631 551
632 #endregion // Simulation
633
634 public override void GetResults() { } 552 public override void GetResults() { }
635 553
636 #region Terrain
637
638 public override void SetTerrain(float[] heightMap) { 554 public override void SetTerrain(float[] heightMap) {
639 TerrainManager.SetTerrain(heightMap); 555 m_heightMap = heightMap;
556 this.TaintedObject("BSScene.SetTerrain", delegate()
557 {
558 BulletSimAPI.SetHeightmap(m_worldID, m_heightMap);
559 });
640 } 560 }
641 561
642 public override void SetWaterLevel(float baseheight) 562 // Someday we will have complex terrain with caves and tunnels
563 // For the moment, it's flat and convex
564 public float GetTerrainHeightAtXYZ(Vector3 loc)
643 { 565 {
644 m_waterLevel = baseheight; 566 return GetTerrainHeightAtXY(loc.X, loc.Y);
645 }
646 // Someday....
647 public float GetWaterLevelAtXYZ(Vector3 loc)
648 {
649 return m_waterLevel;
650 } 567 }
651 568
652 public override void DeleteTerrain() 569 public float GetTerrainHeightAtXY(float tX, float tY)
653 { 570 {
654 // m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader); 571 if (tX < 0 || tX >= Constants.RegionSize || tY < 0 || tY >= Constants.RegionSize)
572 return 30;
573 return m_heightMap[((int)tX) * Constants.RegionSize + ((int)tY)];
655 } 574 }
656 575
657 // Although no one seems to check this, I do support combining. 576 public override void SetWaterLevel(float baseheight)
658 public override bool SupportsCombining()
659 { 577 {
660 return TerrainManager.SupportsCombining(); 578 m_waterLevel = baseheight;
579 // TODO: pass to physics engine so things will float?
661 } 580 }
662 // This call says I am a child to region zero in a mega-region. 'pScene' is that 581 public float GetWaterLevel()
663 // of region zero, 'offset' is my offset from regions zero's origin, and
664 // 'extents' is the largest XY that is handled in my region.
665 public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
666 { 582 {
667 TerrainManager.Combine(pScene, offset, extents); 583 return m_waterLevel;
668 } 584 }
669 585
670 // Unhook all the combining that I know about. 586 public override void DeleteTerrain()
671 public override void UnCombine(PhysicsScene pScene)
672 { 587 {
673 TerrainManager.UnCombine(pScene); 588 // m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader);
674 } 589 }
675 590
676 #endregion // Terrain 591 public override void Dispose()
677
678 public override Dictionary<uint, float> GetTopColliders()
679 { 592 {
680 return new Dictionary<uint, float>(); 593 // m_log.DebugFormat("{0}: Dispose()", LogHeader);
681 }
682 594
683 public override bool IsThreaded { get { return false; } } 595 // make sure no stepping happens while we're deleting stuff
596 m_initialized = false;
684 597
685 #region Taints 598 foreach (KeyValuePair<uint, BSCharacter> kvp in m_avatars)
599 {
600 kvp.Value.Destroy();
601 }
602 m_avatars.Clear();
686 603
687 // Calls to the PhysicsActors can't directly call into the physics engine 604 foreach (KeyValuePair<uint, BSPrim> kvp in m_prims)
688 // because it might be busy. We delay changes to a known time. 605 {
689 // We rely on C#'s closure to save and restore the context for the delegate. 606 kvp.Value.Destroy();
690 public void TaintedObject(String ident, TaintCallback callback) 607 }
691 { 608 m_prims.Clear();
692 if (!m_initialized) return;
693 609
694 lock (_taintLock) 610 // Now that the prims are all cleaned up, there should be no constraints left
611 if (m_constraintCollection != null)
695 { 612 {
696 _taintOperations.Add(new TaintCallbackEntry(ident, callback)); 613 m_constraintCollection.Dispose();
614 m_constraintCollection = null;
697 } 615 }
698 616
699 return; 617 // Anything left in the unmanaged code should be cleaned out
700 } 618 BulletSimAPI.Shutdown(WorldID);
701 619
702 // Sometimes a potentially tainted operation can be used in and out of taint time. 620 // Not logging any more
703 // This routine executes the command immediately if in taint-time otherwise it is queued. 621 PhysicsLogging.Close();
704 public void TaintedObject(bool inTaintTime, string ident, TaintCallback callback)
705 {
706 if (inTaintTime)
707 callback();
708 else
709 TaintedObject(ident, callback);
710 } 622 }
711 623
712 // When someone tries to change a property on a BSPrim or BSCharacter, the object queues 624 public override Dictionary<uint, float> GetTopColliders()
713 // a callback into itself to do the actual property change. That callback is called
714 // here just before the physics engine is called to step the simulation.
715 public void ProcessTaints()
716 { 625 {
717 InTaintTime = true; 626 return new Dictionary<uint, float>();
718 ProcessRegularTaints();
719 ProcessPostTaintTaints();
720 InTaintTime = false;
721 } 627 }
722 628
723 private void ProcessRegularTaints() 629 public override bool IsThreaded { get { return false; } }
630
631 /// <summary>
632 /// Routine to figure out if we need to mesh this prim with our mesher
633 /// </summary>
634 /// <param name="pbs"></param>
635 /// <returns>true if the prim needs meshing</returns>
636 public bool NeedsMeshing(PrimitiveBaseShape pbs)
724 { 637 {
725 if (_taintOperations.Count > 0) // save allocating new list if there is nothing to process 638 // most of this is redundant now as the mesher will return null if it cant mesh a prim
639 // but we still need to check for sculptie meshing being enabled so this is the most
640 // convenient place to do it for now...
641
642 // int iPropertiesNotSupportedDefault = 0;
643
644 if (pbs.SculptEntry && !_meshSculptedPrim)
726 { 645 {
727 int taintCount = m_taintsToProcessPerStep; 646 // Render sculpties as boxes
728 TaintCallbackEntry oneCallback = new TaintCallbackEntry(); 647 return false;
729 while (_taintOperations.Count > 0 && taintCount-- > 0) 648 }
649
650 // if it's a standard box or sphere with no cuts, hollows, twist or top shear, return false since Bullet
651 // can use an internal representation for the prim
652 if (!_forceSimplePrimMeshing)
653 {
654 if ((pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
655 || (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1
656 && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z))
730 { 657 {
731 bool gotOne = false; 658
732 lock (_taintLock) 659 if (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
733 { 660 && pbs.ProfileHollow == 0
734 if (_taintOperations.Count > 0) 661 && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0
735 { 662 && pbs.PathBegin == 0 && pbs.PathEnd == 0
736 oneCallback = _taintOperations[0]; 663 && pbs.PathTaperX == 0 && pbs.PathTaperY == 0
737 _taintOperations.RemoveAt(0); 664 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
738 gotOne = true; 665 && pbs.PathShearX == 0 && pbs.PathShearY == 0)
739 }
740 }
741 if (gotOne)
742 { 666 {
743 try 667 return false;
744 {
745 DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, oneCallback.ident);
746 oneCallback.callback();
747 }
748 catch (Exception e)
749 {
750 DetailLog("{0},BSScene.ProcessTaints,doTaintException,id={1}", DetailLogZero, oneCallback.ident); // DEBUG DEBUG DEBUG
751 m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, oneCallback.ident, e);
752 }
753 } 668 }
754 } 669 }
755 /* 670 }
756 // swizzle a new list into the list location so we can process what's there 671
757 List<TaintCallbackEntry> oldList; 672 /* TODO: verify that the mesher will now do all these shapes
758 lock (_taintLock) 673 if (pbs.ProfileHollow != 0)
674 iPropertiesNotSupportedDefault++;
675
676 if ((pbs.PathBegin != 0) || pbs.PathEnd != 0)
677 iPropertiesNotSupportedDefault++;
678
679 if ((pbs.PathTwistBegin != 0) || (pbs.PathTwist != 0))
680 iPropertiesNotSupportedDefault++;
681
682 if ((pbs.ProfileBegin != 0) || pbs.ProfileEnd != 0)
683 iPropertiesNotSupportedDefault++;
684
685 if ((pbs.PathScaleX != 100) || (pbs.PathScaleY != 100))
686 iPropertiesNotSupportedDefault++;
687
688 if ((pbs.PathShearX != 0) || (pbs.PathShearY != 0))
689 iPropertiesNotSupportedDefault++;
690
691 if (pbs.ProfileShape == ProfileShape.Circle && pbs.PathCurve == (byte)Extrusion.Straight)
692 iPropertiesNotSupportedDefault++;
693
694 if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1 && (pbs.Scale.X != pbs.Scale.Y || pbs.Scale.Y != pbs.Scale.Z || pbs.Scale.Z != pbs.Scale.X))
695 iPropertiesNotSupportedDefault++;
696
697 if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte) Extrusion.Curve1)
698 iPropertiesNotSupportedDefault++;
699
700 // test for torus
701 if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Square)
702 {
703 if (pbs.PathCurve == (byte)Extrusion.Curve1)
759 { 704 {
760 oldList = _taintedObjects; 705 iPropertiesNotSupportedDefault++;
761 _taintedObjects = new List<TaintCallbackEntry>();
762 } 706 }
763 707 }
764 foreach (TaintCallbackEntry tcbe in oldList) 708 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Circle)
709 {
710 if (pbs.PathCurve == (byte)Extrusion.Straight)
765 { 711 {
766 try 712 iPropertiesNotSupportedDefault++;
767 { 713 }
768 DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG 714 // ProfileCurve seems to combine hole shape and profile curve so we need to only compare against the lower 3 bits
769 tcbe.callback(); 715 else if (pbs.PathCurve == (byte)Extrusion.Curve1)
770 } 716 {
771 catch (Exception e) 717 iPropertiesNotSupportedDefault++;
772 {
773 m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e);
774 }
775 } 718 }
776 oldList.Clear();
777 */
778 } 719 }
779 } 720 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle)
780
781 // Schedule an update to happen after all the regular taints are processed.
782 // Note that new requests for the same operation ("ident") for the same object ("ID")
783 // will replace any previous operation by the same object.
784 public void PostTaintObject(String ident, uint ID, TaintCallback callback)
785 {
786 if (!m_initialized) return;
787
788 string uniqueIdent = ident + "-" + ID.ToString();
789 lock (_taintLock)
790 { 721 {
791 _postTaintOperations[uniqueIdent] = new TaintCallbackEntry(uniqueIdent, callback); 722 if (pbs.PathCurve == (byte)Extrusion.Curve1 || pbs.PathCurve == (byte)Extrusion.Curve2)
723 {
724 iPropertiesNotSupportedDefault++;
725 }
792 } 726 }
793 727 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle)
794 return;
795 }
796
797 private void ProcessPostTaintTaints()
798 {
799 if (_postTaintOperations.Count > 0)
800 { 728 {
801 Dictionary<string, TaintCallbackEntry> oldList; 729 if (pbs.PathCurve == (byte)Extrusion.Straight)
802 lock (_taintLock)
803 { 730 {
804 oldList = _postTaintOperations; 731 iPropertiesNotSupportedDefault++;
805 _postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
806 } 732 }
807 733 else if (pbs.PathCurve == (byte)Extrusion.Curve1)
808 foreach (KeyValuePair<string,TaintCallbackEntry> kvp in oldList)
809 { 734 {
810 try 735 iPropertiesNotSupportedDefault++;
811 {
812 DetailLog("{0},BSScene.ProcessPostTaintTaints,doTaint,id={1}", DetailLogZero, kvp.Key); // DEBUG DEBUG DEBUG
813 kvp.Value.callback();
814 }
815 catch (Exception e)
816 {
817 m_log.ErrorFormat("{0}: ProcessPostTaintTaints: {1}: Exception: {2}", LogHeader, kvp.Key, e);
818 }
819 } 736 }
820 oldList.Clear();
821 } 737 }
738 if (iPropertiesNotSupportedDefault == 0)
739 {
740 return false;
741 }
742 */
743 return true;
822 } 744 }
823 745
824 public void PostStepTaintObject(String ident, TaintCallback callback) 746 // Calls to the PhysicsActors can't directly call into the physics engine
747 // because it might be busy. We delay changes to a known time.
748 // We rely on C#'s closure to save and restore the context for the delegate.
749 public void TaintedObject(String ident, TaintCallback callback)
825 { 750 {
826 if (!m_initialized) return; 751 if (!m_initialized) return;
827 752
828 lock (_taintLock) 753 lock (_taintLock)
829 { 754 _taintedObjects.Add(new TaintCallbackEntry(ident, callback));
830 _postStepOperations.Add(new TaintCallbackEntry(ident, callback));
831 }
832
833 return; 755 return;
834 } 756 }
835 757
836 private void ProcessPostStepTaints() 758 // When someone tries to change a property on a BSPrim or BSCharacter, the object queues
759 // a callback into itself to do the actual property change. That callback is called
760 // here just before the physics engine is called to step the simulation.
761 public void ProcessTaints()
837 { 762 {
838 if (_postStepOperations.Count > 0) 763 if (_taintedObjects.Count > 0) // save allocating new list if there is nothing to process
839 { 764 {
765 // swizzle a new list into the list location so we can process what's there
840 List<TaintCallbackEntry> oldList; 766 List<TaintCallbackEntry> oldList;
841 lock (_taintLock) 767 lock (_taintLock)
842 { 768 {
843 oldList = _postStepOperations; 769 oldList = _taintedObjects;
844 _postStepOperations = new List<TaintCallbackEntry>(); 770 _taintedObjects = new List<TaintCallbackEntry>();
845 } 771 }
846 772
847 foreach (TaintCallbackEntry tcbe in oldList) 773 foreach (TaintCallbackEntry tcbe in oldList)
848 { 774 {
849 try 775 try
850 { 776 {
851 DetailLog("{0},BSScene.ProcessPostStepTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG
852 tcbe.callback(); 777 tcbe.callback();
853 } 778 }
854 catch (Exception e) 779 catch (Exception e)
855 { 780 {
856 m_log.ErrorFormat("{0}: ProcessPostStepTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e); 781 m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e);
857 } 782 }
858 } 783 }
859 oldList.Clear(); 784 oldList.Clear();
860 } 785 }
861 } 786 }
862 787
863 public bool AssertInTaintTime(string whereFrom)
864 {
865 if (!InTaintTime)
866 {
867 DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom);
868 m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom);
869 Util.PrintCallStack();
870 }
871 return InTaintTime;
872 }
873
874 #endregion // Taints
875
876 #region Vehicles 788 #region Vehicles
877 789
878 public void VehicleInSceneTypeChanged(BSPrim vehic, Vehicle newType) 790 public void VehicleInSceneTypeChanged(BSPrim vehic, Vehicle newType)
879 { 791 {
880 RemoveVehiclePrim(vehic); 792 if (newType == Vehicle.TYPE_NONE)
881 if (newType != Vehicle.TYPE_NONE)
882 { 793 {
883 // make it so the scene will call us each tick to do vehicle things 794 RemoveVehiclePrim(vehic);
795 }
796 else
797 {
798 // make it so the scene will call us each tick to do vehicle things
884 AddVehiclePrim(vehic); 799 AddVehiclePrim(vehic);
885 } 800 }
886 } 801 }
@@ -912,22 +827,21 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
912 } 827 }
913 828
914 // Some prims have extra vehicle actions 829 // Some prims have extra vehicle actions
915 // Called at taint time! 830 // no locking because only called when physics engine is not busy
916 private void ProcessVehicles(float timeStep) 831 private void ProcessVehicles(float timeStep)
917 { 832 {
918 foreach (BSPhysObject pobj in m_vehicles) 833 foreach (BSPrim prim in m_vehicles)
919 { 834 {
920 pobj.StepVehicle(timeStep); 835 prim.StepVehicle(timeStep);
921 } 836 }
922 } 837 }
923 #endregion Vehicles 838 #endregion Vehicles
924 839
925 #region INI and command line parameter processing 840 #region Parameters
926 841
927 delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val); 842 delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val);
928 delegate float ParamGet(BSScene scene); 843 delegate float ParamGet(BSScene scene);
929 delegate void ParamSet(BSScene scene, string paramName, uint localID, float val); 844 delegate void ParamSet(BSScene scene, string paramName, uint localID, float val);
930 delegate void SetOnObject(BSScene scene, BSPhysObject obj, float val);
931 845
932 private struct ParameterDefn 846 private struct ParameterDefn
933 { 847 {
@@ -937,7 +851,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
937 public ParamUser userParam; // get the value from the configuration file 851 public ParamUser userParam; // get the value from the configuration file
938 public ParamGet getter; // return the current value stored for this parameter 852 public ParamGet getter; // return the current value stored for this parameter
939 public ParamSet setter; // set the current value for this parameter 853 public ParamSet setter; // set the current value for this parameter
940 public SetOnObject onObject; // set the value on an object in the physical domain
941 public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s) 854 public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s)
942 { 855 {
943 name = n; 856 name = n;
@@ -946,17 +859,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
946 userParam = u; 859 userParam = u;
947 getter = g; 860 getter = g;
948 setter = s; 861 setter = s;
949 onObject = null;
950 }
951 public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s, SetOnObject o)
952 {
953 name = n;
954 desc = d;
955 defaultValue = v;
956 userParam = u;
957 getter = g;
958 setter = s;
959 onObject = o;
960 } 862 }
961 } 863 }
962 864
@@ -967,7 +869,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
967 // getters and setters. 869 // getters and setters.
968 // It is easiest to find an existing definition and copy it. 870 // It is easiest to find an existing definition and copy it.
969 // Parameter values are floats. Booleans are converted to a floating value. 871 // Parameter values are floats. Booleans are converted to a floating value.
970 // 872 //
971 // A ParameterDefn() takes the following parameters: 873 // A ParameterDefn() takes the following parameters:
972 // -- the text name of the parameter. This is used for console input and ini file. 874 // -- the text name of the parameter. This is used for console input and ini file.
973 // -- a short text description of the parameter. This shows up in the console listing. 875 // -- a short text description of the parameter. This shows up in the console listing.
@@ -978,7 +880,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
978 // 880 //
979 // The single letter parameters for the delegates are: 881 // The single letter parameters for the delegates are:
980 // s = BSScene 882 // s = BSScene
981 // o = BSPhysObject
982 // p = string parameter name 883 // p = string parameter name
983 // l = localID of referenced object 884 // l = localID of referenced object
984 // v = float value 885 // v = float value
@@ -987,40 +888,25 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
987 { 888 {
988 new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties", 889 new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties",
989 ConfigurationParameters.numericTrue, 890 ConfigurationParameters.numericTrue,
990 (s,cf,p,v) => { s.ShouldMeshSculptedPrim = cf.GetBoolean(p, s.BoolNumeric(v)); }, 891 (s,cf,p,v) => { s._meshSculptedPrim = cf.GetBoolean(p, s.BoolNumeric(v)); },
991 (s) => { return s.NumericBool(s.ShouldMeshSculptedPrim); }, 892 (s) => { return s.NumericBool(s._meshSculptedPrim); },
992 (s,p,l,v) => { s.ShouldMeshSculptedPrim = s.BoolNumeric(v); } ), 893 (s,p,l,v) => { s._meshSculptedPrim = s.BoolNumeric(v); } ),
993 new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects", 894 new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects",
994 ConfigurationParameters.numericFalse, 895 ConfigurationParameters.numericFalse,
995 (s,cf,p,v) => { s.ShouldForceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); }, 896 (s,cf,p,v) => { s._forceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); },
996 (s) => { return s.NumericBool(s.ShouldForceSimplePrimMeshing); }, 897 (s) => { return s.NumericBool(s._forceSimplePrimMeshing); },
997 (s,p,l,v) => { s.ShouldForceSimplePrimMeshing = s.BoolNumeric(v); } ), 898 (s,p,l,v) => { s._forceSimplePrimMeshing = s.BoolNumeric(v); } ),
998 new ParameterDefn("UseHullsForPhysicalObjects", "If true, create hulls for physical objects",
999 ConfigurationParameters.numericTrue,
1000 (s,cf,p,v) => { s.ShouldUseHullsForPhysicalObjects = cf.GetBoolean(p, s.BoolNumeric(v)); },
1001 (s) => { return s.NumericBool(s.ShouldUseHullsForPhysicalObjects); },
1002 (s,p,l,v) => { s.ShouldUseHullsForPhysicalObjects = s.BoolNumeric(v); } ),
1003 899
1004 new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)", 900 new ParameterDefn("MeshLOD", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)",
1005 8f, 901 8f,
1006 (s,cf,p,v) => { s.MeshLOD = (float)cf.GetInt(p, (int)v); }, 902 (s,cf,p,v) => { s.m_meshLOD = cf.GetInt(p, (int)v); },
1007 (s) => { return s.MeshLOD; }, 903 (s) => { return (float)s.m_meshLOD; },
1008 (s,p,l,v) => { s.MeshLOD = v; } ), 904 (s,p,l,v) => { s.m_meshLOD = (int)v; } ),
1009 new ParameterDefn("MeshLevelOfDetailMegaPrim", "Level of detail to render meshes larger than threshold meters", 905 new ParameterDefn("SculptLOD", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)",
1010 16f,
1011 (s,cf,p,v) => { s.MeshMegaPrimLOD = (float)cf.GetInt(p, (int)v); },
1012 (s) => { return s.MeshMegaPrimLOD; },
1013 (s,p,l,v) => { s.MeshMegaPrimLOD = v; } ),
1014 new ParameterDefn("MeshLevelOfDetailMegaPrimThreshold", "Size (in meters) of a mesh before using MeshMegaPrimLOD",
1015 10f,
1016 (s,cf,p,v) => { s.MeshMegaPrimThreshold = (float)cf.GetInt(p, (int)v); },
1017 (s) => { return s.MeshMegaPrimThreshold; },
1018 (s,p,l,v) => { s.MeshMegaPrimThreshold = v; } ),
1019 new ParameterDefn("SculptLevelOfDetail", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)",
1020 32f, 906 32f,
1021 (s,cf,p,v) => { s.SculptLOD = (float)cf.GetInt(p, (int)v); }, 907 (s,cf,p,v) => { s.m_sculptLOD = cf.GetInt(p, (int)v); },
1022 (s) => { return s.SculptLOD; }, 908 (s) => { return (float)s.m_sculptLOD; },
1023 (s,p,l,v) => { s.SculptLOD = v; } ), 909 (s,p,l,v) => { s.m_sculptLOD = (int)v; } ),
1024 910
1025 new ParameterDefn("MaxSubStep", "In simulation step, maximum number of substeps", 911 new ParameterDefn("MaxSubStep", "In simulation step, maximum number of substeps",
1026 10f, 912 10f,
@@ -1042,16 +928,11 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1042 (s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); }, 928 (s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); },
1043 (s) => { return (float)s.m_maxUpdatesPerFrame; }, 929 (s) => { return (float)s.m_maxUpdatesPerFrame; },
1044 (s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ), 930 (s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ),
1045 new ParameterDefn("MaxTaintsToProcessPerStep", "Number of update taints to process before each simulation step",
1046 100f,
1047 (s,cf,p,v) => { s.m_taintsToProcessPerStep = cf.GetInt(p, (int)v); },
1048 (s) => { return (float)s.m_taintsToProcessPerStep; },
1049 (s,p,l,v) => { s.m_taintsToProcessPerStep = (int)v; } ),
1050 new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)", 931 new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)",
1051 10000.01f, 932 10000.01f,
1052 (s,cf,p,v) => { s.MaximumObjectMass = cf.GetFloat(p, v); }, 933 (s,cf,p,v) => { s.m_maximumObjectMass = cf.GetFloat(p, v); },
1053 (s) => { return (float)s.MaximumObjectMass; }, 934 (s) => { return (float)s.m_maximumObjectMass; },
1054 (s,p,l,v) => { s.MaximumObjectMass = v; } ), 935 (s,p,l,v) => { s.m_maximumObjectMass = v; } ),
1055 936
1056 new ParameterDefn("PID_D", "Derivitive factor for motion smoothing", 937 new ParameterDefn("PID_D", "Derivitive factor for motion smoothing",
1057 2200f, 938 2200f,
@@ -1088,118 +969,104 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1088 -9.80665f, 969 -9.80665f,
1089 (s,cf,p,v) => { s.m_params[0].gravity = cf.GetFloat(p, v); }, 970 (s,cf,p,v) => { s.m_params[0].gravity = cf.GetFloat(p, v); },
1090 (s) => { return s.m_params[0].gravity; }, 971 (s) => { return s.m_params[0].gravity; },
1091 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].gravity, p, PhysParameterEntry.APPLY_TO_NONE, v); }, 972 (s,p,l,v) => { s.m_params[0].gravity = v; s.TaintedUpdateParameter(p,l,v); } ),
1092 (s,o,v) => { BulletSimAPI.SetGravity2(s.World.ptr, new Vector3(0f,0f,v)); } ),
1093 973
1094 974
1095 new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)", 975 new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)",
1096 0f, 976 0f,
1097 (s,cf,p,v) => { s.m_params[0].linearDamping = cf.GetFloat(p, v); }, 977 (s,cf,p,v) => { s.m_params[0].linearDamping = cf.GetFloat(p, v); },
1098 (s) => { return s.m_params[0].linearDamping; }, 978 (s) => { return s.m_params[0].linearDamping; },
1099 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearDamping, p, l, v); }, 979 (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].linearDamping, p, l, v); } ),
1100 (s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, v, v); } ),
1101 new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)", 980 new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)",
1102 0f, 981 0f,
1103 (s,cf,p,v) => { s.m_params[0].angularDamping = cf.GetFloat(p, v); }, 982 (s,cf,p,v) => { s.m_params[0].angularDamping = cf.GetFloat(p, v); },
1104 (s) => { return s.m_params[0].angularDamping; }, 983 (s) => { return s.m_params[0].angularDamping; },
1105 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularDamping, p, l, v); }, 984 (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].angularDamping, p, l, v); } ),
1106 (s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, v, v); } ),
1107 new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static", 985 new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static",
1108 0.2f, 986 0.2f,
1109 (s,cf,p,v) => { s.m_params[0].deactivationTime = cf.GetFloat(p, v); }, 987 (s,cf,p,v) => { s.m_params[0].deactivationTime = cf.GetFloat(p, v); },
1110 (s) => { return s.m_params[0].deactivationTime; }, 988 (s) => { return s.m_params[0].deactivationTime; },
1111 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].deactivationTime, p, l, v); }, 989 (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].deactivationTime, p, l, v); } ),
1112 (s,o,v) => { BulletSimAPI.SetDeactivationTime2(o.PhysBody.ptr, v); } ),
1113 new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static", 990 new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static",
1114 0.8f, 991 0.8f,
1115 (s,cf,p,v) => { s.m_params[0].linearSleepingThreshold = cf.GetFloat(p, v); }, 992 (s,cf,p,v) => { s.m_params[0].linearSleepingThreshold = cf.GetFloat(p, v); },
1116 (s) => { return s.m_params[0].linearSleepingThreshold; }, 993 (s) => { return s.m_params[0].linearSleepingThreshold; },
1117 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearSleepingThreshold, p, l, v); }, 994 (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].linearSleepingThreshold, p, l, v); } ),
1118 (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ),
1119 new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static", 995 new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static",
1120 1.0f, 996 1.0f,
1121 (s,cf,p,v) => { s.m_params[0].angularSleepingThreshold = cf.GetFloat(p, v); }, 997 (s,cf,p,v) => { s.m_params[0].angularSleepingThreshold = cf.GetFloat(p, v); },
1122 (s) => { return s.m_params[0].angularSleepingThreshold; }, 998 (s) => { return s.m_params[0].angularSleepingThreshold; },
1123 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularSleepingThreshold, p, l, v); }, 999 (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].angularSleepingThreshold, p, l, v); } ),
1124 (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ),
1125 new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" , 1000 new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" ,
1126 0f, // set to zero to disable 1001 0f, // set to zero to disable
1127 (s,cf,p,v) => { s.m_params[0].ccdMotionThreshold = cf.GetFloat(p, v); }, 1002 (s,cf,p,v) => { s.m_params[0].ccdMotionThreshold = cf.GetFloat(p, v); },
1128 (s) => { return s.m_params[0].ccdMotionThreshold; }, 1003 (s) => { return s.m_params[0].ccdMotionThreshold; },
1129 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdMotionThreshold, p, l, v); }, 1004 (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].ccdMotionThreshold, p, l, v); } ),
1130 (s,o,v) => { BulletSimAPI.SetCcdMotionThreshold2(o.PhysBody.ptr, v); } ),
1131 new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" , 1005 new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" ,
1132 0f, 1006 0f,
1133 (s,cf,p,v) => { s.m_params[0].ccdSweptSphereRadius = cf.GetFloat(p, v); }, 1007 (s,cf,p,v) => { s.m_params[0].ccdSweptSphereRadius = cf.GetFloat(p, v); },
1134 (s) => { return s.m_params[0].ccdSweptSphereRadius; }, 1008 (s) => { return s.m_params[0].ccdSweptSphereRadius; },
1135 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); }, 1009 (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); } ),
1136 (s,o,v) => { BulletSimAPI.SetCcdSweptSphereRadius2(o.PhysBody.ptr, v); } ),
1137 new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" , 1010 new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" ,
1138 0.1f, 1011 0.1f,
1139 (s,cf,p,v) => { s.m_params[0].contactProcessingThreshold = cf.GetFloat(p, v); }, 1012 (s,cf,p,v) => { s.m_params[0].contactProcessingThreshold = cf.GetFloat(p, v); },
1140 (s) => { return s.m_params[0].contactProcessingThreshold; }, 1013 (s) => { return s.m_params[0].contactProcessingThreshold; },
1141 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); }, 1014 (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].contactProcessingThreshold, p, l, v); } ),
1142 (s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.PhysBody.ptr, v); } ),
1143 1015
1144 new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , 1016 new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" ,
1145 0.5f, 1017 0.5f,
1146 (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); }, 1018 (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); },
1147 (s) => { return s.m_params[0].terrainFriction; }, 1019 (s) => { return s.m_params[0].terrainFriction; },
1148 (s,p,l,v) => { s.m_params[0].terrainFriction = v; /* TODO: set on real terrain */} ), 1020 (s,p,l,v) => { s.m_params[0].terrainFriction = v; s.TaintedUpdateParameter(p,l,v); } ),
1149 new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" , 1021 new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" ,
1150 0.8f, 1022 0.8f,
1151 (s,cf,p,v) => { s.m_params[0].terrainHitFraction = cf.GetFloat(p, v); }, 1023 (s,cf,p,v) => { s.m_params[0].terrainHitFraction = cf.GetFloat(p, v); },
1152 (s) => { return s.m_params[0].terrainHitFraction; }, 1024 (s) => { return s.m_params[0].terrainHitFraction; },
1153 (s,p,l,v) => { s.m_params[0].terrainHitFraction = v; /* TODO: set on real terrain */ } ), 1025 (s,p,l,v) => { s.m_params[0].terrainHitFraction = v; s.TaintedUpdateParameter(p,l,v); } ),
1154 new ParameterDefn("TerrainRestitution", "Bouncyness" , 1026 new ParameterDefn("TerrainRestitution", "Bouncyness" ,
1155 0f, 1027 0f,
1156 (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); }, 1028 (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); },
1157 (s) => { return s.m_params[0].terrainRestitution; }, 1029 (s) => { return s.m_params[0].terrainRestitution; },
1158 (s,p,l,v) => { s.m_params[0].terrainRestitution = v; /* TODO: set on real terrain */ } ), 1030 (s,p,l,v) => { s.m_params[0].terrainRestitution = v; s.TaintedUpdateParameter(p,l,v); } ),
1159 new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.", 1031 new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.",
1160 0.2f, 1032 0.5f,
1161 (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); }, 1033 (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); },
1162 (s) => { return s.m_params[0].avatarFriction; }, 1034 (s) => { return s.m_params[0].avatarFriction; },
1163 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ), 1035 (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarFriction, p, l, v); } ),
1164 new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.",
1165 10f,
1166 (s,cf,p,v) => { s.m_params[0].avatarStandingFriction = cf.GetFloat(p, v); },
1167 (s) => { return s.m_params[0].avatarStandingFriction; },
1168 (s,p,l,v) => { s.m_params[0].avatarStandingFriction = v; } ),
1169 new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.", 1036 new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.",
1170 60f, 1037 60f,
1171 (s,cf,p,v) => { s.m_params[0].avatarDensity = cf.GetFloat(p, v); }, 1038 (s,cf,p,v) => { s.m_params[0].avatarDensity = cf.GetFloat(p, v); },
1172 (s) => { return s.m_params[0].avatarDensity; }, 1039 (s) => { return s.m_params[0].avatarDensity; },
1173 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarDensity, p, l, v); } ), 1040 (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarDensity, p, l, v); } ),
1174 new ParameterDefn("AvatarRestitution", "Bouncyness. Changed on avatar recreation.", 1041 new ParameterDefn("AvatarRestitution", "Bouncyness. Changed on avatar recreation.",
1175 0f, 1042 0f,
1176 (s,cf,p,v) => { s.m_params[0].avatarRestitution = cf.GetFloat(p, v); }, 1043 (s,cf,p,v) => { s.m_params[0].avatarRestitution = cf.GetFloat(p, v); },
1177 (s) => { return s.m_params[0].avatarRestitution; }, 1044 (s) => { return s.m_params[0].avatarRestitution; },
1178 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarRestitution, p, l, v); } ), 1045 (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarRestitution, p, l, v); } ),
1179 new ParameterDefn("AvatarCapsuleRadius", "Radius of space around an avatar", 1046 new ParameterDefn("AvatarCapsuleRadius", "Radius of space around an avatar",
1180 0.37f, 1047 0.37f,
1181 (s,cf,p,v) => { s.m_params[0].avatarCapsuleRadius = cf.GetFloat(p, v); }, 1048 (s,cf,p,v) => { s.m_params[0].avatarCapsuleRadius = cf.GetFloat(p, v); },
1182 (s) => { return s.m_params[0].avatarCapsuleRadius; }, 1049 (s) => { return s.m_params[0].avatarCapsuleRadius; },
1183 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleRadius, p, l, v); } ), 1050 (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarCapsuleRadius, p, l, v); } ),
1184 new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar", 1051 new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar",
1185 1.5f, 1052 1.5f,
1186 (s,cf,p,v) => { s.m_params[0].avatarCapsuleHeight = cf.GetFloat(p, v); }, 1053 (s,cf,p,v) => { s.m_params[0].avatarCapsuleHeight = cf.GetFloat(p, v); },
1187 (s) => { return s.m_params[0].avatarCapsuleHeight; }, 1054 (s) => { return s.m_params[0].avatarCapsuleHeight; },
1188 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleHeight, p, l, v); } ), 1055 (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarCapsuleHeight, p, l, v); } ),
1189 new ParameterDefn("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions", 1056 new ParameterDefn("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions",
1190 0.1f, 1057 0.1f,
1191 (s,cf,p,v) => { s.m_params[0].avatarContactProcessingThreshold = cf.GetFloat(p, v); }, 1058 (s,cf,p,v) => { s.m_params[0].avatarContactProcessingThreshold = cf.GetFloat(p, v); },
1192 (s) => { return s.m_params[0].avatarContactProcessingThreshold; }, 1059 (s) => { return s.m_params[0].avatarContactProcessingThreshold; },
1193 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ), 1060 (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ),
1194 1061
1195 1062
1196 new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)", 1063 new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)",
1197 0f, 1064 0f, // zero to disable
1198 (s,cf,p,v) => { s.m_params[0].maxPersistantManifoldPoolSize = cf.GetFloat(p, v); }, 1065 (s,cf,p,v) => { s.m_params[0].maxPersistantManifoldPoolSize = cf.GetFloat(p, v); },
1199 (s) => { return s.m_params[0].maxPersistantManifoldPoolSize; }, 1066 (s) => { return s.m_params[0].maxPersistantManifoldPoolSize; },
1200 (s,p,l,v) => { s.m_params[0].maxPersistantManifoldPoolSize = v; } ), 1067 (s,p,l,v) => { s.m_params[0].maxPersistantManifoldPoolSize = v; } ),
1201 new ParameterDefn("MaxCollisionAlgorithmPoolSize", "Number of collisions pooled (0 means default of 4096)", 1068 new ParameterDefn("MaxCollisionAlgorithmPoolSize", "Number of collisions pooled (0 means default of 4096)",
1202 0f, 1069 0f, // zero to disable
1203 (s,cf,p,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = cf.GetFloat(p, v); }, 1070 (s,cf,p,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = cf.GetFloat(p, v); },
1204 (s) => { return s.m_params[0].maxCollisionAlgorithmPoolSize; }, 1071 (s) => { return s.m_params[0].maxCollisionAlgorithmPoolSize; },
1205 (s,p,l,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = v; } ), 1072 (s,p,l,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = v; } ),
@@ -1214,12 +1081,12 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1214 (s) => { return s.m_params[0].shouldForceUpdateAllAabbs; }, 1081 (s) => { return s.m_params[0].shouldForceUpdateAllAabbs; },
1215 (s,p,l,v) => { s.m_params[0].shouldForceUpdateAllAabbs = v; } ), 1082 (s,p,l,v) => { s.m_params[0].shouldForceUpdateAllAabbs = v; } ),
1216 new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction", 1083 new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction",
1217 ConfigurationParameters.numericTrue, 1084 ConfigurationParameters.numericFalse,
1218 (s,cf,p,v) => { s.m_params[0].shouldRandomizeSolverOrder = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, 1085 (s,cf,p,v) => { s.m_params[0].shouldRandomizeSolverOrder = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
1219 (s) => { return s.m_params[0].shouldRandomizeSolverOrder; }, 1086 (s) => { return s.m_params[0].shouldRandomizeSolverOrder; },
1220 (s,p,l,v) => { s.m_params[0].shouldRandomizeSolverOrder = v; } ), 1087 (s,p,l,v) => { s.m_params[0].shouldRandomizeSolverOrder = v; } ),
1221 new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands", 1088 new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands",
1222 ConfigurationParameters.numericTrue, 1089 ConfigurationParameters.numericFalse,
1223 (s,cf,p,v) => { s.m_params[0].shouldSplitSimulationIslands = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, 1090 (s,cf,p,v) => { s.m_params[0].shouldSplitSimulationIslands = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
1224 (s) => { return s.m_params[0].shouldSplitSimulationIslands; }, 1091 (s) => { return s.m_params[0].shouldSplitSimulationIslands; },
1225 (s,p,l,v) => { s.m_params[0].shouldSplitSimulationIslands = v; } ), 1092 (s,p,l,v) => { s.m_params[0].shouldSplitSimulationIslands = v; } ),
@@ -1234,11 +1101,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1234 (s) => { return s.m_params[0].numberOfSolverIterations; }, 1101 (s) => { return s.m_params[0].numberOfSolverIterations; },
1235 (s,p,l,v) => { s.m_params[0].numberOfSolverIterations = v; } ), 1102 (s,p,l,v) => { s.m_params[0].numberOfSolverIterations = v; } ),
1236 1103
1237 new ParameterDefn("LinksetImplementation", "Type of linkset implementation (0=Constraint, 1=Compound, 2=Manual)",
1238 (float)BSLinkset.LinksetImplementation.Compound,
1239 (s,cf,p,v) => { s.m_params[0].linksetImplementation = cf.GetFloat(p,v); },
1240 (s) => { return s.m_params[0].linksetImplementation; },
1241 (s,p,l,v) => { s.m_params[0].linksetImplementation = v; } ),
1242 new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.", 1104 new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.",
1243 ConfigurationParameters.numericFalse, 1105 ConfigurationParameters.numericFalse,
1244 (s,cf,p,v) => { s.m_params[0].linkConstraintUseFrameOffset = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, 1106 (s,cf,p,v) => { s.m_params[0].linkConstraintUseFrameOffset = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
@@ -1259,27 +1121,28 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1259 (s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = cf.GetFloat(p, v); }, 1121 (s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = cf.GetFloat(p, v); },
1260 (s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; }, 1122 (s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; },
1261 (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ), 1123 (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ),
1262 new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1", 1124 new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=none, 1=all. Default=0",
1263 0.1f, 1125 0.0f,
1264 (s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); }, 1126 (s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); },
1265 (s) => { return s.m_params[0].linkConstraintCFM; }, 1127 (s) => { return s.m_params[0].linkConstraintCFM; },
1266 (s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ), 1128 (s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ),
1267 new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2", 1129 new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2",
1268 0.1f, 1130 0.2f,
1269 (s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); }, 1131 (s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); },
1270 (s) => { return s.m_params[0].linkConstraintERP; }, 1132 (s) => { return s.m_params[0].linkConstraintERP; },
1271 (s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ), 1133 (s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ),
1272 new ParameterDefn("LinkConstraintSolverIterations", "Number of solver iterations when computing constraint. (0 = Bullet default)",
1273 40,
1274 (s,cf,p,v) => { s.m_params[0].linkConstraintSolverIterations = cf.GetFloat(p, v); },
1275 (s) => { return s.m_params[0].linkConstraintSolverIterations; },
1276 (s,p,l,v) => { s.m_params[0].linkConstraintSolverIterations = v; } ),
1277 1134
1278 new ParameterDefn("LogPhysicsStatisticsFrames", "Frames between outputting detailed phys stats. (0 is off)", 1135 new ParameterDefn("DetailedStats", "Frames between outputting detailed phys stats. (0 is off)",
1279 0f, 1136 0f,
1280 (s,cf,p,v) => { s.m_params[0].physicsLoggingFrames = cf.GetInt(p, (int)v); }, 1137 (s,cf,p,v) => { s.m_detailedStatsStep = cf.GetInt(p, (int)v); },
1281 (s) => { return (float)s.m_params[0].physicsLoggingFrames; }, 1138 (s) => { return (float)s.m_detailedStatsStep; },
1282 (s,p,l,v) => { s.m_params[0].physicsLoggingFrames = (int)v; } ), 1139 (s,p,l,v) => { s.m_detailedStatsStep = (int)v; } ),
1140 new ParameterDefn("ShouldDebugLog", "Enables detailed DEBUG log statements",
1141 ConfigurationParameters.numericFalse,
1142 (s,cf,p,v) => { s.ShouldDebugLog = cf.GetBoolean(p, s.BoolNumeric(v)); },
1143 (s) => { return s.NumericBool(s.ShouldDebugLog); },
1144 (s,p,l,v) => { s.ShouldDebugLog = s.BoolNumeric(v); } ),
1145
1283 }; 1146 };
1284 1147
1285 // Convert a boolean to our numeric true and false values 1148 // Convert a boolean to our numeric true and false values
@@ -1337,12 +1200,11 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1337 1200
1338 private PhysParameterEntry[] SettableParameters = new PhysParameterEntry[1]; 1201 private PhysParameterEntry[] SettableParameters = new PhysParameterEntry[1];
1339 1202
1340 // This creates an array in the correct format for returning the list of
1341 // parameters. This is used by the 'list' option of the 'physics' command.
1342 private void BuildParameterTable() 1203 private void BuildParameterTable()
1343 { 1204 {
1344 if (SettableParameters.Length < ParameterDefinitions.Length) 1205 if (SettableParameters.Length < ParameterDefinitions.Length)
1345 { 1206 {
1207
1346 List<PhysParameterEntry> entries = new List<PhysParameterEntry>(); 1208 List<PhysParameterEntry> entries = new List<PhysParameterEntry>();
1347 for (int ii = 0; ii < ParameterDefinitions.Length; ii++) 1209 for (int ii = 0; ii < ParameterDefinitions.Length; ii++)
1348 { 1210 {
@@ -1387,54 +1249,60 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1387 return ret; 1249 return ret;
1388 } 1250 }
1389 1251
1252 // check to see if we are updating a parameter for a particular or all of the prims
1253 protected void UpdateParameterPrims(ref float loc, string parm, uint localID, float val)
1254 {
1255 List<uint> operateOn;
1256 lock (m_prims) operateOn = new List<uint>(m_prims.Keys);
1257 UpdateParameterSet(operateOn, ref loc, parm, localID, val);
1258 }
1259
1260 // check to see if we are updating a parameter for a particular or all of the avatars
1261 protected void UpdateParameterAvatars(ref float loc, string parm, uint localID, float val)
1262 {
1263 List<uint> operateOn;
1264 lock (m_avatars) operateOn = new List<uint>(m_avatars.Keys);
1265 UpdateParameterSet(operateOn, ref loc, parm, localID, val);
1266 }
1267
1390 // update all the localIDs specified 1268 // update all the localIDs specified
1391 // If the local ID is APPLY_TO_NONE, just change the default value 1269 // If the local ID is APPLY_TO_NONE, just change the default value
1392 // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs 1270 // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs
1393 // If the localID is a specific object, apply the parameter change to only that object 1271 // If the localID is a specific object, apply the parameter change to only that object
1394 private void UpdateParameterObject(ref float defaultLoc, string parm, uint localID, float val) 1272 protected void UpdateParameterSet(List<uint> lIDs, ref float defaultLoc, string parm, uint localID, float val)
1395 { 1273 {
1396 List<uint> objectIDs = new List<uint>();
1397 switch (localID) 1274 switch (localID)
1398 { 1275 {
1399 case PhysParameterEntry.APPLY_TO_NONE: 1276 case PhysParameterEntry.APPLY_TO_NONE:
1400 defaultLoc = val; // setting only the default value 1277 defaultLoc = val; // setting only the default value
1401 // This will cause a call into the physical world if some operation is specified (SetOnObject).
1402 objectIDs.Add(TERRAIN_ID);
1403 TaintedUpdateParameter(parm, objectIDs, val);
1404 break; 1278 break;
1405 case PhysParameterEntry.APPLY_TO_ALL: 1279 case PhysParameterEntry.APPLY_TO_ALL:
1406 defaultLoc = val; // setting ALL also sets the default value 1280 defaultLoc = val; // setting ALL also sets the default value
1407 lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys); 1281 List<uint> objectIDs = lIDs;
1408 TaintedUpdateParameter(parm, objectIDs, val); 1282 string xparm = parm.ToLower();
1283 float xval = val;
1284 TaintedObject("BSScene.UpdateParameterSet", delegate() {
1285 foreach (uint lID in objectIDs)
1286 {
1287 BulletSimAPI.UpdateParameter(m_worldID, lID, xparm, xval);
1288 }
1289 });
1409 break; 1290 break;
1410 default: 1291 default:
1411 // setting only one localID 1292 // setting only one localID
1412 objectIDs.Add(localID); 1293 TaintedUpdateParameter(parm, localID, val);
1413 TaintedUpdateParameter(parm, objectIDs, val);
1414 break; 1294 break;
1415 } 1295 }
1416 } 1296 }
1417 1297
1418 // schedule the actual updating of the paramter to when the phys engine is not busy 1298 // schedule the actual updating of the paramter to when the phys engine is not busy
1419 private void TaintedUpdateParameter(string parm, List<uint> lIDs, float val) 1299 protected void TaintedUpdateParameter(string parm, uint localID, float val)
1420 { 1300 {
1301 uint xlocalID = localID;
1302 string xparm = parm.ToLower();
1421 float xval = val; 1303 float xval = val;
1422 List<uint> xlIDs = lIDs; 1304 TaintedObject("BSScene.TaintedUpdateParameter", delegate() {
1423 string xparm = parm; 1305 BulletSimAPI.UpdateParameter(m_worldID, xlocalID, xparm, xval);
1424 TaintedObject("BSScene.UpdateParameterSet", delegate() {
1425 ParameterDefn thisParam;
1426 if (TryGetParameter(xparm, out thisParam))
1427 {
1428 if (thisParam.onObject != null)
1429 {
1430 foreach (uint lID in xlIDs)
1431 {
1432 BSPhysObject theObject = null;
1433 PhysObjects.TryGetValue(lID, out theObject);
1434 thisParam.onObject(this, theObject, xval);
1435 }
1436 }
1437 }
1438 }); 1306 });
1439 } 1307 }
1440 1308
@@ -1458,24 +1326,12 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1458 1326
1459 #endregion Runtime settable parameters 1327 #endregion Runtime settable parameters
1460 1328
1461 // Debugging routine for dumping detailed physical information for vehicle prims
1462 private void DumpVehicles()
1463 {
1464 foreach (BSPrim prim in m_vehicles)
1465 {
1466 BulletSimAPI.DumpRigidBody2(World.ptr, prim.PhysBody.ptr);
1467 BulletSimAPI.DumpCollisionShape2(World.ptr, prim.PhysShape.ptr);
1468 }
1469 }
1470
1471 // Invoke the detailed logger and output something if it's enabled. 1329 // Invoke the detailed logger and output something if it's enabled.
1472 public void DetailLog(string msg, params Object[] args) 1330 public void DetailLog(string msg, params Object[] args)
1473 { 1331 {
1474 PhysicsLogging.Write(msg, args); 1332 PhysicsLogging.Write(msg, args);
1475 // Add the Flush() if debugging crashes. Gets all the messages written out.
1476 PhysicsLogging.Flush();
1477 } 1333 }
1478 // Used to fill in the LocalID when there isn't one. It's the correct number of characters. 1334 // used to fill in the LocalID when there isn't one
1479 public const string DetailLogZero = "0000000000"; 1335 public const string DetailLogZero = "0000000000";
1480 1336
1481} 1337}