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