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