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.cs1255
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]
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 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}