BSPhysObject.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  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.Text;
  30. using OMV = OpenMetaverse;
  31. using OpenSim.Framework;
  32. using OpenSim.Region.Physics.Manager;
  33. namespace OpenSim.Region.Physics.BulletSNPlugin
  34. {
  35. /*
  36. * Class to wrap all objects.
  37. * The rest of BulletSim doesn't need to keep checking for avatars or prims
  38. * unless the difference is significant.
  39. *
  40. * Variables in the physicsl objects are in three forms:
  41. * VariableName: used by the simulator and performs taint operations, etc
  42. * RawVariableName: direct reference to the BulletSim storage for the variable value
  43. * ForceVariableName: direct reference (store and fetch) to the value in the physics engine.
  44. * The last two (and certainly the last one) should be referenced only in taint-time.
  45. */
  46. /*
  47. * As of 20121221, the following are the call sequences (going down) for different script physical functions:
  48. * llApplyImpulse llApplyRotImpulse llSetTorque llSetForce
  49. * SOP.ApplyImpulse SOP.ApplyAngularImpulse SOP.SetAngularImpulse SOP.SetForce
  50. * SOG.ApplyImpulse SOG.ApplyAngularImpulse SOG.SetAngularImpulse
  51. * PA.AddForce PA.AddAngularForce PA.Torque = v PA.Force = v
  52. * BS.ApplyCentralForce BS.ApplyTorque
  53. */
  54. public abstract class BSPhysObject : PhysicsActor
  55. {
  56. protected BSPhysObject()
  57. {
  58. }
  59. protected BSPhysObject(BSScene parentScene, uint localID, string name, string typeName)
  60. {
  61. PhysicsScene = parentScene;
  62. LocalID = localID;
  63. PhysObjectName = name;
  64. TypeName = typeName;
  65. Linkset = BSLinkset.Factory(PhysicsScene, this);
  66. LastAssetBuildFailed = false;
  67. // Default material type
  68. Material = MaterialAttributes.Material.Wood;
  69. CollisionCollection = new CollisionEventUpdate();
  70. SubscribedEventsMs = 0;
  71. CollidingStep = 0;
  72. CollidingGroundStep = 0;
  73. }
  74. // Tell the object to clean up.
  75. public virtual void Destroy()
  76. {
  77. UnRegisterAllPreStepActions();
  78. }
  79. public BSScene PhysicsScene { get; protected set; }
  80. // public override uint LocalID { get; set; } // Use the LocalID definition in PhysicsActor
  81. public string PhysObjectName { get; protected set; }
  82. public string TypeName { get; protected set; }
  83. public BSLinkset Linkset { get; set; }
  84. public BSLinksetInfo LinksetInfo { get; set; }
  85. // Return the object mass without calculating it or having side effects
  86. public abstract float RawMass { get; }
  87. // Set the raw mass but also update physical mass properties (inertia, ...)
  88. // 'inWorld' true if the object has already been added to the dynamic world.
  89. public abstract void UpdatePhysicalMassProperties(float mass, bool inWorld);
  90. // The last value calculated for the prim's inertia
  91. public OMV.Vector3 Inertia { get; set; }
  92. // Reference to the physical body (btCollisionObject) of this object
  93. public BulletBody PhysBody;
  94. // Reference to the physical shape (btCollisionShape) of this object
  95. public BulletShape PhysShape;
  96. // 'true' if the mesh's underlying asset failed to build.
  97. // This will keep us from looping after the first time the build failed.
  98. public bool LastAssetBuildFailed { get; set; }
  99. // The objects base shape information. Null if not a prim type shape.
  100. public PrimitiveBaseShape BaseShape { get; protected set; }
  101. // Some types of objects have preferred physical representations.
  102. // Returns SHAPE_UNKNOWN if there is no preference.
  103. public virtual BSPhysicsShapeType PreferredPhysicalShape
  104. {
  105. get { return BSPhysicsShapeType.SHAPE_UNKNOWN; }
  106. }
  107. // When the physical properties are updated, an EntityProperty holds the update values.
  108. // Keep the current and last EntityProperties to enable computation of differences
  109. // between the current update and the previous values.
  110. public EntityProperties CurrentEntityProperties { get; set; }
  111. public EntityProperties LastEntityProperties { get; set; }
  112. public virtual OMV.Vector3 Scale { get; set; }
  113. public abstract bool IsSolid { get; }
  114. public abstract bool IsStatic { get; }
  115. // Materialness
  116. public MaterialAttributes.Material Material { get; private set; }
  117. public override void SetMaterial(int material)
  118. {
  119. Material = (MaterialAttributes.Material)material;
  120. }
  121. // Stop all physical motion.
  122. public abstract void ZeroMotion(bool inTaintTime);
  123. public abstract void ZeroAngularMotion(bool inTaintTime);
  124. // Step the vehicle simulation for this object. A NOOP if the vehicle was not configured.
  125. public virtual void StepVehicle(float timeStep) { }
  126. // Update the physical location and motion of the object. Called with data from Bullet.
  127. public abstract void UpdateProperties(EntityProperties entprop);
  128. public abstract OMV.Vector3 RawPosition { get; set; }
  129. public abstract OMV.Vector3 ForcePosition { get; set; }
  130. public abstract OMV.Quaternion RawOrientation { get; set; }
  131. public abstract OMV.Quaternion ForceOrientation { get; set; }
  132. // The system is telling us the velocity it wants to move at.
  133. // protected OMV.Vector3 m_targetVelocity; // use the definition in PhysicsActor
  134. public override OMV.Vector3 TargetVelocity
  135. {
  136. get { return m_targetVelocity; }
  137. set
  138. {
  139. m_targetVelocity = value;
  140. Velocity = value;
  141. }
  142. }
  143. public abstract OMV.Vector3 ForceVelocity { get; set; }
  144. public abstract OMV.Vector3 ForceRotationalVelocity { get; set; }
  145. public abstract float ForceBuoyancy { get; set; }
  146. public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; }
  147. #region Collisions
  148. // Requested number of milliseconds between collision events. Zero means disabled.
  149. protected int SubscribedEventsMs { get; set; }
  150. // Given subscription, the time that a collision may be passed up
  151. protected int NextCollisionOkTime { get; set; }
  152. // The simulation step that last had a collision
  153. protected long CollidingStep { get; set; }
  154. // The simulation step that last had a collision with the ground
  155. protected long CollidingGroundStep { get; set; }
  156. // The collision flags we think are set in Bullet
  157. protected CollisionFlags CurrentCollisionFlags { get; set; }
  158. // The collisions that have been collected this tick
  159. protected CollisionEventUpdate CollisionCollection;
  160. // The simulation step is telling this object about a collision.
  161. // Return 'true' if a collision was processed and should be sent up.
  162. // Called at taint time from within the Step() function
  163. public virtual bool Collide(uint collidingWith, BSPhysObject collidee,
  164. OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth)
  165. {
  166. bool ret = false;
  167. // The following lines make IsColliding() and IsCollidingGround() work
  168. CollidingStep = PhysicsScene.SimulationStep;
  169. if (collidingWith <= PhysicsScene.TerrainManager.HighestTerrainID)
  170. {
  171. CollidingGroundStep = PhysicsScene.SimulationStep;
  172. }
  173. // prims in the same linkset cannot collide with each other
  174. if (collidee != null && (this.Linkset.LinksetID == collidee.Linkset.LinksetID))
  175. {
  176. return ret;
  177. }
  178. // if someone has subscribed for collision events....
  179. if (SubscribedEvents()) {
  180. CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
  181. DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}",
  182. LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth);
  183. ret = true;
  184. }
  185. return ret;
  186. }
  187. // Send the collected collisions into the simulator.
  188. // Called at taint time from within the Step() function thus no locking problems
  189. // with CollisionCollection and ObjectsWithNoMoreCollisions.
  190. // Return 'true' if there were some actual collisions passed up
  191. public virtual bool SendCollisions()
  192. {
  193. bool ret = true;
  194. // If the 'no collision' call, force it to happen right now so quick collision_end
  195. bool force = (CollisionCollection.Count == 0);
  196. // throttle the collisions to the number of milliseconds specified in the subscription
  197. if (force || (PhysicsScene.SimulationNowTime >= NextCollisionOkTime))
  198. {
  199. NextCollisionOkTime = PhysicsScene.SimulationNowTime + SubscribedEventsMs;
  200. // We are called if we previously had collisions. If there are no collisions
  201. // this time, send up one last empty event so OpenSim can sense collision end.
  202. if (CollisionCollection.Count == 0)
  203. {
  204. // If I have no collisions this time, remove me from the list of objects with collisions.
  205. ret = false;
  206. }
  207. // DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count);
  208. base.SendCollisionUpdate(CollisionCollection);
  209. // The CollisionCollection instance is passed around in the simulator.
  210. // Make sure we don't have a handle to that one and that a new one is used for next time.
  211. // This fixes an interesting 'gotcha'. If we call CollisionCollection.Clear() here,
  212. // a race condition is created for the other users of this instance.
  213. CollisionCollection = new CollisionEventUpdate();
  214. }
  215. return ret;
  216. }
  217. // Subscribe for collision events.
  218. // Parameter is the millisecond rate the caller wishes collision events to occur.
  219. public override void SubscribeEvents(int ms) {
  220. // DetailLog("{0},{1}.SubscribeEvents,subscribing,ms={2}", LocalID, TypeName, ms);
  221. SubscribedEventsMs = ms;
  222. if (ms > 0)
  223. {
  224. // make sure first collision happens
  225. NextCollisionOkTime = Util.EnvironmentTickCountSubtract(SubscribedEventsMs);
  226. PhysicsScene.TaintedObject(TypeName+".SubscribeEvents", delegate()
  227. {
  228. if (PhysBody.HasPhysicalBody)
  229. CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
  230. });
  231. }
  232. else
  233. {
  234. // Subscribing for zero or less is the same as unsubscribing
  235. UnSubscribeEvents();
  236. }
  237. }
  238. public override void UnSubscribeEvents() {
  239. // DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName);
  240. SubscribedEventsMs = 0;
  241. PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate()
  242. {
  243. // Make sure there is a body there because sometimes destruction happens in an un-ideal order.
  244. if (PhysBody.HasPhysicalBody)
  245. CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
  246. });
  247. }
  248. // Return 'true' if the simulator wants collision events
  249. public override bool SubscribedEvents() {
  250. return (SubscribedEventsMs > 0);
  251. }
  252. #endregion // Collisions
  253. #region Per Simulation Step actions
  254. // There are some actions that must be performed for a physical object before each simulation step.
  255. // These actions are optional so, rather than scanning all the physical objects and asking them
  256. // if they have anything to do, a physical object registers for an event call before the step is performed.
  257. // This bookkeeping makes it easy to add, remove and clean up after all these registrations.
  258. private Dictionary<string, BSScene.PreStepAction> RegisteredActions = new Dictionary<string, BSScene.PreStepAction>();
  259. protected void RegisterPreStepAction(string op, uint id, BSScene.PreStepAction actn)
  260. {
  261. string identifier = op + "-" + id.ToString();
  262. RegisteredActions[identifier] = actn;
  263. PhysicsScene.BeforeStep += actn;
  264. DetailLog("{0},BSPhysObject.RegisterPreStepAction,id={1}", LocalID, identifier);
  265. }
  266. // Unregister a pre step action. Safe to call if the action has not been registered.
  267. protected void UnRegisterPreStepAction(string op, uint id)
  268. {
  269. string identifier = op + "-" + id.ToString();
  270. bool removed = false;
  271. if (RegisteredActions.ContainsKey(identifier))
  272. {
  273. PhysicsScene.BeforeStep -= RegisteredActions[identifier];
  274. RegisteredActions.Remove(identifier);
  275. removed = true;
  276. }
  277. DetailLog("{0},BSPhysObject.UnRegisterPreStepAction,id={1},removed={2}", LocalID, identifier, removed);
  278. }
  279. protected void UnRegisterAllPreStepActions()
  280. {
  281. foreach (KeyValuePair<string, BSScene.PreStepAction> kvp in RegisteredActions)
  282. {
  283. PhysicsScene.BeforeStep -= kvp.Value;
  284. }
  285. RegisteredActions.Clear();
  286. DetailLog("{0},BSPhysObject.UnRegisterAllPreStepActions,", LocalID);
  287. }
  288. #endregion // Per Simulation Step actions
  289. // High performance detailed logging routine used by the physical objects.
  290. protected void DetailLog(string msg, params Object[] args)
  291. {
  292. if (PhysicsScene.PhysicsLogging.Enabled)
  293. PhysicsScene.DetailLog(msg, args);
  294. }
  295. }
  296. }