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.cs946
1 files changed, 0 insertions, 946 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
deleted file mode 100644
index 7017194..0000000
--- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
+++ /dev/null
@@ -1,946 +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.Reflection;
30using System.Runtime.InteropServices;
31using System.Text;
32using System.Threading;
33using OpenSim.Framework;
34using OpenSim.Region.Framework;
35using OpenSim.Region.CoreModules;
36using Logging = OpenSim.Region.CoreModules.Framework.Statistics.Logging;
37using OpenSim.Region.Physics.Manager;
38using Nini.Config;
39using log4net;
40using OpenMetaverse;
41
42namespace OpenSim.Region.Physics.BulletSPlugin
43{
44public sealed class BSScene : PhysicsScene, IPhysicsParameters
45{
46 internal static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
47 internal static readonly string LogHeader = "[BULLETS SCENE]";
48
49 // The name of the region we're working for.
50 public string RegionName { get; private set; }
51
52 public string BulletSimVersion = "?";
53
54 // The handle to the underlying managed or unmanaged version of Bullet being used.
55 public string BulletEngineName { get; private set; }
56 public BSAPITemplate PE;
57
58 public Dictionary<uint, BSPhysObject> PhysObjects;
59 public BSShapeCollection Shapes;
60
61 // Keeping track of the objects with collisions so we can report begin and end of a collision
62 public HashSet<BSPhysObject> ObjectsWithCollisions = new HashSet<BSPhysObject>();
63 public HashSet<BSPhysObject> ObjectsWithNoMoreCollisions = new HashSet<BSPhysObject>();
64 // Keep track of all the avatars so we can send them a collision event
65 // every tick so OpenSim will update its animation.
66 private HashSet<BSPhysObject> m_avatars = new HashSet<BSPhysObject>();
67
68 // let my minuions use my logger
69 public ILog Logger { get { return m_log; } }
70
71 public IMesher mesher;
72 public uint WorldID { get; private set; }
73 public BulletWorld World { get; private set; }
74
75 // All the constraints that have been allocated in this instance.
76 public BSConstraintCollection Constraints { get; private set; }
77
78 // Simulation parameters
79 internal int m_maxSubSteps;
80 internal float m_fixedTimeStep;
81 internal long m_simulationStep = 0;
82 internal float NominalFrameRate { get; set; }
83 public long SimulationStep { get { return m_simulationStep; } }
84 internal int m_taintsToProcessPerStep;
85 internal float LastTimeStep { get; private set; }
86
87 // Physical objects can register for prestep or poststep events
88 public delegate void PreStepAction(float timeStep);
89 public delegate void PostStepAction(float timeStep);
90 public event PreStepAction BeforeStep;
91 public event PreStepAction AfterStep;
92
93 // A value of the time now so all the collision and update routines do not have to get their own
94 // Set to 'now' just before all the prims and actors are called for collisions and updates
95 public int SimulationNowTime { get; private set; }
96
97 // True if initialized and ready to do simulation steps
98 private bool m_initialized = false;
99
100 // Flag which is true when processing taints.
101 // Not guaranteed to be correct all the time (don't depend on this) but good for debugging.
102 public bool InTaintTime { get; private set; }
103
104 // Pinned memory used to pass step information between managed and unmanaged
105 internal int m_maxCollisionsPerFrame;
106 internal CollisionDesc[] m_collisionArray;
107
108 internal int m_maxUpdatesPerFrame;
109 internal EntityProperties[] m_updateArray;
110
111 public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero
112 public const uint GROUNDPLANE_ID = 1;
113 public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here
114
115 public float SimpleWaterLevel { get; set; }
116 public BSTerrainManager TerrainManager { get; private set; }
117
118 public ConfigurationParameters Params
119 {
120 get { return UnmanagedParams[0]; }
121 }
122 public Vector3 DefaultGravity
123 {
124 get { return new Vector3(0f, 0f, Params.gravity); }
125 }
126 // Just the Z value of the gravity
127 public float DefaultGravityZ
128 {
129 get { return Params.gravity; }
130 }
131
132 // When functions in the unmanaged code must be called, it is only
133 // done at a known time just before the simulation step. The taint
134 // system saves all these function calls and executes them in
135 // order before the simulation.
136 public delegate void TaintCallback();
137 private struct TaintCallbackEntry
138 {
139 public String ident;
140 public TaintCallback callback;
141 public TaintCallbackEntry(string i, TaintCallback c)
142 {
143 ident = i;
144 callback = c;
145 }
146 }
147 private Object _taintLock = new Object(); // lock for using the next object
148 private List<TaintCallbackEntry> _taintOperations;
149 private Dictionary<string, TaintCallbackEntry> _postTaintOperations;
150 private List<TaintCallbackEntry> _postStepOperations;
151
152 // A pointer to an instance if this structure is passed to the C++ code
153 // Used to pass basic configuration values to the unmanaged code.
154 internal ConfigurationParameters[] UnmanagedParams;
155
156 // Sometimes you just have to log everything.
157 public Logging.LogWriter PhysicsLogging;
158 private bool m_physicsLoggingEnabled;
159 private string m_physicsLoggingDir;
160 private string m_physicsLoggingPrefix;
161 private int m_physicsLoggingFileMinutes;
162 private bool m_physicsLoggingDoFlush;
163 private bool m_physicsPhysicalDumpEnabled;
164 public float PhysicsMetricDumpFrames { get; set; }
165 // 'true' of the vehicle code is to log lots of details
166 public bool VehicleLoggingEnabled { get; private set; }
167 public bool VehiclePhysicalLoggingEnabled { get; private set; }
168
169 #region Construction and Initialization
170 public BSScene(string identifier)
171 {
172 m_initialized = false;
173 // we are passed the name of the region we're working for.
174 RegionName = identifier;
175 }
176
177 public override void Initialise(IMesher meshmerizer, IConfigSource config)
178 {
179 mesher = meshmerizer;
180 _taintOperations = new List<TaintCallbackEntry>();
181 _postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
182 _postStepOperations = new List<TaintCallbackEntry>();
183 PhysObjects = new Dictionary<uint, BSPhysObject>();
184 Shapes = new BSShapeCollection(this);
185
186 // Allocate pinned memory to pass parameters.
187 UnmanagedParams = new ConfigurationParameters[1];
188
189 // Set default values for physics parameters plus any overrides from the ini file
190 GetInitialParameterValues(config);
191
192 // Get the connection to the physics engine (could be native or one of many DLLs)
193 PE = SelectUnderlyingBulletEngine(BulletEngineName);
194
195 // Enable very detailed logging.
196 // By creating an empty logger when not logging, the log message invocation code
197 // can be left in and every call doesn't have to check for null.
198 if (m_physicsLoggingEnabled)
199 {
200 PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes);
201 PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output error messages.
202 }
203 else
204 {
205 PhysicsLogging = new Logging.LogWriter();
206 }
207
208 // Allocate memory for returning of the updates and collisions from the physics engine
209 m_collisionArray = new CollisionDesc[m_maxCollisionsPerFrame];
210 m_updateArray = new EntityProperties[m_maxUpdatesPerFrame];
211
212 // The bounding box for the simulated world. The origin is 0,0,0 unless we're
213 // a child in a mega-region.
214 // Bullet actually doesn't care about the extents of the simulated
215 // area. It tracks active objects no matter where they are.
216 Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
217
218 World = PE.Initialize(worldExtent, Params, m_maxCollisionsPerFrame, ref m_collisionArray, m_maxUpdatesPerFrame, ref m_updateArray);
219
220 Constraints = new BSConstraintCollection(World);
221
222 TerrainManager = new BSTerrainManager(this);
223 TerrainManager.CreateInitialGroundPlaneAndTerrain();
224
225 m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation);
226
227 InTaintTime = false;
228 m_initialized = true;
229 }
230
231 // All default parameter values are set here. There should be no values set in the
232 // variable definitions.
233 private void GetInitialParameterValues(IConfigSource config)
234 {
235 ConfigurationParameters parms = new ConfigurationParameters();
236 UnmanagedParams[0] = parms;
237
238 BSParam.SetParameterDefaultValues(this);
239
240 if (config != null)
241 {
242 // If there are specifications in the ini file, use those values
243 IConfig pConfig = config.Configs["BulletSim"];
244 if (pConfig != null)
245 {
246 BSParam.SetParameterConfigurationValues(this, pConfig);
247
248 // There are two Bullet implementations to choose from
249 BulletEngineName = pConfig.GetString("BulletEngine", "BulletUnmanaged");
250
251 // Very detailed logging for physics debugging
252 // TODO: the boolean values can be moved to the normal parameter processing.
253 m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false);
254 m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", ".");
255 m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-");
256 m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5);
257 m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false);
258 m_physicsPhysicalDumpEnabled = pConfig.GetBoolean("PhysicsPhysicalDumpEnabled", false);
259 // Very detailed logging for vehicle debugging
260 VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
261 VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false);
262
263 // Do any replacements in the parameters
264 m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
265 }
266
267 // The material characteristics.
268 BSMaterials.InitializeFromDefaults(Params);
269 if (pConfig != null)
270 {
271 // Let the user add new and interesting material property values.
272 BSMaterials.InitializefromParameters(pConfig);
273 }
274 }
275 }
276
277 // A helper function that handles a true/false parameter and returns the proper float number encoding
278 float ParamBoolean(IConfig config, string parmName, float deflt)
279 {
280 float ret = deflt;
281 if (config.Contains(parmName))
282 {
283 ret = ConfigurationParameters.numericFalse;
284 if (config.GetBoolean(parmName, false))
285 {
286 ret = ConfigurationParameters.numericTrue;
287 }
288 }
289 return ret;
290 }
291
292 // Select the connection to the actual Bullet implementation.
293 // The main engine selection is the engineName up to the first hypen.
294 // So "Bullet-2.80-OpenCL-Intel" specifies the 'bullet' class here and the whole name
295 // is passed to the engine to do its special selection, etc.
296 private BSAPITemplate SelectUnderlyingBulletEngine(string engineName)
297 {
298 // For the moment, do a simple switch statement.
299 // Someday do fancyness with looking up the interfaces in the assembly.
300 BSAPITemplate ret = null;
301
302 string selectionName = engineName.ToLower();
303 int hyphenIndex = engineName.IndexOf("-");
304 if (hyphenIndex > 0)
305 selectionName = engineName.ToLower().Substring(0, hyphenIndex - 1);
306
307 switch (selectionName)
308 {
309 case "bulletunmanaged":
310 ret = new BSAPIUnman(engineName, this);
311 break;
312 case "bulletxna":
313 ret = new BSAPIXNA(engineName, this);
314 break;
315 }
316
317 if (ret == null)
318 {
319 m_log.ErrorFormat("{0) COULD NOT SELECT BULLET ENGINE: '[BulletSim]PhysicsEngine' must be either 'BulletUnmanaged-*' or 'BulletXNA-*'", LogHeader);
320 }
321 else
322 {
323 m_log.WarnFormat("{0} Selected bullet engine {1} -> {2}/{3}", LogHeader, engineName, ret.BulletEngineName, ret.BulletEngineVersion);
324 }
325
326 return ret;
327 }
328
329 public override void Dispose()
330 {
331 // m_log.DebugFormat("{0}: Dispose()", LogHeader);
332
333 // make sure no stepping happens while we're deleting stuff
334 m_initialized = false;
335
336 foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
337 {
338 kvp.Value.Destroy();
339 }
340 PhysObjects.Clear();
341
342 // Now that the prims are all cleaned up, there should be no constraints left
343 if (Constraints != null)
344 {
345 Constraints.Dispose();
346 Constraints = null;
347 }
348
349 if (Shapes != null)
350 {
351 Shapes.Dispose();
352 Shapes = null;
353 }
354
355 if (TerrainManager != null)
356 {
357 TerrainManager.ReleaseGroundPlaneAndTerrain();
358 TerrainManager.Dispose();
359 TerrainManager = null;
360 }
361
362 // Anything left in the unmanaged code should be cleaned out
363 PE.Shutdown(World);
364
365 // Not logging any more
366 PhysicsLogging.Close();
367 }
368 #endregion // Construction and Initialization
369
370 #region Prim and Avatar addition and removal
371
372 public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying)
373 {
374 m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader);
375 return null;
376 }
377
378 public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 size, bool isFlying)
379 {
380 // m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName);
381
382 if (!m_initialized) return null;
383
384 BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying);
385 lock (PhysObjects) PhysObjects.Add(localID, actor);
386
387 // TODO: Remove kludge someday.
388 // We must generate a collision for avatars whether they collide or not.
389 // This is required by OpenSim to update avatar animations, etc.
390 lock (m_avatars) m_avatars.Add(actor);
391
392 return actor;
393 }
394
395 public override void RemoveAvatar(PhysicsActor actor)
396 {
397 // m_log.DebugFormat("{0}: RemoveAvatar", LogHeader);
398
399 if (!m_initialized) return;
400
401 BSCharacter bsactor = actor as BSCharacter;
402 if (bsactor != null)
403 {
404 try
405 {
406 lock (PhysObjects) PhysObjects.Remove(actor.LocalID);
407 // Remove kludge someday
408 lock (m_avatars) m_avatars.Remove(bsactor);
409 }
410 catch (Exception e)
411 {
412 m_log.WarnFormat("{0}: Attempt to remove avatar that is not in physics scene: {1}", LogHeader, e);
413 }
414 bsactor.Destroy();
415 // bsactor.dispose();
416 }
417 }
418
419 public override void RemovePrim(PhysicsActor prim)
420 {
421 if (!m_initialized) return;
422
423 BSPrim bsprim = prim as BSPrim;
424 if (bsprim != null)
425 {
426 DetailLog("{0},RemovePrim,call", bsprim.LocalID);
427 // m_log.DebugFormat("{0}: RemovePrim. id={1}/{2}", LogHeader, bsprim.Name, bsprim.LocalID);
428 try
429 {
430 lock (PhysObjects) PhysObjects.Remove(bsprim.LocalID);
431 }
432 catch (Exception e)
433 {
434 m_log.ErrorFormat("{0}: Attempt to remove prim that is not in physics scene: {1}", LogHeader, e);
435 }
436 bsprim.Destroy();
437 // bsprim.dispose();
438 }
439 else
440 {
441 m_log.ErrorFormat("{0}: Attempt to remove prim that is not a BSPrim type.", LogHeader);
442 }
443 }
444
445 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
446 Vector3 size, Quaternion rotation, bool isPhysical, uint localID)
447 {
448 // m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName);
449
450 if (!m_initialized) return null;
451
452 DetailLog("{0},AddPrimShape,call", localID);
453
454 BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, pbs, isPhysical);
455 lock (PhysObjects) PhysObjects.Add(localID, prim);
456 return prim;
457 }
458
459 // This is a call from the simulator saying that some physical property has been updated.
460 // The BulletSim driver senses the changing of relevant properties so this taint
461 // information call is not needed.
462 public override void AddPhysicsActorTaint(PhysicsActor prim) { }
463
464 #endregion // Prim and Avatar addition and removal
465
466 #region Simulation
467 // Simulate one timestep
468 public override float Simulate(float timeStep)
469 {
470 // prevent simulation until we've been initialized
471 if (!m_initialized) return 5.0f;
472
473 LastTimeStep = timeStep;
474
475 int updatedEntityCount = 0;
476 int collidersCount = 0;
477
478 int beforeTime = 0;
479 int simTime = 0;
480
481 // update the prim states while we know the physics engine is not busy
482 int numTaints = _taintOperations.Count;
483
484 InTaintTime = true; // Only used for debugging so locking is not necessary.
485
486 ProcessTaints();
487
488 // Some of the physical objects requre individual, pre-step calls
489 TriggerPreStepEvent(timeStep);
490
491 // the prestep actions might have added taints
492 numTaints += _taintOperations.Count;
493 ProcessTaints();
494
495 InTaintTime = false; // Only used for debugging so locking is not necessary.
496
497 // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world.
498 // Only enable this in a limited test world with few objects.
499 if (m_physicsPhysicalDumpEnabled)
500 PE.DumpAllInfo(World);
501
502 // step the physical world one interval
503 m_simulationStep++;
504 int numSubSteps = 0;
505 try
506 {
507 if (PhysicsLogging.Enabled)
508 beforeTime = Util.EnvironmentTickCount();
509
510 numSubSteps = PE.PhysicsStep(World, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out collidersCount);
511
512 if (PhysicsLogging.Enabled)
513 {
514 simTime = Util.EnvironmentTickCountSubtract(beforeTime);
515 DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}",
516 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps,
517 updatedEntityCount, collidersCount, ObjectsWithCollisions.Count);
518 }
519 }
520 catch (Exception e)
521 {
522 m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}",
523 LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e);
524 DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}",
525 DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount);
526 updatedEntityCount = 0;
527 collidersCount = 0;
528 }
529
530 if ((m_simulationStep % PhysicsMetricDumpFrames) == 0)
531 PE.DumpPhysicsStatistics(World);
532
533 // Get a value for 'now' so all the collision and update routines don't have to get their own.
534 SimulationNowTime = Util.EnvironmentTickCount();
535
536 // If there were collisions, process them by sending the event to the prim.
537 // Collisions must be processed before updates.
538 if (collidersCount > 0)
539 {
540 for (int ii = 0; ii < collidersCount; ii++)
541 {
542 uint cA = m_collisionArray[ii].aID;
543 uint cB = m_collisionArray[ii].bID;
544 Vector3 point = m_collisionArray[ii].point;
545 Vector3 normal = m_collisionArray[ii].normal;
546 SendCollision(cA, cB, point, normal, 0.01f);
547 SendCollision(cB, cA, point, -normal, 0.01f);
548 }
549 }
550
551 // The above SendCollision's batch up the collisions on the objects.
552 // Now push the collisions into the simulator.
553 if (ObjectsWithCollisions.Count > 0)
554 {
555 foreach (BSPhysObject bsp in ObjectsWithCollisions)
556 if (!bsp.SendCollisions())
557 {
558 // If the object is done colliding, see that it's removed from the colliding list
559 ObjectsWithNoMoreCollisions.Add(bsp);
560 }
561 }
562
563 // This is a kludge to get avatar movement updates.
564 // The simulator expects collisions for avatars even if there are have been no collisions.
565 // The event updates avatar animations and stuff.
566 // If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
567 foreach (BSPhysObject bsp in m_avatars)
568 if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice
569 bsp.SendCollisions();
570
571 // Objects that are done colliding are removed from the ObjectsWithCollisions list.
572 // Not done above because it is inside an iteration of ObjectWithCollisions.
573 // This complex collision processing is required to create an empty collision
574 // event call after all real collisions have happened on an object. This enables
575 // the simulator to generate the 'collision end' event.
576 if (ObjectsWithNoMoreCollisions.Count > 0)
577 {
578 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
579 ObjectsWithCollisions.Remove(po);
580 ObjectsWithNoMoreCollisions.Clear();
581 }
582 // Done with collisions.
583
584 // If any of the objects had updated properties, tell the object it has been changed by the physics engine
585 if (updatedEntityCount > 0)
586 {
587 for (int ii = 0; ii < updatedEntityCount; ii++)
588 {
589 EntityProperties entprop = m_updateArray[ii];
590 BSPhysObject pobj;
591 if (PhysObjects.TryGetValue(entprop.ID, out pobj))
592 {
593 pobj.UpdateProperties(entprop);
594 }
595 }
596 }
597
598 TriggerPostStepEvent(timeStep);
599
600 // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world.
601 // Only enable this in a limited test world with few objects.
602 if (m_physicsPhysicalDumpEnabled)
603 PE.DumpAllInfo(World);
604
605 // The physics engine returns the number of milliseconds it simulated this call.
606 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
607 // Multiply by a fixed nominal frame rate to give a rate similar to the simulator (usually 55).
608 return (float)numSubSteps * m_fixedTimeStep * 1000f * NominalFrameRate;
609 }
610
611 // Something has collided
612 private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration)
613 {
614 if (localID <= TerrainManager.HighestTerrainID)
615 {
616 return; // don't send collisions to the terrain
617 }
618
619 BSPhysObject collider;
620 if (!PhysObjects.TryGetValue(localID, out collider))
621 {
622 // If the object that is colliding cannot be found, just ignore the collision.
623 DetailLog("{0},BSScene.SendCollision,colliderNotInObjectList,id={1},with={2}", DetailLogZero, localID, collidingWith);
624 return;
625 }
626
627 // The terrain is not in the physical object list so 'collidee' can be null when Collide() is called.
628 BSPhysObject collidee = null;
629 PhysObjects.TryGetValue(collidingWith, out collidee);
630
631 // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
632
633 if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
634 {
635 // If a collision was posted, remember to send it to the simulator
636 ObjectsWithCollisions.Add(collider);
637 }
638
639 return;
640 }
641
642 #endregion // Simulation
643
644 public override void GetResults() { }
645
646 #region Terrain
647
648 public override void SetTerrain(float[] heightMap) {
649 TerrainManager.SetTerrain(heightMap);
650 }
651
652 public override void SetWaterLevel(float baseheight)
653 {
654 SimpleWaterLevel = baseheight;
655 }
656
657 public override void DeleteTerrain()
658 {
659 // m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader);
660 }
661
662 // Although no one seems to check this, I do support combining.
663 public override bool SupportsCombining()
664 {
665 return TerrainManager.SupportsCombining();
666 }
667 // This call says I am a child to region zero in a mega-region. 'pScene' is that
668 // of region zero, 'offset' is my offset from regions zero's origin, and
669 // 'extents' is the largest XY that is handled in my region.
670 public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
671 {
672 TerrainManager.Combine(pScene, offset, extents);
673 }
674
675 // Unhook all the combining that I know about.
676 public override void UnCombine(PhysicsScene pScene)
677 {
678 TerrainManager.UnCombine(pScene);
679 }
680
681 #endregion // Terrain
682
683 public override Dictionary<uint, float> GetTopColliders()
684 {
685 return new Dictionary<uint, float>();
686 }
687
688 public override bool IsThreaded { get { return false; } }
689
690 #region Taints
691 // The simulation execution order is:
692 // Simulate()
693 // DoOneTimeTaints
694 // TriggerPreStepEvent
695 // DoOneTimeTaints
696 // Step()
697 // ProcessAndForwardCollisions
698 // ProcessAndForwardPropertyUpdates
699 // TriggerPostStepEvent
700
701 // Calls to the PhysicsActors can't directly call into the physics engine
702 // because it might be busy. We delay changes to a known time.
703 // We rely on C#'s closure to save and restore the context for the delegate.
704 public void TaintedObject(String ident, TaintCallback callback)
705 {
706 if (!m_initialized) return;
707
708 lock (_taintLock)
709 {
710 _taintOperations.Add(new TaintCallbackEntry(ident, callback));
711 }
712
713 return;
714 }
715
716 // Sometimes a potentially tainted operation can be used in and out of taint time.
717 // This routine executes the command immediately if in taint-time otherwise it is queued.
718 public void TaintedObject(bool inTaintTime, string ident, TaintCallback callback)
719 {
720 if (inTaintTime)
721 callback();
722 else
723 TaintedObject(ident, callback);
724 }
725
726 private void TriggerPreStepEvent(float timeStep)
727 {
728 PreStepAction actions = BeforeStep;
729 if (actions != null)
730 actions(timeStep);
731
732 }
733
734 private void TriggerPostStepEvent(float timeStep)
735 {
736 PreStepAction actions = AfterStep;
737 if (actions != null)
738 actions(timeStep);
739
740 }
741
742 // When someone tries to change a property on a BSPrim or BSCharacter, the object queues
743 // a callback into itself to do the actual property change. That callback is called
744 // here just before the physics engine is called to step the simulation.
745 public void ProcessTaints()
746 {
747 ProcessRegularTaints();
748 ProcessPostTaintTaints();
749 }
750
751 private void ProcessRegularTaints()
752 {
753 if (_taintOperations.Count > 0) // save allocating new list if there is nothing to process
754 {
755 // swizzle a new list into the list location so we can process what's there
756 List<TaintCallbackEntry> oldList;
757 lock (_taintLock)
758 {
759 oldList = _taintOperations;
760 _taintOperations = new List<TaintCallbackEntry>();
761 }
762
763 foreach (TaintCallbackEntry tcbe in oldList)
764 {
765 try
766 {
767 DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG
768 tcbe.callback();
769 }
770 catch (Exception e)
771 {
772 m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e);
773 }
774 }
775 oldList.Clear();
776 }
777 }
778
779 // Schedule an update to happen after all the regular taints are processed.
780 // Note that new requests for the same operation ("ident") for the same object ("ID")
781 // will replace any previous operation by the same object.
782 public void PostTaintObject(String ident, uint ID, TaintCallback callback)
783 {
784 string uniqueIdent = ident + "-" + ID.ToString();
785 lock (_taintLock)
786 {
787 _postTaintOperations[uniqueIdent] = new TaintCallbackEntry(uniqueIdent, callback);
788 }
789
790 return;
791 }
792
793 // Taints that happen after the normal taint processing but before the simulation step.
794 private void ProcessPostTaintTaints()
795 {
796 if (_postTaintOperations.Count > 0)
797 {
798 Dictionary<string, TaintCallbackEntry> oldList;
799 lock (_taintLock)
800 {
801 oldList = _postTaintOperations;
802 _postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
803 }
804
805 foreach (KeyValuePair<string,TaintCallbackEntry> kvp in oldList)
806 {
807 try
808 {
809 DetailLog("{0},BSScene.ProcessPostTaintTaints,doTaint,id={1}", DetailLogZero, kvp.Key); // DEBUG DEBUG DEBUG
810 kvp.Value.callback();
811 }
812 catch (Exception e)
813 {
814 m_log.ErrorFormat("{0}: ProcessPostTaintTaints: {1}: Exception: {2}", LogHeader, kvp.Key, e);
815 }
816 }
817 oldList.Clear();
818 }
819 }
820
821 // Only used for debugging. Does not change state of anything so locking is not necessary.
822 public bool AssertInTaintTime(string whereFrom)
823 {
824 if (!InTaintTime)
825 {
826 DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom);
827 m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom);
828 Util.PrintCallStack(DetailLog);
829 }
830 return InTaintTime;
831 }
832
833 #endregion // Taints
834
835 #region INI and command line parameter processing
836
837 #region IPhysicsParameters
838 // Get the list of parameters this physics engine supports
839 public PhysParameterEntry[] GetParameterList()
840 {
841 BSParam.BuildParameterTable();
842 return BSParam.SettableParameters;
843 }
844
845 // Set parameter on a specific or all instances.
846 // Return 'false' if not able to set the parameter.
847 // Setting the value in the m_params block will change the value the physics engine
848 // will use the next time since it's pinned and shared memory.
849 // Some of the values require calling into the physics engine to get the new
850 // value activated ('terrainFriction' for instance).
851 public bool SetPhysicsParameter(string parm, float val, uint localID)
852 {
853 bool ret = false;
854 BSParam.ParameterDefn theParam;
855 if (BSParam.TryGetParameter(parm, out theParam))
856 {
857 theParam.setter(this, parm, localID, val);
858 ret = true;
859 }
860 return ret;
861 }
862
863 // update all the localIDs specified
864 // If the local ID is APPLY_TO_NONE, just change the default value
865 // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs
866 // If the localID is a specific object, apply the parameter change to only that object
867 internal delegate void AssignVal(float x);
868 internal void UpdateParameterObject(AssignVal setDefault, string parm, uint localID, float val)
869 {
870 List<uint> objectIDs = new List<uint>();
871 switch (localID)
872 {
873 case PhysParameterEntry.APPLY_TO_NONE:
874 setDefault(val); // setting only the default value
875 // This will cause a call into the physical world if some operation is specified (SetOnObject).
876 objectIDs.Add(TERRAIN_ID);
877 TaintedUpdateParameter(parm, objectIDs, val);
878 break;
879 case PhysParameterEntry.APPLY_TO_ALL:
880 setDefault(val); // setting ALL also sets the default value
881 lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys);
882 TaintedUpdateParameter(parm, objectIDs, val);
883 break;
884 default:
885 // setting only one localID
886 objectIDs.Add(localID);
887 TaintedUpdateParameter(parm, objectIDs, val);
888 break;
889 }
890 }
891
892 // schedule the actual updating of the paramter to when the phys engine is not busy
893 private void TaintedUpdateParameter(string parm, List<uint> lIDs, float val)
894 {
895 float xval = val;
896 List<uint> xlIDs = lIDs;
897 string xparm = parm;
898 TaintedObject("BSScene.UpdateParameterSet", delegate() {
899 BSParam.ParameterDefn thisParam;
900 if (BSParam.TryGetParameter(xparm, out thisParam))
901 {
902 if (thisParam.onObject != null)
903 {
904 foreach (uint lID in xlIDs)
905 {
906 BSPhysObject theObject = null;
907 PhysObjects.TryGetValue(lID, out theObject);
908 thisParam.onObject(this, theObject, xval);
909 }
910 }
911 }
912 });
913 }
914
915 // Get parameter.
916 // Return 'false' if not able to get the parameter.
917 public bool GetPhysicsParameter(string parm, out float value)
918 {
919 float val = 0f;
920 bool ret = false;
921 BSParam.ParameterDefn theParam;
922 if (BSParam.TryGetParameter(parm, out theParam))
923 {
924 val = theParam.getter(this);
925 ret = true;
926 }
927 value = val;
928 return ret;
929 }
930
931 #endregion IPhysicsParameters
932
933 #endregion Runtime settable parameters
934
935 // Invoke the detailed logger and output something if it's enabled.
936 public void DetailLog(string msg, params Object[] args)
937 {
938 PhysicsLogging.Write(msg, args);
939 // Add the Flush() if debugging crashes. Gets all the messages written out.
940 if (m_physicsLoggingDoFlush) PhysicsLogging.Flush();
941 }
942 // Used to fill in the LocalID when there isn't one. It's the correct number of characters.
943 public const string DetailLogZero = "0000000000";
944
945}
946}