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.cs980
1 files changed, 564 insertions, 416 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
index a31c578..2fee95e 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,13 +331,51 @@ 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);
317 } 339 }
318 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 }
375 #endregion // Construction and Initialization
376
377 #region Prim and Avatar addition and removal
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 {
321 m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader); 381 m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader);
@@ -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,14 +479,14 @@ 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
@@ -425,25 +496,34 @@ public class BSScene : PhysicsScene, IPhysicsParameters
425 // step the physical world one interval 496 // step the physical world one interval
426 m_simulationStep++; 497 m_simulationStep++;
427 int numSubSteps = 0; 498 int numSubSteps = 0;
499
428 try 500 try
429 { 501 {
430 numSubSteps = BulletSimAPI.PhysicsStep(m_worldID, timeStep, m_maxSubSteps, m_fixedTimeStep, 502 if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG
503 if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount();
504
505 numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep,
431 out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr); 506 out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr);
432 // DetailLog("{0},Simulate,call, substeps={1}, updates={2}, colliders={3}", DetailLogZero, numSubSteps, updatedEntityCount, collidersCount); 507
508 if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime);
509 DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}",
510 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount);
511 if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG
433 } 512 }
434 catch (Exception e) 513 catch (Exception e)
435 { 514 {
436 m_log.WarnFormat("{0},PhysicsStep Exception: substeps={1}, updates={2}, colliders={3}, e={4}", LogHeader, numSubSteps, updatedEntityCount, collidersCount, e); 515 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); 516 LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e);
438 // updatedEntityCount = 0; 517 DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}",
518 DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount);
519 updatedEntityCount = 0;
439 collidersCount = 0; 520 collidersCount = 0;
440 } 521 }
441 522
442
443 // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in 523 // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in
444 524
445 // Get a value for 'now' so all the collision and update routines don't have to get their own 525 // Get a value for 'now' so all the collision and update routines don't have to get their own
446 m_simulationNowTime = Util.EnvironmentTickCount(); 526 SimulationNowTime = Util.EnvironmentTickCount();
447 527
448 // If there were collisions, process them by sending the event to the prim. 528 // If there were collisions, process them by sending the event to the prim.
449 // Collisions must be processed before updates. 529 // Collisions must be processed before updates.
@@ -462,19 +542,32 @@ public class BSScene : PhysicsScene, IPhysicsParameters
462 542
463 // The above SendCollision's batch up the collisions on the objects. 543 // The above SendCollision's batch up the collisions on the objects.
464 // Now push the collisions into the simulator. 544 // Now push the collisions into the simulator.
465 foreach (BSPrim bsp in m_primsWithCollisions) 545 if (ObjectsWithCollisions.Count > 0)
466 bsp.SendCollisions(); 546 {
467 m_primsWithCollisions.Clear(); 547 foreach (BSPhysObject bsp in ObjectsWithCollisions)
468 548 if (!bsp.SendCollisions())
469 // This is a kludge to get avatar movement updated. 549 {
470 // Don't send collisions only if there were collisions -- send everytime. 550 // 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 551 ObjectsWithNoMoreCollisions.Add(bsp);
472 // avatar animations and stuff. 552 }
473 // foreach (BSCharacter bsc in m_avatarsWithCollisions) 553 }
474 // bsc.SendCollisions(); 554
475 foreach (KeyValuePair<uint, BSCharacter> kvp in m_avatars) 555 // This is a kludge to get avatar movement updates.
476 kvp.Value.SendCollisions(); 556 // The simulator expects collisions for avatars even if there are have been no collisions.
477 m_avatarsWithCollisions.Clear(); 557 // The event updates avatar animations and stuff.
558 // If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
559 foreach (BSPhysObject bsp in m_avatars)
560 if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice
561 bsp.SendCollisions();
562
563 // Objects that are done colliding are removed from the ObjectsWithCollisions list.
564 // Not done above because it is inside an iteration of ObjectWithCollisions.
565 if (ObjectsWithNoMoreCollisions.Count > 0)
566 {
567 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
568 ObjectsWithCollisions.Remove(po);
569 ObjectsWithNoMoreCollisions.Clear();
570 }
478 571
479 // If any of the objects had updated properties, tell the object it has been changed by the physics engine 572 // If any of the objects had updated properties, tell the object it has been changed by the physics engine
480 if (updatedEntityCount > 0) 573 if (updatedEntityCount > 0)
@@ -482,320 +575,316 @@ public class BSScene : PhysicsScene, IPhysicsParameters
482 for (int ii = 0; ii < updatedEntityCount; ii++) 575 for (int ii = 0; ii < updatedEntityCount; ii++)
483 { 576 {
484 EntityProperties entprop = m_updateArray[ii]; 577 EntityProperties entprop = m_updateArray[ii];
485 BSPrim prim; 578 BSPhysObject pobj;
486 if (m_prims.TryGetValue(entprop.ID, out prim)) 579 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 { 580 {
494 actor.UpdateProperties(entprop); 581 pobj.UpdateProperties(entprop);
495 continue;
496 } 582 }
497 } 583 }
498 } 584 }
499 585
500 // If enabled, call into the physics engine to dump statistics 586 ProcessPostStepTaints();
501 if (m_detailedStatsStep > 0)
502 {
503 if ((m_simulationStep % m_detailedStatsStep) == 0)
504 {
505 BulletSimAPI.DumpBulletStatistics();
506 }
507 }
508 587
509 // this is a waste since the outside routine also calcuates the physics simulation 588 // 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. 589 // Only enable this in a limited test world with few objects.
511 // long simulateTotalTime = Util.EnvironmentTickCountSubtract(simulateStartTime); 590 // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG
512 // return (timeStep * (float)simulateTotalTime); 591
513 592 // The physics engine returns the number of milliseconds it simulated this call.
514 // TODO: FIX THIS: fps calculation possibly wrong. 593 // 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 594 // We multiply by 55 to give a recognizable running rate (55 or less).
516 // that by the physics simulation gives a slower frame rate. 595 return numSubSteps * m_fixedTimeStep * 1000 * 55;
517 long totalSimulationTime = Util.EnvironmentTickCountSubtract(simulateStartTime); 596 // return timeStep * 1000 * 55;
518 if (totalSimulationTime >= timeStep)
519 return 0;
520 return 1f / (timeStep + totalSimulationTime);
521 } 597 }
522 598
523 // Something has collided 599 // Something has collided
524 private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penitration) 600 private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration)
525 { 601 {
526 if (localID == TERRAIN_ID || localID == GROUNDPLANE_ID) 602 if (localID <= TerrainManager.HighestTerrainID)
527 { 603 {
528 return; // don't send collisions to the terrain 604 return; // don't send collisions to the terrain
529 } 605 }
530 606
531 ActorTypes type = ActorTypes.Prim; 607 BSPhysObject collider;
532 if (collidingWith == TERRAIN_ID || collidingWith == GROUNDPLANE_ID) 608 if (!PhysObjects.TryGetValue(localID, out collider))
533 type = ActorTypes.Ground; 609 {
534 else if (m_avatars.ContainsKey(collidingWith)) 610 // If the object that is colliding cannot be found, just ignore the collision.
535 type = ActorTypes.Agent; 611 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; 612 return;
542 } 613 }
543 BSCharacter actor; 614
544 if (m_avatars.TryGetValue(localID, out actor)) { 615 // 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); 616 BSPhysObject collidee = null;
546 m_avatarsWithCollisions.Add(actor); 617 PhysObjects.TryGetValue(collidingWith, out collidee);
547 return; 618
619 // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
620
621 if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
622 {
623 // If a collision was posted, remember to send it to the simulator
624 ObjectsWithCollisions.Add(collider);
548 } 625 }
626
549 return; 627 return;
550 } 628 }
551 629
630 #endregion // Simulation
631
552 public override void GetResults() { } 632 public override void GetResults() { }
553 633
634 #region Terrain
635
554 public override void SetTerrain(float[] heightMap) { 636 public override void SetTerrain(float[] heightMap) {
555 m_heightMap = heightMap; 637 TerrainManager.SetTerrain(heightMap);
556 this.TaintedObject("BSScene.SetTerrain", delegate()
557 {
558 BulletSimAPI.SetHeightmap(m_worldID, m_heightMap);
559 });
560 } 638 }
561 639
562 // Someday we will have complex terrain with caves and tunnels 640 public override void SetWaterLevel(float baseheight)
563 // For the moment, it's flat and convex
564 public float GetTerrainHeightAtXYZ(Vector3 loc)
565 { 641 {
566 return GetTerrainHeightAtXY(loc.X, loc.Y); 642 m_waterLevel = baseheight;
643 }
644 // Someday....
645 public float GetWaterLevelAtXYZ(Vector3 loc)
646 {
647 return m_waterLevel;
567 } 648 }
568 649
569 public float GetTerrainHeightAtXY(float tX, float tY) 650 public override void DeleteTerrain()
570 { 651 {
571 if (tX < 0 || tX >= Constants.RegionSize || tY < 0 || tY >= Constants.RegionSize) 652 // m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader);
572 return 30;
573 return m_heightMap[((int)tX) * Constants.RegionSize + ((int)tY)];
574 } 653 }
575 654
576 public override void SetWaterLevel(float baseheight) 655 // Although no one seems to check this, I do support combining.
656 public override bool SupportsCombining()
577 { 657 {
578 m_waterLevel = baseheight; 658 return TerrainManager.SupportsCombining();
579 // TODO: pass to physics engine so things will float?
580 } 659 }
581 public float GetWaterLevel() 660 // This call says I am a child to region zero in a mega-region. 'pScene' is that
661 // of region zero, 'offset' is my offset from regions zero's origin, and
662 // 'extents' is the largest XY that is handled in my region.
663 public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
582 { 664 {
583 return m_waterLevel; 665 TerrainManager.Combine(pScene, offset, extents);
584 } 666 }
585 667
586 public override void DeleteTerrain() 668 // Unhook all the combining that I know about.
669 public override void UnCombine(PhysicsScene pScene)
587 { 670 {
588 // m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader); 671 TerrainManager.UnCombine(pScene);
589 } 672 }
590 673
591 public override void Dispose() 674 #endregion // Terrain
675
676 public override Dictionary<uint, float> GetTopColliders()
592 { 677 {
593 // m_log.DebugFormat("{0}: Dispose()", LogHeader); 678 return new Dictionary<uint, float>();
679 }
594 680
595 // make sure no stepping happens while we're deleting stuff 681 public override bool IsThreaded { get { return false; } }
596 m_initialized = false;
597 682
598 foreach (KeyValuePair<uint, BSCharacter> kvp in m_avatars) 683 #region Taints
599 {
600 kvp.Value.Destroy();
601 }
602 m_avatars.Clear();
603 684
604 foreach (KeyValuePair<uint, BSPrim> kvp in m_prims) 685 // Calls to the PhysicsActors can't directly call into the physics engine
605 { 686 // because it might be busy. We delay changes to a known time.
606 kvp.Value.Destroy(); 687 // We rely on C#'s closure to save and restore the context for the delegate.
607 } 688 public void TaintedObject(String ident, TaintCallback callback)
608 m_prims.Clear(); 689 {
690 if (!m_initialized) return;
609 691
610 // Now that the prims are all cleaned up, there should be no constraints left 692 lock (_taintLock)
611 if (m_constraintCollection != null)
612 { 693 {
613 m_constraintCollection.Dispose(); 694 _taintOperations.Add(new TaintCallbackEntry(ident, callback));
614 m_constraintCollection = null;
615 } 695 }
616 696
617 // Anything left in the unmanaged code should be cleaned out 697 return;
618 BulletSimAPI.Shutdown(WorldID);
619
620 // Not logging any more
621 PhysicsLogging.Close();
622 } 698 }
623 699
624 public override Dictionary<uint, float> GetTopColliders() 700 // Sometimes a potentially tainted operation can be used in and out of taint time.
701 // This routine executes the command immediately if in taint-time otherwise it is queued.
702 public void TaintedObject(bool inTaintTime, string ident, TaintCallback callback)
625 { 703 {
626 return new Dictionary<uint, float>(); 704 if (inTaintTime)
705 callback();
706 else
707 TaintedObject(ident, callback);
627 } 708 }
628 709
629 public override bool IsThreaded { get { return false; } } 710 // When someone tries to change a property on a BSPrim or BSCharacter, the object queues
630 711 // a callback into itself to do the actual property change. That callback is called
631 /// <summary> 712 // 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 713 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 { 714 {
638 // most of this is redundant now as the mesher will return null if it cant mesh a prim 715 InTaintTime = true;
639 // but we still need to check for sculptie meshing being enabled so this is the most 716 ProcessRegularTaints();
640 // convenient place to do it for now... 717 ProcessPostTaintTaints();
641 718 InTaintTime = false;
642 // int iPropertiesNotSupportedDefault = 0; 719 }
643
644 if (pbs.SculptEntry && !_meshSculptedPrim)
645 {
646 // Render sculpties as boxes
647 return false;
648 }
649 720
650 // if it's a standard box or sphere with no cuts, hollows, twist or top shear, return false since Bullet 721 private void ProcessRegularTaints()
651 // can use an internal representation for the prim 722 {
652 if (!_forceSimplePrimMeshing) 723 if (_taintOperations.Count > 0) // save allocating new list if there is nothing to process
653 { 724 {
654 if ((pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight) 725 /*
655 || (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1 726 // Code to limit the number of taints processed per step. Meant to limit step time.
656 && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z)) 727 // Unsure if a good idea as code assumes that taints are done before the step.
728 int taintCount = m_taintsToProcessPerStep;
729 TaintCallbackEntry oneCallback = new TaintCallbackEntry();
730 while (_taintOperations.Count > 0 && taintCount-- > 0)
657 { 731 {
658 732 bool gotOne = false;
659 if (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 733 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 { 734 {
667 return false; 735 if (_taintOperations.Count > 0)
736 {
737 oneCallback = _taintOperations[0];
738 _taintOperations.RemoveAt(0);
739 gotOne = true;
740 }
741 }
742 if (gotOne)
743 {
744 try
745 {
746 DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, oneCallback.ident);
747 oneCallback.callback();
748 }
749 catch (Exception e)
750 {
751 DetailLog("{0},BSScene.ProcessTaints,doTaintException,id={1}", DetailLogZero, oneCallback.ident); // DEBUG DEBUG DEBUG
752 m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, oneCallback.ident, e);
753 }
668 } 754 }
669 } 755 }
670 } 756 if (_taintOperations.Count > 0)
671
672 /* TODO: verify that the mesher will now do all these shapes
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)
704 { 757 {
705 iPropertiesNotSupportedDefault++; 758 DetailLog("{0},BSScene.ProcessTaints,leftTaintsOnList,numNotProcessed={1}", DetailLogZero, _taintOperations.Count);
706 } 759 }
707 } 760 */
708 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Circle) 761 // swizzle a new list into the list location so we can process what's there
709 { 762 List<TaintCallbackEntry> oldList;
710 if (pbs.PathCurve == (byte)Extrusion.Straight) 763 lock (_taintLock)
711 { 764 {
712 iPropertiesNotSupportedDefault++; 765 oldList = _taintOperations;
766 _taintOperations = new List<TaintCallbackEntry>();
713 } 767 }
714 // ProfileCurve seems to combine hole shape and profile curve so we need to only compare against the lower 3 bits 768
715 else if (pbs.PathCurve == (byte)Extrusion.Curve1) 769 foreach (TaintCallbackEntry tcbe in oldList)
716 { 770 {
717 iPropertiesNotSupportedDefault++; 771 try
772 {
773 DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG
774 tcbe.callback();
775 }
776 catch (Exception e)
777 {
778 m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e);
779 }
718 } 780 }
781 oldList.Clear();
719 } 782 }
720 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle) 783 }
784
785 // Schedule an update to happen after all the regular taints are processed.
786 // Note that new requests for the same operation ("ident") for the same object ("ID")
787 // will replace any previous operation by the same object.
788 public void PostTaintObject(String ident, uint ID, TaintCallback callback)
789 {
790 if (!m_initialized) return;
791
792 string uniqueIdent = ident + "-" + ID.ToString();
793 lock (_taintLock)
721 { 794 {
722 if (pbs.PathCurve == (byte)Extrusion.Curve1 || pbs.PathCurve == (byte)Extrusion.Curve2) 795 _postTaintOperations[uniqueIdent] = new TaintCallbackEntry(uniqueIdent, callback);
723 {
724 iPropertiesNotSupportedDefault++;
725 }
726 } 796 }
727 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle) 797
798 return;
799 }
800
801 private void ProcessPostTaintTaints()
802 {
803 if (_postTaintOperations.Count > 0)
728 { 804 {
729 if (pbs.PathCurve == (byte)Extrusion.Straight) 805 Dictionary<string, TaintCallbackEntry> oldList;
806 lock (_taintLock)
730 { 807 {
731 iPropertiesNotSupportedDefault++; 808 oldList = _postTaintOperations;
809 _postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
732 } 810 }
733 else if (pbs.PathCurve == (byte)Extrusion.Curve1) 811
812 foreach (KeyValuePair<string,TaintCallbackEntry> kvp in oldList)
734 { 813 {
735 iPropertiesNotSupportedDefault++; 814 try
815 {
816 DetailLog("{0},BSScene.ProcessPostTaintTaints,doTaint,id={1}", DetailLogZero, kvp.Key); // DEBUG DEBUG DEBUG
817 kvp.Value.callback();
818 }
819 catch (Exception e)
820 {
821 m_log.ErrorFormat("{0}: ProcessPostTaintTaints: {1}: Exception: {2}", LogHeader, kvp.Key, e);
822 }
736 } 823 }
824 oldList.Clear();
737 } 825 }
738 if (iPropertiesNotSupportedDefault == 0)
739 {
740 return false;
741 }
742 */
743 return true;
744 } 826 }
745 827
746 // Calls to the PhysicsActors can't directly call into the physics engine 828 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 { 829 {
751 if (!m_initialized) return; 830 if (!m_initialized) return;
752 831
753 lock (_taintLock) 832 lock (_taintLock)
754 _taintedObjects.Add(new TaintCallbackEntry(ident, callback)); 833 {
834 _postStepOperations.Add(new TaintCallbackEntry(ident, callback));
835 }
836
755 return; 837 return;
756 } 838 }
757 839
758 // When someone tries to change a property on a BSPrim or BSCharacter, the object queues 840 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 { 841 {
763 if (_taintedObjects.Count > 0) // save allocating new list if there is nothing to process 842 if (_postStepOperations.Count > 0)
764 { 843 {
765 // swizzle a new list into the list location so we can process what's there
766 List<TaintCallbackEntry> oldList; 844 List<TaintCallbackEntry> oldList;
767 lock (_taintLock) 845 lock (_taintLock)
768 { 846 {
769 oldList = _taintedObjects; 847 oldList = _postStepOperations;
770 _taintedObjects = new List<TaintCallbackEntry>(); 848 _postStepOperations = new List<TaintCallbackEntry>();
771 } 849 }
772 850
773 foreach (TaintCallbackEntry tcbe in oldList) 851 foreach (TaintCallbackEntry tcbe in oldList)
774 { 852 {
775 try 853 try
776 { 854 {
855 DetailLog("{0},BSScene.ProcessPostStepTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG
777 tcbe.callback(); 856 tcbe.callback();
778 } 857 }
779 catch (Exception e) 858 catch (Exception e)
780 { 859 {
781 m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e); 860 m_log.ErrorFormat("{0}: ProcessPostStepTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e);
782 } 861 }
783 } 862 }
784 oldList.Clear(); 863 oldList.Clear();
785 } 864 }
786 } 865 }
787 866
867 public bool AssertInTaintTime(string whereFrom)
868 {
869 if (!InTaintTime)
870 {
871 DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom);
872 m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom);
873 Util.PrintCallStack();
874 }
875 return InTaintTime;
876 }
877
878 #endregion // Taints
879
788 #region Vehicles 880 #region Vehicles
789 881
790 public void VehicleInSceneTypeChanged(BSPrim vehic, Vehicle newType) 882 public void VehicleInSceneTypeChanged(BSPrim vehic, Vehicle newType)
791 { 883 {
792 if (newType == Vehicle.TYPE_NONE) 884 RemoveVehiclePrim(vehic);
793 { 885 if (newType != Vehicle.TYPE_NONE)
794 RemoveVehiclePrim(vehic);
795 }
796 else
797 { 886 {
798 // make it so the scene will call us each tick to do vehicle things 887 // make it so the scene will call us each tick to do vehicle things
799 AddVehiclePrim(vehic); 888 AddVehiclePrim(vehic);
800 } 889 }
801 } 890 }
@@ -827,21 +916,22 @@ public class BSScene : PhysicsScene, IPhysicsParameters
827 } 916 }
828 917
829 // Some prims have extra vehicle actions 918 // Some prims have extra vehicle actions
830 // no locking because only called when physics engine is not busy 919 // Called at taint time!
831 private void ProcessVehicles(float timeStep) 920 private void ProcessVehicles(float timeStep)
832 { 921 {
833 foreach (BSPrim prim in m_vehicles) 922 foreach (BSPhysObject pobj in m_vehicles)
834 { 923 {
835 prim.StepVehicle(timeStep); 924 pobj.StepVehicle(timeStep);
836 } 925 }
837 } 926 }
838 #endregion Vehicles 927 #endregion Vehicles
839 928
840 #region Parameters 929 #region INI and command line parameter processing
841 930
842 delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val); 931 delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val);
843 delegate float ParamGet(BSScene scene); 932 delegate float ParamGet(BSScene scene);
844 delegate void ParamSet(BSScene scene, string paramName, uint localID, float val); 933 delegate void ParamSet(BSScene scene, string paramName, uint localID, float val);
934 delegate void SetOnObject(BSScene scene, BSPhysObject obj, float val);
845 935
846 private struct ParameterDefn 936 private struct ParameterDefn
847 { 937 {
@@ -851,6 +941,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
851 public ParamUser userParam; // get the value from the configuration file 941 public ParamUser userParam; // get the value from the configuration file
852 public ParamGet getter; // return the current value stored for this parameter 942 public ParamGet getter; // return the current value stored for this parameter
853 public ParamSet setter; // set the current value for this parameter 943 public ParamSet setter; // set the current value for this parameter
944 public SetOnObject onObject; // set the value on an object in the physical domain
854 public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s) 945 public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s)
855 { 946 {
856 name = n; 947 name = n;
@@ -859,6 +950,17 @@ public class BSScene : PhysicsScene, IPhysicsParameters
859 userParam = u; 950 userParam = u;
860 getter = g; 951 getter = g;
861 setter = s; 952 setter = s;
953 onObject = null;
954 }
955 public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s, SetOnObject o)
956 {
957 name = n;
958 desc = d;
959 defaultValue = v;
960 userParam = u;
961 getter = g;
962 setter = s;
963 onObject = o;
862 } 964 }
863 } 965 }
864 966
@@ -869,7 +971,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
869 // getters and setters. 971 // getters and setters.
870 // It is easiest to find an existing definition and copy it. 972 // It is easiest to find an existing definition and copy it.
871 // Parameter values are floats. Booleans are converted to a floating value. 973 // Parameter values are floats. Booleans are converted to a floating value.
872 // 974 //
873 // A ParameterDefn() takes the following parameters: 975 // A ParameterDefn() takes the following parameters:
874 // -- the text name of the parameter. This is used for console input and ini file. 976 // -- 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. 977 // -- a short text description of the parameter. This shows up in the console listing.
@@ -880,6 +982,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
880 // 982 //
881 // The single letter parameters for the delegates are: 983 // The single letter parameters for the delegates are:
882 // s = BSScene 984 // s = BSScene
985 // o = BSPhysObject
883 // p = string parameter name 986 // p = string parameter name
884 // l = localID of referenced object 987 // l = localID of referenced object
885 // v = float value 988 // v = float value
@@ -888,25 +991,40 @@ public class BSScene : PhysicsScene, IPhysicsParameters
888 { 991 {
889 new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties", 992 new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties",
890 ConfigurationParameters.numericTrue, 993 ConfigurationParameters.numericTrue,
891 (s,cf,p,v) => { s._meshSculptedPrim = cf.GetBoolean(p, s.BoolNumeric(v)); }, 994 (s,cf,p,v) => { s.ShouldMeshSculptedPrim = cf.GetBoolean(p, s.BoolNumeric(v)); },
892 (s) => { return s.NumericBool(s._meshSculptedPrim); }, 995 (s) => { return s.NumericBool(s.ShouldMeshSculptedPrim); },
893 (s,p,l,v) => { s._meshSculptedPrim = s.BoolNumeric(v); } ), 996 (s,p,l,v) => { s.ShouldMeshSculptedPrim = s.BoolNumeric(v); } ),
894 new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects", 997 new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects",
895 ConfigurationParameters.numericFalse, 998 ConfigurationParameters.numericFalse,
896 (s,cf,p,v) => { s._forceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); }, 999 (s,cf,p,v) => { s.ShouldForceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); },
897 (s) => { return s.NumericBool(s._forceSimplePrimMeshing); }, 1000 (s) => { return s.NumericBool(s.ShouldForceSimplePrimMeshing); },
898 (s,p,l,v) => { s._forceSimplePrimMeshing = s.BoolNumeric(v); } ), 1001 (s,p,l,v) => { s.ShouldForceSimplePrimMeshing = s.BoolNumeric(v); } ),
1002 new ParameterDefn("UseHullsForPhysicalObjects", "If true, create hulls for physical objects",
1003 ConfigurationParameters.numericTrue,
1004 (s,cf,p,v) => { s.ShouldUseHullsForPhysicalObjects = cf.GetBoolean(p, s.BoolNumeric(v)); },
1005 (s) => { return s.NumericBool(s.ShouldUseHullsForPhysicalObjects); },
1006 (s,p,l,v) => { s.ShouldUseHullsForPhysicalObjects = s.BoolNumeric(v); } ),
899 1007
900 new ParameterDefn("MeshLOD", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)", 1008 new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)",
901 8f, 1009 8f,
902 (s,cf,p,v) => { s.m_meshLOD = cf.GetInt(p, (int)v); }, 1010 (s,cf,p,v) => { s.MeshLOD = (float)cf.GetInt(p, (int)v); },
903 (s) => { return (float)s.m_meshLOD; }, 1011 (s) => { return s.MeshLOD; },
904 (s,p,l,v) => { s.m_meshLOD = (int)v; } ), 1012 (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)", 1013 new ParameterDefn("MeshLevelOfDetailMegaPrim", "Level of detail to render meshes larger than threshold meters",
1014 16f,
1015 (s,cf,p,v) => { s.MeshMegaPrimLOD = (float)cf.GetInt(p, (int)v); },
1016 (s) => { return s.MeshMegaPrimLOD; },
1017 (s,p,l,v) => { s.MeshMegaPrimLOD = v; } ),
1018 new ParameterDefn("MeshLevelOfDetailMegaPrimThreshold", "Size (in meters) of a mesh before using MeshMegaPrimLOD",
1019 10f,
1020 (s,cf,p,v) => { s.MeshMegaPrimThreshold = (float)cf.GetInt(p, (int)v); },
1021 (s) => { return s.MeshMegaPrimThreshold; },
1022 (s,p,l,v) => { s.MeshMegaPrimThreshold = v; } ),
1023 new ParameterDefn("SculptLevelOfDetail", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)",
906 32f, 1024 32f,
907 (s,cf,p,v) => { s.m_sculptLOD = cf.GetInt(p, (int)v); }, 1025 (s,cf,p,v) => { s.SculptLOD = (float)cf.GetInt(p, (int)v); },
908 (s) => { return (float)s.m_sculptLOD; }, 1026 (s) => { return s.SculptLOD; },
909 (s,p,l,v) => { s.m_sculptLOD = (int)v; } ), 1027 (s,p,l,v) => { s.SculptLOD = v; } ),
910 1028
911 new ParameterDefn("MaxSubStep", "In simulation step, maximum number of substeps", 1029 new ParameterDefn("MaxSubStep", "In simulation step, maximum number of substeps",
912 10f, 1030 10f,
@@ -928,11 +1046,16 @@ public class BSScene : PhysicsScene, IPhysicsParameters
928 (s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); }, 1046 (s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); },
929 (s) => { return (float)s.m_maxUpdatesPerFrame; }, 1047 (s) => { return (float)s.m_maxUpdatesPerFrame; },
930 (s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ), 1048 (s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ),
1049 new ParameterDefn("MaxTaintsToProcessPerStep", "Number of update taints to process before each simulation step",
1050 500f,
1051 (s,cf,p,v) => { s.m_taintsToProcessPerStep = cf.GetInt(p, (int)v); },
1052 (s) => { return (float)s.m_taintsToProcessPerStep; },
1053 (s,p,l,v) => { s.m_taintsToProcessPerStep = (int)v; } ),
931 new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)", 1054 new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)",
932 10000.01f, 1055 10000.01f,
933 (s,cf,p,v) => { s.m_maximumObjectMass = cf.GetFloat(p, v); }, 1056 (s,cf,p,v) => { s.MaximumObjectMass = cf.GetFloat(p, v); },
934 (s) => { return (float)s.m_maximumObjectMass; }, 1057 (s) => { return (float)s.MaximumObjectMass; },
935 (s,p,l,v) => { s.m_maximumObjectMass = v; } ), 1058 (s,p,l,v) => { s.MaximumObjectMass = v; } ),
936 1059
937 new ParameterDefn("PID_D", "Derivitive factor for motion smoothing", 1060 new ParameterDefn("PID_D", "Derivitive factor for motion smoothing",
938 2200f, 1061 2200f,
@@ -969,104 +1092,118 @@ public class BSScene : PhysicsScene, IPhysicsParameters
969 -9.80665f, 1092 -9.80665f,
970 (s,cf,p,v) => { s.m_params[0].gravity = cf.GetFloat(p, v); }, 1093 (s,cf,p,v) => { s.m_params[0].gravity = cf.GetFloat(p, v); },
971 (s) => { return s.m_params[0].gravity; }, 1094 (s) => { return s.m_params[0].gravity; },
972 (s,p,l,v) => { s.m_params[0].gravity = v; s.TaintedUpdateParameter(p,l,v); } ), 1095 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].gravity, p, PhysParameterEntry.APPLY_TO_NONE, v); },
1096 (s,o,v) => { BulletSimAPI.SetGravity2(s.World.ptr, new Vector3(0f,0f,v)); } ),
973 1097
974 1098
975 new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)", 1099 new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)",
976 0f, 1100 0f,
977 (s,cf,p,v) => { s.m_params[0].linearDamping = cf.GetFloat(p, v); }, 1101 (s,cf,p,v) => { s.m_params[0].linearDamping = cf.GetFloat(p, v); },
978 (s) => { return s.m_params[0].linearDamping; }, 1102 (s) => { return s.m_params[0].linearDamping; },
979 (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].linearDamping, p, l, v); } ), 1103 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearDamping, p, l, v); },
1104 (s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, v, s.m_params[0].angularDamping); } ),
980 new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)", 1105 new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)",
981 0f, 1106 0f,
982 (s,cf,p,v) => { s.m_params[0].angularDamping = cf.GetFloat(p, v); }, 1107 (s,cf,p,v) => { s.m_params[0].angularDamping = cf.GetFloat(p, v); },
983 (s) => { return s.m_params[0].angularDamping; }, 1108 (s) => { return s.m_params[0].angularDamping; },
984 (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].angularDamping, p, l, v); } ), 1109 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularDamping, p, l, v); },
1110 (s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, s.m_params[0].linearDamping, v); } ),
985 new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static", 1111 new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static",
986 0.2f, 1112 0.2f,
987 (s,cf,p,v) => { s.m_params[0].deactivationTime = cf.GetFloat(p, v); }, 1113 (s,cf,p,v) => { s.m_params[0].deactivationTime = cf.GetFloat(p, v); },
988 (s) => { return s.m_params[0].deactivationTime; }, 1114 (s) => { return s.m_params[0].deactivationTime; },
989 (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].deactivationTime, p, l, v); } ), 1115 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].deactivationTime, p, l, v); },
1116 (s,o,v) => { BulletSimAPI.SetDeactivationTime2(o.PhysBody.ptr, v); } ),
990 new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static", 1117 new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static",
991 0.8f, 1118 0.8f,
992 (s,cf,p,v) => { s.m_params[0].linearSleepingThreshold = cf.GetFloat(p, v); }, 1119 (s,cf,p,v) => { s.m_params[0].linearSleepingThreshold = cf.GetFloat(p, v); },
993 (s) => { return s.m_params[0].linearSleepingThreshold; }, 1120 (s) => { return s.m_params[0].linearSleepingThreshold; },
994 (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].linearSleepingThreshold, p, l, v); } ), 1121 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearSleepingThreshold, p, l, v); },
1122 (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ),
995 new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static", 1123 new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static",
996 1.0f, 1124 1.0f,
997 (s,cf,p,v) => { s.m_params[0].angularSleepingThreshold = cf.GetFloat(p, v); }, 1125 (s,cf,p,v) => { s.m_params[0].angularSleepingThreshold = cf.GetFloat(p, v); },
998 (s) => { return s.m_params[0].angularSleepingThreshold; }, 1126 (s) => { return s.m_params[0].angularSleepingThreshold; },
999 (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].angularSleepingThreshold, p, l, v); } ), 1127 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularSleepingThreshold, p, l, v); },
1128 (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ),
1000 new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" , 1129 new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" ,
1001 0f, // set to zero to disable 1130 0f, // set to zero to disable
1002 (s,cf,p,v) => { s.m_params[0].ccdMotionThreshold = cf.GetFloat(p, v); }, 1131 (s,cf,p,v) => { s.m_params[0].ccdMotionThreshold = cf.GetFloat(p, v); },
1003 (s) => { return s.m_params[0].ccdMotionThreshold; }, 1132 (s) => { return s.m_params[0].ccdMotionThreshold; },
1004 (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].ccdMotionThreshold, p, l, v); } ), 1133 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdMotionThreshold, p, l, v); },
1134 (s,o,v) => { BulletSimAPI.SetCcdMotionThreshold2(o.PhysBody.ptr, v); } ),
1005 new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" , 1135 new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" ,
1006 0f, 1136 0f,
1007 (s,cf,p,v) => { s.m_params[0].ccdSweptSphereRadius = cf.GetFloat(p, v); }, 1137 (s,cf,p,v) => { s.m_params[0].ccdSweptSphereRadius = cf.GetFloat(p, v); },
1008 (s) => { return s.m_params[0].ccdSweptSphereRadius; }, 1138 (s) => { return s.m_params[0].ccdSweptSphereRadius; },
1009 (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); } ), 1139 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); },
1140 (s,o,v) => { BulletSimAPI.SetCcdSweptSphereRadius2(o.PhysBody.ptr, v); } ),
1010 new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" , 1141 new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" ,
1011 0.1f, 1142 0.1f,
1012 (s,cf,p,v) => { s.m_params[0].contactProcessingThreshold = cf.GetFloat(p, v); }, 1143 (s,cf,p,v) => { s.m_params[0].contactProcessingThreshold = cf.GetFloat(p, v); },
1013 (s) => { return s.m_params[0].contactProcessingThreshold; }, 1144 (s) => { return s.m_params[0].contactProcessingThreshold; },
1014 (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].contactProcessingThreshold, p, l, v); } ), 1145 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); },
1146 (s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.PhysBody.ptr, v); } ),
1015 1147
1016 new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , 1148 new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" ,
1017 0.5f, 1149 0.5f,
1018 (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); }, 1150 (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); },
1019 (s) => { return s.m_params[0].terrainFriction; }, 1151 (s) => { return s.m_params[0].terrainFriction; },
1020 (s,p,l,v) => { s.m_params[0].terrainFriction = v; s.TaintedUpdateParameter(p,l,v); } ), 1152 (s,p,l,v) => { s.m_params[0].terrainFriction = v; /* TODO: set on real terrain */} ),
1021 new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" , 1153 new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" ,
1022 0.8f, 1154 0.8f,
1023 (s,cf,p,v) => { s.m_params[0].terrainHitFraction = cf.GetFloat(p, v); }, 1155 (s,cf,p,v) => { s.m_params[0].terrainHitFraction = cf.GetFloat(p, v); },
1024 (s) => { return s.m_params[0].terrainHitFraction; }, 1156 (s) => { return s.m_params[0].terrainHitFraction; },
1025 (s,p,l,v) => { s.m_params[0].terrainHitFraction = v; s.TaintedUpdateParameter(p,l,v); } ), 1157 (s,p,l,v) => { s.m_params[0].terrainHitFraction = v; /* TODO: set on real terrain */ } ),
1026 new ParameterDefn("TerrainRestitution", "Bouncyness" , 1158 new ParameterDefn("TerrainRestitution", "Bouncyness" ,
1027 0f, 1159 0f,
1028 (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); }, 1160 (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); },
1029 (s) => { return s.m_params[0].terrainRestitution; }, 1161 (s) => { return s.m_params[0].terrainRestitution; },
1030 (s,p,l,v) => { s.m_params[0].terrainRestitution = v; s.TaintedUpdateParameter(p,l,v); } ), 1162 (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.", 1163 new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.",
1032 0.5f, 1164 0.2f,
1033 (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); }, 1165 (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); },
1034 (s) => { return s.m_params[0].avatarFriction; }, 1166 (s) => { return s.m_params[0].avatarFriction; },
1035 (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarFriction, p, l, v); } ), 1167 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ),
1168 new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.",
1169 10f,
1170 (s,cf,p,v) => { s.m_params[0].avatarStandingFriction = cf.GetFloat(p, v); },
1171 (s) => { return s.m_params[0].avatarStandingFriction; },
1172 (s,p,l,v) => { s.m_params[0].avatarStandingFriction = v; } ),
1036 new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.", 1173 new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.",
1037 60f, 1174 60f,
1038 (s,cf,p,v) => { s.m_params[0].avatarDensity = cf.GetFloat(p, v); }, 1175 (s,cf,p,v) => { s.m_params[0].avatarDensity = cf.GetFloat(p, v); },
1039 (s) => { return s.m_params[0].avatarDensity; }, 1176 (s) => { return s.m_params[0].avatarDensity; },
1040 (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarDensity, p, l, v); } ), 1177 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarDensity, p, l, v); } ),
1041 new ParameterDefn("AvatarRestitution", "Bouncyness. Changed on avatar recreation.", 1178 new ParameterDefn("AvatarRestitution", "Bouncyness. Changed on avatar recreation.",
1042 0f, 1179 0f,
1043 (s,cf,p,v) => { s.m_params[0].avatarRestitution = cf.GetFloat(p, v); }, 1180 (s,cf,p,v) => { s.m_params[0].avatarRestitution = cf.GetFloat(p, v); },
1044 (s) => { return s.m_params[0].avatarRestitution; }, 1181 (s) => { return s.m_params[0].avatarRestitution; },
1045 (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarRestitution, p, l, v); } ), 1182 (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", 1183 new ParameterDefn("AvatarCapsuleRadius", "Radius of space around an avatar",
1047 0.37f, 1184 0.37f,
1048 (s,cf,p,v) => { s.m_params[0].avatarCapsuleRadius = cf.GetFloat(p, v); }, 1185 (s,cf,p,v) => { s.m_params[0].avatarCapsuleRadius = cf.GetFloat(p, v); },
1049 (s) => { return s.m_params[0].avatarCapsuleRadius; }, 1186 (s) => { return s.m_params[0].avatarCapsuleRadius; },
1050 (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarCapsuleRadius, p, l, v); } ), 1187 (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", 1188 new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar",
1052 1.5f, 1189 1.5f,
1053 (s,cf,p,v) => { s.m_params[0].avatarCapsuleHeight = cf.GetFloat(p, v); }, 1190 (s,cf,p,v) => { s.m_params[0].avatarCapsuleHeight = cf.GetFloat(p, v); },
1054 (s) => { return s.m_params[0].avatarCapsuleHeight; }, 1191 (s) => { return s.m_params[0].avatarCapsuleHeight; },
1055 (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarCapsuleHeight, p, l, v); } ), 1192 (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", 1193 new ParameterDefn("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions",
1057 0.1f, 1194 0.1f,
1058 (s,cf,p,v) => { s.m_params[0].avatarContactProcessingThreshold = cf.GetFloat(p, v); }, 1195 (s,cf,p,v) => { s.m_params[0].avatarContactProcessingThreshold = cf.GetFloat(p, v); },
1059 (s) => { return s.m_params[0].avatarContactProcessingThreshold; }, 1196 (s) => { return s.m_params[0].avatarContactProcessingThreshold; },
1060 (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ), 1197 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ),
1061 1198
1062 1199
1063 new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)", 1200 new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)",
1064 0f, // zero to disable 1201 0f,
1065 (s,cf,p,v) => { s.m_params[0].maxPersistantManifoldPoolSize = cf.GetFloat(p, v); }, 1202 (s,cf,p,v) => { s.m_params[0].maxPersistantManifoldPoolSize = cf.GetFloat(p, v); },
1066 (s) => { return s.m_params[0].maxPersistantManifoldPoolSize; }, 1203 (s) => { return s.m_params[0].maxPersistantManifoldPoolSize; },
1067 (s,p,l,v) => { s.m_params[0].maxPersistantManifoldPoolSize = v; } ), 1204 (s,p,l,v) => { s.m_params[0].maxPersistantManifoldPoolSize = v; } ),
1068 new ParameterDefn("MaxCollisionAlgorithmPoolSize", "Number of collisions pooled (0 means default of 4096)", 1205 new ParameterDefn("MaxCollisionAlgorithmPoolSize", "Number of collisions pooled (0 means default of 4096)",
1069 0f, // zero to disable 1206 0f,
1070 (s,cf,p,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = cf.GetFloat(p, v); }, 1207 (s,cf,p,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = cf.GetFloat(p, v); },
1071 (s) => { return s.m_params[0].maxCollisionAlgorithmPoolSize; }, 1208 (s) => { return s.m_params[0].maxCollisionAlgorithmPoolSize; },
1072 (s,p,l,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = v; } ), 1209 (s,p,l,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = v; } ),
@@ -1081,12 +1218,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters
1081 (s) => { return s.m_params[0].shouldForceUpdateAllAabbs; }, 1218 (s) => { return s.m_params[0].shouldForceUpdateAllAabbs; },
1082 (s,p,l,v) => { s.m_params[0].shouldForceUpdateAllAabbs = v; } ), 1219 (s,p,l,v) => { s.m_params[0].shouldForceUpdateAllAabbs = v; } ),
1083 new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction", 1220 new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction",
1084 ConfigurationParameters.numericFalse, 1221 ConfigurationParameters.numericTrue,
1085 (s,cf,p,v) => { s.m_params[0].shouldRandomizeSolverOrder = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, 1222 (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; }, 1223 (s) => { return s.m_params[0].shouldRandomizeSolverOrder; },
1087 (s,p,l,v) => { s.m_params[0].shouldRandomizeSolverOrder = v; } ), 1224 (s,p,l,v) => { s.m_params[0].shouldRandomizeSolverOrder = v; } ),
1088 new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands", 1225 new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands",
1089 ConfigurationParameters.numericFalse, 1226 ConfigurationParameters.numericTrue,
1090 (s,cf,p,v) => { s.m_params[0].shouldSplitSimulationIslands = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, 1227 (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; }, 1228 (s) => { return s.m_params[0].shouldSplitSimulationIslands; },
1092 (s,p,l,v) => { s.m_params[0].shouldSplitSimulationIslands = v; } ), 1229 (s,p,l,v) => { s.m_params[0].shouldSplitSimulationIslands = v; } ),
@@ -1101,6 +1238,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
1101 (s) => { return s.m_params[0].numberOfSolverIterations; }, 1238 (s) => { return s.m_params[0].numberOfSolverIterations; },
1102 (s,p,l,v) => { s.m_params[0].numberOfSolverIterations = v; } ), 1239 (s,p,l,v) => { s.m_params[0].numberOfSolverIterations = v; } ),
1103 1240
1241 new ParameterDefn("LinksetImplementation", "Type of linkset implementation (0=Constraint, 1=Compound, 2=Manual)",
1242 (float)BSLinkset.LinksetImplementation.Compound,
1243 (s,cf,p,v) => { s.m_params[0].linksetImplementation = cf.GetFloat(p,v); },
1244 (s) => { return s.m_params[0].linksetImplementation; },
1245 (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.", 1246 new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.",
1105 ConfigurationParameters.numericFalse, 1247 ConfigurationParameters.numericFalse,
1106 (s,cf,p,v) => { s.m_params[0].linkConstraintUseFrameOffset = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, 1248 (s,cf,p,v) => { s.m_params[0].linkConstraintUseFrameOffset = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
@@ -1121,28 +1263,27 @@ public class BSScene : PhysicsScene, IPhysicsParameters
1121 (s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = cf.GetFloat(p, v); }, 1263 (s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = cf.GetFloat(p, v); },
1122 (s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; }, 1264 (s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; },
1123 (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ), 1265 (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", 1266 new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1",
1125 0.0f, 1267 0.1f,
1126 (s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); }, 1268 (s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); },
1127 (s) => { return s.m_params[0].linkConstraintCFM; }, 1269 (s) => { return s.m_params[0].linkConstraintCFM; },
1128 (s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ), 1270 (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", 1271 new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2",
1130 0.2f, 1272 0.1f,
1131 (s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); }, 1273 (s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); },
1132 (s) => { return s.m_params[0].linkConstraintERP; }, 1274 (s) => { return s.m_params[0].linkConstraintERP; },
1133 (s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ), 1275 (s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ),
1276 new ParameterDefn("LinkConstraintSolverIterations", "Number of solver iterations when computing constraint. (0 = Bullet default)",
1277 40,
1278 (s,cf,p,v) => { s.m_params[0].linkConstraintSolverIterations = cf.GetFloat(p, v); },
1279 (s) => { return s.m_params[0].linkConstraintSolverIterations; },
1280 (s,p,l,v) => { s.m_params[0].linkConstraintSolverIterations = v; } ),
1134 1281
1135 new ParameterDefn("DetailedStats", "Frames between outputting detailed phys stats. (0 is off)", 1282 new ParameterDefn("LogPhysicsStatisticsFrames", "Frames between outputting detailed phys stats. (0 is off)",
1136 0f, 1283 0f,
1137 (s,cf,p,v) => { s.m_detailedStatsStep = cf.GetInt(p, (int)v); }, 1284 (s,cf,p,v) => { s.m_params[0].physicsLoggingFrames = cf.GetInt(p, (int)v); },
1138 (s) => { return (float)s.m_detailedStatsStep; }, 1285 (s) => { return (float)s.m_params[0].physicsLoggingFrames; },
1139 (s,p,l,v) => { s.m_detailedStatsStep = (int)v; } ), 1286 (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 }; 1287 };
1147 1288
1148 // Convert a boolean to our numeric true and false values 1289 // Convert a boolean to our numeric true and false values
@@ -1200,11 +1341,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters
1200 1341
1201 private PhysParameterEntry[] SettableParameters = new PhysParameterEntry[1]; 1342 private PhysParameterEntry[] SettableParameters = new PhysParameterEntry[1];
1202 1343
1344 // This creates an array in the correct format for returning the list of
1345 // parameters. This is used by the 'list' option of the 'physics' command.
1203 private void BuildParameterTable() 1346 private void BuildParameterTable()
1204 { 1347 {
1205 if (SettableParameters.Length < ParameterDefinitions.Length) 1348 if (SettableParameters.Length < ParameterDefinitions.Length)
1206 { 1349 {
1207
1208 List<PhysParameterEntry> entries = new List<PhysParameterEntry>(); 1350 List<PhysParameterEntry> entries = new List<PhysParameterEntry>();
1209 for (int ii = 0; ii < ParameterDefinitions.Length; ii++) 1351 for (int ii = 0; ii < ParameterDefinitions.Length; ii++)
1210 { 1352 {
@@ -1249,60 +1391,54 @@ public class BSScene : PhysicsScene, IPhysicsParameters
1249 return ret; 1391 return ret;
1250 } 1392 }
1251 1393
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 1394 // update all the localIDs specified
1269 // If the local ID is APPLY_TO_NONE, just change the default value 1395 // 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 1396 // 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 1397 // 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) 1398 private void UpdateParameterObject(ref float defaultLoc, string parm, uint localID, float val)
1273 { 1399 {
1400 List<uint> objectIDs = new List<uint>();
1274 switch (localID) 1401 switch (localID)
1275 { 1402 {
1276 case PhysParameterEntry.APPLY_TO_NONE: 1403 case PhysParameterEntry.APPLY_TO_NONE:
1277 defaultLoc = val; // setting only the default value 1404 defaultLoc = val; // setting only the default value
1405 // This will cause a call into the physical world if some operation is specified (SetOnObject).
1406 objectIDs.Add(TERRAIN_ID);
1407 TaintedUpdateParameter(parm, objectIDs, val);
1278 break; 1408 break;
1279 case PhysParameterEntry.APPLY_TO_ALL: 1409 case PhysParameterEntry.APPLY_TO_ALL:
1280 defaultLoc = val; // setting ALL also sets the default value 1410 defaultLoc = val; // setting ALL also sets the default value
1281 List<uint> objectIDs = lIDs; 1411 lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys);
1282 string xparm = parm.ToLower(); 1412 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; 1413 break;
1291 default: 1414 default:
1292 // setting only one localID 1415 // setting only one localID
1293 TaintedUpdateParameter(parm, localID, val); 1416 objectIDs.Add(localID);
1417 TaintedUpdateParameter(parm, objectIDs, val);
1294 break; 1418 break;
1295 } 1419 }
1296 } 1420 }
1297 1421
1298 // schedule the actual updating of the paramter to when the phys engine is not busy 1422 // 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) 1423 private void TaintedUpdateParameter(string parm, List<uint> lIDs, float val)
1300 { 1424 {
1301 uint xlocalID = localID;
1302 string xparm = parm.ToLower();
1303 float xval = val; 1425 float xval = val;
1304 TaintedObject("BSScene.TaintedUpdateParameter", delegate() { 1426 List<uint> xlIDs = lIDs;
1305 BulletSimAPI.UpdateParameter(m_worldID, xlocalID, xparm, xval); 1427 string xparm = parm;
1428 TaintedObject("BSScene.UpdateParameterSet", delegate() {
1429 ParameterDefn thisParam;
1430 if (TryGetParameter(xparm, out thisParam))
1431 {
1432 if (thisParam.onObject != null)
1433 {
1434 foreach (uint lID in xlIDs)
1435 {
1436 BSPhysObject theObject = null;
1437 PhysObjects.TryGetValue(lID, out theObject);
1438 thisParam.onObject(this, theObject, xval);
1439 }
1440 }
1441 }
1306 }); 1442 });
1307 } 1443 }
1308 1444
@@ -1326,12 +1462,24 @@ public class BSScene : PhysicsScene, IPhysicsParameters
1326 1462
1327 #endregion Runtime settable parameters 1463 #endregion Runtime settable parameters
1328 1464
1465 // Debugging routine for dumping detailed physical information for vehicle prims
1466 private void DumpVehicles()
1467 {
1468 foreach (BSPrim prim in m_vehicles)
1469 {
1470 BulletSimAPI.DumpRigidBody2(World.ptr, prim.PhysBody.ptr);
1471 BulletSimAPI.DumpCollisionShape2(World.ptr, prim.PhysShape.ptr);
1472 }
1473 }
1474
1329 // Invoke the detailed logger and output something if it's enabled. 1475 // Invoke the detailed logger and output something if it's enabled.
1330 public void DetailLog(string msg, params Object[] args) 1476 public void DetailLog(string msg, params Object[] args)
1331 { 1477 {
1332 PhysicsLogging.Write(msg, args); 1478 PhysicsLogging.Write(msg, args);
1479 // Add the Flush() if debugging crashes. Gets all the messages written out.
1480 // PhysicsLogging.Flush();
1333 } 1481 }
1334 // used to fill in the LocalID when there isn't one 1482 // Used to fill in the LocalID when there isn't one. It's the correct number of characters.
1335 public const string DetailLogZero = "0000000000"; 1483 public const string DetailLogZero = "0000000000";
1336 1484
1337} 1485}