diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs | 1255 |
1 files changed, 798 insertions, 457 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index 2b3fa25..a465613 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs | |||
@@ -39,51 +39,49 @@ namespace OpenSim.Region.Physics.BulletSPlugin | |||
39 | { | 39 | { |
40 | 40 | ||
41 | [Serializable] | 41 | [Serializable] |
42 | public sealed class BSPrim : BSPhysObject | 42 | public class BSPrim : BSPhysObject |
43 | { | 43 | { |
44 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 44 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
45 | private static readonly string LogHeader = "[BULLETS PRIM]"; | 45 | private static readonly string LogHeader = "[BULLETS PRIM]"; |
46 | 46 | ||
47 | // _size is what the user passed. Scale is what we pass to the physics engine with the mesh. | 47 | // _size is what the user passed. Scale is what we pass to the physics engine with the mesh. |
48 | // Often Scale is unity because the meshmerizer will apply _size when creating the mesh. | ||
49 | private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user | 48 | private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user |
50 | 49 | ||
51 | private bool _grabbed; | 50 | private bool _grabbed; |
52 | private bool _isSelected; | 51 | private bool _isSelected; |
53 | private bool _isVolumeDetect; | 52 | private bool _isVolumeDetect; |
53 | |||
54 | // _position is what the simulator thinks the positions of the prim is. | ||
54 | private OMV.Vector3 _position; | 55 | private OMV.Vector3 _position; |
56 | |||
55 | private float _mass; // the mass of this object | 57 | private float _mass; // the mass of this object |
56 | private float _density; | ||
57 | private OMV.Vector3 _force; | 58 | private OMV.Vector3 _force; |
58 | private OMV.Vector3 _velocity; | 59 | private OMV.Vector3 _velocity; |
59 | private OMV.Vector3 _torque; | 60 | private OMV.Vector3 _torque; |
60 | private float _collisionScore; | ||
61 | private OMV.Vector3 _acceleration; | 61 | private OMV.Vector3 _acceleration; |
62 | private OMV.Quaternion _orientation; | 62 | private OMV.Quaternion _orientation; |
63 | private int _physicsActorType; | 63 | private int _physicsActorType; |
64 | private bool _isPhysical; | 64 | private bool _isPhysical; |
65 | private bool _flying; | 65 | private bool _flying; |
66 | private float _friction; | ||
67 | private float _restitution; | ||
68 | private bool _setAlwaysRun; | 66 | private bool _setAlwaysRun; |
69 | private bool _throttleUpdates; | 67 | private bool _throttleUpdates; |
70 | private bool _isColliding; | ||
71 | private bool _collidingGround; | ||
72 | private bool _collidingObj; | ||
73 | private bool _floatOnWater; | 68 | private bool _floatOnWater; |
74 | private OMV.Vector3 _rotationalVelocity; | 69 | private OMV.Vector3 _rotationalVelocity; |
75 | private bool _kinematic; | 70 | private bool _kinematic; |
76 | private float _buoyancy; | 71 | private float _buoyancy; |
77 | 72 | ||
78 | private BSDynamics _vehicle; | 73 | private int CrossingFailures { get; set; } |
79 | 74 | ||
75 | public BSDynamics VehicleController { get; private set; } | ||
76 | |||
77 | private BSVMotor _targetMotor; | ||
80 | private OMV.Vector3 _PIDTarget; | 78 | private OMV.Vector3 _PIDTarget; |
81 | private bool _usePID; | ||
82 | private float _PIDTau; | 79 | private float _PIDTau; |
83 | private bool _useHoverPID; | 80 | |
81 | private BSFMotor _hoverMotor; | ||
84 | private float _PIDHoverHeight; | 82 | private float _PIDHoverHeight; |
85 | private PIDHoverType _PIDHoverType; | 83 | private PIDHoverType _PIDHoverType; |
86 | private float _PIDHoverTao; | 84 | private float _PIDHoverTau; |
87 | 85 | ||
88 | public BSPrim(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, | 86 | public BSPrim(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, |
89 | OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical) | 87 | OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical) |
@@ -93,31 +91,29 @@ public sealed class BSPrim : BSPhysObject | |||
93 | _physicsActorType = (int)ActorTypes.Prim; | 91 | _physicsActorType = (int)ActorTypes.Prim; |
94 | _position = pos; | 92 | _position = pos; |
95 | _size = size; | 93 | _size = size; |
96 | Scale = size; // the scale will be set by CreateGeom depending on object type | 94 | Scale = size; // prims are the size the user wants them to be (different for BSCharactes). |
97 | _orientation = rotation; | 95 | _orientation = rotation; |
98 | _buoyancy = 1f; | 96 | _buoyancy = 0f; |
99 | _velocity = OMV.Vector3.Zero; | 97 | _velocity = OMV.Vector3.Zero; |
100 | _rotationalVelocity = OMV.Vector3.Zero; | 98 | _rotationalVelocity = OMV.Vector3.Zero; |
101 | BaseShape = pbs; | 99 | BaseShape = pbs; |
102 | _isPhysical = pisPhysical; | 100 | _isPhysical = pisPhysical; |
103 | _isVolumeDetect = false; | 101 | _isVolumeDetect = false; |
104 | _friction = PhysicsScene.Params.defaultFriction; // TODO: compute based on object material | ||
105 | _density = PhysicsScene.Params.defaultDensity; // TODO: compute based on object material | ||
106 | _restitution = PhysicsScene.Params.defaultRestitution; | ||
107 | _vehicle = new BSDynamics(PhysicsScene, this); // add vehicleness | ||
108 | _mass = CalculateMass(); | ||
109 | 102 | ||
110 | // No body or shape yet | 103 | VehicleController = new BSDynamics(PhysicsScene, this); // add vehicleness |
111 | PhysBody = new BulletBody(LocalID, IntPtr.Zero); | 104 | |
112 | PhysShape = new BulletShape(IntPtr.Zero); | 105 | _mass = CalculateMass(); |
113 | 106 | ||
114 | DetailLog("{0},BSPrim.constructor,call", LocalID); | 107 | DetailLog("{0},BSPrim.constructor,call", LocalID); |
115 | // do the actual object creation at taint time | 108 | // do the actual object creation at taint time |
116 | PhysicsScene.TaintedObject("BSPrim.create", delegate() | 109 | PhysicsScene.TaintedObject("BSPrim.create", delegate() |
117 | { | 110 | { |
111 | // Make sure the object is being created with some sanity. | ||
112 | ExtremeSanityCheck(true /* inTaintTime */); | ||
113 | |||
118 | CreateGeomAndObject(true); | 114 | CreateGeomAndObject(true); |
119 | 115 | ||
120 | CurrentCollisionFlags = BulletSimAPI.GetCollisionFlags2(PhysBody.ptr); | 116 | CurrentCollisionFlags = PhysicsScene.PE.GetCollisionFlags(PhysBody); |
121 | }); | 117 | }); |
122 | } | 118 | } |
123 | 119 | ||
@@ -125,15 +121,7 @@ public sealed class BSPrim : BSPhysObject | |||
125 | public override void Destroy() | 121 | public override void Destroy() |
126 | { | 122 | { |
127 | // m_log.DebugFormat("{0}: Destroy, id={1}", LogHeader, LocalID); | 123 | // m_log.DebugFormat("{0}: Destroy, id={1}", LogHeader, LocalID); |
128 | 124 | base.Destroy(); | |
129 | // Undo any links between me and any other object | ||
130 | BSPhysObject parentBefore = Linkset.LinksetRoot; | ||
131 | int childrenBefore = Linkset.NumberOfChildren; | ||
132 | |||
133 | Linkset = Linkset.RemoveMeFromLinkset(this); | ||
134 | |||
135 | DetailLog("{0},BSPrim.Destroy,call,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}", | ||
136 | LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren); | ||
137 | 125 | ||
138 | // Undo any vehicle properties | 126 | // Undo any vehicle properties |
139 | this.VehicleType = (int)Vehicle.TYPE_NONE; | 127 | this.VehicleType = (int)Vehicle.TYPE_NONE; |
@@ -142,8 +130,10 @@ public sealed class BSPrim : BSPhysObject | |||
142 | { | 130 | { |
143 | DetailLog("{0},BSPrim.Destroy,taint,", LocalID); | 131 | DetailLog("{0},BSPrim.Destroy,taint,", LocalID); |
144 | // If there are physical body and shape, release my use of same. | 132 | // If there are physical body and shape, release my use of same. |
145 | PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null); | 133 | PhysicsScene.Shapes.DereferenceBody(PhysBody, null); |
146 | PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null); | 134 | PhysBody.Clear(); |
135 | PhysicsScene.Shapes.DereferenceShape(PhysShape, null); | ||
136 | PhysShape.Clear(); | ||
147 | }); | 137 | }); |
148 | } | 138 | } |
149 | 139 | ||
@@ -157,31 +147,37 @@ public sealed class BSPrim : BSPhysObject | |||
157 | // We presume the scale and size are the same. If scale must be changed for | 147 | // We presume the scale and size are the same. If scale must be changed for |
158 | // the physical shape, that is done when the geometry is built. | 148 | // the physical shape, that is done when the geometry is built. |
159 | _size = value; | 149 | _size = value; |
150 | Scale = _size; | ||
160 | ForceBodyShapeRebuild(false); | 151 | ForceBodyShapeRebuild(false); |
161 | } | 152 | } |
162 | } | 153 | } |
163 | // Scale is what we set in the physics engine. It is different than 'size' in that | ||
164 | // 'size' can be encorporated into the mesh. In that case, the scale is <1,1,1>. | ||
165 | public override OMV.Vector3 Scale { get; set; } | ||
166 | 154 | ||
167 | public override PrimitiveBaseShape Shape { | 155 | public override PrimitiveBaseShape Shape { |
168 | set { | 156 | set { |
169 | BaseShape = value; | 157 | BaseShape = value; |
158 | LastAssetBuildFailed = false; | ||
170 | ForceBodyShapeRebuild(false); | 159 | ForceBodyShapeRebuild(false); |
171 | } | 160 | } |
172 | } | 161 | } |
173 | // Whatever the linkset wants is what I want. | 162 | // 'unknown' says to choose the best type |
174 | public override BSPhysicsShapeType PreferredPhysicalShape | 163 | public override BSPhysicsShapeType PreferredPhysicalShape |
175 | { get { return Linkset.PreferredPhysicalShape(this); } } | 164 | { get { return BSPhysicsShapeType.SHAPE_UNKNOWN; } } |
176 | 165 | ||
177 | public override bool ForceBodyShapeRebuild(bool inTaintTime) | 166 | public override bool ForceBodyShapeRebuild(bool inTaintTime) |
178 | { | 167 | { |
179 | LastAssetBuildFailed = false; | 168 | if (inTaintTime) |
180 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ForceBodyShapeRebuild", delegate() | ||
181 | { | 169 | { |
182 | _mass = CalculateMass(); // changing the shape changes the mass | 170 | _mass = CalculateMass(); // changing the shape changes the mass |
183 | CreateGeomAndObject(true); | 171 | CreateGeomAndObject(true); |
184 | }); | 172 | } |
173 | else | ||
174 | { | ||
175 | PhysicsScene.TaintedObject("BSPrim.ForceBodyShapeRebuild", delegate() | ||
176 | { | ||
177 | _mass = CalculateMass(); // changing the shape changes the mass | ||
178 | CreateGeomAndObject(true); | ||
179 | }); | ||
180 | } | ||
185 | return true; | 181 | return true; |
186 | } | 182 | } |
187 | public override bool Grabbed { | 183 | public override bool Grabbed { |
@@ -189,46 +185,44 @@ public sealed class BSPrim : BSPhysObject | |||
189 | } | 185 | } |
190 | } | 186 | } |
191 | public override bool Selected { | 187 | public override bool Selected { |
192 | set { | 188 | set |
193 | _isSelected = value; | 189 | { |
194 | PhysicsScene.TaintedObject("BSPrim.setSelected", delegate() | 190 | if (value != _isSelected) |
195 | { | 191 | { |
196 | DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected); | 192 | _isSelected = value; |
197 | SetObjectDynamic(false); | 193 | PhysicsScene.TaintedObject("BSPrim.setSelected", delegate() |
198 | }); | 194 | { |
195 | DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected); | ||
196 | SetObjectDynamic(false); | ||
197 | }); | ||
198 | } | ||
199 | } | 199 | } |
200 | } | 200 | } |
201 | public override void CrossingFailure() { return; } | 201 | public override bool IsSelected |
202 | { | ||
203 | get { return _isSelected; } | ||
204 | } | ||
202 | 205 | ||
203 | // link me to the specified parent | 206 | public override void CrossingFailure() |
204 | public override void link(PhysicsActor obj) { | 207 | { |
205 | BSPrim parent = obj as BSPrim; | 208 | CrossingFailures++; |
206 | if (parent != null) | 209 | if (CrossingFailures > BSParam.CrossingFailuresBeforeOutOfBounds) |
207 | { | 210 | { |
208 | BSPhysObject parentBefore = Linkset.LinksetRoot; | 211 | base.RaiseOutOfBounds(RawPosition); |
209 | int childrenBefore = Linkset.NumberOfChildren; | 212 | } |
210 | 213 | else if (CrossingFailures == BSParam.CrossingFailuresBeforeOutOfBounds) | |
211 | Linkset = parent.Linkset.AddMeToLinkset(this); | 214 | { |
212 | 215 | m_log.WarnFormat("{0} Too many crossing failures for {1}", LogHeader, Name); | |
213 | DetailLog("{0},BSPrim.link,call,parentBefore={1}, childrenBefore=={2}, parentAfter={3}, childrenAfter={4}", | ||
214 | LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren); | ||
215 | } | 216 | } |
216 | return; | 217 | return; |
217 | } | 218 | } |
218 | 219 | ||
220 | // link me to the specified parent | ||
221 | public override void link(PhysicsActor obj) { | ||
222 | } | ||
223 | |||
219 | // delink me from my linkset | 224 | // delink me from my linkset |
220 | public override void delink() { | 225 | public override void delink() { |
221 | // TODO: decide if this parent checking needs to happen at taint time | ||
222 | // Race condition here: if link() and delink() in same simulation tick, the delink will not happen | ||
223 | |||
224 | BSPhysObject parentBefore = Linkset.LinksetRoot; | ||
225 | int childrenBefore = Linkset.NumberOfChildren; | ||
226 | |||
227 | Linkset = Linkset.RemoveMeFromLinkset(this); | ||
228 | |||
229 | DetailLog("{0},BSPrim.delink,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}, ", | ||
230 | LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren); | ||
231 | return; | ||
232 | } | 226 | } |
233 | 227 | ||
234 | // Set motion values to zero. | 228 | // Set motion values to zero. |
@@ -244,7 +238,8 @@ public sealed class BSPrim : BSPhysObject | |||
244 | // Zero some other properties in the physics engine | 238 | // Zero some other properties in the physics engine |
245 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() | 239 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() |
246 | { | 240 | { |
247 | BulletSimAPI.ClearAllForces2(PhysBody.ptr); | 241 | if (PhysBody.HasPhysicalBody) |
242 | PhysicsScene.PE.ClearAllForces(PhysBody); | ||
248 | }); | 243 | }); |
249 | } | 244 | } |
250 | public override void ZeroAngularMotion(bool inTaintTime) | 245 | public override void ZeroAngularMotion(bool inTaintTime) |
@@ -253,16 +248,107 @@ public sealed class BSPrim : BSPhysObject | |||
253 | // Zero some other properties in the physics engine | 248 | // Zero some other properties in the physics engine |
254 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() | 249 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() |
255 | { | 250 | { |
256 | BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); | 251 | // DetailLog("{0},BSPrim.ZeroAngularMotion,call,rotVel={1}", LocalID, _rotationalVelocity); |
257 | BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); | 252 | if (PhysBody.HasPhysicalBody) |
253 | { | ||
254 | PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity); | ||
255 | PhysicsScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity); | ||
256 | } | ||
258 | }); | 257 | }); |
259 | } | 258 | } |
260 | 259 | ||
260 | bool TryExperimentalLockAxisCode = false; | ||
261 | BSConstraint LockAxisConstraint = null; | ||
261 | public override void LockAngularMotion(OMV.Vector3 axis) | 262 | public override void LockAngularMotion(OMV.Vector3 axis) |
262 | { | 263 | { |
263 | DetailLog("{0},BSPrim.LockAngularMotion,call,axis={1}", LocalID, axis); | 264 | DetailLog("{0},BSPrim.LockAngularMotion,call,axis={1}", LocalID, axis); |
265 | |||
266 | // "1" means free, "0" means locked | ||
267 | OMV.Vector3 locking = new OMV.Vector3(1f, 1f, 1f); | ||
268 | if (axis.X != 1) locking.X = 0f; | ||
269 | if (axis.Y != 1) locking.Y = 0f; | ||
270 | if (axis.Z != 1) locking.Z = 0f; | ||
271 | LockedAxis = locking; | ||
272 | |||
273 | if (TryExperimentalLockAxisCode && LockedAxis != LockedAxisFree) | ||
274 | { | ||
275 | // Lock that axis by creating a 6DOF constraint that has one end in the world and | ||
276 | // the other in the object. | ||
277 | // http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?p=20817 | ||
278 | // http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?p=26380 | ||
279 | |||
280 | PhysicsScene.TaintedObject("BSPrim.LockAngularMotion", delegate() | ||
281 | { | ||
282 | CleanUpLockAxisPhysicals(true /* inTaintTime */); | ||
283 | |||
284 | BSConstraint6Dof axisConstrainer = new BSConstraint6Dof(PhysicsScene.World, PhysBody, | ||
285 | OMV.Vector3.Zero, OMV.Quaternion.Inverse(RawOrientation), | ||
286 | true /* useLinearReferenceFrameB */, true /* disableCollisionsBetweenLinkedBodies */); | ||
287 | LockAxisConstraint = axisConstrainer; | ||
288 | PhysicsScene.Constraints.AddConstraint(LockAxisConstraint); | ||
289 | |||
290 | // The constraint is tied to the world and oriented to the prim. | ||
291 | |||
292 | // Free to move linearly | ||
293 | OMV.Vector3 linearLow = OMV.Vector3.Zero; | ||
294 | OMV.Vector3 linearHigh = PhysicsScene.TerrainManager.DefaultRegionSize; | ||
295 | axisConstrainer.SetLinearLimits(linearLow, linearHigh); | ||
296 | |||
297 | // Angular with some axis locked | ||
298 | float f2PI = (float)Math.PI * 2f; | ||
299 | OMV.Vector3 angularLow = new OMV.Vector3(-f2PI, -f2PI, -f2PI); | ||
300 | OMV.Vector3 angularHigh = new OMV.Vector3(f2PI, f2PI, f2PI); | ||
301 | if (LockedAxis.X != 1f) | ||
302 | { | ||
303 | angularLow.X = 0f; | ||
304 | angularHigh.X = 0f; | ||
305 | } | ||
306 | if (LockedAxis.Y != 1f) | ||
307 | { | ||
308 | angularLow.Y = 0f; | ||
309 | angularHigh.Y = 0f; | ||
310 | } | ||
311 | if (LockedAxis.Z != 1f) | ||
312 | { | ||
313 | angularLow.Z = 0f; | ||
314 | angularHigh.Z = 0f; | ||
315 | } | ||
316 | axisConstrainer.SetAngularLimits(angularLow, angularHigh); | ||
317 | |||
318 | DetailLog("{0},BSPrim.LockAngularMotion,create,linLow={1},linHi={2},angLow={3},angHi={4}", | ||
319 | LocalID, linearLow, linearHigh, angularLow, angularHigh); | ||
320 | |||
321 | // Constants from one of the posts mentioned above and used in Bullet's ConstraintDemo. | ||
322 | axisConstrainer.TranslationalLimitMotor(true /* enable */, 5.0f, 0.1f); | ||
323 | |||
324 | axisConstrainer.RecomputeConstraintVariables(RawMass); | ||
325 | }); | ||
326 | } | ||
327 | else | ||
328 | { | ||
329 | // Everything seems unlocked | ||
330 | CleanUpLockAxisPhysicals(false /* inTaintTime */); | ||
331 | } | ||
332 | |||
264 | return; | 333 | return; |
265 | } | 334 | } |
335 | // Get rid of any constraint built for LockAxis | ||
336 | // Most often the constraint is removed when the constraint collection is cleaned for this prim. | ||
337 | private void CleanUpLockAxisPhysicals(bool inTaintTime) | ||
338 | { | ||
339 | if (LockAxisConstraint != null) | ||
340 | { | ||
341 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.CleanUpLockAxisPhysicals", delegate() | ||
342 | { | ||
343 | if (LockAxisConstraint != null) | ||
344 | { | ||
345 | PhysicsScene.Constraints.RemoveAndDestroyConstraint(LockAxisConstraint); | ||
346 | LockAxisConstraint = null; | ||
347 | DetailLog("{0},BSPrim.CleanUpLockAxisPhysicals,destroyingConstraint", LocalID); | ||
348 | } | ||
349 | }); | ||
350 | } | ||
351 | } | ||
266 | 352 | ||
267 | public override OMV.Vector3 RawPosition | 353 | public override OMV.Vector3 RawPosition |
268 | { | 354 | { |
@@ -271,41 +357,41 @@ public sealed class BSPrim : BSPhysObject | |||
271 | } | 357 | } |
272 | public override OMV.Vector3 Position { | 358 | public override OMV.Vector3 Position { |
273 | get { | 359 | get { |
274 | // child prims move around based on their parent. Need to get the latest location | ||
275 | if (!Linkset.IsRoot(this)) | ||
276 | _position = Linkset.Position(this); | ||
277 | |||
278 | // don't do the GetObjectPosition for root elements because this function is called a zillion times. | 360 | // don't do the GetObjectPosition for root elements because this function is called a zillion times. |
279 | // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr); | 361 | // _position = ForcePosition; |
280 | return _position; | 362 | return _position; |
281 | } | 363 | } |
282 | set { | 364 | set { |
283 | // If the position must be forced into the physics engine, use ForcePosition. | 365 | // If the position must be forced into the physics engine, use ForcePosition. |
366 | // All positions are given in world positions. | ||
284 | if (_position == value) | 367 | if (_position == value) |
285 | { | 368 | { |
369 | DetailLog("{0},BSPrim.setPosition,call,positionNotChanging,pos={1},orient={2}", LocalID, _position, _orientation); | ||
286 | return; | 370 | return; |
287 | } | 371 | } |
288 | _position = value; | 372 | _position = value; |
289 | // TODO: what does it mean to set the position of a child prim?? Rebuild the constraint? | ||
290 | PositionSanityCheck(false); | 373 | PositionSanityCheck(false); |
374 | |||
291 | PhysicsScene.TaintedObject("BSPrim.setPosition", delegate() | 375 | PhysicsScene.TaintedObject("BSPrim.setPosition", delegate() |
292 | { | 376 | { |
293 | // DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); | 377 | DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); |
294 | BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); | 378 | ForcePosition = _position; |
295 | ActivateIfPhysical(false); | ||
296 | }); | 379 | }); |
297 | } | 380 | } |
298 | } | 381 | } |
382 | |||
299 | public override OMV.Vector3 ForcePosition { | 383 | public override OMV.Vector3 ForcePosition { |
300 | get { | 384 | get { |
301 | _position = BulletSimAPI.GetPosition2(PhysBody.ptr); | 385 | _position = PhysicsScene.PE.GetPosition(PhysBody); |
302 | return _position; | 386 | return _position; |
303 | } | 387 | } |
304 | set { | 388 | set { |
305 | _position = value; | 389 | _position = value; |
306 | // PositionSanityCheck(); // Don't do this! Causes a loop and caller should know better. | 390 | if (PhysBody.HasPhysicalBody) |
307 | BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); | 391 | { |
308 | ActivateIfPhysical(false); | 392 | PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); |
393 | ActivateIfPhysical(false); | ||
394 | } | ||
309 | } | 395 | } |
310 | } | 396 | } |
311 | 397 | ||
@@ -316,119 +402,231 @@ public sealed class BSPrim : BSPhysObject | |||
316 | { | 402 | { |
317 | bool ret = false; | 403 | bool ret = false; |
318 | 404 | ||
319 | float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); | 405 | // We don't care where non-physical items are placed |
406 | if (!IsPhysicallyActive) | ||
407 | return ret; | ||
408 | |||
409 | if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(RawPosition)) | ||
410 | { | ||
411 | // The physical object is out of the known/simulated area. | ||
412 | // Upper levels of code will handle the transition to other areas so, for | ||
413 | // the time, we just ignore the position. | ||
414 | return ret; | ||
415 | } | ||
416 | |||
417 | float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); | ||
320 | OMV.Vector3 upForce = OMV.Vector3.Zero; | 418 | OMV.Vector3 upForce = OMV.Vector3.Zero; |
321 | if (Position.Z < terrainHeight) | 419 | float approxSize = Math.Max(Size.X, Math.Max(Size.Y, Size.Z)); |
420 | if ((RawPosition.Z + approxSize / 2f) < terrainHeight) | ||
322 | { | 421 | { |
323 | DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight); | 422 | DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, RawPosition, terrainHeight); |
324 | float targetHeight = terrainHeight + (Size.Z / 2f); | 423 | float targetHeight = terrainHeight + (Size.Z / 2f); |
325 | // Upforce proportional to the distance away from the terrain. Correct the error in 1 sec. | 424 | // If the object is below ground it just has to be moved up because pushing will |
326 | upForce.Z = (terrainHeight - Position.Z) * 1f; | 425 | // not get it through the terrain |
426 | _position.Z = targetHeight; | ||
427 | if (inTaintTime) | ||
428 | { | ||
429 | ForcePosition = _position; | ||
430 | } | ||
431 | // If we are throwing the object around, zero its other forces | ||
432 | ZeroMotion(inTaintTime); | ||
327 | ret = true; | 433 | ret = true; |
328 | } | 434 | } |
329 | 435 | ||
330 | if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) | 436 | if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) |
331 | { | 437 | { |
332 | float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position); | 438 | float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position); |
333 | // TODO: a floating motor so object will bob in the water | 439 | // TODO: a floating motor so object will bob in the water |
334 | if (Math.Abs(Position.Z - waterHeight) > 0.1f) | 440 | if (Math.Abs(RawPosition.Z - waterHeight) > 0.1f) |
335 | { | 441 | { |
336 | // Upforce proportional to the distance away from the water. Correct the error in 1 sec. | 442 | // Upforce proportional to the distance away from the water. Correct the error in 1 sec. |
337 | upForce.Z = (waterHeight - Position.Z) * 1f; | 443 | upForce.Z = (waterHeight - RawPosition.Z) * 1f; |
444 | |||
445 | // Apply upforce and overcome gravity. | ||
446 | OMV.Vector3 correctionForce = upForce - PhysicsScene.DefaultGravity; | ||
447 | DetailLog("{0},BSPrim.PositionSanityCheck,applyForce,pos={1},upForce={2},correctionForce={3}", LocalID, _position, upForce, correctionForce); | ||
448 | AddForce(correctionForce, false, inTaintTime); | ||
338 | ret = true; | 449 | ret = true; |
339 | } | 450 | } |
340 | } | 451 | } |
341 | 452 | ||
342 | // TODO: check for out of bounds | 453 | return ret; |
454 | } | ||
455 | |||
456 | // Occasionally things will fly off and really get lost. | ||
457 | // Find the wanderers and bring them back. | ||
458 | // Return 'true' if some parameter need some sanity. | ||
459 | private bool ExtremeSanityCheck(bool inTaintTime) | ||
460 | { | ||
461 | bool ret = false; | ||
343 | 462 | ||
344 | // The above code computes a force to apply to correct any out-of-bounds problems. Apply same. | 463 | uint wayOutThere = Constants.RegionSize * Constants.RegionSize; |
345 | if (ret) | 464 | // There have been instances of objects getting thrown way out of bounds and crashing |
465 | // the border crossing code. | ||
466 | if ( _position.X < -Constants.RegionSize || _position.X > wayOutThere | ||
467 | || _position.Y < -Constants.RegionSize || _position.Y > wayOutThere | ||
468 | || _position.Z < -Constants.RegionSize || _position.Z > wayOutThere) | ||
346 | { | 469 | { |
347 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.PositionSanityCheck:belowTerrain", delegate() | 470 | _position = new OMV.Vector3(10, 10, 50); |
348 | { | 471 | ZeroMotion(inTaintTime); |
349 | // Apply upforce and overcome gravity. | 472 | ret = true; |
350 | ForceVelocity = ForceVelocity + upForce - PhysicsScene.DefaultGravity; | 473 | } |
351 | }); | 474 | if (_velocity.LengthSquared() > BSParam.MaxLinearVelocity) |
475 | { | ||
476 | _velocity = Util.ClampV(_velocity, BSParam.MaxLinearVelocity); | ||
477 | ret = true; | ||
352 | } | 478 | } |
479 | if (_rotationalVelocity.LengthSquared() > BSParam.MaxAngularVelocitySquared) | ||
480 | { | ||
481 | _rotationalVelocity = Util.ClampV(_rotationalVelocity, BSParam.MaxAngularVelocity); | ||
482 | ret = true; | ||
483 | } | ||
484 | |||
353 | return ret; | 485 | return ret; |
354 | } | 486 | } |
355 | 487 | ||
356 | // Return the effective mass of the object. | 488 | // Return the effective mass of the object. |
357 | // If there are multiple items in the linkset, add them together for the root | 489 | // The definition of this call is to return the mass of the prim. |
490 | // If the simulator cares about the mass of the linkset, it will sum it itself. | ||
358 | public override float Mass | 491 | public override float Mass |
359 | { | 492 | { |
360 | get | 493 | get { return _mass; } |
361 | { | 494 | } |
362 | return Linkset.LinksetMass; | 495 | // TotalMass returns the mass of the large object the prim may be in (overridden by linkset code) |
363 | // return _mass; | 496 | public virtual float TotalMass |
364 | } | 497 | { |
498 | get { return _mass; } | ||
365 | } | 499 | } |
366 | |||
367 | // used when we only want this prim's mass and not the linkset thing | 500 | // used when we only want this prim's mass and not the linkset thing |
368 | public override float RawMass { | 501 | public override float RawMass { |
369 | get { return _mass; } | 502 | get { return _mass; } |
370 | } | 503 | } |
371 | // Set the physical mass to the passed mass. | 504 | // Set the physical mass to the passed mass. |
372 | // Note that this does not change _mass! | 505 | // Note that this does not change _mass! |
373 | public override void UpdatePhysicalMassProperties(float physMass) | 506 | public override void UpdatePhysicalMassProperties(float physMass, bool inWorld) |
374 | { | 507 | { |
375 | if (IsStatic) | 508 | if (PhysBody.HasPhysicalBody && PhysShape.HasPhysicalShape) |
376 | { | 509 | { |
377 | Inertia = OMV.Vector3.Zero; | 510 | if (IsStatic) |
378 | BulletSimAPI.SetMassProps2(PhysBody.ptr, 0f, Inertia); | 511 | { |
379 | BulletSimAPI.UpdateInertiaTensor2(PhysBody.ptr); | 512 | PhysicsScene.PE.SetGravity(PhysBody, PhysicsScene.DefaultGravity); |
513 | Inertia = OMV.Vector3.Zero; | ||
514 | PhysicsScene.PE.SetMassProps(PhysBody, 0f, Inertia); | ||
515 | PhysicsScene.PE.UpdateInertiaTensor(PhysBody); | ||
516 | } | ||
517 | else | ||
518 | { | ||
519 | if (inWorld) | ||
520 | { | ||
521 | // Changing interesting properties doesn't change proxy and collision cache | ||
522 | // information. The Bullet solution is to re-add the object to the world | ||
523 | // after parameters are changed. | ||
524 | PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, PhysBody); | ||
525 | } | ||
526 | |||
527 | // The computation of mass props requires gravity to be set on the object. | ||
528 | Gravity = ComputeGravity(Buoyancy); | ||
529 | PhysicsScene.PE.SetGravity(PhysBody, Gravity); | ||
530 | |||
531 | Inertia = PhysicsScene.PE.CalculateLocalInertia(PhysShape, physMass); | ||
532 | PhysicsScene.PE.SetMassProps(PhysBody, physMass, Inertia); | ||
533 | PhysicsScene.PE.UpdateInertiaTensor(PhysBody); | ||
534 | |||
535 | DetailLog("{0},BSPrim.UpdateMassProperties,mass={1},localInertia={2},grav={3},inWorld={4}", | ||
536 | LocalID, physMass, Inertia, Gravity, inWorld); | ||
537 | |||
538 | if (inWorld) | ||
539 | { | ||
540 | AddObjectToPhysicalWorld(); | ||
541 | } | ||
542 | } | ||
380 | } | 543 | } |
381 | else | 544 | } |
545 | |||
546 | // Return what gravity should be set to this very moment | ||
547 | public OMV.Vector3 ComputeGravity(float buoyancy) | ||
548 | { | ||
549 | OMV.Vector3 ret = PhysicsScene.DefaultGravity; | ||
550 | |||
551 | if (!IsStatic) | ||
382 | { | 552 | { |
383 | Inertia = BulletSimAPI.CalculateLocalInertia2(PhysShape.ptr, physMass); | 553 | ret *= (1f - buoyancy); |
384 | BulletSimAPI.SetMassProps2(PhysBody.ptr, physMass, Inertia); | 554 | ret *= GravModifier; |
385 | BulletSimAPI.UpdateInertiaTensor2(PhysBody.ptr); | ||
386 | // center of mass is at the zero of the object | ||
387 | // DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(PhysBody.ptr, ForcePosition, ForceOrientation); | ||
388 | DetailLog("{0},BSPrim.UpdateMassProperties,mass={1},localInertia={2}", LocalID, physMass, Inertia); | ||
389 | } | 555 | } |
556 | |||
557 | return ret; | ||
390 | } | 558 | } |
391 | 559 | ||
392 | // Is this used? | 560 | // Is this used? |
393 | public override OMV.Vector3 CenterOfMass | 561 | public override OMV.Vector3 CenterOfMass |
394 | { | 562 | { |
395 | get { return Linkset.CenterOfMass; } | 563 | get { return RawPosition; } |
396 | } | 564 | } |
397 | 565 | ||
398 | // Is this used? | 566 | // Is this used? |
399 | public override OMV.Vector3 GeometricCenter | 567 | public override OMV.Vector3 GeometricCenter |
400 | { | 568 | { |
401 | get { return Linkset.GeometricCenter; } | 569 | get { return RawPosition; } |
402 | } | 570 | } |
403 | 571 | ||
404 | public override OMV.Vector3 Force { | 572 | public override OMV.Vector3 Force { |
405 | get { return _force; } | 573 | get { return _force; } |
406 | set { | 574 | set { |
407 | _force = value; | 575 | _force = value; |
408 | PhysicsScene.TaintedObject("BSPrim.setForce", delegate() | 576 | if (_force != OMV.Vector3.Zero) |
409 | { | 577 | { |
410 | // DetailLog("{0},BSPrim.setForce,taint,force={1}", LocalID, _force); | 578 | // If the force is non-zero, it must be reapplied each tick because |
411 | BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force); | 579 | // Bullet clears the forces applied last frame. |
412 | }); | 580 | RegisterPreStepAction("BSPrim.setForce", LocalID, |
581 | delegate(float timeStep) | ||
582 | { | ||
583 | if (!IsPhysicallyActive || _force == OMV.Vector3.Zero) | ||
584 | { | ||
585 | UnRegisterPreStepAction("BSPrim.setForce", LocalID); | ||
586 | return; | ||
587 | } | ||
588 | |||
589 | DetailLog("{0},BSPrim.setForce,preStep,force={1}", LocalID, _force); | ||
590 | if (PhysBody.HasPhysicalBody) | ||
591 | { | ||
592 | PhysicsScene.PE.ApplyCentralForce(PhysBody, _force); | ||
593 | ActivateIfPhysical(false); | ||
594 | } | ||
595 | } | ||
596 | ); | ||
597 | } | ||
598 | else | ||
599 | { | ||
600 | UnRegisterPreStepAction("BSPrim.setForce", LocalID); | ||
601 | } | ||
413 | } | 602 | } |
414 | } | 603 | } |
415 | 604 | ||
416 | public override int VehicleType { | 605 | public override int VehicleType { |
417 | get { | 606 | get { |
418 | return (int)_vehicle.Type; // if we are a vehicle, return that type | 607 | return (int)VehicleController.Type; // if we are a vehicle, return that type |
419 | } | 608 | } |
420 | set { | 609 | set { |
421 | Vehicle type = (Vehicle)value; | 610 | Vehicle type = (Vehicle)value; |
422 | 611 | ||
423 | // Tell the scene about the vehicle so it will get processing each frame. | ||
424 | PhysicsScene.VehicleInSceneTypeChanged(this, type); | ||
425 | |||
426 | PhysicsScene.TaintedObject("setVehicleType", delegate() | 612 | PhysicsScene.TaintedObject("setVehicleType", delegate() |
427 | { | 613 | { |
428 | // Done at taint time so we're sure the physics engine is not using the variables | 614 | // Done at taint time so we're sure the physics engine is not using the variables |
429 | // Vehicle code changes the parameters for this vehicle type. | 615 | // Vehicle code changes the parameters for this vehicle type. |
430 | _vehicle.ProcessTypeChange(type); | 616 | VehicleController.ProcessTypeChange(type); |
431 | ActivateIfPhysical(false); | 617 | ActivateIfPhysical(false); |
618 | |||
619 | // If an active vehicle, register the vehicle code to be called before each step | ||
620 | if (VehicleController.Type == Vehicle.TYPE_NONE) | ||
621 | { | ||
622 | UnRegisterPreStepAction("BSPrim.Vehicle", LocalID); | ||
623 | UnRegisterPostStepAction("BSPrim.Vehicle", LocalID); | ||
624 | } | ||
625 | else | ||
626 | { | ||
627 | RegisterPreStepAction("BSPrim.Vehicle", LocalID, VehicleController.Step); | ||
628 | RegisterPostStepAction("BSPrim.Vehicle", LocalID, VehicleController.PostStep); | ||
629 | } | ||
432 | }); | 630 | }); |
433 | } | 631 | } |
434 | } | 632 | } |
@@ -436,7 +634,7 @@ public sealed class BSPrim : BSPhysObject | |||
436 | { | 634 | { |
437 | PhysicsScene.TaintedObject("BSPrim.VehicleFloatParam", delegate() | 635 | PhysicsScene.TaintedObject("BSPrim.VehicleFloatParam", delegate() |
438 | { | 636 | { |
439 | _vehicle.ProcessFloatVehicleParam((Vehicle)param, value); | 637 | VehicleController.ProcessFloatVehicleParam((Vehicle)param, value); |
440 | ActivateIfPhysical(false); | 638 | ActivateIfPhysical(false); |
441 | }); | 639 | }); |
442 | } | 640 | } |
@@ -444,7 +642,7 @@ public sealed class BSPrim : BSPhysObject | |||
444 | { | 642 | { |
445 | PhysicsScene.TaintedObject("BSPrim.VehicleVectorParam", delegate() | 643 | PhysicsScene.TaintedObject("BSPrim.VehicleVectorParam", delegate() |
446 | { | 644 | { |
447 | _vehicle.ProcessVectorVehicleParam((Vehicle)param, value); | 645 | VehicleController.ProcessVectorVehicleParam((Vehicle)param, value); |
448 | ActivateIfPhysical(false); | 646 | ActivateIfPhysical(false); |
449 | }); | 647 | }); |
450 | } | 648 | } |
@@ -452,7 +650,7 @@ public sealed class BSPrim : BSPhysObject | |||
452 | { | 650 | { |
453 | PhysicsScene.TaintedObject("BSPrim.VehicleRotationParam", delegate() | 651 | PhysicsScene.TaintedObject("BSPrim.VehicleRotationParam", delegate() |
454 | { | 652 | { |
455 | _vehicle.ProcessRotationVehicleParam((Vehicle)param, rotation); | 653 | VehicleController.ProcessRotationVehicleParam((Vehicle)param, rotation); |
456 | ActivateIfPhysical(false); | 654 | ActivateIfPhysical(false); |
457 | }); | 655 | }); |
458 | } | 656 | } |
@@ -460,27 +658,10 @@ public sealed class BSPrim : BSPhysObject | |||
460 | { | 658 | { |
461 | PhysicsScene.TaintedObject("BSPrim.VehicleFlags", delegate() | 659 | PhysicsScene.TaintedObject("BSPrim.VehicleFlags", delegate() |
462 | { | 660 | { |
463 | _vehicle.ProcessVehicleFlags(param, remove); | 661 | VehicleController.ProcessVehicleFlags(param, remove); |
464 | }); | 662 | }); |
465 | } | 663 | } |
466 | 664 | ||
467 | // Called each simulation step to advance vehicle characteristics. | ||
468 | // Called from Scene when doing simulation step so we're in taint processing time. | ||
469 | public override void StepVehicle(float timeStep) | ||
470 | { | ||
471 | if (IsPhysical && _vehicle.IsActive) | ||
472 | { | ||
473 | _vehicle.Step(timeStep); | ||
474 | /* // TEST TEST DEBUG DEBUG -- trying to reduce the extra action of Bullet simulation step | ||
475 | PhysicsScene.PostTaintObject("BSPrim.StepVehicles", LocalID, delegate() | ||
476 | { | ||
477 | // This resets the interpolation values and recomputes the tensor variables | ||
478 | BulletSimAPI.SetCenterOfMassByPosRot2(BSBody.ptr, ForcePosition, ForceOrientation); | ||
479 | }); | ||
480 | */ | ||
481 | } | ||
482 | } | ||
483 | |||
484 | // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more | 665 | // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more |
485 | public override void SetVolumeDetect(int param) { | 666 | public override void SetVolumeDetect(int param) { |
486 | bool newValue = (param != 0); | 667 | bool newValue = (param != 0); |
@@ -495,6 +676,81 @@ public sealed class BSPrim : BSPhysObject | |||
495 | } | 676 | } |
496 | return; | 677 | return; |
497 | } | 678 | } |
679 | public override void SetMaterial(int material) | ||
680 | { | ||
681 | base.SetMaterial(material); | ||
682 | PhysicsScene.TaintedObject("BSPrim.SetMaterial", delegate() | ||
683 | { | ||
684 | UpdatePhysicalParameters(); | ||
685 | }); | ||
686 | } | ||
687 | public override float Friction | ||
688 | { | ||
689 | get { return base.Friction; } | ||
690 | set | ||
691 | { | ||
692 | if (base.Friction != value) | ||
693 | { | ||
694 | base.Friction = value; | ||
695 | PhysicsScene.TaintedObject("BSPrim.setFriction", delegate() | ||
696 | { | ||
697 | UpdatePhysicalParameters(); | ||
698 | }); | ||
699 | } | ||
700 | } | ||
701 | } | ||
702 | public override float Restitution | ||
703 | { | ||
704 | get { return base.Restitution; } | ||
705 | set | ||
706 | { | ||
707 | if (base.Restitution != value) | ||
708 | { | ||
709 | base.Restitution = value; | ||
710 | PhysicsScene.TaintedObject("BSPrim.setRestitution", delegate() | ||
711 | { | ||
712 | UpdatePhysicalParameters(); | ||
713 | }); | ||
714 | } | ||
715 | } | ||
716 | } | ||
717 | // The simulator/viewer keep density as 100kg/m3. | ||
718 | // Remember to use BSParam.DensityScaleFactor to create the physical density. | ||
719 | public override float Density | ||
720 | { | ||
721 | get { return base.Density; } | ||
722 | set | ||
723 | { | ||
724 | if (base.Density != value) | ||
725 | { | ||
726 | base.Density = value; | ||
727 | PhysicsScene.TaintedObject("BSPrim.setDensity", delegate() | ||
728 | { | ||
729 | UpdatePhysicalParameters(); | ||
730 | }); | ||
731 | } | ||
732 | } | ||
733 | } | ||
734 | public override float GravModifier | ||
735 | { | ||
736 | get { return base.GravModifier; } | ||
737 | set | ||
738 | { | ||
739 | if (base.GravModifier != value) | ||
740 | { | ||
741 | base.GravModifier = value; | ||
742 | PhysicsScene.TaintedObject("BSPrim.setGravityModifier", delegate() | ||
743 | { | ||
744 | UpdatePhysicalParameters(); | ||
745 | }); | ||
746 | } | ||
747 | } | ||
748 | } | ||
749 | public override OMV.Vector3 RawVelocity | ||
750 | { | ||
751 | get { return _velocity; } | ||
752 | set { _velocity = value; } | ||
753 | } | ||
498 | public override OMV.Vector3 Velocity { | 754 | public override OMV.Vector3 Velocity { |
499 | get { return _velocity; } | 755 | get { return _velocity; } |
500 | set { | 756 | set { |
@@ -502,30 +758,53 @@ public sealed class BSPrim : BSPhysObject | |||
502 | PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate() | 758 | PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate() |
503 | { | 759 | { |
504 | // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity); | 760 | // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity); |
505 | BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity); | 761 | ForceVelocity = _velocity; |
506 | }); | 762 | }); |
507 | } | 763 | } |
508 | } | 764 | } |
509 | public override OMV.Vector3 ForceVelocity { | 765 | public override OMV.Vector3 ForceVelocity { |
510 | get { return _velocity; } | 766 | get { return _velocity; } |
511 | set { | 767 | set { |
512 | _velocity = value; | 768 | PhysicsScene.AssertInTaintTime("BSPrim.ForceVelocity"); |
513 | BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity); | 769 | |
770 | _velocity = Util.ClampV(value, BSParam.MaxLinearVelocity); | ||
771 | if (PhysBody.HasPhysicalBody) | ||
772 | { | ||
773 | DetailLog("{0},BSPrim.ForceVelocity,taint,vel={1}", LocalID, _velocity); | ||
774 | PhysicsScene.PE.SetLinearVelocity(PhysBody, _velocity); | ||
775 | ActivateIfPhysical(false); | ||
776 | } | ||
514 | } | 777 | } |
515 | } | 778 | } |
516 | public override OMV.Vector3 Torque { | 779 | public override OMV.Vector3 Torque { |
517 | get { return _torque; } | 780 | get { return _torque; } |
518 | set { | 781 | set { |
519 | _torque = value; | 782 | _torque = value; |
520 | AddAngularForce(_torque, false, false); | 783 | if (_torque != OMV.Vector3.Zero) |
784 | { | ||
785 | // If the torque is non-zero, it must be reapplied each tick because | ||
786 | // Bullet clears the forces applied last frame. | ||
787 | RegisterPreStepAction("BSPrim.setTorque", LocalID, | ||
788 | delegate(float timeStep) | ||
789 | { | ||
790 | if (!IsPhysicallyActive || _torque == OMV.Vector3.Zero) | ||
791 | { | ||
792 | UnRegisterPreStepAction("BSPrim.setTorque", LocalID); | ||
793 | return; | ||
794 | } | ||
795 | |||
796 | if (PhysBody.HasPhysicalBody) | ||
797 | AddAngularForce(_torque, false, true); | ||
798 | } | ||
799 | ); | ||
800 | } | ||
801 | else | ||
802 | { | ||
803 | UnRegisterPreStepAction("BSPrim.setTorque", LocalID); | ||
804 | } | ||
521 | // DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, _torque); | 805 | // DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, _torque); |
522 | } | 806 | } |
523 | } | 807 | } |
524 | public override float CollisionScore { | ||
525 | get { return _collisionScore; } | ||
526 | set { _collisionScore = value; | ||
527 | } | ||
528 | } | ||
529 | public override OMV.Vector3 Acceleration { | 808 | public override OMV.Vector3 Acceleration { |
530 | get { return _acceleration; } | 809 | get { return _acceleration; } |
531 | set { _acceleration = value; } | 810 | set { _acceleration = value; } |
@@ -537,23 +816,16 @@ public sealed class BSPrim : BSPhysObject | |||
537 | } | 816 | } |
538 | public override OMV.Quaternion Orientation { | 817 | public override OMV.Quaternion Orientation { |
539 | get { | 818 | get { |
540 | // Children move around because tied to parent. Get a fresh value. | ||
541 | if (!Linkset.IsRoot(this)) | ||
542 | { | ||
543 | _orientation = Linkset.Orientation(this); | ||
544 | } | ||
545 | return _orientation; | 819 | return _orientation; |
546 | } | 820 | } |
547 | set { | 821 | set { |
548 | if (_orientation == value) | 822 | if (_orientation == value) |
549 | return; | 823 | return; |
550 | _orientation = value; | 824 | _orientation = value; |
551 | // TODO: what does it mean if a child in a linkset changes its orientation? Rebuild the constraint? | 825 | |
552 | PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate() | 826 | PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate() |
553 | { | 827 | { |
554 | // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr); | 828 | ForceOrientation = _orientation; |
555 | // DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation); | ||
556 | BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); | ||
557 | }); | 829 | }); |
558 | } | 830 | } |
559 | } | 831 | } |
@@ -562,13 +834,14 @@ public sealed class BSPrim : BSPhysObject | |||
562 | { | 834 | { |
563 | get | 835 | get |
564 | { | 836 | { |
565 | _orientation = BulletSimAPI.GetOrientation2(PhysBody.ptr); | 837 | _orientation = PhysicsScene.PE.GetOrientation(PhysBody); |
566 | return _orientation; | 838 | return _orientation; |
567 | } | 839 | } |
568 | set | 840 | set |
569 | { | 841 | { |
570 | _orientation = value; | 842 | _orientation = value; |
571 | BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); | 843 | if (PhysBody.HasPhysicalBody) |
844 | PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); | ||
572 | } | 845 | } |
573 | } | 846 | } |
574 | public override int PhysicsActorType { | 847 | public override int PhysicsActorType { |
@@ -583,10 +856,11 @@ public sealed class BSPrim : BSPhysObject | |||
583 | _isPhysical = value; | 856 | _isPhysical = value; |
584 | PhysicsScene.TaintedObject("BSPrim.setIsPhysical", delegate() | 857 | PhysicsScene.TaintedObject("BSPrim.setIsPhysical", delegate() |
585 | { | 858 | { |
586 | // DetailLog("{0},setIsPhysical,taint,isPhys={1}", LocalID, _isPhysical); | 859 | DetailLog("{0},setIsPhysical,taint,isPhys={1}", LocalID, _isPhysical); |
587 | SetObjectDynamic(true); | 860 | SetObjectDynamic(true); |
588 | // whether phys-to-static or static-to-phys, the object is not moving. | 861 | // whether phys-to-static or static-to-phys, the object is not moving. |
589 | ZeroMotion(true); | 862 | ZeroMotion(true); |
863 | |||
590 | }); | 864 | }); |
591 | } | 865 | } |
592 | } | 866 | } |
@@ -604,6 +878,12 @@ public sealed class BSPrim : BSPhysObject | |||
604 | get { return !IsPhantom && !_isVolumeDetect; } | 878 | get { return !IsPhantom && !_isVolumeDetect; } |
605 | } | 879 | } |
606 | 880 | ||
881 | // The object is moving and is actively being dynamic in the physical world | ||
882 | public override bool IsPhysicallyActive | ||
883 | { | ||
884 | get { return !_isSelected && IsPhysical; } | ||
885 | } | ||
886 | |||
607 | // Make gravity work if the object is physical and not selected | 887 | // Make gravity work if the object is physical and not selected |
608 | // Called at taint-time!! | 888 | // Called at taint-time!! |
609 | private void SetObjectDynamic(bool forceRebuild) | 889 | private void SetObjectDynamic(bool forceRebuild) |
@@ -618,19 +898,24 @@ public sealed class BSPrim : BSPhysObject | |||
618 | // isSolid: other objects bounce off of this object | 898 | // isSolid: other objects bounce off of this object |
619 | // isVolumeDetect: other objects pass through but can generate collisions | 899 | // isVolumeDetect: other objects pass through but can generate collisions |
620 | // collisionEvents: whether this object returns collision events | 900 | // collisionEvents: whether this object returns collision events |
621 | private void UpdatePhysicalParameters() | 901 | public virtual void UpdatePhysicalParameters() |
622 | { | 902 | { |
623 | // DetailLog("{0},BSPrim.UpdatePhysicalParameters,entry,body={1},shape={2}", LocalID, BSBody, BSShape); | 903 | if (!PhysBody.HasPhysicalBody) |
904 | { | ||
905 | // This would only happen if updates are called for during initialization when the body is not set up yet. | ||
906 | DetailLog("{0},BSPrim.UpdatePhysicalParameters,taint,calledWithNoPhysBody", LocalID); | ||
907 | return; | ||
908 | } | ||
624 | 909 | ||
625 | // Mangling all the physical properties requires the object not be in the physical world. | 910 | // Mangling all the physical properties requires the object not be in the physical world. |
626 | // This is a NOOP if the object is not in the world (BulletSim and Bullet ignore objects not found). | 911 | // This is a NOOP if the object is not in the world (BulletSim and Bullet ignore objects not found). |
627 | BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, PhysBody.ptr); | 912 | PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, PhysBody); |
628 | 913 | ||
629 | // Set up the object physicalness (does gravity and collisions move this object) | 914 | // Set up the object physicalness (does gravity and collisions move this object) |
630 | MakeDynamic(IsStatic); | 915 | MakeDynamic(IsStatic); |
631 | 916 | ||
632 | // Update vehicle specific parameters (after MakeDynamic() so can change physical parameters) | 917 | // Update vehicle specific parameters (after MakeDynamic() so can change physical parameters) |
633 | _vehicle.Refresh(); | 918 | VehicleController.Refresh(); |
634 | 919 | ||
635 | // Arrange for collision events if the simulator wants them | 920 | // Arrange for collision events if the simulator wants them |
636 | EnableCollisions(SubscribedEvents()); | 921 | EnableCollisions(SubscribedEvents()); |
@@ -638,25 +923,13 @@ public sealed class BSPrim : BSPhysObject | |||
638 | // Make solid or not (do things bounce off or pass through this object). | 923 | // Make solid or not (do things bounce off or pass through this object). |
639 | MakeSolid(IsSolid); | 924 | MakeSolid(IsSolid); |
640 | 925 | ||
641 | BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, PhysBody.ptr); | 926 | AddObjectToPhysicalWorld(); |
642 | 927 | ||
643 | // Rebuild its shape | 928 | // Rebuild its shape |
644 | BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr); | 929 | PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, PhysBody); |
645 | |||
646 | // Collision filter can be set only when the object is in the world | ||
647 | if (PhysBody.collisionFilter != 0 || PhysBody.collisionMask != 0) | ||
648 | { | ||
649 | BulletSimAPI.SetCollisionFilterMask2(PhysBody.ptr, (uint)PhysBody.collisionFilter, (uint)PhysBody.collisionMask); | ||
650 | } | ||
651 | |||
652 | // Recompute any linkset parameters. | ||
653 | // When going from non-physical to physical, this re-enables the constraints that | ||
654 | // had been automatically disabled when the mass was set to zero. | ||
655 | // For compound based linksets, this enables and disables interactions of the children. | ||
656 | Linkset.Refresh(this); | ||
657 | 930 | ||
658 | DetailLog("{0},BSPrim.UpdatePhysicalParameters,taintExit,static={1},solid={2},mass={3},collide={4},cf={5:X},body={6},shape={7}", | 931 | DetailLog("{0},BSPrim.UpdatePhysicalParameters,taintExit,static={1},solid={2},mass={3},collide={4},cf={5:X},cType={6},body={7},shape={8}", |
659 | LocalID, IsStatic, IsSolid, _mass, SubscribedEvents(), CurrentCollisionFlags, PhysBody, PhysShape); | 932 | LocalID, IsStatic, IsSolid, Mass, SubscribedEvents(), CurrentCollisionFlags, PhysBody.collisionType, PhysBody, PhysShape); |
660 | } | 933 | } |
661 | 934 | ||
662 | // "Making dynamic" means changing to and from static. | 935 | // "Making dynamic" means changing to and from static. |
@@ -664,79 +937,78 @@ public sealed class BSPrim : BSPhysObject | |||
664 | // When dynamic, the object can fall and be pushed by others. | 937 | // When dynamic, the object can fall and be pushed by others. |
665 | // This is independent of its 'solidness' which controls what passes through | 938 | // This is independent of its 'solidness' which controls what passes through |
666 | // this object and what interacts with it. | 939 | // this object and what interacts with it. |
667 | private void MakeDynamic(bool makeStatic) | 940 | protected virtual void MakeDynamic(bool makeStatic) |
668 | { | 941 | { |
669 | if (makeStatic) | 942 | if (makeStatic) |
670 | { | 943 | { |
671 | // Become a Bullet 'static' object type | 944 | // Become a Bullet 'static' object type |
672 | CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT); | 945 | CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_STATIC_OBJECT); |
673 | // Stop all movement | 946 | // Stop all movement |
674 | ZeroMotion(true); | 947 | ZeroMotion(true); |
675 | // Center of mass is at the center of the object | 948 | |
676 | // DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.PhysBody.ptr, _position, _orientation); | 949 | // Set various physical properties so other object interact properly |
950 | MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, false); | ||
951 | PhysicsScene.PE.SetFriction(PhysBody, Friction); | ||
952 | PhysicsScene.PE.SetRestitution(PhysBody, Restitution); | ||
953 | |||
677 | // Mass is zero which disables a bunch of physics stuff in Bullet | 954 | // Mass is zero which disables a bunch of physics stuff in Bullet |
678 | UpdatePhysicalMassProperties(0f); | 955 | UpdatePhysicalMassProperties(0f, false); |
679 | // Set collision detection parameters | 956 | // Set collision detection parameters |
680 | if (PhysicsScene.Params.ccdMotionThreshold > 0f) | 957 | if (BSParam.CcdMotionThreshold > 0f) |
681 | { | 958 | { |
682 | BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, PhysicsScene.Params.ccdMotionThreshold); | 959 | PhysicsScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); |
683 | BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius); | 960 | PhysicsScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); |
684 | } | 961 | } |
685 | // There can be special things needed for implementing linksets | 962 | |
686 | Linkset.MakeStatic(this); | ||
687 | // The activation state is 'disabled' so Bullet will not try to act on it. | 963 | // The activation state is 'disabled' so Bullet will not try to act on it. |
688 | BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_SIMULATION); | 964 | // PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_SIMULATION); |
689 | // Start it out sleeping and physical actions could wake it up. | 965 | // Start it out sleeping and physical actions could wake it up. |
690 | // BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ISLAND_SLEEPING); | 966 | PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.ISLAND_SLEEPING); |
691 | 967 | ||
692 | PhysBody.collisionFilter = CollisionFilterGroups.StaticObjectFilter; | 968 | // This collides like a static object |
693 | PhysBody.collisionMask = CollisionFilterGroups.StaticObjectMask; | 969 | PhysBody.collisionType = CollisionType.Static; |
694 | } | 970 | } |
695 | else | 971 | else |
696 | { | 972 | { |
697 | // Not a Bullet static object | 973 | // Not a Bullet static object |
698 | CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT); | 974 | CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.CF_STATIC_OBJECT); |
699 | 975 | ||
700 | // Set various physical properties so internal dynamic properties will get computed correctly as they are set | 976 | // Set various physical properties so other object interact properly |
701 | BulletSimAPI.SetFriction2(PhysBody.ptr, PhysicsScene.Params.defaultFriction); | 977 | PhysicsScene.PE.SetFriction(PhysBody, Friction); |
702 | BulletSimAPI.SetRestitution2(PhysBody.ptr, PhysicsScene.Params.defaultRestitution); | 978 | PhysicsScene.PE.SetRestitution(PhysBody, Restitution); |
979 | // DetailLog("{0},BSPrim.MakeDynamic,frict={1},rest={2}", LocalID, Friction, Restitution); | ||
703 | 980 | ||
704 | // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382 | 981 | // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382 |
705 | // Since this can be called multiple times, only zero forces when becoming physical | 982 | // Since this can be called multiple times, only zero forces when becoming physical |
706 | // BulletSimAPI.ClearAllForces2(BSBody.ptr); | 983 | // PhysicsScene.PE.ClearAllForces(BSBody); |
707 | 984 | ||
708 | // For good measure, make sure the transform is set through to the motion state | 985 | // For good measure, make sure the transform is set through to the motion state |
709 | BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); | 986 | ForcePosition = _position; |
710 | 987 | ForceVelocity = _velocity; | |
711 | // Center of mass is at the center of the object | 988 | ForceRotationalVelocity = _rotationalVelocity; |
712 | // DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.PhysBody.ptr, _position, _orientation); | ||
713 | 989 | ||
714 | // A dynamic object has mass | 990 | // A dynamic object has mass |
715 | UpdatePhysicalMassProperties(RawMass); | 991 | UpdatePhysicalMassProperties(RawMass, false); |
716 | 992 | ||
717 | // Set collision detection parameters | 993 | // Set collision detection parameters |
718 | if (PhysicsScene.Params.ccdMotionThreshold > 0f) | 994 | if (BSParam.CcdMotionThreshold > 0f) |
719 | { | 995 | { |
720 | BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, PhysicsScene.Params.ccdMotionThreshold); | 996 | PhysicsScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); |
721 | BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius); | 997 | PhysicsScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); |
722 | } | 998 | } |
723 | 999 | ||
724 | // Various values for simulation limits | 1000 | // Various values for simulation limits |
725 | BulletSimAPI.SetDamping2(PhysBody.ptr, PhysicsScene.Params.linearDamping, PhysicsScene.Params.angularDamping); | 1001 | PhysicsScene.PE.SetDamping(PhysBody, BSParam.LinearDamping, BSParam.AngularDamping); |
726 | BulletSimAPI.SetDeactivationTime2(PhysBody.ptr, PhysicsScene.Params.deactivationTime); | 1002 | PhysicsScene.PE.SetDeactivationTime(PhysBody, BSParam.DeactivationTime); |
727 | BulletSimAPI.SetSleepingThresholds2(PhysBody.ptr, PhysicsScene.Params.linearSleepingThreshold, PhysicsScene.Params.angularSleepingThreshold); | 1003 | PhysicsScene.PE.SetSleepingThresholds(PhysBody, BSParam.LinearSleepingThreshold, BSParam.AngularSleepingThreshold); |
728 | BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, PhysicsScene.Params.contactProcessingThreshold); | 1004 | PhysicsScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); |
729 | 1005 | ||
730 | // There might be special things needed for implementing linksets. | 1006 | // This collides like an object. |
731 | Linkset.MakeDynamic(this); | 1007 | PhysBody.collisionType = CollisionType.Dynamic; |
732 | 1008 | ||
733 | // Force activation of the object so Bullet will act on it. | 1009 | // Force activation of the object so Bullet will act on it. |
734 | // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects. | 1010 | // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects. |
735 | BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ACTIVE_TAG); | 1011 | PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.ACTIVE_TAG); |
736 | // BulletSimAPI.Activate2(BSBody.ptr, true); | ||
737 | |||
738 | PhysBody.collisionFilter = CollisionFilterGroups.ObjectFilter; | ||
739 | PhysBody.collisionMask = CollisionFilterGroups.ObjectMask; | ||
740 | } | 1012 | } |
741 | } | 1013 | } |
742 | 1014 | ||
@@ -746,7 +1018,7 @@ public sealed class BSPrim : BSPhysObject | |||
746 | // the functions after this one set up the state of a possibly newly created collision body. | 1018 | // the functions after this one set up the state of a possibly newly created collision body. |
747 | private void MakeSolid(bool makeSolid) | 1019 | private void MakeSolid(bool makeSolid) |
748 | { | 1020 | { |
749 | CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(PhysBody.ptr); | 1021 | CollisionObjectTypes bodyType = (CollisionObjectTypes)PhysicsScene.PE.GetBodyType(PhysBody); |
750 | if (makeSolid) | 1022 | if (makeSolid) |
751 | { | 1023 | { |
752 | // Verify the previous code created the correct shape for this type of thing. | 1024 | // Verify the previous code created the correct shape for this type of thing. |
@@ -754,7 +1026,7 @@ public sealed class BSPrim : BSPhysObject | |||
754 | { | 1026 | { |
755 | m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for solidity. id={1}, type={2}", LogHeader, LocalID, bodyType); | 1027 | m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for solidity. id={1}, type={2}", LogHeader, LocalID, bodyType); |
756 | } | 1028 | } |
757 | CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); | 1029 | CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); |
758 | } | 1030 | } |
759 | else | 1031 | else |
760 | { | 1032 | { |
@@ -762,9 +1034,10 @@ public sealed class BSPrim : BSPhysObject | |||
762 | { | 1034 | { |
763 | m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType); | 1035 | m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType); |
764 | } | 1036 | } |
765 | CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); | 1037 | CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); |
766 | PhysBody.collisionFilter = CollisionFilterGroups.VolumeDetectFilter; | 1038 | |
767 | PhysBody.collisionMask = CollisionFilterGroups.VolumeDetectMask; | 1039 | // Change collision info from a static object to a ghosty collision object |
1040 | PhysBody.collisionType = CollisionType.VolumeDetect; | ||
768 | } | 1041 | } |
769 | } | 1042 | } |
770 | 1043 | ||
@@ -773,8 +1046,8 @@ public sealed class BSPrim : BSPhysObject | |||
773 | // Called in taint-time!! | 1046 | // Called in taint-time!! |
774 | private void ActivateIfPhysical(bool forceIt) | 1047 | private void ActivateIfPhysical(bool forceIt) |
775 | { | 1048 | { |
776 | if (IsPhysical) | 1049 | if (IsPhysical && PhysBody.HasPhysicalBody) |
777 | BulletSimAPI.Activate2(PhysBody.ptr, forceIt); | 1050 | PhysicsScene.PE.Activate(PhysBody, forceIt); |
778 | } | 1051 | } |
779 | 1052 | ||
780 | // Turn on or off the flag controlling whether collision events are returned to the simulator. | 1053 | // Turn on or off the flag controlling whether collision events are returned to the simulator. |
@@ -782,11 +1055,27 @@ public sealed class BSPrim : BSPhysObject | |||
782 | { | 1055 | { |
783 | if (wantsCollisionEvents) | 1056 | if (wantsCollisionEvents) |
784 | { | 1057 | { |
785 | CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); | 1058 | CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); |
1059 | } | ||
1060 | else | ||
1061 | { | ||
1062 | CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); | ||
1063 | } | ||
1064 | } | ||
1065 | |||
1066 | // Add me to the physical world. | ||
1067 | // Object MUST NOT already be in the world. | ||
1068 | // This routine exists because some assorted properties get mangled by adding to the world. | ||
1069 | internal void AddObjectToPhysicalWorld() | ||
1070 | { | ||
1071 | if (PhysBody.HasPhysicalBody) | ||
1072 | { | ||
1073 | PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, PhysBody); | ||
786 | } | 1074 | } |
787 | else | 1075 | else |
788 | { | 1076 | { |
789 | CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); | 1077 | m_log.ErrorFormat("{0} Attempt to add physical object without body. id={1}", LogHeader, LocalID); |
1078 | DetailLog("{0},BSPrim.AddObjectToPhysicalWorld,addObjectWithoutBody,cType={1}", LocalID, PhysBody.collisionType); | ||
790 | } | 1079 | } |
791 | } | 1080 | } |
792 | 1081 | ||
@@ -805,18 +1094,6 @@ public sealed class BSPrim : BSPhysObject | |||
805 | get { return _throttleUpdates; } | 1094 | get { return _throttleUpdates; } |
806 | set { _throttleUpdates = value; } | 1095 | set { _throttleUpdates = value; } |
807 | } | 1096 | } |
808 | public override bool IsColliding { | ||
809 | get { return (CollidingStep == PhysicsScene.SimulationStep); } | ||
810 | set { _isColliding = value; } | ||
811 | } | ||
812 | public override bool CollidingGround { | ||
813 | get { return (CollidingGroundStep == PhysicsScene.SimulationStep); } | ||
814 | set { _collidingGround = value; } | ||
815 | } | ||
816 | public override bool CollidingObj { | ||
817 | get { return _collidingObj; } | ||
818 | set { _collidingObj = value; } | ||
819 | } | ||
820 | public bool IsPhantom { | 1097 | public bool IsPhantom { |
821 | get { | 1098 | get { |
822 | // SceneObjectPart removes phantom objects from the physics scene | 1099 | // SceneObjectPart removes phantom objects from the physics scene |
@@ -831,32 +1108,23 @@ public sealed class BSPrim : BSPhysObject | |||
831 | PhysicsScene.TaintedObject("BSPrim.setFloatOnWater", delegate() | 1108 | PhysicsScene.TaintedObject("BSPrim.setFloatOnWater", delegate() |
832 | { | 1109 | { |
833 | if (_floatOnWater) | 1110 | if (_floatOnWater) |
834 | CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); | 1111 | CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); |
835 | else | 1112 | else |
836 | CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); | 1113 | CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); |
837 | }); | 1114 | }); |
838 | } | 1115 | } |
839 | } | 1116 | } |
840 | public override OMV.Vector3 RotationalVelocity { | 1117 | public override OMV.Vector3 RotationalVelocity { |
841 | get { | 1118 | get { |
842 | /* | ||
843 | OMV.Vector3 pv = OMV.Vector3.Zero; | ||
844 | // if close to zero, report zero | ||
845 | // This is copied from ODE but I'm not sure why it returns zero but doesn't | ||
846 | // zero the property in the physics engine. | ||
847 | if (_rotationalVelocity.ApproxEquals(pv, 0.2f)) | ||
848 | return pv; | ||
849 | */ | ||
850 | |||
851 | return _rotationalVelocity; | 1119 | return _rotationalVelocity; |
852 | } | 1120 | } |
853 | set { | 1121 | set { |
854 | _rotationalVelocity = value; | 1122 | _rotationalVelocity = value; |
1123 | Util.ClampV(_rotationalVelocity, BSParam.MaxAngularVelocity); | ||
855 | // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity); | 1124 | // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity); |
856 | PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate() | 1125 | PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate() |
857 | { | 1126 | { |
858 | DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity); | 1127 | ForceRotationalVelocity = _rotationalVelocity; |
859 | BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity); | ||
860 | }); | 1128 | }); |
861 | } | 1129 | } |
862 | } | 1130 | } |
@@ -865,8 +1133,14 @@ public sealed class BSPrim : BSPhysObject | |||
865 | return _rotationalVelocity; | 1133 | return _rotationalVelocity; |
866 | } | 1134 | } |
867 | set { | 1135 | set { |
868 | _rotationalVelocity = value; | 1136 | _rotationalVelocity = Util.ClampV(value, BSParam.MaxAngularVelocity); |
869 | BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity); | 1137 | if (PhysBody.HasPhysicalBody) |
1138 | { | ||
1139 | DetailLog("{0},BSPrim.ForceRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity); | ||
1140 | PhysicsScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity); | ||
1141 | // PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity); | ||
1142 | ActivateIfPhysical(false); | ||
1143 | } | ||
870 | } | 1144 | } |
871 | } | 1145 | } |
872 | public override bool Kinematic { | 1146 | public override bool Kinematic { |
@@ -890,27 +1164,130 @@ public sealed class BSPrim : BSPhysObject | |||
890 | set { | 1164 | set { |
891 | _buoyancy = value; | 1165 | _buoyancy = value; |
892 | // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); | 1166 | // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); |
893 | // Buoyancy is faked by changing the gravity applied to the object | 1167 | // Force the recalculation of the various inertia,etc variables in the object |
894 | float grav = PhysicsScene.Params.gravity * (1f - _buoyancy); | 1168 | UpdatePhysicalMassProperties(RawMass, true); |
895 | BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav)); | 1169 | DetailLog("{0},BSPrim.ForceBuoyancy,buoy={1},mass={2},grav={3}", LocalID, _buoyancy, RawMass, Gravity); |
1170 | ActivateIfPhysical(false); | ||
896 | } | 1171 | } |
897 | } | 1172 | } |
898 | 1173 | ||
899 | // Used for MoveTo | 1174 | // Used for MoveTo |
900 | public override OMV.Vector3 PIDTarget { | 1175 | public override OMV.Vector3 PIDTarget { |
901 | set { _PIDTarget = value; } | 1176 | set |
902 | } | 1177 | { |
903 | public override bool PIDActive { | 1178 | // TODO: add a sanity check -- don't move more than a region or something like that. |
904 | set { _usePID = value; } | 1179 | _PIDTarget = value; |
1180 | } | ||
905 | } | 1181 | } |
906 | public override float PIDTau { | 1182 | public override float PIDTau { |
907 | set { _PIDTau = value; } | 1183 | set { _PIDTau = value; } |
908 | } | 1184 | } |
1185 | public override bool PIDActive { | ||
1186 | set { | ||
1187 | if (value) | ||
1188 | { | ||
1189 | // We're taking over after this. | ||
1190 | ZeroMotion(true); | ||
1191 | |||
1192 | _targetMotor = new BSVMotor("BSPrim.PIDTarget", | ||
1193 | _PIDTau, // timeScale | ||
1194 | BSMotor.Infinite, // decay time scale | ||
1195 | BSMotor.InfiniteVector, // friction timescale | ||
1196 | 1f // efficiency | ||
1197 | ); | ||
1198 | _targetMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG so motor will output detail log messages. | ||
1199 | _targetMotor.SetTarget(_PIDTarget); | ||
1200 | _targetMotor.SetCurrent(RawPosition); | ||
1201 | /* | ||
1202 | _targetMotor = new BSPIDVMotor("BSPrim.PIDTarget"); | ||
1203 | _targetMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG so motor will output detail log messages. | ||
1204 | |||
1205 | _targetMotor.SetTarget(_PIDTarget); | ||
1206 | _targetMotor.SetCurrent(RawPosition); | ||
1207 | _targetMotor.TimeScale = _PIDTau; | ||
1208 | _targetMotor.Efficiency = 1f; | ||
1209 | */ | ||
1210 | |||
1211 | RegisterPreStepAction("BSPrim.PIDTarget", LocalID, delegate(float timeStep) | ||
1212 | { | ||
1213 | if (!IsPhysicallyActive) | ||
1214 | { | ||
1215 | UnRegisterPreStepAction("BSPrim.PIDTarget", LocalID); | ||
1216 | return; | ||
1217 | } | ||
1218 | |||
1219 | OMV.Vector3 origPosition = RawPosition; // DEBUG DEBUG (for printout below) | ||
1220 | |||
1221 | // 'movePosition' is where we'd like the prim to be at this moment. | ||
1222 | OMV.Vector3 movePosition = RawPosition + _targetMotor.Step(timeStep); | ||
1223 | |||
1224 | // If we are very close to our target, turn off the movement motor. | ||
1225 | if (_targetMotor.ErrorIsZero()) | ||
1226 | { | ||
1227 | DetailLog("{0},BSPrim.PIDTarget,zeroMovement,movePos={1},pos={2},mass={3}", | ||
1228 | LocalID, movePosition, RawPosition, Mass); | ||
1229 | ForcePosition = _targetMotor.TargetValue; | ||
1230 | _targetMotor.Enabled = false; | ||
1231 | } | ||
1232 | else | ||
1233 | { | ||
1234 | _position = movePosition; | ||
1235 | PositionSanityCheck(true /* intaintTime */); | ||
1236 | ForcePosition = _position; | ||
1237 | } | ||
1238 | DetailLog("{0},BSPrim.PIDTarget,move,fromPos={1},movePos={2}", LocalID, origPosition, movePosition); | ||
1239 | }); | ||
1240 | } | ||
1241 | else | ||
1242 | { | ||
1243 | // Stop any targetting | ||
1244 | UnRegisterPreStepAction("BSPrim.PIDTarget", LocalID); | ||
1245 | } | ||
1246 | } | ||
1247 | } | ||
909 | 1248 | ||
910 | // Used for llSetHoverHeight and maybe vehicle height | 1249 | // Used for llSetHoverHeight and maybe vehicle height |
911 | // Hover Height will override MoveTo target's Z | 1250 | // Hover Height will override MoveTo target's Z |
912 | public override bool PIDHoverActive { | 1251 | public override bool PIDHoverActive { |
913 | set { _useHoverPID = value; } | 1252 | set { |
1253 | if (value) | ||
1254 | { | ||
1255 | // Turning the target on | ||
1256 | _hoverMotor = new BSFMotor("BSPrim.Hover", | ||
1257 | _PIDHoverTau, // timeScale | ||
1258 | BSMotor.Infinite, // decay time scale | ||
1259 | BSMotor.Infinite, // friction timescale | ||
1260 | 1f // efficiency | ||
1261 | ); | ||
1262 | _hoverMotor.SetTarget(ComputeCurrentPIDHoverHeight()); | ||
1263 | _hoverMotor.SetCurrent(RawPosition.Z); | ||
1264 | _hoverMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG so motor will output detail log messages. | ||
1265 | |||
1266 | RegisterPreStepAction("BSPrim.Hover", LocalID, delegate(float timeStep) | ||
1267 | { | ||
1268 | // Don't do hovering while the object is selected. | ||
1269 | if (!IsPhysicallyActive) | ||
1270 | return; | ||
1271 | |||
1272 | _hoverMotor.SetCurrent(RawPosition.Z); | ||
1273 | _hoverMotor.SetTarget(ComputeCurrentPIDHoverHeight()); | ||
1274 | float targetHeight = _hoverMotor.Step(timeStep); | ||
1275 | |||
1276 | // 'targetHeight' is where we'd like the Z of the prim to be at this moment. | ||
1277 | // Compute the amount of force to push us there. | ||
1278 | float moveForce = (targetHeight - RawPosition.Z) * Mass; | ||
1279 | // Undo anything the object thinks it's doing at the moment | ||
1280 | moveForce = -RawVelocity.Z * Mass; | ||
1281 | |||
1282 | PhysicsScene.PE.ApplyCentralImpulse(PhysBody, new OMV.Vector3(0f, 0f, moveForce)); | ||
1283 | DetailLog("{0},BSPrim.Hover,move,targHt={1},moveForce={2},mass={3}", LocalID, targetHeight, moveForce, Mass); | ||
1284 | }); | ||
1285 | } | ||
1286 | else | ||
1287 | { | ||
1288 | UnRegisterPreStepAction("BSPrim.Hover", LocalID); | ||
1289 | } | ||
1290 | } | ||
914 | } | 1291 | } |
915 | public override float PIDHoverHeight { | 1292 | public override float PIDHoverHeight { |
916 | set { _PIDHoverHeight = value; } | 1293 | set { _PIDHoverHeight = value; } |
@@ -919,8 +1296,35 @@ public sealed class BSPrim : BSPhysObject | |||
919 | set { _PIDHoverType = value; } | 1296 | set { _PIDHoverType = value; } |
920 | } | 1297 | } |
921 | public override float PIDHoverTau { | 1298 | public override float PIDHoverTau { |
922 | set { _PIDHoverTao = value; } | 1299 | set { _PIDHoverTau = value; } |
923 | } | 1300 | } |
1301 | // Based on current position, determine what we should be hovering at now. | ||
1302 | // Must recompute often. What if we walked offa cliff> | ||
1303 | private float ComputeCurrentPIDHoverHeight() | ||
1304 | { | ||
1305 | float ret = _PIDHoverHeight; | ||
1306 | float groundHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); | ||
1307 | |||
1308 | switch (_PIDHoverType) | ||
1309 | { | ||
1310 | case PIDHoverType.Ground: | ||
1311 | ret = groundHeight + _PIDHoverHeight; | ||
1312 | break; | ||
1313 | case PIDHoverType.GroundAndWater: | ||
1314 | float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(RawPosition); | ||
1315 | if (groundHeight > waterHeight) | ||
1316 | { | ||
1317 | ret = groundHeight + _PIDHoverHeight; | ||
1318 | } | ||
1319 | else | ||
1320 | { | ||
1321 | ret = waterHeight + _PIDHoverHeight; | ||
1322 | } | ||
1323 | break; | ||
1324 | } | ||
1325 | return ret; | ||
1326 | } | ||
1327 | |||
924 | 1328 | ||
925 | // For RotLookAt | 1329 | // For RotLookAt |
926 | public override OMV.Quaternion APIDTarget { set { return; } } | 1330 | public override OMV.Quaternion APIDTarget { set { return; } } |
@@ -928,54 +1332,73 @@ public sealed class BSPrim : BSPhysObject | |||
928 | public override float APIDStrength { set { return; } } | 1332 | public override float APIDStrength { set { return; } } |
929 | public override float APIDDamping { set { return; } } | 1333 | public override float APIDDamping { set { return; } } |
930 | 1334 | ||
931 | private List<OMV.Vector3> m_accumulatedForces = new List<OMV.Vector3>(); | ||
932 | public override void AddForce(OMV.Vector3 force, bool pushforce) { | 1335 | public override void AddForce(OMV.Vector3 force, bool pushforce) { |
933 | AddForce(force, pushforce, false); | 1336 | // Per documentation, max force is limited. |
1337 | OMV.Vector3 addForce = Util.ClampV(force, BSParam.MaxAddForceMagnitude); | ||
1338 | |||
1339 | // Since this force is being applied in only one step, make this a force per second. | ||
1340 | addForce /= PhysicsScene.LastTimeStep; | ||
1341 | AddForce(addForce, pushforce, false /* inTaintTime */); | ||
934 | } | 1342 | } |
1343 | |||
935 | // Applying a force just adds this to the total force on the object. | 1344 | // Applying a force just adds this to the total force on the object. |
1345 | // This added force will only last the next simulation tick. | ||
936 | public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { | 1346 | public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { |
937 | // for an object, doesn't matter if force is a pushforce or not | 1347 | // for an object, doesn't matter if force is a pushforce or not |
938 | if (force.IsFinite()) | 1348 | if (IsPhysicallyActive) |
939 | { | ||
940 | // _force += force; | ||
941 | lock (m_accumulatedForces) | ||
942 | m_accumulatedForces.Add(new OMV.Vector3(force)); | ||
943 | } | ||
944 | else | ||
945 | { | 1349 | { |
946 | m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID); | 1350 | if (force.IsFinite()) |
947 | return; | ||
948 | } | ||
949 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddForce", delegate() | ||
950 | { | ||
951 | OMV.Vector3 fSum = OMV.Vector3.Zero; | ||
952 | lock (m_accumulatedForces) | ||
953 | { | 1351 | { |
954 | // Sum the accumulated additional forces for one big force to apply once. | 1352 | // DetailLog("{0},BSPrim.addForce,call,force={1}", LocalID, addForce); |
955 | foreach (OMV.Vector3 v in m_accumulatedForces) | 1353 | |
1354 | OMV.Vector3 addForce = force; | ||
1355 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddForce", delegate() | ||
956 | { | 1356 | { |
957 | fSum += v; | 1357 | // Bullet adds this central force to the total force for this tick |
958 | } | 1358 | DetailLog("{0},BSPrim.addForce,taint,force={1}", LocalID, addForce); |
959 | m_accumulatedForces.Clear(); | 1359 | if (PhysBody.HasPhysicalBody) |
1360 | { | ||
1361 | PhysicsScene.PE.ApplyCentralForce(PhysBody, addForce); | ||
1362 | ActivateIfPhysical(false); | ||
1363 | } | ||
1364 | }); | ||
960 | } | 1365 | } |
961 | DetailLog("{0},BSPrim.AddForce,taint,force={1}", LocalID, fSum); | 1366 | else |
962 | if (fSum != OMV.Vector3.Zero) | 1367 | { |
963 | BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, fSum); | 1368 | m_log.WarnFormat("{0}: AddForce: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID); |
964 | }); | 1369 | return; |
1370 | } | ||
1371 | } | ||
965 | } | 1372 | } |
966 | 1373 | ||
967 | // An impulse force is scaled by the mass of the object. | 1374 | public void AddForceImpulse(OMV.Vector3 impulse, bool pushforce, bool inTaintTime) { |
968 | public void ApplyForceImpulse(OMV.Vector3 impulse, bool inTaintTime) | 1375 | // for an object, doesn't matter if force is a pushforce or not |
969 | { | 1376 | if (!IsPhysicallyActive) |
970 | OMV.Vector3 applyImpulse = impulse; | ||
971 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyForceImpulse", delegate() | ||
972 | { | 1377 | { |
973 | DetailLog("{0},BSPrim.ApplyForceImpulse,taint,tImpulse={1}", LocalID, applyImpulse); | 1378 | if (impulse.IsFinite()) |
974 | BulletSimAPI.ApplyCentralImpulse2(PhysBody.ptr, applyImpulse); | 1379 | { |
975 | }); | 1380 | OMV.Vector3 addImpulse = Util.ClampV(impulse, BSParam.MaxAddForceMagnitude); |
1381 | // DetailLog("{0},BSPrim.addForceImpulse,call,impulse={1}", LocalID, impulse); | ||
1382 | |||
1383 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddImpulse", delegate() | ||
1384 | { | ||
1385 | // Bullet adds this impulse immediately to the velocity | ||
1386 | DetailLog("{0},BSPrim.addForceImpulse,taint,impulseforce={1}", LocalID, addImpulse); | ||
1387 | if (PhysBody.HasPhysicalBody) | ||
1388 | { | ||
1389 | PhysicsScene.PE.ApplyCentralImpulse(PhysBody, addImpulse); | ||
1390 | ActivateIfPhysical(false); | ||
1391 | } | ||
1392 | }); | ||
1393 | } | ||
1394 | else | ||
1395 | { | ||
1396 | m_log.WarnFormat("{0}: AddForceImpulse: Got a NaN impulse applied to a prim. LocalID={1}", LogHeader, LocalID); | ||
1397 | return; | ||
1398 | } | ||
1399 | } | ||
976 | } | 1400 | } |
977 | 1401 | ||
978 | private List<OMV.Vector3> m_accumulatedAngularForces = new List<OMV.Vector3>(); | ||
979 | public override void AddAngularForce(OMV.Vector3 force, bool pushforce) { | 1402 | public override void AddAngularForce(OMV.Vector3 force, bool pushforce) { |
980 | AddAngularForce(force, pushforce, false); | 1403 | AddAngularForce(force, pushforce, false); |
981 | } | 1404 | } |
@@ -983,42 +1406,38 @@ public sealed class BSPrim : BSPhysObject | |||
983 | { | 1406 | { |
984 | if (force.IsFinite()) | 1407 | if (force.IsFinite()) |
985 | { | 1408 | { |
986 | // _force += force; | 1409 | OMV.Vector3 angForce = force; |
987 | lock (m_accumulatedAngularForces) | 1410 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddAngularForce", delegate() |
988 | m_accumulatedAngularForces.Add(new OMV.Vector3(force)); | 1411 | { |
1412 | if (PhysBody.HasPhysicalBody) | ||
1413 | { | ||
1414 | DetailLog("{0},BSPrim.AddAngularForce,taint,angForce={1}", LocalID, angForce); | ||
1415 | PhysicsScene.PE.ApplyTorque(PhysBody, angForce); | ||
1416 | ActivateIfPhysical(false); | ||
1417 | } | ||
1418 | }); | ||
989 | } | 1419 | } |
990 | else | 1420 | else |
991 | { | 1421 | { |
992 | m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID); | 1422 | m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID); |
993 | return; | 1423 | return; |
994 | } | 1424 | } |
995 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddAngularForce", delegate() | ||
996 | { | ||
997 | OMV.Vector3 fSum = OMV.Vector3.Zero; | ||
998 | lock (m_accumulatedAngularForces) | ||
999 | { | ||
1000 | // Sum the accumulated additional forces for one big force to apply once. | ||
1001 | foreach (OMV.Vector3 v in m_accumulatedAngularForces) | ||
1002 | { | ||
1003 | fSum += v; | ||
1004 | } | ||
1005 | m_accumulatedAngularForces.Clear(); | ||
1006 | } | ||
1007 | DetailLog("{0},BSPrim.AddAngularForce,taint,aForce={1}", LocalID, fSum); | ||
1008 | if (fSum != OMV.Vector3.Zero) | ||
1009 | { | ||
1010 | BulletSimAPI.ApplyTorque2(PhysBody.ptr, fSum); | ||
1011 | _torque = fSum; | ||
1012 | } | ||
1013 | }); | ||
1014 | } | 1425 | } |
1426 | |||
1015 | // A torque impulse. | 1427 | // A torque impulse. |
1428 | // ApplyTorqueImpulse adds torque directly to the angularVelocity. | ||
1429 | // AddAngularForce accumulates the force and applied it to the angular velocity all at once. | ||
1430 | // Computed as: angularVelocity += impulse * inertia; | ||
1016 | public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime) | 1431 | public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime) |
1017 | { | 1432 | { |
1018 | OMV.Vector3 applyImpulse = impulse; | 1433 | OMV.Vector3 applyImpulse = impulse; |
1019 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate() | 1434 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate() |
1020 | { | 1435 | { |
1021 | BulletSimAPI.ApplyTorqueImpulse2(PhysBody.ptr, applyImpulse); | 1436 | if (PhysBody.HasPhysicalBody) |
1437 | { | ||
1438 | PhysicsScene.PE.ApplyTorqueImpulse(PhysBody, applyImpulse); | ||
1439 | ActivateIfPhysical(false); | ||
1440 | } | ||
1022 | }); | 1441 | }); |
1023 | } | 1442 | } |
1024 | 1443 | ||
@@ -1301,23 +1720,10 @@ public sealed class BSPrim : BSPhysObject | |||
1301 | profileEnd = 1.0f - (float)BaseShape.ProfileEnd * 2.0e-5f; | 1720 | profileEnd = 1.0f - (float)BaseShape.ProfileEnd * 2.0e-5f; |
1302 | volume *= (profileEnd - profileBegin); | 1721 | volume *= (profileEnd - profileBegin); |
1303 | 1722 | ||
1304 | returnMass = _density * volume; | 1723 | returnMass = Density * BSParam.DensityScaleFactor * volume; |
1305 | 1724 | DetailLog("{0},BSPrim.CalculateMass,den={1},vol={2},mass={3}", LocalID, Density, volume, returnMass); | |
1306 | /* Comment out code that computes the mass of the linkset. That is done in the Linkset class. | ||
1307 | if (IsRootOfLinkset) | ||
1308 | { | ||
1309 | foreach (BSPrim prim in _childrenPrims) | ||
1310 | { | ||
1311 | returnMass += prim.CalculateMass(); | ||
1312 | } | ||
1313 | } | ||
1314 | */ | ||
1315 | |||
1316 | if (returnMass <= 0) | ||
1317 | returnMass = 0.0001f; | ||
1318 | 1725 | ||
1319 | if (returnMass > PhysicsScene.MaximumObjectMass) | 1726 | returnMass = Util.Clamp(returnMass, BSParam.MinimumObjectMass, BSParam.MaximumObjectMass); |
1320 | returnMass = PhysicsScene.MaximumObjectMass; | ||
1321 | 1727 | ||
1322 | return returnMass; | 1728 | return returnMass; |
1323 | }// end CalculateMass | 1729 | }// end CalculateMass |
@@ -1326,135 +1732,73 @@ public sealed class BSPrim : BSPhysObject | |||
1326 | // Rebuild the geometry and object. | 1732 | // Rebuild the geometry and object. |
1327 | // This is called when the shape changes so we need to recreate the mesh/hull. | 1733 | // This is called when the shape changes so we need to recreate the mesh/hull. |
1328 | // Called at taint-time!!! | 1734 | // Called at taint-time!!! |
1329 | private void CreateGeomAndObject(bool forceRebuild) | 1735 | public void CreateGeomAndObject(bool forceRebuild) |
1330 | { | 1736 | { |
1331 | // If this prim is part of a linkset, we must remove and restore the physical | ||
1332 | // links if the body is rebuilt. | ||
1333 | bool needToRestoreLinkset = false; | ||
1334 | bool needToRestoreVehicle = false; | ||
1335 | |||
1336 | // Create the correct physical representation for this type of object. | 1737 | // Create the correct physical representation for this type of object. |
1337 | // Updates PhysBody and PhysShape with the new information. | 1738 | // Updates base.PhysBody and base.PhysShape with the new information. |
1338 | // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary. | 1739 | // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary. |
1339 | // Returns 'true' if either the body or the shape was changed. | 1740 | PhysicsScene.Shapes.GetBodyAndShape(false /*forceRebuild */, PhysicsScene.World, this, null, delegate(BulletBody dBody) |
1340 | PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, null, delegate(BulletBody dBody) | ||
1341 | { | 1741 | { |
1342 | // Called if the current prim body is about to be destroyed. | 1742 | // Called if the current prim body is about to be destroyed. |
1343 | // Remove all the physical dependencies on the old body. | 1743 | // Remove all the physical dependencies on the old body. |
1344 | // (Maybe someday make the changing of BSShape an event handled by BSLinkset.) | 1744 | // (Maybe someday make the changing of BSShape an event to be subscribed to by BSLinkset, ...) |
1345 | needToRestoreLinkset = Linkset.RemoveBodyDependencies(this); | 1745 | RemoveBodyDependencies(); |
1346 | needToRestoreVehicle = _vehicle.RemoveBodyDependencies(this); | ||
1347 | }); | 1746 | }); |
1348 | 1747 | ||
1349 | if (needToRestoreLinkset) | ||
1350 | { | ||
1351 | // If physical body dependencies were removed, restore them | ||
1352 | Linkset.RestoreBodyDependencies(this); | ||
1353 | } | ||
1354 | if (needToRestoreVehicle) | ||
1355 | { | ||
1356 | // If physical body dependencies were removed, restore them | ||
1357 | _vehicle.RestoreBodyDependencies(this); | ||
1358 | } | ||
1359 | |||
1360 | // Make sure the properties are set on the new object | 1748 | // Make sure the properties are set on the new object |
1361 | UpdatePhysicalParameters(); | 1749 | UpdatePhysicalParameters(); |
1362 | return; | 1750 | return; |
1363 | } | 1751 | } |
1364 | 1752 | ||
1365 | // The physics engine says that properties have updated. Update same and inform | 1753 | protected virtual void RemoveBodyDependencies() |
1366 | // the world that things have changed. | 1754 | { |
1367 | // TODO: do we really need to check for changed? Maybe just copy values and call RequestPhysicsterseUpdate() | 1755 | VehicleController.RemoveBodyDependencies(this); |
1368 | enum UpdatedProperties { | ||
1369 | Position = 1 << 0, | ||
1370 | Rotation = 1 << 1, | ||
1371 | Velocity = 1 << 2, | ||
1372 | Acceleration = 1 << 3, | ||
1373 | RotationalVel = 1 << 4 | ||
1374 | } | 1756 | } |
1375 | 1757 | ||
1376 | const float ROTATION_TOLERANCE = 0.01f; | 1758 | // The physics engine says that properties have updated. Update same and inform |
1377 | const float VELOCITY_TOLERANCE = 0.001f; | 1759 | // the world that things have changed. |
1378 | const float POSITION_TOLERANCE = 0.05f; | ||
1379 | const float ACCELERATION_TOLERANCE = 0.01f; | ||
1380 | const float ROTATIONAL_VELOCITY_TOLERANCE = 0.01f; | ||
1381 | |||
1382 | public override void UpdateProperties(EntityProperties entprop) | 1760 | public override void UpdateProperties(EntityProperties entprop) |
1383 | { | 1761 | { |
1384 | /* | 1762 | TriggerPreUpdatePropertyAction(ref entprop); |
1385 | UpdatedProperties changed = 0; | 1763 | |
1386 | // assign to the local variables so the normal set action does not happen | 1764 | // A temporary kludge to suppress the rotational effects introduced on vehicles by Bullet |
1387 | // if (_position != entprop.Position) | 1765 | // TODO: handle physics introduced by Bullet with computed vehicle physics. |
1388 | if (!_position.ApproxEquals(entprop.Position, POSITION_TOLERANCE)) | 1766 | if (VehicleController.IsActive) |
1389 | { | ||
1390 | _position = entprop.Position; | ||
1391 | changed |= UpdatedProperties.Position; | ||
1392 | } | ||
1393 | // if (_orientation != entprop.Rotation) | ||
1394 | if (!_orientation.ApproxEquals(entprop.Rotation, ROTATION_TOLERANCE)) | ||
1395 | { | ||
1396 | _orientation = entprop.Rotation; | ||
1397 | changed |= UpdatedProperties.Rotation; | ||
1398 | } | ||
1399 | // if (_velocity != entprop.Velocity) | ||
1400 | if (!_velocity.ApproxEquals(entprop.Velocity, VELOCITY_TOLERANCE)) | ||
1401 | { | ||
1402 | _velocity = entprop.Velocity; | ||
1403 | changed |= UpdatedProperties.Velocity; | ||
1404 | } | ||
1405 | // if (_acceleration != entprop.Acceleration) | ||
1406 | if (!_acceleration.ApproxEquals(entprop.Acceleration, ACCELERATION_TOLERANCE)) | ||
1407 | { | ||
1408 | _acceleration = entprop.Acceleration; | ||
1409 | changed |= UpdatedProperties.Acceleration; | ||
1410 | } | ||
1411 | // if (_rotationalVelocity != entprop.RotationalVelocity) | ||
1412 | if (!_rotationalVelocity.ApproxEquals(entprop.RotationalVelocity, ROTATIONAL_VELOCITY_TOLERANCE)) | ||
1413 | { | ||
1414 | _rotationalVelocity = entprop.RotationalVelocity; | ||
1415 | changed |= UpdatedProperties.RotationalVel; | ||
1416 | } | ||
1417 | if (changed != 0) | ||
1418 | { | 1767 | { |
1419 | // Only update the position of single objects and linkset roots | 1768 | entprop.RotationalVelocity = OMV.Vector3.Zero; |
1420 | if (Linkset.IsRoot(this)) | ||
1421 | { | ||
1422 | base.RequestPhysicsterseUpdate(); | ||
1423 | } | ||
1424 | } | 1769 | } |
1425 | */ | ||
1426 | 1770 | ||
1427 | // Don't check for damping here -- it's done in BulletSim and SceneObjectPart. | 1771 | // DetailLog("{0},BSPrim.UpdateProperties,entry,entprop={1}", LocalID, entprop); // DEBUG DEBUG |
1428 | 1772 | ||
1429 | // Updates only for individual prims and for the root object of a linkset. | 1773 | // Assign directly to the local variables so the normal set actions do not happen |
1430 | if (Linkset.IsRoot(this)) | 1774 | _position = entprop.Position; |
1431 | { | 1775 | _orientation = entprop.Rotation; |
1432 | // Assign directly to the local variables so the normal set action does not happen | 1776 | // DEBUG DEBUG DEBUG -- smooth velocity changes a bit. The simulator seems to be |
1433 | _position = entprop.Position; | 1777 | // very sensitive to velocity changes. |
1434 | _orientation = entprop.Rotation; | 1778 | if (entprop.Velocity == OMV.Vector3.Zero || !entprop.Velocity.ApproxEquals(_velocity, BSParam.UpdateVelocityChangeThreshold)) |
1435 | _velocity = entprop.Velocity; | 1779 | _velocity = entprop.Velocity; |
1436 | _acceleration = entprop.Acceleration; | 1780 | _acceleration = entprop.Acceleration; |
1437 | _rotationalVelocity = entprop.RotationalVelocity; | 1781 | _rotationalVelocity = entprop.RotationalVelocity; |
1438 | 1782 | ||
1439 | // The sanity check can change the velocity and/or position. | 1783 | // DetailLog("{0},BSPrim.UpdateProperties,afterAssign,entprop={1}", LocalID, entprop); // DEBUG DEBUG |
1440 | if (PositionSanityCheck(true)) | ||
1441 | { | ||
1442 | entprop.Position = _position; | ||
1443 | entprop.Velocity = _velocity; | ||
1444 | } | ||
1445 | 1784 | ||
1446 | // remember the current and last set values | 1785 | // The sanity check can change the velocity and/or position. |
1447 | LastEntityProperties = CurrentEntityProperties; | 1786 | if (PositionSanityCheck(true /* inTaintTime */ )) |
1448 | CurrentEntityProperties = entprop; | 1787 | { |
1788 | entprop.Position = _position; | ||
1789 | entprop.Velocity = _velocity; | ||
1790 | entprop.RotationalVelocity = _rotationalVelocity; | ||
1791 | entprop.Acceleration = _acceleration; | ||
1792 | } | ||
1449 | 1793 | ||
1450 | OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation; | 1794 | OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation; // DEBUG DEBUG DEBUG |
1451 | DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}", | 1795 | DetailLog("{0},BSPrim.UpdateProperties,call,entProp={1},dir={2}", LocalID, entprop, direction); |
1452 | LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity); | ||
1453 | 1796 | ||
1454 | // BulletSimAPI.DumpRigidBody2(PhysicsScene.World.ptr, BSBody.ptr); // DEBUG DEBUG DEBUG | 1797 | // remember the current and last set values |
1798 | LastEntityProperties = CurrentEntityProperties; | ||
1799 | CurrentEntityProperties = entprop; | ||
1455 | 1800 | ||
1456 | base.RequestPhysicsterseUpdate(); | 1801 | base.RequestPhysicsterseUpdate(); |
1457 | } | ||
1458 | /* | 1802 | /* |
1459 | else | 1803 | else |
1460 | { | 1804 | { |
@@ -1464,9 +1808,6 @@ public sealed class BSPrim : BSPhysObject | |||
1464 | entprop.Acceleration, entprop.RotationalVelocity); | 1808 | entprop.Acceleration, entprop.RotationalVelocity); |
1465 | } | 1809 | } |
1466 | */ | 1810 | */ |
1467 | |||
1468 | // The linkset implimentation might want to know about this. | ||
1469 | Linkset.UpdateProperties(this); | ||
1470 | } | 1811 | } |
1471 | } | 1812 | } |
1472 | } | 1813 | } |