aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSScene.cs1283
1 files changed, 0 insertions, 1283 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
deleted file mode 100644
index 0bace14..0000000
--- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
+++ /dev/null
@@ -1,1283 +0,0 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Linq;
30using System.Reflection;
31using System.Runtime.InteropServices;
32using System.Text;
33using System.Threading;
34using OpenSim.Framework;
35using OpenSim.Framework.Monitoring;
36using OpenSim.Region.Framework;
37using OpenSim.Region.CoreModules;
38using Logging = OpenSim.Region.CoreModules.Framework.Statistics.Logging;
39using OpenSim.Region.Physics.Manager;
40using Nini.Config;
41using log4net;
42using OpenMetaverse;
43
44namespace OpenSim.Region.Physics.BulletSPlugin
45{
46public sealed class BSScene : PhysicsScene, IPhysicsParameters
47{
48 internal static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
49 internal static readonly string LogHeader = "[BULLETS SCENE]";
50
51 // The name of the region we're working for.
52 public string RegionName { get; private set; }
53
54 public string BulletSimVersion = "?";
55
56 // The handle to the underlying managed or unmanaged version of Bullet being used.
57 public string BulletEngineName { get; private set; }
58 public BSAPITemplate PE;
59
60 // If the physics engine is running on a separate thread
61 public Thread m_physicsThread;
62
63 public Dictionary<uint, BSPhysObject> PhysObjects;
64 public BSShapeCollection Shapes;
65
66 // Keeping track of the objects with collisions so we can report begin and end of a collision
67 public HashSet<BSPhysObject> ObjectsWithCollisions = new HashSet<BSPhysObject>();
68 public HashSet<BSPhysObject> ObjectsWithNoMoreCollisions = new HashSet<BSPhysObject>();
69
70 // All the collision processing is protected with this lock object
71 public Object CollisionLock = new Object();
72
73 // Properties are updated here
74 public Object UpdateLock = new Object();
75 public HashSet<BSPhysObject> ObjectsWithUpdates = new HashSet<BSPhysObject>();
76
77 // Keep track of all the avatars so we can send them a collision event
78 // every tick so OpenSim will update its animation.
79 private HashSet<BSPhysObject> AvatarsInScene = new HashSet<BSPhysObject>();
80 private Object AvatarsInSceneLock = new Object();
81
82 // let my minuions use my logger
83 public ILog Logger { get { return m_log; } }
84
85 public IMesher mesher;
86 public uint WorldID { get; private set; }
87 public BulletWorld World { get; private set; }
88
89 // All the constraints that have been allocated in this instance.
90 public BSConstraintCollection Constraints { get; private set; }
91
92 // Simulation parameters
93 //internal float m_physicsStepTime; // if running independently, the interval simulated by default
94
95 internal int m_maxSubSteps;
96 internal float m_fixedTimeStep;
97
98 internal float m_simulatedTime; // the time simulated previously. Used for physics framerate calc.
99
100 internal long m_simulationStep = 0; // The current simulation step.
101 public long SimulationStep { get { return m_simulationStep; } }
102 // A number to use for SimulationStep that is probably not any step value
103 // Used by the collision code (which remembers the step when a collision happens) to remember not any simulation step.
104 public static long NotASimulationStep = -1234;
105
106 internal float LastTimeStep { get; private set; } // The simulation time from the last invocation of Simulate()
107
108 internal float NominalFrameRate { get; set; } // Parameterized ideal frame rate that simulation is scaled to
109
110 // Physical objects can register for prestep or poststep events
111 public delegate void PreStepAction(float timeStep);
112 public delegate void PostStepAction(float timeStep);
113 public event PreStepAction BeforeStep;
114 public event PostStepAction AfterStep;
115
116 // A value of the time 'now' so all the collision and update routines do not have to get their own
117 // Set to 'now' just before all the prims and actors are called for collisions and updates
118 public int SimulationNowTime { get; private set; }
119
120 // True if initialized and ready to do simulation steps
121 private bool m_initialized = false;
122
123 // Flag which is true when processing taints.
124 // Not guaranteed to be correct all the time (don't depend on this) but good for debugging.
125 public bool InTaintTime { get; private set; }
126
127 // Pinned memory used to pass step information between managed and unmanaged
128 internal int m_maxCollisionsPerFrame;
129 internal CollisionDesc[] m_collisionArray;
130
131 internal int m_maxUpdatesPerFrame;
132 internal EntityProperties[] m_updateArray;
133
134 /// <summary>
135 /// Used to control physics simulation timing if Bullet is running on its own thread.
136 /// </summary>
137 private ManualResetEvent m_updateWaitEvent;
138
139 public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero
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 public float SimpleWaterLevel { get; set; }
144 public BSTerrainManager TerrainManager { get; private set; }
145
146 public ConfigurationParameters Params
147 {
148 get { return UnmanagedParams[0]; }
149 }
150 public Vector3 DefaultGravity
151 {
152 get { return new Vector3(0f, 0f, Params.gravity); }
153 }
154 // Just the Z value of the gravity
155 public float DefaultGravityZ
156 {
157 get { return Params.gravity; }
158 }
159
160 // When functions in the unmanaged code must be called, it is only
161 // done at a known time just before the simulation step. The taint
162 // system saves all these function calls and executes them in
163 // order before the simulation.
164 public delegate void TaintCallback();
165 private struct TaintCallbackEntry
166 {
167 public String originator;
168 public String ident;
169 public TaintCallback callback;
170 public TaintCallbackEntry(string pIdent, TaintCallback pCallBack)
171 {
172 originator = BSScene.DetailLogZero;
173 ident = pIdent;
174 callback = pCallBack;
175 }
176 public TaintCallbackEntry(string pOrigin, string pIdent, TaintCallback pCallBack)
177 {
178 originator = pOrigin;
179 ident = pIdent;
180 callback = pCallBack;
181 }
182 }
183 private Object _taintLock = new Object(); // lock for using the next object
184 private List<TaintCallbackEntry> _taintOperations;
185 private Dictionary<string, TaintCallbackEntry> _postTaintOperations;
186 private List<TaintCallbackEntry> _postStepOperations;
187
188 // A pointer to an instance if this structure is passed to the C++ code
189 // Used to pass basic configuration values to the unmanaged code.
190 internal ConfigurationParameters[] UnmanagedParams;
191
192 // Sometimes you just have to log everything.
193 public Logging.LogWriter PhysicsLogging;
194 private bool m_physicsLoggingEnabled;
195 private string m_physicsLoggingDir;
196 private string m_physicsLoggingPrefix;
197 private int m_physicsLoggingFileMinutes;
198 private bool m_physicsLoggingDoFlush;
199 private bool m_physicsPhysicalDumpEnabled;
200 public int PhysicsMetricDumpFrames { get; set; }
201 // 'true' of the vehicle code is to log lots of details
202 public bool VehicleLoggingEnabled { get; private set; }
203 public bool VehiclePhysicalLoggingEnabled { get; private set; }
204
205 #region Construction and Initialization
206 public BSScene(string engineType, string identifier)
207 {
208 m_initialized = false;
209
210 // The name of the region we're working for is passed to us. Keep for identification.
211 RegionName = identifier;
212
213 // Set identifying variables in the PhysicsScene interface.
214 EngineType = engineType;
215 Name = EngineType + "/" + RegionName;
216 }
217
218 // Old version of initialization that assumes legacy sized regions (256x256)
219 public override void Initialise(IMesher meshmerizer, IConfigSource config)
220 {
221 m_log.ErrorFormat("{0} WARNING WARNING WARNING! BulletSim initialized without region extent specification. Terrain will be messed up.");
222 Vector3 regionExtent = new Vector3( Constants.RegionSize, Constants.RegionSize, Constants.RegionSize);
223 Initialise(meshmerizer, config, regionExtent);
224
225 }
226
227 public override void Initialise(IMesher meshmerizer, IConfigSource config, Vector3 regionExtent)
228 {
229 mesher = meshmerizer;
230 _taintOperations = new List<TaintCallbackEntry>();
231 _postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
232 _postStepOperations = new List<TaintCallbackEntry>();
233 PhysObjects = new Dictionary<uint, BSPhysObject>();
234 Shapes = new BSShapeCollection(this);
235
236 m_simulatedTime = 0f;
237 LastTimeStep = 0.1f;
238
239 // Allocate pinned memory to pass parameters.
240 UnmanagedParams = new ConfigurationParameters[1];
241
242 // Set default values for physics parameters plus any overrides from the ini file
243 GetInitialParameterValues(config);
244
245 // Force some parameters to values depending on other configurations
246 // Only use heightmap terrain implementation if terrain larger than legacy size
247 if ((uint)regionExtent.X > Constants.RegionSize || (uint)regionExtent.Y > Constants.RegionSize)
248 {
249 m_log.WarnFormat("{0} Forcing terrain implementation to heightmap for large region", LogHeader);
250 BSParam.TerrainImplementation = (float)BSTerrainPhys.TerrainImplementation.Heightmap;
251 }
252
253 // Get the connection to the physics engine (could be native or one of many DLLs)
254 PE = SelectUnderlyingBulletEngine(BulletEngineName);
255
256 // Enable very detailed logging.
257 // By creating an empty logger when not logging, the log message invocation code
258 // can be left in and every call doesn't have to check for null.
259 if (m_physicsLoggingEnabled)
260 {
261 PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes, m_physicsLoggingDoFlush);
262 PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output its own error messages.
263 }
264 else
265 {
266 PhysicsLogging = new Logging.LogWriter();
267 }
268
269 // Allocate memory for returning of the updates and collisions from the physics engine
270 m_collisionArray = new CollisionDesc[m_maxCollisionsPerFrame];
271 m_updateArray = new EntityProperties[m_maxUpdatesPerFrame];
272
273 // The bounding box for the simulated world. The origin is 0,0,0 unless we're
274 // a child in a mega-region.
275 // Bullet actually doesn't care about the extents of the simulated
276 // area. It tracks active objects no matter where they are.
277 Vector3 worldExtent = regionExtent;
278
279 World = PE.Initialize(worldExtent, Params, m_maxCollisionsPerFrame, ref m_collisionArray, m_maxUpdatesPerFrame, ref m_updateArray);
280
281 Constraints = new BSConstraintCollection(World);
282
283 TerrainManager = new BSTerrainManager(this, worldExtent);
284 TerrainManager.CreateInitialGroundPlaneAndTerrain();
285
286 // Put some informational messages into the log file.
287 m_log.InfoFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation);
288
289 InTaintTime = false;
290 m_initialized = true;
291
292 // If the physics engine runs on its own thread, start same.
293 if (BSParam.UseSeparatePhysicsThread)
294 {
295 // The physics simulation should happen independently of the heartbeat loop
296 m_physicsThread
297 = WorkManager.StartThread(
298 BulletSPluginPhysicsThread,
299 string.Format("{0} ({1})", BulletEngineName, RegionName),
300 ThreadPriority.Normal,
301 true,
302 true);
303 }
304 }
305
306 // All default parameter values are set here. There should be no values set in the
307 // variable definitions.
308 private void GetInitialParameterValues(IConfigSource config)
309 {
310 ConfigurationParameters parms = new ConfigurationParameters();
311 UnmanagedParams[0] = parms;
312
313 BSParam.SetParameterDefaultValues(this);
314
315 if (config != null)
316 {
317 // If there are specifications in the ini file, use those values
318 IConfig pConfig = config.Configs["BulletSim"];
319 if (pConfig != null)
320 {
321 BSParam.SetParameterConfigurationValues(this, pConfig);
322
323 // There are two Bullet implementations to choose from
324 BulletEngineName = pConfig.GetString("BulletEngine", "BulletUnmanaged");
325
326 // Very detailed logging for physics debugging
327 // TODO: the boolean values can be moved to the normal parameter processing.
328 m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false);
329 m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", ".");
330 m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-");
331 m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5);
332 m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false);
333 m_physicsPhysicalDumpEnabled = pConfig.GetBoolean("PhysicsPhysicalDumpEnabled", false);
334 // Very detailed logging for vehicle debugging
335 VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
336 VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false);
337
338 // Do any replacements in the parameters
339 m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
340 }
341 else
342 {
343 // Nothing in the configuration INI file so assume unmanaged and other defaults.
344 BulletEngineName = "BulletUnmanaged";
345 m_physicsLoggingEnabled = false;
346 VehicleLoggingEnabled = false;
347 }
348
349 // The material characteristics.
350 BSMaterials.InitializeFromDefaults(Params);
351 if (pConfig != null)
352 {
353 // Let the user add new and interesting material property values.
354 BSMaterials.InitializefromParameters(pConfig);
355 }
356 }
357 }
358
359 // A helper function that handles a true/false parameter and returns the proper float number encoding
360 float ParamBoolean(IConfig config, string parmName, float deflt)
361 {
362 float ret = deflt;
363 if (config.Contains(parmName))
364 {
365 ret = ConfigurationParameters.numericFalse;
366 if (config.GetBoolean(parmName, false))
367 {
368 ret = ConfigurationParameters.numericTrue;
369 }
370 }
371 return ret;
372 }
373
374 // Select the connection to the actual Bullet implementation.
375 // The main engine selection is the engineName up to the first hypen.
376 // So "Bullet-2.80-OpenCL-Intel" specifies the 'bullet' class here and the whole name
377 // is passed to the engine to do its special selection, etc.
378 private BSAPITemplate SelectUnderlyingBulletEngine(string engineName)
379 {
380 // For the moment, do a simple switch statement.
381 // Someday do fancyness with looking up the interfaces in the assembly.
382 BSAPITemplate ret = null;
383
384 string selectionName = engineName.ToLower();
385 int hyphenIndex = engineName.IndexOf("-");
386 if (hyphenIndex > 0)
387 selectionName = engineName.ToLower().Substring(0, hyphenIndex - 1);
388
389 switch (selectionName)
390 {
391 case "bullet":
392 case "bulletunmanaged":
393 ret = new BSAPIUnman(engineName, this);
394 break;
395 case "bulletxna":
396 ret = new BSAPIXNA(engineName, this);
397 // Disable some features that are not implemented in BulletXNA
398 m_log.InfoFormat("{0} Disabling some physics features not implemented by BulletXNA", LogHeader);
399 m_log.InfoFormat("{0} Disabling ShouldUseBulletHACD", LogHeader);
400 BSParam.ShouldUseBulletHACD = false;
401 m_log.InfoFormat("{0} Disabling ShouldUseSingleConvexHullForPrims", LogHeader);
402 BSParam.ShouldUseSingleConvexHullForPrims = false;
403 m_log.InfoFormat("{0} Disabling ShouldUseGImpactShapeForPrims", LogHeader);
404 BSParam.ShouldUseGImpactShapeForPrims = false;
405 m_log.InfoFormat("{0} Setting terrain implimentation to Heightmap", LogHeader);
406 BSParam.TerrainImplementation = (float)BSTerrainPhys.TerrainImplementation.Heightmap;
407 break;
408 }
409
410 if (ret == null)
411 {
412 m_log.ErrorFormat("{0} COULD NOT SELECT BULLET ENGINE: '[BulletSim]PhysicsEngine' must be either 'BulletUnmanaged-*' or 'BulletXNA-*'", LogHeader);
413 }
414 else
415 {
416 m_log.InfoFormat("{0} Selected bullet engine {1} -> {2}/{3}", LogHeader, engineName, ret.BulletEngineName, ret.BulletEngineVersion);
417 }
418
419 return ret;
420 }
421
422 public override void Dispose()
423 {
424 // m_log.DebugFormat("{0}: Dispose()", LogHeader);
425
426 // make sure no stepping happens while we're deleting stuff
427 m_initialized = false;
428
429 lock (PhysObjects)
430 {
431 foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
432 {
433 kvp.Value.Destroy();
434 }
435 PhysObjects.Clear();
436 }
437
438 // Now that the prims are all cleaned up, there should be no constraints left
439 if (Constraints != null)
440 {
441 Constraints.Dispose();
442 Constraints = null;
443 }
444
445 if (Shapes != null)
446 {
447 Shapes.Dispose();
448 Shapes = null;
449 }
450
451 if (TerrainManager != null)
452 {
453 TerrainManager.ReleaseGroundPlaneAndTerrain();
454 TerrainManager.Dispose();
455 TerrainManager = null;
456 }
457
458 // Anything left in the unmanaged code should be cleaned out
459 PE.Shutdown(World);
460
461 // Not logging any more
462 PhysicsLogging.Close();
463 }
464 #endregion // Construction and Initialization
465
466 #region Prim and Avatar addition and removal
467
468 public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 velocity, Vector3 size, bool isFlying)
469 {
470 m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader);
471 return null;
472 }
473
474 public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 velocity, Vector3 size, bool isFlying)
475 {
476 // m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName);
477
478 if (!m_initialized) return null;
479
480 BSCharacter actor = new BSCharacter(localID, avName, this, position, velocity, size, isFlying);
481 lock (PhysObjects)
482 PhysObjects.Add(localID, actor);
483
484 // TODO: Remove kludge someday.
485 // We must generate a collision for avatars whether they collide or not.
486 // This is required by OpenSim to update avatar animations, etc.
487 lock (AvatarsInSceneLock)
488 AvatarsInScene.Add(actor);
489
490 return actor;
491 }
492
493 public override void RemoveAvatar(PhysicsActor actor)
494 {
495 // m_log.DebugFormat("{0}: RemoveAvatar", LogHeader);
496
497 if (!m_initialized) return;
498
499 BSCharacter bsactor = actor as BSCharacter;
500 if (bsactor != null)
501 {
502 try
503 {
504 lock (PhysObjects)
505 PhysObjects.Remove(bsactor.LocalID);
506 // Remove kludge someday
507 lock (AvatarsInSceneLock)
508 AvatarsInScene.Remove(bsactor);
509 }
510 catch (Exception e)
511 {
512 m_log.WarnFormat("{0}: Attempt to remove avatar that is not in physics scene: {1}", LogHeader, e);
513 }
514 bsactor.Destroy();
515 // bsactor.dispose();
516 }
517 else
518 {
519 m_log.ErrorFormat("{0}: Requested to remove avatar that is not a BSCharacter. ID={1}, type={2}",
520 LogHeader, actor.LocalID, actor.GetType().Name);
521 }
522 }
523
524 public override void RemovePrim(PhysicsActor prim)
525 {
526 if (!m_initialized) return;
527
528 BSPhysObject bsprim = prim as BSPhysObject;
529 if (bsprim != null)
530 {
531 DetailLog("{0},RemovePrim,call", bsprim.LocalID);
532 // m_log.DebugFormat("{0}: RemovePrim. id={1}/{2}", LogHeader, bsprim.Name, bsprim.LocalID);
533 try
534 {
535 lock (PhysObjects) PhysObjects.Remove(bsprim.LocalID);
536 }
537 catch (Exception e)
538 {
539 m_log.ErrorFormat("{0}: Attempt to remove prim that is not in physics scene: {1}", LogHeader, e);
540 }
541 bsprim.Destroy();
542 // bsprim.dispose();
543 }
544 else
545 {
546 m_log.ErrorFormat("{0}: Attempt to remove prim that is not a BSPrim type.", LogHeader);
547 }
548 }
549
550 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
551 Vector3 size, Quaternion rotation, bool isPhysical, uint localID)
552 {
553 // m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName);
554
555 if (!m_initialized) return null;
556
557 // DetailLog("{0},BSScene.AddPrimShape,call", localID);
558
559 BSPhysObject prim = new BSPrimLinkable(localID, primName, this, position, size, rotation, pbs, isPhysical);
560 lock (PhysObjects) PhysObjects.Add(localID, prim);
561 return prim;
562 }
563
564 // This is a call from the simulator saying that some physical property has been updated.
565 // The BulletSim driver senses the changing of relevant properties so this taint
566 // information call is not needed.
567 public override void AddPhysicsActorTaint(PhysicsActor prim) { }
568
569 #endregion // Prim and Avatar addition and removal
570
571 #region Simulation
572
573 // Call from the simulator to send physics information to the simulator objects.
574 // This pushes all the collision and property update events into the objects in
575 // the simulator and, since it is on the heartbeat thread, there is an implicit
576 // locking of those data structures from other heartbeat events.
577 // If the physics engine is running on a separate thread, the update information
578 // will be in the ObjectsWithCollions and ObjectsWithUpdates structures.
579 public override float Simulate(float timeStep)
580 {
581 if (!BSParam.UseSeparatePhysicsThread)
582 {
583 DoPhysicsStep(timeStep);
584 }
585 return SendUpdatesToSimulator(timeStep);
586 }
587
588 // Call the physics engine to do one 'timeStep' and collect collisions and updates
589 // into ObjectsWithCollisions and ObjectsWithUpdates data structures.
590 private void DoPhysicsStep(float timeStep)
591 {
592 // prevent simulation until we've been initialized
593 if (!m_initialized) return;
594
595 LastTimeStep = timeStep;
596
597 int updatedEntityCount = 0;
598 int collidersCount = 0;
599
600 int beforeTime = Util.EnvironmentTickCount();
601 int simTime = 0;
602
603 int numTaints = _taintOperations.Count;
604 InTaintTime = true; // Only used for debugging so locking is not necessary.
605
606 // update the prim states while we know the physics engine is not busy
607 ProcessTaints();
608
609 // Some of the physical objects requre individual, pre-step calls
610 // (vehicles and avatar movement, in particular)
611 TriggerPreStepEvent(timeStep);
612
613 // the prestep actions might have added taints
614 numTaints += _taintOperations.Count;
615 ProcessTaints();
616
617 InTaintTime = false; // Only used for debugging so locking is not necessary.
618
619 // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world.
620 // Only enable this in a limited test world with few objects.
621 if (m_physicsPhysicalDumpEnabled)
622 PE.DumpAllInfo(World);
623
624 // step the physical world one interval
625 m_simulationStep++;
626 int numSubSteps = 0;
627 try
628 {
629 numSubSteps = PE.PhysicsStep(World, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out collidersCount);
630
631 }
632 catch (Exception e)
633 {
634 m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}",
635 LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e);
636 DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}",
637 DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount);
638 updatedEntityCount = 0;
639 collidersCount = 0;
640 }
641
642 // Make the physics engine dump useful statistics periodically
643 if (PhysicsMetricDumpFrames != 0 && ((m_simulationStep % PhysicsMetricDumpFrames) == 0))
644 PE.DumpPhysicsStatistics(World);
645
646 // Get a value for 'now' so all the collision and update routines don't have to get their own.
647 SimulationNowTime = Util.EnvironmentTickCount();
648
649 // Send collision information to the colliding objects. The objects decide if the collision
650 // is 'real' (like linksets don't collide with themselves) and the individual objects
651 // know if the simulator has subscribed to collisions.
652 lock (CollisionLock)
653 {
654 if (collidersCount > 0)
655 {
656 lock (PhysObjects)
657 {
658 for (int ii = 0; ii < collidersCount; ii++)
659 {
660 uint cA = m_collisionArray[ii].aID;
661 uint cB = m_collisionArray[ii].bID;
662 Vector3 point = m_collisionArray[ii].point;
663 Vector3 normal = m_collisionArray[ii].normal;
664 float penetration = m_collisionArray[ii].penetration;
665 SendCollision(cA, cB, point, normal, penetration);
666 SendCollision(cB, cA, point, -normal, penetration);
667 }
668 }
669 }
670 }
671
672 // If any of the objects had updated properties, tell the managed objects about the update
673 // and remember that there was a change so it will be passed to the simulator.
674 lock (UpdateLock)
675 {
676 if (updatedEntityCount > 0)
677 {
678 lock (PhysObjects)
679 {
680 for (int ii = 0; ii < updatedEntityCount; ii++)
681 {
682 EntityProperties entprop = m_updateArray[ii];
683 BSPhysObject pobj;
684 if (PhysObjects.TryGetValue(entprop.ID, out pobj))
685 {
686 if (pobj.IsInitialized)
687 pobj.UpdateProperties(entprop);
688 }
689 }
690 }
691 }
692 }
693
694 // Some actors want to know when the simulation step is complete.
695 TriggerPostStepEvent(timeStep);
696
697 simTime = Util.EnvironmentTickCountSubtract(beforeTime);
698 if (PhysicsLogging.Enabled)
699 {
700 DetailLog("{0},DoPhysicsStep,complete,frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}",
701 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps,
702 updatedEntityCount, collidersCount, ObjectsWithCollisions.Count);
703 }
704
705 // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world.
706 // Only enable this in a limited test world with few objects.
707 if (m_physicsPhysicalDumpEnabled)
708 PE.DumpAllInfo(World);
709
710 // The physics engine returns the number of milliseconds it simulated this call.
711 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
712 // Multiply by a fixed nominal frame rate to give a rate similar to the simulator (usually 55).
713// m_simulatedTime += (float)numSubSteps * m_fixedTimeStep * 1000f * NominalFrameRate;
714 m_simulatedTime += (float)numSubSteps * m_fixedTimeStep;
715 }
716
717 // Called by a BSPhysObject to note that it has changed properties and this information
718 // should be passed up to the simulator at the proper time.
719 // Note: this is called by the BSPhysObject from invocation via DoPhysicsStep() above so
720 // this is is under UpdateLock.
721 public void PostUpdate(BSPhysObject updatee)
722 {
723 lock (UpdateLock)
724 {
725 ObjectsWithUpdates.Add(updatee);
726 }
727 }
728
729 // The simulator thinks it is physics time so return all the collisions and position
730 // updates that were collected in actual physics simulation.
731 private float SendUpdatesToSimulator(float timeStep)
732 {
733 if (!m_initialized) return 5.0f;
734
735 DetailLog("{0},SendUpdatesToSimulator,collisions={1},updates={2},simedTime={3}",
736 BSScene.DetailLogZero, ObjectsWithCollisions.Count, ObjectsWithUpdates.Count, m_simulatedTime);
737 // Push the collisions into the simulator.
738 lock (CollisionLock)
739 {
740 if (ObjectsWithCollisions.Count > 0)
741 {
742 foreach (BSPhysObject bsp in ObjectsWithCollisions)
743 if (!bsp.SendCollisions())
744 {
745 // If the object is done colliding, see that it's removed from the colliding list
746 ObjectsWithNoMoreCollisions.Add(bsp);
747 }
748 }
749
750 // This is a kludge to get avatar movement updates.
751 // The simulator expects collisions for avatars even if there are have been no collisions.
752 // The event updates avatar animations and stuff.
753 // If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
754 // Note that we get a copy of the list to search because SendCollision() can take a while.
755 HashSet<BSPhysObject> tempAvatarsInScene;
756 lock (AvatarsInSceneLock)
757 {
758 tempAvatarsInScene = new HashSet<BSPhysObject>(AvatarsInScene);
759 }
760 foreach (BSPhysObject actor in tempAvatarsInScene)
761 {
762 if (!ObjectsWithCollisions.Contains(actor)) // don't call avatars twice
763 actor.SendCollisions();
764 }
765 tempAvatarsInScene = null;
766
767 // Objects that are done colliding are removed from the ObjectsWithCollisions list.
768 // Not done above because it is inside an iteration of ObjectWithCollisions.
769 // This complex collision processing is required to create an empty collision
770 // event call after all real collisions have happened on an object. This allows
771 // the simulator to generate the 'collision end' event.
772 if (ObjectsWithNoMoreCollisions.Count > 0)
773 {
774 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
775 ObjectsWithCollisions.Remove(po);
776 ObjectsWithNoMoreCollisions.Clear();
777 }
778 }
779
780 // Call the simulator for each object that has physics property updates.
781 HashSet<BSPhysObject> updatedObjects = null;
782 lock (UpdateLock)
783 {
784 if (ObjectsWithUpdates.Count > 0)
785 {
786 updatedObjects = ObjectsWithUpdates;
787 ObjectsWithUpdates = new HashSet<BSPhysObject>();
788 }
789 }
790 if (updatedObjects != null)
791 {
792 foreach (BSPhysObject obj in updatedObjects)
793 {
794 obj.RequestPhysicsterseUpdate();
795 }
796 updatedObjects.Clear();
797 }
798
799 // Return the framerate simulated to give the above returned results.
800 // (Race condition here but this is just bookkeeping so rare mistakes do not merit a lock).
801 // undo math above
802 float simTime = m_simulatedTime / timeStep;
803 m_simulatedTime = 0f;
804 return simTime;
805 }
806
807 // Something has collided
808 private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration)
809 {
810 if (localID <= TerrainManager.HighestTerrainID)
811 {
812 return; // don't send collisions to the terrain
813 }
814
815 BSPhysObject collider;
816 // NOTE that PhysObjects was locked before the call to SendCollision().
817 if (!PhysObjects.TryGetValue(localID, out collider))
818 {
819 // If the object that is colliding cannot be found, just ignore the collision.
820 DetailLog("{0},BSScene.SendCollision,colliderNotInObjectList,id={1},with={2}", DetailLogZero, localID, collidingWith);
821 return;
822 }
823
824 // Note: the terrain is not in the physical object list so 'collidee' can be null when Collide() is called.
825 BSPhysObject collidee = null;
826 PhysObjects.TryGetValue(collidingWith, out collidee);
827
828 // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
829
830 if (collider.IsInitialized)
831 {
832 if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
833 {
834 // If a collision was 'good', remember to send it to the simulator
835 lock (CollisionLock)
836 {
837 ObjectsWithCollisions.Add(collider);
838 }
839 }
840 }
841
842 return;
843 }
844
845 public void BulletSPluginPhysicsThread()
846 {
847 Thread.CurrentThread.Priority = ThreadPriority.Highest;
848 m_updateWaitEvent = new ManualResetEvent(false);
849
850 while (m_initialized)
851 {
852 int beginSimulationRealtimeMS = Util.EnvironmentTickCount();
853
854 if (BSParam.Active)
855 DoPhysicsStep(BSParam.PhysicsTimeStep);
856
857 int simulationRealtimeMS = Util.EnvironmentTickCountSubtract(beginSimulationRealtimeMS);
858 int simulationTimeVsRealtimeDifferenceMS = ((int)(BSParam.PhysicsTimeStep*1000f)) - simulationRealtimeMS;
859
860 if (simulationTimeVsRealtimeDifferenceMS > 0)
861 {
862 // The simulation of the time interval took less than realtime.
863 // Do a wait for the rest of realtime.
864 m_updateWaitEvent.WaitOne(simulationTimeVsRealtimeDifferenceMS);
865 //Thread.Sleep(simulationTimeVsRealtimeDifferenceMS);
866 }
867 else
868 {
869 // The simulation took longer than realtime.
870 // Do some scaling of simulation time.
871 // TODO.
872 DetailLog("{0},BulletSPluginPhysicsThread,longerThanRealtime={1}", BSScene.DetailLogZero, simulationTimeVsRealtimeDifferenceMS);
873 }
874
875 Watchdog.UpdateThread();
876 }
877
878 Watchdog.RemoveThread();
879 }
880
881 #endregion // Simulation
882
883 public override void GetResults() { }
884
885 #region Terrain
886
887 public override void SetTerrain(float[] heightMap) {
888 TerrainManager.SetTerrain(heightMap);
889 }
890
891 public override void SetWaterLevel(float baseheight)
892 {
893 SimpleWaterLevel = baseheight;
894 }
895
896 public override void DeleteTerrain()
897 {
898 // m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader);
899 }
900
901 // Although no one seems to check this, I do support combining.
902 public override bool SupportsCombining()
903 {
904 return TerrainManager.SupportsCombining();
905 }
906 // This call says I am a child to region zero in a mega-region. 'pScene' is that
907 // of region zero, 'offset' is my offset from regions zero's origin, and
908 // 'extents' is the largest XY that is handled in my region.
909 public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
910 {
911 TerrainManager.Combine(pScene, offset, extents);
912 }
913
914 // Unhook all the combining that I know about.
915 public override void UnCombine(PhysicsScene pScene)
916 {
917 TerrainManager.UnCombine(pScene);
918 }
919
920 #endregion // Terrain
921
922 public override Dictionary<uint, float> GetTopColliders()
923 {
924 Dictionary<uint, float> topColliders;
925
926 lock (PhysObjects)
927 {
928 foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
929 {
930 kvp.Value.ComputeCollisionScore();
931 }
932
933 List<BSPhysObject> orderedPrims = new List<BSPhysObject>(PhysObjects.Values);
934 orderedPrims.OrderByDescending(p => p.CollisionScore);
935 topColliders = orderedPrims.Take(25).ToDictionary(p => p.LocalID, p => p.CollisionScore);
936 }
937
938 return topColliders;
939 }
940
941 public override bool IsThreaded { get { return false; } }
942
943 #region Extensions
944 public override object Extension(string pFunct, params object[] pParams)
945 {
946 DetailLog("{0} BSScene.Extension,op={1}", DetailLogZero, pFunct);
947 return base.Extension(pFunct, pParams);
948 }
949 #endregion // Extensions
950
951 public static string PrimitiveBaseShapeToString(PrimitiveBaseShape pbs)
952 {
953 float pathShearX = pbs.PathShearX < 128 ? (float)pbs.PathShearX * 0.01f : (float)(pbs.PathShearX - 256) * 0.01f;
954 float pathShearY = pbs.PathShearY < 128 ? (float)pbs.PathShearY * 0.01f : (float)(pbs.PathShearY - 256) * 0.01f;
955 float pathBegin = (float)pbs.PathBegin * 2.0e-5f;
956 float pathEnd = 1.0f - (float)pbs.PathEnd * 2.0e-5f;
957 float pathScaleX = (float)(200 - pbs.PathScaleX) * 0.01f;
958 float pathScaleY = (float)(200 - pbs.PathScaleY) * 0.01f;
959 float pathTaperX = pbs.PathTaperX * 0.01f;
960 float pathTaperY = pbs.PathTaperY * 0.01f;
961
962 float profileBegin = (float)pbs.ProfileBegin * 2.0e-5f;
963 float profileEnd = 1.0f - (float)pbs.ProfileEnd * 2.0e-5f;
964 float profileHollow = (float)pbs.ProfileHollow * 2.0e-5f;
965 if (profileHollow > 0.95f)
966 profileHollow = 0.95f;
967
968 StringBuilder buff = new StringBuilder();
969 buff.Append("shape=");
970 buff.Append(((ProfileShape)pbs.ProfileShape).ToString());
971 buff.Append(",");
972 buff.Append("hollow=");
973 buff.Append(((HollowShape)pbs.HollowShape).ToString());
974 buff.Append(",");
975 buff.Append("pathCurve=");
976 buff.Append(((Extrusion)pbs.PathCurve).ToString());
977 buff.Append(",");
978 buff.Append("profCurve=");
979 buff.Append(((Extrusion)pbs.ProfileCurve).ToString());
980 buff.Append(",");
981 buff.Append("profHollow=");
982 buff.Append(profileHollow.ToString());
983 buff.Append(",");
984 buff.Append("pathBegEnd=");
985 buff.Append(pathBegin.ToString());
986 buff.Append("/");
987 buff.Append(pathEnd.ToString());
988 buff.Append(",");
989 buff.Append("profileBegEnd=");
990 buff.Append(profileBegin.ToString());
991 buff.Append("/");
992 buff.Append(profileEnd.ToString());
993 buff.Append(",");
994 buff.Append("scaleXY=");
995 buff.Append(pathScaleX.ToString());
996 buff.Append("/");
997 buff.Append(pathScaleY.ToString());
998 buff.Append(",");
999 buff.Append("shearXY=");
1000 buff.Append(pathShearX.ToString());
1001 buff.Append("/");
1002 buff.Append(pathShearY.ToString());
1003 buff.Append(",");
1004 buff.Append("taperXY=");
1005 buff.Append(pbs.PathTaperX.ToString());
1006 buff.Append("/");
1007 buff.Append(pbs.PathTaperY.ToString());
1008 buff.Append(",");
1009 buff.Append("skew=");
1010 buff.Append(pbs.PathSkew.ToString());
1011 buff.Append(",");
1012 buff.Append("twist/Beg=");
1013 buff.Append(pbs.PathTwist.ToString());
1014 buff.Append("/");
1015 buff.Append(pbs.PathTwistBegin.ToString());
1016
1017 return buff.ToString();
1018 }
1019
1020 #region Taints
1021 // The simulation execution order is:
1022 // Simulate()
1023 // DoOneTimeTaints
1024 // TriggerPreStepEvent
1025 // DoOneTimeTaints
1026 // Step()
1027 // ProcessAndSendToSimulatorCollisions
1028 // ProcessAndSendToSimulatorPropertyUpdates
1029 // TriggerPostStepEvent
1030
1031 // Calls to the PhysicsActors can't directly call into the physics engine
1032 // because it might be busy. We delay changes to a known time.
1033 // We rely on C#'s closure to save and restore the context for the delegate.
1034 public void TaintedObject(string pOriginator, string pIdent, TaintCallback pCallback)
1035 {
1036 TaintedObject(false /*inTaintTime*/, pOriginator, pIdent, pCallback);
1037 }
1038 public void TaintedObject(uint pOriginator, String pIdent, TaintCallback pCallback)
1039 {
1040 TaintedObject(false /*inTaintTime*/, m_physicsLoggingEnabled ? pOriginator.ToString() : BSScene.DetailLogZero, pIdent, pCallback);
1041 }
1042 public void TaintedObject(bool inTaintTime, String pIdent, TaintCallback pCallback)
1043 {
1044 TaintedObject(inTaintTime, BSScene.DetailLogZero, pIdent, pCallback);
1045 }
1046 public void TaintedObject(bool inTaintTime, uint pOriginator, String pIdent, TaintCallback pCallback)
1047 {
1048 TaintedObject(inTaintTime, m_physicsLoggingEnabled ? pOriginator.ToString() : BSScene.DetailLogZero, pIdent, pCallback);
1049 }
1050 // Sometimes a potentially tainted operation can be used in and out of taint time.
1051 // This routine executes the command immediately if in taint-time otherwise it is queued.
1052 public void TaintedObject(bool inTaintTime, string pOriginator, string pIdent, TaintCallback pCallback)
1053 {
1054 if (!m_initialized) return;
1055
1056 if (inTaintTime)
1057 pCallback();
1058 else
1059 {
1060 lock (_taintLock)
1061 {
1062 _taintOperations.Add(new TaintCallbackEntry(pOriginator, pIdent, pCallback));
1063 }
1064 }
1065 }
1066
1067 private void TriggerPreStepEvent(float timeStep)
1068 {
1069 PreStepAction actions = BeforeStep;
1070 if (actions != null)
1071 actions(timeStep);
1072
1073 }
1074
1075 private void TriggerPostStepEvent(float timeStep)
1076 {
1077 PostStepAction actions = AfterStep;
1078 if (actions != null)
1079 actions(timeStep);
1080
1081 }
1082
1083 // When someone tries to change a property on a BSPrim or BSCharacter, the object queues
1084 // a callback into itself to do the actual property change. That callback is called
1085 // here just before the physics engine is called to step the simulation.
1086 public void ProcessTaints()
1087 {
1088 ProcessRegularTaints();
1089 ProcessPostTaintTaints();
1090 }
1091
1092 private void ProcessRegularTaints()
1093 {
1094 if (m_initialized && _taintOperations.Count > 0) // save allocating new list if there is nothing to process
1095 {
1096 // swizzle a new list into the list location so we can process what's there
1097 List<TaintCallbackEntry> oldList;
1098 lock (_taintLock)
1099 {
1100 oldList = _taintOperations;
1101 _taintOperations = new List<TaintCallbackEntry>();
1102 }
1103
1104 foreach (TaintCallbackEntry tcbe in oldList)
1105 {
1106 try
1107 {
1108 DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", tcbe.originator, tcbe.ident); // DEBUG DEBUG DEBUG
1109 tcbe.callback();
1110 }
1111 catch (Exception e)
1112 {
1113 m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e);
1114 }
1115 }
1116 oldList.Clear();
1117 }
1118 }
1119
1120 // Schedule an update to happen after all the regular taints are processed.
1121 // Note that new requests for the same operation ("ident") for the same object ("ID")
1122 // will replace any previous operation by the same object.
1123 public void PostTaintObject(String ident, uint ID, TaintCallback callback)
1124 {
1125 string IDAsString = ID.ToString();
1126 string uniqueIdent = ident + "-" + IDAsString;
1127 lock (_taintLock)
1128 {
1129 _postTaintOperations[uniqueIdent] = new TaintCallbackEntry(IDAsString, uniqueIdent, callback);
1130 }
1131
1132 return;
1133 }
1134
1135 // Taints that happen after the normal taint processing but before the simulation step.
1136 private void ProcessPostTaintTaints()
1137 {
1138 if (m_initialized && _postTaintOperations.Count > 0)
1139 {
1140 Dictionary<string, TaintCallbackEntry> oldList;
1141 lock (_taintLock)
1142 {
1143 oldList = _postTaintOperations;
1144 _postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
1145 }
1146
1147 foreach (KeyValuePair<string,TaintCallbackEntry> kvp in oldList)
1148 {
1149 try
1150 {
1151 DetailLog("{0},BSScene.ProcessPostTaintTaints,doTaint,id={1}", DetailLogZero, kvp.Key); // DEBUG DEBUG DEBUG
1152 kvp.Value.callback();
1153 }
1154 catch (Exception e)
1155 {
1156 m_log.ErrorFormat("{0}: ProcessPostTaintTaints: {1}: Exception: {2}", LogHeader, kvp.Key, e);
1157 }
1158 }
1159 oldList.Clear();
1160 }
1161 }
1162
1163 // Only used for debugging. Does not change state of anything so locking is not necessary.
1164 public bool AssertInTaintTime(string whereFrom)
1165 {
1166 if (!InTaintTime)
1167 {
1168 DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom);
1169 m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom);
1170 // Util.PrintCallStack(DetailLog);
1171 }
1172 return InTaintTime;
1173 }
1174
1175 #endregion // Taints
1176
1177 #region IPhysicsParameters
1178 // Get the list of parameters this physics engine supports
1179 public PhysParameterEntry[] GetParameterList()
1180 {
1181 BSParam.BuildParameterTable();
1182 return BSParam.SettableParameters;
1183 }
1184
1185 // Set parameter on a specific or all instances.
1186 // Return 'false' if not able to set the parameter.
1187 // Setting the value in the m_params block will change the value the physics engine
1188 // will use the next time since it's pinned and shared memory.
1189 // Some of the values require calling into the physics engine to get the new
1190 // value activated ('terrainFriction' for instance).
1191 public bool SetPhysicsParameter(string parm, string val, uint localID)
1192 {
1193 bool ret = false;
1194
1195 BSParam.ParameterDefnBase theParam;
1196 if (BSParam.TryGetParameter(parm, out theParam))
1197 {
1198 // Set the value in the C# code
1199 theParam.SetValue(this, val);
1200
1201 // Optionally set the parameter in the unmanaged code
1202 if (theParam.HasSetOnObject)
1203 {
1204 // update all the localIDs specified
1205 // If the local ID is APPLY_TO_NONE, just change the default value
1206 // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs
1207 // If the localID is a specific object, apply the parameter change to only that object
1208 List<uint> objectIDs = new List<uint>();
1209 switch (localID)
1210 {
1211 case PhysParameterEntry.APPLY_TO_NONE:
1212 // This will cause a call into the physical world if some operation is specified (SetOnObject).
1213 objectIDs.Add(TERRAIN_ID);
1214 TaintedUpdateParameter(parm, objectIDs, val);
1215 break;
1216 case PhysParameterEntry.APPLY_TO_ALL:
1217 lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys);
1218 TaintedUpdateParameter(parm, objectIDs, val);
1219 break;
1220 default:
1221 // setting only one localID
1222 objectIDs.Add(localID);
1223 TaintedUpdateParameter(parm, objectIDs, val);
1224 break;
1225 }
1226 }
1227
1228 ret = true;
1229 }
1230 return ret;
1231 }
1232
1233 // schedule the actual updating of the paramter to when the phys engine is not busy
1234 private void TaintedUpdateParameter(string parm, List<uint> lIDs, string val)
1235 {
1236 string xval = val;
1237 List<uint> xlIDs = lIDs;
1238 string xparm = parm;
1239 TaintedObject(DetailLogZero, "BSScene.UpdateParameterSet", delegate() {
1240 BSParam.ParameterDefnBase thisParam;
1241 if (BSParam.TryGetParameter(xparm, out thisParam))
1242 {
1243 if (thisParam.HasSetOnObject)
1244 {
1245 foreach (uint lID in xlIDs)
1246 {
1247 BSPhysObject theObject = null;
1248 if (PhysObjects.TryGetValue(lID, out theObject))
1249 thisParam.SetOnObject(this, theObject);
1250 }
1251 }
1252 }
1253 });
1254 }
1255
1256 // Get parameter.
1257 // Return 'false' if not able to get the parameter.
1258 public bool GetPhysicsParameter(string parm, out string value)
1259 {
1260 string val = String.Empty;
1261 bool ret = false;
1262 BSParam.ParameterDefnBase theParam;
1263 if (BSParam.TryGetParameter(parm, out theParam))
1264 {
1265 val = theParam.GetValue(this);
1266 ret = true;
1267 }
1268 value = val;
1269 return ret;
1270 }
1271
1272 #endregion IPhysicsParameters
1273
1274 // Invoke the detailed logger and output something if it's enabled.
1275 public void DetailLog(string msg, params Object[] args)
1276 {
1277 PhysicsLogging.Write(msg, args);
1278 }
1279 // Used to fill in the LocalID when there isn't one. It's the correct number of characters.
1280 public const string DetailLogZero = "0000000000";
1281
1282}
1283}