BSScene.cs 61 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340
  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. // Test sculpties (verified that they don't work)
  42. // Compute physics FPS reasonably
  43. // Based on material, set density and friction
  44. // Don't use constraints in linksets of non-physical objects. Means having to move children manually.
  45. // Four states of prim: Physical, regular, phantom and selected. Are we modeling these correctly?
  46. // In SL one can set both physical and phantom (gravity, does not effect others, makes collisions with ground)
  47. // At the moment, physical and phantom causes object to drop through the terrain
  48. // Physical phantom objects and related typing (collision options )
  49. // Check out llVolumeDetect. Must do something for that.
  50. // Use collision masks for collision with terrain and phantom objects
  51. // More efficient memory usage when passing hull information from BSPrim to BulletSim
  52. // Should prim.link() and prim.delink() membership checking happen at taint time?
  53. // Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once.
  54. // Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect
  55. // Implement LockAngularMotion
  56. // Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation)
  57. // Remove mesh and Hull stuff. Use mesh passed to bullet and use convexdecom from bullet.
  58. // Add PID movement operations. What does ScenePresence.MoveToTarget do?
  59. // Check terrain size. 128 or 127?
  60. // Raycast
  61. //
  62. namespace OpenSim.Region.Physics.BulletSPlugin
  63. {
  64. public sealed class BSScene : PhysicsScene, IPhysicsParameters
  65. {
  66. private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
  67. private static readonly string LogHeader = "[BULLETS SCENE]";
  68. // The name of the region we're working for.
  69. public string RegionName { get; private set; }
  70. public string BulletSimVersion = "?";
  71. public Dictionary<uint, BSPhysObject> PhysObjects;
  72. public BSShapeCollection Shapes;
  73. // Keeping track of the objects with collisions so we can report begin and end of a collision
  74. public HashSet<BSPhysObject> ObjectsWithCollisions = new HashSet<BSPhysObject>();
  75. public HashSet<BSPhysObject> ObjectsWithNoMoreCollisions = new HashSet<BSPhysObject>();
  76. // Keep track of all the avatars so we can send them a collision event
  77. // every tick so OpenSim will update its animation.
  78. private HashSet<BSPhysObject> m_avatars = new HashSet<BSPhysObject>();
  79. // List of all the objects that have vehicle properties and should be called
  80. // to update each physics step.
  81. private List<BSPhysObject> m_vehicles = new List<BSPhysObject>();
  82. // let my minuions use my logger
  83. public ILog Logger { get { return m_log; } }
  84. public IMesher mesher;
  85. // Level of Detail values kept as float because that's what the Meshmerizer wants
  86. public float MeshLOD { get; private set; }
  87. public float MeshMegaPrimLOD { get; private set; }
  88. public float MeshMegaPrimThreshold { get; private set; }
  89. public float SculptLOD { get; private set; }
  90. public uint WorldID { get; private set; }
  91. public BulletSim World { get; private set; }
  92. // All the constraints that have been allocated in this instance.
  93. public BSConstraintCollection Constraints { get; private set; }
  94. // Simulation parameters
  95. private int m_maxSubSteps;
  96. private float m_fixedTimeStep;
  97. private long m_simulationStep = 0;
  98. public long SimulationStep { get { return m_simulationStep; } }
  99. private int m_taintsToProcessPerStep;
  100. // A value of the time now so all the collision and update routines do not have to get their own
  101. // Set to 'now' just before all the prims and actors are called for collisions and updates
  102. public int SimulationNowTime { get; private set; }
  103. // True if initialized and ready to do simulation steps
  104. private bool m_initialized = false;
  105. // Pinned memory used to pass step information between managed and unmanaged
  106. private int m_maxCollisionsPerFrame;
  107. private CollisionDesc[] m_collisionArray;
  108. private GCHandle m_collisionArrayPinnedHandle;
  109. private int m_maxUpdatesPerFrame;
  110. private EntityProperties[] m_updateArray;
  111. private GCHandle m_updateArrayPinnedHandle;
  112. public bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed
  113. public bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes
  114. public bool ShouldUseHullsForPhysicalObjects { get; private set; } // 'true' if should create hulls for physical objects
  115. public float PID_D { get; private set; } // derivative
  116. public float PID_P { get; private set; } // proportional
  117. public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero
  118. public const uint GROUNDPLANE_ID = 1;
  119. public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here
  120. private float m_waterLevel;
  121. public BSTerrainManager TerrainManager { get; private set; }
  122. public ConfigurationParameters Params
  123. {
  124. get { return m_params[0]; }
  125. }
  126. public Vector3 DefaultGravity
  127. {
  128. get { return new Vector3(0f, 0f, Params.gravity); }
  129. }
  130. // Just the Z value of the gravity
  131. public float DefaultGravityZ
  132. {
  133. get { return Params.gravity; }
  134. }
  135. public float MaximumObjectMass { get; private set; }
  136. // When functions in the unmanaged code must be called, it is only
  137. // done at a known time just before the simulation step. The taint
  138. // system saves all these function calls and executes them in
  139. // order before the simulation.
  140. public delegate void TaintCallback();
  141. private struct TaintCallbackEntry
  142. {
  143. public String ident;
  144. public TaintCallback callback;
  145. public TaintCallbackEntry(string i, TaintCallback c)
  146. {
  147. ident = i;
  148. callback = c;
  149. }
  150. }
  151. private Object _taintLock = new Object(); // lock for using the next object
  152. private List<TaintCallbackEntry> _taintedObjects;
  153. // A pointer to an instance if this structure is passed to the C++ code
  154. // Used to pass basic configuration values to the unmanaged code.
  155. ConfigurationParameters[] m_params;
  156. GCHandle m_paramsHandle;
  157. // Handle to the callback used by the unmanaged code to call into the managed code.
  158. // Used for debug logging.
  159. // Need to store the handle in a persistant variable so it won't be freed.
  160. private BulletSimAPI.DebugLogCallback m_DebugLogCallbackHandle;
  161. // Sometimes you just have to log everything.
  162. public Logging.LogWriter PhysicsLogging;
  163. private bool m_physicsLoggingEnabled;
  164. private string m_physicsLoggingDir;
  165. private string m_physicsLoggingPrefix;
  166. private int m_physicsLoggingFileMinutes;
  167. // 'true' of the vehicle code is to log lots of details
  168. public bool VehicleLoggingEnabled { get; private set; }
  169. #region Construction and Initialization
  170. public BSScene(string identifier)
  171. {
  172. m_initialized = false;
  173. // we are passed the name of the region we're working for.
  174. RegionName = identifier;
  175. }
  176. public override void Initialise(IMesher meshmerizer, IConfigSource config)
  177. {
  178. mesher = meshmerizer;
  179. _taintedObjects = new List<TaintCallbackEntry>();
  180. PhysObjects = new Dictionary<uint, BSPhysObject>();
  181. Shapes = new BSShapeCollection(this);
  182. // Allocate pinned memory to pass parameters.
  183. m_params = new ConfigurationParameters[1];
  184. m_paramsHandle = GCHandle.Alloc(m_params, GCHandleType.Pinned);
  185. // Set default values for physics parameters plus any overrides from the ini file
  186. GetInitialParameterValues(config);
  187. // allocate more pinned memory close to the above in an attempt to get the memory all together
  188. m_collisionArray = new CollisionDesc[m_maxCollisionsPerFrame];
  189. m_collisionArrayPinnedHandle = GCHandle.Alloc(m_collisionArray, GCHandleType.Pinned);
  190. m_updateArray = new EntityProperties[m_maxUpdatesPerFrame];
  191. m_updateArrayPinnedHandle = GCHandle.Alloc(m_updateArray, GCHandleType.Pinned);
  192. // Enable very detailed logging.
  193. // By creating an empty logger when not logging, the log message invocation code
  194. // can be left in and every call doesn't have to check for null.
  195. if (m_physicsLoggingEnabled)
  196. {
  197. PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes);
  198. }
  199. else
  200. {
  201. PhysicsLogging = new Logging.LogWriter();
  202. }
  203. // If Debug logging level, enable logging from the unmanaged code
  204. m_DebugLogCallbackHandle = null;
  205. if (m_log.IsDebugEnabled || PhysicsLogging.Enabled)
  206. {
  207. m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader);
  208. if (PhysicsLogging.Enabled)
  209. // The handle is saved in a variable to make sure it doesn't get freed after this call
  210. m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLoggerPhysLog);
  211. else
  212. m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger);
  213. }
  214. // Get the version of the DLL
  215. // TODO: this doesn't work yet. Something wrong with marshaling the returned string.
  216. // BulletSimVersion = BulletSimAPI.GetVersion();
  217. // m_log.WarnFormat("{0}: BulletSim.dll version='{1}'", LogHeader, BulletSimVersion);
  218. // The bounding box for the simulated world. The origin is 0,0,0 unless we're
  219. // a child in a mega-region.
  220. // Bullet actually doesn't care about the extents of the simulated
  221. // area. It tracks active objects no matter where they are.
  222. Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
  223. // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader);
  224. World = new BulletSim(0, this, BulletSimAPI.Initialize2(worldExtent, m_paramsHandle.AddrOfPinnedObject(),
  225. m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(),
  226. m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject(),
  227. m_DebugLogCallbackHandle));
  228. Constraints = new BSConstraintCollection(World);
  229. TerrainManager = new BSTerrainManager(this);
  230. TerrainManager.CreateInitialGroundPlaneAndTerrain();
  231. m_initialized = true;
  232. }
  233. // All default parameter values are set here. There should be no values set in the
  234. // variable definitions.
  235. private void GetInitialParameterValues(IConfigSource config)
  236. {
  237. ConfigurationParameters parms = new ConfigurationParameters();
  238. m_params[0] = parms;
  239. SetParameterDefaultValues();
  240. if (config != null)
  241. {
  242. // If there are specifications in the ini file, use those values
  243. IConfig pConfig = config.Configs["BulletSim"];
  244. if (pConfig != null)
  245. {
  246. SetParameterConfigurationValues(pConfig);
  247. // Very detailed logging for physics debugging
  248. m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false);
  249. m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", ".");
  250. m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-");
  251. m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5);
  252. // Very detailed logging for vehicle debugging
  253. VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
  254. // Do any replacements in the parameters
  255. m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
  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. TerrainManager.ReleaseGroundPlaneAndTerrain();
  289. foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
  290. {
  291. kvp.Value.Destroy();
  292. }
  293. PhysObjects.Clear();
  294. // Now that the prims are all cleaned up, there should be no constraints left
  295. if (Constraints != null)
  296. {
  297. Constraints.Dispose();
  298. Constraints = null;
  299. }
  300. if (Shapes != null)
  301. {
  302. Shapes.Dispose();
  303. Shapes = null;
  304. }
  305. // Anything left in the unmanaged code should be cleaned out
  306. BulletSimAPI.Shutdown2(World.ptr);
  307. // Not logging any more
  308. PhysicsLogging.Close();
  309. }
  310. #endregion // Construction and Initialization
  311. #region Prim and Avatar addition and removal
  312. public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying)
  313. {
  314. m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader);
  315. return null;
  316. }
  317. public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 size, bool isFlying)
  318. {
  319. // m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName);
  320. if (!m_initialized) return null;
  321. BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying);
  322. lock (PhysObjects) PhysObjects.Add(localID, actor);
  323. // TODO: Remove kludge someday.
  324. // We must generate a collision for avatars whether they collide or not.
  325. // This is required by OpenSim to update avatar animations, etc.
  326. lock (m_avatars) m_avatars.Add(actor);
  327. return actor;
  328. }
  329. public override void RemoveAvatar(PhysicsActor actor)
  330. {
  331. // m_log.DebugFormat("{0}: RemoveAvatar", LogHeader);
  332. if (!m_initialized) return;
  333. BSCharacter bsactor = actor as BSCharacter;
  334. if (bsactor != null)
  335. {
  336. try
  337. {
  338. lock (PhysObjects) PhysObjects.Remove(actor.LocalID);
  339. // Remove kludge someday
  340. lock (m_avatars) m_avatars.Remove(bsactor);
  341. }
  342. catch (Exception e)
  343. {
  344. m_log.WarnFormat("{0}: Attempt to remove avatar that is not in physics scene: {1}", LogHeader, e);
  345. }
  346. bsactor.Destroy();
  347. // bsactor.dispose();
  348. }
  349. }
  350. public override void RemovePrim(PhysicsActor prim)
  351. {
  352. if (!m_initialized) return;
  353. BSPrim bsprim = prim as BSPrim;
  354. if (bsprim != null)
  355. {
  356. DetailLog("{0},RemovePrim,call", bsprim.LocalID);
  357. // m_log.DebugFormat("{0}: RemovePrim. id={1}/{2}", LogHeader, bsprim.Name, bsprim.LocalID);
  358. try
  359. {
  360. lock (PhysObjects) PhysObjects.Remove(bsprim.LocalID);
  361. }
  362. catch (Exception e)
  363. {
  364. m_log.ErrorFormat("{0}: Attempt to remove prim that is not in physics scene: {1}", LogHeader, e);
  365. }
  366. bsprim.Destroy();
  367. // bsprim.dispose();
  368. }
  369. else
  370. {
  371. m_log.ErrorFormat("{0}: Attempt to remove prim that is not a BSPrim type.", LogHeader);
  372. }
  373. }
  374. public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
  375. Vector3 size, Quaternion rotation, bool isPhysical, uint localID)
  376. {
  377. // m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName);
  378. if (!m_initialized) return null;
  379. DetailLog("{0},AddPrimShape,call", localID);
  380. BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, pbs, isPhysical);
  381. lock (PhysObjects) PhysObjects.Add(localID, prim);
  382. return prim;
  383. }
  384. // This is a call from the simulator saying that some physical property has been updated.
  385. // The BulletSim driver senses the changing of relevant properties so this taint
  386. // information call is not needed.
  387. public override void AddPhysicsActorTaint(PhysicsActor prim) { }
  388. #endregion // Prim and Avatar addition and removal
  389. #region Simulation
  390. // Simulate one timestep
  391. public override float Simulate(float timeStep)
  392. {
  393. int updatedEntityCount = 0;
  394. IntPtr updatedEntitiesPtr;
  395. int collidersCount = 0;
  396. IntPtr collidersPtr;
  397. int beforeTime = 0;
  398. int simTime = 0;
  399. // prevent simulation until we've been initialized
  400. if (!m_initialized) return 5.0f;
  401. // update the prim states while we know the physics engine is not busy
  402. int numTaints = _taintedObjects.Count;
  403. ProcessTaints();
  404. // Some of the prims operate with special vehicle properties
  405. ProcessVehicles(timeStep);
  406. numTaints += _taintedObjects.Count;
  407. ProcessTaints(); // the vehicles might have added taints
  408. // step the physical world one interval
  409. m_simulationStep++;
  410. int numSubSteps = 0;
  411. // DEBUG
  412. // DetailLog("{0},BSScene.Simulate,beforeStep,ntaimts={1},step={2}", DetailLogZero, numTaints, m_simulationStep);
  413. try
  414. {
  415. if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount();
  416. numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep,
  417. out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr);
  418. if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime);
  419. DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}",
  420. DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount);
  421. }
  422. catch (Exception e)
  423. {
  424. m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}",
  425. LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e);
  426. DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}",
  427. DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount);
  428. updatedEntityCount = 0;
  429. collidersCount = 0;
  430. }
  431. // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in
  432. // Get a value for 'now' so all the collision and update routines don't have to get their own
  433. SimulationNowTime = Util.EnvironmentTickCount();
  434. // If there were collisions, process them by sending the event to the prim.
  435. // Collisions must be processed before updates.
  436. if (collidersCount > 0)
  437. {
  438. for (int ii = 0; ii < collidersCount; ii++)
  439. {
  440. uint cA = m_collisionArray[ii].aID;
  441. uint cB = m_collisionArray[ii].bID;
  442. Vector3 point = m_collisionArray[ii].point;
  443. Vector3 normal = m_collisionArray[ii].normal;
  444. SendCollision(cA, cB, point, normal, 0.01f);
  445. SendCollision(cB, cA, point, -normal, 0.01f);
  446. }
  447. }
  448. // The above SendCollision's batch up the collisions on the objects.
  449. // Now push the collisions into the simulator.
  450. if (ObjectsWithCollisions.Count > 0)
  451. {
  452. foreach (BSPhysObject bsp in ObjectsWithCollisions)
  453. if (!bsp.SendCollisions())
  454. {
  455. // If the object is done colliding, see that it's removed from the colliding list
  456. ObjectsWithNoMoreCollisions.Add(bsp);
  457. }
  458. }
  459. // This is a kludge to get avatar movement updates.
  460. // The simulator expects collisions for avatars even if there are have been no collisions.
  461. // The event updates avatar animations and stuff.
  462. // If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
  463. foreach (BSPhysObject bsp in m_avatars)
  464. if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice
  465. bsp.SendCollisions();
  466. // Objects that are done colliding are removed from the ObjectsWithCollisions list.
  467. // Not done above because it is inside an iteration of ObjectWithCollisions.
  468. if (ObjectsWithNoMoreCollisions.Count > 0)
  469. {
  470. foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
  471. ObjectsWithCollisions.Remove(po);
  472. ObjectsWithNoMoreCollisions.Clear();
  473. }
  474. // If any of the objects had updated properties, tell the object it has been changed by the physics engine
  475. if (updatedEntityCount > 0)
  476. {
  477. for (int ii = 0; ii < updatedEntityCount; ii++)
  478. {
  479. EntityProperties entprop = m_updateArray[ii];
  480. BSPhysObject pobj;
  481. if (PhysObjects.TryGetValue(entprop.ID, out pobj))
  482. {
  483. pobj.UpdateProperties(entprop);
  484. }
  485. }
  486. }
  487. // This causes the unmanaged code to output ALL the values found in ALL the objects in the world.
  488. // Only enable this in a limited test world with few objects.
  489. // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG
  490. // The physics engine returns the number of milliseconds it simulated this call.
  491. // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
  492. // We multiply by 55 to give a recognizable running rate (55 or less).
  493. return numSubSteps * m_fixedTimeStep * 1000 * 55;
  494. // return timeStep * 1000 * 55;
  495. }
  496. // Something has collided
  497. private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration)
  498. {
  499. if (localID <= TerrainManager.HighestTerrainID)
  500. {
  501. return; // don't send collisions to the terrain
  502. }
  503. BSPhysObject collider;
  504. if (!PhysObjects.TryGetValue(localID, out collider))
  505. {
  506. // If the object that is colliding cannot be found, just ignore the collision.
  507. DetailLog("{0},BSScene.SendCollision,colliderNotInObjectList,id={1},with={2}", DetailLogZero, localID, collidingWith);
  508. return;
  509. }
  510. // The terrain is not in the physical object list so 'collidee' can be null when Collide() is called.
  511. BSPhysObject collidee = null;
  512. PhysObjects.TryGetValue(collidingWith, out collidee);
  513. // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
  514. if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
  515. {
  516. // If a collision was posted, remember to send it to the simulator
  517. ObjectsWithCollisions.Add(collider);
  518. }
  519. return;
  520. }
  521. #endregion // Simulation
  522. public override void GetResults() { }
  523. #region Terrain
  524. public override void SetTerrain(float[] heightMap) {
  525. TerrainManager.SetTerrain(heightMap);
  526. }
  527. public override void SetWaterLevel(float baseheight)
  528. {
  529. m_waterLevel = baseheight;
  530. }
  531. // Someday....
  532. public float GetWaterLevelAtXYZ(Vector3 loc)
  533. {
  534. return m_waterLevel;
  535. }
  536. public override void DeleteTerrain()
  537. {
  538. // m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader);
  539. }
  540. // Although no one seems to check this, I do support combining.
  541. public override bool SupportsCombining()
  542. {
  543. return TerrainManager.SupportsCombining();
  544. }
  545. // This call says I am a child to region zero in a mega-region. 'pScene' is that
  546. // of region zero, 'offset' is my offset from regions zero's origin, and
  547. // 'extents' is the largest XY that is handled in my region.
  548. public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
  549. {
  550. TerrainManager.Combine(pScene, offset, extents);
  551. }
  552. // Unhook all the combining that I know about.
  553. public override void UnCombine(PhysicsScene pScene)
  554. {
  555. TerrainManager.UnCombine(pScene);
  556. }
  557. #endregion // Terrain
  558. public override Dictionary<uint, float> GetTopColliders()
  559. {
  560. return new Dictionary<uint, float>();
  561. }
  562. public override bool IsThreaded { get { return false; } }
  563. // Calls to the PhysicsActors can't directly call into the physics engine
  564. // because it might be busy. We delay changes to a known time.
  565. // We rely on C#'s closure to save and restore the context for the delegate.
  566. public void TaintedObject(String ident, TaintCallback callback)
  567. {
  568. if (!m_initialized) return;
  569. lock (_taintLock)
  570. {
  571. _taintedObjects.Add(new TaintCallbackEntry(ident, callback));
  572. }
  573. return;
  574. }
  575. // When someone tries to change a property on a BSPrim or BSCharacter, the object queues
  576. // a callback into itself to do the actual property change. That callback is called
  577. // here just before the physics engine is called to step the simulation.
  578. public void ProcessTaints()
  579. {
  580. if (_taintedObjects.Count > 0) // save allocating new list if there is nothing to process
  581. {
  582. int taintCount = m_taintsToProcessPerStep;
  583. TaintCallbackEntry oneCallback = new TaintCallbackEntry();
  584. while (_taintedObjects.Count > 0 && taintCount-- > 0)
  585. {
  586. bool gotOne = false;
  587. lock (_taintLock)
  588. {
  589. if (_taintedObjects.Count > 0)
  590. {
  591. oneCallback = _taintedObjects[0];
  592. _taintedObjects.RemoveAt(0);
  593. gotOne = true;
  594. }
  595. }
  596. if (gotOne)
  597. {
  598. try
  599. {
  600. DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, oneCallback.ident);
  601. oneCallback.callback();
  602. }
  603. catch (Exception e)
  604. {
  605. DetailLog("{0},BSScene.ProcessTaints,doTaintException,id={1}", DetailLogZero, oneCallback.ident); // DEBUG DEBUG DEBUG
  606. m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, oneCallback.ident, e);
  607. }
  608. }
  609. }
  610. /*
  611. // swizzle a new list into the list location so we can process what's there
  612. List<TaintCallbackEntry> oldList;
  613. lock (_taintLock)
  614. {
  615. oldList = _taintedObjects;
  616. _taintedObjects = new List<TaintCallbackEntry>();
  617. }
  618. foreach (TaintCallbackEntry tcbe in oldList)
  619. {
  620. try
  621. {
  622. DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG
  623. tcbe.callback();
  624. }
  625. catch (Exception e)
  626. {
  627. m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e);
  628. }
  629. }
  630. oldList.Clear();
  631. */
  632. }
  633. }
  634. #region Vehicles
  635. public void VehicleInSceneTypeChanged(BSPrim vehic, Vehicle newType)
  636. {
  637. RemoveVehiclePrim(vehic);
  638. if (newType != Vehicle.TYPE_NONE)
  639. {
  640. // make it so the scene will call us each tick to do vehicle things
  641. AddVehiclePrim(vehic);
  642. }
  643. }
  644. // Make so the scene will call this prim for vehicle actions each tick.
  645. // Safe to call if prim is already in the vehicle list.
  646. public void AddVehiclePrim(BSPrim vehicle)
  647. {
  648. lock (m_vehicles)
  649. {
  650. if (!m_vehicles.Contains(vehicle))
  651. {
  652. m_vehicles.Add(vehicle);
  653. }
  654. }
  655. }
  656. // Remove a prim from our list of vehicles.
  657. // Safe to call if the prim is not in the vehicle list.
  658. public void RemoveVehiclePrim(BSPrim vehicle)
  659. {
  660. lock (m_vehicles)
  661. {
  662. if (m_vehicles.Contains(vehicle))
  663. {
  664. m_vehicles.Remove(vehicle);
  665. }
  666. }
  667. }
  668. // Some prims have extra vehicle actions
  669. // Called at taint time!
  670. private void ProcessVehicles(float timeStep)
  671. {
  672. foreach (BSPhysObject pobj in m_vehicles)
  673. {
  674. pobj.StepVehicle(timeStep);
  675. }
  676. }
  677. #endregion Vehicles
  678. #region INI and command line parameter processing
  679. delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val);
  680. delegate float ParamGet(BSScene scene);
  681. delegate void ParamSet(BSScene scene, string paramName, uint localID, float val);
  682. delegate void SetOnObject(BSScene scene, BSPhysObject obj, float val);
  683. private struct ParameterDefn
  684. {
  685. public string name; // string name of the parameter
  686. public string desc; // a short description of what the parameter means
  687. public float defaultValue; // default value if not specified anywhere else
  688. public ParamUser userParam; // get the value from the configuration file
  689. public ParamGet getter; // return the current value stored for this parameter
  690. public ParamSet setter; // set the current value for this parameter
  691. public SetOnObject onObject; // set the value on an object in the physical domain
  692. public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s)
  693. {
  694. name = n;
  695. desc = d;
  696. defaultValue = v;
  697. userParam = u;
  698. getter = g;
  699. setter = s;
  700. onObject = null;
  701. }
  702. public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s, SetOnObject o)
  703. {
  704. name = n;
  705. desc = d;
  706. defaultValue = v;
  707. userParam = u;
  708. getter = g;
  709. setter = s;
  710. onObject = o;
  711. }
  712. }
  713. // List of all of the externally visible parameters.
  714. // For each parameter, this table maps a text name to getter and setters.
  715. // To add a new externally referencable/settable parameter, add the paramter storage
  716. // location somewhere in the program and make an entry in this table with the
  717. // getters and setters.
  718. // It is easiest to find an existing definition and copy it.
  719. // Parameter values are floats. Booleans are converted to a floating value.
  720. //
  721. // A ParameterDefn() takes the following parameters:
  722. // -- the text name of the parameter. This is used for console input and ini file.
  723. // -- a short text description of the parameter. This shows up in the console listing.
  724. // -- a delegate for fetching the parameter from the ini file.
  725. // Should handle fetching the right type from the ini file and converting it.
  726. // -- a delegate for getting the value as a float
  727. // -- a delegate for setting the value from a float
  728. //
  729. // The single letter parameters for the delegates are:
  730. // s = BSScene
  731. // o = BSPhysObject
  732. // p = string parameter name
  733. // l = localID of referenced object
  734. // v = float value
  735. // cf = parameter configuration class (for fetching values from ini file)
  736. private ParameterDefn[] ParameterDefinitions =
  737. {
  738. new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties",
  739. ConfigurationParameters.numericTrue,
  740. (s,cf,p,v) => { s.ShouldMeshSculptedPrim = cf.GetBoolean(p, s.BoolNumeric(v)); },
  741. (s) => { return s.NumericBool(s.ShouldMeshSculptedPrim); },
  742. (s,p,l,v) => { s.ShouldMeshSculptedPrim = s.BoolNumeric(v); } ),
  743. new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects",
  744. ConfigurationParameters.numericFalse,
  745. (s,cf,p,v) => { s.ShouldForceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); },
  746. (s) => { return s.NumericBool(s.ShouldForceSimplePrimMeshing); },
  747. (s,p,l,v) => { s.ShouldForceSimplePrimMeshing = s.BoolNumeric(v); } ),
  748. new ParameterDefn("UseHullsForPhysicalObjects", "If true, create hulls for physical objects",
  749. ConfigurationParameters.numericTrue,
  750. (s,cf,p,v) => { s.ShouldUseHullsForPhysicalObjects = cf.GetBoolean(p, s.BoolNumeric(v)); },
  751. (s) => { return s.NumericBool(s.ShouldUseHullsForPhysicalObjects); },
  752. (s,p,l,v) => { s.ShouldUseHullsForPhysicalObjects = s.BoolNumeric(v); } ),
  753. new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)",
  754. 8f,
  755. (s,cf,p,v) => { s.MeshLOD = (float)cf.GetInt(p, (int)v); },
  756. (s) => { return s.MeshLOD; },
  757. (s,p,l,v) => { s.MeshLOD = v; } ),
  758. new ParameterDefn("MeshLevelOfDetailMegaPrim", "Level of detail to render meshes larger than threshold meters",
  759. 16f,
  760. (s,cf,p,v) => { s.MeshMegaPrimLOD = (float)cf.GetInt(p, (int)v); },
  761. (s) => { return s.MeshMegaPrimLOD; },
  762. (s,p,l,v) => { s.MeshMegaPrimLOD = v; } ),
  763. new ParameterDefn("MeshLevelOfDetailMegaPrimThreshold", "Size (in meters) of a mesh before using MeshMegaPrimLOD",
  764. 10f,
  765. (s,cf,p,v) => { s.MeshMegaPrimThreshold = (float)cf.GetInt(p, (int)v); },
  766. (s) => { return s.MeshMegaPrimThreshold; },
  767. (s,p,l,v) => { s.MeshMegaPrimThreshold = v; } ),
  768. new ParameterDefn("SculptLevelOfDetail", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)",
  769. 32f,
  770. (s,cf,p,v) => { s.SculptLOD = (float)cf.GetInt(p, (int)v); },
  771. (s) => { return s.SculptLOD; },
  772. (s,p,l,v) => { s.SculptLOD = v; } ),
  773. new ParameterDefn("MaxSubStep", "In simulation step, maximum number of substeps",
  774. 10f,
  775. (s,cf,p,v) => { s.m_maxSubSteps = cf.GetInt(p, (int)v); },
  776. (s) => { return (float)s.m_maxSubSteps; },
  777. (s,p,l,v) => { s.m_maxSubSteps = (int)v; } ),
  778. new ParameterDefn("FixedTimeStep", "In simulation step, seconds of one substep (1/60)",
  779. 1f / 60f,
  780. (s,cf,p,v) => { s.m_fixedTimeStep = cf.GetFloat(p, v); },
  781. (s) => { return (float)s.m_fixedTimeStep; },
  782. (s,p,l,v) => { s.m_fixedTimeStep = v; } ),
  783. new ParameterDefn("MaxCollisionsPerFrame", "Max collisions returned at end of each frame",
  784. 2048f,
  785. (s,cf,p,v) => { s.m_maxCollisionsPerFrame = cf.GetInt(p, (int)v); },
  786. (s) => { return (float)s.m_maxCollisionsPerFrame; },
  787. (s,p,l,v) => { s.m_maxCollisionsPerFrame = (int)v; } ),
  788. new ParameterDefn("MaxUpdatesPerFrame", "Max updates returned at end of each frame",
  789. 8000f,
  790. (s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); },
  791. (s) => { return (float)s.m_maxUpdatesPerFrame; },
  792. (s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ),
  793. new ParameterDefn("MaxTaintsToProcessPerStep", "Number of update taints to process before each simulation step",
  794. 100f,
  795. (s,cf,p,v) => { s.m_taintsToProcessPerStep = cf.GetInt(p, (int)v); },
  796. (s) => { return (float)s.m_taintsToProcessPerStep; },
  797. (s,p,l,v) => { s.m_taintsToProcessPerStep = (int)v; } ),
  798. new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)",
  799. 10000.01f,
  800. (s,cf,p,v) => { s.MaximumObjectMass = cf.GetFloat(p, v); },
  801. (s) => { return (float)s.MaximumObjectMass; },
  802. (s,p,l,v) => { s.MaximumObjectMass = v; } ),
  803. new ParameterDefn("PID_D", "Derivitive factor for motion smoothing",
  804. 2200f,
  805. (s,cf,p,v) => { s.PID_D = cf.GetFloat(p, v); },
  806. (s) => { return (float)s.PID_D; },
  807. (s,p,l,v) => { s.PID_D = v; } ),
  808. new ParameterDefn("PID_P", "Parameteric factor for motion smoothing",
  809. 900f,
  810. (s,cf,p,v) => { s.PID_P = cf.GetFloat(p, v); },
  811. (s) => { return (float)s.PID_P; },
  812. (s,p,l,v) => { s.PID_P = v; } ),
  813. new ParameterDefn("DefaultFriction", "Friction factor used on new objects",
  814. 0.5f,
  815. (s,cf,p,v) => { s.m_params[0].defaultFriction = cf.GetFloat(p, v); },
  816. (s) => { return s.m_params[0].defaultFriction; },
  817. (s,p,l,v) => { s.m_params[0].defaultFriction = v; } ),
  818. new ParameterDefn("DefaultDensity", "Density for new objects" ,
  819. 10.000006836f, // Aluminum g/cm3
  820. (s,cf,p,v) => { s.m_params[0].defaultDensity = cf.GetFloat(p, v); },
  821. (s) => { return s.m_params[0].defaultDensity; },
  822. (s,p,l,v) => { s.m_params[0].defaultDensity = v; } ),
  823. new ParameterDefn("DefaultRestitution", "Bouncyness of an object" ,
  824. 0f,
  825. (s,cf,p,v) => { s.m_params[0].defaultRestitution = cf.GetFloat(p, v); },
  826. (s) => { return s.m_params[0].defaultRestitution; },
  827. (s,p,l,v) => { s.m_params[0].defaultRestitution = v; } ),
  828. new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)",
  829. 0f,
  830. (s,cf,p,v) => { s.m_params[0].collisionMargin = cf.GetFloat(p, v); },
  831. (s) => { return s.m_params[0].collisionMargin; },
  832. (s,p,l,v) => { s.m_params[0].collisionMargin = v; } ),
  833. new ParameterDefn("Gravity", "Vertical force of gravity (negative means down)",
  834. -9.80665f,
  835. (s,cf,p,v) => { s.m_params[0].gravity = cf.GetFloat(p, v); },
  836. (s) => { return s.m_params[0].gravity; },
  837. (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].gravity, p, PhysParameterEntry.APPLY_TO_NONE, v); },
  838. (s,o,v) => { BulletSimAPI.SetGravity2(s.World.ptr, new Vector3(0f,0f,v)); } ),
  839. new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)",
  840. 0f,
  841. (s,cf,p,v) => { s.m_params[0].linearDamping = cf.GetFloat(p, v); },
  842. (s) => { return s.m_params[0].linearDamping; },
  843. (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearDamping, p, l, v); },
  844. (s,o,v) => { BulletSimAPI.SetDamping2(o.BSBody.ptr, v, v); } ),
  845. new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)",
  846. 0f,
  847. (s,cf,p,v) => { s.m_params[0].angularDamping = cf.GetFloat(p, v); },
  848. (s) => { return s.m_params[0].angularDamping; },
  849. (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularDamping, p, l, v); },
  850. (s,o,v) => { BulletSimAPI.SetDamping2(o.BSBody.ptr, v, v); } ),
  851. new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static",
  852. 0.2f,
  853. (s,cf,p,v) => { s.m_params[0].deactivationTime = cf.GetFloat(p, v); },
  854. (s) => { return s.m_params[0].deactivationTime; },
  855. (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].deactivationTime, p, l, v); },
  856. (s,o,v) => { BulletSimAPI.SetDeactivationTime2(o.BSBody.ptr, v); } ),
  857. new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static",
  858. 0.8f,
  859. (s,cf,p,v) => { s.m_params[0].linearSleepingThreshold = cf.GetFloat(p, v); },
  860. (s) => { return s.m_params[0].linearSleepingThreshold; },
  861. (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearSleepingThreshold, p, l, v); },
  862. (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.BSBody.ptr, v, v); } ),
  863. new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static",
  864. 1.0f,
  865. (s,cf,p,v) => { s.m_params[0].angularSleepingThreshold = cf.GetFloat(p, v); },
  866. (s) => { return s.m_params[0].angularSleepingThreshold; },
  867. (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularSleepingThreshold, p, l, v); },
  868. (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.BSBody.ptr, v, v); } ),
  869. new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" ,
  870. 0f, // set to zero to disable
  871. (s,cf,p,v) => { s.m_params[0].ccdMotionThreshold = cf.GetFloat(p, v); },
  872. (s) => { return s.m_params[0].ccdMotionThreshold; },
  873. (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdMotionThreshold, p, l, v); },
  874. (s,o,v) => { BulletSimAPI.SetCcdMotionThreshold2(o.BSBody.ptr, v); } ),
  875. new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" ,
  876. 0f,
  877. (s,cf,p,v) => { s.m_params[0].ccdSweptSphereRadius = cf.GetFloat(p, v); },
  878. (s) => { return s.m_params[0].ccdSweptSphereRadius; },
  879. (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); },
  880. (s,o,v) => { BulletSimAPI.SetCcdSweepSphereRadius2(o.BSBody.ptr, v); } ),
  881. new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" ,
  882. 0.1f,
  883. (s,cf,p,v) => { s.m_params[0].contactProcessingThreshold = cf.GetFloat(p, v); },
  884. (s) => { return s.m_params[0].contactProcessingThreshold; },
  885. (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); },
  886. (s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.BSBody.ptr, v); } ),
  887. new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" ,
  888. 0.5f,
  889. (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); },
  890. (s) => { return s.m_params[0].terrainFriction; },
  891. (s,p,l,v) => { s.m_params[0].terrainFriction = v; /* TODO: set on real terrain */} ),
  892. new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" ,
  893. 0.8f,
  894. (s,cf,p,v) => { s.m_params[0].terrainHitFraction = cf.GetFloat(p, v); },
  895. (s) => { return s.m_params[0].terrainHitFraction; },
  896. (s,p,l,v) => { s.m_params[0].terrainHitFraction = v; /* TODO: set on real terrain */ } ),
  897. new ParameterDefn("TerrainRestitution", "Bouncyness" ,
  898. 0f,
  899. (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); },
  900. (s) => { return s.m_params[0].terrainRestitution; },
  901. (s,p,l,v) => { s.m_params[0].terrainRestitution = v; /* TODO: set on real terrain */ } ),
  902. new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.",
  903. 0.2f,
  904. (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); },
  905. (s) => { return s.m_params[0].avatarFriction; },
  906. (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ),
  907. new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.",
  908. 10f,
  909. (s,cf,p,v) => { s.m_params[0].avatarStandingFriction = cf.GetFloat(p, v); },
  910. (s) => { return s.m_params[0].avatarStandingFriction; },
  911. (s,p,l,v) => { s.m_params[0].avatarStandingFriction = v; } ),
  912. new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.",
  913. 60f,
  914. (s,cf,p,v) => { s.m_params[0].avatarDensity = cf.GetFloat(p, v); },
  915. (s) => { return s.m_params[0].avatarDensity; },
  916. (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarDensity, p, l, v); } ),
  917. new ParameterDefn("AvatarRestitution", "Bouncyness. Changed on avatar recreation.",
  918. 0f,
  919. (s,cf,p,v) => { s.m_params[0].avatarRestitution = cf.GetFloat(p, v); },
  920. (s) => { return s.m_params[0].avatarRestitution; },
  921. (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarRestitution, p, l, v); } ),
  922. new ParameterDefn("AvatarCapsuleRadius", "Radius of space around an avatar",
  923. 0.37f,
  924. (s,cf,p,v) => { s.m_params[0].avatarCapsuleRadius = cf.GetFloat(p, v); },
  925. (s) => { return s.m_params[0].avatarCapsuleRadius; },
  926. (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleRadius, p, l, v); } ),
  927. new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar",
  928. 1.5f,
  929. (s,cf,p,v) => { s.m_params[0].avatarCapsuleHeight = cf.GetFloat(p, v); },
  930. (s) => { return s.m_params[0].avatarCapsuleHeight; },
  931. (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleHeight, p, l, v); } ),
  932. new ParameterDefn("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions",
  933. 0.1f,
  934. (s,cf,p,v) => { s.m_params[0].avatarContactProcessingThreshold = cf.GetFloat(p, v); },
  935. (s) => { return s.m_params[0].avatarContactProcessingThreshold; },
  936. (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ),
  937. new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)",
  938. 0f,
  939. (s,cf,p,v) => { s.m_params[0].maxPersistantManifoldPoolSize = cf.GetFloat(p, v); },
  940. (s) => { return s.m_params[0].maxPersistantManifoldPoolSize; },
  941. (s,p,l,v) => { s.m_params[0].maxPersistantManifoldPoolSize = v; } ),
  942. new ParameterDefn("MaxCollisionAlgorithmPoolSize", "Number of collisions pooled (0 means default of 4096)",
  943. 0f,
  944. (s,cf,p,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = cf.GetFloat(p, v); },
  945. (s) => { return s.m_params[0].maxCollisionAlgorithmPoolSize; },
  946. (s,p,l,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = v; } ),
  947. new ParameterDefn("ShouldDisableContactPoolDynamicAllocation", "Enable to allow large changes in object count",
  948. ConfigurationParameters.numericFalse,
  949. (s,cf,p,v) => { s.m_params[0].shouldDisableContactPoolDynamicAllocation = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
  950. (s) => { return s.m_params[0].shouldDisableContactPoolDynamicAllocation; },
  951. (s,p,l,v) => { s.m_params[0].shouldDisableContactPoolDynamicAllocation = v; } ),
  952. new ParameterDefn("ShouldForceUpdateAllAabbs", "Enable to recomputer AABBs every simulator step",
  953. ConfigurationParameters.numericFalse,
  954. (s,cf,p,v) => { s.m_params[0].shouldForceUpdateAllAabbs = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
  955. (s) => { return s.m_params[0].shouldForceUpdateAllAabbs; },
  956. (s,p,l,v) => { s.m_params[0].shouldForceUpdateAllAabbs = v; } ),
  957. new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction",
  958. ConfigurationParameters.numericTrue,
  959. (s,cf,p,v) => { s.m_params[0].shouldRandomizeSolverOrder = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
  960. (s) => { return s.m_params[0].shouldRandomizeSolverOrder; },
  961. (s,p,l,v) => { s.m_params[0].shouldRandomizeSolverOrder = v; } ),
  962. new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands",
  963. ConfigurationParameters.numericTrue,
  964. (s,cf,p,v) => { s.m_params[0].shouldSplitSimulationIslands = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
  965. (s) => { return s.m_params[0].shouldSplitSimulationIslands; },
  966. (s,p,l,v) => { s.m_params[0].shouldSplitSimulationIslands = v; } ),
  967. new ParameterDefn("ShouldEnableFrictionCaching", "Enable friction computation caching",
  968. ConfigurationParameters.numericFalse,
  969. (s,cf,p,v) => { s.m_params[0].shouldEnableFrictionCaching = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
  970. (s) => { return s.m_params[0].shouldEnableFrictionCaching; },
  971. (s,p,l,v) => { s.m_params[0].shouldEnableFrictionCaching = v; } ),
  972. new ParameterDefn("NumberOfSolverIterations", "Number of internal iterations (0 means default)",
  973. 0f, // zero says use Bullet default
  974. (s,cf,p,v) => { s.m_params[0].numberOfSolverIterations = cf.GetFloat(p, v); },
  975. (s) => { return s.m_params[0].numberOfSolverIterations; },
  976. (s,p,l,v) => { s.m_params[0].numberOfSolverIterations = v; } ),
  977. new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.",
  978. ConfigurationParameters.numericFalse,
  979. (s,cf,p,v) => { s.m_params[0].linkConstraintUseFrameOffset = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
  980. (s) => { return s.m_params[0].linkConstraintUseFrameOffset; },
  981. (s,p,l,v) => { s.m_params[0].linkConstraintUseFrameOffset = v; } ),
  982. new ParameterDefn("LinkConstraintEnableTransMotor", "Whether to enable translational motor on linkset constraints",
  983. ConfigurationParameters.numericTrue,
  984. (s,cf,p,v) => { s.m_params[0].linkConstraintEnableTransMotor = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
  985. (s) => { return s.m_params[0].linkConstraintEnableTransMotor; },
  986. (s,p,l,v) => { s.m_params[0].linkConstraintEnableTransMotor = v; } ),
  987. new ParameterDefn("LinkConstraintTransMotorMaxVel", "Maximum velocity to be applied by translational motor in linkset constraints",
  988. 5.0f,
  989. (s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxVel = cf.GetFloat(p, v); },
  990. (s) => { return s.m_params[0].linkConstraintTransMotorMaxVel; },
  991. (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxVel = v; } ),
  992. new ParameterDefn("LinkConstraintTransMotorMaxForce", "Maximum force to be applied by translational motor in linkset constraints",
  993. 0.1f,
  994. (s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = cf.GetFloat(p, v); },
  995. (s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; },
  996. (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ),
  997. new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1",
  998. 0.001f,
  999. (s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); },
  1000. (s) => { return s.m_params[0].linkConstraintCFM; },
  1001. (s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ),
  1002. new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2",
  1003. 0.8f,
  1004. (s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); },
  1005. (s) => { return s.m_params[0].linkConstraintERP; },
  1006. (s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ),
  1007. new ParameterDefn("LinkConstraintSolverIterations", "Number of solver iterations when computing constraint. (0 = Bullet default)",
  1008. 40,
  1009. (s,cf,p,v) => { s.m_params[0].linkConstraintSolverIterations = cf.GetFloat(p, v); },
  1010. (s) => { return s.m_params[0].linkConstraintSolverIterations; },
  1011. (s,p,l,v) => { s.m_params[0].linkConstraintSolverIterations = v; } ),
  1012. new ParameterDefn("LogPhysicsStatisticsFrames", "Frames between outputting detailed phys stats. (0 is off)",
  1013. 0f,
  1014. (s,cf,p,v) => { s.m_params[0].physicsLoggingFrames = cf.GetInt(p, (int)v); },
  1015. (s) => { return (float)s.m_params[0].physicsLoggingFrames; },
  1016. (s,p,l,v) => { s.m_params[0].physicsLoggingFrames = (int)v; } ),
  1017. };
  1018. // Convert a boolean to our numeric true and false values
  1019. public float NumericBool(bool b)
  1020. {
  1021. return (b ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse);
  1022. }
  1023. // Convert numeric true and false values to a boolean
  1024. public bool BoolNumeric(float b)
  1025. {
  1026. return (b == ConfigurationParameters.numericTrue ? true : false);
  1027. }
  1028. // Search through the parameter definitions and return the matching
  1029. // ParameterDefn structure.
  1030. // Case does not matter as names are compared after converting to lower case.
  1031. // Returns 'false' if the parameter is not found.
  1032. private bool TryGetParameter(string paramName, out ParameterDefn defn)
  1033. {
  1034. bool ret = false;
  1035. ParameterDefn foundDefn = new ParameterDefn();
  1036. string pName = paramName.ToLower();
  1037. foreach (ParameterDefn parm in ParameterDefinitions)
  1038. {
  1039. if (pName == parm.name.ToLower())
  1040. {
  1041. foundDefn = parm;
  1042. ret = true;
  1043. break;
  1044. }
  1045. }
  1046. defn = foundDefn;
  1047. return ret;
  1048. }
  1049. // Pass through the settable parameters and set the default values
  1050. private void SetParameterDefaultValues()
  1051. {
  1052. foreach (ParameterDefn parm in ParameterDefinitions)
  1053. {
  1054. parm.setter(this, parm.name, PhysParameterEntry.APPLY_TO_NONE, parm.defaultValue);
  1055. }
  1056. }
  1057. // Get user set values out of the ini file.
  1058. private void SetParameterConfigurationValues(IConfig cfg)
  1059. {
  1060. foreach (ParameterDefn parm in ParameterDefinitions)
  1061. {
  1062. parm.userParam(this, cfg, parm.name, parm.defaultValue);
  1063. }
  1064. }
  1065. private PhysParameterEntry[] SettableParameters = new PhysParameterEntry[1];
  1066. // This creates an array in the correct format for returning the list of
  1067. // parameters. This is used by the 'list' option of the 'physics' command.
  1068. private void BuildParameterTable()
  1069. {
  1070. if (SettableParameters.Length < ParameterDefinitions.Length)
  1071. {
  1072. List<PhysParameterEntry> entries = new List<PhysParameterEntry>();
  1073. for (int ii = 0; ii < ParameterDefinitions.Length; ii++)
  1074. {
  1075. ParameterDefn pd = ParameterDefinitions[ii];
  1076. entries.Add(new PhysParameterEntry(pd.name, pd.desc));
  1077. }
  1078. // make the list in alphabetical order for estetic reasons
  1079. entries.Sort(delegate(PhysParameterEntry ppe1, PhysParameterEntry ppe2)
  1080. {
  1081. return ppe1.name.CompareTo(ppe2.name);
  1082. });
  1083. SettableParameters = entries.ToArray();
  1084. }
  1085. }
  1086. #region IPhysicsParameters
  1087. // Get the list of parameters this physics engine supports
  1088. public PhysParameterEntry[] GetParameterList()
  1089. {
  1090. BuildParameterTable();
  1091. return SettableParameters;
  1092. }
  1093. // Set parameter on a specific or all instances.
  1094. // Return 'false' if not able to set the parameter.
  1095. // Setting the value in the m_params block will change the value the physics engine
  1096. // will use the next time since it's pinned and shared memory.
  1097. // Some of the values require calling into the physics engine to get the new
  1098. // value activated ('terrainFriction' for instance).
  1099. public bool SetPhysicsParameter(string parm, float val, uint localID)
  1100. {
  1101. bool ret = false;
  1102. ParameterDefn theParam;
  1103. if (TryGetParameter(parm, out theParam))
  1104. {
  1105. theParam.setter(this, parm, localID, val);
  1106. ret = true;
  1107. }
  1108. return ret;
  1109. }
  1110. // update all the localIDs specified
  1111. // If the local ID is APPLY_TO_NONE, just change the default value
  1112. // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs
  1113. // If the localID is a specific object, apply the parameter change to only that object
  1114. protected void UpdateParameterObject(ref float defaultLoc, string parm, uint localID, float val)
  1115. {
  1116. List<uint> objectIDs = new List<uint>();
  1117. switch (localID)
  1118. {
  1119. case PhysParameterEntry.APPLY_TO_NONE:
  1120. defaultLoc = val; // setting only the default value
  1121. // This will cause a call into the physical world if some operation is specified (SetOnObject).
  1122. objectIDs.Add(TERRAIN_ID);
  1123. TaintedUpdateParameter(parm, objectIDs, val);
  1124. break;
  1125. case PhysParameterEntry.APPLY_TO_ALL:
  1126. defaultLoc = val; // setting ALL also sets the default value
  1127. lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys);
  1128. TaintedUpdateParameter(parm, objectIDs, val);
  1129. break;
  1130. default:
  1131. // setting only one localID
  1132. objectIDs.Add(localID);
  1133. TaintedUpdateParameter(parm, objectIDs, val);
  1134. break;
  1135. }
  1136. }
  1137. // schedule the actual updating of the paramter to when the phys engine is not busy
  1138. protected void TaintedUpdateParameter(string parm, List<uint> lIDs, float val)
  1139. {
  1140. float xval = val;
  1141. List<uint> xlIDs = lIDs;
  1142. string xparm = parm;
  1143. TaintedObject("BSScene.UpdateParameterSet", delegate() {
  1144. ParameterDefn thisParam;
  1145. if (TryGetParameter(xparm, out thisParam))
  1146. {
  1147. if (thisParam.onObject != null)
  1148. {
  1149. foreach (uint lID in xlIDs)
  1150. {
  1151. BSPhysObject theObject = null;
  1152. PhysObjects.TryGetValue(lID, out theObject);
  1153. thisParam.onObject(this, theObject, xval);
  1154. }
  1155. }
  1156. }
  1157. });
  1158. }
  1159. // Get parameter.
  1160. // Return 'false' if not able to get the parameter.
  1161. public bool GetPhysicsParameter(string parm, out float value)
  1162. {
  1163. float val = 0f;
  1164. bool ret = false;
  1165. ParameterDefn theParam;
  1166. if (TryGetParameter(parm, out theParam))
  1167. {
  1168. val = theParam.getter(this);
  1169. ret = true;
  1170. }
  1171. value = val;
  1172. return ret;
  1173. }
  1174. #endregion IPhysicsParameters
  1175. #endregion Runtime settable parameters
  1176. // Invoke the detailed logger and output something if it's enabled.
  1177. public void DetailLog(string msg, params Object[] args)
  1178. {
  1179. PhysicsLogging.Write(msg, args);
  1180. // Add the Flush() if debugging crashes to get all the messages written out.
  1181. // PhysicsLogging.Flush();
  1182. }
  1183. // Used to fill in the LocalID when there isn't one. It's the correct number of characters.
  1184. public const string DetailLogZero = "0000000000";
  1185. }
  1186. }