BSScene.cs 38 KB

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