aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs')
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs1147
1 files changed, 690 insertions, 457 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
index 2b3fa25..0323b0d 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
@@ -39,51 +39,47 @@ namespace OpenSim.Region.Physics.BulletSPlugin
39{ 39{
40 40
41 [Serializable] 41 [Serializable]
42public sealed class BSPrim : BSPhysObject 42public 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 public BSDynamics VehicleController { get; private set; }
79 74
75 private BSVMotor _targetMotor;
80 private OMV.Vector3 _PIDTarget; 76 private OMV.Vector3 _PIDTarget;
81 private bool _usePID;
82 private float _PIDTau; 77 private float _PIDTau;
83 private bool _useHoverPID; 78
79 private BSFMotor _hoverMotor;
84 private float _PIDHoverHeight; 80 private float _PIDHoverHeight;
85 private PIDHoverType _PIDHoverType; 81 private PIDHoverType _PIDHoverType;
86 private float _PIDHoverTao; 82 private float _PIDHoverTau;
87 83
88 public BSPrim(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, 84 public BSPrim(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size,
89 OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical) 85 OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical)
@@ -93,23 +89,18 @@ public sealed class BSPrim : BSPhysObject
93 _physicsActorType = (int)ActorTypes.Prim; 89 _physicsActorType = (int)ActorTypes.Prim;
94 _position = pos; 90 _position = pos;
95 _size = size; 91 _size = size;
96 Scale = size; // the scale will be set by CreateGeom depending on object type 92 Scale = size; // prims are the size the user wants them to be (different for BSCharactes).
97 _orientation = rotation; 93 _orientation = rotation;
98 _buoyancy = 1f; 94 _buoyancy = 0f;
99 _velocity = OMV.Vector3.Zero; 95 _velocity = OMV.Vector3.Zero;
100 _rotationalVelocity = OMV.Vector3.Zero; 96 _rotationalVelocity = OMV.Vector3.Zero;
101 BaseShape = pbs; 97 BaseShape = pbs;
102 _isPhysical = pisPhysical; 98 _isPhysical = pisPhysical;
103 _isVolumeDetect = false; 99 _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 100
110 // No body or shape yet 101 VehicleController = new BSDynamics(PhysicsScene, this); // add vehicleness
111 PhysBody = new BulletBody(LocalID, IntPtr.Zero); 102
112 PhysShape = new BulletShape(IntPtr.Zero); 103 _mass = CalculateMass();
113 104
114 DetailLog("{0},BSPrim.constructor,call", LocalID); 105 DetailLog("{0},BSPrim.constructor,call", LocalID);
115 // do the actual object creation at taint time 106 // do the actual object creation at taint time
@@ -117,7 +108,7 @@ public sealed class BSPrim : BSPhysObject
117 { 108 {
118 CreateGeomAndObject(true); 109 CreateGeomAndObject(true);
119 110
120 CurrentCollisionFlags = BulletSimAPI.GetCollisionFlags2(PhysBody.ptr); 111 CurrentCollisionFlags = PhysicsScene.PE.GetCollisionFlags(PhysBody);
121 }); 112 });
122 } 113 }
123 114
@@ -125,15 +116,7 @@ public sealed class BSPrim : BSPhysObject
125 public override void Destroy() 116 public override void Destroy()
126 { 117 {
127 // m_log.DebugFormat("{0}: Destroy, id={1}", LogHeader, LocalID); 118 // m_log.DebugFormat("{0}: Destroy, id={1}", LogHeader, LocalID);
128 119 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 120
138 // Undo any vehicle properties 121 // Undo any vehicle properties
139 this.VehicleType = (int)Vehicle.TYPE_NONE; 122 this.VehicleType = (int)Vehicle.TYPE_NONE;
@@ -142,8 +125,10 @@ public sealed class BSPrim : BSPhysObject
142 { 125 {
143 DetailLog("{0},BSPrim.Destroy,taint,", LocalID); 126 DetailLog("{0},BSPrim.Destroy,taint,", LocalID);
144 // If there are physical body and shape, release my use of same. 127 // If there are physical body and shape, release my use of same.
145 PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null); 128 PhysicsScene.Shapes.DereferenceBody(PhysBody, null);
146 PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null); 129 PhysBody.Clear();
130 PhysicsScene.Shapes.DereferenceShape(PhysShape, null);
131 PhysShape.Clear();
147 }); 132 });
148 } 133 }
149 134
@@ -157,31 +142,37 @@ public sealed class BSPrim : BSPhysObject
157 // We presume the scale and size are the same. If scale must be changed for 142 // 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. 143 // the physical shape, that is done when the geometry is built.
159 _size = value; 144 _size = value;
145 Scale = _size;
160 ForceBodyShapeRebuild(false); 146 ForceBodyShapeRebuild(false);
161 } 147 }
162 } 148 }
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 149
167 public override PrimitiveBaseShape Shape { 150 public override PrimitiveBaseShape Shape {
168 set { 151 set {
169 BaseShape = value; 152 BaseShape = value;
153 LastAssetBuildFailed = false;
170 ForceBodyShapeRebuild(false); 154 ForceBodyShapeRebuild(false);
171 } 155 }
172 } 156 }
173 // Whatever the linkset wants is what I want. 157 // 'unknown' says to choose the best type
174 public override BSPhysicsShapeType PreferredPhysicalShape 158 public override BSPhysicsShapeType PreferredPhysicalShape
175 { get { return Linkset.PreferredPhysicalShape(this); } } 159 { get { return BSPhysicsShapeType.SHAPE_UNKNOWN; } }
176 160
177 public override bool ForceBodyShapeRebuild(bool inTaintTime) 161 public override bool ForceBodyShapeRebuild(bool inTaintTime)
178 { 162 {
179 LastAssetBuildFailed = false; 163 if (inTaintTime)
180 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ForceBodyShapeRebuild", delegate()
181 { 164 {
182 _mass = CalculateMass(); // changing the shape changes the mass 165 _mass = CalculateMass(); // changing the shape changes the mass
183 CreateGeomAndObject(true); 166 CreateGeomAndObject(true);
184 }); 167 }
168 else
169 {
170 PhysicsScene.TaintedObject("BSPrim.ForceBodyShapeRebuild", delegate()
171 {
172 _mass = CalculateMass(); // changing the shape changes the mass
173 CreateGeomAndObject(true);
174 });
175 }
185 return true; 176 return true;
186 } 177 }
187 public override bool Grabbed { 178 public override bool Grabbed {
@@ -189,46 +180,31 @@ public sealed class BSPrim : BSPhysObject
189 } 180 }
190 } 181 }
191 public override bool Selected { 182 public override bool Selected {
192 set { 183 set
193 _isSelected = value; 184 {
194 PhysicsScene.TaintedObject("BSPrim.setSelected", delegate() 185 if (value != _isSelected)
195 { 186 {
196 DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected); 187 _isSelected = value;
197 SetObjectDynamic(false); 188 PhysicsScene.TaintedObject("BSPrim.setSelected", delegate()
198 }); 189 {
190 DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected);
191 SetObjectDynamic(false);
192 });
193 }
199 } 194 }
200 } 195 }
196 public override bool IsSelected
197 {
198 get { return _isSelected; }
199 }
201 public override void CrossingFailure() { return; } 200 public override void CrossingFailure() { return; }
202 201
203 // link me to the specified parent 202 // link me to the specified parent
204 public override void link(PhysicsActor obj) { 203 public override void link(PhysicsActor obj) {
205 BSPrim parent = obj as BSPrim;
206 if (parent != null)
207 {
208 BSPhysObject parentBefore = Linkset.LinksetRoot;
209 int childrenBefore = Linkset.NumberOfChildren;
210
211 Linkset = parent.Linkset.AddMeToLinkset(this);
212
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 return;
217 } 204 }
218 205
219 // delink me from my linkset 206 // delink me from my linkset
220 public override void delink() { 207 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 } 208 }
233 209
234 // Set motion values to zero. 210 // Set motion values to zero.
@@ -244,7 +220,8 @@ public sealed class BSPrim : BSPhysObject
244 // Zero some other properties in the physics engine 220 // Zero some other properties in the physics engine
245 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() 221 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate()
246 { 222 {
247 BulletSimAPI.ClearAllForces2(PhysBody.ptr); 223 if (PhysBody.HasPhysicalBody)
224 PhysicsScene.PE.ClearAllForces(PhysBody);
248 }); 225 });
249 } 226 }
250 public override void ZeroAngularMotion(bool inTaintTime) 227 public override void ZeroAngularMotion(bool inTaintTime)
@@ -253,14 +230,57 @@ public sealed class BSPrim : BSPhysObject
253 // Zero some other properties in the physics engine 230 // Zero some other properties in the physics engine
254 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() 231 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate()
255 { 232 {
256 BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); 233 // DetailLog("{0},BSPrim.ZeroAngularMotion,call,rotVel={1}", LocalID, _rotationalVelocity);
257 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); 234 if (PhysBody.HasPhysicalBody)
235 {
236 PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity);
237 PhysicsScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity);
238 }
258 }); 239 });
259 } 240 }
260 241
261 public override void LockAngularMotion(OMV.Vector3 axis) 242 public override void LockAngularMotion(OMV.Vector3 axis)
262 { 243 {
263 DetailLog("{0},BSPrim.LockAngularMotion,call,axis={1}", LocalID, axis); 244 DetailLog("{0},BSPrim.LockAngularMotion,call,axis={1}", LocalID, axis);
245
246 OMV.Vector3 locking = new OMV.Vector3(1f, 1f, 1f);
247 if (axis.X != 1) locking.X = 0f;
248 if (axis.Y != 1) locking.Y = 0f;
249 if (axis.Z != 1) locking.Z = 0f;
250 LockedAxis = locking;
251
252 /* Not implemented yet
253 if (LockedAxis != LockedAxisFree)
254 {
255 // Something is locked so start the thingy that keeps that axis from changing
256 RegisterPreUpdatePropertyAction("BSPrim.LockAngularMotion", delegate(ref EntityProperties entprop)
257 {
258 if (LockedAxis != LockedAxisFree)
259 {
260 if (IsPhysicallyActive)
261 {
262 // Bullet can lock axis but it only works for global axis.
263 // Check if this prim is aligned on global axis and use Bullet's
264 // system if so.
265
266 ForceOrientation = entprop.Rotation;
267 ForceRotationalVelocity = entprop.RotationalVelocity;
268 }
269 }
270 else
271 {
272 UnRegisterPreUpdatePropertyAction("BSPrim.LockAngularMotion");
273 }
274
275 });
276 }
277 else
278 {
279 // Everything seems unlocked
280 UnRegisterPreUpdatePropertyAction("BSPrim.LockAngularMotion");
281 }
282 */
283
264 return; 284 return;
265 } 285 }
266 286
@@ -271,41 +291,41 @@ public sealed class BSPrim : BSPhysObject
271 } 291 }
272 public override OMV.Vector3 Position { 292 public override OMV.Vector3 Position {
273 get { 293 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. 294 // 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); 295 // _position = ForcePosition;
280 return _position; 296 return _position;
281 } 297 }
282 set { 298 set {
283 // If the position must be forced into the physics engine, use ForcePosition. 299 // If the position must be forced into the physics engine, use ForcePosition.
300 // All positions are given in world positions.
284 if (_position == value) 301 if (_position == value)
285 { 302 {
303 DetailLog("{0},BSPrim.setPosition,call,positionNotChanging,pos={1},orient={2}", LocalID, _position, _orientation);
286 return; 304 return;
287 } 305 }
288 _position = value; 306 _position = value;
289 // TODO: what does it mean to set the position of a child prim?? Rebuild the constraint?
290 PositionSanityCheck(false); 307 PositionSanityCheck(false);
308
291 PhysicsScene.TaintedObject("BSPrim.setPosition", delegate() 309 PhysicsScene.TaintedObject("BSPrim.setPosition", delegate()
292 { 310 {
293 // DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); 311 DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
294 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 312 ForcePosition = _position;
295 ActivateIfPhysical(false);
296 }); 313 });
297 } 314 }
298 } 315 }
316
299 public override OMV.Vector3 ForcePosition { 317 public override OMV.Vector3 ForcePosition {
300 get { 318 get {
301 _position = BulletSimAPI.GetPosition2(PhysBody.ptr); 319 _position = PhysicsScene.PE.GetPosition(PhysBody);
302 return _position; 320 return _position;
303 } 321 }
304 set { 322 set {
305 _position = value; 323 _position = value;
306 // PositionSanityCheck(); // Don't do this! Causes a loop and caller should know better. 324 if (PhysBody.HasPhysicalBody)
307 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 325 {
308 ActivateIfPhysical(false); 326 PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation);
327 ActivateIfPhysical(false);
328 }
309 } 329 }
310 } 330 }
311 331
@@ -316,119 +336,199 @@ public sealed class BSPrim : BSPhysObject
316 { 336 {
317 bool ret = false; 337 bool ret = false;
318 338
319 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); 339 // We don't care where non-physical items are placed
340 if (!IsPhysicallyActive)
341 return ret;
342
343 if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(RawPosition))
344 {
345 // The physical object is out of the known/simulated area.
346 // Upper levels of code will handle the transition to other areas so, for
347 // the time, we just ignore the position.
348 return ret;
349 }
350
351 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition);
320 OMV.Vector3 upForce = OMV.Vector3.Zero; 352 OMV.Vector3 upForce = OMV.Vector3.Zero;
321 if (Position.Z < terrainHeight) 353 float approxSize = Math.Max(Size.X, Math.Max(Size.Y, Size.Z));
354 if ((RawPosition.Z + approxSize / 2f) < terrainHeight)
322 { 355 {
323 DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight); 356 DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, RawPosition, terrainHeight);
324 float targetHeight = terrainHeight + (Size.Z / 2f); 357 float targetHeight = terrainHeight + (Size.Z / 2f);
325 // Upforce proportional to the distance away from the terrain. Correct the error in 1 sec. 358 // If the object is below ground it just has to be moved up because pushing will
326 upForce.Z = (terrainHeight - Position.Z) * 1f; 359 // not get it through the terrain
360 _position.Z = targetHeight;
361 if (inTaintTime)
362 {
363 ForcePosition = _position;
364 }
365 // If we are throwing the object around, zero its other forces
366 ZeroMotion(inTaintTime);
327 ret = true; 367 ret = true;
328 } 368 }
329 369
330 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) 370 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
331 { 371 {
332 float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position); 372 float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position);
333 // TODO: a floating motor so object will bob in the water 373 // TODO: a floating motor so object will bob in the water
334 if (Math.Abs(Position.Z - waterHeight) > 0.1f) 374 if (Math.Abs(RawPosition.Z - waterHeight) > 0.1f)
335 { 375 {
336 // Upforce proportional to the distance away from the water. Correct the error in 1 sec. 376 // Upforce proportional to the distance away from the water. Correct the error in 1 sec.
337 upForce.Z = (waterHeight - Position.Z) * 1f; 377 upForce.Z = (waterHeight - RawPosition.Z) * 1f;
378
379 // Apply upforce and overcome gravity.
380 OMV.Vector3 correctionForce = upForce - PhysicsScene.DefaultGravity;
381 DetailLog("{0},BSPrim.PositionSanityCheck,applyForce,pos={1},upForce={2},correctionForce={3}", LocalID, _position, upForce, correctionForce);
382 AddForce(correctionForce, false, inTaintTime);
338 ret = true; 383 ret = true;
339 } 384 }
340 } 385 }
341 386
342 // TODO: check for out of bounds
343
344 // The above code computes a force to apply to correct any out-of-bounds problems. Apply same.
345 if (ret)
346 {
347 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.PositionSanityCheck:belowTerrain", delegate()
348 {
349 // Apply upforce and overcome gravity.
350 ForceVelocity = ForceVelocity + upForce - PhysicsScene.DefaultGravity;
351 });
352 }
353 return ret; 387 return ret;
354 } 388 }
355 389
356 // Return the effective mass of the object. 390 // Return the effective mass of the object.
357 // If there are multiple items in the linkset, add them together for the root 391 // The definition of this call is to return the mass of the prim.
392 // If the simulator cares about the mass of the linkset, it will sum it itself.
358 public override float Mass 393 public override float Mass
359 { 394 {
360 get 395 get { return _mass; }
361 { 396 }
362 return Linkset.LinksetMass; 397 // TotalMass returns the mass of the large object the prim may be in (overridden by linkset code)
363 // return _mass; 398 public virtual float TotalMass
364 } 399 {
400 get { return _mass; }
365 } 401 }
366
367 // used when we only want this prim's mass and not the linkset thing 402 // used when we only want this prim's mass and not the linkset thing
368 public override float RawMass { 403 public override float RawMass {
369 get { return _mass; } 404 get { return _mass; }
370 } 405 }
371 // Set the physical mass to the passed mass. 406 // Set the physical mass to the passed mass.
372 // Note that this does not change _mass! 407 // Note that this does not change _mass!
373 public override void UpdatePhysicalMassProperties(float physMass) 408 public override void UpdatePhysicalMassProperties(float physMass, bool inWorld)
374 { 409 {
375 if (IsStatic) 410 if (PhysBody.HasPhysicalBody)
376 { 411 {
377 Inertia = OMV.Vector3.Zero; 412 if (IsStatic)
378 BulletSimAPI.SetMassProps2(PhysBody.ptr, 0f, Inertia); 413 {
379 BulletSimAPI.UpdateInertiaTensor2(PhysBody.ptr); 414 PhysicsScene.PE.SetGravity(PhysBody, PhysicsScene.DefaultGravity);
415 Inertia = OMV.Vector3.Zero;
416 PhysicsScene.PE.SetMassProps(PhysBody, 0f, Inertia);
417 PhysicsScene.PE.UpdateInertiaTensor(PhysBody);
418 }
419 else
420 {
421 if (inWorld)
422 {
423 // Changing interesting properties doesn't change proxy and collision cache
424 // information. The Bullet solution is to re-add the object to the world
425 // after parameters are changed.
426 PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, PhysBody);
427 }
428
429 // The computation of mass props requires gravity to be set on the object.
430 Gravity = ComputeGravity(Buoyancy);
431 PhysicsScene.PE.SetGravity(PhysBody, Gravity);
432
433 Inertia = PhysicsScene.PE.CalculateLocalInertia(PhysShape, physMass);
434 PhysicsScene.PE.SetMassProps(PhysBody, physMass, Inertia);
435 PhysicsScene.PE.UpdateInertiaTensor(PhysBody);
436
437 DetailLog("{0},BSPrim.UpdateMassProperties,mass={1},localInertia={2},grav={3},inWorld={4}",
438 LocalID, physMass, Inertia, Gravity, inWorld);
439
440 if (inWorld)
441 {
442 AddObjectToPhysicalWorld();
443 }
444 }
380 } 445 }
381 else 446 }
447
448 // Return what gravity should be set to this very moment
449 public OMV.Vector3 ComputeGravity(float buoyancy)
450 {
451 OMV.Vector3 ret = PhysicsScene.DefaultGravity;
452
453 if (!IsStatic)
382 { 454 {
383 Inertia = BulletSimAPI.CalculateLocalInertia2(PhysShape.ptr, physMass); 455 ret *= (1f - buoyancy);
384 BulletSimAPI.SetMassProps2(PhysBody.ptr, physMass, Inertia); 456 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 } 457 }
458
459 return ret;
390 } 460 }
391 461
392 // Is this used? 462 // Is this used?
393 public override OMV.Vector3 CenterOfMass 463 public override OMV.Vector3 CenterOfMass
394 { 464 {
395 get { return Linkset.CenterOfMass; } 465 get { return RawPosition; }
396 } 466 }
397 467
398 // Is this used? 468 // Is this used?
399 public override OMV.Vector3 GeometricCenter 469 public override OMV.Vector3 GeometricCenter
400 { 470 {
401 get { return Linkset.GeometricCenter; } 471 get { return RawPosition; }
402 } 472 }
403 473
404 public override OMV.Vector3 Force { 474 public override OMV.Vector3 Force {
405 get { return _force; } 475 get { return _force; }
406 set { 476 set {
407 _force = value; 477 _force = value;
408 PhysicsScene.TaintedObject("BSPrim.setForce", delegate() 478 if (_force != OMV.Vector3.Zero)
409 { 479 {
410 // DetailLog("{0},BSPrim.setForce,taint,force={1}", LocalID, _force); 480 // If the force is non-zero, it must be reapplied each tick because
411 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force); 481 // Bullet clears the forces applied last frame.
412 }); 482 RegisterPreStepAction("BSPrim.setForce", LocalID,
483 delegate(float timeStep)
484 {
485 if (!IsPhysicallyActive || _force == OMV.Vector3.Zero)
486 {
487 UnRegisterPreStepAction("BSPrim.setForce", LocalID);
488 return;
489 }
490
491 DetailLog("{0},BSPrim.setForce,preStep,force={1}", LocalID, _force);
492 if (PhysBody.HasPhysicalBody)
493 {
494 PhysicsScene.PE.ApplyCentralForce(PhysBody, _force);
495 ActivateIfPhysical(false);
496 }
497 }
498 );
499 }
500 else
501 {
502 UnRegisterPreStepAction("BSPrim.setForce", LocalID);
503 }
413 } 504 }
414 } 505 }
415 506
416 public override int VehicleType { 507 public override int VehicleType {
417 get { 508 get {
418 return (int)_vehicle.Type; // if we are a vehicle, return that type 509 return (int)VehicleController.Type; // if we are a vehicle, return that type
419 } 510 }
420 set { 511 set {
421 Vehicle type = (Vehicle)value; 512 Vehicle type = (Vehicle)value;
422 513
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() 514 PhysicsScene.TaintedObject("setVehicleType", delegate()
427 { 515 {
428 // Done at taint time so we're sure the physics engine is not using the variables 516 // 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. 517 // Vehicle code changes the parameters for this vehicle type.
430 _vehicle.ProcessTypeChange(type); 518 VehicleController.ProcessTypeChange(type);
431 ActivateIfPhysical(false); 519 ActivateIfPhysical(false);
520
521 // If an active vehicle, register the vehicle code to be called before each step
522 if (VehicleController.Type == Vehicle.TYPE_NONE)
523 {
524 UnRegisterPreStepAction("BSPrim.Vehicle", LocalID);
525 PhysicsScene.AfterStep -= VehicleController.PostStep;
526 }
527 else
528 {
529 RegisterPreStepAction("BSPrim.Vehicle", LocalID, VehicleController.Step);
530 PhysicsScene.AfterStep += VehicleController.PostStep;
531 }
432 }); 532 });
433 } 533 }
434 } 534 }
@@ -436,7 +536,7 @@ public sealed class BSPrim : BSPhysObject
436 { 536 {
437 PhysicsScene.TaintedObject("BSPrim.VehicleFloatParam", delegate() 537 PhysicsScene.TaintedObject("BSPrim.VehicleFloatParam", delegate()
438 { 538 {
439 _vehicle.ProcessFloatVehicleParam((Vehicle)param, value); 539 VehicleController.ProcessFloatVehicleParam((Vehicle)param, value);
440 ActivateIfPhysical(false); 540 ActivateIfPhysical(false);
441 }); 541 });
442 } 542 }
@@ -444,7 +544,7 @@ public sealed class BSPrim : BSPhysObject
444 { 544 {
445 PhysicsScene.TaintedObject("BSPrim.VehicleVectorParam", delegate() 545 PhysicsScene.TaintedObject("BSPrim.VehicleVectorParam", delegate()
446 { 546 {
447 _vehicle.ProcessVectorVehicleParam((Vehicle)param, value); 547 VehicleController.ProcessVectorVehicleParam((Vehicle)param, value);
448 ActivateIfPhysical(false); 548 ActivateIfPhysical(false);
449 }); 549 });
450 } 550 }
@@ -452,7 +552,7 @@ public sealed class BSPrim : BSPhysObject
452 { 552 {
453 PhysicsScene.TaintedObject("BSPrim.VehicleRotationParam", delegate() 553 PhysicsScene.TaintedObject("BSPrim.VehicleRotationParam", delegate()
454 { 554 {
455 _vehicle.ProcessRotationVehicleParam((Vehicle)param, rotation); 555 VehicleController.ProcessRotationVehicleParam((Vehicle)param, rotation);
456 ActivateIfPhysical(false); 556 ActivateIfPhysical(false);
457 }); 557 });
458 } 558 }
@@ -460,27 +560,10 @@ public sealed class BSPrim : BSPhysObject
460 { 560 {
461 PhysicsScene.TaintedObject("BSPrim.VehicleFlags", delegate() 561 PhysicsScene.TaintedObject("BSPrim.VehicleFlags", delegate()
462 { 562 {
463 _vehicle.ProcessVehicleFlags(param, remove); 563 VehicleController.ProcessVehicleFlags(param, remove);
464 }); 564 });
465 } 565 }
466 566
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 567 // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more
485 public override void SetVolumeDetect(int param) { 568 public override void SetVolumeDetect(int param) {
486 bool newValue = (param != 0); 569 bool newValue = (param != 0);
@@ -495,6 +578,81 @@ public sealed class BSPrim : BSPhysObject
495 } 578 }
496 return; 579 return;
497 } 580 }
581 public override void SetMaterial(int material)
582 {
583 base.SetMaterial(material);
584 PhysicsScene.TaintedObject("BSPrim.SetMaterial", delegate()
585 {
586 UpdatePhysicalParameters();
587 });
588 }
589 public override float Friction
590 {
591 get { return base.Friction; }
592 set
593 {
594 if (base.Friction != value)
595 {
596 base.Friction = value;
597 PhysicsScene.TaintedObject("BSPrim.setFriction", delegate()
598 {
599 UpdatePhysicalParameters();
600 });
601 }
602 }
603 }
604 public override float Restitution
605 {
606 get { return base.Restitution; }
607 set
608 {
609 if (base.Restitution != value)
610 {
611 base.Restitution = value;
612 PhysicsScene.TaintedObject("BSPrim.setRestitution", delegate()
613 {
614 UpdatePhysicalParameters();
615 });
616 }
617 }
618 }
619 // The simulator/viewer keep density as 100kg/m3.
620 // Remember to use BSParam.DensityScaleFactor to create the physical density.
621 public override float Density
622 {
623 get { return base.Density; }
624 set
625 {
626 if (base.Density != value)
627 {
628 base.Density = value;
629 PhysicsScene.TaintedObject("BSPrim.setDensity", delegate()
630 {
631 UpdatePhysicalParameters();
632 });
633 }
634 }
635 }
636 public override float GravModifier
637 {
638 get { return base.GravModifier; }
639 set
640 {
641 if (base.GravModifier != value)
642 {
643 base.GravModifier = value;
644 PhysicsScene.TaintedObject("BSPrim.setGravityModifier", delegate()
645 {
646 UpdatePhysicalParameters();
647 });
648 }
649 }
650 }
651 public override OMV.Vector3 RawVelocity
652 {
653 get { return _velocity; }
654 set { _velocity = value; }
655 }
498 public override OMV.Vector3 Velocity { 656 public override OMV.Vector3 Velocity {
499 get { return _velocity; } 657 get { return _velocity; }
500 set { 658 set {
@@ -502,30 +660,53 @@ public sealed class BSPrim : BSPhysObject
502 PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate() 660 PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate()
503 { 661 {
504 // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity); 662 // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity);
505 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity); 663 ForceVelocity = _velocity;
506 }); 664 });
507 } 665 }
508 } 666 }
509 public override OMV.Vector3 ForceVelocity { 667 public override OMV.Vector3 ForceVelocity {
510 get { return _velocity; } 668 get { return _velocity; }
511 set { 669 set {
670 PhysicsScene.AssertInTaintTime("BSPrim.ForceVelocity");
671
512 _velocity = value; 672 _velocity = value;
513 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity); 673 if (PhysBody.HasPhysicalBody)
674 {
675 DetailLog("{0},BSPrim.ForceVelocity,taint,vel={1}", LocalID, _velocity);
676 PhysicsScene.PE.SetLinearVelocity(PhysBody, _velocity);
677 ActivateIfPhysical(false);
678 }
514 } 679 }
515 } 680 }
516 public override OMV.Vector3 Torque { 681 public override OMV.Vector3 Torque {
517 get { return _torque; } 682 get { return _torque; }
518 set { 683 set {
519 _torque = value; 684 _torque = value;
520 AddAngularForce(_torque, false, false); 685 if (_torque != OMV.Vector3.Zero)
686 {
687 // If the torque is non-zero, it must be reapplied each tick because
688 // Bullet clears the forces applied last frame.
689 RegisterPreStepAction("BSPrim.setTorque", LocalID,
690 delegate(float timeStep)
691 {
692 if (!IsPhysicallyActive || _torque == OMV.Vector3.Zero)
693 {
694 UnRegisterPreStepAction("BSPrim.setTorque", LocalID);
695 return;
696 }
697
698 if (PhysBody.HasPhysicalBody)
699 AddAngularForce(_torque, false, true);
700 }
701 );
702 }
703 else
704 {
705 UnRegisterPreStepAction("BSPrim.setTorque", LocalID);
706 }
521 // DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, _torque); 707 // DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, _torque);
522 } 708 }
523 } 709 }
524 public override float CollisionScore {
525 get { return _collisionScore; }
526 set { _collisionScore = value;
527 }
528 }
529 public override OMV.Vector3 Acceleration { 710 public override OMV.Vector3 Acceleration {
530 get { return _acceleration; } 711 get { return _acceleration; }
531 set { _acceleration = value; } 712 set { _acceleration = value; }
@@ -537,23 +718,16 @@ public sealed class BSPrim : BSPhysObject
537 } 718 }
538 public override OMV.Quaternion Orientation { 719 public override OMV.Quaternion Orientation {
539 get { 720 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; 721 return _orientation;
546 } 722 }
547 set { 723 set {
548 if (_orientation == value) 724 if (_orientation == value)
549 return; 725 return;
550 _orientation = value; 726 _orientation = value;
551 // TODO: what does it mean if a child in a linkset changes its orientation? Rebuild the constraint? 727
552 PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate() 728 PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate()
553 { 729 {
554 // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr); 730 ForceOrientation = _orientation;
555 // DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation);
556 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
557 }); 731 });
558 } 732 }
559 } 733 }
@@ -562,13 +736,14 @@ public sealed class BSPrim : BSPhysObject
562 { 736 {
563 get 737 get
564 { 738 {
565 _orientation = BulletSimAPI.GetOrientation2(PhysBody.ptr); 739 _orientation = PhysicsScene.PE.GetOrientation(PhysBody);
566 return _orientation; 740 return _orientation;
567 } 741 }
568 set 742 set
569 { 743 {
570 _orientation = value; 744 _orientation = value;
571 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 745 if (PhysBody.HasPhysicalBody)
746 PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation);
572 } 747 }
573 } 748 }
574 public override int PhysicsActorType { 749 public override int PhysicsActorType {
@@ -583,7 +758,7 @@ public sealed class BSPrim : BSPhysObject
583 _isPhysical = value; 758 _isPhysical = value;
584 PhysicsScene.TaintedObject("BSPrim.setIsPhysical", delegate() 759 PhysicsScene.TaintedObject("BSPrim.setIsPhysical", delegate()
585 { 760 {
586 // DetailLog("{0},setIsPhysical,taint,isPhys={1}", LocalID, _isPhysical); 761 DetailLog("{0},setIsPhysical,taint,isPhys={1}", LocalID, _isPhysical);
587 SetObjectDynamic(true); 762 SetObjectDynamic(true);
588 // whether phys-to-static or static-to-phys, the object is not moving. 763 // whether phys-to-static or static-to-phys, the object is not moving.
589 ZeroMotion(true); 764 ZeroMotion(true);
@@ -604,6 +779,12 @@ public sealed class BSPrim : BSPhysObject
604 get { return !IsPhantom && !_isVolumeDetect; } 779 get { return !IsPhantom && !_isVolumeDetect; }
605 } 780 }
606 781
782 // The object is moving and is actively being dynamic in the physical world
783 public override bool IsPhysicallyActive
784 {
785 get { return !_isSelected && IsPhysical; }
786 }
787
607 // Make gravity work if the object is physical and not selected 788 // Make gravity work if the object is physical and not selected
608 // Called at taint-time!! 789 // Called at taint-time!!
609 private void SetObjectDynamic(bool forceRebuild) 790 private void SetObjectDynamic(bool forceRebuild)
@@ -618,19 +799,24 @@ public sealed class BSPrim : BSPhysObject
618 // isSolid: other objects bounce off of this object 799 // isSolid: other objects bounce off of this object
619 // isVolumeDetect: other objects pass through but can generate collisions 800 // isVolumeDetect: other objects pass through but can generate collisions
620 // collisionEvents: whether this object returns collision events 801 // collisionEvents: whether this object returns collision events
621 private void UpdatePhysicalParameters() 802 public virtual void UpdatePhysicalParameters()
622 { 803 {
623 // DetailLog("{0},BSPrim.UpdatePhysicalParameters,entry,body={1},shape={2}", LocalID, BSBody, BSShape); 804 if (!PhysBody.HasPhysicalBody)
805 {
806 // This would only happen if updates are called for during initialization when the body is not set up yet.
807 DetailLog("{0},BSPrim.UpdatePhysicalParameters,taint,calledWithNoPhysBody", LocalID);
808 return;
809 }
624 810
625 // Mangling all the physical properties requires the object not be in the physical world. 811 // 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). 812 // 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); 813 PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, PhysBody);
628 814
629 // Set up the object physicalness (does gravity and collisions move this object) 815 // Set up the object physicalness (does gravity and collisions move this object)
630 MakeDynamic(IsStatic); 816 MakeDynamic(IsStatic);
631 817
632 // Update vehicle specific parameters (after MakeDynamic() so can change physical parameters) 818 // Update vehicle specific parameters (after MakeDynamic() so can change physical parameters)
633 _vehicle.Refresh(); 819 VehicleController.Refresh();
634 820
635 // Arrange for collision events if the simulator wants them 821 // Arrange for collision events if the simulator wants them
636 EnableCollisions(SubscribedEvents()); 822 EnableCollisions(SubscribedEvents());
@@ -638,25 +824,13 @@ public sealed class BSPrim : BSPhysObject
638 // Make solid or not (do things bounce off or pass through this object). 824 // Make solid or not (do things bounce off or pass through this object).
639 MakeSolid(IsSolid); 825 MakeSolid(IsSolid);
640 826
641 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, PhysBody.ptr); 827 AddObjectToPhysicalWorld();
642 828
643 // Rebuild its shape 829 // Rebuild its shape
644 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr); 830 PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, PhysBody);
645 831
646 // Collision filter can be set only when the object is in the world 832 DetailLog("{0},BSPrim.UpdatePhysicalParameters,taintExit,static={1},solid={2},mass={3},collide={4},cf={5:X},cType={6},body={7},shape={8}",
647 if (PhysBody.collisionFilter != 0 || PhysBody.collisionMask != 0) 833 LocalID, IsStatic, IsSolid, Mass, SubscribedEvents(), CurrentCollisionFlags, PhysBody.collisionType, PhysBody, PhysShape);
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
658 DetailLog("{0},BSPrim.UpdatePhysicalParameters,taintExit,static={1},solid={2},mass={3},collide={4},cf={5:X},body={6},shape={7}",
659 LocalID, IsStatic, IsSolid, _mass, SubscribedEvents(), CurrentCollisionFlags, PhysBody, PhysShape);
660 } 834 }
661 835
662 // "Making dynamic" means changing to and from static. 836 // "Making dynamic" means changing to and from static.
@@ -664,79 +838,76 @@ public sealed class BSPrim : BSPhysObject
664 // When dynamic, the object can fall and be pushed by others. 838 // When dynamic, the object can fall and be pushed by others.
665 // This is independent of its 'solidness' which controls what passes through 839 // This is independent of its 'solidness' which controls what passes through
666 // this object and what interacts with it. 840 // this object and what interacts with it.
667 private void MakeDynamic(bool makeStatic) 841 protected virtual void MakeDynamic(bool makeStatic)
668 { 842 {
669 if (makeStatic) 843 if (makeStatic)
670 { 844 {
671 // Become a Bullet 'static' object type 845 // Become a Bullet 'static' object type
672 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT); 846 CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_STATIC_OBJECT);
673 // Stop all movement 847 // Stop all movement
674 ZeroMotion(true); 848 ZeroMotion(true);
675 // Center of mass is at the center of the object 849
676 // DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.PhysBody.ptr, _position, _orientation); 850 // Set various physical properties so other object interact properly
851 MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, false);
852 PhysicsScene.PE.SetFriction(PhysBody, Friction);
853 PhysicsScene.PE.SetRestitution(PhysBody, Restitution);
854
677 // Mass is zero which disables a bunch of physics stuff in Bullet 855 // Mass is zero which disables a bunch of physics stuff in Bullet
678 UpdatePhysicalMassProperties(0f); 856 UpdatePhysicalMassProperties(0f, false);
679 // Set collision detection parameters 857 // Set collision detection parameters
680 if (PhysicsScene.Params.ccdMotionThreshold > 0f) 858 if (BSParam.CcdMotionThreshold > 0f)
681 { 859 {
682 BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, PhysicsScene.Params.ccdMotionThreshold); 860 PhysicsScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold);
683 BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius); 861 PhysicsScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius);
684 } 862 }
685 // There can be special things needed for implementing linksets 863
686 Linkset.MakeStatic(this);
687 // The activation state is 'disabled' so Bullet will not try to act on it. 864 // The activation state is 'disabled' so Bullet will not try to act on it.
688 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_SIMULATION); 865 // PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_SIMULATION);
689 // Start it out sleeping and physical actions could wake it up. 866 // Start it out sleeping and physical actions could wake it up.
690 // BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ISLAND_SLEEPING); 867 PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.ISLAND_SLEEPING);
691 868
692 PhysBody.collisionFilter = CollisionFilterGroups.StaticObjectFilter; 869 // This collides like a static object
693 PhysBody.collisionMask = CollisionFilterGroups.StaticObjectMask; 870 PhysBody.collisionType = CollisionType.Static;
694 } 871 }
695 else 872 else
696 { 873 {
697 // Not a Bullet static object 874 // Not a Bullet static object
698 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT); 875 CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.CF_STATIC_OBJECT);
699 876
700 // Set various physical properties so internal dynamic properties will get computed correctly as they are set 877 // Set various physical properties so other object interact properly
701 BulletSimAPI.SetFriction2(PhysBody.ptr, PhysicsScene.Params.defaultFriction); 878 PhysicsScene.PE.SetFriction(PhysBody, Friction);
702 BulletSimAPI.SetRestitution2(PhysBody.ptr, PhysicsScene.Params.defaultRestitution); 879 PhysicsScene.PE.SetRestitution(PhysBody, Restitution);
880 // DetailLog("{0},BSPrim.MakeDynamic,frict={1},rest={2}", LocalID, Friction, Restitution);
703 881
704 // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382 882 // 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 883 // Since this can be called multiple times, only zero forces when becoming physical
706 // BulletSimAPI.ClearAllForces2(BSBody.ptr); 884 // PhysicsScene.PE.ClearAllForces(BSBody);
707 885
708 // For good measure, make sure the transform is set through to the motion state 886 // For good measure, make sure the transform is set through to the motion state
709 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 887 ForcePosition = _position;
710
711 // Center of mass is at the center of the object
712 // DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.PhysBody.ptr, _position, _orientation);
713 888
714 // A dynamic object has mass 889 // A dynamic object has mass
715 UpdatePhysicalMassProperties(RawMass); 890 UpdatePhysicalMassProperties(RawMass, false);
716 891
717 // Set collision detection parameters 892 // Set collision detection parameters
718 if (PhysicsScene.Params.ccdMotionThreshold > 0f) 893 if (BSParam.CcdMotionThreshold > 0f)
719 { 894 {
720 BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, PhysicsScene.Params.ccdMotionThreshold); 895 PhysicsScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold);
721 BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius); 896 PhysicsScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius);
722 } 897 }
723 898
724 // Various values for simulation limits 899 // Various values for simulation limits
725 BulletSimAPI.SetDamping2(PhysBody.ptr, PhysicsScene.Params.linearDamping, PhysicsScene.Params.angularDamping); 900 PhysicsScene.PE.SetDamping(PhysBody, BSParam.LinearDamping, BSParam.AngularDamping);
726 BulletSimAPI.SetDeactivationTime2(PhysBody.ptr, PhysicsScene.Params.deactivationTime); 901 PhysicsScene.PE.SetDeactivationTime(PhysBody, BSParam.DeactivationTime);
727 BulletSimAPI.SetSleepingThresholds2(PhysBody.ptr, PhysicsScene.Params.linearSleepingThreshold, PhysicsScene.Params.angularSleepingThreshold); 902 PhysicsScene.PE.SetSleepingThresholds(PhysBody, BSParam.LinearSleepingThreshold, BSParam.AngularSleepingThreshold);
728 BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, PhysicsScene.Params.contactProcessingThreshold); 903 PhysicsScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold);
729 904
730 // There might be special things needed for implementing linksets. 905 // This collides like an object.
731 Linkset.MakeDynamic(this); 906 PhysBody.collisionType = CollisionType.Dynamic;
732 907
733 // Force activation of the object so Bullet will act on it. 908 // Force activation of the object so Bullet will act on it.
734 // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects. 909 // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects.
735 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ACTIVE_TAG); 910 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 } 911 }
741 } 912 }
742 913
@@ -746,7 +917,7 @@ public sealed class BSPrim : BSPhysObject
746 // the functions after this one set up the state of a possibly newly created collision body. 917 // the functions after this one set up the state of a possibly newly created collision body.
747 private void MakeSolid(bool makeSolid) 918 private void MakeSolid(bool makeSolid)
748 { 919 {
749 CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(PhysBody.ptr); 920 CollisionObjectTypes bodyType = (CollisionObjectTypes)PhysicsScene.PE.GetBodyType(PhysBody);
750 if (makeSolid) 921 if (makeSolid)
751 { 922 {
752 // Verify the previous code created the correct shape for this type of thing. 923 // Verify the previous code created the correct shape for this type of thing.
@@ -754,7 +925,7 @@ public sealed class BSPrim : BSPhysObject
754 { 925 {
755 m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for solidity. id={1}, type={2}", LogHeader, LocalID, bodyType); 926 m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for solidity. id={1}, type={2}", LogHeader, LocalID, bodyType);
756 } 927 }
757 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); 928 CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE);
758 } 929 }
759 else 930 else
760 { 931 {
@@ -762,9 +933,10 @@ public sealed class BSPrim : BSPhysObject
762 { 933 {
763 m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType); 934 m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType);
764 } 935 }
765 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); 936 CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE);
766 PhysBody.collisionFilter = CollisionFilterGroups.VolumeDetectFilter; 937
767 PhysBody.collisionMask = CollisionFilterGroups.VolumeDetectMask; 938 // Change collision info from a static object to a ghosty collision object
939 PhysBody.collisionType = CollisionType.VolumeDetect;
768 } 940 }
769 } 941 }
770 942
@@ -773,8 +945,8 @@ public sealed class BSPrim : BSPhysObject
773 // Called in taint-time!! 945 // Called in taint-time!!
774 private void ActivateIfPhysical(bool forceIt) 946 private void ActivateIfPhysical(bool forceIt)
775 { 947 {
776 if (IsPhysical) 948 if (IsPhysical && PhysBody.HasPhysicalBody)
777 BulletSimAPI.Activate2(PhysBody.ptr, forceIt); 949 PhysicsScene.PE.Activate(PhysBody, forceIt);
778 } 950 }
779 951
780 // Turn on or off the flag controlling whether collision events are returned to the simulator. 952 // Turn on or off the flag controlling whether collision events are returned to the simulator.
@@ -782,11 +954,27 @@ public sealed class BSPrim : BSPhysObject
782 { 954 {
783 if (wantsCollisionEvents) 955 if (wantsCollisionEvents)
784 { 956 {
785 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); 957 CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
958 }
959 else
960 {
961 CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
962 }
963 }
964
965 // Add me to the physical world.
966 // Object MUST NOT already be in the world.
967 // This routine exists because some assorted properties get mangled by adding to the world.
968 internal void AddObjectToPhysicalWorld()
969 {
970 if (PhysBody.HasPhysicalBody)
971 {
972 PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, PhysBody);
786 } 973 }
787 else 974 else
788 { 975 {
789 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); 976 m_log.ErrorFormat("{0} Attempt to add physical object without body. id={1}", LogHeader, LocalID);
977 DetailLog("{0},BSPrim.AddObjectToPhysicalWorld,addObjectWithoutBody,cType={1}", LocalID, PhysBody.collisionType);
790 } 978 }
791 } 979 }
792 980
@@ -805,18 +993,6 @@ public sealed class BSPrim : BSPhysObject
805 get { return _throttleUpdates; } 993 get { return _throttleUpdates; }
806 set { _throttleUpdates = value; } 994 set { _throttleUpdates = value; }
807 } 995 }
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 { 996 public bool IsPhantom {
821 get { 997 get {
822 // SceneObjectPart removes phantom objects from the physics scene 998 // SceneObjectPart removes phantom objects from the physics scene
@@ -831,32 +1007,23 @@ public sealed class BSPrim : BSPhysObject
831 PhysicsScene.TaintedObject("BSPrim.setFloatOnWater", delegate() 1007 PhysicsScene.TaintedObject("BSPrim.setFloatOnWater", delegate()
832 { 1008 {
833 if (_floatOnWater) 1009 if (_floatOnWater)
834 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); 1010 CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER);
835 else 1011 else
836 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); 1012 CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER);
837 }); 1013 });
838 } 1014 }
839 } 1015 }
840 public override OMV.Vector3 RotationalVelocity { 1016 public override OMV.Vector3 RotationalVelocity {
841 get { 1017 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; 1018 return _rotationalVelocity;
852 } 1019 }
853 set { 1020 set {
854 _rotationalVelocity = value; 1021 _rotationalVelocity = value;
1022 Util.ClampV(_rotationalVelocity, BSParam.MaxAngularVelocity);
855 // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity); 1023 // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity);
856 PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate() 1024 PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate()
857 { 1025 {
858 DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity); 1026 ForceRotationalVelocity = _rotationalVelocity;
859 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
860 }); 1027 });
861 } 1028 }
862 } 1029 }
@@ -866,7 +1033,13 @@ public sealed class BSPrim : BSPhysObject
866 } 1033 }
867 set { 1034 set {
868 _rotationalVelocity = value; 1035 _rotationalVelocity = value;
869 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity); 1036 if (PhysBody.HasPhysicalBody)
1037 {
1038 DetailLog("{0},BSPrim.ForceRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity);
1039 PhysicsScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity);
1040 // PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity);
1041 ActivateIfPhysical(false);
1042 }
870 } 1043 }
871 } 1044 }
872 public override bool Kinematic { 1045 public override bool Kinematic {
@@ -890,9 +1063,10 @@ public sealed class BSPrim : BSPhysObject
890 set { 1063 set {
891 _buoyancy = value; 1064 _buoyancy = value;
892 // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); 1065 // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
893 // Buoyancy is faked by changing the gravity applied to the object 1066 // Force the recalculation of the various inertia,etc variables in the object
894 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy); 1067 DetailLog("{0},BSPrim.ForceBuoyancy,buoy={1},mass={2}", LocalID, _buoyancy, _mass);
895 BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav)); 1068 UpdatePhysicalMassProperties(_mass, true);
1069 ActivateIfPhysical(false);
896 } 1070 }
897 } 1071 }
898 1072
@@ -900,17 +1074,112 @@ public sealed class BSPrim : BSPhysObject
900 public override OMV.Vector3 PIDTarget { 1074 public override OMV.Vector3 PIDTarget {
901 set { _PIDTarget = value; } 1075 set { _PIDTarget = value; }
902 } 1076 }
903 public override bool PIDActive {
904 set { _usePID = value; }
905 }
906 public override float PIDTau { 1077 public override float PIDTau {
907 set { _PIDTau = value; } 1078 set { _PIDTau = value; }
908 } 1079 }
1080 public override bool PIDActive {
1081 set {
1082 if (value)
1083 {
1084 // We're taking over after this.
1085 ZeroMotion(true);
1086
1087 _targetMotor = new BSVMotor("BSPrim.PIDTarget",
1088 _PIDTau, // timeScale
1089 BSMotor.Infinite, // decay time scale
1090 BSMotor.InfiniteVector, // friction timescale
1091 1f // efficiency
1092 );
1093 _targetMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG so motor will output detail log messages.
1094 _targetMotor.SetTarget(_PIDTarget);
1095 _targetMotor.SetCurrent(RawPosition);
1096 /*
1097 _targetMotor = new BSPIDVMotor("BSPrim.PIDTarget");
1098 _targetMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG so motor will output detail log messages.
1099
1100 _targetMotor.SetTarget(_PIDTarget);
1101 _targetMotor.SetCurrent(RawPosition);
1102 _targetMotor.TimeScale = _PIDTau;
1103 _targetMotor.Efficiency = 1f;
1104 */
1105
1106 RegisterPreStepAction("BSPrim.PIDTarget", LocalID, delegate(float timeStep)
1107 {
1108 if (!IsPhysicallyActive)
1109 {
1110 UnRegisterPreStepAction("BSPrim.PIDTarget", LocalID);
1111 return;
1112 }
1113
1114 OMV.Vector3 origPosition = RawPosition; // DEBUG DEBUG (for printout below)
1115
1116 // 'movePosition' is where we'd like the prim to be at this moment.
1117 OMV.Vector3 movePosition = RawPosition + _targetMotor.Step(timeStep);
1118
1119 // If we are very close to our target, turn off the movement motor.
1120 if (_targetMotor.ErrorIsZero())
1121 {
1122 DetailLog("{0},BSPrim.PIDTarget,zeroMovement,movePos={1},pos={2},mass={3}",
1123 LocalID, movePosition, RawPosition, Mass);
1124 ForcePosition = _targetMotor.TargetValue;
1125 _targetMotor.Enabled = false;
1126 }
1127 else
1128 {
1129 ForcePosition = movePosition;
1130 }
1131 DetailLog("{0},BSPrim.PIDTarget,move,fromPos={1},movePos={2}", LocalID, origPosition, movePosition);
1132 });
1133 }
1134 else
1135 {
1136 // Stop any targetting
1137 UnRegisterPreStepAction("BSPrim.PIDTarget", LocalID);
1138 }
1139 }
1140 }
909 1141
910 // Used for llSetHoverHeight and maybe vehicle height 1142 // Used for llSetHoverHeight and maybe vehicle height
911 // Hover Height will override MoveTo target's Z 1143 // Hover Height will override MoveTo target's Z
912 public override bool PIDHoverActive { 1144 public override bool PIDHoverActive {
913 set { _useHoverPID = value; } 1145 set {
1146 if (value)
1147 {
1148 // Turning the target on
1149 _hoverMotor = new BSFMotor("BSPrim.Hover",
1150 _PIDHoverTau, // timeScale
1151 BSMotor.Infinite, // decay time scale
1152 BSMotor.Infinite, // friction timescale
1153 1f // efficiency
1154 );
1155 _hoverMotor.SetTarget(ComputeCurrentPIDHoverHeight());
1156 _hoverMotor.SetCurrent(RawPosition.Z);
1157 _hoverMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG so motor will output detail log messages.
1158
1159 RegisterPreStepAction("BSPrim.Hover", LocalID, delegate(float timeStep)
1160 {
1161 if (!IsPhysicallyActive)
1162 return;
1163
1164 _hoverMotor.SetCurrent(RawPosition.Z);
1165 _hoverMotor.SetTarget(ComputeCurrentPIDHoverHeight());
1166 float targetHeight = _hoverMotor.Step(timeStep);
1167
1168 // 'targetHeight' is where we'd like the Z of the prim to be at this moment.
1169 // Compute the amount of force to push us there.
1170 float moveForce = (targetHeight - RawPosition.Z) * Mass;
1171 // Undo anything the object thinks it's doing at the moment
1172 moveForce = -RawVelocity.Z * Mass;
1173
1174 PhysicsScene.PE.ApplyCentralImpulse(PhysBody, new OMV.Vector3(0f, 0f, moveForce));
1175 DetailLog("{0},BSPrim.Hover,move,targHt={1},moveForce={2},mass={3}", LocalID, targetHeight, moveForce, Mass);
1176 });
1177 }
1178 else
1179 {
1180 UnRegisterPreStepAction("BSPrim.Hover", LocalID);
1181 }
1182 }
914 } 1183 }
915 public override float PIDHoverHeight { 1184 public override float PIDHoverHeight {
916 set { _PIDHoverHeight = value; } 1185 set { _PIDHoverHeight = value; }
@@ -919,63 +1188,109 @@ public sealed class BSPrim : BSPhysObject
919 set { _PIDHoverType = value; } 1188 set { _PIDHoverType = value; }
920 } 1189 }
921 public override float PIDHoverTau { 1190 public override float PIDHoverTau {
922 set { _PIDHoverTao = value; } 1191 set { _PIDHoverTau = value; }
1192 }
1193 // Based on current position, determine what we should be hovering at now.
1194 // Must recompute often. What if we walked offa cliff>
1195 private float ComputeCurrentPIDHoverHeight()
1196 {
1197 float ret = _PIDHoverHeight;
1198 float groundHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition);
1199
1200 switch (_PIDHoverType)
1201 {
1202 case PIDHoverType.Ground:
1203 ret = groundHeight + _PIDHoverHeight;
1204 break;
1205 case PIDHoverType.GroundAndWater:
1206 float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(RawPosition);
1207 if (groundHeight > waterHeight)
1208 {
1209 ret = groundHeight + _PIDHoverHeight;
1210 }
1211 else
1212 {
1213 ret = waterHeight + _PIDHoverHeight;
1214 }
1215 break;
1216 }
1217 return ret;
923 } 1218 }
924 1219
1220
925 // For RotLookAt 1221 // For RotLookAt
926 public override OMV.Quaternion APIDTarget { set { return; } } 1222 public override OMV.Quaternion APIDTarget { set { return; } }
927 public override bool APIDActive { set { return; } } 1223 public override bool APIDActive { set { return; } }
928 public override float APIDStrength { set { return; } } 1224 public override float APIDStrength { set { return; } }
929 public override float APIDDamping { set { return; } } 1225 public override float APIDDamping { set { return; } }
930 1226
931 private List<OMV.Vector3> m_accumulatedForces = new List<OMV.Vector3>();
932 public override void AddForce(OMV.Vector3 force, bool pushforce) { 1227 public override void AddForce(OMV.Vector3 force, bool pushforce) {
933 AddForce(force, pushforce, false); 1228 // Per documentation, max force is limited.
1229 OMV.Vector3 addForce = Util.ClampV(force, BSParam.MaxAddForceMagnitude);
1230
1231 // Since this force is being applied in only one step, make this a force per second.
1232 addForce /= PhysicsScene.LastTimeStep;
1233 AddForce(addForce, pushforce, false /* inTaintTime */);
934 } 1234 }
1235
935 // Applying a force just adds this to the total force on the object. 1236 // Applying a force just adds this to the total force on the object.
1237 // This added force will only last the next simulation tick.
936 public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { 1238 public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) {
937 // for an object, doesn't matter if force is a pushforce or not 1239 // for an object, doesn't matter if force is a pushforce or not
938 if (force.IsFinite()) 1240 if (IsPhysicallyActive)
939 {
940 // _force += force;
941 lock (m_accumulatedForces)
942 m_accumulatedForces.Add(new OMV.Vector3(force));
943 }
944 else
945 {
946 m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID);
947 return;
948 }
949 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddForce", delegate()
950 { 1241 {
951 OMV.Vector3 fSum = OMV.Vector3.Zero; 1242 if (force.IsFinite())
952 lock (m_accumulatedForces)
953 { 1243 {
954 // Sum the accumulated additional forces for one big force to apply once. 1244 // DetailLog("{0},BSPrim.addForce,call,force={1}", LocalID, addForce);
955 foreach (OMV.Vector3 v in m_accumulatedForces) 1245
1246 OMV.Vector3 addForce = force;
1247 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddForce", delegate()
956 { 1248 {
957 fSum += v; 1249 // Bullet adds this central force to the total force for this tick
958 } 1250 DetailLog("{0},BSPrim.addForce,taint,force={1}", LocalID, addForce);
959 m_accumulatedForces.Clear(); 1251 if (PhysBody.HasPhysicalBody)
1252 {
1253 PhysicsScene.PE.ApplyCentralForce(PhysBody, addForce);
1254 ActivateIfPhysical(false);
1255 }
1256 });
960 } 1257 }
961 DetailLog("{0},BSPrim.AddForce,taint,force={1}", LocalID, fSum); 1258 else
962 if (fSum != OMV.Vector3.Zero) 1259 {
963 BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, fSum); 1260 m_log.WarnFormat("{0}: AddForce: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID);
964 }); 1261 return;
1262 }
1263 }
965 } 1264 }
966 1265
967 // An impulse force is scaled by the mass of the object. 1266 public void AddForceImpulse(OMV.Vector3 impulse, bool pushforce, bool inTaintTime) {
968 public void ApplyForceImpulse(OMV.Vector3 impulse, bool inTaintTime) 1267 // for an object, doesn't matter if force is a pushforce or not
969 { 1268 if (!IsPhysicallyActive)
970 OMV.Vector3 applyImpulse = impulse;
971 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyForceImpulse", delegate()
972 { 1269 {
973 DetailLog("{0},BSPrim.ApplyForceImpulse,taint,tImpulse={1}", LocalID, applyImpulse); 1270 if (impulse.IsFinite())
974 BulletSimAPI.ApplyCentralImpulse2(PhysBody.ptr, applyImpulse); 1271 {
975 }); 1272 OMV.Vector3 addImpulse = Util.ClampV(impulse, BSParam.MaxAddForceMagnitude);
1273 // DetailLog("{0},BSPrim.addForceImpulse,call,impulse={1}", LocalID, impulse);
1274
1275 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddImpulse", delegate()
1276 {
1277 // Bullet adds this impulse immediately to the velocity
1278 DetailLog("{0},BSPrim.addForceImpulse,taint,impulseforce={1}", LocalID, addImpulse);
1279 if (PhysBody.HasPhysicalBody)
1280 {
1281 PhysicsScene.PE.ApplyCentralImpulse(PhysBody, addImpulse);
1282 ActivateIfPhysical(false);
1283 }
1284 });
1285 }
1286 else
1287 {
1288 m_log.WarnFormat("{0}: AddForceImpulse: Got a NaN impulse applied to a prim. LocalID={1}", LogHeader, LocalID);
1289 return;
1290 }
1291 }
976 } 1292 }
977 1293
978 private List<OMV.Vector3> m_accumulatedAngularForces = new List<OMV.Vector3>();
979 public override void AddAngularForce(OMV.Vector3 force, bool pushforce) { 1294 public override void AddAngularForce(OMV.Vector3 force, bool pushforce) {
980 AddAngularForce(force, pushforce, false); 1295 AddAngularForce(force, pushforce, false);
981 } 1296 }
@@ -983,42 +1298,37 @@ public sealed class BSPrim : BSPhysObject
983 { 1298 {
984 if (force.IsFinite()) 1299 if (force.IsFinite())
985 { 1300 {
986 // _force += force; 1301 OMV.Vector3 angForce = force;
987 lock (m_accumulatedAngularForces) 1302 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddAngularForce", delegate()
988 m_accumulatedAngularForces.Add(new OMV.Vector3(force)); 1303 {
1304 if (PhysBody.HasPhysicalBody)
1305 {
1306 PhysicsScene.PE.ApplyTorque(PhysBody, angForce);
1307 ActivateIfPhysical(false);
1308 }
1309 });
989 } 1310 }
990 else 1311 else
991 { 1312 {
992 m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID); 1313 m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID);
993 return; 1314 return;
994 } 1315 }
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 } 1316 }
1317
1015 // A torque impulse. 1318 // A torque impulse.
1319 // ApplyTorqueImpulse adds torque directly to the angularVelocity.
1320 // AddAngularForce accumulates the force and applied it to the angular velocity all at once.
1321 // Computed as: angularVelocity += impulse * inertia;
1016 public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime) 1322 public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime)
1017 { 1323 {
1018 OMV.Vector3 applyImpulse = impulse; 1324 OMV.Vector3 applyImpulse = impulse;
1019 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate() 1325 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate()
1020 { 1326 {
1021 BulletSimAPI.ApplyTorqueImpulse2(PhysBody.ptr, applyImpulse); 1327 if (PhysBody.HasPhysicalBody)
1328 {
1329 PhysicsScene.PE.ApplyTorqueImpulse(PhysBody, applyImpulse);
1330 ActivateIfPhysical(false);
1331 }
1022 }); 1332 });
1023 } 1333 }
1024 1334
@@ -1301,23 +1611,10 @@ public sealed class BSPrim : BSPhysObject
1301 profileEnd = 1.0f - (float)BaseShape.ProfileEnd * 2.0e-5f; 1611 profileEnd = 1.0f - (float)BaseShape.ProfileEnd * 2.0e-5f;
1302 volume *= (profileEnd - profileBegin); 1612 volume *= (profileEnd - profileBegin);
1303 1613
1304 returnMass = _density * volume; 1614 returnMass = Density * BSParam.DensityScaleFactor * volume;
1305 1615 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 1616
1319 if (returnMass > PhysicsScene.MaximumObjectMass) 1617 returnMass = Util.Clamp(returnMass, BSParam.MinimumObjectMass, BSParam.MaximumObjectMass);
1320 returnMass = PhysicsScene.MaximumObjectMass;
1321 1618
1322 return returnMass; 1619 return returnMass;
1323 }// end CalculateMass 1620 }// end CalculateMass
@@ -1326,135 +1623,74 @@ public sealed class BSPrim : BSPhysObject
1326 // Rebuild the geometry and object. 1623 // Rebuild the geometry and object.
1327 // This is called when the shape changes so we need to recreate the mesh/hull. 1624 // This is called when the shape changes so we need to recreate the mesh/hull.
1328 // Called at taint-time!!! 1625 // Called at taint-time!!!
1329 private void CreateGeomAndObject(bool forceRebuild) 1626 public void CreateGeomAndObject(bool forceRebuild)
1330 { 1627 {
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. 1628 // Create the correct physical representation for this type of object.
1337 // Updates PhysBody and PhysShape with the new information. 1629 // Updates base.PhysBody and base.PhysShape with the new information.
1338 // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary. 1630 // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary.
1339 // Returns 'true' if either the body or the shape was changed. 1631 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 { 1632 {
1342 // Called if the current prim body is about to be destroyed. 1633 // Called if the current prim body is about to be destroyed.
1343 // Remove all the physical dependencies on the old body. 1634 // Remove all the physical dependencies on the old body.
1344 // (Maybe someday make the changing of BSShape an event handled by BSLinkset.) 1635 // (Maybe someday make the changing of BSShape an event to be subscribed to by BSLinkset, ...)
1345 needToRestoreLinkset = Linkset.RemoveBodyDependencies(this); 1636 RemoveBodyDependencies();
1346 needToRestoreVehicle = _vehicle.RemoveBodyDependencies(this);
1347 }); 1637 });
1348 1638
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 1639 // Make sure the properties are set on the new object
1361 UpdatePhysicalParameters(); 1640 UpdatePhysicalParameters();
1362 return; 1641 return;
1363 } 1642 }
1364 1643
1365 // The physics engine says that properties have updated. Update same and inform 1644 protected virtual void RemoveBodyDependencies()
1366 // the world that things have changed. 1645 {
1367 // TODO: do we really need to check for changed? Maybe just copy values and call RequestPhysicsterseUpdate() 1646 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 } 1647 }
1375 1648
1376 const float ROTATION_TOLERANCE = 0.01f; 1649 // The physics engine says that properties have updated. Update same and inform
1377 const float VELOCITY_TOLERANCE = 0.001f; 1650 // 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) 1651 public override void UpdateProperties(EntityProperties entprop)
1383 { 1652 {
1384 /* 1653 TriggerPreUpdatePropertyAction(ref entprop);
1385 UpdatedProperties changed = 0; 1654
1386 // assign to the local variables so the normal set action does not happen 1655 // A temporary kludge to suppress the rotational effects introduced on vehicles by Bullet
1387 // if (_position != entprop.Position) 1656 // TODO: handle physics introduced by Bullet with computed vehicle physics.
1388 if (!_position.ApproxEquals(entprop.Position, POSITION_TOLERANCE)) 1657 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 { 1658 {
1419 // Only update the position of single objects and linkset roots 1659 entprop.RotationalVelocity = OMV.Vector3.Zero;
1420 if (Linkset.IsRoot(this))
1421 {
1422 base.RequestPhysicsterseUpdate();
1423 }
1424 } 1660 }
1425 */
1426 1661
1427 // Don't check for damping here -- it's done in BulletSim and SceneObjectPart. 1662 // DetailLog("{0},BSPrim.UpdateProperties,entry,entprop={1}", LocalID, entprop); // DEBUG DEBUG
1428 1663
1429 // Updates only for individual prims and for the root object of a linkset. 1664 // Assign directly to the local variables so the normal set actions do not happen
1430 if (Linkset.IsRoot(this)) 1665 _position = entprop.Position;
1431 { 1666 _orientation = entprop.Rotation;
1432 // Assign directly to the local variables so the normal set action does not happen 1667 // _velocity = entprop.Velocity;
1433 _position = entprop.Position; 1668 // DEBUG DEBUG DEBUG -- smooth velocity changes a bit. The simulator seems to be
1434 _orientation = entprop.Rotation; 1669 // very sensitive to velocity changes.
1670 if (!entprop.Velocity.ApproxEquals(_velocity, 0.1f))
1435 _velocity = entprop.Velocity; 1671 _velocity = entprop.Velocity;
1436 _acceleration = entprop.Acceleration; 1672 _acceleration = entprop.Acceleration;
1437 _rotationalVelocity = entprop.RotationalVelocity; 1673 _rotationalVelocity = entprop.RotationalVelocity;
1438 1674
1439 // The sanity check can change the velocity and/or position. 1675 // 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 1676
1446 // remember the current and last set values 1677 // The sanity check can change the velocity and/or position.
1447 LastEntityProperties = CurrentEntityProperties; 1678 if (PositionSanityCheck(true /* inTaintTime */ ))
1448 CurrentEntityProperties = entprop; 1679 {
1680 entprop.Position = _position;
1681 entprop.Velocity = _velocity;
1682 entprop.RotationalVelocity = _rotationalVelocity;
1683 entprop.Acceleration = _acceleration;
1684 }
1449 1685
1450 OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation; 1686 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}", 1687 DetailLog("{0},BSPrim.UpdateProperties,call,entProp={1},dir={2}", LocalID, entprop, direction);
1452 LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity);
1453 1688
1454 // BulletSimAPI.DumpRigidBody2(PhysicsScene.World.ptr, BSBody.ptr); // DEBUG DEBUG DEBUG 1689 // remember the current and last set values
1690 LastEntityProperties = CurrentEntityProperties;
1691 CurrentEntityProperties = entprop;
1455 1692
1456 base.RequestPhysicsterseUpdate(); 1693 base.RequestPhysicsterseUpdate();
1457 }
1458 /* 1694 /*
1459 else 1695 else
1460 { 1696 {
@@ -1464,9 +1700,6 @@ public sealed class BSPrim : BSPhysObject
1464 entprop.Acceleration, entprop.RotationalVelocity); 1700 entprop.Acceleration, entprop.RotationalVelocity);
1465 } 1701 }
1466 */ 1702 */
1467
1468 // The linkset implimentation might want to know about this.
1469 Linkset.UpdateProperties(this);
1470 } 1703 }
1471} 1704}
1472} 1705}