BSScene.cs 56 KB

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