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