aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Physics')
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs175
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs4
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSConstraint6Dof.cs4
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs1018
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs28
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs283
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs17
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs200
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSMotors.cs431
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs37
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs285
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSScene.cs120
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs114
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs17
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs156
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs45
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs203
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs278
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt218
-rw-r--r--OpenSim/Region/Physics/ChOdePlugin/ODEPrim.cs4
-rw-r--r--OpenSim/Region/Physics/Manager/IMesher.cs2
-rw-r--r--OpenSim/Region/Physics/Manager/PhysicsActor.cs13
-rw-r--r--OpenSim/Region/Physics/Manager/ZeroMesher.cs9
-rw-r--r--OpenSim/Region/Physics/Meshing/Meshmerizer.cs26
-rw-r--r--OpenSim/Region/Physics/OdePlugin/ODEPrim.cs5
-rw-r--r--OpenSim/Region/Physics/OdePlugin/Tests/ODETestClass.cs3
-rw-r--r--OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs6
-rw-r--r--OpenSim/Region/Physics/UbitOdePlugin/ODEMeshWorker.cs6
28 files changed, 2583 insertions, 1124 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
index 4c195e1..57c5898 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
@@ -69,6 +69,8 @@ public sealed class BSCharacter : BSPhysObject
69 private OMV.Vector3 _appliedVelocity; // the last velocity applied to the avatar 69 private OMV.Vector3 _appliedVelocity; // the last velocity applied to the avatar
70 private float _currentFriction; // the friction currently being used (changed by setVelocity). 70 private float _currentFriction; // the friction currently being used (changed by setVelocity).
71 71
72 private BSVMotor _velocityMotor;
73
72 private OMV.Vector3 _PIDTarget; 74 private OMV.Vector3 _PIDTarget;
73 private bool _usePID; 75 private bool _usePID;
74 private float _PIDTau; 76 private float _PIDTau;
@@ -89,6 +91,18 @@ public sealed class BSCharacter : BSPhysObject
89 if (_size.X == 0f) _size.X = PhysicsScene.Params.avatarCapsuleDepth; 91 if (_size.X == 0f) _size.X = PhysicsScene.Params.avatarCapsuleDepth;
90 if (_size.Y == 0f) _size.Y = PhysicsScene.Params.avatarCapsuleWidth; 92 if (_size.Y == 0f) _size.Y = PhysicsScene.Params.avatarCapsuleWidth;
91 93
94 // A motor to control the acceleration and deceleration of the avatar movement.
95 // _velocityMotor = new BSVMotor("BSCharacter.Velocity", 3f, 5f, BSMotor.InfiniteVector, 1f);
96 // _velocityMotor = new BSPIDVMotor("BSCharacter.Velocity", 3f, 5f, BSMotor.InfiniteVector, 1f);
97 // Infinite decay and timescale values so motor only changes current to target values.
98 _velocityMotor = new BSVMotor("BSCharacter.Velocity",
99 0.2f, // time scale
100 BSMotor.Infinite, // decay time scale
101 BSMotor.InfiniteVector, // friction timescale
102 1f // efficiency
103 );
104 _velocityMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG so motor will output detail log messages.
105
92 _flying = isFlying; 106 _flying = isFlying;
93 _orientation = OMV.Quaternion.Identity; 107 _orientation = OMV.Quaternion.Identity;
94 _velocity = OMV.Vector3.Zero; 108 _velocity = OMV.Vector3.Zero;
@@ -105,12 +119,12 @@ public sealed class BSCharacter : BSPhysObject
105 DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}", 119 DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}",
106 LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass); 120 LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass);
107 121
108 // do actual create at taint time 122 // do actual creation in taint time
109 PhysicsScene.TaintedObject("BSCharacter.create", delegate() 123 PhysicsScene.TaintedObject("BSCharacter.create", delegate()
110 { 124 {
111 DetailLog("{0},BSCharacter.create,taint", LocalID); 125 DetailLog("{0},BSCharacter.create,taint", LocalID);
112 // New body and shape into PhysBody and PhysShape 126 // New body and shape into PhysBody and PhysShape
113 PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this, null, null); 127 PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this);
114 128
115 SetPhysicalProperties(); 129 SetPhysicalProperties();
116 }); 130 });
@@ -124,7 +138,9 @@ public sealed class BSCharacter : BSPhysObject
124 PhysicsScene.TaintedObject("BSCharacter.destroy", delegate() 138 PhysicsScene.TaintedObject("BSCharacter.destroy", delegate()
125 { 139 {
126 PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null); 140 PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null);
141 PhysBody.Clear();
127 PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null); 142 PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null);
143 PhysShape.Clear();
128 }); 144 });
129 } 145 }
130 146
@@ -136,6 +152,10 @@ public sealed class BSCharacter : BSPhysObject
136 ForcePosition = _position; 152 ForcePosition = _position;
137 // Set the velocity and compute the proper friction 153 // Set the velocity and compute the proper friction
138 ForceVelocity = _velocity; 154 ForceVelocity = _velocity;
155 // Setting the current and target in the motor will cause it to start computing any deceleration.
156 _velocityMotor.Reset();
157 _velocityMotor.SetCurrent(_velocity);
158 _velocityMotor.SetTarget(_velocity);
139 159
140 // This will enable or disable the flying buoyancy of the avatar. 160 // This will enable or disable the flying buoyancy of the avatar.
141 // Needs to be reset especially when an avatar is recreated after crossing a region boundry. 161 // Needs to be reset especially when an avatar is recreated after crossing a region boundry.
@@ -165,9 +185,8 @@ public sealed class BSCharacter : BSPhysObject
165 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr); 185 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr);
166 186
167 // Do this after the object has been added to the world 187 // Do this after the object has been added to the world
168 BulletSimAPI.SetCollisionFilterMask2(PhysBody.ptr, 188 PhysBody.collisionType = CollisionType.Avatar;
169 (uint)CollisionFilterGroups.AvatarFilter, 189 PhysBody.ApplyCollisionMask();
170 (uint)CollisionFilterGroups.AvatarMask);
171 } 190 }
172 191
173 public override void RequestPhysicsterseUpdate() 192 public override void RequestPhysicsterseUpdate()
@@ -187,6 +206,11 @@ public sealed class BSCharacter : BSPhysObject
187 set { 206 set {
188 // When an avatar's size is set, only the height is changed. 207 // When an avatar's size is set, only the height is changed.
189 _size = value; 208 _size = value;
209 // Old versions of ScenePresence passed only the height. If width and/or depth are zero,
210 // replace with the default values.
211 if (_size.X == 0f) _size.X = PhysicsScene.Params.avatarCapsuleDepth;
212 if (_size.Y == 0f) _size.Y = PhysicsScene.Params.avatarCapsuleWidth;
213
190 ComputeAvatarScale(_size); 214 ComputeAvatarScale(_size);
191 ComputeAvatarVolumeAndMass(); 215 ComputeAvatarVolumeAndMass();
192 DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}", 216 DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}",
@@ -194,15 +218,18 @@ public sealed class BSCharacter : BSPhysObject
194 218
195 PhysicsScene.TaintedObject("BSCharacter.setSize", delegate() 219 PhysicsScene.TaintedObject("BSCharacter.setSize", delegate()
196 { 220 {
197 BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale); 221 if (PhysBody.HasPhysicalBody && PhysShape.HasPhysicalShape)
198 UpdatePhysicalMassProperties(RawMass); 222 {
223 BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale);
224 UpdatePhysicalMassProperties(RawMass);
225 // Make sure this change appears as a property update event
226 BulletSimAPI.PushUpdate2(PhysBody.ptr);
227 }
199 }); 228 });
200 229
201 } 230 }
202 } 231 }
203 232
204 public override OMV.Vector3 Scale { get; set; }
205
206 public override PrimitiveBaseShape Shape 233 public override PrimitiveBaseShape Shape
207 { 234 {
208 set { BaseShape = value; } 235 set { BaseShape = value; }
@@ -230,13 +257,15 @@ public sealed class BSCharacter : BSPhysObject
230 public override void ZeroMotion(bool inTaintTime) 257 public override void ZeroMotion(bool inTaintTime)
231 { 258 {
232 _velocity = OMV.Vector3.Zero; 259 _velocity = OMV.Vector3.Zero;
260 _velocityMotor.Zero();
233 _acceleration = OMV.Vector3.Zero; 261 _acceleration = OMV.Vector3.Zero;
234 _rotationalVelocity = OMV.Vector3.Zero; 262 _rotationalVelocity = OMV.Vector3.Zero;
235 263
236 // Zero some other properties directly into the physics engine 264 // Zero some other properties directly into the physics engine
237 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() 265 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
238 { 266 {
239 BulletSimAPI.ClearAllForces2(PhysBody.ptr); 267 if (PhysBody.HasPhysicalBody)
268 BulletSimAPI.ClearAllForces2(PhysBody.ptr);
240 }); 269 });
241 } 270 }
242 public override void ZeroAngularMotion(bool inTaintTime) 271 public override void ZeroAngularMotion(bool inTaintTime)
@@ -245,10 +274,13 @@ public sealed class BSCharacter : BSPhysObject
245 274
246 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() 275 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
247 { 276 {
248 BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); 277 if (PhysBody.HasPhysicalBody)
249 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); 278 {
250 // The next also get rid of applied linear force but the linear velocity is untouched. 279 BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
251 BulletSimAPI.ClearForces2(PhysBody.ptr); 280 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
281 // The next also get rid of applied linear force but the linear velocity is untouched.
282 BulletSimAPI.ClearForces2(PhysBody.ptr);
283 }
252 }); 284 });
253 } 285 }
254 286
@@ -273,7 +305,8 @@ public sealed class BSCharacter : BSPhysObject
273 PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate() 305 PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate()
274 { 306 {
275 DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); 307 DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
276 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 308 if (PhysBody.HasPhysicalBody)
309 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
277 }); 310 });
278 } 311 }
279 } 312 }
@@ -297,6 +330,15 @@ public sealed class BSCharacter : BSPhysObject
297 { 330 {
298 bool ret = false; 331 bool ret = false;
299 332
333 // TODO: check for out of bounds
334 if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(_position))
335 {
336 // The character is out of the known/simulated area.
337 // Upper levels of code will handle the transition to other areas so, for
338 // the time, we just ignore the position.
339 return ret;
340 }
341
300 // If below the ground, move the avatar up 342 // If below the ground, move the avatar up
301 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); 343 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
302 if (Position.Z < terrainHeight) 344 if (Position.Z < terrainHeight)
@@ -307,7 +349,7 @@ public sealed class BSCharacter : BSPhysObject
307 } 349 }
308 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) 350 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
309 { 351 {
310 float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position); 352 float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position);
311 if (Position.Z < waterHeight) 353 if (Position.Z < waterHeight)
312 { 354 {
313 _position.Z = waterHeight; 355 _position.Z = waterHeight;
@@ -315,7 +357,6 @@ public sealed class BSCharacter : BSPhysObject
315 } 357 }
316 } 358 }
317 359
318 // TODO: check for out of bounds
319 return ret; 360 return ret;
320 } 361 }
321 362
@@ -332,7 +373,8 @@ public sealed class BSCharacter : BSPhysObject
332 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate() 373 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate()
333 { 374 {
334 DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); 375 DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
335 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 376 if (PhysBody.HasPhysicalBody)
377 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
336 }); 378 });
337 ret = true; 379 ret = true;
338 } 380 }
@@ -359,7 +401,8 @@ public sealed class BSCharacter : BSPhysObject
359 PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate() 401 PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate()
360 { 402 {
361 DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force); 403 DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force);
362 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force); 404 if (PhysBody.HasPhysicalBody)
405 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
363 }); 406 });
364 } 407 }
365 } 408 }
@@ -376,10 +419,38 @@ public sealed class BSCharacter : BSPhysObject
376 419
377 public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } } 420 public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } }
378 public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } } 421 public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } }
422
423 // Sets the target in the motor. This starts the changing of the avatar's velocity.
424 public override OMV.Vector3 TargetVelocity
425 {
426 get
427 {
428 return _velocityMotor.TargetValue;
429 }
430 set
431 {
432 DetailLog("{0},BSCharacter.setTargetVelocity,call,vel={1}", LocalID, value);
433 OMV.Vector3 targetVel = value;
434 PhysicsScene.TaintedObject("BSCharacter.setTargetVelocity", delegate()
435 {
436 float timeStep = 0.089f; // DEBUG DEBUG FIX FIX FIX
437 _velocityMotor.Reset();
438 _velocityMotor.SetTarget(targetVel);
439 _velocityMotor.SetCurrent(_velocity);
440 // Compute a velocity value and make sure it gets pushed into the avatar.
441 // This makes sure the avatar will start from a stop.
442 ForceVelocity = _velocityMotor.Step(timeStep);
443 });
444 }
445 }
446 // Directly setting velocity means this is what the user really wants now.
379 public override OMV.Vector3 Velocity { 447 public override OMV.Vector3 Velocity {
380 get { return _velocity; } 448 get { return _velocity; }
381 set { 449 set {
382 _velocity = value; 450 _velocity = value;
451 _velocityMotor.Reset();
452 _velocityMotor.SetCurrent(_velocity);
453 _velocityMotor.SetTarget(_velocity);
383 // m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, _velocity); 454 // m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, _velocity);
384 PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate() 455 PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate()
385 { 456 {
@@ -391,6 +462,8 @@ public sealed class BSCharacter : BSPhysObject
391 public override OMV.Vector3 ForceVelocity { 462 public override OMV.Vector3 ForceVelocity {
392 get { return _velocity; } 463 get { return _velocity; }
393 set { 464 set {
465 PhysicsScene.AssertInTaintTime("BSCharacter.ForceVelocity");
466
394 // Depending on whether the avatar is moving or not, change the friction 467 // Depending on whether the avatar is moving or not, change the friction
395 // to keep the avatar from slipping around 468 // to keep the avatar from slipping around
396 if (_velocity.Length() == 0) 469 if (_velocity.Length() == 0)
@@ -398,7 +471,8 @@ public sealed class BSCharacter : BSPhysObject
398 if (_currentFriction != PhysicsScene.Params.avatarStandingFriction) 471 if (_currentFriction != PhysicsScene.Params.avatarStandingFriction)
399 { 472 {
400 _currentFriction = PhysicsScene.Params.avatarStandingFriction; 473 _currentFriction = PhysicsScene.Params.avatarStandingFriction;
401 BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction); 474 if (PhysBody.HasPhysicalBody)
475 BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction);
402 } 476 }
403 } 477 }
404 else 478 else
@@ -406,7 +480,8 @@ public sealed class BSCharacter : BSPhysObject
406 if (_currentFriction != PhysicsScene.Params.avatarFriction) 480 if (_currentFriction != PhysicsScene.Params.avatarFriction)
407 { 481 {
408 _currentFriction = PhysicsScene.Params.avatarFriction; 482 _currentFriction = PhysicsScene.Params.avatarFriction;
409 BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction); 483 if (PhysBody.HasPhysicalBody)
484 BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction);
410 } 485 }
411 } 486 }
412 _velocity = value; 487 _velocity = value;
@@ -443,8 +518,11 @@ public sealed class BSCharacter : BSPhysObject
443 // m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation); 518 // m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation);
444 PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate() 519 PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate()
445 { 520 {
446 // _position = BulletSimAPI.GetPosition2(BSBody.ptr); 521 if (PhysBody.HasPhysicalBody)
447 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 522 {
523 // _position = BulletSimAPI.GetPosition2(BSBody.ptr);
524 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
525 }
448 }); 526 });
449 } 527 }
450 } 528 }
@@ -482,6 +560,13 @@ public sealed class BSCharacter : BSPhysObject
482 get { return _flying; } 560 get { return _flying; }
483 set { 561 set {
484 _flying = value; 562 _flying = value;
563
564 // Velocity movement is different when flying: flying velocity degrades over time.
565 if (_flying)
566 _velocityMotor.TargetValueDecayTimeScale = 1f;
567 else
568 _velocityMotor.TargetValueDecayTimeScale = BSMotor.Infinite;
569
485 // simulate flying by changing the effect of gravity 570 // simulate flying by changing the effect of gravity
486 Buoyancy = ComputeBuoyancyFromFlying(_flying); 571 Buoyancy = ComputeBuoyancyFromFlying(_flying);
487 } 572 }
@@ -517,10 +602,13 @@ public sealed class BSCharacter : BSPhysObject
517 _floatOnWater = value; 602 _floatOnWater = value;
518 PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate() 603 PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate()
519 { 604 {
520 if (_floatOnWater) 605 if (PhysBody.HasPhysicalBody)
521 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); 606 {
522 else 607 if (_floatOnWater)
523 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); 608 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
609 else
610 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
611 }
524 }); 612 });
525 } 613 }
526 } 614 }
@@ -549,11 +637,15 @@ public sealed class BSCharacter : BSPhysObject
549 } 637 }
550 public override float ForceBuoyancy { 638 public override float ForceBuoyancy {
551 get { return _buoyancy; } 639 get { return _buoyancy; }
552 set { _buoyancy = value; 640 set {
641 PhysicsScene.AssertInTaintTime("BSCharacter.ForceBuoyancy");
642
643 _buoyancy = value;
553 DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); 644 DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
554 // Buoyancy is faked by changing the gravity applied to the object 645 // Buoyancy is faked by changing the gravity applied to the object
555 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy); 646 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
556 BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav)); 647 if (PhysBody.HasPhysicalBody)
648 BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav));
557 } 649 }
558 } 650 }
559 651
@@ -599,7 +691,8 @@ public sealed class BSCharacter : BSPhysObject
599 PhysicsScene.TaintedObject("BSCharacter.AddForce", delegate() 691 PhysicsScene.TaintedObject("BSCharacter.AddForce", delegate()
600 { 692 {
601 DetailLog("{0},BSCharacter.setAddForce,taint,addedForce={1}", LocalID, _force); 693 DetailLog("{0},BSCharacter.setAddForce,taint,addedForce={1}", LocalID, _force);
602 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force); 694 if (PhysBody.HasPhysicalBody)
695 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
603 }); 696 });
604 } 697 }
605 else 698 else
@@ -616,9 +709,6 @@ public sealed class BSCharacter : BSPhysObject
616 709
617 private void ComputeAvatarScale(OMV.Vector3 size) 710 private void ComputeAvatarScale(OMV.Vector3 size)
618 { 711 {
619 // The 'size' given by the simulator is the mid-point of the avatar
620 // and X and Y are unspecified.
621
622 OMV.Vector3 newScale = size; 712 OMV.Vector3 newScale = size;
623 // newScale.X = PhysicsScene.Params.avatarCapsuleWidth; 713 // newScale.X = PhysicsScene.Params.avatarCapsuleWidth;
624 // newScale.Y = PhysicsScene.Params.avatarCapsuleDepth; 714 // newScale.Y = PhysicsScene.Params.avatarCapsuleDepth;
@@ -667,6 +757,22 @@ public sealed class BSCharacter : BSPhysObject
667 LastEntityProperties = CurrentEntityProperties; 757 LastEntityProperties = CurrentEntityProperties;
668 CurrentEntityProperties = entprop; 758 CurrentEntityProperties = entprop;
669 759
760 // Avatars don't respond to world friction, etc. They only go the speed I tell them too.
761 // Special kludge here for falling. Even though the target velocity might not have a
762 // Z component, the avatar could be falling (walked off a ledge, stopped flying, ...)
763 // and that velocity component must be retained.
764 float timeStep = 0.089f; // DEBUG DEBUG FIX FIX FIX
765 OMV.Vector3 stepVelocity = _velocityMotor.Step(timeStep);
766 // Remove next line so avatars don't fly up forever. DEBUG DEBUG this is only temporary.
767 // stepVelocity.Z += entprop.Velocity.Z;
768 _velocity = stepVelocity;
769 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
770 /*
771 OMV.Vector3 stepVelocity = _velocityMotor.Step(timeStep);
772 OMV.Vector3 avVel = new OMV.Vector3(stepVelocity.X, stepVelocity.Y, entprop.Velocity.Z);
773 _velocity = avVel;
774 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, avVel);
775
670 if (entprop.Velocity != LastEntityProperties.Velocity) 776 if (entprop.Velocity != LastEntityProperties.Velocity)
671 { 777 {
672 // Changes in the velocity are suppressed in avatars. 778 // Changes in the velocity are suppressed in avatars.
@@ -675,9 +781,10 @@ public sealed class BSCharacter : BSPhysObject
675 _velocity = avVel; 781 _velocity = avVel;
676 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, avVel); 782 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, avVel);
677 } 783 }
784 */
678 785
679 // Tell the linkset about value changes 786 // Tell the linkset about value changes
680 Linkset.UpdateProperties(this); 787 Linkset.UpdateProperties(this, true);
681 788
682 // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop. 789 // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop.
683 // base.RequestPhysicsterseUpdate(); 790 // base.RequestPhysicsterseUpdate();
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs
index 65fac00..6b1e304 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs
@@ -57,7 +57,7 @@ public abstract class BSConstraint : IDisposable
57 if (m_enabled) 57 if (m_enabled)
58 { 58 {
59 m_enabled = false; 59 m_enabled = false;
60 if (m_constraint.ptr != IntPtr.Zero) 60 if (m_constraint.HasPhysicalConstraint)
61 { 61 {
62 bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr); 62 bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr);
63 m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,id1={1},body1={2},id2={3},body2={4},success={5}", 63 m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,id1={1},body1={2},id2={3},body2={4},success={5}",
@@ -65,7 +65,7 @@ public abstract class BSConstraint : IDisposable
65 m_body1.ID, m_body1.ptr.ToString("X"), 65 m_body1.ID, m_body1.ptr.ToString("X"),
66 m_body2.ID, m_body2.ptr.ToString("X"), 66 m_body2.ID, m_body2.ptr.ToString("X"),
67 success); 67 success);
68 m_constraint.ptr = System.IntPtr.Zero; 68 m_constraint.Clear();
69 } 69 }
70 } 70 }
71 } 71 }
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint6Dof.cs b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint6Dof.cs
index 23ef052..b073555 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint6Dof.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint6Dof.cs
@@ -65,7 +65,7 @@ public sealed class BSConstraint6Dof : BSConstraint
65 m_world = world; 65 m_world = world;
66 m_body1 = obj1; 66 m_body1 = obj1;
67 m_body2 = obj2; 67 m_body2 = obj2;
68 if (obj1.ptr == IntPtr.Zero || obj2.ptr == IntPtr.Zero) 68 if (!obj1.HasPhysicalBody || !obj2.HasPhysicalBody)
69 { 69 {
70 world.physicsScene.DetailLog("{0},BS6DOFConstraint,badBodyPtr,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}", 70 world.physicsScene.DetailLog("{0},BS6DOFConstraint,badBodyPtr,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
71 BSScene.DetailLogZero, world.worldID, 71 BSScene.DetailLogZero, world.worldID,
@@ -83,7 +83,7 @@ public sealed class BSConstraint6Dof : BSConstraint
83 world.physicsScene.DetailLog("{0},BS6DofConstraint,createMidPoint,wID={1}, csrt={2}, rID={3}, rBody={4}, cID={5}, cBody={6}", 83 world.physicsScene.DetailLog("{0},BS6DofConstraint,createMidPoint,wID={1}, csrt={2}, rID={3}, rBody={4}, cID={5}, cBody={6}",
84 BSScene.DetailLogZero, world.worldID, m_constraint.ptr.ToString("X"), 84 BSScene.DetailLogZero, world.worldID, m_constraint.ptr.ToString("X"),
85 obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X")); 85 obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X"));
86 if (m_constraint.ptr == IntPtr.Zero) 86 if (!m_constraint.HasPhysicalConstraint)
87 { 87 {
88 world.physicsScene.Logger.ErrorFormat("{0} Failed creation of 6Dof constraint. rootID={1}, childID={2}", 88 world.physicsScene.Logger.ErrorFormat("{0} Failed creation of 6Dof constraint. rootID={1}, childID={2}",
89 LogHeader, obj1.ID, obj2.ID); 89 LogHeader, obj1.ID, obj2.ID);
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
index dbc9039..a5acd86 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
@@ -24,30 +24,17 @@
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 * 26 *
27 27 * The quotations from http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial
28/* RA: June 14, 2011. Copied from ODEDynamics.cs and converted to 28 * are Copyright (c) 2009 Linden Research, Inc and are used under their license
29 * call the BulletSim system. 29 * of Creative Commons Attribution-Share Alike 3.0
30 */ 30 * (http://creativecommons.org/licenses/by-sa/3.0/).
31/* Revised Aug, Sept 2009 by Kitto Flora. ODEDynamics.cs replaces
32 * ODEVehicleSettings.cs. It and ODEPrim.cs are re-organised:
33 * ODEPrim.cs contains methods dealing with Prim editing, Prim
34 * characteristics and Kinetic motion.
35 * ODEDynamics.cs contains methods dealing with Prim Physical motion
36 * (dynamics) and the associated settings. Old Linear and angular
37 * motors for dynamic motion have been replace with MoveLinear()
38 * and MoveAngular(); 'Physical' is used only to switch ODE dynamic
39 * simualtion on/off; VEHICAL_TYPE_NONE/VEHICAL_TYPE_<other> is to
40 * switch between 'VEHICLE' parameter use and general dynamics
41 * settings use.
42 */ 31 */
43 32
44using System; 33using System;
45using System.Collections.Generic; 34using System.Collections.Generic;
46using System.Reflection; 35using System.Reflection;
47using System.Runtime.InteropServices; 36using System.Runtime.InteropServices;
48using log4net;
49using OpenMetaverse; 37using OpenMetaverse;
50using OpenSim.Framework;
51using OpenSim.Region.Physics.Manager; 38using OpenSim.Region.Physics.Manager;
52 39
53namespace OpenSim.Region.Physics.BulletSPlugin 40namespace OpenSim.Region.Physics.BulletSPlugin
@@ -80,10 +67,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
80 private Quaternion m_referenceFrame = Quaternion.Identity; 67 private Quaternion m_referenceFrame = Quaternion.Identity;
81 68
82 // Linear properties 69 // Linear properties
70 private BSVMotor m_linearMotor = new BSVMotor("LinearMotor");
83 private Vector3 m_linearMotorDirection = Vector3.Zero; // velocity requested by LSL, decayed by time 71 private Vector3 m_linearMotorDirection = Vector3.Zero; // velocity requested by LSL, decayed by time
84 private Vector3 m_linearMotorOffset = Vector3.Zero; // the point of force can be offset from the center 72 private Vector3 m_linearMotorOffset = Vector3.Zero; // the point of force can be offset from the center
85 private Vector3 m_linearMotorDirectionLASTSET = Vector3.Zero; // velocity requested by LSL 73 private Vector3 m_linearMotorDirectionLASTSET = Vector3.Zero; // velocity requested by LSL
86 private Vector3 m_newVelocity = Vector3.Zero; // velocity computed to be applied to body
87 private Vector3 m_linearFrictionTimescale = Vector3.Zero; 74 private Vector3 m_linearFrictionTimescale = Vector3.Zero;
88 private float m_linearMotorDecayTimescale = 0; 75 private float m_linearMotorDecayTimescale = 0;
89 private float m_linearMotorTimescale = 0; 76 private float m_linearMotorTimescale = 0;
@@ -93,16 +80,18 @@ namespace OpenSim.Region.Physics.BulletSPlugin
93 // private Vector3 m_linearMotorOffset = Vector3.Zero; 80 // private Vector3 m_linearMotorOffset = Vector3.Zero;
94 81
95 //Angular properties 82 //Angular properties
83 private BSVMotor m_angularMotor = new BSVMotor("AngularMotor");
96 private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor 84 private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor
97 // private int m_angularMotorApply = 0; // application frame counter 85 // private int m_angularMotorApply = 0; // application frame counter
98 private Vector3 m_angularMotorVelocity = Vector3.Zero; // current angular motor velocity 86 private Vector3 m_angularMotorVelocity = Vector3.Zero; // current angular motor velocity
99 private float m_angularMotorTimescale = 0; // motor angular velocity ramp up rate 87 private float m_angularMotorTimescale = 0; // motor angular velocity ramp up rate
100 private float m_angularMotorDecayTimescale = 0; // motor angular velocity decay rate 88 private float m_angularMotorDecayTimescale = 0; // motor angular velocity decay rate
101 private Vector3 m_angularFrictionTimescale = Vector3.Zero; // body angular velocity decay rate 89 private Vector3 m_angularFrictionTimescale = Vector3.Zero; // body angular velocity decay rate
102 private Vector3 m_lastAngularVelocity = Vector3.Zero; // what was last applied to body 90 private Vector3 m_lastAngularVelocity = Vector3.Zero;
103 private Vector3 m_lastVertAttractor = Vector3.Zero; // what VA was last applied to body 91 private Vector3 m_lastVertAttractor = Vector3.Zero; // what VA was last applied to body
104 92
105 //Deflection properties 93 //Deflection properties
94 private BSVMotor m_angularDeflectionMotor = new BSVMotor("AngularDeflection");
106 private float m_angularDeflectionEfficiency = 0; 95 private float m_angularDeflectionEfficiency = 0;
107 private float m_angularDeflectionTimescale = 0; 96 private float m_angularDeflectionTimescale = 0;
108 private float m_linearDeflectionEfficiency = 0; 97 private float m_linearDeflectionEfficiency = 0;
@@ -114,6 +103,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
114 private float m_bankingTimescale = 0; 103 private float m_bankingTimescale = 0;
115 104
116 //Hover and Buoyancy properties 105 //Hover and Buoyancy properties
106 private BSVMotor m_hoverMotor = new BSVMotor("Hover");
117 private float m_VhoverHeight = 0f; 107 private float m_VhoverHeight = 0f;
118 private float m_VhoverEfficiency = 0f; 108 private float m_VhoverEfficiency = 0f;
119 private float m_VhoverTimescale = 0f; 109 private float m_VhoverTimescale = 0f;
@@ -124,8 +114,15 @@ namespace OpenSim.Region.Physics.BulletSPlugin
124 // Therefore only m_VehicleBuoyancy=1 (0g) will use the script-requested .Z velocity. 114 // Therefore only m_VehicleBuoyancy=1 (0g) will use the script-requested .Z velocity.
125 115
126 //Attractor properties 116 //Attractor properties
127 private float m_verticalAttractionEfficiency = 1.0f; // damped 117 private BSVMotor m_verticalAttractionMotor = new BSVMotor("VerticalAttraction");
128 private float m_verticalAttractionTimescale = 500f; // Timescale > 300 means no vert attractor. 118 private float m_verticalAttractionEfficiency = 1.0f; // damped
119 private float m_verticalAttractionCutoff = 500f; // per the documentation
120 // Timescale > cutoff means no vert attractor.
121 private float m_verticalAttractionTimescale = 510f;
122
123 // Just some recomputed constants:
124 static readonly float PIOverFour = ((float)Math.PI) / 4f;
125 static readonly float PIOverTwo = ((float)Math.PI) / 2f;
129 126
130 public BSDynamics(BSScene myScene, BSPrim myPrim) 127 public BSDynamics(BSScene myScene, BSPrim myPrim)
131 { 128 {
@@ -137,7 +134,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
137 // Return 'true' if this vehicle is doing vehicle things 134 // Return 'true' if this vehicle is doing vehicle things
138 public bool IsActive 135 public bool IsActive
139 { 136 {
140 get { return Type != Vehicle.TYPE_NONE; } 137 get { return Type != Vehicle.TYPE_NONE && Prim.IsPhysical; }
141 } 138 }
142 139
143 internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue) 140 internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue)
@@ -152,13 +149,15 @@ namespace OpenSim.Region.Physics.BulletSPlugin
152 m_angularDeflectionTimescale = Math.Max(pValue, 0.01f); 149 m_angularDeflectionTimescale = Math.Max(pValue, 0.01f);
153 break; 150 break;
154 case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE: 151 case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE:
155 m_angularMotorDecayTimescale = Math.Max(pValue, 0.01f); 152 m_angularMotorDecayTimescale = ClampInRange(0.01f, pValue, 120);
153 m_angularMotor.TargetValueDecayTimeScale = m_angularMotorDecayTimescale;
156 break; 154 break;
157 case Vehicle.ANGULAR_MOTOR_TIMESCALE: 155 case Vehicle.ANGULAR_MOTOR_TIMESCALE:
158 m_angularMotorTimescale = Math.Max(pValue, 0.01f); 156 m_angularMotorTimescale = Math.Max(pValue, 0.01f);
157 m_angularMotor.TimeScale = m_angularMotorTimescale;
159 break; 158 break;
160 case Vehicle.BANKING_EFFICIENCY: 159 case Vehicle.BANKING_EFFICIENCY:
161 m_bankingEfficiency = Math.Max(-1f, Math.Min(pValue, 1f)); 160 m_bankingEfficiency = ClampInRange(-1f, pValue, 1f);
162 break; 161 break;
163 case Vehicle.BANKING_MIX: 162 case Vehicle.BANKING_MIX:
164 m_bankingMix = Math.Max(pValue, 0.01f); 163 m_bankingMix = Math.Max(pValue, 0.01f);
@@ -167,10 +166,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
167 m_bankingTimescale = Math.Max(pValue, 0.01f); 166 m_bankingTimescale = Math.Max(pValue, 0.01f);
168 break; 167 break;
169 case Vehicle.BUOYANCY: 168 case Vehicle.BUOYANCY:
170 m_VehicleBuoyancy = Math.Max(-1f, Math.Min(pValue, 1f)); 169 m_VehicleBuoyancy = ClampInRange(-1f, pValue, 1f);
171 break; 170 break;
172 case Vehicle.HOVER_EFFICIENCY: 171 case Vehicle.HOVER_EFFICIENCY:
173 m_VhoverEfficiency = Math.Max(0f, Math.Min(pValue, 1f)); 172 m_VhoverEfficiency = ClampInRange(0f, pValue, 1f);
174 break; 173 break;
175 case Vehicle.HOVER_HEIGHT: 174 case Vehicle.HOVER_HEIGHT:
176 m_VhoverHeight = pValue; 175 m_VhoverHeight = pValue;
@@ -185,33 +184,40 @@ namespace OpenSim.Region.Physics.BulletSPlugin
185 m_linearDeflectionTimescale = Math.Max(pValue, 0.01f); 184 m_linearDeflectionTimescale = Math.Max(pValue, 0.01f);
186 break; 185 break;
187 case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE: 186 case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE:
188 m_linearMotorDecayTimescale = Math.Max(pValue, 0.01f); 187 m_linearMotorDecayTimescale = ClampInRange(0.01f, pValue, 120);
188 m_linearMotor.TargetValueDecayTimeScale = m_linearMotorDecayTimescale;
189 break; 189 break;
190 case Vehicle.LINEAR_MOTOR_TIMESCALE: 190 case Vehicle.LINEAR_MOTOR_TIMESCALE:
191 m_linearMotorTimescale = Math.Max(pValue, 0.01f); 191 m_linearMotorTimescale = Math.Max(pValue, 0.01f);
192 m_linearMotor.TimeScale = m_linearMotorTimescale;
192 break; 193 break;
193 case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY: 194 case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY:
194 m_verticalAttractionEfficiency = Math.Max(0.1f, Math.Min(pValue, 1f)); 195 m_verticalAttractionEfficiency = ClampInRange(0.1f, pValue, 1f);
196 m_verticalAttractionMotor.Efficiency = m_verticalAttractionEfficiency;
195 break; 197 break;
196 case Vehicle.VERTICAL_ATTRACTION_TIMESCALE: 198 case Vehicle.VERTICAL_ATTRACTION_TIMESCALE:
197 m_verticalAttractionTimescale = Math.Max(pValue, 0.01f); 199 m_verticalAttractionTimescale = Math.Max(pValue, 0.01f);
200 m_verticalAttractionMotor.TimeScale = m_verticalAttractionTimescale;
198 break; 201 break;
199 202
200 // These are vector properties but the engine lets you use a single float value to 203 // These are vector properties but the engine lets you use a single float value to
201 // set all of the components to the same value 204 // set all of the components to the same value
202 case Vehicle.ANGULAR_FRICTION_TIMESCALE: 205 case Vehicle.ANGULAR_FRICTION_TIMESCALE:
203 m_angularFrictionTimescale = new Vector3(pValue, pValue, pValue); 206 m_angularFrictionTimescale = new Vector3(pValue, pValue, pValue);
207 m_angularMotor.FrictionTimescale = m_angularFrictionTimescale;
204 break; 208 break;
205 case Vehicle.ANGULAR_MOTOR_DIRECTION: 209 case Vehicle.ANGULAR_MOTOR_DIRECTION:
206 m_angularMotorDirection = new Vector3(pValue, pValue, pValue); 210 m_angularMotorDirection = new Vector3(pValue, pValue, pValue);
207 // m_angularMotorApply = 100; 211 m_angularMotor.SetTarget(m_angularMotorDirection);
208 break; 212 break;
209 case Vehicle.LINEAR_FRICTION_TIMESCALE: 213 case Vehicle.LINEAR_FRICTION_TIMESCALE:
210 m_linearFrictionTimescale = new Vector3(pValue, pValue, pValue); 214 m_linearFrictionTimescale = new Vector3(pValue, pValue, pValue);
215 m_linearMotor.FrictionTimescale = m_linearFrictionTimescale;
211 break; 216 break;
212 case Vehicle.LINEAR_MOTOR_DIRECTION: 217 case Vehicle.LINEAR_MOTOR_DIRECTION:
213 m_linearMotorDirection = new Vector3(pValue, pValue, pValue); 218 m_linearMotorDirection = new Vector3(pValue, pValue, pValue);
214 m_linearMotorDirectionLASTSET = new Vector3(pValue, pValue, pValue); 219 m_linearMotorDirectionLASTSET = new Vector3(pValue, pValue, pValue);
220 m_linearMotor.SetTarget(m_linearMotorDirection);
215 break; 221 break;
216 case Vehicle.LINEAR_MOTOR_OFFSET: 222 case Vehicle.LINEAR_MOTOR_OFFSET:
217 m_linearMotorOffset = new Vector3(pValue, pValue, pValue); 223 m_linearMotorOffset = new Vector3(pValue, pValue, pValue);
@@ -227,21 +233,24 @@ namespace OpenSim.Region.Physics.BulletSPlugin
227 { 233 {
228 case Vehicle.ANGULAR_FRICTION_TIMESCALE: 234 case Vehicle.ANGULAR_FRICTION_TIMESCALE:
229 m_angularFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); 235 m_angularFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z);
236 m_angularMotor.FrictionTimescale = m_angularFrictionTimescale;
230 break; 237 break;
231 case Vehicle.ANGULAR_MOTOR_DIRECTION: 238 case Vehicle.ANGULAR_MOTOR_DIRECTION:
232 // Limit requested angular speed to 2 rps= 4 pi rads/sec 239 // Limit requested angular speed to 2 rps= 4 pi rads/sec
233 pValue.X = Math.Max(-12.56f, Math.Min(pValue.X, 12.56f)); 240 pValue.X = ClampInRange(-12.56f, pValue.X, 12.56f);
234 pValue.Y = Math.Max(-12.56f, Math.Min(pValue.Y, 12.56f)); 241 pValue.Y = ClampInRange(-12.56f, pValue.Y, 12.56f);
235 pValue.Z = Math.Max(-12.56f, Math.Min(pValue.Z, 12.56f)); 242 pValue.Z = ClampInRange(-12.56f, pValue.Z, 12.56f);
236 m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); 243 m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z);
237 // m_angularMotorApply = 100; 244 m_angularMotor.SetTarget(m_angularMotorDirection);
238 break; 245 break;
239 case Vehicle.LINEAR_FRICTION_TIMESCALE: 246 case Vehicle.LINEAR_FRICTION_TIMESCALE:
240 m_linearFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); 247 m_linearFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z);
248 m_linearMotor.FrictionTimescale = m_linearFrictionTimescale;
241 break; 249 break;
242 case Vehicle.LINEAR_MOTOR_DIRECTION: 250 case Vehicle.LINEAR_MOTOR_DIRECTION:
243 m_linearMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); 251 m_linearMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z);
244 m_linearMotorDirectionLASTSET = new Vector3(pValue.X, pValue.Y, pValue.Z); 252 m_linearMotorDirectionLASTSET = new Vector3(pValue.X, pValue.Y, pValue.Z);
253 m_linearMotor.SetTarget(m_linearMotorDirection);
245 break; 254 break;
246 case Vehicle.LINEAR_MOTOR_OFFSET: 255 case Vehicle.LINEAR_MOTOR_OFFSET:
247 m_linearMotorOffset = new Vector3(pValue.X, pValue.Y, pValue.Z); 256 m_linearMotorOffset = new Vector3(pValue.X, pValue.Y, pValue.Z);
@@ -303,7 +312,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
303 m_VhoverEfficiency = 0; 312 m_VhoverEfficiency = 0;
304 m_VhoverTimescale = 0; 313 m_VhoverTimescale = 0;
305 m_VehicleBuoyancy = 0; 314 m_VehicleBuoyancy = 0;
306 315
307 m_linearDeflectionEfficiency = 1; 316 m_linearDeflectionEfficiency = 1;
308 m_linearDeflectionTimescale = 1; 317 m_linearDeflectionTimescale = 1;
309 318
@@ -319,6 +328,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
319 328
320 m_referenceFrame = Quaternion.Identity; 329 m_referenceFrame = Quaternion.Identity;
321 m_flags = (VehicleFlag)0; 330 m_flags = (VehicleFlag)0;
331
322 break; 332 break;
323 333
324 case Vehicle.TYPE_SLED: 334 case Vehicle.TYPE_SLED:
@@ -351,10 +361,14 @@ namespace OpenSim.Region.Physics.BulletSPlugin
351 m_bankingMix = 1; 361 m_bankingMix = 1;
352 362
353 m_referenceFrame = Quaternion.Identity; 363 m_referenceFrame = Quaternion.Identity;
354 m_flags |= (VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_ROLL_ONLY | VehicleFlag.LIMIT_MOTOR_UP); 364 m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
355 m_flags &= 365 | VehicleFlag.HOVER_TERRAIN_ONLY
356 ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | 366 | VehicleFlag.HOVER_GLOBAL_HEIGHT
357 VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY); 367 | VehicleFlag.HOVER_UP_ONLY);
368 m_flags |= (VehicleFlag.NO_DEFLECTION_UP
369 | VehicleFlag.LIMIT_ROLL_ONLY
370 | VehicleFlag.LIMIT_MOTOR_UP);
371
358 break; 372 break;
359 case Vehicle.TYPE_CAR: 373 case Vehicle.TYPE_CAR:
360 m_linearMotorDirection = Vector3.Zero; 374 m_linearMotorDirection = Vector3.Zero;
@@ -498,6 +512,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
498 m_bankingEfficiency = 0; 512 m_bankingEfficiency = 0;
499 m_bankingMix = 0.7f; 513 m_bankingMix = 0.7f;
500 m_bankingTimescale = 5; 514 m_bankingTimescale = 5;
515
501 m_referenceFrame = Quaternion.Identity; 516 m_referenceFrame = Quaternion.Identity;
502 517
503 m_referenceFrame = Quaternion.Identity; 518 m_referenceFrame = Quaternion.Identity;
@@ -510,6 +525,26 @@ namespace OpenSim.Region.Physics.BulletSPlugin
510 | VehicleFlag.HOVER_GLOBAL_HEIGHT); 525 | VehicleFlag.HOVER_GLOBAL_HEIGHT);
511 break; 526 break;
512 } 527 }
528
529 // Update any physical parameters based on this type.
530 Refresh();
531
532 m_linearMotor = new BSVMotor("LinearMotor", m_linearMotorTimescale,
533 m_linearMotorDecayTimescale, m_linearFrictionTimescale,
534 1f);
535 m_linearMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
536
537 m_angularMotor = new BSVMotor("AngularMotor", m_angularMotorTimescale,
538 m_angularMotorDecayTimescale, m_angularFrictionTimescale,
539 1f);
540 m_angularMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
541
542 m_verticalAttractionMotor = new BSVMotor("VerticalAttraction", m_verticalAttractionTimescale,
543 BSMotor.Infinite, BSMotor.InfiniteVector,
544 m_verticalAttractionEfficiency);
545 // Z goes away and we keep X and Y
546 m_verticalAttractionMotor.FrictionTimescale = new Vector3(BSMotor.Infinite, BSMotor.Infinite, 0.1f);
547 m_verticalAttractionMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
513 } 548 }
514 549
515 // Some of the properties of this prim may have changed. 550 // Some of the properties of this prim may have changed.
@@ -518,13 +553,32 @@ namespace OpenSim.Region.Physics.BulletSPlugin
518 { 553 {
519 if (IsActive) 554 if (IsActive)
520 { 555 {
521 // Friction effects are handled by this vehicle code 556 // Remember the mass so we don't have to fetch it every step
522 BulletSimAPI.SetFriction2(Prim.PhysBody.ptr, 0f); 557 m_vehicleMass = Prim.Linkset.LinksetMass;
523 BulletSimAPI.SetHitFraction2(Prim.PhysBody.ptr, 0f);
524 558
525 // BulletSimAPI.SetAngularDamping2(Prim.PhysBody.ptr, 0.8f); 559 // Friction affects are handled by this vehicle code
560 float friction = 0f;
561 BulletSimAPI.SetFriction2(Prim.PhysBody.ptr, friction);
526 562
527 VDetailLog("{0},BSDynamics.Refresh,zeroingFriction and adding damping", Prim.LocalID); 563 // Moderate angular movement introduced by Bullet.
564 // TODO: possibly set AngularFactor and LinearFactor for the type of vehicle.
565 // Maybe compute linear and angular factor and damping from params.
566 float angularDamping = PhysicsScene.Params.vehicleAngularDamping;
567 BulletSimAPI.SetAngularDamping2(Prim.PhysBody.ptr, angularDamping);
568
569 // Vehicles report collision events so we know when it's on the ground
570 BulletSimAPI.AddToCollisionFlags2(Prim.PhysBody.ptr, CollisionFlags.BS_VEHICLE_COLLISIONS);
571
572 Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(Prim.PhysShape.ptr, m_vehicleMass);
573 BulletSimAPI.SetMassProps2(Prim.PhysBody.ptr, m_vehicleMass, localInertia);
574 BulletSimAPI.UpdateInertiaTensor2(Prim.PhysBody.ptr);
575
576 VDetailLog("{0},BSDynamics.Refresh,mass={1},frict={2},inert={3},aDamp={4}",
577 Prim.LocalID, m_vehicleMass, friction, localInertia, angularDamping);
578 }
579 else
580 {
581 BulletSimAPI.RemoveFromCollisionFlags2(Prim.PhysBody.ptr, CollisionFlags.BS_VEHICLE_COLLISIONS);
528 } 582 }
529 } 583 }
530 584
@@ -546,116 +600,317 @@ namespace OpenSim.Region.Physics.BulletSPlugin
546 Refresh(); 600 Refresh();
547 } 601 }
548 602
603 #region Known vehicle value functions
604 // Vehicle physical parameters that we buffer from constant getting and setting.
605 // The "m_known*" values are unknown until they are fetched and the m_knownHas flag is set.
606 // Changing is remembered and the parameter is stored back into the physics engine only if updated.
607 // This does two things: 1) saves continuious calls into unmanaged code, and
608 // 2) signals when a physics property update must happen back to the simulator
609 // to update values modified for the vehicle.
610 private int m_knownChanged;
611 private int m_knownHas;
612 private float m_knownTerrainHeight;
613 private float m_knownWaterLevel;
614 private Vector3 m_knownPosition;
615 private Vector3 m_knownVelocity;
616 private Vector3 m_knownForce;
617 private Quaternion m_knownOrientation;
618 private Vector3 m_knownRotationalVelocity;
619 private Vector3 m_knownRotationalForce;
620 private Vector3 m_knownForwardVelocity; // vehicle relative forward speed
621
622 private const int m_knownChangedPosition = 1 << 0;
623 private const int m_knownChangedVelocity = 1 << 1;
624 private const int m_knownChangedForce = 1 << 2;
625 private const int m_knownChangedOrientation = 1 << 3;
626 private const int m_knownChangedRotationalVelocity = 1 << 4;
627 private const int m_knownChangedRotationalForce = 1 << 5;
628 private const int m_knownChangedTerrainHeight = 1 << 6;
629 private const int m_knownChangedWaterLevel = 1 << 7;
630 private const int m_knownChangedForwardVelocity = 1 << 8;
631
632 private void ForgetKnownVehicleProperties()
633 {
634 m_knownHas = 0;
635 m_knownChanged = 0;
636 }
637 private void PushKnownChanged()
638 {
639 if (m_knownChanged != 0)
640 {
641 if ((m_knownChanged & m_knownChangedPosition) != 0)
642 Prim.ForcePosition = VehiclePosition;
643 if ((m_knownChanged & m_knownChangedOrientation) != 0)
644 Prim.ForceOrientation = VehicleOrientation;
645 if ((m_knownChanged & m_knownChangedVelocity) != 0)
646 {
647 Prim.ForceVelocity = VehicleVelocity;
648 BulletSimAPI.SetInterpolationLinearVelocity2(Prim.PhysBody.ptr, VehicleVelocity);
649 }
650 if ((m_knownChanged & m_knownChangedForce) != 0)
651 Prim.AddForce((Vector3)m_knownForce, false, true);
652
653 if ((m_knownChanged & m_knownChangedRotationalVelocity) != 0)
654 {
655 Prim.ForceRotationalVelocity = VehicleRotationalVelocity;
656 // Fake out Bullet by making it think the velocity is the same as last time.
657 BulletSimAPI.SetInterpolationAngularVelocity2(Prim.PhysBody.ptr, VehicleRotationalVelocity);
658 }
659 if ((m_knownChanged & m_knownChangedRotationalForce) != 0)
660 Prim.AddAngularForce((Vector3)m_knownRotationalForce, false, true);
661
662 // If we set one of the values (ie, the physics engine didn't do it) we must force
663 // an UpdateProperties event to send the changes up to the simulator.
664 BulletSimAPI.PushUpdate2(Prim.PhysBody.ptr);
665 }
666 m_knownChanged = 0;
667 }
668
669 // Since the computation of terrain height can be a little involved, this routine
670 // is used ot fetch the height only once for each vehicle simulation step.
671 private float GetTerrainHeight(Vector3 pos)
672 {
673 if ((m_knownHas & m_knownChangedTerrainHeight) == 0)
674 {
675 m_knownTerrainHeight = Prim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos);
676 m_knownHas |= m_knownChangedTerrainHeight;
677 }
678 return m_knownTerrainHeight;
679 }
680
681 // Since the computation of water level can be a little involved, this routine
682 // is used ot fetch the level only once for each vehicle simulation step.
683 private float GetWaterLevel(Vector3 pos)
684 {
685 if ((m_knownHas & m_knownChangedWaterLevel) == 0)
686 {
687 m_knownWaterLevel = Prim.PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(pos);
688 m_knownHas |= m_knownChangedWaterLevel;
689 }
690 return (float)m_knownWaterLevel;
691 }
692
693 private Vector3 VehiclePosition
694 {
695 get
696 {
697 if ((m_knownHas & m_knownChangedPosition) == 0)
698 {
699 m_knownPosition = Prim.ForcePosition;
700 m_knownHas |= m_knownChangedPosition;
701 }
702 return (Vector3)m_knownPosition;
703 }
704 set
705 {
706 m_knownPosition = value;
707 m_knownChanged |= m_knownChangedPosition;
708 }
709 }
710
711 private Quaternion VehicleOrientation
712 {
713 get
714 {
715 if ((m_knownHas & m_knownChangedOrientation) == 0)
716 {
717 m_knownOrientation = Prim.ForceOrientation;
718 m_knownHas |= m_knownChangedOrientation;
719 }
720 return (Quaternion)m_knownOrientation;
721 }
722 set
723 {
724 m_knownOrientation = value;
725 m_knownChanged |= m_knownChangedOrientation;
726 }
727 }
728
729 private Vector3 VehicleVelocity
730 {
731 get
732 {
733 if ((m_knownHas & m_knownChangedVelocity) == 0)
734 {
735 m_knownVelocity = Prim.ForceVelocity;
736 m_knownHas |= m_knownChangedVelocity;
737 }
738 return (Vector3)m_knownVelocity;
739 }
740 set
741 {
742 m_knownVelocity = value;
743 m_knownChanged |= m_knownChangedVelocity;
744 }
745 }
746
747 private void VehicleAddForce(Vector3 aForce)
748 {
749 m_knownForce += aForce;
750 m_knownChanged |= m_knownChangedForce;
751 }
752
753 private Vector3 VehicleRotationalVelocity
754 {
755 get
756 {
757 if ((m_knownHas & m_knownChangedRotationalVelocity) == 0)
758 {
759 m_knownRotationalVelocity = Prim.ForceRotationalVelocity;
760 m_knownHas |= m_knownChangedRotationalVelocity;
761 }
762 return (Vector3)m_knownRotationalVelocity;
763 }
764 set
765 {
766 m_knownRotationalVelocity = value;
767 m_knownChanged |= m_knownChangedRotationalVelocity;
768 }
769 }
770 private void VehicleAddAngularForce(Vector3 aForce)
771 {
772 m_knownRotationalForce += aForce;
773 m_knownChanged |= m_knownChangedRotationalForce;
774 }
775 // Vehicle relative forward velocity
776 private Vector3 VehicleForwardVelocity
777 {
778 get
779 {
780 if ((m_knownHas & m_knownChangedForwardVelocity) == 0)
781 {
782 m_knownForwardVelocity = VehicleVelocity * Quaternion.Inverse(Quaternion.Normalize(VehicleOrientation));
783 m_knownHas |= m_knownChangedForwardVelocity;
784 }
785 return (Vector3)m_knownForwardVelocity;
786 }
787 }
788 private float VehicleForwardSpeed
789 {
790 get
791 {
792 return VehicleForwardVelocity.X;
793 }
794 }
795
796 #endregion // Known vehicle value functions
797
549 // One step of the vehicle properties for the next 'pTimestep' seconds. 798 // One step of the vehicle properties for the next 'pTimestep' seconds.
550 internal void Step(float pTimestep) 799 internal void Step(float pTimestep)
551 { 800 {
552 if (!IsActive) return; 801 if (!IsActive) return;
553 802
554 // DEBUG 803 ForgetKnownVehicleProperties();
555 // Because Bullet does apply forces to the vehicle, our last computed
556 // linear and angular velocities are not what is happening now.
557 // Vector3 externalAngularVelocity = Prim.ForceRotationalVelocity - m_lastAngularVelocity;
558 // m_lastAngularVelocity += (externalAngularVelocity * 0.5f) * pTimestep;
559 // m_lastAngularVelocity = Prim.ForceRotationalVelocity; // DEBUG: account for what Bullet did last time
560 // m_lastLinearVelocityVector = Prim.ForceVelocity * Quaternion.Inverse(Prim.ForceOrientation); // DEBUG:
561 // END DEBUG
562
563 m_vehicleMass = Prim.Linkset.LinksetMass;
564 804
565 MoveLinear(pTimestep); 805 MoveLinear(pTimestep);
566 // Commented out for debug
567 MoveAngular(pTimestep); 806 MoveAngular(pTimestep);
568 // Prim.ApplyTorqueImpulse(-Prim.RotationalVelocity * m_vehicleMass, false); // DEBUG DEBUG
569 // Prim.ForceRotationalVelocity = -Prim.RotationalVelocity; // DEBUG DEBUG
570 807
571 LimitRotation(pTimestep); 808 LimitRotation(pTimestep);
572 809
573 // remember the position so next step we can limit absolute movement effects 810 // remember the position so next step we can limit absolute movement effects
574 m_lastPositionVector = Prim.ForcePosition; 811 m_lastPositionVector = VehiclePosition;
575 812
576 VDetailLog("{0},BSDynamics.Step,frict={1},grav={2},inertia={3},mass={4}", // DEBUG DEBUG 813 // If we forced the changing of some vehicle parameters, update the values and
577 Prim.LocalID, 814 // for the physics engine to note the changes so an UpdateProperties event will happen.
578 BulletSimAPI.GetFriction2(Prim.PhysBody.ptr), 815 PushKnownChanged();
579 BulletSimAPI.GetGravity2(Prim.PhysBody.ptr), 816
580 Prim.Inertia,
581 m_vehicleMass
582 );
583 VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}", 817 VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}",
584 Prim.LocalID, Prim.ForcePosition, Prim.Force, Prim.ForceVelocity, Prim.RotationalVelocity); 818 Prim.LocalID, VehiclePosition, Prim.Force, VehicleVelocity, VehicleRotationalVelocity);
585 }// end Step 819 }
586 820
587 // Apply the effect of the linear motor. 821 // Apply the effect of the linear motor and other linear motions (like hover and float).
588 // Also does hover and float.
589 private void MoveLinear(float pTimestep) 822 private void MoveLinear(float pTimestep)
590 { 823 {
591 // m_linearMotorDirection is the target direction we are moving relative to the vehicle coordinates 824 Vector3 linearMotorContribution = m_linearMotor.Step(pTimestep);
592 // m_lastLinearVelocityVector is the current speed we are moving in that direction
593 if (m_linearMotorDirection.LengthSquared() > 0.001f)
594 {
595 Vector3 origDir = m_linearMotorDirection; // DEBUG
596 Vector3 origVel = m_lastLinearVelocityVector; // DEBUG
597 // DEBUG: the vehicle velocity rotated to be relative to vehicle coordinates for comparison
598 Vector3 vehicleVelocity = Prim.ForceVelocity * Quaternion.Inverse(Prim.ForceOrientation); // DEBUG
599 825
600 // Add (desiredVelocity - lastAppliedVelocity) / howLongItShouldTakeToComplete 826 // The movement computed in the linear motor is relative to the vehicle
601 Vector3 addAmount = (m_linearMotorDirection - m_lastLinearVelocityVector)/(m_linearMotorTimescale) * pTimestep; 827 // coordinates. Rotate the movement to world coordinates.
602 m_lastLinearVelocityVector += addAmount; 828 linearMotorContribution *= VehicleOrientation;
603 829
604 float decayFactor = (1.0f / m_linearMotorDecayTimescale) * pTimestep; 830 // ==================================================================
605 m_linearMotorDirection *= (1f - decayFactor); 831 // Buoyancy: force to overcome gravity.
832 // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g;
833 // So, if zero, don't change anything (let gravity happen). If one, negate the effect of gravity.
834 Vector3 buoyancyContribution = Prim.PhysicsScene.DefaultGravity * m_VehicleBuoyancy;
606 835
607 // Rotate new object velocity from vehicle relative to world coordinates 836 Vector3 terrainHeightContribution = ComputeLinearTerrainHeightCorrection(pTimestep);
608 m_newVelocity = m_lastLinearVelocityVector * Prim.ForceOrientation;
609 837
610 // Apply friction for next time 838 Vector3 hoverContribution = ComputeLinearHover(pTimestep);
611 Vector3 frictionFactor = (Vector3.One / m_linearFrictionTimescale) * pTimestep;
612 m_lastLinearVelocityVector *= (Vector3.One - frictionFactor);
613 839
614 VDetailLog("{0},MoveLinear,nonZero,origlmDir={1},origlvVel={2},vehVel={3},add={4},decay={5},frict={6},lmDir={7},lvVec={8},newVel={9}", 840 ComputeLinearBlockingEndPoint(pTimestep);
615 Prim.LocalID, origDir, origVel, vehicleVelocity, addAmount, decayFactor, frictionFactor, 841
616 m_linearMotorDirection, m_lastLinearVelocityVector, m_newVelocity); 842 Vector3 limitMotorUpContribution = ComputeLinearMotorUp(pTimestep);
617 } 843
618 else 844 // ==================================================================
619 { 845 Vector3 newVelocity = linearMotorContribution
620 // if what remains of direction is very small, zero it. 846 + terrainHeightContribution
621 m_linearMotorDirection = Vector3.Zero; 847 + hoverContribution
622 m_lastLinearVelocityVector = Vector3.Zero; 848 + limitMotorUpContribution;
623 m_newVelocity = Vector3.Zero; 849
850 Vector3 newForce = buoyancyContribution;
624 851
625 VDetailLog("{0},MoveLinear,zeroed", Prim.LocalID); 852 // If not changing some axis, reduce out velocity
853 if ((m_flags & (VehicleFlag.NO_X)) != 0)
854 newVelocity.X = 0;
855 if ((m_flags & (VehicleFlag.NO_Y)) != 0)
856 newVelocity.Y = 0;
857 if ((m_flags & (VehicleFlag.NO_Z)) != 0)
858 newVelocity.Z = 0;
859
860 // ==================================================================
861 // Clamp high or low velocities
862 float newVelocityLengthSq = newVelocity.LengthSquared();
863 if (newVelocityLengthSq > 1000f)
864 {
865 newVelocity /= newVelocity.Length();
866 newVelocity *= 1000f;
626 } 867 }
868 else if (newVelocityLengthSq < 0.001f)
869 newVelocity = Vector3.Zero;
627 870
628 // m_newVelocity is velocity computed from linear motor in world coordinates 871 // ==================================================================
872 // Stuff new linear velocity into the vehicle.
873 // Since the velocity is just being set, it is not scaled by pTimeStep. Bullet will do that for us.
874 VehicleVelocity = newVelocity;
629 875
630 // Gravity and Buoyancy 876 // Other linear forces are applied as forces.
631 // There is some gravity, make a gravity force vector that is applied after object velocity. 877 Vector3 totalDownForce = newForce * m_vehicleMass;
632 // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g; 878 if (!totalDownForce.ApproxEquals(Vector3.Zero, 0.01f))
633 Vector3 grav = Prim.PhysicsScene.DefaultGravity * (1f - m_VehicleBuoyancy); 879 {
880 VehicleAddForce(totalDownForce);
881 }
634 882
635 /* 883 VDetailLog("{0}, MoveLinear,done,newVel={1},totDown={2},IsColliding={3}",
636 * RA: Not sure why one would do this unless we are hoping external forces are doing gravity, ... 884 Prim.LocalID, newVelocity, totalDownForce, Prim.IsColliding);
637 // Preserve the current Z velocity 885 VDetailLog("{0}, MoveLinear,done,linContrib={1},terrContrib={2},hoverContrib={3},limitContrib={4},buoyContrib={5}",
638 Vector3 vel_now = m_prim.Velocity; 886 Prim.LocalID,
639 m_dir.Z = vel_now.Z; // Preserve the accumulated falling velocity 887 linearMotorContribution, terrainHeightContribution, hoverContribution,
640 */ 888 limitMotorUpContribution, buoyancyContribution
889 );
641 890
642 Vector3 pos = Prim.ForcePosition; 891 } // end MoveLinear()
643// Vector3 accel = new Vector3(-(m_dir.X - m_lastLinearVelocityVector.X / 0.1f), -(m_dir.Y - m_lastLinearVelocityVector.Y / 0.1f), m_dir.Z - m_lastLinearVelocityVector.Z / 0.1f);
644 892
893 public Vector3 ComputeLinearTerrainHeightCorrection(float pTimestep)
894 {
895 Vector3 ret = Vector3.Zero;
645 // If below the terrain, move us above the ground a little. 896 // If below the terrain, move us above the ground a little.
646 float terrainHeight = Prim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos); 897 // TODO: Consider taking the rotated size of the object or possibly casting a ray.
647 // Taking the rotated size doesn't work here because m_prim.Size is the size of the root prim and not the linkset. 898 if (VehiclePosition.Z < GetTerrainHeight(VehiclePosition))
648 // TODO: Add a m_prim.LinkSet.Size similar to m_prim.LinkSet.Mass.
649 // Vector3 rotatedSize = m_prim.Size * m_prim.ForceOrientation;
650 // if (rotatedSize.Z < terrainHeight)
651 if (pos.Z < terrainHeight)
652 { 899 {
653 pos.Z = terrainHeight + 2; 900 // TODO: correct position by applying force rather than forcing position.
654 Prim.ForcePosition = pos; 901 Vector3 newPosition = VehiclePosition;
655 VDetailLog("{0},MoveLinear,terrainHeight,terrainHeight={1},pos={2}", Prim.LocalID, terrainHeight, pos); 902 newPosition.Z = GetTerrainHeight(VehiclePosition) + 1f;
903 VehiclePosition = newPosition;
904 VDetailLog("{0}, MoveLinear,terrainHeight,terrainHeight={1},pos={2}",
905 Prim.LocalID, GetTerrainHeight(VehiclePosition), VehiclePosition);
656 } 906 }
907 return ret;
908 }
909
910 public Vector3 ComputeLinearHover(float pTimestep)
911 {
912 Vector3 ret = Vector3.Zero;
657 913
658 // Check if hovering
659 // m_VhoverEfficiency: 0=bouncy, 1=totally damped 914 // m_VhoverEfficiency: 0=bouncy, 1=totally damped
660 // m_VhoverTimescale: time to achieve height 915 // m_VhoverTimescale: time to achieve height
661 if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0) 916 if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0)
@@ -663,11 +918,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin
663 // We should hover, get the target height 918 // We should hover, get the target height
664 if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0) 919 if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0)
665 { 920 {
666 m_VhoverTargetHeight = Prim.PhysicsScene.GetWaterLevelAtXYZ(pos) + m_VhoverHeight; 921 m_VhoverTargetHeight = GetWaterLevel(VehiclePosition) + m_VhoverHeight;
667 } 922 }
668 if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0) 923 if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0)
669 { 924 {
670 m_VhoverTargetHeight = terrainHeight + m_VhoverHeight; 925 m_VhoverTargetHeight = GetTerrainHeight(VehiclePosition) + m_VhoverHeight;
671 } 926 }
672 if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0) 927 if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0)
673 { 928 {
@@ -677,45 +932,47 @@ namespace OpenSim.Region.Physics.BulletSPlugin
677 if ((m_flags & VehicleFlag.HOVER_UP_ONLY) != 0) 932 if ((m_flags & VehicleFlag.HOVER_UP_ONLY) != 0)
678 { 933 {
679 // If body is already heigher, use its height as target height 934 // If body is already heigher, use its height as target height
680 if (pos.Z > m_VhoverTargetHeight) 935 if (VehiclePosition.Z > m_VhoverTargetHeight)
681 m_VhoverTargetHeight = pos.Z; 936 m_VhoverTargetHeight = VehiclePosition.Z;
682 } 937 }
938
683 if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0) 939 if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0)
684 { 940 {
685 if (Math.Abs(pos.Z - m_VhoverTargetHeight) > 0.2f) 941 if (Math.Abs(VehiclePosition.Z - m_VhoverTargetHeight) > 0.2f)
686 { 942 {
943 Vector3 pos = VehiclePosition;
687 pos.Z = m_VhoverTargetHeight; 944 pos.Z = m_VhoverTargetHeight;
688 Prim.ForcePosition = pos; 945 VehiclePosition = pos;
689 } 946 }
690 } 947 }
691 else 948 else
692 { 949 {
693 float verticalError = pos.Z - m_VhoverTargetHeight; 950 // Error is positive if below the target and negative if above.
694 // RA: where does the 50 come from? 951 float verticalError = m_VhoverTargetHeight - VehiclePosition.Z;
695 float verticalCorrectionVelocity = pTimestep * ((verticalError * 50.0f) / m_VhoverTimescale); 952 float verticalCorrectionVelocity = verticalError / m_VhoverTimescale;
696 // Replace Vertical speed with correction figure if significant 953
697 if (Math.Abs(verticalError) > 0.01f) 954 // TODO: implement m_VhoverEfficiency correctly
698 { 955 if (Math.Abs(verticalError) > m_VhoverEfficiency)
699 m_newVelocity.Z += verticalCorrectionVelocity;
700 //KF: m_VhoverEfficiency is not yet implemented
701 }
702 else if (verticalError < -0.01)
703 {
704 m_newVelocity.Z -= verticalCorrectionVelocity;
705 }
706 else
707 { 956 {
708 m_newVelocity.Z = 0f; 957 ret = new Vector3(0f, 0f, verticalCorrectionVelocity);
709 } 958 }
710 } 959 }
711 960
712 VDetailLog("{0},MoveLinear,hover,pos={1},dir={2},height={3},target={4}", Prim.LocalID, pos, m_newVelocity, m_VhoverHeight, m_VhoverTargetHeight); 961 VDetailLog("{0}, MoveLinear,hover,pos={1},ret={2},hoverTS={3},height={4},target={5}",
962 Prim.LocalID, VehiclePosition, ret, m_VhoverTimescale, m_VhoverHeight, m_VhoverTargetHeight);
713 } 963 }
714 964
965 return ret;
966 }
967
968 public bool ComputeLinearBlockingEndPoint(float pTimestep)
969 {
970 bool changed = false;
971
972 Vector3 pos = VehiclePosition;
715 Vector3 posChange = pos - m_lastPositionVector; 973 Vector3 posChange = pos - m_lastPositionVector;
716 if (m_BlockingEndPoint != Vector3.Zero) 974 if (m_BlockingEndPoint != Vector3.Zero)
717 { 975 {
718 bool changed = false;
719 if (pos.X >= (m_BlockingEndPoint.X - (float)1)) 976 if (pos.X >= (m_BlockingEndPoint.X - (float)1))
720 { 977 {
721 pos.X -= posChange.X + 1; 978 pos.X -= posChange.X + 1;
@@ -743,233 +1000,109 @@ namespace OpenSim.Region.Physics.BulletSPlugin
743 } 1000 }
744 if (changed) 1001 if (changed)
745 { 1002 {
746 Prim.ForcePosition = pos; 1003 VehiclePosition = pos;
747 VDetailLog("{0},MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}", 1004 VDetailLog("{0}, MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}",
748 Prim.LocalID, m_BlockingEndPoint, posChange, pos); 1005 Prim.LocalID, m_BlockingEndPoint, posChange, pos);
749 } 1006 }
750 } 1007 }
1008 return changed;
1009 }
751 1010
752 #region downForce 1011 // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags :
753 Vector3 downForce = Vector3.Zero; 1012 // Prevent ground vehicles from motoring into the sky. This flag has a subtle effect when
1013 // used with conjunction with banking: the strength of the banking will decay when the
1014 // vehicle no longer experiences collisions. The decay timescale is the same as
1015 // VEHICLE_BANKING_TIMESCALE. This is to help prevent ground vehicles from steering
1016 // when they are in mid jump.
1017 // TODO: this code is wrong. Also, what should it do for boats (height from water)?
1018 // This is just using the ground and a general collision check. Should really be using
1019 // a downward raycast to find what is below.
1020 public Vector3 ComputeLinearMotorUp(float pTimestep)
1021 {
1022 Vector3 ret = Vector3.Zero;
1023 float distanceAboveGround = 0f;
754 1024
755 if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0) 1025 if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0)
756 { 1026 {
757 // If the vehicle is motoring into the sky, get it going back down. 1027 float targetHeight = Type == Vehicle.TYPE_BOAT ? GetWaterLevel(VehiclePosition) : GetTerrainHeight(VehiclePosition);
758 // Is this an angular force or both linear and angular?? 1028 distanceAboveGround = VehiclePosition.Z - targetHeight;
759 float distanceAboveGround = pos.Z - terrainHeight; 1029 // Not colliding if the vehicle is off the ground
760 if (distanceAboveGround > 2f) 1030 if (!Prim.IsColliding)
761 { 1031 {
762 // downForce = new Vector3(0, 0, (-distanceAboveGround / m_bankingTimescale) * pTimestep);
763 // downForce = new Vector3(0, 0, -distanceAboveGround / m_bankingTimescale); 1032 // downForce = new Vector3(0, 0, -distanceAboveGround / m_bankingTimescale);
764 downForce = new Vector3(0, 0, -distanceAboveGround); 1033 ret = new Vector3(0, 0, -distanceAboveGround);
765 } 1034 }
766 // TODO: this calculation is all wrong. From the description at 1035 // TODO: this calculation is wrong. From the description at
767 // (http://wiki.secondlife.com/wiki/Category:LSL_Vehicle), the downForce 1036 // (http://wiki.secondlife.com/wiki/Category:LSL_Vehicle), the downForce
768 // has a decay factor. This says this force should 1037 // has a decay factor. This says this force should
769 // be computed with a motor. 1038 // be computed with a motor.
770 VDetailLog("{0},MoveLinear,limitMotorUp,distAbove={1},downForce={2}", 1039 // TODO: add interaction with banking.
771 Prim.LocalID, distanceAboveGround, downForce);
772 }
773 #endregion // downForce
774
775 // If not changing some axis, reduce out velocity
776 if ((m_flags & (VehicleFlag.NO_X)) != 0)
777 m_newVelocity.X = 0;
778 if ((m_flags & (VehicleFlag.NO_Y)) != 0)
779 m_newVelocity.Y = 0;
780 if ((m_flags & (VehicleFlag.NO_Z)) != 0)
781 m_newVelocity.Z = 0;
782
783 // Clamp REALLY high or low velocities
784 if (m_newVelocity.LengthSquared() > 1e6f)
785 {
786 m_newVelocity /= m_newVelocity.Length();
787 m_newVelocity *= 1000f;
788 }
789 else if (m_newVelocity.LengthSquared() < 1e-6f)
790 m_newVelocity = Vector3.Zero;
791
792 // Stuff new linear velocity into the vehicle
793 Prim.ForceVelocity = m_newVelocity;
794 // Prim.ApplyForceImpulse((m_newVelocity - Prim.Velocity) * m_vehicleMass, false); // DEBUG DEBUG
795
796 Vector3 totalDownForce = downForce + grav;
797 if (totalDownForce != Vector3.Zero)
798 {
799 Prim.AddForce(totalDownForce * m_vehicleMass, false);
800 // Prim.ApplyForceImpulse(totalDownForce * m_vehicleMass, false);
801 } 1040 }
802 1041 VDetailLog("{0}, MoveLinear,limitMotorUp,distAbove={1},colliding={2},ret={3}",
803 VDetailLog("{0},MoveLinear,done,lmDir={1},lmVel={2},newVel={3},primVel={4},totalDown={5}", 1042 Prim.LocalID, distanceAboveGround, Prim.IsColliding, ret);
804 Prim.LocalID, m_linearMotorDirection, m_lastLinearVelocityVector, m_newVelocity, Prim.Velocity, totalDownForce); 1043 return ret;
805 1044 }
806 } // end MoveLinear()
807 1045
808 // ======================================================================= 1046 // =======================================================================
1047 // =======================================================================
809 // Apply the effect of the angular motor. 1048 // Apply the effect of the angular motor.
1049 // The 'contribution' is how much angular correction velocity each function wants.
1050 // All the contributions are added together and the resulting velocity is
1051 // set directly on the vehicle.
810 private void MoveAngular(float pTimestep) 1052 private void MoveAngular(float pTimestep)
811 { 1053 {
812 // m_angularMotorDirection // angular velocity requested by LSL motor 1054 // The user wants this many radians per second angular change?
813 // m_angularMotorApply // application frame counter 1055 Vector3 angularMotorContribution = m_angularMotor.Step(pTimestep);
814 // m_angularMotorVelocity // current angular motor velocity (ramps up and down) 1056
815 // m_angularMotorTimescale // motor angular velocity ramp up rate 1057 // ==================================================================
816 // m_angularMotorDecayTimescale // motor angular velocity decay rate 1058 // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags :
817 // m_angularFrictionTimescale // body angular velocity decay rate 1059 // This flag prevents linear deflection parallel to world z-axis. This is useful
818 // m_lastAngularVelocity // what was last applied to body 1060 // for preventing ground vehicles with large linear deflection, like bumper cars,
819 1061 // from climbing their linear deflection into the sky.
820 if (m_angularMotorDirection.LengthSquared() > 0.0001) 1062 // That is, NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement
821 { 1063 if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0)
822 Vector3 origVel = m_angularMotorVelocity;
823 Vector3 origDir = m_angularMotorDirection;
824
825 // new velocity += error / ( time to get there / step interval)
826 // requested direction - current vehicle direction
827 m_angularMotorVelocity += (m_angularMotorDirection - m_angularMotorVelocity) / (m_angularMotorTimescale / pTimestep);
828 // decay requested direction
829 m_angularMotorDirection *= (1.0f - (pTimestep * 1.0f/m_angularMotorDecayTimescale));
830
831 VDetailLog("{0},MoveAngular,angularMotorApply,angTScale={1},timeStep={2},origvel={3},origDir={4},vel={5}",
832 Prim.LocalID, m_angularMotorTimescale, pTimestep, origVel, origDir, m_angularMotorVelocity);
833 }
834 else
835 { 1064 {
836 m_angularMotorVelocity = Vector3.Zero; 1065 angularMotorContribution.X = 0f;
1066 angularMotorContribution.Y = 0f;
1067 VDetailLog("{0}, MoveAngular,noDeflectionUp,angularMotorContrib={1}", Prim.LocalID, angularMotorContribution);
837 } 1068 }
838 1069
839 #region Vertical attactor 1070 Vector3 verticalAttractionContribution = ComputeAngularVerticalAttraction();
840 1071
841 Vector3 vertattr = Vector3.Zero; 1072 Vector3 deflectionContribution = ComputeAngularDeflection();
842 Vector3 deflection = Vector3.Zero;
843 Vector3 banking = Vector3.Zero;
844 1073
845 // If vertical attaction timescale is reasonable and we applied an angular force last time... 1074 Vector3 bankingContribution = ComputeAngularBanking();
846 if (m_verticalAttractionTimescale < 300 && m_lastAngularVelocity != Vector3.Zero)
847 {
848 float VAservo = pTimestep * 0.2f / m_verticalAttractionTimescale;
849 if (Prim.IsColliding)
850 VAservo = pTimestep * 0.05f / (m_verticalAttractionTimescale);
851
852 VAservo *= (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency);
853
854 // Create a vector of the vehicle "up" in world coordinates
855 Vector3 verticalError = Vector3.UnitZ * Prim.ForceOrientation;
856 // verticalError.X and .Y are the World error amounts. They are 0 when there is no
857 // error (Vehicle Body is 'vertical'), and .Z will be 1. As the body leans to its
858 // side |.X| will increase to 1 and .Z fall to 0. As body inverts |.X| will fall
859 // and .Z will go // negative. Similar for tilt and |.Y|. .X and .Y must be
860 // modulated to prevent a stable inverted body.
861
862 // Error is 0 (no error) to +/- 2 (max error)
863 if (verticalError.Z < 0.0f)
864 {
865 verticalError.X = 2.0f - verticalError.X;
866 verticalError.Y = 2.0f - verticalError.Y;
867 }
868 // scale it by VAservo (timestep and timescale)
869 verticalError = verticalError * VAservo;
870 1075
871 // As the body rotates around the X axis, then verticalError.Y increases; Rotated around Y 1076 // ==================================================================
872 // then .X increases, so change Body angular velocity X based on Y, and Y based on X. 1077 m_lastVertAttractor = verticalAttractionContribution;
873 // Z is not changed.
874 vertattr.X = verticalError.Y;
875 vertattr.Y = - verticalError.X;
876 vertattr.Z = 0f;
877 1078
878 // scaling appears better usingsquare-law 1079 m_lastAngularVelocity = angularMotorContribution
879 Vector3 angularVelocity = Prim.ForceRotationalVelocity; 1080 + verticalAttractionContribution
880 float bounce = 1.0f - (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency); 1081 + deflectionContribution
881 vertattr.X += bounce * angularVelocity.X; 1082 + bankingContribution;
882 vertattr.Y += bounce * angularVelocity.Y;
883
884 VDetailLog("{0},MoveAngular,verticalAttraction,VAservo={1},effic={2},verticalError={3},bounce={4},vertattr={5}",
885 Prim.LocalID, VAservo, m_verticalAttractionEfficiency, verticalError, bounce, vertattr);
886
887 }
888 #endregion // Vertical attactor
889 1083
890 #region Deflection 1084 // ==================================================================
891 1085 // Apply the correction velocity.
892 if (m_angularDeflectionEfficiency != 0) 1086 // TODO: Should this be applied as an angular force (torque)?
1087 if (!m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f))
893 { 1088 {
894 // Compute a scaled vector that points in the preferred axis (X direction) 1089 VehicleRotationalVelocity = m_lastAngularVelocity;
895 Vector3 scaledDefaultDirection =
896 new Vector3((pTimestep * 10 * (m_angularDeflectionEfficiency / m_angularDeflectionTimescale)), 0, 0);
897 // Adding the current vehicle orientation and reference frame displaces the orientation to the frame.
898 // Rotate the scaled default axix relative to the actual vehicle direction giving where it should point.
899 Vector3 preferredAxisOfMotion = scaledDefaultDirection * Quaternion.Add(Prim.ForceOrientation, m_referenceFrame);
900
901 // Scale by efficiency and timescale
902 deflection = (preferredAxisOfMotion * (m_angularDeflectionEfficiency) / m_angularDeflectionTimescale) * pTimestep;
903
904 VDetailLog("{0},MoveAngular,Deflection,perfAxis={1},deflection={2}",
905 Prim.LocalID, preferredAxisOfMotion, deflection);
906 // This deflection computation is not correct.
907 deflection = Vector3.Zero;
908 }
909
910 #endregion
911 1090
912 #region Banking 1091 VDetailLog("{0}, MoveAngular,done,nonZero,angMotorContrib={1},vertAttrContrib={2},bankContrib={3},deflectContrib={4},totalContrib={5}",
913 1092 Prim.LocalID,
914 if (m_bankingEfficiency != 0) 1093 angularMotorContribution, verticalAttractionContribution,
1094 bankingContribution, deflectionContribution,
1095 m_lastAngularVelocity
1096 );
1097 }
1098 else
915 { 1099 {
916 Vector3 dir = Vector3.One * Prim.ForceOrientation; 1100 // The vehicle is not adding anything angular wise.
917 float mult = (m_bankingMix*m_bankingMix)*-1*(m_bankingMix < 0 ? -1 : 1); 1101 VehicleRotationalVelocity = Vector3.Zero;
918 //Changes which way it banks in and out of turns 1102 VDetailLog("{0}, MoveAngular,done,zero", Prim.LocalID);
919
920 //Use the square of the efficiency, as it looks much more how SL banking works
921 float effSquared = (m_bankingEfficiency*m_bankingEfficiency);
922 if (m_bankingEfficiency < 0)
923 effSquared *= -1; //Keep the negative!
924
925 float mix = Math.Abs(m_bankingMix);
926 if (m_angularMotorVelocity.X == 0)
927 {
928 /*if (!parent.Orientation.ApproxEquals(this.m_referenceFrame, 0.25f))
929 {
930 Vector3 axisAngle;
931 float angle;
932 parent.Orientation.GetAxisAngle(out axisAngle, out angle);
933 Vector3 rotatedVel = parent.Velocity * parent.Orientation;
934 if ((rotatedVel.X < 0 && axisAngle.Y > 0) || (rotatedVel.X > 0 && axisAngle.Y < 0))
935 m_angularMotorVelocity.X += (effSquared * (mult * mix)) * (1f) * 10;
936 else
937 m_angularMotorVelocity.X += (effSquared * (mult * mix)) * (-1f) * 10;
938 }*/
939 }
940 else
941 banking.Z += (effSquared*(mult*mix))*(m_angularMotorVelocity.X) * 4;
942 if (!Prim.IsColliding && Math.Abs(m_angularMotorVelocity.X) > mix)
943 //If they are colliding, we probably shouldn't shove the prim around... probably
944 {
945 float angVelZ = m_angularMotorVelocity.X*-1;
946 /*if(angVelZ > mix)
947 angVelZ = mix;
948 else if(angVelZ < -mix)
949 angVelZ = -mix;*/
950 //This controls how fast and how far the banking occurs
951 Vector3 bankingRot = new Vector3(angVelZ*(effSquared*mult), 0, 0);
952 if (bankingRot.X > 3)
953 bankingRot.X = 3;
954 else if (bankingRot.X < -3)
955 bankingRot.X = -3;
956 bankingRot *= Prim.ForceOrientation;
957 banking += bankingRot;
958 }
959 m_angularMotorVelocity.X *= m_bankingEfficiency == 1 ? 0.0f : 1 - m_bankingEfficiency;
960 VDetailLog("{0},MoveAngular,Banking,bEff={1},angMotVel={2},banking={3}",
961 Prim.LocalID, m_bankingEfficiency, m_angularMotorVelocity, banking);
962 } 1103 }
963 1104
964 #endregion 1105 // ==================================================================
965
966 m_lastVertAttractor = vertattr;
967
968 // Sum velocities
969 m_lastAngularVelocity = m_angularMotorVelocity + vertattr + banking + deflection;
970
971 #region Linear Motor Offset
972
973 //Offset section 1106 //Offset section
974 if (m_linearMotorOffset != Vector3.Zero) 1107 if (m_linearMotorOffset != Vector3.Zero)
975 { 1108 {
@@ -985,8 +1118,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin
985 // 1118 //
986 // The torque created is the linear velocity crossed with the offset 1119 // The torque created is the linear velocity crossed with the offset
987 1120
988 // NOTE: this computation does should be in the linear section 1121 // TODO: this computation should be in the linear section
989 // because there we know the impulse being applied. 1122 // because that is where we know the impulse being applied.
990 Vector3 torqueFromOffset = Vector3.Zero; 1123 Vector3 torqueFromOffset = Vector3.Zero;
991 // torqueFromOffset = Vector3.Cross(m_linearMotorOffset, appliedImpulse); 1124 // torqueFromOffset = Vector3.Cross(m_linearMotorOffset, appliedImpulse);
992 if (float.IsNaN(torqueFromOffset.X)) 1125 if (float.IsNaN(torqueFromOffset.X))
@@ -995,47 +1128,184 @@ namespace OpenSim.Region.Physics.BulletSPlugin
995 torqueFromOffset.Y = 0; 1128 torqueFromOffset.Y = 0;
996 if (float.IsNaN(torqueFromOffset.Z)) 1129 if (float.IsNaN(torqueFromOffset.Z))
997 torqueFromOffset.Z = 0; 1130 torqueFromOffset.Z = 0;
998 torqueFromOffset *= m_vehicleMass; 1131
999 Prim.ApplyTorqueImpulse(torqueFromOffset, true); 1132 VehicleAddAngularForce(torqueFromOffset * m_vehicleMass);
1000 VDetailLog("{0},BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", Prim.LocalID, torqueFromOffset); 1133 VDetailLog("{0}, BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", Prim.LocalID, torqueFromOffset);
1001 } 1134 }
1002 1135
1003 #endregion 1136 }
1137 // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
1138 // Some vehicles, like boats, should always keep their up-side up. This can be done by
1139 // enabling the "vertical attractor" behavior that springs the vehicle's local z-axis to
1140 // the world z-axis (a.k.a. "up"). To take advantage of this feature you would set the
1141 // VEHICLE_VERTICAL_ATTRACTION_TIMESCALE to control the period of the spring frequency,
1142 // and then set the VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY to control the damping. An
1143 // efficiency of 0.0 will cause the spring to wobble around its equilibrium, while an
1144 // efficiency of 1.0 will cause the spring to reach its equilibrium with exponential decay.
1145 public Vector3 ComputeAngularVerticalAttraction()
1146 {
1147 Vector3 ret = Vector3.Zero;
1004 1148
1005 if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0) 1149 // If vertical attaction timescale is reasonable
1150 if (m_verticalAttractionTimescale < m_verticalAttractionCutoff)
1006 { 1151 {
1007 m_lastAngularVelocity.X = 0; 1152 // Take a vector pointing up and convert it from world to vehicle relative coords.
1008 m_lastAngularVelocity.Y = 0; 1153 Vector3 verticalError = Vector3.UnitZ * VehicleOrientation;
1009 VDetailLog("{0},MoveAngular,noDeflectionUp,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity); 1154
1155 // If vertical attraction correction is needed, the vector that was pointing up (UnitZ)
1156 // is now:
1157 // leaning to one side: rotated around the X axis with the Y value going
1158 // from zero (nearly straight up) to one (completely to the side)) or
1159 // leaning front-to-back: rotated around the Y axis with the value of X being between
1160 // zero and one.
1161 // The value of Z is how far the rotation is off with 1 meaning none and 0 being 90 degrees.
1162
1163 // Y error means needed rotation around X axis and visa versa.
1164 // Since the error goes from zero to one, the asin is the corresponding angle.
1165 ret.X = (float)Math.Asin(verticalError.Y);
1166 // (Tilt forward (positive X) needs to tilt back (rotate negative) around Y axis.)
1167 ret.Y = -(float)Math.Asin(verticalError.X);
1168
1169 // If verticalError.Z is negative, the vehicle is upside down. Add additional push.
1170 if (verticalError.Z < 0f)
1171 {
1172 ret.X += PIOverFour;
1173 ret.Y += PIOverFour;
1174 }
1175
1176 // 'ret' is now the necessary velocity to correct tilt in one second.
1177 // Correction happens over a number of seconds.
1178 Vector3 unscaledContrib = ret;
1179 ret /= m_verticalAttractionTimescale;
1180
1181 VDetailLog("{0}, MoveAngular,verticalAttraction,,verticalError={1},unscaled={2},eff={3},ts={4},vertAttr={5}",
1182 Prim.LocalID, verticalError, unscaledContrib, m_verticalAttractionEfficiency, m_verticalAttractionTimescale, ret);
1010 } 1183 }
1184 return ret;
1185 }
1186
1187 // Return the angular correction to correct the direction the vehicle is pointing to be
1188 // the direction is should want to be pointing.
1189 // The vehicle is moving in some direction and correct its orientation to it is pointing
1190 // in that direction.
1191 // TODO: implement reference frame.
1192 public Vector3 ComputeAngularDeflection()
1193 {
1194 Vector3 ret = Vector3.Zero;
1195 return ret; // DEBUG DEBUG DEBUG
1196 // Disable angular deflection for the moment.
1197 // Since angularMotorUp and angularDeflection are computed independently, they will calculate
1198 // approximately the same X or Y correction. When added together (when contributions are combined)
1199 // this creates an over-correction and then wabbling as the target is overshot.
1200 // TODO: rethink how the different correction computations inter-relate.
1011 1201
1012 if (m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f)) 1202 if (m_angularDeflectionEfficiency != 0)
1013 { 1203 {
1014 m_lastAngularVelocity = Vector3.Zero; // Reduce small value to zero. 1204 // The direction the vehicle is moving
1015 Prim.ZeroAngularMotion(true); 1205 Vector3 movingDirection = VehicleVelocity;
1016 VDetailLog("{0},MoveAngular,zeroAngularMotion,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity); 1206 movingDirection.Normalize();
1207
1208 // The direction the vehicle is pointing
1209 Vector3 pointingDirection = Vector3.UnitX * VehicleOrientation;
1210 pointingDirection.Normalize();
1211
1212 // The difference between what is and what should be.
1213 Vector3 deflectionError = movingDirection - pointingDirection;
1214
1215 // Don't try to correct very large errors (not our job)
1216 if (Math.Abs(deflectionError.X) > PIOverFour) deflectionError.X = 0f;
1217 if (Math.Abs(deflectionError.Y) > PIOverFour) deflectionError.Y = 0f;
1218 if (Math.Abs(deflectionError.Z) > PIOverFour) deflectionError.Z = 0f;
1219
1220 // ret = m_angularDeflectionCorrectionMotor(1f, deflectionError);
1221
1222 // Scale the correction by recovery timescale and efficiency
1223 ret = (-deflectionError) * m_angularDeflectionEfficiency;
1224 ret /= m_angularDeflectionTimescale;
1225
1226 VDetailLog("{0}, MoveAngular,Deflection,movingDir={1},pointingDir={2},deflectError={3},ret={4}",
1227 Prim.LocalID, movingDirection, pointingDirection, deflectionError, ret);
1228 VDetailLog("{0}, MoveAngular,Deflection,fwdSpd={1},defEff={2},defTS={3}",
1229 Prim.LocalID, VehicleForwardSpeed, m_angularDeflectionEfficiency, m_angularDeflectionTimescale);
1017 } 1230 }
1018 else 1231 return ret;
1232 }
1233
1234 // Return an angular change to rotate the vehicle around the Z axis when the vehicle
1235 // is tipped around the X axis.
1236 // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
1237 // The vertical attractor feature must be enabled in order for the banking behavior to
1238 // function. The way banking works is this: a rotation around the vehicle's roll-axis will
1239 // produce a angular velocity around the yaw-axis, causing the vehicle to turn. The magnitude
1240 // of the yaw effect will be proportional to the
1241 // VEHICLE_BANKING_EFFICIENCY, the angle of the roll rotation, and sometimes the vehicle's
1242 // velocity along its preferred axis of motion.
1243 // The VEHICLE_BANKING_EFFICIENCY can vary between -1 and +1. When it is positive then any
1244 // positive rotation (by the right-hand rule) about the roll-axis will effect a
1245 // (negative) torque around the yaw-axis, making it turn to the right--that is the
1246 // vehicle will lean into the turn, which is how real airplanes and motorcycle's work.
1247 // Negating the banking coefficient will make it so that the vehicle leans to the
1248 // outside of the turn (not very "physical" but might allow interesting vehicles so why not?).
1249 // The VEHICLE_BANKING_MIX is a fake (i.e. non-physical) parameter that is useful for making
1250 // banking vehicles do what you want rather than what the laws of physics allow.
1251 // For example, consider a real motorcycle...it must be moving forward in order for
1252 // it to turn while banking, however video-game motorcycles are often configured
1253 // to turn in place when at a dead stop--because they are often easier to control
1254 // that way using the limited interface of the keyboard or game controller. The
1255 // VEHICLE_BANKING_MIX enables combinations of both realistic and non-realistic
1256 // banking by functioning as a slider between a banking that is correspondingly
1257 // totally static (0.0) and totally dynamic (1.0). By "static" we mean that the
1258 // banking effect depends only on the vehicle's rotation about its roll-axis compared
1259 // to "dynamic" where the banking is also proportional to its velocity along its
1260 // roll-axis. Finding the best value of the "mixture" will probably require trial and error.
1261 // The time it takes for the banking behavior to defeat a preexisting angular velocity about the
1262 // world z-axis is determined by the VEHICLE_BANKING_TIMESCALE. So if you want the vehicle to
1263 // bank quickly then give it a banking timescale of about a second or less, otherwise you can
1264 // make a sluggish vehicle by giving it a timescale of several seconds.
1265 public Vector3 ComputeAngularBanking()
1266 {
1267 Vector3 ret = Vector3.Zero;
1268
1269 if (m_bankingEfficiency != 0 && m_verticalAttractionTimescale < m_verticalAttractionCutoff)
1019 { 1270 {
1020 // Apply to the body. 1271 // This works by rotating a unit vector to the orientation of the vehicle. The
1021 // The above calculates the absolute angular velocity needed. Angular velocity is massless. 1272 // roll (tilt) will be Y component of a tilting Z vector (zero for no tilt
1022 // Since we are stuffing the angular velocity directly into the object, the computed 1273 // up to one for full over).
1023 // velocity needs to be scaled by the timestep. 1274 Vector3 rollComponents = Vector3.UnitZ * VehicleOrientation;
1024 Vector3 applyAngularForce = ((m_lastAngularVelocity * pTimestep) - Prim.ForceRotationalVelocity); 1275
1025 Prim.ForceRotationalVelocity = applyAngularForce; 1276 // Figure out the yaw value for this much roll.
1026 1277 float turnComponent = rollComponents.Y * rollComponents.Y * m_bankingEfficiency;
1027 // Decay the angular movement for next time 1278 // Keep the sign
1028 Vector3 decayamount = (Vector3.One / m_angularFrictionTimescale) * pTimestep; 1279 if (rollComponents.Y < 0f)
1029 m_lastAngularVelocity *= Vector3.One - decayamount; 1280 turnComponent = -turnComponent;
1030 1281
1031 VDetailLog("{0},MoveAngular,done,newRotVel={1},decay={2},lastAngular={3}", 1282 // TODO: there must be a better computation of the banking force.
1032 Prim.LocalID, applyAngularForce, decayamount, m_lastAngularVelocity); 1283 float bankingTurnForce = turnComponent;
1284
1285 // actual error = static turn error + dynamic turn error
1286 float mixedBankingError = bankingTurnForce * (1f - m_bankingMix) + bankingTurnForce * m_bankingMix * VehicleForwardSpeed;
1287 // TODO: the banking effect should not go to infinity but what to limit it to?
1288 mixedBankingError = ClampInRange(-20f, mixedBankingError, 20f);
1289
1290 // Build the force vector to change rotation from what it is to what it should be
1291 ret.Z = -mixedBankingError;
1292
1293 // Don't do it all at once.
1294 ret /= m_bankingTimescale;
1295
1296 VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},turnComp={3},bankErr={4},mixedBankErr={5},ret={6}",
1297 Prim.LocalID, rollComponents, VehicleForwardSpeed, turnComponent, bankingTurnForce, mixedBankingError, ret);
1033 } 1298 }
1034 } //end MoveAngular 1299 return ret;
1300 }
1035 1301
1302 // This is from previous instantiations of XXXDynamics.cs.
1303 // Applies roll reference frame.
1304 // TODO: is this the right way to separate the code to do this operation?
1305 // Should this be in MoveAngular()?
1036 internal void LimitRotation(float timestep) 1306 internal void LimitRotation(float timestep)
1037 { 1307 {
1038 Quaternion rotq = Prim.ForceOrientation; 1308 Quaternion rotq = VehicleOrientation;
1039 Quaternion m_rot = rotq; 1309 Quaternion m_rot = rotq;
1040 if (m_RollreferenceFrame != Quaternion.Identity) 1310 if (m_RollreferenceFrame != Quaternion.Identity)
1041 { 1311 {
@@ -1063,12 +1333,18 @@ namespace OpenSim.Region.Physics.BulletSPlugin
1063 } 1333 }
1064 if (rotq != m_rot) 1334 if (rotq != m_rot)
1065 { 1335 {
1066 Prim.ForceOrientation = m_rot; 1336 VehicleOrientation = m_rot;
1067 VDetailLog("{0},LimitRotation,done,orig={1},new={2}", Prim.LocalID, rotq, m_rot); 1337 VDetailLog("{0}, LimitRotation,done,orig={1},new={2}", Prim.LocalID, rotq, m_rot);
1068 } 1338 }
1069 1339
1070 } 1340 }
1071 1341
1342 private float ClampInRange(float low, float val, float high)
1343 {
1344 return Math.Max(low, Math.Min(val, high));
1345 // return Utils.Clamp(val, low, high);
1346 }
1347
1072 // Invoke the detailed logger and output something if it's enabled. 1348 // Invoke the detailed logger and output something if it's enabled.
1073 private void VDetailLog(string msg, params Object[] args) 1349 private void VDetailLog(string msg, params Object[] args)
1074 { 1350 {
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
index 0df4310..2017fa5 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
@@ -32,6 +32,15 @@ using OMV = OpenMetaverse;
32 32
33namespace OpenSim.Region.Physics.BulletSPlugin 33namespace OpenSim.Region.Physics.BulletSPlugin
34{ 34{
35
36// A BSPrim can get individual information about its linkedness attached
37// to it through an instance of a subclass of LinksetInfo.
38// Each type of linkset will define the information needed for its type.
39public abstract class BSLinksetInfo
40{
41 public virtual void Clear() { }
42}
43
35public abstract class BSLinkset 44public abstract class BSLinkset
36{ 45{
37 // private static string LogHeader = "[BULLETSIM LINKSET]"; 46 // private static string LogHeader = "[BULLETSIM LINKSET]";
@@ -87,13 +96,6 @@ public abstract class BSLinkset
87 return BSPhysicsShapeType.SHAPE_UNKNOWN; 96 return BSPhysicsShapeType.SHAPE_UNKNOWN;
88 } 97 }
89 98
90 // Linksets move around the children so the linkset might need to compute the child position
91 public virtual OMV.Vector3 Position(BSPhysObject member)
92 { return member.RawPosition; }
93 public virtual OMV.Quaternion Orientation(BSPhysObject member)
94 { return member.RawOrientation; }
95 // TODO: does this need to be done for Velocity and RotationalVelocityy?
96
97 // We keep the prim's mass in the linkset structure since it could be dependent on other prims 99 // We keep the prim's mass in the linkset structure since it could be dependent on other prims
98 protected float m_mass; 100 protected float m_mass;
99 public float LinksetMass 101 public float LinksetMass
@@ -116,7 +118,7 @@ public abstract class BSLinkset
116 get { return ComputeLinksetGeometricCenter(); } 118 get { return ComputeLinksetGeometricCenter(); }
117 } 119 }
118 120
119 protected void Initialize(BSScene scene, BSPhysObject parent) 121 protected BSLinkset(BSScene scene, BSPhysObject parent)
120 { 122 {
121 // A simple linkset of one (no children) 123 // A simple linkset of one (no children)
122 LinksetID = m_nextLinksetID++; 124 LinksetID = m_nextLinksetID++;
@@ -127,6 +129,7 @@ public abstract class BSLinkset
127 LinksetRoot = parent; 129 LinksetRoot = parent;
128 m_children = new HashSet<BSPhysObject>(); 130 m_children = new HashSet<BSPhysObject>();
129 m_mass = parent.RawMass; 131 m_mass = parent.RawMass;
132 Rebuilding = false;
130 } 133 }
131 134
132 // Link to a linkset where the child knows the parent. 135 // Link to a linkset where the child knows the parent.
@@ -219,7 +222,7 @@ public abstract class BSLinkset
219 // I am the root of a linkset and a new child is being added 222 // I am the root of a linkset and a new child is being added
220 // Called while LinkActivity is locked. 223 // Called while LinkActivity is locked.
221 protected abstract void AddChildToLinkset(BSPhysObject child); 224 protected abstract void AddChildToLinkset(BSPhysObject child);
222 225
223 // I am the root of a linkset and one of my children is being removed. 226 // I am the root of a linkset and one of my children is being removed.
224 // Safe to call even if the child is not really in my linkset. 227 // Safe to call even if the child is not really in my linkset.
225 protected abstract void RemoveChildFromLinkset(BSPhysObject child); 228 protected abstract void RemoveChildFromLinkset(BSPhysObject child);
@@ -229,6 +232,10 @@ public abstract class BSLinkset
229 // May be called at runtime or taint-time. 232 // May be called at runtime or taint-time.
230 public abstract void Refresh(BSPhysObject requestor); 233 public abstract void Refresh(BSPhysObject requestor);
231 234
235 // Flag denoting the linkset is in the process of being rebuilt.
236 // Used to know not the schedule a rebuild in the middle of a rebuild.
237 protected bool Rebuilding { get; set; }
238
232 // The object is going dynamic (physical). Do any setup necessary 239 // The object is going dynamic (physical). Do any setup necessary
233 // for a dynamic linkset. 240 // for a dynamic linkset.
234 // Only the state of the passed object can be modified. The rest of the linkset 241 // Only the state of the passed object can be modified. The rest of the linkset
@@ -245,8 +252,9 @@ public abstract class BSLinkset
245 252
246 // Called when a parameter update comes from the physics engine for any object 253 // Called when a parameter update comes from the physics engine for any object
247 // of the linkset is received. 254 // of the linkset is received.
255 // Passed flag is update came from physics engine (true) or the user (false).
248 // Called at taint-time!! 256 // Called at taint-time!!
249 public abstract void UpdateProperties(BSPhysObject physObject); 257 public abstract void UpdateProperties(BSPhysObject physObject, bool physicalUpdate);
250 258
251 // Routine used when rebuilding the body of the root of the linkset 259 // Routine used when rebuilding the body of the root of the linkset
252 // Destroy all the constraints have have been made to root. 260 // Destroy all the constraints have have been made to root.
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs
index b9c2cf9..2a7b72c 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs
@@ -28,22 +28,54 @@ using System;
28using System.Collections.Generic; 28using System.Collections.Generic;
29using System.Text; 29using System.Text;
30 30
31using OpenSim.Framework;
32
31using OMV = OpenMetaverse; 33using OMV = OpenMetaverse;
32 34
33namespace OpenSim.Region.Physics.BulletSPlugin 35namespace OpenSim.Region.Physics.BulletSPlugin
34{ 36{
37
38// When a child is linked, the relationship position of the child to the parent
39// is remembered so the child's world position can be recomputed when it is
40// removed from the linkset.
41sealed class BSLinksetCompoundInfo : BSLinksetInfo
42{
43 public OMV.Vector3 OffsetPos;
44 public OMV.Quaternion OffsetRot;
45 public BSLinksetCompoundInfo(OMV.Vector3 p, OMV.Quaternion r)
46 {
47 OffsetPos = p;
48 OffsetRot = r;
49 }
50 public override void Clear()
51 {
52 OffsetPos = OMV.Vector3.Zero;
53 OffsetRot = OMV.Quaternion.Identity;
54 }
55 public override string ToString()
56 {
57 StringBuilder buff = new StringBuilder();
58 buff.Append("<p=");
59 buff.Append(OffsetPos.ToString());
60 buff.Append(",r=");
61 buff.Append(OffsetRot.ToString());
62 buff.Append(">");
63 return buff.ToString();
64 }
65};
66
35public sealed class BSLinksetCompound : BSLinkset 67public sealed class BSLinksetCompound : BSLinkset
36{ 68{
37 private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]"; 69 private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]";
38 70
39 public BSLinksetCompound(BSScene scene, BSPhysObject parent) 71 public BSLinksetCompound(BSScene scene, BSPhysObject parent) : base(scene, parent)
40 { 72 {
41 base.Initialize(scene, parent);
42 } 73 }
43 74
44 // For compound implimented linksets, if there are children, use compound shape for the root. 75 // For compound implimented linksets, if there are children, use compound shape for the root.
45 public override BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor) 76 public override BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
46 { 77 {
78 // Returning 'unknown' means we don't have a preference.
47 BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN; 79 BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN;
48 if (IsRoot(requestor) && HasAnyChildren) 80 if (IsRoot(requestor) && HasAnyChildren)
49 { 81 {
@@ -55,23 +87,27 @@ public sealed class BSLinksetCompound : BSLinkset
55 87
56 // When physical properties are changed the linkset needs to recalculate 88 // When physical properties are changed the linkset needs to recalculate
57 // its internal properties. 89 // its internal properties.
58 // This is queued in the 'post taint' queue so the
59 // refresh will happen once after all the other taints are applied.
60 public override void Refresh(BSPhysObject requestor) 90 public override void Refresh(BSPhysObject requestor)
61 { 91 {
62 // External request for Refresh (from BSPrim) is not necessary 92 // Something changed so do the rebuilding thing
63 // InternalRefresh(requestor); 93 // ScheduleRebuild();
64 } 94 }
65 95
66 private void InternalRefresh(BSPhysObject requestor) 96 // Schedule a refresh to happen after all the other taint processing.
97 private void ScheduleRebuild(BSPhysObject requestor)
67 { 98 {
68 DetailLog("{0},BSLinksetCompound.Refresh,schedulingRefresh,requestor={1}", LinksetRoot.LocalID, requestor.LocalID); 99 DetailLog("{0},BSLinksetCompound.Refresh,schedulingRefresh,rebuilding={1}",
69 // Queue to happen after all the other taint processing 100 requestor.LocalID, Rebuilding);
70 PhysicsScene.PostTaintObject("BSLinksetCompound.Refresh", requestor.LocalID, delegate() 101 // When rebuilding, it is possible to set properties that would normally require a rebuild.
102 // If already rebuilding, don't request another rebuild.
103 if (!Rebuilding)
71 { 104 {
72 if (IsRoot(requestor) && HasAnyChildren) 105 PhysicsScene.PostTaintObject("BSLinksetCompound.Refresh", LinksetRoot.LocalID, delegate()
73 RecomputeLinksetCompound(); 106 {
74 }); 107 if (HasAnyChildren)
108 RecomputeLinksetCompound();
109 });
110 }
75 } 111 }
76 112
77 // The object is going dynamic (physical). Do any setup necessary 113 // The object is going dynamic (physical). Do any setup necessary
@@ -84,12 +120,23 @@ public sealed class BSLinksetCompound : BSLinkset
84 { 120 {
85 bool ret = false; 121 bool ret = false;
86 DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child)); 122 DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child));
87 if (!IsRoot(child)) 123 if (IsRoot(child))
124 {
125 // The root is going dynamic. Make sure mass is properly set.
126 m_mass = ComputeLinksetMass();
127 ScheduleRebuild(LinksetRoot);
128 }
129 else
88 { 130 {
89 // Physical children are removed from the world as the shape ofthe root compound 131 // The origional prims are removed from the world as the shape of the root compound
90 // shape takes over. 132 // shape takes over.
91 BulletSimAPI.AddToCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); 133 BulletSimAPI.AddToCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
92 BulletSimAPI.ForceActivationState2(child.PhysBody.ptr, ActivationState.DISABLE_SIMULATION); 134 BulletSimAPI.ForceActivationState2(child.PhysBody.ptr, ActivationState.DISABLE_SIMULATION);
135 // We don't want collisions from the old linkset children.
136 BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
137
138 child.PhysBody.collisionType = CollisionType.LinksetChild;
139
93 ret = true; 140 ret = true;
94 } 141 }
95 return ret; 142 return ret;
@@ -104,33 +151,39 @@ public sealed class BSLinksetCompound : BSLinkset
104 { 151 {
105 bool ret = false; 152 bool ret = false;
106 DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child)); 153 DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child));
107 if (!IsRoot(child)) 154 if (IsRoot(child))
155 {
156 ScheduleRebuild(LinksetRoot);
157 }
158 else
108 { 159 {
109 // The non-physical children can come back to life. 160 // The non-physical children can come back to life.
110 BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); 161 BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
111 // Don't force activation so setting of DISABLE_SIMULATION can stay. 162
163 child.PhysBody.collisionType = CollisionType.LinksetChild;
164
165 // Don't force activation so setting of DISABLE_SIMULATION can stay if used.
112 BulletSimAPI.Activate2(child.PhysBody.ptr, false); 166 BulletSimAPI.Activate2(child.PhysBody.ptr, false);
113 ret = true; 167 ret = true;
114 } 168 }
115 return ret; 169 return ret;
116 } 170 }
117 171
118 // Called at taint-time!! 172 public override void UpdateProperties(BSPhysObject updated, bool physicalUpdate)
119 public override void UpdateProperties(BSPhysObject updated)
120 { 173 {
121 // Nothing to do for constraints on property updates 174 // The user moving a child around requires the rebuilding of the linkset compound shape
122 } 175 // One problem is this happens when a border is crossed -- the simulator implementation
123 176 // is to store the position into the group which causes the move of the object
124 // The children move around in relationship to the root. 177 // but it also means all the child positions get updated.
125 // Just grab the current values of wherever it is right now. 178 // What would cause an unnecessary rebuild so we make sure the linkset is in a
126 public override OMV.Vector3 Position(BSPhysObject member) 179 // region before bothering to do a rebuild.
127 { 180 if (!IsRoot(updated)
128 return BulletSimAPI.GetPosition2(member.PhysBody.ptr); 181 && !physicalUpdate
129 } 182 && PhysicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition))
130 183 {
131 public override OMV.Quaternion Orientation(BSPhysObject member) 184 updated.LinksetInfo = null;
132 { 185 ScheduleRebuild(updated);
133 return BulletSimAPI.GetOrientation2(member.PhysBody.ptr); 186 }
134 } 187 }
135 188
136 // Routine called when rebuilding the body of some member of the linkset. 189 // Routine called when rebuilding the body of some member of the linkset.
@@ -146,20 +199,58 @@ public sealed class BSLinksetCompound : BSLinkset
146 199
147 if (!IsRoot(child)) 200 if (!IsRoot(child))
148 { 201 {
149 // Cause the current shape to be freed and the new one to be built. 202 // Because it is a convenient time, recompute child world position and rotation based on
150 InternalRefresh(LinksetRoot); 203 // its position in the linkset.
151 ret = true; 204 RecomputeChildWorldPosition(child, true);
152 } 205 }
153 206
207 // Cannot schedule a refresh/rebuild here because this routine is called when
208 // the linkset is being rebuilt.
209 // InternalRefresh(LinksetRoot);
210
154 return ret; 211 return ret;
155 } 212 }
156 213
157 // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true', 214 // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
158 // this routine will restore the removed constraints. 215 // this routine will restore the removed constraints.
159 // Called at taint-time!! 216 // Called at taint-time!!
160 public override void RestoreBodyDependencies(BSPrim child) 217 public override void RestoreBodyDependencies(BSPrim child)
161 { 218 {
162 // The Refresh operation queued by RemoveBodyDependencies() will build any missing constraints. 219 }
220
221 // When the linkset is built, the child shape is added to the compound shape relative to the
222 // root shape. The linkset then moves around but this does not move the actual child
223 // prim. The child prim's location must be recomputed based on the location of the root shape.
224 private void RecomputeChildWorldPosition(BSPhysObject child, bool inTaintTime)
225 {
226 BSLinksetCompoundInfo lci = child.LinksetInfo as BSLinksetCompoundInfo;
227 if (lci != null)
228 {
229 if (inTaintTime)
230 {
231 OMV.Vector3 oldPos = child.RawPosition;
232 child.ForcePosition = LinksetRoot.RawPosition + lci.OffsetPos;
233 child.ForceOrientation = LinksetRoot.RawOrientation * lci.OffsetRot;
234 DetailLog("{0},BSLinksetCompound.RecomputeChildWorldPosition,oldPos={1},lci={2},newPos={3}",
235 child.LocalID, oldPos, lci, child.RawPosition);
236 }
237 else
238 {
239 // TaintedObject is not used here so the raw position is set now and not at taint-time.
240 child.Position = LinksetRoot.RawPosition + lci.OffsetPos;
241 child.Orientation = LinksetRoot.RawOrientation * lci.OffsetRot;
242 }
243 }
244 else
245 {
246 // This happens when children have been added to the linkset but the linkset
247 // has not been constructed yet. So like, at taint time, adding children to a linkset
248 // and then changing properties of the children (makePhysical, for instance)
249 // but the post-print action of actually rebuilding the linkset has not yet happened.
250 // PhysicsScene.Logger.WarnFormat("{0} Restoring linkset child position failed because of no relative position computed. ID={1}",
251 // LogHeader, child.LocalID);
252 DetailLog("{0},BSLinksetCompound.recomputeChildWorldPosition,noRelativePositonInfo", child.LocalID);
253 }
163 } 254 }
164 255
165 // ================================================================ 256 // ================================================================
@@ -174,14 +265,14 @@ public sealed class BSLinksetCompound : BSLinkset
174 265
175 DetailLog("{0},BSLinksetCompound.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID); 266 DetailLog("{0},BSLinksetCompound.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
176 267
177 // Cause constraints and assorted properties to be recomputed before the next simulation step. 268 // Rebuild the compound shape with the new child shape included
178 InternalRefresh(LinksetRoot); 269 ScheduleRebuild(child);
179 } 270 }
180 return; 271 return;
181 } 272 }
182 273
183 // Remove the specified child from the linkset. 274 // Remove the specified child from the linkset.
184 // Safe to call even if the child is not really in my linkset. 275 // Safe to call even if the child is not really in the linkset.
185 protected override void RemoveChildFromLinkset(BSPhysObject child) 276 protected override void RemoveChildFromLinkset(BSPhysObject child)
186 { 277 {
187 if (m_children.Remove(child)) 278 if (m_children.Remove(child))
@@ -192,6 +283,7 @@ public sealed class BSLinksetCompound : BSLinkset
192 child.LocalID, child.PhysBody.ptr.ToString("X")); 283 child.LocalID, child.PhysBody.ptr.ToString("X"));
193 284
194 // Cause the child's body to be rebuilt and thus restored to normal operation 285 // Cause the child's body to be rebuilt and thus restored to normal operation
286 RecomputeChildWorldPosition(child, false);
195 child.ForceBodyShapeRebuild(false); 287 child.ForceBodyShapeRebuild(false);
196 288
197 if (!HasAnyChildren) 289 if (!HasAnyChildren)
@@ -201,8 +293,8 @@ public sealed class BSLinksetCompound : BSLinkset
201 } 293 }
202 else 294 else
203 { 295 {
204 // Schedule a rebuild of the linkset before the next simulation tick. 296 // Rebuild the compound shape with the child removed
205 InternalRefresh(LinksetRoot); 297 ScheduleRebuild(child);
206 } 298 }
207 } 299 }
208 return; 300 return;
@@ -215,54 +307,83 @@ public sealed class BSLinksetCompound : BSLinkset
215 // Called at taint time!! 307 // Called at taint time!!
216 private void RecomputeLinksetCompound() 308 private void RecomputeLinksetCompound()
217 { 309 {
218 // Cause the root shape to be rebuilt as a compound object with just the root in it 310 try
219 LinksetRoot.ForceBodyShapeRebuild(true); 311 {
312 // Suppress rebuilding while rebuilding
313 Rebuilding = true;
220 314
221 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}", 315 // Cause the root shape to be rebuilt as a compound object with just the root in it
222 LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren); 316 LinksetRoot.ForceBodyShapeRebuild(true);
223 317
224 // Add a shape for each of the other children in the linkset 318 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}",
225 ForEachMember(delegate(BSPhysObject cPrim) 319 LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren);
226 { 320
227 if (!IsRoot(cPrim)) 321 // Add a shape for each of the other children in the linkset
322 ForEachMember(delegate(BSPhysObject cPrim)
228 { 323 {
229 // Each child position and rotation is given relative to the root. 324 if (!IsRoot(cPrim))
230 OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation); 325 {
231 OMV.Vector3 displacementPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation; 326 // Compute the displacement of the child from the root of the linkset.
232 OMV.Quaternion displacementRot = cPrim.RawOrientation * invRootOrientation; 327 // This info is saved in the child prim so the relationship does not
328 // change over time and the new child position can be computed
329 // when the linkset is being disassembled (the linkset may have moved).
330 BSLinksetCompoundInfo lci = cPrim.LinksetInfo as BSLinksetCompoundInfo;
331 if (lci == null)
332 {
333 // Each child position and rotation is given relative to the root.
334 OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation);
335 OMV.Vector3 displacementPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation;
336 OMV.Quaternion displacementRot = cPrim.RawOrientation * invRootOrientation;
337
338 // Save relative position for recomputing child's world position after moving linkset.
339 lci = new BSLinksetCompoundInfo(displacementPos, displacementRot);
340 cPrim.LinksetInfo = lci;
341 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,creatingRelPos,lci={1}", cPrim.LocalID, lci);
342 }
233 343
234 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addMemberToShape,mID={1},mShape={2},dispPos={3},dispRot={4}", 344 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addMemberToShape,mID={1},mShape={2},dispPos={3},dispRot={4}",
235 LinksetRoot.LocalID, cPrim.LocalID, cPrim.PhysShape, displacementPos, displacementRot); 345 LinksetRoot.LocalID, cPrim.LocalID, cPrim.PhysShape, lci.OffsetPos, lci.OffsetRot);
236 346
237 if (cPrim.PhysShape.isNativeShape) 347 if (cPrim.PhysShape.isNativeShape)
238 { 348 {
239 // Native shapes are not shared so we need to create a new one. 349 // A native shape is turning into a hull collision shape because native
240 // A mesh or hull is created because scale is not available on a native shape. 350 // shapes are not shared so we have to hullify it so it will be tracked
241 // (TODO: Bullet does have a btScaledCollisionShape. Can that be used?) 351 // and freed at the correct time. This also solves the scaling problem
242 BulletShape saveShape = cPrim.PhysShape; 352 // (native shapes scaled but hull/meshes are assumed to not be).
243 cPrim.PhysShape.ptr = IntPtr.Zero; // Don't let the create free the child's shape 353 // TODO: decide of the native shape can just be used in the compound shape.
244 PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null); 354 // Use call to CreateGeomNonSpecial().
245 BulletShape newShape = cPrim.PhysShape; 355 BulletShape saveShape = cPrim.PhysShape;
246 cPrim.PhysShape = saveShape; 356 cPrim.PhysShape.Clear(); // Don't let the create free the child's shape
247 BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, newShape.ptr, displacementPos, displacementRot); 357 // PhysicsScene.Shapes.CreateGeomNonSpecial(true, cPrim, null);
248 } 358 PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null);
249 else 359 BulletShape newShape = cPrim.PhysShape;
250 { 360 cPrim.PhysShape = saveShape;
251 // For the shared shapes (meshes and hulls), just use the shape in the child. 361 BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, newShape.ptr, lci.OffsetPos, lci.OffsetRot);
252 if (PhysicsScene.Shapes.ReferenceShape(cPrim.PhysShape)) 362 }
363 else
253 { 364 {
254 PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}", 365 // For the shared shapes (meshes and hulls), just use the shape in the child.
255 LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape); 366 // The reference count added here will be decremented when the compound shape
367 // is destroyed in BSShapeCollection (the child shapes are looped over and dereferenced).
368 if (PhysicsScene.Shapes.ReferenceShape(cPrim.PhysShape))
369 {
370 PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}",
371 LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape);
372 }
373 BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, cPrim.PhysShape.ptr, lci.OffsetPos, lci.OffsetRot);
256 } 374 }
257 BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, cPrim.PhysShape.ptr, displacementPos, displacementRot);
258 } 375 }
259 } 376 return false; // 'false' says to move onto the next child in the list
260 return false; // 'false' says to move onto the next child in the list 377 });
261 });
262 378
263 // With all of the linkset packed into the root prim, it has the mass of everyone. 379 // With all of the linkset packed into the root prim, it has the mass of everyone.
264 float linksetMass = LinksetMass; 380 float linksetMass = LinksetMass;
265 LinksetRoot.UpdatePhysicalMassProperties(linksetMass); 381 LinksetRoot.UpdatePhysicalMassProperties(linksetMass);
382 }
383 finally
384 {
385 Rebuilding = false;
386 }
266 387
267 BulletSimAPI.RecalculateCompoundShapeLocalAabb2(LinksetRoot.PhysShape.ptr); 388 BulletSimAPI.RecalculateCompoundShapeLocalAabb2(LinksetRoot.PhysShape.ptr);
268 389
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs
index c855fda..8c36c31 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs
@@ -36,9 +36,8 @@ public sealed class BSLinksetConstraints : BSLinkset
36{ 36{
37 // private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINTS]"; 37 // private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINTS]";
38 38
39 public BSLinksetConstraints(BSScene scene, BSPhysObject parent) 39 public BSLinksetConstraints(BSScene scene, BSPhysObject parent) : base(scene, parent)
40 { 40 {
41 base.Initialize(scene, parent);
42 } 41 }
43 42
44 // When physical properties are changed the linkset needs to recalculate 43 // When physical properties are changed the linkset needs to recalculate
@@ -79,23 +78,11 @@ public sealed class BSLinksetConstraints : BSLinkset
79 } 78 }
80 79
81 // Called at taint-time!! 80 // Called at taint-time!!
82 public override void UpdateProperties(BSPhysObject updated) 81 public override void UpdateProperties(BSPhysObject updated, bool inTaintTime)
83 { 82 {
84 // Nothing to do for constraints on property updates 83 // Nothing to do for constraints on property updates
85 } 84 }
86 85
87 // The children of the linkset are moved around by the constraints.
88 // Just grab the current values of wherever it is right now.
89 public override OMV.Vector3 Position(BSPhysObject member)
90 {
91 return BulletSimAPI.GetPosition2(member.PhysBody.ptr);
92 }
93
94 public override OMV.Quaternion Orientation(BSPhysObject member)
95 {
96 return BulletSimAPI.GetOrientation2(member.PhysBody.ptr);
97 }
98
99 // Routine called when rebuilding the body of some member of the linkset. 86 // Routine called when rebuilding the body of some member of the linkset.
100 // Destroy all the constraints have have been made to root and set 87 // Destroy all the constraints have have been made to root and set
101 // up to rebuild the constraints before the next simulation step. 88 // up to rebuild the constraints before the next simulation step.
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs b/OpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs
new file mode 100755
index 0000000..c113a43
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs
@@ -0,0 +1,200 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30using System.Reflection;
31using Nini.Config;
32
33namespace OpenSim.Region.Physics.BulletSPlugin
34{
35
36public struct MaterialAttributes
37{
38 // Material type values that correspond with definitions for LSL
39 public enum Material : int
40 {
41 Stone = 0,
42 Metal,
43 Glass,
44 Wood,
45 Flesh,
46 Plastic,
47 Rubber,
48 Light,
49 // Hereafter are BulletSim additions
50 Avatar,
51 NumberOfTypes // the count of types in the enum.
52 }
53
54 // Names must be in the order of the above enum.
55 // These names must coorespond to the lower case field names in the MaterialAttributes
56 // structure as reflection is used to select the field to put the value in.
57 public static readonly string[] MaterialAttribs = { "Density", "Friction", "Restitution"};
58
59 public MaterialAttributes(string t, float d, float f, float r)
60 {
61 type = t;
62 density = d;
63 friction = f;
64 restitution = r;
65 }
66 public string type;
67 public float density;
68 public float friction;
69 public float restitution;
70}
71
72public static class BSMaterials
73{
74 // Attributes for each material type
75 private static readonly MaterialAttributes[] Attributes;
76
77 // Map of material name to material type code
78 public static readonly Dictionary<string, MaterialAttributes.Material> MaterialMap;
79
80 static BSMaterials()
81 {
82 // Attribute sets for both the non-physical and physical instances of materials.
83 Attributes = new MaterialAttributes[(int)MaterialAttributes.Material.NumberOfTypes * 2];
84
85 // Map of name to type code.
86 MaterialMap = new Dictionary<string, MaterialAttributes.Material>();
87 MaterialMap.Add("Stone", MaterialAttributes.Material.Stone);
88 MaterialMap.Add("Metal", MaterialAttributes.Material.Metal);
89 MaterialMap.Add("Glass", MaterialAttributes.Material.Glass);
90 MaterialMap.Add("Wood", MaterialAttributes.Material.Wood);
91 MaterialMap.Add("Flesh", MaterialAttributes.Material.Flesh);
92 MaterialMap.Add("Plastic", MaterialAttributes.Material.Plastic);
93 MaterialMap.Add("Rubber", MaterialAttributes.Material.Rubber);
94 MaterialMap.Add("Light", MaterialAttributes.Material.Light);
95 MaterialMap.Add("Avatar", MaterialAttributes.Material.Avatar);
96 }
97
98 // This is where all the default material attributes are defined.
99 public static void InitializeFromDefaults(ConfigurationParameters parms)
100 {
101 // Values from http://wiki.secondlife.com/wiki/PRIM_MATERIAL
102 float dDensity = parms.defaultDensity;
103 float dFriction = parms.defaultFriction;
104 float dRestitution = parms.defaultRestitution;
105 Attributes[(int)MaterialAttributes.Material.Stone] =
106 new MaterialAttributes("stone",dDensity, 0.8f, 0.4f);
107 Attributes[(int)MaterialAttributes.Material.Metal] =
108 new MaterialAttributes("metal",dDensity, 0.3f, 0.4f);
109 Attributes[(int)MaterialAttributes.Material.Glass] =
110 new MaterialAttributes("glass",dDensity, 0.2f, 0.7f);
111 Attributes[(int)MaterialAttributes.Material.Wood] =
112 new MaterialAttributes("wood",dDensity, 0.6f, 0.5f);
113 Attributes[(int)MaterialAttributes.Material.Flesh] =
114 new MaterialAttributes("flesh",dDensity, 0.9f, 0.3f);
115 Attributes[(int)MaterialAttributes.Material.Plastic] =
116 new MaterialAttributes("plastic",dDensity, 0.4f, 0.7f);
117 Attributes[(int)MaterialAttributes.Material.Rubber] =
118 new MaterialAttributes("rubber",dDensity, 0.9f, 0.9f);
119 Attributes[(int)MaterialAttributes.Material.Light] =
120 new MaterialAttributes("light",dDensity, dFriction, dRestitution);
121 Attributes[(int)MaterialAttributes.Material.Avatar] =
122 new MaterialAttributes("avatar",60f, 0.2f, 0f);
123
124 Attributes[(int)MaterialAttributes.Material.Stone + (int)MaterialAttributes.Material.NumberOfTypes] =
125 new MaterialAttributes("stonePhysical",dDensity, 0.8f, 0.4f);
126 Attributes[(int)MaterialAttributes.Material.Metal + (int)MaterialAttributes.Material.NumberOfTypes] =
127 new MaterialAttributes("metalPhysical",dDensity, 0.8f, 0.4f);
128 Attributes[(int)MaterialAttributes.Material.Glass + (int)MaterialAttributes.Material.NumberOfTypes] =
129 new MaterialAttributes("glassPhysical",dDensity, 0.8f, 0.7f);
130 Attributes[(int)MaterialAttributes.Material.Wood + (int)MaterialAttributes.Material.NumberOfTypes] =
131 new MaterialAttributes("woodPhysical",dDensity, 0.8f, 0.5f);
132 Attributes[(int)MaterialAttributes.Material.Flesh + (int)MaterialAttributes.Material.NumberOfTypes] =
133 new MaterialAttributes("fleshPhysical",dDensity, 0.8f, 0.3f);
134 Attributes[(int)MaterialAttributes.Material.Plastic + (int)MaterialAttributes.Material.NumberOfTypes] =
135 new MaterialAttributes("plasticPhysical",dDensity, 0.8f, 0.7f);
136 Attributes[(int)MaterialAttributes.Material.Rubber + (int)MaterialAttributes.Material.NumberOfTypes] =
137 new MaterialAttributes("rubberPhysical",dDensity, 0.8f, 0.9f);
138 Attributes[(int)MaterialAttributes.Material.Light + (int)MaterialAttributes.Material.NumberOfTypes] =
139 new MaterialAttributes("lightPhysical",dDensity, dFriction, dRestitution);
140 Attributes[(int)MaterialAttributes.Material.Avatar + (int)MaterialAttributes.Material.NumberOfTypes] =
141 new MaterialAttributes("avatarPhysical",60f, 0.2f, 0f);
142 }
143
144 // Under the [BulletSim] section, one can change the individual material
145 // attribute values. The format of the configuration parameter is:
146 // <materialName><Attribute>["Physical"] = floatValue
147 // For instance:
148 // [BulletSim]
149 // StoneFriction = 0.2
150 // FleshRestitutionPhysical = 0.8
151 // Materials can have different parameters for their static and
152 // physical instantiations. When setting the non-physical value,
153 // both values are changed. Setting the physical value only changes
154 // the physical value.
155 public static void InitializefromParameters(IConfig pConfig)
156 {
157 foreach (KeyValuePair<string, MaterialAttributes.Material> kvp in MaterialMap)
158 {
159 string matName = kvp.Key;
160 foreach (string attribName in MaterialAttributes.MaterialAttribs)
161 {
162 string paramName = matName + attribName;
163 if (pConfig.Contains(paramName))
164 {
165 float paramValue = pConfig.GetFloat(paramName);
166 SetAttributeValue((int)kvp.Value, attribName, paramValue);
167 // set the physical value also
168 SetAttributeValue((int)kvp.Value + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue);
169 }
170 paramName += "Physical";
171 if (pConfig.Contains(paramName))
172 {
173 float paramValue = pConfig.GetFloat(paramName);
174 SetAttributeValue((int)kvp.Value + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue);
175 }
176 }
177 }
178 }
179
180 // Use reflection to set the value in the attribute structure.
181 private static void SetAttributeValue(int matType, string attribName, float val)
182 {
183 MaterialAttributes thisAttrib = Attributes[matType];
184 FieldInfo fieldInfo = thisAttrib.GetType().GetField(attribName.ToLower());
185 if (fieldInfo != null)
186 {
187 fieldInfo.SetValue(thisAttrib, val);
188 Attributes[matType] = thisAttrib;
189 }
190 }
191
192 // Given a material type, return a structure of attributes.
193 public static MaterialAttributes GetAttributes(MaterialAttributes.Material type, bool isPhysical)
194 {
195 int ind = (int)type;
196 if (isPhysical) ind += (int)MaterialAttributes.Material.NumberOfTypes;
197 return Attributes[ind];
198 }
199}
200}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs b/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs
index bc6e4c4..34a87c6 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs
@@ -1,104 +1,327 @@
1using System; 1/*
2using System.Collections.Generic; 2 * Copyright (c) Contributors, http://opensimulator.org/
3using System.Text; 3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4using OpenMetaverse; 4 *
5 5 * Redistribution and use in source and binary forms, with or without
6namespace OpenSim.Region.Physics.BulletSPlugin 6 * modification, are permitted provided that the following conditions are met:
7{ 7 * * Redistributions of source code must retain the above copyright
8public abstract class BSMotor 8 * notice, this list of conditions and the following disclaimer.
9{ 9 * * Redistributions in binary form must reproduce the above copyright
10 public virtual void Reset() { } 10 * notice, this list of conditions and the following disclaimer in the
11 public virtual void Zero() { } 11 * documentation and/or other materials provided with the distribution.
12} 12 * * Neither the name of the OpenSimulator Project nor the
13// Can all the incremental stepping be replaced with motor classes? 13 * names of its contributors may be used to endorse or promote products
14public class BSVMotor : BSMotor 14 * derived from this software without specific prior written permission.
15{ 15 *
16 public Vector3 FrameOfReference { get; set; } 16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 public Vector3 Offset { get; set; } 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 public float TimeScale { get; set; } 19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 public float TargetValueDecayTimeScale { get; set; } 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 public Vector3 CurrentValueReductionTimescale { get; set; } 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 public float Efficiency { get; set; } 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 public Vector3 TargetValue { get; private set; } 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 public Vector3 CurrentValue { get; private set; } 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 26 *
27 27 */
28 28using System;
29 BSVMotor(float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency) 29using System.Collections.Generic;
30 { 30using System.Text;
31 TimeScale = timeScale; 31using OpenMetaverse;
32 TargetValueDecayTimeScale = decayTimeScale; 32using OpenSim.Framework;
33 CurrentValueReductionTimescale = frictionTimeScale; 33
34 Efficiency = efficiency; 34namespace OpenSim.Region.Physics.BulletSPlugin
35 } 35{
36 public void SetCurrent(Vector3 current) 36public abstract class BSMotor
37 { 37{
38 CurrentValue = current; 38 // Timescales and other things can be turned off by setting them to 'infinite'.
39 } 39 public const float Infinite = 12345.6f;
40 public void SetTarget(Vector3 target) 40 public readonly static Vector3 InfiniteVector = new Vector3(BSMotor.Infinite, BSMotor.Infinite, BSMotor.Infinite);
41 { 41
42 TargetValue = target; 42 public BSMotor(string useName)
43 } 43 {
44 public Vector3 Step(float timeStep) 44 UseName = useName;
45 { 45 PhysicsScene = null;
46 if (CurrentValue.LengthSquared() > 0.001f) 46 }
47 { 47 public virtual void Reset() { }
48 // Vector3 origDir = Target; // DEBUG 48 public virtual void Zero() { }
49 // Vector3 origVel = CurrentValue; // DEBUG 49 public virtual void GenerateTestOutput(float timeStep) { }
50 50
51 // Add (desiredVelocity - currentAppliedVelocity) / howLongItShouldTakeToComplete 51 // A name passed at motor creation for easily identifyable debugging messages.
52 Vector3 addAmount = (TargetValue - CurrentValue)/(TargetValue) * timeStep; 52 public string UseName { get; private set; }
53 CurrentValue += addAmount; 53
54 54 // Used only for outputting debug information. Might not be set so check for null.
55 float decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep; 55 public BSScene PhysicsScene { get; set; }
56 TargetValue *= (1f - decayFactor); 56 protected void MDetailLog(string msg, params Object[] parms)
57 57 {
58 Vector3 frictionFactor = (Vector3.One / CurrentValueReductionTimescale) * timeStep; 58 if (PhysicsScene != null)
59 CurrentValue *= (Vector3.One - frictionFactor); 59 {
60 } 60 if (PhysicsScene.VehicleLoggingEnabled)
61 else 61 {
62 { 62 PhysicsScene.DetailLog(msg, parms);
63 // if what remains of direction is very small, zero it. 63 }
64 TargetValue = Vector3.Zero; 64 }
65 CurrentValue = Vector3.Zero; 65 }
66 66}
67 // VDetailLog("{0},MoveLinear,zeroed", Prim.LocalID); 67
68 } 68// Motor which moves CurrentValue to TargetValue over TimeScale seconds.
69 return CurrentValue; 69// The TargetValue decays in TargetValueDecayTimeScale and
70 } 70// the CurrentValue will be held back by FrictionTimeScale.
71} 71// This motor will "zero itself" over time in that the targetValue will
72 72// decay to zero and the currentValue will follow it to that zero.
73public class BSFMotor : BSMotor 73// The overall effect is for the returned correction value to go from large
74{ 74// values (the total difference between current and target minus friction)
75 public float TimeScale { get; set; } 75// to small and eventually zero values.
76 public float DecayTimeScale { get; set; } 76// TimeScale and TargetDelayTimeScale may be 'infinite' which means no decay.
77 public float Friction { get; set; } 77
78 public float Efficiency { get; set; } 78// For instance, if something is moving at speed X and the desired speed is Y,
79 79// CurrentValue is X and TargetValue is Y. As the motor is stepped, new
80 public float Target { get; private set; } 80// values of CurrentValue are returned that approach the TargetValue.
81 public float CurrentValue { get; private set; } 81// The feature of decaying TargetValue is so vehicles will eventually
82 82// come to a stop rather than run forever. This can be disabled by
83 BSFMotor(float timeScale, float decayTimescale, float friction, float efficiency) 83// setting TargetValueDecayTimescale to 'infinite'.
84 { 84// The change from CurrentValue to TargetValue is linear over TimeScale seconds.
85 } 85public class BSVMotor : BSMotor
86 public void SetCurrent(float target) 86{
87 { 87 // public Vector3 FrameOfReference { get; set; }
88 } 88 // public Vector3 Offset { get; set; }
89 public void SetTarget(float target) 89
90 { 90 public virtual float TimeScale { get; set; }
91 } 91 public virtual float TargetValueDecayTimeScale { get; set; }
92 public float Step(float timeStep) 92 public virtual Vector3 FrictionTimescale { get; set; }
93 { 93 public virtual float Efficiency { get; set; }
94 return 0f; 94
95 } 95 public virtual float ErrorZeroThreshold { get; set; }
96} 96
97public class BSPIDMotor : BSMotor 97 public virtual Vector3 TargetValue { get; protected set; }
98{ 98 public virtual Vector3 CurrentValue { get; protected set; }
99 // TODO: write and use this one 99 public virtual Vector3 LastError { get; protected set; }
100 BSPIDMotor() 100
101 { 101 public BSVMotor(string useName)
102 } 102 : base(useName)
103} 103 {
104} 104 TimeScale = TargetValueDecayTimeScale = BSMotor.Infinite;
105 Efficiency = 1f;
106 FrictionTimescale = BSMotor.InfiniteVector;
107 CurrentValue = TargetValue = Vector3.Zero;
108 ErrorZeroThreshold = 0.01f;
109 }
110 public BSVMotor(string useName, float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency)
111 : this(useName)
112 {
113 TimeScale = timeScale;
114 TargetValueDecayTimeScale = decayTimeScale;
115 FrictionTimescale = frictionTimeScale;
116 Efficiency = efficiency;
117 CurrentValue = TargetValue = Vector3.Zero;
118 }
119 public void SetCurrent(Vector3 current)
120 {
121 CurrentValue = current;
122 }
123 public void SetTarget(Vector3 target)
124 {
125 TargetValue = target;
126 }
127 public override void Zero()
128 {
129 base.Zero();
130 CurrentValue = TargetValue = Vector3.Zero;
131 }
132
133 // Compute the next step and return the new current value
134 public virtual Vector3 Step(float timeStep)
135 {
136 Vector3 origTarget = TargetValue; // DEBUG
137 Vector3 origCurrVal = CurrentValue; // DEBUG
138
139 Vector3 correction = Vector3.Zero;
140 Vector3 error = TargetValue - CurrentValue;
141 if (!error.ApproxEquals(Vector3.Zero, ErrorZeroThreshold))
142 {
143 correction = Step(timeStep, error);
144
145 CurrentValue += correction;
146
147 // The desired value reduces to zero which also reduces the difference with current.
148 // If the decay time is infinite, don't decay at all.
149 float decayFactor = 0f;
150 if (TargetValueDecayTimeScale != BSMotor.Infinite)
151 {
152 decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep;
153 TargetValue *= (1f - decayFactor);
154 }
155
156 // The amount we can correct the error is reduced by the friction
157 Vector3 frictionFactor = Vector3.Zero;
158 if (FrictionTimescale != BSMotor.InfiniteVector)
159 {
160 // frictionFactor = (Vector3.One / FrictionTimescale) * timeStep;
161 // Individual friction components can be 'infinite' so compute each separately.
162 frictionFactor.X = (FrictionTimescale.X == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.X);
163 frictionFactor.Y = (FrictionTimescale.Y == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.Y);
164 frictionFactor.Z = (FrictionTimescale.Z == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.Z);
165 frictionFactor *= timeStep;
166 CurrentValue *= (Vector3.One - frictionFactor);
167 }
168
169 MDetailLog("{0}, BSVMotor.Step,nonZero,{1},origCurr={2},origTarget={3},timeStep={4},err={5},corr={6}",
170 BSScene.DetailLogZero, UseName, origCurrVal, origTarget,
171 timeStep, error, correction);
172 MDetailLog("{0}, BSVMotor.Step,nonZero,{1},tgtDecayTS={2},decayFact={3},frictTS={4},frictFact={5},tgt={6},curr={7}",
173 BSScene.DetailLogZero, UseName,
174 TargetValueDecayTimeScale, decayFactor, FrictionTimescale, frictionFactor,
175 TargetValue, CurrentValue);
176 }
177 else
178 {
179 // Difference between what we have and target is small. Motor is done.
180 CurrentValue = TargetValue;
181 MDetailLog("{0}, BSVMotor.Step,zero,{1},origTgt={2},origCurr={3},ret={2}",
182 BSScene.DetailLogZero, UseName, origCurrVal, origTarget, CurrentValue);
183 }
184
185 return CurrentValue;
186 }
187 public virtual Vector3 Step(float timeStep, Vector3 error)
188 {
189 LastError = error;
190 Vector3 returnCorrection = Vector3.Zero;
191 if (!error.ApproxEquals(Vector3.Zero, ErrorZeroThreshold))
192 {
193 // correction = error / secondsItShouldTakeToCorrect
194 Vector3 correctionAmount;
195 if (TimeScale == 0f || TimeScale == BSMotor.Infinite)
196 correctionAmount = error * timeStep;
197 else
198 correctionAmount = error / TimeScale * timeStep;
199
200 returnCorrection = correctionAmount;
201 MDetailLog("{0}, BSVMotor.Step,nonZero,{1},timeStep={2},timeScale={3},err={4},corr={5}",
202 BSScene.DetailLogZero, UseName, timeStep, TimeScale, error, correctionAmount);
203 }
204 return returnCorrection;
205 }
206
207 // The user sets all the parameters and calls this which outputs values until error is zero.
208 public override void GenerateTestOutput(float timeStep)
209 {
210 // maximum number of outputs to generate.
211 int maxOutput = 50;
212 MDetailLog("{0},BSVMotor.Test,{1},===================================== BEGIN Test Output", BSScene.DetailLogZero, UseName);
213 MDetailLog("{0},BSVMotor.Test,{1},timeScale={2},targDlyTS={3},frictTS={4},eff={5},curr={6},tgt={7}",
214 BSScene.DetailLogZero, UseName,
215 TimeScale, TargetValueDecayTimeScale, FrictionTimescale, Efficiency,
216 CurrentValue, TargetValue);
217
218 LastError = BSMotor.InfiniteVector;
219 while (maxOutput-- > 0 && !LastError.ApproxEquals(Vector3.Zero, ErrorZeroThreshold))
220 {
221 Vector3 lastStep = Step(timeStep);
222 MDetailLog("{0},BSVMotor.Test,{1},cur={2},tgt={3},lastError={4},lastStep={5}",
223 BSScene.DetailLogZero, UseName, CurrentValue, TargetValue, LastError, lastStep);
224 }
225 MDetailLog("{0},BSVMotor.Test,{1},===================================== END Test Output", BSScene.DetailLogZero, UseName);
226
227
228 }
229
230 public override string ToString()
231 {
232 return String.Format("<{0},curr={1},targ={2},decayTS={3},frictTS={4}>",
233 UseName, CurrentValue, TargetValue, TargetValueDecayTimeScale, FrictionTimescale);
234 }
235}
236
237public class BSFMotor : BSMotor
238{
239 public float TimeScale { get; set; }
240 public float DecayTimeScale { get; set; }
241 public float Friction { get; set; }
242 public float Efficiency { get; set; }
243
244 public float Target { get; private set; }
245 public float CurrentValue { get; private set; }
246
247 public BSFMotor(string useName, float timeScale, float decayTimescale, float friction, float efficiency)
248 : base(useName)
249 {
250 }
251 public void SetCurrent(float target)
252 {
253 }
254 public void SetTarget(float target)
255 {
256 }
257 public virtual float Step(float timeStep)
258 {
259 return 0f;
260 }
261}
262
263// Proportional, Integral, Derivitive Motor
264// Good description at http://www.answers.com/topic/pid-controller . Includes processes for choosing p, i and d factors.
265public class BSPIDVMotor : BSVMotor
266{
267 // Larger makes more overshoot, smaller means converge quicker. Range of 0.1 to 10.
268 public Vector3 proportionFactor { get; set; }
269 public Vector3 integralFactor { get; set; }
270 public Vector3 derivFactor { get; set; }
271 // Arbritrary factor range.
272 // EfficiencyHigh means move quickly to the correct number. EfficiencyLow means might over correct.
273 public float EfficiencyHigh = 0.4f;
274 public float EfficiencyLow = 4.0f;
275
276 Vector3 IntegralFactor { get; set; }
277
278 public BSPIDVMotor(string useName)
279 : base(useName)
280 {
281 proportionFactor = new Vector3(1.00f, 1.00f, 1.00f);
282 integralFactor = new Vector3(1.00f, 1.00f, 1.00f);
283 derivFactor = new Vector3(1.00f, 1.00f, 1.00f);
284 IntegralFactor = Vector3.Zero;
285 LastError = Vector3.Zero;
286 }
287
288 public override void Zero()
289 {
290 base.Zero();
291 }
292
293 public override float Efficiency
294 {
295 get { return base.Efficiency; }
296 set
297 {
298 base.Efficiency = Util.Clamp(value, 0f, 1f);
299 // Compute factors based on efficiency.
300 // If efficiency is high (1f), use a factor value that moves the error value to zero with little overshoot.
301 // If efficiency is low (0f), use a factor value that overcorrects.
302 // TODO: might want to vary contribution of different factor depending on efficiency.
303 float factor = ((1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow) / 3f;
304 // float factor = (1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow;
305 proportionFactor = new Vector3(factor, factor, factor);
306 integralFactor = new Vector3(factor, factor, factor);
307 derivFactor = new Vector3(factor, factor, factor);
308 }
309 }
310
311 // Ignore Current and Target Values and just advance the PID computation on this error.
312 public override Vector3 Step(float timeStep, Vector3 error)
313 {
314 // Add up the error so we can integrate over the accumulated errors
315 IntegralFactor += error * timeStep;
316
317 // A simple derivitive is the rate of change from the last error.
318 Vector3 derivFactor = (error - LastError) * timeStep;
319 LastError = error;
320
321 // Correction = -(proportionOfPresentError + accumulationOfPastError + rateOfChangeOfError)
322 Vector3 ret = -(error * proportionFactor + IntegralFactor * integralFactor + derivFactor * derivFactor);
323
324 return ret;
325 }
326}
327}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
index f6a890e..92a5f2f 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
@@ -60,6 +60,9 @@ public abstract class BSPhysObject : PhysicsActor
60 Linkset = BSLinkset.Factory(PhysicsScene, this); 60 Linkset = BSLinkset.Factory(PhysicsScene, this);
61 LastAssetBuildFailed = false; 61 LastAssetBuildFailed = false;
62 62
63 // Default material type
64 Material = MaterialAttributes.Material.Wood;
65
63 CollisionCollection = new CollisionEventUpdate(); 66 CollisionCollection = new CollisionEventUpdate();
64 SubscribedEventsMs = 0; 67 SubscribedEventsMs = 0;
65 CollidingStep = 0; 68 CollidingStep = 0;
@@ -72,6 +75,7 @@ public abstract class BSPhysObject : PhysicsActor
72 public string TypeName { get; protected set; } 75 public string TypeName { get; protected set; }
73 76
74 public BSLinkset Linkset { get; set; } 77 public BSLinkset Linkset { get; set; }
78 public BSLinksetInfo LinksetInfo { get; set; }
75 79
76 // Return the object mass without calculating it or having side effects 80 // Return the object mass without calculating it or having side effects
77 public abstract float RawMass { get; } 81 public abstract float RawMass { get; }
@@ -105,10 +109,17 @@ public abstract class BSPhysObject : PhysicsActor
105 public EntityProperties CurrentEntityProperties { get; set; } 109 public EntityProperties CurrentEntityProperties { get; set; }
106 public EntityProperties LastEntityProperties { get; set; } 110 public EntityProperties LastEntityProperties { get; set; }
107 111
108 public abstract OMV.Vector3 Scale { get; set; } 112 public virtual OMV.Vector3 Scale { get; set; }
109 public abstract bool IsSolid { get; } 113 public abstract bool IsSolid { get; }
110 public abstract bool IsStatic { get; } 114 public abstract bool IsStatic { get; }
111 115
116 // Materialness
117 public MaterialAttributes.Material Material { get; private set; }
118 public override void SetMaterial(int material)
119 {
120 Material = (MaterialAttributes.Material)material;
121 }
122
112 // Stop all physical motion. 123 // Stop all physical motion.
113 public abstract void ZeroMotion(bool inTaintTime); 124 public abstract void ZeroMotion(bool inTaintTime);
114 public abstract void ZeroAngularMotion(bool inTaintTime); 125 public abstract void ZeroAngularMotion(bool inTaintTime);
@@ -128,6 +139,17 @@ public abstract class BSPhysObject : PhysicsActor
128 public abstract OMV.Quaternion RawOrientation { get; set; } 139 public abstract OMV.Quaternion RawOrientation { get; set; }
129 public abstract OMV.Quaternion ForceOrientation { get; set; } 140 public abstract OMV.Quaternion ForceOrientation { get; set; }
130 141
142 // The system is telling us the velocity it wants to move at.
143 protected OMV.Vector3 m_targetVelocity;
144 public override OMV.Vector3 TargetVelocity
145 {
146 get { return m_targetVelocity; }
147 set
148 {
149 m_targetVelocity = value;
150 Velocity = value;
151 }
152 }
131 public abstract OMV.Vector3 ForceVelocity { get; set; } 153 public abstract OMV.Vector3 ForceVelocity { get; set; }
132 154
133 public abstract OMV.Vector3 ForceRotationalVelocity { get; set; } 155 public abstract OMV.Vector3 ForceRotationalVelocity { get; set; }
@@ -192,7 +214,7 @@ public abstract class BSPhysObject : PhysicsActor
192 { 214 {
193 bool ret = true; 215 bool ret = true;
194 // If the 'no collision' call, force it to happen right now so quick collision_end 216 // If the 'no collision' call, force it to happen right now so quick collision_end
195 bool force = CollisionCollection.Count == 0; 217 bool force = (CollisionCollection.Count == 0);
196 218
197 // throttle the collisions to the number of milliseconds specified in the subscription 219 // throttle the collisions to the number of milliseconds specified in the subscription
198 if (force || (PhysicsScene.SimulationNowTime >= NextCollisionOkTime)) 220 if (force || (PhysicsScene.SimulationNowTime >= NextCollisionOkTime))
@@ -210,8 +232,10 @@ public abstract class BSPhysObject : PhysicsActor
210 // DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count); 232 // DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count);
211 base.SendCollisionUpdate(CollisionCollection); 233 base.SendCollisionUpdate(CollisionCollection);
212 234
213 // The collisionCollection structure is passed around in the simulator. 235 // The CollisionCollection instance is passed around in the simulator.
214 // Make sure we don't have a handle to that one and that a new one is used for next time. 236 // Make sure we don't have a handle to that one and that a new one is used for next time.
237 // This fixes an interesting 'gotcha'. If we call CollisionCollection.Clear() here,
238 // a race condition is created for the other users of this instance.
215 CollisionCollection = new CollisionEventUpdate(); 239 CollisionCollection = new CollisionEventUpdate();
216 } 240 }
217 return ret; 241 return ret;
@@ -229,7 +253,8 @@ public abstract class BSPhysObject : PhysicsActor
229 253
230 PhysicsScene.TaintedObject(TypeName+".SubscribeEvents", delegate() 254 PhysicsScene.TaintedObject(TypeName+".SubscribeEvents", delegate()
231 { 255 {
232 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); 256 if (PhysBody.HasPhysicalBody)
257 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
233 }); 258 });
234 } 259 }
235 else 260 else
@@ -243,7 +268,9 @@ public abstract class BSPhysObject : PhysicsActor
243 SubscribedEventsMs = 0; 268 SubscribedEventsMs = 0;
244 PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate() 269 PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate()
245 { 270 {
246 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); 271 // Make sure there is a body there because sometimes destruction happens in an un-ideal order.
272 if (PhysBody.HasPhysicalBody)
273 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
247 }); 274 });
248 } 275 }
249 // Return 'true' if the simulator wants collision events 276 // Return 'true' if the simulator wants collision events
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
index 2b3fa25..68a0db6 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
@@ -45,7 +45,6 @@ public sealed class BSPrim : BSPhysObject
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;
@@ -93,7 +92,7 @@ public sealed class BSPrim : BSPhysObject
93 _physicsActorType = (int)ActorTypes.Prim; 92 _physicsActorType = (int)ActorTypes.Prim;
94 _position = pos; 93 _position = pos;
95 _size = size; 94 _size = size;
96 Scale = size; // the scale will be set by CreateGeom depending on object type 95 Scale = size; // prims are the size the user wants them to be (different for BSCharactes).
97 _orientation = rotation; 96 _orientation = rotation;
98 _buoyancy = 1f; 97 _buoyancy = 1f;
99 _velocity = OMV.Vector3.Zero; 98 _velocity = OMV.Vector3.Zero;
@@ -101,15 +100,20 @@ public sealed class BSPrim : BSPhysObject
101 BaseShape = pbs; 100 BaseShape = pbs;
102 _isPhysical = pisPhysical; 101 _isPhysical = pisPhysical;
103 _isVolumeDetect = false; 102 _isVolumeDetect = false;
104 _friction = PhysicsScene.Params.defaultFriction; // TODO: compute based on object material 103
105 _density = PhysicsScene.Params.defaultDensity; // TODO: compute based on object material 104 // Someday set default attributes based on the material but, for now, we don't know the prim material yet.
105 // MaterialAttributes primMat = BSMaterials.GetAttributes(Material, pisPhysical);
106 _density = PhysicsScene.Params.defaultDensity;
107 _friction = PhysicsScene.Params.defaultFriction;
106 _restitution = PhysicsScene.Params.defaultRestitution; 108 _restitution = PhysicsScene.Params.defaultRestitution;
109
107 _vehicle = new BSDynamics(PhysicsScene, this); // add vehicleness 110 _vehicle = new BSDynamics(PhysicsScene, this); // add vehicleness
111
108 _mass = CalculateMass(); 112 _mass = CalculateMass();
109 113
110 // No body or shape yet 114 // No body or shape yet
111 PhysBody = new BulletBody(LocalID, IntPtr.Zero); 115 PhysBody = new BulletBody(LocalID);
112 PhysShape = new BulletShape(IntPtr.Zero); 116 PhysShape = new BulletShape();
113 117
114 DetailLog("{0},BSPrim.constructor,call", LocalID); 118 DetailLog("{0},BSPrim.constructor,call", LocalID);
115 // do the actual object creation at taint time 119 // do the actual object creation at taint time
@@ -143,7 +147,9 @@ public sealed class BSPrim : BSPhysObject
143 DetailLog("{0},BSPrim.Destroy,taint,", LocalID); 147 DetailLog("{0},BSPrim.Destroy,taint,", LocalID);
144 // If there are physical body and shape, release my use of same. 148 // If there are physical body and shape, release my use of same.
145 PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null); 149 PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null);
150 PhysBody.Clear();
146 PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null); 151 PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null);
152 PhysShape.Clear();
147 }); 153 });
148 } 154 }
149 155
@@ -157,12 +163,10 @@ public sealed class BSPrim : BSPhysObject
157 // We presume the scale and size are the same. If scale must be changed for 163 // 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. 164 // the physical shape, that is done when the geometry is built.
159 _size = value; 165 _size = value;
166 Scale = _size;
160 ForceBodyShapeRebuild(false); 167 ForceBodyShapeRebuild(false);
161 } 168 }
162 } 169 }
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 170
167 public override PrimitiveBaseShape Shape { 171 public override PrimitiveBaseShape Shape {
168 set { 172 set {
@@ -189,13 +193,17 @@ public sealed class BSPrim : BSPhysObject
189 } 193 }
190 } 194 }
191 public override bool Selected { 195 public override bool Selected {
192 set { 196 set
193 _isSelected = value; 197 {
194 PhysicsScene.TaintedObject("BSPrim.setSelected", delegate() 198 if (value != _isSelected)
195 { 199 {
196 DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected); 200 _isSelected = value;
197 SetObjectDynamic(false); 201 PhysicsScene.TaintedObject("BSPrim.setSelected", delegate()
198 }); 202 {
203 DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected);
204 SetObjectDynamic(false);
205 });
206 }
199 } 207 }
200 } 208 }
201 public override void CrossingFailure() { return; } 209 public override void CrossingFailure() { return; }
@@ -244,7 +252,8 @@ public sealed class BSPrim : BSPhysObject
244 // Zero some other properties in the physics engine 252 // Zero some other properties in the physics engine
245 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() 253 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate()
246 { 254 {
247 BulletSimAPI.ClearAllForces2(PhysBody.ptr); 255 if (PhysBody.HasPhysicalBody)
256 BulletSimAPI.ClearAllForces2(PhysBody.ptr);
248 }); 257 });
249 } 258 }
250 public override void ZeroAngularMotion(bool inTaintTime) 259 public override void ZeroAngularMotion(bool inTaintTime)
@@ -253,8 +262,12 @@ public sealed class BSPrim : BSPhysObject
253 // Zero some other properties in the physics engine 262 // Zero some other properties in the physics engine
254 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() 263 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate()
255 { 264 {
256 BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); 265 // DetailLog("{0},BSPrim.ZeroAngularMotion,call,rotVel={1}", LocalID, _rotationalVelocity);
257 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); 266 if (PhysBody.HasPhysicalBody)
267 {
268 BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
269 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
270 }
258 }); 271 });
259 } 272 }
260 273
@@ -271,9 +284,12 @@ public sealed class BSPrim : BSPhysObject
271 } 284 }
272 public override OMV.Vector3 Position { 285 public override OMV.Vector3 Position {
273 get { 286 get {
287 /* NOTE: this refetch is not necessary. The simulator knows about linkset children
288 * and does not fetch this position info for children. Thus this is commented out.
274 // child prims move around based on their parent. Need to get the latest location 289 // child prims move around based on their parent. Need to get the latest location
275 if (!Linkset.IsRoot(this)) 290 if (!Linkset.IsRoot(this))
276 _position = Linkset.Position(this); 291 _position = Linkset.PositionGet(this);
292 */
277 293
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 = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr);
@@ -281,18 +297,22 @@ public sealed class BSPrim : BSPhysObject
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,taint,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
309 // A linkset might need to know if a component information changed.
310 Linkset.UpdateProperties(this, false);
311
291 PhysicsScene.TaintedObject("BSPrim.setPosition", delegate() 312 PhysicsScene.TaintedObject("BSPrim.setPosition", delegate()
292 { 313 {
293 // DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); 314 DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
294 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 315 ForcePosition = _position;
295 ActivateIfPhysical(false);
296 }); 316 });
297 } 317 }
298 } 318 }
@@ -303,9 +323,11 @@ public sealed class BSPrim : BSPhysObject
303 } 323 }
304 set { 324 set {
305 _position = value; 325 _position = value;
306 // PositionSanityCheck(); // Don't do this! Causes a loop and caller should know better. 326 if (PhysBody.HasPhysicalBody)
307 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 327 {
308 ActivateIfPhysical(false); 328 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
329 ActivateIfPhysical(false);
330 }
309 } 331 }
310 } 332 }
311 333
@@ -316,39 +338,46 @@ public sealed class BSPrim : BSPhysObject
316 { 338 {
317 bool ret = false; 339 bool ret = false;
318 340
341 if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(_position))
342 {
343 // The physical object is out of the known/simulated area.
344 // Upper levels of code will handle the transition to other areas so, for
345 // the time, we just ignore the position.
346 return ret;
347 }
348
319 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); 349 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
320 OMV.Vector3 upForce = OMV.Vector3.Zero; 350 OMV.Vector3 upForce = OMV.Vector3.Zero;
321 if (Position.Z < terrainHeight) 351 if (RawPosition.Z < terrainHeight)
322 { 352 {
323 DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight); 353 DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight);
324 float targetHeight = terrainHeight + (Size.Z / 2f); 354 float targetHeight = terrainHeight + (Size.Z / 2f);
325 // Upforce proportional to the distance away from the terrain. Correct the error in 1 sec. 355 // Upforce proportional to the distance away from the terrain. Correct the error in 1 sec.
326 upForce.Z = (terrainHeight - Position.Z) * 1f; 356 upForce.Z = (terrainHeight - RawPosition.Z) * 1f;
327 ret = true; 357 ret = true;
328 } 358 }
329 359
330 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) 360 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
331 { 361 {
332 float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position); 362 float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position);
333 // TODO: a floating motor so object will bob in the water 363 // TODO: a floating motor so object will bob in the water
334 if (Math.Abs(Position.Z - waterHeight) > 0.1f) 364 if (Math.Abs(RawPosition.Z - waterHeight) > 0.1f)
335 { 365 {
336 // Upforce proportional to the distance away from the water. Correct the error in 1 sec. 366 // Upforce proportional to the distance away from the water. Correct the error in 1 sec.
337 upForce.Z = (waterHeight - Position.Z) * 1f; 367 upForce.Z = (waterHeight - RawPosition.Z) * 1f;
338 ret = true; 368 ret = true;
339 } 369 }
340 } 370 }
341 371
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. 372 // The above code computes a force to apply to correct any out-of-bounds problems. Apply same.
373 // TODO: This should be intergrated with a geneal physics action mechanism.
374 // TODO: This should be moderated with PID'ness.
345 if (ret) 375 if (ret)
346 { 376 {
347 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.PositionSanityCheck:belowTerrain", delegate() 377 // Apply upforce and overcome gravity.
348 { 378 OMV.Vector3 correctionForce = upForce - PhysicsScene.DefaultGravity;
349 // Apply upforce and overcome gravity. 379 DetailLog("{0},BSPrim.PositionSanityCheck,applyForce,pos={1},upForce={2},correctionForce={3}", LocalID, _position, upForce, correctionForce);
350 ForceVelocity = ForceVelocity + upForce - PhysicsScene.DefaultGravity; 380 AddForce(correctionForce, false, inTaintTime);
351 });
352 } 381 }
353 return ret; 382 return ret;
354 } 383 }
@@ -408,7 +437,8 @@ public sealed class BSPrim : BSPhysObject
408 PhysicsScene.TaintedObject("BSPrim.setForce", delegate() 437 PhysicsScene.TaintedObject("BSPrim.setForce", delegate()
409 { 438 {
410 // DetailLog("{0},BSPrim.setForce,taint,force={1}", LocalID, _force); 439 // DetailLog("{0},BSPrim.setForce,taint,force={1}", LocalID, _force);
411 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force); 440 if (PhysBody.HasPhysicalBody)
441 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
412 }); 442 });
413 } 443 }
414 } 444 }
@@ -502,15 +532,18 @@ public sealed class BSPrim : BSPhysObject
502 PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate() 532 PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate()
503 { 533 {
504 // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity); 534 // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity);
505 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity); 535 ForceVelocity = _velocity;
506 }); 536 });
507 } 537 }
508 } 538 }
509 public override OMV.Vector3 ForceVelocity { 539 public override OMV.Vector3 ForceVelocity {
510 get { return _velocity; } 540 get { return _velocity; }
511 set { 541 set {
542 PhysicsScene.AssertInTaintTime("BSPrim.ForceVelocity");
543
512 _velocity = value; 544 _velocity = value;
513 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity); 545 if (PhysBody.HasPhysicalBody)
546 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
514 } 547 }
515 } 548 }
516 public override OMV.Vector3 Torque { 549 public override OMV.Vector3 Torque {
@@ -537,23 +570,32 @@ public sealed class BSPrim : BSPhysObject
537 } 570 }
538 public override OMV.Quaternion Orientation { 571 public override OMV.Quaternion Orientation {
539 get { 572 get {
573 /* NOTE: this refetch is not necessary. The simulator knows about linkset children
574 * and does not fetch this position info for children. Thus this is commented out.
540 // Children move around because tied to parent. Get a fresh value. 575 // Children move around because tied to parent. Get a fresh value.
541 if (!Linkset.IsRoot(this)) 576 if (!Linkset.IsRoot(this))
542 { 577 {
543 _orientation = Linkset.Orientation(this); 578 _orientation = Linkset.OrientationGet(this);
544 } 579 }
580 */
545 return _orientation; 581 return _orientation;
546 } 582 }
547 set { 583 set {
548 if (_orientation == value) 584 if (_orientation == value)
549 return; 585 return;
550 _orientation = value; 586 _orientation = value;
551 // TODO: what does it mean if a child in a linkset changes its orientation? Rebuild the constraint? 587
588 // A linkset might need to know if a component information changed.
589 Linkset.UpdateProperties(this, false);
590
552 PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate() 591 PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate()
553 { 592 {
554 // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr); 593 if (PhysBody.HasPhysicalBody)
555 // DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation); 594 {
556 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 595 // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr);
596 // DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation);
597 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
598 }
557 }); 599 });
558 } 600 }
559 } 601 }
@@ -644,10 +686,7 @@ public sealed class BSPrim : BSPhysObject
644 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr); 686 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr);
645 687
646 // Collision filter can be set only when the object is in the world 688 // Collision filter can be set only when the object is in the world
647 if (PhysBody.collisionFilter != 0 || PhysBody.collisionMask != 0) 689 PhysBody.ApplyCollisionMask();
648 {
649 BulletSimAPI.SetCollisionFilterMask2(PhysBody.ptr, (uint)PhysBody.collisionFilter, (uint)PhysBody.collisionMask);
650 }
651 690
652 // Recompute any linkset parameters. 691 // Recompute any linkset parameters.
653 // When going from non-physical to physical, this re-enables the constraints that 692 // When going from non-physical to physical, this re-enables the constraints that
@@ -672,8 +711,12 @@ public sealed class BSPrim : BSPhysObject
672 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT); 711 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
673 // Stop all movement 712 // Stop all movement
674 ZeroMotion(true); 713 ZeroMotion(true);
675 // Center of mass is at the center of the object 714
676 // DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.PhysBody.ptr, _position, _orientation); 715 // Set various physical properties so other object interact properly
716 MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, false);
717 BulletSimAPI.SetFriction2(PhysBody.ptr, matAttrib.friction);
718 BulletSimAPI.SetRestitution2(PhysBody.ptr, matAttrib.restitution);
719
677 // Mass is zero which disables a bunch of physics stuff in Bullet 720 // Mass is zero which disables a bunch of physics stuff in Bullet
678 UpdatePhysicalMassProperties(0f); 721 UpdatePhysicalMassProperties(0f);
679 // Set collision detection parameters 722 // Set collision detection parameters
@@ -682,24 +725,27 @@ public sealed class BSPrim : BSPhysObject
682 BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, PhysicsScene.Params.ccdMotionThreshold); 725 BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, PhysicsScene.Params.ccdMotionThreshold);
683 BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius); 726 BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius);
684 } 727 }
685 // There can be special things needed for implementing linksets 728
686 Linkset.MakeStatic(this);
687 // The activation state is 'disabled' so Bullet will not try to act on it. 729 // The activation state is 'disabled' so Bullet will not try to act on it.
688 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_SIMULATION); 730 // BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_SIMULATION);
689 // Start it out sleeping and physical actions could wake it up. 731 // Start it out sleeping and physical actions could wake it up.
690 // BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ISLAND_SLEEPING); 732 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ISLAND_SLEEPING);
733
734 // This collides like a static object
735 PhysBody.collisionType = CollisionType.Static;
691 736
692 PhysBody.collisionFilter = CollisionFilterGroups.StaticObjectFilter; 737 // There can be special things needed for implementing linksets
693 PhysBody.collisionMask = CollisionFilterGroups.StaticObjectMask; 738 Linkset.MakeStatic(this);
694 } 739 }
695 else 740 else
696 { 741 {
697 // Not a Bullet static object 742 // Not a Bullet static object
698 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT); 743 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
699 744
700 // Set various physical properties so internal dynamic properties will get computed correctly as they are set 745 // Set various physical properties so other object interact properly
701 BulletSimAPI.SetFriction2(PhysBody.ptr, PhysicsScene.Params.defaultFriction); 746 MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, true);
702 BulletSimAPI.SetRestitution2(PhysBody.ptr, PhysicsScene.Params.defaultRestitution); 747 BulletSimAPI.SetFriction2(PhysBody.ptr, matAttrib.friction);
748 BulletSimAPI.SetRestitution2(PhysBody.ptr, matAttrib.restitution);
703 749
704 // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382 750 // 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 751 // Since this can be called multiple times, only zero forces when becoming physical
@@ -727,16 +773,15 @@ public sealed class BSPrim : BSPhysObject
727 BulletSimAPI.SetSleepingThresholds2(PhysBody.ptr, PhysicsScene.Params.linearSleepingThreshold, PhysicsScene.Params.angularSleepingThreshold); 773 BulletSimAPI.SetSleepingThresholds2(PhysBody.ptr, PhysicsScene.Params.linearSleepingThreshold, PhysicsScene.Params.angularSleepingThreshold);
728 BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, PhysicsScene.Params.contactProcessingThreshold); 774 BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, PhysicsScene.Params.contactProcessingThreshold);
729 775
730 // There might be special things needed for implementing linksets. 776 // This collides like an object.
731 Linkset.MakeDynamic(this); 777 PhysBody.collisionType = CollisionType.Dynamic;
732 778
733 // Force activation of the object so Bullet will act on it. 779 // Force activation of the object so Bullet will act on it.
734 // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects. 780 // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects.
735 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ACTIVE_TAG); 781 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ACTIVE_TAG);
736 // BulletSimAPI.Activate2(BSBody.ptr, true);
737 782
738 PhysBody.collisionFilter = CollisionFilterGroups.ObjectFilter; 783 // There might be special things needed for implementing linksets.
739 PhysBody.collisionMask = CollisionFilterGroups.ObjectMask; 784 Linkset.MakeDynamic(this);
740 } 785 }
741 } 786 }
742 787
@@ -763,8 +808,9 @@ public sealed class BSPrim : BSPhysObject
763 m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType); 808 m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType);
764 } 809 }
765 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); 810 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
766 PhysBody.collisionFilter = CollisionFilterGroups.VolumeDetectFilter; 811
767 PhysBody.collisionMask = CollisionFilterGroups.VolumeDetectMask; 812 // Change collision info from a static object to a ghosty collision object
813 PhysBody.collisionType = CollisionType.VolumeDetect;
768 } 814 }
769 } 815 }
770 816
@@ -839,15 +885,6 @@ public sealed class BSPrim : BSPhysObject
839 } 885 }
840 public override OMV.Vector3 RotationalVelocity { 886 public override OMV.Vector3 RotationalVelocity {
841 get { 887 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; 888 return _rotationalVelocity;
852 } 889 }
853 set { 890 set {
@@ -856,7 +893,8 @@ public sealed class BSPrim : BSPhysObject
856 PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate() 893 PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate()
857 { 894 {
858 DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity); 895 DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity);
859 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity); 896 if (PhysBody.HasPhysicalBody)
897 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
860 }); 898 });
861 } 899 }
862 } 900 }
@@ -891,8 +929,11 @@ public sealed class BSPrim : BSPhysObject
891 _buoyancy = value; 929 _buoyancy = value;
892 // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); 930 // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
893 // Buoyancy is faked by changing the gravity applied to the object 931 // Buoyancy is faked by changing the gravity applied to the object
894 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy); 932 if (PhysBody.HasPhysicalBody)
895 BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav)); 933 {
934 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
935 BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav));
936 }
896 } 937 }
897 } 938 }
898 939
@@ -960,7 +1001,8 @@ public sealed class BSPrim : BSPhysObject
960 } 1001 }
961 DetailLog("{0},BSPrim.AddForce,taint,force={1}", LocalID, fSum); 1002 DetailLog("{0},BSPrim.AddForce,taint,force={1}", LocalID, fSum);
962 if (fSum != OMV.Vector3.Zero) 1003 if (fSum != OMV.Vector3.Zero)
963 BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, fSum); 1004 if (PhysBody.HasPhysicalBody)
1005 BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, fSum);
964 }); 1006 });
965 } 1007 }
966 1008
@@ -971,7 +1013,8 @@ public sealed class BSPrim : BSPhysObject
971 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyForceImpulse", delegate() 1013 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyForceImpulse", delegate()
972 { 1014 {
973 DetailLog("{0},BSPrim.ApplyForceImpulse,taint,tImpulse={1}", LocalID, applyImpulse); 1015 DetailLog("{0},BSPrim.ApplyForceImpulse,taint,tImpulse={1}", LocalID, applyImpulse);
974 BulletSimAPI.ApplyCentralImpulse2(PhysBody.ptr, applyImpulse); 1016 if (PhysBody.HasPhysicalBody)
1017 BulletSimAPI.ApplyCentralImpulse2(PhysBody.ptr, applyImpulse);
975 }); 1018 });
976 } 1019 }
977 1020
@@ -1007,18 +1050,23 @@ public sealed class BSPrim : BSPhysObject
1007 DetailLog("{0},BSPrim.AddAngularForce,taint,aForce={1}", LocalID, fSum); 1050 DetailLog("{0},BSPrim.AddAngularForce,taint,aForce={1}", LocalID, fSum);
1008 if (fSum != OMV.Vector3.Zero) 1051 if (fSum != OMV.Vector3.Zero)
1009 { 1052 {
1010 BulletSimAPI.ApplyTorque2(PhysBody.ptr, fSum); 1053 if (PhysBody.HasPhysicalBody)
1054 BulletSimAPI.ApplyTorque2(PhysBody.ptr, fSum);
1011 _torque = fSum; 1055 _torque = fSum;
1012 } 1056 }
1013 }); 1057 });
1014 } 1058 }
1015 // A torque impulse. 1059 // A torque impulse.
1060 // ApplyTorqueImpulse adds torque directly to the angularVelocity.
1061 // AddAngularForce accumulates the force and applied it to the angular velocity all at once.
1062 // Computed as: angularVelocity += impulse * inertia;
1016 public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime) 1063 public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime)
1017 { 1064 {
1018 OMV.Vector3 applyImpulse = impulse; 1065 OMV.Vector3 applyImpulse = impulse;
1019 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate() 1066 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate()
1020 { 1067 {
1021 BulletSimAPI.ApplyTorqueImpulse2(PhysBody.ptr, applyImpulse); 1068 if (PhysBody.HasPhysicalBody)
1069 BulletSimAPI.ApplyTorqueImpulse2(PhysBody.ptr, applyImpulse);
1022 }); 1070 });
1023 } 1071 }
1024 1072
@@ -1326,7 +1374,7 @@ public sealed class BSPrim : BSPhysObject
1326 // Rebuild the geometry and object. 1374 // Rebuild the geometry and object.
1327 // This is called when the shape changes so we need to recreate the mesh/hull. 1375 // This is called when the shape changes so we need to recreate the mesh/hull.
1328 // Called at taint-time!!! 1376 // Called at taint-time!!!
1329 private void CreateGeomAndObject(bool forceRebuild) 1377 public void CreateGeomAndObject(bool forceRebuild)
1330 { 1378 {
1331 // If this prim is part of a linkset, we must remove and restore the physical 1379 // If this prim is part of a linkset, we must remove and restore the physical
1332 // links if the body is rebuilt. 1380 // links if the body is rebuilt.
@@ -1336,12 +1384,11 @@ public sealed class BSPrim : BSPhysObject
1336 // Create the correct physical representation for this type of object. 1384 // Create the correct physical representation for this type of object.
1337 // Updates PhysBody and PhysShape with the new information. 1385 // Updates PhysBody and PhysShape with the new information.
1338 // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary. 1386 // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary.
1339 // Returns 'true' if either the body or the shape was changed.
1340 PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, null, delegate(BulletBody dBody) 1387 PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, null, delegate(BulletBody dBody)
1341 { 1388 {
1342 // Called if the current prim body is about to be destroyed. 1389 // Called if the current prim body is about to be destroyed.
1343 // Remove all the physical dependencies on the old body. 1390 // Remove all the physical dependencies on the old body.
1344 // (Maybe someday make the changing of BSShape an event handled by BSLinkset.) 1391 // (Maybe someday make the changing of BSShape an event to be subscribed to by BSLinkset, ...)
1345 needToRestoreLinkset = Linkset.RemoveBodyDependencies(this); 1392 needToRestoreLinkset = Linkset.RemoveBodyDependencies(this);
1346 needToRestoreVehicle = _vehicle.RemoveBodyDependencies(this); 1393 needToRestoreVehicle = _vehicle.RemoveBodyDependencies(this);
1347 }); 1394 });
@@ -1381,54 +1428,16 @@ public sealed class BSPrim : BSPhysObject
1381 1428
1382 public override void UpdateProperties(EntityProperties entprop) 1429 public override void UpdateProperties(EntityProperties entprop)
1383 { 1430 {
1384 /* 1431 // Updates only for individual prims and for the root object of a linkset.
1385 UpdatedProperties changed = 0; 1432 if (Linkset.IsRoot(this))
1386 // assign to the local variables so the normal set action does not happen
1387 // if (_position != entprop.Position)
1388 if (!_position.ApproxEquals(entprop.Position, POSITION_TOLERANCE))
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 { 1433 {
1419 // Only update the position of single objects and linkset roots 1434 // A temporary kludge to suppress the rotational effects introduced on vehicles by Bullet
1420 if (Linkset.IsRoot(this)) 1435 // TODO: handle physics introduced by Bullet with computed vehicle physics.
1436 if (_vehicle.IsActive)
1421 { 1437 {
1422 base.RequestPhysicsterseUpdate(); 1438 entprop.RotationalVelocity = OMV.Vector3.Zero;
1423 } 1439 }
1424 }
1425 */
1426 1440
1427 // Don't check for damping here -- it's done in BulletSim and SceneObjectPart.
1428
1429 // Updates only for individual prims and for the root object of a linkset.
1430 if (Linkset.IsRoot(this))
1431 {
1432 // Assign directly to the local variables so the normal set action does not happen 1441 // Assign directly to the local variables so the normal set action does not happen
1433 _position = entprop.Position; 1442 _position = entprop.Position;
1434 _orientation = entprop.Rotation; 1443 _orientation = entprop.Rotation;
@@ -1437,21 +1446,19 @@ public sealed class BSPrim : BSPhysObject
1437 _rotationalVelocity = entprop.RotationalVelocity; 1446 _rotationalVelocity = entprop.RotationalVelocity;
1438 1447
1439 // The sanity check can change the velocity and/or position. 1448 // The sanity check can change the velocity and/or position.
1440 if (PositionSanityCheck(true)) 1449 if (IsPhysical && PositionSanityCheck(true))
1441 { 1450 {
1442 entprop.Position = _position; 1451 entprop.Position = _position;
1443 entprop.Velocity = _velocity; 1452 entprop.Velocity = _velocity;
1444 } 1453 }
1445 1454
1446 // remember the current and last set values 1455 OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation; // DEBUG DEBUG DEBUG
1447 LastEntityProperties = CurrentEntityProperties;
1448 CurrentEntityProperties = entprop;
1449
1450 OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation;
1451 DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}", 1456 DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}",
1452 LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity); 1457 LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity);
1453 1458
1454 // BulletSimAPI.DumpRigidBody2(PhysicsScene.World.ptr, BSBody.ptr); // DEBUG DEBUG DEBUG 1459 // remember the current and last set values
1460 LastEntityProperties = CurrentEntityProperties;
1461 CurrentEntityProperties = entprop;
1455 1462
1456 base.RequestPhysicsterseUpdate(); 1463 base.RequestPhysicsterseUpdate();
1457 } 1464 }
@@ -1466,7 +1473,7 @@ public sealed class BSPrim : BSPhysObject
1466 */ 1473 */
1467 1474
1468 // The linkset implimentation might want to know about this. 1475 // The linkset implimentation might want to know about this.
1469 Linkset.UpdateProperties(this); 1476 Linkset.UpdateProperties(this, true);
1470 } 1477 }
1471} 1478}
1472} 1479}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
index 27a78d1..2ca4912 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
@@ -39,23 +39,10 @@ using log4net;
39using OpenMetaverse; 39using OpenMetaverse;
40 40
41// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim) 41// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim)
42// Test sculpties (verified that they don't work)
43// Compute physics FPS reasonably
44// Based on material, set density and friction 42// Based on material, set density and friction
45// Don't use constraints in linksets of non-physical objects. Means having to move children manually.
46// Four states of prim: Physical, regular, phantom and selected. Are we modeling these correctly?
47// In SL one can set both physical and phantom (gravity, does not effect others, makes collisions with ground)
48// At the moment, physical and phantom causes object to drop through the terrain
49// Physical phantom objects and related typing (collision options )
50// Check out llVolumeDetect. Must do something for that.
51// Use collision masks for collision with terrain and phantom objects
52// More efficient memory usage when passing hull information from BSPrim to BulletSim 43// More efficient memory usage when passing hull information from BSPrim to BulletSim
53// Should prim.link() and prim.delink() membership checking happen at taint time?
54// Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once.
55// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect 44// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect
56// Implement LockAngularMotion 45// Implement LockAngularMotion
57// Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation)
58// Remove mesh and Hull stuff. Use mesh passed to bullet and use convexdecom from bullet.
59// Add PID movement operations. What does ScenePresence.MoveToTarget do? 46// Add PID movement operations. What does ScenePresence.MoveToTarget do?
60// Check terrain size. 128 or 127? 47// Check terrain size. 128 or 127?
61// Raycast 48// Raycast
@@ -109,6 +96,19 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
109 public long SimulationStep { get { return m_simulationStep; } } 96 public long SimulationStep { get { return m_simulationStep; } }
110 private int m_taintsToProcessPerStep; 97 private int m_taintsToProcessPerStep;
111 98
99 // Avatar parameters
100 public float ParamAvatarFriction { get; private set; }
101 public float ParamAvatarStandingFriction { get; private set; }
102 public float ParamAvatarDensity { get; private set; }
103 public float ParamAvatarRestitution { get; private set; }
104 public float ParamAvatarCapsuleWidth { get; private set; }
105 public float ParamAvatarCapsuleDepth { get; private set; }
106 public float ParamAvatarCapsuleHeight { get; private set; }
107 public float ParamAvatarContactProcessingThreshold { get; private set; }
108
109 public delegate void PreStepAction(float timeStep);
110 public event PreStepAction BeforeStep;
111
112 // A value of the time now so all the collision and update routines do not have to get their own 112 // A value of the time now so all the collision and update routines do not have to get their own
113 // Set to 'now' just before all the prims and actors are called for collisions and updates 113 // Set to 'now' just before all the prims and actors are called for collisions and updates
114 public int SimulationNowTime { get; private set; } 114 public int SimulationNowTime { get; private set; }
@@ -140,7 +140,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
140 public const uint GROUNDPLANE_ID = 1; 140 public const uint GROUNDPLANE_ID = 1;
141 public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here 141 public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here
142 142
143 private float m_waterLevel; 143 public float SimpleWaterLevel { get; set; }
144 public BSTerrainManager TerrainManager { get; private set; } 144 public BSTerrainManager TerrainManager { get; private set; }
145 145
146 public ConfigurationParameters Params 146 public ConfigurationParameters Params
@@ -195,8 +195,10 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
195 private string m_physicsLoggingDir; 195 private string m_physicsLoggingDir;
196 private string m_physicsLoggingPrefix; 196 private string m_physicsLoggingPrefix;
197 private int m_physicsLoggingFileMinutes; 197 private int m_physicsLoggingFileMinutes;
198 private bool m_physicsLoggingDoFlush;
198 // 'true' of the vehicle code is to log lots of details 199 // 'true' of the vehicle code is to log lots of details
199 public bool VehicleLoggingEnabled { get; private set; } 200 public bool VehicleLoggingEnabled { get; private set; }
201 public bool VehiclePhysicalLoggingEnabled { get; private set; }
200 202
201 #region Construction and Initialization 203 #region Construction and Initialization
202 public BSScene(string identifier) 204 public BSScene(string identifier)
@@ -234,6 +236,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
234 if (m_physicsLoggingEnabled) 236 if (m_physicsLoggingEnabled)
235 { 237 {
236 PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes); 238 PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes);
239 PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output error messages.
237 } 240 }
238 else 241 else
239 { 242 {
@@ -302,12 +305,22 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
302 m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", "."); 305 m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", ".");
303 m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-"); 306 m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-");
304 m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5); 307 m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5);
308 m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false);
305 // Very detailed logging for vehicle debugging 309 // Very detailed logging for vehicle debugging
306 VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false); 310 VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
311 VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false);
307 312
308 // Do any replacements in the parameters 313 // Do any replacements in the parameters
309 m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName); 314 m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
310 } 315 }
316
317 // The material characteristics.
318 BSMaterials.InitializeFromDefaults(Params);
319 if (pConfig != null)
320 {
321 // Let the user add new and interesting material property values.
322 BSMaterials.InitializefromParameters(pConfig);
323 }
311 } 324 }
312 } 325 }
313 326
@@ -345,8 +358,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
345 // make sure no stepping happens while we're deleting stuff 358 // make sure no stepping happens while we're deleting stuff
346 m_initialized = false; 359 m_initialized = false;
347 360
348 TerrainManager.ReleaseGroundPlaneAndTerrain();
349
350 foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects) 361 foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
351 { 362 {
352 kvp.Value.Destroy(); 363 kvp.Value.Destroy();
@@ -366,6 +377,13 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
366 Shapes = null; 377 Shapes = null;
367 } 378 }
368 379
380 if (TerrainManager != null)
381 {
382 TerrainManager.ReleaseGroundPlaneAndTerrain();
383 TerrainManager.Dispose();
384 TerrainManager = null;
385 }
386
369 // Anything left in the unmanaged code should be cleaned out 387 // Anything left in the unmanaged code should be cleaned out
370 BulletSimAPI.Shutdown2(World.ptr); 388 BulletSimAPI.Shutdown2(World.ptr);
371 389
@@ -490,8 +508,10 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
490 ProcessTaints(); 508 ProcessTaints();
491 509
492 // Some of the prims operate with special vehicle properties 510 // Some of the prims operate with special vehicle properties
493 ProcessVehicles(timeStep); 511 DoPreStepActions(timeStep);
494 ProcessTaints(); // the vehicles might have added taints 512
513 // the prestep actions might have added taints
514 ProcessTaints();
495 515
496 // step the physical world one interval 516 // step the physical world one interval
497 m_simulationStep++; 517 m_simulationStep++;
@@ -499,16 +519,17 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
499 519
500 try 520 try
501 { 521 {
502 if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG 522 if (VehiclePhysicalLoggingEnabled) DumpVehicles(); // DEBUG
503 if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount(); 523 if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount();
504 524
505 numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep, 525 numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep,
506 out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr); 526 out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr);
507 527
508 if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime); 528 if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime);
509 DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}", 529 DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}",
510 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount); 530 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps,
511 if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG 531 updatedEntityCount, collidersCount, ObjectsWithCollisions.Count);
532 if (VehiclePhysicalLoggingEnabled) DumpVehicles(); // DEBUG
512 } 533 }
513 catch (Exception e) 534 catch (Exception e)
514 { 535 {
@@ -520,9 +541,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
520 collidersCount = 0; 541 collidersCount = 0;
521 } 542 }
522 543
523 // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in 544 // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in.
524 545
525 // Get a value for 'now' so all the collision and update routines don't have to get their own 546 // Get a value for 'now' so all the collision and update routines don't have to get their own.
526 SimulationNowTime = Util.EnvironmentTickCount(); 547 SimulationNowTime = Util.EnvironmentTickCount();
527 548
528 // If there were collisions, process them by sending the event to the prim. 549 // If there were collisions, process them by sending the event to the prim.
@@ -562,12 +583,16 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
562 583
563 // Objects that are done colliding are removed from the ObjectsWithCollisions list. 584 // Objects that are done colliding are removed from the ObjectsWithCollisions list.
564 // Not done above because it is inside an iteration of ObjectWithCollisions. 585 // Not done above because it is inside an iteration of ObjectWithCollisions.
586 // This complex collision processing is required to create an empty collision
587 // event call after all collisions have happened on an object. This enables
588 // the simulator to generate the 'collision end' event.
565 if (ObjectsWithNoMoreCollisions.Count > 0) 589 if (ObjectsWithNoMoreCollisions.Count > 0)
566 { 590 {
567 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions) 591 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
568 ObjectsWithCollisions.Remove(po); 592 ObjectsWithCollisions.Remove(po);
569 ObjectsWithNoMoreCollisions.Clear(); 593 ObjectsWithNoMoreCollisions.Clear();
570 } 594 }
595 // Done with collisions.
571 596
572 // If any of the objects had updated properties, tell the object it has been changed by the physics engine 597 // If any of the objects had updated properties, tell the object it has been changed by the physics engine
573 if (updatedEntityCount > 0) 598 if (updatedEntityCount > 0)
@@ -585,15 +610,14 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
585 610
586 ProcessPostStepTaints(); 611 ProcessPostStepTaints();
587 612
588 // This causes the unmanaged code to output ALL the values found in ALL the objects in the world. 613 // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world.
589 // Only enable this in a limited test world with few objects. 614 // Only enable this in a limited test world with few objects.
590 // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG 615 // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG
591 616
592 // The physics engine returns the number of milliseconds it simulated this call. 617 // The physics engine returns the number of milliseconds it simulated this call.
593 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS. 618 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
594 // We multiply by 55 to give a recognizable running rate (55 or less). 619 // Multiply by 55 to give a nominal frame rate of 55.
595 return numSubSteps * m_fixedTimeStep * 1000 * 55; 620 return (float)numSubSteps * m_fixedTimeStep * 1000f * 55f;
596 // return timeStep * 1000 * 55;
597 } 621 }
598 622
599 // Something has collided 623 // Something has collided
@@ -639,12 +663,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
639 663
640 public override void SetWaterLevel(float baseheight) 664 public override void SetWaterLevel(float baseheight)
641 { 665 {
642 m_waterLevel = baseheight; 666 SimpleWaterLevel = baseheight;
643 }
644 // Someday....
645 public float GetWaterLevelAtXYZ(Vector3 loc)
646 {
647 return m_waterLevel;
648 } 667 }
649 668
650 public override void DeleteTerrain() 669 public override void DeleteTerrain()
@@ -915,6 +934,16 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
915 } 934 }
916 } 935 }
917 936
937 private void DoPreStepActions(float timeStep)
938 {
939 ProcessVehicles(timeStep);
940
941 PreStepAction actions = BeforeStep;
942 if (actions != null)
943 actions(timeStep);
944
945 }
946
918 // Some prims have extra vehicle actions 947 // Some prims have extra vehicle actions
919 // Called at taint time! 948 // Called at taint time!
920 private void ProcessVehicles(float timeStep) 949 private void ProcessVehicles(float timeStep)
@@ -979,6 +1008,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
979 // Should handle fetching the right type from the ini file and converting it. 1008 // Should handle fetching the right type from the ini file and converting it.
980 // -- a delegate for getting the value as a float 1009 // -- a delegate for getting the value as a float
981 // -- a delegate for setting the value from a float 1010 // -- a delegate for setting the value from a float
1011 // -- an optional delegate to update the value in the world. Most often used to
1012 // push the new value to an in-world object.
982 // 1013 //
983 // The single letter parameters for the delegates are: 1014 // The single letter parameters for the delegates are:
984 // s = BSScene 1015 // s = BSScene
@@ -1069,7 +1100,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1069 (s,p,l,v) => { s.PID_P = v; } ), 1100 (s,p,l,v) => { s.PID_P = v; } ),
1070 1101
1071 new ParameterDefn("DefaultFriction", "Friction factor used on new objects", 1102 new ParameterDefn("DefaultFriction", "Friction factor used on new objects",
1072 0.5f, 1103 0.2f,
1073 (s,cf,p,v) => { s.m_params[0].defaultFriction = cf.GetFloat(p, v); }, 1104 (s,cf,p,v) => { s.m_params[0].defaultFriction = cf.GetFloat(p, v); },
1074 (s) => { return s.m_params[0].defaultFriction; }, 1105 (s) => { return s.m_params[0].defaultFriction; },
1075 (s,p,l,v) => { s.m_params[0].defaultFriction = v; } ), 1106 (s,p,l,v) => { s.m_params[0].defaultFriction = v; } ),
@@ -1084,7 +1115,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1084 (s) => { return s.m_params[0].defaultRestitution; }, 1115 (s) => { return s.m_params[0].defaultRestitution; },
1085 (s,p,l,v) => { s.m_params[0].defaultRestitution = v; } ), 1116 (s,p,l,v) => { s.m_params[0].defaultRestitution = v; } ),
1086 new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)", 1117 new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)",
1087 0f, 1118 0.04f,
1088 (s,cf,p,v) => { s.m_params[0].collisionMargin = cf.GetFloat(p, v); }, 1119 (s,cf,p,v) => { s.m_params[0].collisionMargin = cf.GetFloat(p, v); },
1089 (s) => { return s.m_params[0].collisionMargin; }, 1120 (s) => { return s.m_params[0].collisionMargin; },
1090 (s,p,l,v) => { s.m_params[0].collisionMargin = v; } ), 1121 (s,p,l,v) => { s.m_params[0].collisionMargin = v; } ),
@@ -1151,7 +1182,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1151 (s) => { return s.m_params[0].terrainImplementation; }, 1182 (s) => { return s.m_params[0].terrainImplementation; },
1152 (s,p,l,v) => { s.m_params[0].terrainImplementation = v; } ), 1183 (s,p,l,v) => { s.m_params[0].terrainImplementation = v; } ),
1153 new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , 1184 new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" ,
1154 0.5f, 1185 0.3f,
1155 (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); }, 1186 (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); },
1156 (s) => { return s.m_params[0].terrainFriction; }, 1187 (s) => { return s.m_params[0].terrainFriction; },
1157 (s,p,l,v) => { s.m_params[0].terrainFriction = v; /* TODO: set on real terrain */} ), 1188 (s,p,l,v) => { s.m_params[0].terrainFriction = v; /* TODO: set on real terrain */} ),
@@ -1165,13 +1196,19 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1165 (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); }, 1196 (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); },
1166 (s) => { return s.m_params[0].terrainRestitution; }, 1197 (s) => { return s.m_params[0].terrainRestitution; },
1167 (s,p,l,v) => { s.m_params[0].terrainRestitution = v; /* TODO: set on real terrain */ } ), 1198 (s,p,l,v) => { s.m_params[0].terrainRestitution = v; /* TODO: set on real terrain */ } ),
1199 new ParameterDefn("TerrainCollisionMargin", "Margin where collision checking starts" ,
1200 0.04f,
1201 (s,cf,p,v) => { s.m_params[0].terrainCollisionMargin = cf.GetFloat(p, v); },
1202 (s) => { return s.m_params[0].terrainCollisionMargin; },
1203 (s,p,l,v) => { s.m_params[0].terrainCollisionMargin = v; /* TODO: set on real terrain */ } ),
1204
1168 new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.", 1205 new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.",
1169 0.2f, 1206 0.2f,
1170 (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); }, 1207 (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); },
1171 (s) => { return s.m_params[0].avatarFriction; }, 1208 (s) => { return s.m_params[0].avatarFriction; },
1172 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ), 1209 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ),
1173 new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.", 1210 new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.",
1174 10f, 1211 10.0f,
1175 (s,cf,p,v) => { s.m_params[0].avatarStandingFriction = cf.GetFloat(p, v); }, 1212 (s,cf,p,v) => { s.m_params[0].avatarStandingFriction = cf.GetFloat(p, v); },
1176 (s) => { return s.m_params[0].avatarStandingFriction; }, 1213 (s) => { return s.m_params[0].avatarStandingFriction; },
1177 (s,p,l,v) => { s.m_params[0].avatarStandingFriction = v; } ), 1214 (s,p,l,v) => { s.m_params[0].avatarStandingFriction = v; } ),
@@ -1206,6 +1243,11 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1206 (s) => { return s.m_params[0].avatarContactProcessingThreshold; }, 1243 (s) => { return s.m_params[0].avatarContactProcessingThreshold; },
1207 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ), 1244 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ),
1208 1245
1246 new ParameterDefn("VehicleAngularDamping", "Factor to damp vehicle angular movement per second (0.0 - 1.0)",
1247 0.95f,
1248 (s,cf,p,v) => { s.m_params[0].vehicleAngularDamping = cf.GetFloat(p, v); },
1249 (s) => { return s.m_params[0].vehicleAngularDamping; },
1250 (s,p,l,v) => { s.m_params[0].vehicleAngularDamping = v; } ),
1209 1251
1210 new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)", 1252 new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)",
1211 0f, 1253 0f,
@@ -1487,7 +1529,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1487 { 1529 {
1488 PhysicsLogging.Write(msg, args); 1530 PhysicsLogging.Write(msg, args);
1489 // Add the Flush() if debugging crashes. Gets all the messages written out. 1531 // Add the Flush() if debugging crashes. Gets all the messages written out.
1490 // PhysicsLogging.Flush(); 1532 if (m_physicsLoggingDoFlush) PhysicsLogging.Flush();
1491 } 1533 }
1492 // Used to fill in the LocalID when there isn't one. It's the correct number of characters. 1534 // Used to fill in the LocalID when there isn't one. It's the correct number of characters.
1493 public const string DetailLogZero = "0000000000"; 1535 public const string DetailLogZero = "0000000000";
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
index 892c34b..d6e2fe9 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
@@ -65,9 +65,16 @@ public sealed class BSShapeCollection : IDisposable
65 private Dictionary<System.UInt64, MeshDesc> Meshes = new Dictionary<System.UInt64, MeshDesc>(); 65 private Dictionary<System.UInt64, MeshDesc> Meshes = new Dictionary<System.UInt64, MeshDesc>();
66 private Dictionary<System.UInt64, HullDesc> Hulls = new Dictionary<System.UInt64, HullDesc>(); 66 private Dictionary<System.UInt64, HullDesc> Hulls = new Dictionary<System.UInt64, HullDesc>();
67 67
68 private bool DDetail = false;
69
68 public BSShapeCollection(BSScene physScene) 70 public BSShapeCollection(BSScene physScene)
69 { 71 {
70 PhysicsScene = physScene; 72 PhysicsScene = physScene;
73 // Set the next to 'true' for very detailed shape update detailed logging (detailed details?)
74 // While detailed debugging is still active, this is better than commenting out all the
75 // DetailLog statements. When debugging slows down, this and the protected logging
76 // statements can be commented/removed.
77 DDetail = true;
71 } 78 }
72 79
73 public void Dispose() 80 public void Dispose()
@@ -119,6 +126,11 @@ public sealed class BSShapeCollection : IDisposable
119 return ret; 126 return ret;
120 } 127 }
121 128
129 public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim)
130 {
131 return GetBodyAndShape(forceRebuild, sim, prim, null, null);
132 }
133
122 // Track another user of a body. 134 // Track another user of a body.
123 // We presume the caller has allocated the body. 135 // We presume the caller has allocated the body.
124 // Bodies only have one user so the body is just put into the world if not already there. 136 // Bodies only have one user so the body is just put into the world if not already there.
@@ -126,13 +138,13 @@ public sealed class BSShapeCollection : IDisposable
126 { 138 {
127 lock (m_collectionActivityLock) 139 lock (m_collectionActivityLock)
128 { 140 {
129 DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body); 141 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body);
130 PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.ReferenceBody", delegate() 142 PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.ReferenceBody", delegate()
131 { 143 {
132 if (!BulletSimAPI.IsInWorld2(body.ptr)) 144 if (!BulletSimAPI.IsInWorld2(body.ptr))
133 { 145 {
134 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr); 146 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr);
135 DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body); 147 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body);
136 } 148 }
137 }); 149 });
138 } 150 }
@@ -142,14 +154,14 @@ public sealed class BSShapeCollection : IDisposable
142 // Called when releasing use of a BSBody. BSShape is handled separately. 154 // Called when releasing use of a BSBody. BSShape is handled separately.
143 public void DereferenceBody(BulletBody body, bool inTaintTime, BodyDestructionCallback bodyCallback ) 155 public void DereferenceBody(BulletBody body, bool inTaintTime, BodyDestructionCallback bodyCallback )
144 { 156 {
145 if (body.ptr == IntPtr.Zero) 157 if (!body.HasPhysicalBody)
146 return; 158 return;
147 159
148 lock (m_collectionActivityLock) 160 lock (m_collectionActivityLock)
149 { 161 {
150 PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceBody", delegate() 162 PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceBody", delegate()
151 { 163 {
152 DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1},inTaintTime={2}", 164 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1},inTaintTime={2}",
153 body.ID, body, inTaintTime); 165 body.ID, body, inTaintTime);
154 // If the caller needs to know the old body is going away, pass the event up. 166 // If the caller needs to know the old body is going away, pass the event up.
155 if (bodyCallback != null) bodyCallback(body); 167 if (bodyCallback != null) bodyCallback(body);
@@ -157,7 +169,7 @@ public sealed class BSShapeCollection : IDisposable
157 if (BulletSimAPI.IsInWorld2(body.ptr)) 169 if (BulletSimAPI.IsInWorld2(body.ptr))
158 { 170 {
159 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr); 171 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);
160 DetailLog("{0},BSShapeCollection.DereferenceBody,removingFromWorld. Body={1}", body.ID, body); 172 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,removingFromWorld. Body={1}", body.ID, body);
161 } 173 }
162 174
163 // Zero any reference to the shape so it is not freed when the body is deleted. 175 // Zero any reference to the shape so it is not freed when the body is deleted.
@@ -184,7 +196,7 @@ public sealed class BSShapeCollection : IDisposable
184 { 196 {
185 // There is an existing instance of this mesh. 197 // There is an existing instance of this mesh.
186 meshDesc.referenceCount++; 198 meshDesc.referenceCount++;
187 DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}", 199 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}",
188 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); 200 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
189 } 201 }
190 else 202 else
@@ -194,7 +206,7 @@ public sealed class BSShapeCollection : IDisposable
194 meshDesc.shapeKey = shape.shapeKey; 206 meshDesc.shapeKey = shape.shapeKey;
195 // We keep a reference to the underlying IMesh data so a hull can be built 207 // We keep a reference to the underlying IMesh data so a hull can be built
196 meshDesc.referenceCount = 1; 208 meshDesc.referenceCount = 1;
197 DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}", 209 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}",
198 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); 210 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
199 ret = true; 211 ret = true;
200 } 212 }
@@ -207,7 +219,7 @@ public sealed class BSShapeCollection : IDisposable
207 { 219 {
208 // There is an existing instance of this hull. 220 // There is an existing instance of this hull.
209 hullDesc.referenceCount++; 221 hullDesc.referenceCount++;
210 DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}", 222 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}",
211 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); 223 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
212 } 224 }
213 else 225 else
@@ -216,7 +228,7 @@ public sealed class BSShapeCollection : IDisposable
216 hullDesc.ptr = shape.ptr; 228 hullDesc.ptr = shape.ptr;
217 hullDesc.shapeKey = shape.shapeKey; 229 hullDesc.shapeKey = shape.shapeKey;
218 hullDesc.referenceCount = 1; 230 hullDesc.referenceCount = 1;
219 DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}", 231 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}",
220 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); 232 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
221 ret = true; 233 ret = true;
222 234
@@ -236,17 +248,17 @@ public sealed class BSShapeCollection : IDisposable
236 // Release the usage of a shape. 248 // Release the usage of a shape.
237 public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback) 249 public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback)
238 { 250 {
239 if (shape.ptr == IntPtr.Zero) 251 if (!shape.HasPhysicalShape)
240 return; 252 return;
241 253
242 PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceShape", delegate() 254 PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceShape", delegate()
243 { 255 {
244 if (shape.ptr != IntPtr.Zero) 256 if (shape.HasPhysicalShape)
245 { 257 {
246 if (shape.isNativeShape) 258 if (shape.isNativeShape)
247 { 259 {
248 // Native shapes are not tracked and are released immediately 260 // Native shapes are not tracked and are released immediately
249 DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}", 261 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}",
250 BSScene.DetailLogZero, shape.ptr.ToString("X"), inTaintTime); 262 BSScene.DetailLogZero, shape.ptr.ToString("X"), inTaintTime);
251 if (shapeCallback != null) shapeCallback(shape); 263 if (shapeCallback != null) shapeCallback(shape);
252 BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr); 264 BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
@@ -286,7 +298,7 @@ public sealed class BSShapeCollection : IDisposable
286 if (shapeCallback != null) shapeCallback(shape); 298 if (shapeCallback != null) shapeCallback(shape);
287 meshDesc.lastReferenced = System.DateTime.Now; 299 meshDesc.lastReferenced = System.DateTime.Now;
288 Meshes[shape.shapeKey] = meshDesc; 300 Meshes[shape.shapeKey] = meshDesc;
289 DetailLog("{0},BSShapeCollection.DereferenceMesh,shape={1},refCnt={2}", 301 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceMesh,shape={1},refCnt={2}",
290 BSScene.DetailLogZero, shape, meshDesc.referenceCount); 302 BSScene.DetailLogZero, shape, meshDesc.referenceCount);
291 303
292 } 304 }
@@ -307,7 +319,7 @@ public sealed class BSShapeCollection : IDisposable
307 319
308 hullDesc.lastReferenced = System.DateTime.Now; 320 hullDesc.lastReferenced = System.DateTime.Now;
309 Hulls[shape.shapeKey] = hullDesc; 321 Hulls[shape.shapeKey] = hullDesc;
310 DetailLog("{0},BSShapeCollection.DereferenceHull,shape={1},refCnt={2}", 322 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceHull,shape={1},refCnt={2}",
311 BSScene.DetailLogZero, shape, hullDesc.referenceCount); 323 BSScene.DetailLogZero, shape, hullDesc.referenceCount);
312 } 324 }
313 } 325 }
@@ -325,13 +337,13 @@ public sealed class BSShapeCollection : IDisposable
325 // Failed the sanity check!! 337 // Failed the sanity check!!
326 PhysicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}", 338 PhysicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}",
327 LogHeader, shape.type, shape.ptr.ToString("X")); 339 LogHeader, shape.type, shape.ptr.ToString("X"));
328 DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}", 340 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}",
329 BSScene.DetailLogZero, shape.type, shape.ptr.ToString("X")); 341 BSScene.DetailLogZero, shape.type, shape.ptr.ToString("X"));
330 return; 342 return;
331 } 343 }
332 344
333 int numChildren = BulletSimAPI.GetNumberOfCompoundChildren2(shape.ptr); 345 int numChildren = BulletSimAPI.GetNumberOfCompoundChildren2(shape.ptr);
334 DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", BSScene.DetailLogZero, shape, numChildren); 346 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", BSScene.DetailLogZero, shape, numChildren);
335 347
336 for (int ii = numChildren - 1; ii >= 0; ii--) 348 for (int ii = numChildren - 1; ii >= 0; ii--)
337 { 349 {
@@ -379,7 +391,7 @@ public sealed class BSShapeCollection : IDisposable
379 } 391 }
380 } 392 }
381 393
382 DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo); 394 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo);
383 395
384 if (shapeInfo.type != BSPhysicsShapeType.SHAPE_UNKNOWN) 396 if (shapeInfo.type != BSPhysicsShapeType.SHAPE_UNKNOWN)
385 { 397 {
@@ -408,19 +420,19 @@ public sealed class BSShapeCollection : IDisposable
408 if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE) 420 if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE)
409 { 421 {
410 // an avatar capsule is close to a native shape (it is not shared) 422 // an avatar capsule is close to a native shape (it is not shared)
411 ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_CAPSULE, 423 GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_CAPSULE,
412 FixedShapeKey.KEY_CAPSULE, shapeCallback); 424 FixedShapeKey.KEY_CAPSULE, shapeCallback);
413 DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape); 425 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape);
414 ret = true; 426 ret = true;
415 haveShape = true; 427 haveShape = true;
416 } 428 }
417 429
418 // Compound shapes are handled special as they are rebuilt from scratch. 430 // Compound shapes are handled special as they are rebuilt from scratch.
419 // This isn't too great a hardship since most of the child shapes will already been created. 431 // This isn't too great a hardship since most of the child shapes will have already been created.
420 if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND) 432 if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND)
421 { 433 {
422 ret = GetReferenceToCompoundShape(prim, shapeCallback); 434 ret = GetReferenceToCompoundShape(prim, shapeCallback);
423 DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape); 435 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape);
424 haveShape = true; 436 haveShape = true;
425 } 437 }
426 438
@@ -433,7 +445,7 @@ public sealed class BSShapeCollection : IDisposable
433 } 445 }
434 446
435 // Create a mesh/hull shape or a native shape if 'nativeShapePossible' is 'true'. 447 // Create a mesh/hull shape or a native shape if 'nativeShapePossible' is 'true'.
436 private bool CreateGeomNonSpecial(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback) 448 public bool CreateGeomNonSpecial(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback)
437 { 449 {
438 bool ret = false; 450 bool ret = false;
439 bool haveShape = false; 451 bool haveShape = false;
@@ -453,19 +465,27 @@ public sealed class BSShapeCollection : IDisposable
453 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100 465 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
454 && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) ) 466 && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) )
455 { 467 {
468 // Get the scale of any existing shape so we can see if the new shape is same native type and same size.
469 OMV.Vector3 scaleOfExistingShape = OMV.Vector3.Zero;
470 if (prim.PhysShape.HasPhysicalShape)
471 scaleOfExistingShape = BulletSimAPI.GetLocalScaling2(prim.PhysShape.ptr);
472
473 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,maybeNative,force={1},primScale={2},primSize={3},primShape={4}",
474 prim.LocalID, forceRebuild, prim.Scale, prim.Size, prim.PhysShape.type);
475
456 // It doesn't look like Bullet scales spheres so make sure the scales are all equal 476 // It doesn't look like Bullet scales spheres so make sure the scales are all equal
457 if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1) 477 if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1)
458 && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z) 478 && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z)
459 { 479 {
460 haveShape = true; 480 haveShape = true;
461 if (forceRebuild 481 if (forceRebuild
462 || prim.Scale != prim.Size 482 || prim.Scale != scaleOfExistingShape
463 || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_SPHERE 483 || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_SPHERE
464 ) 484 )
465 { 485 {
466 ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_SPHERE, 486 ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_SPHERE,
467 FixedShapeKey.KEY_SPHERE, shapeCallback); 487 FixedShapeKey.KEY_SPHERE, shapeCallback);
468 DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}", 488 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}",
469 prim.LocalID, forceRebuild, prim.PhysShape); 489 prim.LocalID, forceRebuild, prim.PhysShape);
470 } 490 }
471 } 491 }
@@ -473,13 +493,13 @@ public sealed class BSShapeCollection : IDisposable
473 { 493 {
474 haveShape = true; 494 haveShape = true;
475 if (forceRebuild 495 if (forceRebuild
476 || prim.Scale != prim.Size 496 || prim.Scale != scaleOfExistingShape
477 || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_BOX 497 || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_BOX
478 ) 498 )
479 { 499 {
480 ret = GetReferenceToNativeShape( prim, BSPhysicsShapeType.SHAPE_BOX, 500 ret = GetReferenceToNativeShape( prim, BSPhysicsShapeType.SHAPE_BOX,
481 FixedShapeKey.KEY_BOX, shapeCallback); 501 FixedShapeKey.KEY_BOX, shapeCallback);
482 DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}", 502 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}",
483 prim.LocalID, forceRebuild, prim.PhysShape); 503 prim.LocalID, forceRebuild, prim.PhysShape);
484 } 504 }
485 } 505 }
@@ -504,13 +524,13 @@ public sealed class BSShapeCollection : IDisposable
504 { 524 {
505 // Update prim.BSShape to reference a hull of this shape. 525 // Update prim.BSShape to reference a hull of this shape.
506 ret = GetReferenceToHull(prim,shapeCallback); 526 ret = GetReferenceToHull(prim,shapeCallback);
507 DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}", 527 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}",
508 prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X")); 528 prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X"));
509 } 529 }
510 else 530 else
511 { 531 {
512 ret = GetReferenceToMesh(prim, shapeCallback); 532 ret = GetReferenceToMesh(prim, shapeCallback);
513 DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}", 533 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}",
514 prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X")); 534 prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X"));
515 } 535 }
516 return ret; 536 return ret;
@@ -528,9 +548,10 @@ public sealed class BSShapeCollection : IDisposable
528 BulletShape newShape = BuildPhysicalNativeShape(prim, shapeType, shapeKey); 548 BulletShape newShape = BuildPhysicalNativeShape(prim, shapeType, shapeKey);
529 549
530 // Don't need to do a 'ReferenceShape()' here because native shapes are not shared. 550 // Don't need to do a 'ReferenceShape()' here because native shapes are not shared.
531 DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}", 551 if (DDetail) DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}",
532 prim.LocalID, newShape, prim.Scale); 552 prim.LocalID, newShape, prim.Scale);
533 553
554 // native shapes are scaled by Bullet
534 prim.PhysShape = newShape; 555 prim.PhysShape = newShape;
535 return true; 556 return true;
536 } 557 }
@@ -554,16 +575,14 @@ public sealed class BSShapeCollection : IDisposable
554 newShape = new BulletShape( 575 newShape = new BulletShape(
555 BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1f, 1f, prim.Scale) 576 BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1f, 1f, prim.Scale)
556 , shapeType); 577 , shapeType);
557 DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale); 578 if (DDetail) DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale);
558 } 579 }
559 else 580 else
560 { 581 {
561 // Native shapes are scaled in Bullet so set the scaling to the size 582 // Native shapes are scaled in Bullet so set the scaling to the size
562 prim.Scale = prim.Size;
563 nativeShapeData.Scale = prim.Scale;
564 newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, nativeShapeData), shapeType); 583 newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, nativeShapeData), shapeType);
565 } 584 }
566 if (newShape.ptr == IntPtr.Zero) 585 if (!newShape.HasPhysicalShape)
567 { 586 {
568 PhysicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}", 587 PhysicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}",
569 LogHeader, prim.LocalID, shapeType); 588 LogHeader, prim.LocalID, shapeType);
@@ -580,7 +599,7 @@ public sealed class BSShapeCollection : IDisposable
580 // Called at taint-time! 599 // Called at taint-time!
581 private bool GetReferenceToMesh(BSPhysObject prim, ShapeDestructionCallback shapeCallback) 600 private bool GetReferenceToMesh(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
582 { 601 {
583 BulletShape newShape = new BulletShape(IntPtr.Zero); 602 BulletShape newShape = new BulletShape();
584 603
585 float lod; 604 float lod;
586 System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod); 605 System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
@@ -589,7 +608,7 @@ public sealed class BSShapeCollection : IDisposable
589 if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_MESH) 608 if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_MESH)
590 return false; 609 return false;
591 610
592 DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2}", 611 if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2}",
593 prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newMeshKey.ToString("X")); 612 prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newMeshKey.ToString("X"));
594 613
595 // Since we're recreating new, get rid of the reference to the previous shape 614 // Since we're recreating new, get rid of the reference to the previous shape
@@ -601,8 +620,6 @@ public sealed class BSShapeCollection : IDisposable
601 620
602 ReferenceShape(newShape); 621 ReferenceShape(newShape);
603 622
604 // meshes are already scaled by the meshmerizer
605 prim.Scale = new OMV.Vector3(1f, 1f, 1f);
606 prim.PhysShape = newShape; 623 prim.PhysShape = newShape;
607 624
608 return true; // 'true' means a new shape has been added to this prim 625 return true; // 'true' means a new shape has been added to this prim
@@ -620,8 +637,7 @@ public sealed class BSShapeCollection : IDisposable
620 } 637 }
621 else 638 else
622 { 639 {
623 // Pass false for physicalness as this creates some sort of bounding box which we don't need 640 meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true, false, false, false);
624 meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false);
625 641
626 if (meshData != null) 642 if (meshData != null)
627 { 643 {
@@ -663,7 +679,7 @@ public sealed class BSShapeCollection : IDisposable
663 if (newHullKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_HULL) 679 if (newHullKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_HULL)
664 return false; 680 return false;
665 681
666 DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}", 682 if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}",
667 prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newHullKey.ToString("X")); 683 prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newHullKey.ToString("X"));
668 684
669 // Remove usage of the previous shape. 685 // Remove usage of the previous shape.
@@ -674,8 +690,6 @@ public sealed class BSShapeCollection : IDisposable
674 690
675 ReferenceShape(newShape); 691 ReferenceShape(newShape);
676 692
677 // hulls are already scaled by the meshmerizer
678 prim.Scale = new OMV.Vector3(1f, 1f, 1f);
679 prim.PhysShape = newShape; 693 prim.PhysShape = newShape;
680 return true; // 'true' means a new shape has been added to this prim 694 return true; // 'true' means a new shape has been added to this prim
681 } 695 }
@@ -694,8 +708,8 @@ public sealed class BSShapeCollection : IDisposable
694 else 708 else
695 { 709 {
696 // Build a new hull in the physical world 710 // Build a new hull in the physical world
697 // Pass false for physicalness as this creates some sort of bounding box which we don't need 711 // Pass true for physicalness as this creates some sort of bounding box which we don't need
698 IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false); 712 IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true, false, false, false);
699 if (meshData != null) 713 if (meshData != null)
700 { 714 {
701 715
@@ -784,7 +798,7 @@ public sealed class BSShapeCollection : IDisposable
784 BulletShape newShape = new BulletShape(hullPtr, BSPhysicsShapeType.SHAPE_HULL); 798 BulletShape newShape = new BulletShape(hullPtr, BSPhysicsShapeType.SHAPE_HULL);
785 newShape.shapeKey = newHullKey; 799 newShape.shapeKey = newHullKey;
786 800
787 return newShape; // 'true' means a new shape has been added to this prim 801 return newShape;
788 } 802 }
789 803
790 // Callback from convex hull creater with a newly created hull. 804 // Callback from convex hull creater with a newly created hull.
@@ -809,7 +823,7 @@ public sealed class BSShapeCollection : IDisposable
809 // Create the shape for the root prim and add it to the compound shape. Cannot be a native shape. 823 // Create the shape for the root prim and add it to the compound shape. Cannot be a native shape.
810 CreateGeomMeshOrHull(prim, shapeCallback); 824 CreateGeomMeshOrHull(prim, shapeCallback);
811 BulletSimAPI.AddChildShapeToCompoundShape2(cShape.ptr, prim.PhysShape.ptr, OMV.Vector3.Zero, OMV.Quaternion.Identity); 825 BulletSimAPI.AddChildShapeToCompoundShape2(cShape.ptr, prim.PhysShape.ptr, OMV.Vector3.Zero, OMV.Quaternion.Identity);
812 DetailLog("{0},BSShapeCollection.GetReferenceToCompoundShape,addRootPrim,compShape={1},rootShape={2}", 826 if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToCompoundShape,addRootPrim,compShape={1},rootShape={2}",
813 prim.LocalID, cShape, prim.PhysShape); 827 prim.LocalID, cShape, prim.PhysShape);
814 828
815 prim.PhysShape = cShape; 829 prim.PhysShape = cShape;
@@ -851,7 +865,7 @@ public sealed class BSShapeCollection : IDisposable
851 private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim) 865 private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim)
852 { 866 {
853 // If the shape was successfully created, nothing more to do 867 // If the shape was successfully created, nothing more to do
854 if (newShape.ptr != IntPtr.Zero) 868 if (newShape.HasPhysicalShape)
855 return newShape; 869 return newShape;
856 870
857 // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset 871 // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset
@@ -910,7 +924,7 @@ public sealed class BSShapeCollection : IDisposable
910 bool ret = false; 924 bool ret = false;
911 925
912 // the mesh, hull or native shape must have already been created in Bullet 926 // the mesh, hull or native shape must have already been created in Bullet
913 bool mustRebuild = (prim.PhysBody.ptr == IntPtr.Zero); 927 bool mustRebuild = !prim.PhysBody.HasPhysicalBody;
914 928
915 // If there is an existing body, verify it's of an acceptable type. 929 // If there is an existing body, verify it's of an acceptable type.
916 // If not a solid object, body is a GhostObject. Otherwise a RigidBody. 930 // If not a solid object, body is a GhostObject. Otherwise a RigidBody.
@@ -936,13 +950,13 @@ public sealed class BSShapeCollection : IDisposable
936 { 950 {
937 bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr, 951 bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr,
938 prim.LocalID, prim.RawPosition, prim.RawOrientation); 952 prim.LocalID, prim.RawPosition, prim.RawOrientation);
939 DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); 953 if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
940 } 954 }
941 else 955 else
942 { 956 {
943 bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr, 957 bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
944 prim.LocalID, prim.RawPosition, prim.RawOrientation); 958 prim.LocalID, prim.RawPosition, prim.RawOrientation);
945 DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); 959 if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
946 } 960 }
947 aBody = new BulletBody(prim.LocalID, bodyPtr); 961 aBody = new BulletBody(prim.LocalID, bodyPtr);
948 962
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs
index 3ca756c..2b120d6 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs
@@ -93,7 +93,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
93 { 93 {
94 m_mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, m_mapInfo.ID, 94 m_mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, m_mapInfo.ID,
95 m_mapInfo.minCoords, m_mapInfo.maxCoords, 95 m_mapInfo.minCoords, m_mapInfo.maxCoords,
96 m_mapInfo.heightMap, BSTerrainManager.TERRAIN_COLLISION_MARGIN); 96 m_mapInfo.heightMap, PhysicsScene.Params.terrainCollisionMargin);
97 97
98 // Create the terrain shape from the mapInfo 98 // Create the terrain shape from the mapInfo
99 m_mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(m_mapInfo.Ptr), 99 m_mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(m_mapInfo.Ptr),
@@ -121,9 +121,8 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
121 // redo its bounding box now that it is in the world 121 // redo its bounding box now that it is in the world
122 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr); 122 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
123 123
124 BulletSimAPI.SetCollisionFilterMask2(m_mapInfo.terrainBody.ptr, 124 m_mapInfo.terrainBody.collisionType = CollisionType.Terrain;
125 (uint)CollisionFilterGroups.TerrainFilter, 125 m_mapInfo.terrainBody.ApplyCollisionMask();
126 (uint)CollisionFilterGroups.TerrainMask);
127 126
128 // Make it so the terrain will not move or be considered for movement. 127 // Make it so the terrain will not move or be considered for movement.
129 BulletSimAPI.ForceActivationState2(m_mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION); 128 BulletSimAPI.ForceActivationState2(m_mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
@@ -136,7 +135,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
136 { 135 {
137 if (m_mapInfo != null) 136 if (m_mapInfo != null)
138 { 137 {
139 if (m_mapInfo.terrainBody.ptr != IntPtr.Zero) 138 if (m_mapInfo.terrainBody.HasPhysicalBody)
140 { 139 {
141 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr); 140 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
142 // Frees both the body and the shape. 141 // Frees both the body and the shape.
@@ -148,7 +147,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
148 } 147 }
149 148
150 // The passed position is relative to the base of the region. 149 // The passed position is relative to the base of the region.
151 public override float GetHeightAtXYZ(Vector3 pos) 150 public override float GetTerrainHeightAtXYZ(Vector3 pos)
152 { 151 {
153 float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET; 152 float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
154 153
@@ -166,5 +165,11 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
166 } 165 }
167 return ret; 166 return ret;
168 } 167 }
168
169 // The passed position is relative to the base of the region.
170 public override float GetWaterLevelAtXYZ(Vector3 pos)
171 {
172 return PhysicsScene.SimpleWaterLevel;
173 }
169} 174}
170} 175}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
index 23fcfd3..3428b9c 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
@@ -62,11 +62,12 @@ public abstract class BSTerrainPhys : IDisposable
62 ID = id; 62 ID = id;
63 } 63 }
64 public abstract void Dispose(); 64 public abstract void Dispose();
65 public abstract float GetHeightAtXYZ(Vector3 pos); 65 public abstract float GetTerrainHeightAtXYZ(Vector3 pos);
66 public abstract float GetWaterLevelAtXYZ(Vector3 pos);
66} 67}
67 68
68// ========================================================================================== 69// ==========================================================================================
69public sealed class BSTerrainManager 70public sealed class BSTerrainManager : IDisposable
70{ 71{
71 static string LogHeader = "[BULLETSIM TERRAIN MANAGER]"; 72 static string LogHeader = "[BULLETSIM TERRAIN MANAGER]";
72 73
@@ -75,13 +76,12 @@ public sealed class BSTerrainManager
75 public const float HEIGHT_INITIALIZATION = 24.987f; 76 public const float HEIGHT_INITIALIZATION = 24.987f;
76 public const float HEIGHT_INITIAL_LASTHEIGHT = 24.876f; 77 public const float HEIGHT_INITIAL_LASTHEIGHT = 24.876f;
77 public const float HEIGHT_GETHEIGHT_RET = 24.765f; 78 public const float HEIGHT_GETHEIGHT_RET = 24.765f;
79 public const float WATER_HEIGHT_GETHEIGHT_RET = 19.998f;
78 80
79 // If the min and max height are equal, we reduce the min by this 81 // If the min and max height are equal, we reduce the min by this
80 // amount to make sure that a bounding box is built for the terrain. 82 // amount to make sure that a bounding box is built for the terrain.
81 public const float HEIGHT_EQUAL_FUDGE = 0.2f; 83 public const float HEIGHT_EQUAL_FUDGE = 0.2f;
82 84
83 public const float TERRAIN_COLLISION_MARGIN = 0.0f;
84
85 // Until the whole simulator is changed to pass us the region size, we rely on constants. 85 // Until the whole simulator is changed to pass us the region size, we rely on constants.
86 public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); 86 public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
87 87
@@ -122,6 +122,11 @@ public sealed class BSTerrainManager
122 MegaRegionParentPhysicsScene = null; 122 MegaRegionParentPhysicsScene = null;
123 } 123 }
124 124
125 public void Dispose()
126 {
127 ReleaseGroundPlaneAndTerrain();
128 }
129
125 // Create the initial instance of terrain and the underlying ground plane. 130 // Create the initial instance of terrain and the underlying ground plane.
126 // This is called from the initialization routine so we presume it is 131 // This is called from the initialization routine so we presume it is
127 // safe to call Bullet in real time. We hope no one is moving prims around yet. 132 // safe to call Bullet in real time. We hope no one is moving prims around yet.
@@ -129,7 +134,8 @@ public sealed class BSTerrainManager
129 { 134 {
130 // The ground plane is here to catch things that are trying to drop to negative infinity 135 // The ground plane is here to catch things that are trying to drop to negative infinity
131 BulletShape groundPlaneShape = new BulletShape( 136 BulletShape groundPlaneShape = new BulletShape(
132 BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f, TERRAIN_COLLISION_MARGIN), 137 BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f,
138 PhysicsScene.Params.terrainCollisionMargin),
133 BSPhysicsShapeType.SHAPE_GROUNDPLANE); 139 BSPhysicsShapeType.SHAPE_GROUNDPLANE);
134 m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID, 140 m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID,
135 BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID, 141 BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID,
@@ -139,8 +145,8 @@ public sealed class BSTerrainManager
139 // Ground plane does not move 145 // Ground plane does not move
140 BulletSimAPI.ForceActivationState2(m_groundPlane.ptr, ActivationState.DISABLE_SIMULATION); 146 BulletSimAPI.ForceActivationState2(m_groundPlane.ptr, ActivationState.DISABLE_SIMULATION);
141 // Everything collides with the ground plane. 147 // Everything collides with the ground plane.
142 BulletSimAPI.SetCollisionFilterMask2(m_groundPlane.ptr, 148 m_groundPlane.collisionType = CollisionType.Groundplane;
143 (uint)CollisionFilterGroups.GroundPlaneFilter, (uint)CollisionFilterGroups.GroundPlaneMask); 149 m_groundPlane.ApplyCollisionMask();
144 150
145 // Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain. 151 // Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain.
146 BSTerrainPhys initialTerrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize); 152 BSTerrainPhys initialTerrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize);
@@ -150,13 +156,13 @@ public sealed class BSTerrainManager
150 // Release all the terrain structures we might have allocated 156 // Release all the terrain structures we might have allocated
151 public void ReleaseGroundPlaneAndTerrain() 157 public void ReleaseGroundPlaneAndTerrain()
152 { 158 {
153 if (m_groundPlane.ptr != IntPtr.Zero) 159 if (m_groundPlane.HasPhysicalBody)
154 { 160 {
155 if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr)) 161 if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr))
156 { 162 {
157 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_groundPlane.ptr); 163 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_groundPlane.ptr);
158 } 164 }
159 m_groundPlane.ptr = IntPtr.Zero; 165 m_groundPlane.Clear();
160 } 166 }
161 167
162 ReleaseTerrain(); 168 ReleaseTerrain();
@@ -165,17 +171,22 @@ public sealed class BSTerrainManager
165 // Release all the terrain we have allocated 171 // Release all the terrain we have allocated
166 public void ReleaseTerrain() 172 public void ReleaseTerrain()
167 { 173 {
168 foreach (KeyValuePair<Vector3, BSTerrainPhys> kvp in m_terrains) 174 lock (m_terrains)
169 { 175 {
170 kvp.Value.Dispose(); 176 foreach (KeyValuePair<Vector3, BSTerrainPhys> kvp in m_terrains)
177 {
178 kvp.Value.Dispose();
179 }
180 m_terrains.Clear();
171 } 181 }
172 m_terrains.Clear();
173 } 182 }
174 183
175 // The simulator wants to set a new heightmap for the terrain. 184 // The simulator wants to set a new heightmap for the terrain.
176 public void SetTerrain(float[] heightMap) { 185 public void SetTerrain(float[] heightMap) {
177 float[] localHeightMap = heightMap; 186 float[] localHeightMap = heightMap;
178 PhysicsScene.TaintedObject("TerrainManager.SetTerrain", delegate() 187 // If there are multiple requests for changes to the same terrain between ticks,
188 // only do that last one.
189 PhysicsScene.PostTaintObject("TerrainManager.SetTerrain-"+ m_worldOffset.ToString(), 0, delegate()
179 { 190 {
180 if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null) 191 if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null)
181 { 192 {
@@ -211,6 +222,7 @@ public sealed class BSTerrainManager
211 // terrain shape is created and added to the body. 222 // terrain shape is created and added to the body.
212 // This call is most often used to update the heightMap and parameters of the terrain. 223 // This call is most often used to update the heightMap and parameters of the terrain.
213 // (The above does suggest that some simplification/refactoring is in order.) 224 // (The above does suggest that some simplification/refactoring is in order.)
225 // Called during taint-time.
214 private void UpdateTerrain(uint id, float[] heightMap, 226 private void UpdateTerrain(uint id, float[] heightMap,
215 Vector3 minCoords, Vector3 maxCoords, bool inTaintTime) 227 Vector3 minCoords, Vector3 maxCoords, bool inTaintTime)
216 { 228 {
@@ -220,7 +232,7 @@ public sealed class BSTerrainManager
220 // Find high and low points of passed heightmap. 232 // Find high and low points of passed heightmap.
221 // The min and max passed in is usually the area objects can be in (maximum 233 // The min and max passed in is usually the area objects can be in (maximum
222 // object height, for instance). The terrain wants the bounding box for the 234 // object height, for instance). The terrain wants the bounding box for the
223 // terrain so we replace passed min and max Z with the actual terrain min/max Z. 235 // terrain so replace passed min and max Z with the actual terrain min/max Z.
224 float minZ = float.MaxValue; 236 float minZ = float.MaxValue;
225 float maxZ = float.MinValue; 237 float maxZ = float.MinValue;
226 foreach (float height in heightMap) 238 foreach (float height in heightMap)
@@ -238,15 +250,15 @@ public sealed class BSTerrainManager
238 250
239 Vector3 terrainRegionBase = new Vector3(minCoords.X, minCoords.Y, 0f); 251 Vector3 terrainRegionBase = new Vector3(minCoords.X, minCoords.Y, 0f);
240 252
241 BSTerrainPhys terrainPhys; 253 lock (m_terrains)
242 if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys))
243 { 254 {
244 // There is already a terrain in this spot. Free the old and build the new. 255 BSTerrainPhys terrainPhys;
245 DetailLog("{0},UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}", 256 if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys))
246 BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords);
247
248 PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateTerrain:UpdateExisting", delegate()
249 { 257 {
258 // There is already a terrain in this spot. Free the old and build the new.
259 DetailLog("{0},UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}",
260 BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords);
261
250 // Remove old terrain from the collection 262 // Remove old terrain from the collection
251 m_terrains.Remove(terrainRegionBase); 263 m_terrains.Remove(terrainRegionBase);
252 // Release any physical memory it may be using. 264 // Release any physical memory it may be using.
@@ -271,35 +283,24 @@ public sealed class BSTerrainManager
271 // I hate doing this, but just bail 283 // I hate doing this, but just bail
272 return; 284 return;
273 } 285 }
274 }); 286 }
275 } 287 else
276 else 288 {
277 { 289 // We don't know about this terrain so either we are creating a new terrain or
278 // We don't know about this terrain so either we are creating a new terrain or 290 // our mega-prim child is giving us a new terrain to add to the phys world
279 // our mega-prim child is giving us a new terrain to add to the phys world
280
281 // if this is a child terrain, calculate a unique terrain id
282 uint newTerrainID = id;
283 if (newTerrainID >= BSScene.CHILDTERRAIN_ID)
284 newTerrainID = ++m_terrainCount;
285
286 float[] heightMapX = heightMap;
287 Vector3 minCoordsX = minCoords;
288 Vector3 maxCoordsX = maxCoords;
289 291
290 DetailLog("{0},UpdateTerrain:NewTerrain,call,id={1}, minC={2}, maxC={3}", 292 // if this is a child terrain, calculate a unique terrain id
291 BSScene.DetailLogZero, newTerrainID, minCoords, minCoords); 293 uint newTerrainID = id;
294 if (newTerrainID >= BSScene.CHILDTERRAIN_ID)
295 newTerrainID = ++m_terrainCount;
292 296
293 // Code that must happen at taint-time 297 DetailLog("{0},UpdateTerrain:NewTerrain,taint,newID={1},minCoord={2},maxCoord={3}",
294 PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateTerrain:NewTerrain", delegate() 298 BSScene.DetailLogZero, newTerrainID, minCoords, minCoords);
295 {
296 DetailLog("{0},UpdateTerrain:NewTerrain,taint,baseX={1},baseY={2}",
297 BSScene.DetailLogZero, minCoordsX.X, minCoordsX.Y);
298 BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords); 299 BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
299 m_terrains.Add(terrainRegionBase, newTerrainPhys); 300 m_terrains.Add(terrainRegionBase, newTerrainPhys);
300 301
301 m_terrainModified = true; 302 m_terrainModified = true;
302 }); 303 }
303 } 304 }
304 } 305 }
305 306
@@ -331,6 +332,13 @@ public sealed class BSTerrainManager
331 return newTerrainPhys; 332 return newTerrainPhys;
332 } 333 }
333 334
335 // Return 'true' of this position is somewhere in known physical terrain space
336 public bool IsWithinKnownTerrain(Vector3 pos)
337 {
338 Vector3 terrainBaseXYZ;
339 BSTerrainPhys physTerrain;
340 return GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ);
341 }
334 342
335 // Given an X and Y, find the height of the terrain. 343 // Given an X and Y, find the height of the terrain.
336 // Since we could be handling multiple terrains for a mega-region, 344 // Since we could be handling multiple terrains for a mega-region,
@@ -341,40 +349,74 @@ public sealed class BSTerrainManager
341 private float lastHeightTX = 999999f; 349 private float lastHeightTX = 999999f;
342 private float lastHeightTY = 999999f; 350 private float lastHeightTY = 999999f;
343 private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT; 351 private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT;
344 public float GetTerrainHeightAtXYZ(Vector3 loc) 352 public float GetTerrainHeightAtXYZ(Vector3 pos)
345 { 353 {
346 float tX = loc.X; 354 float tX = pos.X;
347 float tY = loc.Y; 355 float tY = pos.Y;
348 // You'd be surprized at the number of times this routine is called 356 // You'd be surprized at the number of times this routine is called
349 // with the same parameters as last time. 357 // with the same parameters as last time.
350 if (!m_terrainModified && lastHeightTX == tX && lastHeightTY == tY) 358 if (!m_terrainModified && (lastHeightTX == tX) && (lastHeightTY == tY))
351 return lastHeight; 359 return lastHeight;
360 m_terrainModified = false;
352 361
353 lastHeightTX = tX; 362 lastHeightTX = tX;
354 lastHeightTY = tY; 363 lastHeightTY = tY;
355 float ret = HEIGHT_GETHEIGHT_RET; 364 float ret = HEIGHT_GETHEIGHT_RET;
356 365
357 int offsetX = ((int)(tX / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X; 366 Vector3 terrainBaseXYZ;
358 int offsetY = ((int)(tY / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y;
359 Vector3 terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f);
360
361 BSTerrainPhys physTerrain; 367 BSTerrainPhys physTerrain;
362 if (m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain)) 368 if (GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ))
363 { 369 {
364 ret = physTerrain.GetHeightAtXYZ(loc - terrainBaseXYZ); 370 ret = physTerrain.GetTerrainHeightAtXYZ(pos - terrainBaseXYZ);
365 DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXYZ,loc={1},base={2},height={3}",
366 BSScene.DetailLogZero, loc, terrainBaseXYZ, ret);
367 } 371 }
368 else 372 else
369 { 373 {
370 PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}", 374 PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}",
371 LogHeader, PhysicsScene.RegionName, tX, tY); 375 LogHeader, PhysicsScene.RegionName, tX, tY);
376 DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXYZ,terrainNotFound,pos={1},base={2}",
377 BSScene.DetailLogZero, pos, terrainBaseXYZ);
372 } 378 }
373 m_terrainModified = false; 379
374 lastHeight = ret; 380 lastHeight = ret;
375 return ret; 381 return ret;
376 } 382 }
377 383
384 public float GetWaterLevelAtXYZ(Vector3 pos)
385 {
386 float ret = WATER_HEIGHT_GETHEIGHT_RET;
387
388 Vector3 terrainBaseXYZ;
389 BSTerrainPhys physTerrain;
390 if (GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ))
391 {
392 ret = physTerrain.GetWaterLevelAtXYZ(pos);
393 }
394 else
395 {
396 PhysicsScene.Logger.ErrorFormat("{0} GetWaterHeightAtXY: terrain not found: pos={1}, terrainBase={2}, height={3}",
397 LogHeader, PhysicsScene.RegionName, pos, terrainBaseXYZ, ret);
398 }
399 return ret;
400 }
401
402 // Given an address, return 'true' of there is a description of that terrain and output
403 // the descriptor class and the 'base' fo the addresses therein.
404 private bool GetTerrainPhysicalAtXYZ(Vector3 pos, out BSTerrainPhys outPhysTerrain, out Vector3 outTerrainBase)
405 {
406 int offsetX = ((int)(pos.X / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X;
407 int offsetY = ((int)(pos.Y / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y;
408 Vector3 terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f);
409
410 BSTerrainPhys physTerrain = null;
411 lock (m_terrains)
412 {
413 m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain);
414 }
415 outTerrainBase = terrainBaseXYZ;
416 outPhysTerrain = physTerrain;
417 return (physTerrain != null);
418 }
419
378 // Although no one seems to check this, I do support combining. 420 // Although no one seems to check this, I do support combining.
379 public bool SupportsCombining() 421 public bool SupportsCombining()
380 { 422 {
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs
index dca7150..6dc0d92 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs
@@ -88,11 +88,13 @@ public sealed class BSTerrainMesh : BSTerrainPhys
88 // Something is very messed up and a crash is in our future. 88 // Something is very messed up and a crash is in our future.
89 return; 89 return;
90 } 90 }
91 PhysicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,indices={1},indSz={2},vertices={3},vertSz={4}",
92 ID, indicesCount, indices.Length, verticesCount, vertices.Length);
91 93
92 m_terrainShape = new BulletShape(BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr, 94 m_terrainShape = new BulletShape(BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
93 indicesCount, indices, verticesCount, vertices), 95 indicesCount, indices, verticesCount, vertices),
94 BSPhysicsShapeType.SHAPE_MESH); 96 BSPhysicsShapeType.SHAPE_MESH);
95 if (m_terrainShape.ptr == IntPtr.Zero) 97 if (!m_terrainShape.HasPhysicalShape)
96 { 98 {
97 // DISASTER!! 99 // DISASTER!!
98 PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape", ID); 100 PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape", ID);
@@ -105,7 +107,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys
105 Quaternion rot = Quaternion.Identity; 107 Quaternion rot = Quaternion.Identity;
106 108
107 m_terrainBody = new BulletBody(id, BulletSimAPI.CreateBodyWithDefaultMotionState2( m_terrainShape.ptr, ID, pos, rot)); 109 m_terrainBody = new BulletBody(id, BulletSimAPI.CreateBodyWithDefaultMotionState2( m_terrainShape.ptr, ID, pos, rot));
108 if (m_terrainBody.ptr == IntPtr.Zero) 110 if (!m_terrainBody.HasPhysicalBody)
109 { 111 {
110 // DISASTER!! 112 // DISASTER!!
111 physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase); 113 physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase);
@@ -122,15 +124,14 @@ public sealed class BSTerrainMesh : BSTerrainPhys
122 // Static objects are not very massive. 124 // Static objects are not very massive.
123 BulletSimAPI.SetMassProps2(m_terrainBody.ptr, 0f, Vector3.Zero); 125 BulletSimAPI.SetMassProps2(m_terrainBody.ptr, 0f, Vector3.Zero);
124 126
125 // Return the new terrain to the world of physical objects 127 // Put the new terrain to the world of physical objects
126 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr); 128 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr);
127 129
128 // redo its bounding box now that it is in the world 130 // Redo its bounding box now that it is in the world
129 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_terrainBody.ptr); 131 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_terrainBody.ptr);
130 132
131 BulletSimAPI.SetCollisionFilterMask2(m_terrainBody.ptr, 133 m_terrainBody.collisionType = CollisionType.Terrain;
132 (uint)CollisionFilterGroups.TerrainFilter, 134 m_terrainBody.ApplyCollisionMask();
133 (uint)CollisionFilterGroups.TerrainMask);
134 135
135 // Make it so the terrain will not move or be considered for movement. 136 // Make it so the terrain will not move or be considered for movement.
136 BulletSimAPI.ForceActivationState2(m_terrainBody.ptr, ActivationState.DISABLE_SIMULATION); 137 BulletSimAPI.ForceActivationState2(m_terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
@@ -138,7 +139,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys
138 139
139 public override void Dispose() 140 public override void Dispose()
140 { 141 {
141 if (m_terrainBody.ptr != IntPtr.Zero) 142 if (m_terrainBody.HasPhysicalBody)
142 { 143 {
143 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr); 144 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr);
144 // Frees both the body and the shape. 145 // Frees both the body and the shape.
@@ -146,7 +147,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys
146 } 147 }
147 } 148 }
148 149
149 public override float GetHeightAtXYZ(Vector3 pos) 150 public override float GetTerrainHeightAtXYZ(Vector3 pos)
150 { 151 {
151 // For the moment use the saved heightmap to get the terrain height. 152 // For the moment use the saved heightmap to get the terrain height.
152 // TODO: raycast downward to find the true terrain below the position. 153 // TODO: raycast downward to find the true terrain below the position.
@@ -167,6 +168,12 @@ public sealed class BSTerrainMesh : BSTerrainPhys
167 return ret; 168 return ret;
168 } 169 }
169 170
171 // The passed position is relative to the base of the region.
172 public override float GetWaterLevelAtXYZ(Vector3 pos)
173 {
174 return PhysicsScene.SimpleWaterLevel;
175 }
176
170 // Convert the passed heightmap to mesh information suitable for CreateMeshShape2(). 177 // Convert the passed heightmap to mesh information suitable for CreateMeshShape2().
171 // Return 'true' if successfully created. 178 // Return 'true' if successfully created.
172 public static bool ConvertHeightmapToMesh( 179 public static bool ConvertHeightmapToMesh(
@@ -188,6 +195,11 @@ public sealed class BSTerrainMesh : BSTerrainPhys
188 // Simple mesh creation which assumes magnification == 1. 195 // Simple mesh creation which assumes magnification == 1.
189 // TODO: do a more general solution that scales, adds new vertices and smoothes the result. 196 // TODO: do a more general solution that scales, adds new vertices and smoothes the result.
190 197
198 // Create an array of vertices that is sizeX+1 by sizeY+1 (note the loop
199 // from zero to <= sizeX). The triangle indices are then generated as two triangles
200 // per heightmap point. There are sizeX by sizeY of these squares. The extra row and
201 // column of vertices are used to complete the triangles of the last row and column
202 // of the heightmap.
191 try 203 try
192 { 204 {
193 // One vertice per heightmap value plus the vertices off the top and bottom edge. 205 // One vertice per heightmap value plus the vertices off the top and bottom edge.
@@ -200,16 +212,18 @@ public sealed class BSTerrainMesh : BSTerrainPhys
200 float magY = (float)sizeY / extentY; 212 float magY = (float)sizeY / extentY;
201 physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,totVert={1},totInd={2},extentBase={3},magX={4},magY={5}", 213 physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,totVert={1},totInd={2},extentBase={3},magX={4},magY={5}",
202 BSScene.DetailLogZero, totalVertices, totalIndices, extentBase, magX, magY); 214 BSScene.DetailLogZero, totalVertices, totalIndices, extentBase, magX, magY);
215 float minHeight = float.MaxValue;
203 // Note that sizeX+1 vertices are created since there is land between this and the next region. 216 // Note that sizeX+1 vertices are created since there is land between this and the next region.
204 for (int yy = 0; yy <= sizeY; yy++) 217 for (int yy = 0; yy <= sizeY; yy++)
205 { 218 {
206 for (int xx = 0; xx <= sizeX; xx++) // Hint: the "<=" means we got through sizeX + 1 times 219 for (int xx = 0; xx <= sizeX; xx++) // Hint: the "<=" means we go around sizeX + 1 times
207 { 220 {
208 int offset = yy * sizeX + xx; 221 int offset = yy * sizeX + xx;
209 // Extend the height from the height from the last row or column 222 // Extend the height with the height from the last row or column
210 if (yy == sizeY) offset -= sizeX; 223 if (yy == sizeY) offset -= sizeX;
211 if (xx == sizeX) offset -= 1; 224 if (xx == sizeX) offset -= 1;
212 float height = heightMap[offset]; 225 float height = heightMap[offset];
226 minHeight = Math.Min(minHeight, height);
213 vertices[verticesCount + 0] = (float)xx * magX + extentBase.X; 227 vertices[verticesCount + 0] = (float)xx * magX + extentBase.X;
214 vertices[verticesCount + 1] = (float)yy * magY + extentBase.Y; 228 vertices[verticesCount + 1] = (float)yy * magY + extentBase.Y;
215 vertices[verticesCount + 2] = height + extentBase.Z; 229 vertices[verticesCount + 2] = height + extentBase.Z;
@@ -217,14 +231,12 @@ public sealed class BSTerrainMesh : BSTerrainPhys
217 } 231 }
218 } 232 }
219 verticesCount = verticesCount / 3; 233 verticesCount = verticesCount / 3;
220 physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,completeVerts,verCount={1}",
221 BSScene.DetailLogZero, verticesCount);
222 234
223 for (int yy = 0; yy < sizeY; yy++) 235 for (int yy = 0; yy < sizeY; yy++)
224 { 236 {
225 for (int xx = 0; xx < sizeX; xx++) 237 for (int xx = 0; xx < sizeX; xx++)
226 { 238 {
227 int offset = yy * sizeX + xx; 239 int offset = yy * (sizeX + 1) + xx;
228 // Each vertices is presumed to be the upper left corner of a box of two triangles 240 // Each vertices is presumed to be the upper left corner of a box of two triangles
229 indices[indicesCount + 0] = offset; 241 indices[indicesCount + 0] = offset;
230 indices[indicesCount + 1] = offset + 1; 242 indices[indicesCount + 1] = offset + 1;
@@ -235,8 +247,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys
235 indicesCount += 6; 247 indicesCount += 6;
236 } 248 }
237 } 249 }
238 physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,completeIndices,indCount={1}", // DEEBUG DEBUG DEBUG 250
239 LogHeader, indicesCount); // DEBUG
240 ret = true; 251 ret = true;
241 } 252 }
242 catch (Exception e) 253 catch (Exception e)
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
index e60a760..962b540 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
@@ -25,6 +25,7 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27using System; 27using System;
28using System.Collections.Generic;
28using System.Runtime.InteropServices; 29using System.Runtime.InteropServices;
29using System.Security; 30using System.Security;
30using System.Text; 31using System.Text;
@@ -32,93 +33,6 @@ using OpenMetaverse;
32 33
33namespace OpenSim.Region.Physics.BulletSPlugin { 34namespace OpenSim.Region.Physics.BulletSPlugin {
34 35
35// Classes to allow some type checking for the API
36// These hold pointers to allocated objects in the unmanaged space.
37
38// The physics engine controller class created at initialization
39public struct BulletSim
40{
41 public BulletSim(uint worldId, BSScene bss, IntPtr xx)
42 {
43 ptr = xx;
44 worldID = worldId;
45 physicsScene = bss;
46 }
47 public IntPtr ptr;
48 public uint worldID;
49 // The scene is only in here so very low level routines have a handle to print debug/error messages
50 public BSScene physicsScene;
51}
52
53// An allocated Bullet btRigidBody
54public struct BulletBody
55{
56 public BulletBody(uint id, IntPtr xx)
57 {
58 ID = id;
59 ptr = xx;
60 collisionFilter = 0;
61 collisionMask = 0;
62 }
63 public IntPtr ptr;
64 public uint ID;
65 public CollisionFilterGroups collisionFilter;
66 public CollisionFilterGroups collisionMask;
67 public override string ToString()
68 {
69 StringBuilder buff = new StringBuilder();
70 buff.Append("<id=");
71 buff.Append(ID.ToString());
72 buff.Append(",p=");
73 buff.Append(ptr.ToString("X"));
74 if (collisionFilter != 0 || collisionMask != 0)
75 {
76 buff.Append(",f=");
77 buff.Append(collisionFilter.ToString("X"));
78 buff.Append(",m=");
79 buff.Append(collisionMask.ToString("X"));
80 }
81 buff.Append(">");
82 return buff.ToString();
83 }
84}
85
86public struct BulletShape
87{
88 public BulletShape(IntPtr xx)
89 {
90 ptr = xx;
91 type=BSPhysicsShapeType.SHAPE_UNKNOWN;
92 shapeKey = (System.UInt64)FixedShapeKey.KEY_NONE;
93 isNativeShape = false;
94 }
95 public BulletShape(IntPtr xx, BSPhysicsShapeType typ)
96 {
97 ptr = xx;
98 type = typ;
99 shapeKey = 0;
100 isNativeShape = false;
101 }
102 public IntPtr ptr;
103 public BSPhysicsShapeType type;
104 public System.UInt64 shapeKey;
105 public bool isNativeShape;
106 public override string ToString()
107 {
108 StringBuilder buff = new StringBuilder();
109 buff.Append("<p=");
110 buff.Append(ptr.ToString("X"));
111 buff.Append(",s=");
112 buff.Append(type.ToString());
113 buff.Append(",k=");
114 buff.Append(shapeKey.ToString("X"));
115 buff.Append(",n=");
116 buff.Append(isNativeShape.ToString());
117 buff.Append(">");
118 return buff.ToString();
119 }
120}
121
122 // Constraint type values as defined by Bullet 36 // Constraint type values as defined by Bullet
123public enum ConstraintType : int 37public enum ConstraintType : int
124{ 38{
@@ -132,44 +46,6 @@ public enum ConstraintType : int
132 MAX_CONSTRAINT_TYPE 46 MAX_CONSTRAINT_TYPE
133} 47}
134 48
135// An allocated Bullet btConstraint
136public struct BulletConstraint
137{
138 public BulletConstraint(IntPtr xx)
139 {
140 ptr = xx;
141 }
142 public IntPtr ptr;
143}
144
145// An allocated HeightMapThing which holds various heightmap info.
146// Made a class rather than a struct so there would be only one
147// instance of this and C# will pass around pointers rather
148// than making copies.
149public class BulletHeightMapInfo
150{
151 public BulletHeightMapInfo(uint id, float[] hm, IntPtr xx) {
152 ID = id;
153 Ptr = xx;
154 heightMap = hm;
155 terrainRegionBase = Vector3.Zero;
156 minCoords = new Vector3(100f, 100f, 25f);
157 maxCoords = new Vector3(101f, 101f, 26f);
158 minZ = maxZ = 0f;
159 sizeX = sizeY = 256f;
160 }
161 public uint ID;
162 public IntPtr Ptr;
163 public float[] heightMap;
164 public Vector3 terrainRegionBase;
165 public Vector3 minCoords;
166 public Vector3 maxCoords;
167 public float sizeX, sizeY;
168 public float minZ, maxZ;
169 public BulletShape terrainShape;
170 public BulletBody terrainBody;
171}
172
173// =============================================================================== 49// ===============================================================================
174[StructLayout(LayoutKind.Sequential)] 50[StructLayout(LayoutKind.Sequential)]
175public struct ConvexHull 51public struct ConvexHull
@@ -287,6 +163,8 @@ public struct ConfigurationParameters
287 public float terrainFriction; 163 public float terrainFriction;
288 public float terrainHitFraction; 164 public float terrainHitFraction;
289 public float terrainRestitution; 165 public float terrainRestitution;
166 public float terrainCollisionMargin;
167
290 public float avatarFriction; 168 public float avatarFriction;
291 public float avatarStandingFriction; 169 public float avatarStandingFriction;
292 public float avatarDensity; 170 public float avatarDensity;
@@ -296,6 +174,8 @@ public struct ConfigurationParameters
296 public float avatarCapsuleHeight; 174 public float avatarCapsuleHeight;
297 public float avatarContactProcessingThreshold; 175 public float avatarContactProcessingThreshold;
298 176
177 public float vehicleAngularDamping;
178
299 public float maxPersistantManifoldPoolSize; 179 public float maxPersistantManifoldPoolSize;
300 public float maxCollisionAlgorithmPoolSize; 180 public float maxCollisionAlgorithmPoolSize;
301 public float shouldDisableContactPoolDynamicAllocation; 181 public float shouldDisableContactPoolDynamicAllocation;
@@ -353,56 +233,35 @@ public enum CollisionFlags : uint
353 CF_CHARACTER_OBJECT = 1 << 4, 233 CF_CHARACTER_OBJECT = 1 << 4,
354 CF_DISABLE_VISUALIZE_OBJECT = 1 << 5, 234 CF_DISABLE_VISUALIZE_OBJECT = 1 << 5,
355 CF_DISABLE_SPU_COLLISION_PROCESS = 1 << 6, 235 CF_DISABLE_SPU_COLLISION_PROCESS = 1 << 6,
356 // Following used by BulletSim to control collisions 236 // Following used by BulletSim to control collisions and updates
357 BS_SUBSCRIBE_COLLISION_EVENTS = 1 << 10, 237 BS_SUBSCRIBE_COLLISION_EVENTS = 1 << 10,
358 BS_FLOATS_ON_WATER = 1 << 11, 238 BS_FLOATS_ON_WATER = 1 << 11,
239 BS_VEHICLE_COLLISIONS = 1 << 12,
359 BS_NONE = 0, 240 BS_NONE = 0,
360 BS_ALL = 0xFFFFFFFF, 241 BS_ALL = 0xFFFFFFFF
361
362 // These are the collision flags switched depending on physical state.
363 // The other flags are used for other things and should not be fooled with.
364 BS_ACTIVE = CF_STATIC_OBJECT
365 | CF_KINEMATIC_OBJECT
366 | CF_NO_CONTACT_RESPONSE
367}; 242};
368 243
369// Values for collisions groups and masks 244// Values f collisions groups and masks
370public enum CollisionFilterGroups : uint 245public enum CollisionFilterGroups : uint
371{ 246{
372 // Don't use the bit definitions!! Define the use in a 247 // Don't use the bit definitions!! Define the use in a
373 // filter/mask definition below. This way collision interactions 248 // filter/mask definition below. This way collision interactions
374 // are more easily debugged. 249 // are more easily found and debugged.
375 BNoneFilter = 0, 250 BNoneGroup = 0,
376 BDefaultFilter = 1 << 0, 251 BDefaultGroup = 1 << 0,
377 BStaticFilter = 1 << 1, 252 BStaticGroup = 1 << 1,
378 BKinematicFilter = 1 << 2, 253 BKinematicGroup = 1 << 2,
379 BDebrisFilter = 1 << 3, 254 BDebrisGroup = 1 << 3,
380 BSensorTrigger = 1 << 4, 255 BSensorTrigger = 1 << 4,
381 BCharacterFilter = 1 << 5, 256 BCharacterGroup = 1 << 5,
382 BAllFilter = 0xFFFFFFFF, 257 BAllGroup = 0xFFFFFFFF,
383 // Filter groups defined by BulletSim 258 // Filter groups defined by BulletSim
384 BGroundPlaneFilter = 1 << 10, 259 BGroundPlaneGroup = 1 << 10,
385 BTerrainFilter = 1 << 11, 260 BTerrainGroup = 1 << 11,
386 BRaycastFilter = 1 << 12, 261 BRaycastGroup = 1 << 12,
387 BSolidFilter = 1 << 13, 262 BSolidGroup = 1 << 13,
388 BLinksetFilter = 1 << 14, 263 // BLinksetGroup = xx // a linkset proper is either static or dynamic
389 264 BLinksetChildGroup = 1 << 14,
390 // The collsion filters and masked are defined in one place -- don't want them scattered
391 AvatarFilter = BCharacterFilter,
392 AvatarMask = BAllFilter,
393 ObjectFilter = BSolidFilter,
394 ObjectMask = BAllFilter,
395 StaticObjectFilter = BStaticFilter,
396 StaticObjectMask = BAllFilter & ~BStaticFilter, // static objects don't collide with each other
397 LinksetFilter = BLinksetFilter,
398 LinksetMask = BAllFilter & ~BLinksetFilter, // linkset objects don't collide with each other
399 VolumeDetectFilter = BSensorTrigger,
400 VolumeDetectMask = ~BSensorTrigger,
401 TerrainFilter = BTerrainFilter,
402 TerrainMask = BAllFilter & ~BStaticFilter, // static objects on the ground don't collide
403 GroundPlaneFilter = BGroundPlaneFilter,
404 GroundPlaneMask = BAllFilter
405
406}; 265};
407 266
408// CFM controls the 'hardness' of the constraint. 0=fixed, 0..1=violatable. Default=0 267// CFM controls the 'hardness' of the constraint. 0=fixed, 0..1=violatable. Default=0
@@ -429,7 +288,7 @@ public enum ConstraintParamAxis : int
429 288
430// =============================================================================== 289// ===============================================================================
431static class BulletSimAPI { 290static class BulletSimAPI {
432 291// ===============================================================================
433// Link back to the managed code for outputting log messages 292// Link back to the managed code for outputting log messages
434[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 293[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
435public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)]string msg); 294public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)]string msg);
@@ -482,6 +341,9 @@ public static extern IntPtr BuildNativeShape2(IntPtr world, ShapeData shapeData)
482public static extern bool IsNativeShape2(IntPtr shape); 341public static extern bool IsNativeShape2(IntPtr shape);
483 342
484[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 343[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
344public static extern void SetShapeCollisionMargin(IntPtr shape, float margin);
345
346[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
485public static extern IntPtr BuildCapsuleShape2(IntPtr world, float radius, float height, Vector3 scale); 347public static extern IntPtr BuildCapsuleShape2(IntPtr world, float radius, float height, Vector3 scale);
486 348
487[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 349[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
@@ -938,7 +800,7 @@ public static extern IntPtr GetConstraintRef2(IntPtr obj, int index);
938public static extern int GetNumConstraintRefs2(IntPtr obj); 800public static extern int GetNumConstraintRefs2(IntPtr obj);
939 801
940[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 802[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
941public static extern void SetCollisionFilterMask2(IntPtr body, uint filter, uint mask); 803public static extern bool SetCollisionGroupMask2(IntPtr body, uint filter, uint mask);
942 804
943// ===================================================================================== 805// =====================================================================================
944// btCollisionShape entries 806// btCollisionShape entries
@@ -1000,13 +862,16 @@ public static extern void DumpRigidBody2(IntPtr sim, IntPtr collisionObject);
1000public static extern void DumpCollisionShape2(IntPtr sim, IntPtr collisionShape); 862public static extern void DumpCollisionShape2(IntPtr sim, IntPtr collisionShape);
1001 863
1002[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 864[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
865public static extern void DumpMapInfo2(IntPtr sim, IntPtr manInfo);
866
867[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1003public static extern void DumpConstraint2(IntPtr sim, IntPtr constrain); 868public static extern void DumpConstraint2(IntPtr sim, IntPtr constrain);
1004 869
1005[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 870[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1006public static extern void DumpAllInfo2(IntPtr sim); 871public static extern void DumpActivationInfo2(IntPtr sim);
1007 872
1008[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 873[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1009public static extern void DumpMapInfo2(IntPtr sim, IntPtr manInfo); 874public static extern void DumpAllInfo2(IntPtr sim);
1010 875
1011[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 876[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1012public static extern void DumpPhysicsStatistics2(IntPtr sim); 877public static extern void DumpPhysicsStatistics2(IntPtr sim);
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs
new file mode 100755
index 0000000..662177f
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs
@@ -0,0 +1,278 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30using OMV = OpenMetaverse;
31
32namespace OpenSim.Region.Physics.BulletSPlugin
33{
34// Classes to allow some type checking for the API
35// These hold pointers to allocated objects in the unmanaged space.
36
37// The physics engine controller class created at initialization
38public struct BulletSim
39{
40 public BulletSim(uint worldId, BSScene bss, IntPtr xx)
41 {
42 ptr = xx;
43 worldID = worldId;
44 physicsScene = bss;
45 }
46 public IntPtr ptr;
47 public uint worldID;
48 // The scene is only in here so very low level routines have a handle to print debug/error messages
49 public BSScene physicsScene;
50}
51
52// An allocated Bullet btRigidBody
53public struct BulletBody
54{
55 public BulletBody(uint id) : this(id, IntPtr.Zero)
56 {
57 }
58 public BulletBody(uint id, IntPtr xx)
59 {
60 ID = id;
61 ptr = xx;
62 collisionType = CollisionType.Static;
63 }
64 public IntPtr ptr;
65 public uint ID;
66 public CollisionType collisionType;
67
68 public void Clear()
69 {
70 ptr = IntPtr.Zero;
71 }
72 public bool HasPhysicalBody { get { return ptr != IntPtr.Zero; } }
73
74 // Apply the specificed collision mask into the physical world
75 public void ApplyCollisionMask()
76 {
77 // Should assert the body has been added to the physical world.
78 // (The collision masks are stored in the collision proxy cache which only exists for
79 // a collision body that is in the world.)
80 BulletSimAPI.SetCollisionGroupMask2(ptr,
81 BulletSimData.CollisionTypeMasks[collisionType].group,
82 BulletSimData.CollisionTypeMasks[collisionType].mask);
83 }
84
85 public override string ToString()
86 {
87 StringBuilder buff = new StringBuilder();
88 buff.Append("<id=");
89 buff.Append(ID.ToString());
90 buff.Append(",p=");
91 buff.Append(ptr.ToString("X"));
92 buff.Append(",c=");
93 buff.Append(collisionType);
94 buff.Append(">");
95 return buff.ToString();
96 }
97}
98
99public struct BulletShape
100{
101 public BulletShape(IntPtr xx) : this(xx, BSPhysicsShapeType.SHAPE_UNKNOWN)
102 {
103 }
104 public BulletShape(IntPtr xx, BSPhysicsShapeType typ)
105 {
106 ptr = xx;
107 type = typ;
108 shapeKey = (System.UInt64)FixedShapeKey.KEY_NONE;
109 isNativeShape = false;
110 }
111 public IntPtr ptr;
112 public BSPhysicsShapeType type;
113 public System.UInt64 shapeKey;
114 public bool isNativeShape;
115
116 public void Clear()
117 {
118 ptr = IntPtr.Zero;
119 }
120 public bool HasPhysicalShape { get { return ptr != IntPtr.Zero; } }
121
122 public override string ToString()
123 {
124 StringBuilder buff = new StringBuilder();
125 buff.Append("<p=");
126 buff.Append(ptr.ToString("X"));
127 buff.Append(",s=");
128 buff.Append(type.ToString());
129 buff.Append(",k=");
130 buff.Append(shapeKey.ToString("X"));
131 buff.Append(",n=");
132 buff.Append(isNativeShape.ToString());
133 buff.Append(">");
134 return buff.ToString();
135 }
136}
137
138// An allocated Bullet btConstraint
139public struct BulletConstraint
140{
141 public BulletConstraint(IntPtr xx)
142 {
143 ptr = xx;
144 }
145 public IntPtr ptr;
146
147 public void Clear()
148 {
149 ptr = IntPtr.Zero;
150 }
151 public bool HasPhysicalConstraint { get { return ptr != IntPtr.Zero; } }
152}
153
154// An allocated HeightMapThing which holds various heightmap info.
155// Made a class rather than a struct so there would be only one
156// instance of this and C# will pass around pointers rather
157// than making copies.
158public class BulletHeightMapInfo
159{
160 public BulletHeightMapInfo(uint id, float[] hm, IntPtr xx) {
161 ID = id;
162 Ptr = xx;
163 heightMap = hm;
164 terrainRegionBase = OMV.Vector3.Zero;
165 minCoords = new OMV.Vector3(100f, 100f, 25f);
166 maxCoords = new OMV.Vector3(101f, 101f, 26f);
167 minZ = maxZ = 0f;
168 sizeX = sizeY = 256f;
169 }
170 public uint ID;
171 public IntPtr Ptr;
172 public float[] heightMap;
173 public OMV.Vector3 terrainRegionBase;
174 public OMV.Vector3 minCoords;
175 public OMV.Vector3 maxCoords;
176 public float sizeX, sizeY;
177 public float minZ, maxZ;
178 public BulletShape terrainShape;
179 public BulletBody terrainBody;
180}
181
182// The general class of collsion object.
183public enum CollisionType
184{
185 Avatar,
186 Groundplane,
187 Terrain,
188 Static,
189 Dynamic,
190 VolumeDetect,
191 // Linkset, // A linkset should be either Static or Dynamic
192 LinksetChild,
193 Unknown
194};
195
196// Hold specification of group and mask collision flags for a CollisionType
197public struct CollisionTypeFilterGroup
198{
199 public CollisionTypeFilterGroup(CollisionType t, uint g, uint m)
200 {
201 type = t;
202 group = g;
203 mask = m;
204 }
205 public CollisionType type;
206 public uint group;
207 public uint mask;
208};
209
210 /* NOTE: old definitions kept for reference. Delete when things are working.
211 // The collsion filters and masked are defined in one place -- don't want them scattered
212 AvatarGroup = BCharacterGroup,
213 AvatarMask = BAllGroup,
214 ObjectGroup = BSolidGroup,
215 ObjectMask = BAllGroup,
216 StaticObjectGroup = BStaticGroup,
217 StaticObjectMask = AvatarGroup | ObjectGroup, // static things don't interact with much
218 LinksetGroup = BLinksetGroup,
219 LinksetMask = BAllGroup,
220 LinksetChildGroup = BLinksetChildGroup,
221 LinksetChildMask = BNoneGroup, // Linkset children disappear from the world
222 VolumeDetectGroup = BSensorTrigger,
223 VolumeDetectMask = ~BSensorTrigger,
224 TerrainGroup = BTerrainGroup,
225 TerrainMask = BAllGroup & ~BStaticGroup, // static objects on the ground don't collide
226 GroundPlaneGroup = BGroundPlaneGroup,
227 GroundPlaneMask = BAllGroup
228 */
229
230public static class BulletSimData
231{
232
233// Map of collisionTypes to flags for collision groups and masks.
234// As mentioned above, don't use the CollisionFilterGroups definitions directly in the code
235// but, instead, use references to this dictionary. Finding and debugging
236// collision flag problems will be made easier.
237public static Dictionary<CollisionType, CollisionTypeFilterGroup> CollisionTypeMasks
238 = new Dictionary<CollisionType, CollisionTypeFilterGroup>()
239{
240 { CollisionType.Avatar,
241 new CollisionTypeFilterGroup(CollisionType.Avatar,
242 (uint)CollisionFilterGroups.BCharacterGroup,
243 (uint)CollisionFilterGroups.BAllGroup)
244 },
245 { CollisionType.Groundplane,
246 new CollisionTypeFilterGroup(CollisionType.Groundplane,
247 (uint)CollisionFilterGroups.BGroundPlaneGroup,
248 (uint)CollisionFilterGroups.BAllGroup)
249 },
250 { CollisionType.Terrain,
251 new CollisionTypeFilterGroup(CollisionType.Terrain,
252 (uint)CollisionFilterGroups.BTerrainGroup,
253 (uint)(CollisionFilterGroups.BAllGroup & ~CollisionFilterGroups.BStaticGroup))
254 },
255 { CollisionType.Static,
256 new CollisionTypeFilterGroup(CollisionType.Static,
257 (uint)CollisionFilterGroups.BStaticGroup,
258 (uint)(CollisionFilterGroups.BCharacterGroup | CollisionFilterGroups.BSolidGroup))
259 },
260 { CollisionType.Dynamic,
261 new CollisionTypeFilterGroup(CollisionType.Dynamic,
262 (uint)CollisionFilterGroups.BSolidGroup,
263 (uint)(CollisionFilterGroups.BAllGroup))
264 },
265 { CollisionType.VolumeDetect,
266 new CollisionTypeFilterGroup(CollisionType.VolumeDetect,
267 (uint)CollisionFilterGroups.BSensorTrigger,
268 (uint)(~CollisionFilterGroups.BSensorTrigger))
269 },
270 { CollisionType.LinksetChild,
271 new CollisionTypeFilterGroup(CollisionType.LinksetChild,
272 (uint)CollisionFilterGroups.BTerrainGroup,
273 (uint)(CollisionFilterGroups.BNoneGroup))
274 },
275};
276
277}
278}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt
new file mode 100755
index 0000000..c084ab4
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt
@@ -0,0 +1,218 @@
1CURRENT PRIORITIES
2=================================================
3Smooth avatar movement with motor
4 Should motor update be all at taint-time?
5Enable vehicle border crossings (at least as poorly as ODE)
6 Terrain skirts
7 Avatar created in previous region and not new region when crossing border
8 Vehicle recreated in new sim at small Z value (offset from root value?) (DONE)
9Vehicle movement on terrain smoothness
10Vehicle script tuning/debugging
11 Avanti speed script
12 Weapon shooter script
13limitMotorUp calibration (more down?)
14
15CRASHES
16=================================================
1720121129.1411: editting/moving phys object across region boundries causes crash
18 getPos-> btRigidBody::upcast -> getBodyType -> BOOM
1920121128.1600: mesh object not rezzing (no physics mesh).
20 Causes many errors. Doesn't stop after first error with box shape.
21 Eventually crashes when deleting the object.
2220121206.1434: rez Sam-pan into OSGrid BulletSim11 region
23 Immediate simulator crash. Mono does not output any stacktrace and
24 log just stops after reporting taint-time linking of the linkset.
25
26VEHICLES TODO LIST:
27=================================================
28Border crossing with linked vehicle causes crash
29Vehicles (Move smoothly)
30Add vehicle collisions so IsColliding is properly reported.
31 Needed for banking, limitMotorUp, movementLimiting, ...
32Some vehicles should not be able to turn if no speed or off ground.
33Cannot edit/move a vehicle being ridden: it jumps back to the origional position.
34Neb car jiggling left and right
35 Happens on terrain and any other mesh object. Flat cubes are much smoother.
36 This has been reduced but not eliminated.
37Implement referenceFrame for all the motion routines.
38Angular motion around Z moves the vehicle in world Z and not vehicle Z in ODE.
39 Verify that angular motion specified around Z moves in the vehicle coordinates.
40Verify llGetVel() is returning a smooth and good value for vehicle movement.
41llGetVel() should return the root's velocity if requested in a child prim.
42Implement function efficiency for lineaar and angular motion.
43After getting off a vehicle, the root prim is phantom (can be walked through)
44 Need to force a position update for the root prim after compound shape destruction
45Linkset explosion after three "rides" on Nebadon lite vehicle (LinksetConstraint)
46For limitMotorUp, use raycast down to find if vehicle is in the air.
47Remove vehicle angular velocity zeroing in BSPrim.UpdateProperties().
48 A kludge that isn't fixing the real problem of Bullet adding extra motion.
49Incorporate inter-relationship of angular corrections. For instance, angularDeflection
50 and angularMotorUp will compute same X or Y correction. When added together
51 creates over-correction and over-shoot and wabbling.
52
53BULLETSIM TODO LIST:
54=================================================
55Revisit CollisionMargin. Builders notice the 0.04 spacing between prims.
56Duplicating a physical prim causes old prim to jump away
57 Dup a phys prim and the original become unselected and thus interacts w/ selected prim.
58Scenes with hundred of thousands of static objects take a lot of physics CPU time.
59BSPrim.Force should set a continious force on the prim. The force should be
60 applied each tick. Some limits?
61Gun sending shooter flying.
62Collision margin (gap between physical objects lying on each other)
63Boundry checking (crashes related to crossing boundry)
64 Add check for border edge position for avatars and objects.
65 Verify the events are created for border crossings.
66Avatar rotation (check out changes to ScenePresence for physical rotation)
67Avatar running (what does phys engine need to do?)
68Small physical objects do not interact correctly
69 Create chain of .5x.5x.1 torui and make all but top physical so to hang.
70 The chain will fall apart and pairs will dance around on ground
71 Chains of 1x1x.2 will stay connected but will dance.
72 Chains above 2x2x.4 are move stable and get stablier as torui get larger.
73Add PID motor for avatar movement (slow to stop, ...)
74setForce should set a constant force. Different than AddImpulse.
75Implement raycast.
76Implement ShapeCollection.Dispose()
77Implement water as a plain so raycasting and collisions can happen with same.
78Add osGetPhysicsEngineName() so scripters can tell whether BulletSim or ODE
79 Also osGetPhysicsEngineVerion() maybe.
80Linkset.Position and Linkset.Orientation requre rewrite to properly return
81 child position. LinksetConstraint acts like it's at taint time!!
82Implement LockAngularMotion -- implements llSetStatus(ROTATE_AXIS_*, T/F)
83Should the different PID factors have non-equal contributions for different
84 values of Efficiency?
85
86LINKSETS
87======================================================
88Linksets should allow collisions to individual children
89 Add LocalID to children shapes in LinksetCompound and create events for individuals
90LinksetCompound: when one of the children changes orientation (like tires
91 turning on a vehicle, the whole compound object is rebuilt. Optimize this
92 so orientation/position of individual children can change without a rebuild.
93Verify/think through scripts in children of linksets. What do they reference
94 and return when getting position, velocity, ...
95Confirm constraint linksets still work after making all the changes for compound linksets.
96Add 'changed' flag or similar to reduce the number of times a linkset is rebuilt.
97 For compound linksets, add ability to remove or reposition individual child shapes.
98Disable activity of passive linkset children.
99 Since the linkset is a compound object, the old prims are left lying
100 around and need to be phantomized so they don't collide, ...
101Speed up creation of large physical linksets
102 For instance, sitting in Neb's car (130 prims) takes several seconds to become physical.
103 REALLY bad for very large physical linksets (freezes the sim for many seconds).
104Eliminate collisions between objects in a linkset. (LinksetConstraint)
105 Have UserPointer point to struct with localID and linksetID?
106 Objects in original linkset still collide with each other?
107
108MORE
109======================================================
110Test avatar walking up stairs. How does compare with SL.
111 Radius of the capsule affects ability to climb edges.
112Debounce avatar contact so legs don't keep folding up when standing.
113Implement LSL physics controls. Like STATUS_ROTATE_X.
114Add border extensions to terrain to help region crossings and objects leaving region.
115
116Performance test with lots of avatars. Can BulletSim support a thousand?
117Optimize collisions in C++: only send up to the object subscribed to collisions.
118 Use collision subscription and remove the collsion(A,B) and collision(B,A)
119Check whether SimMotionState needs large if statement (see TODO).
120
121Implement 'top colliders' info.
122Avatar jump
123Performance measurement and changes to make quicker.
124Implement detailed physics stats (GetStats()).
125
126Measure performance improvement from hulls
127Test not using ghost objects for volume detect implementation.
128Performance of closures and delegates for taint processing
129 Are there faster ways?
130 Is any slowdown introduced by the existing implementation significant?
131Is there are more efficient method of implementing pre and post step actions?
132 See http://www.codeproject.com/Articles/29922/Weak-Events-in-C
133
134Physics Arena central pyramid: why is one side permiable?
135
136INTERNAL IMPROVEMENT/CLEANUP
137=================================================
138Consider moving prim/character body and shape destruction in destroy()
139 to postTimeTime rather than protecting all the potential sets that
140 might have been queued up.
141Remove unused fields from ShapeData (not used in API2)
142Remove unused fields from pinned memory shared parameter block
143 Create parameter variables in BSScene to replace same.
144Breakout code for mesh/hull/compound/native into separate BSShape* classes
145 Standardize access to building and reference code.
146 The skeleton classes are in the sources but are not complete or linked in.
147Make BSBody and BSShape real classes to centralize creation/changin/destruction
148 Convert state and parameter calls from BulletSimAPI direct calls to
149 calls on BSBody and BSShape
150Generalize Dynamics and PID with standardized motors.
151Generalize Linkset and vehicles into PropertyManagers
152 Methods for Refresh, RemoveBodyDependencies, RestoreBodyDependencies
153 Potentially add events for shape destruction, etc.
154Complete implemention of preStepActions
155 Replace vehicle step call with prestep event.
156 Is there a need for postStepActions? postStepTaints?
157Implement linkset by setting position of children when root updated. (LinksetManual)
158 Linkset implementation using manual prim movement.
159LinkablePrim class? Would that simplify/centralize the linkset logic?
160BSScene.UpdateParameterSet() is broken. How to set params on objects?
161Remove HeightmapInfo from terrain specification
162 Since C++ code does not need terrain height, this structure et al are not needed.
163Add floating motor for BS_FLOATS_ON_WATER so prim and avatar will
164 bob at the water level. BSPrim.PositionSanityCheck().
165Should taints check for existance or activeness of target?
166 When destroying linksets/etc, taints can be generated for objects that are
167 actually gone when the taint happens. Crashes don't happen because the taint closure
168 keeps the object from being freed, but that is just an accident.
169 Possibly have and 'active' flag that is checked by the taint processor?
170
171THREADING
172=================================================
173Do taint action immediately if not actually executing Bullet.
174 Add lock around Bullet execution and just do taint actions if simulation is not happening.
175
176DONE DONE DONE DONE
177=================================================
178Cleanup code in BSDynamics by using motors. (Resolution: started)
179Consider implementing terrain with a mesh rather than heightmap. (Resolution: done)
180 Would have better and adjustable resolution.
181Build terrain mesh so heighmap is height of the center of the square meter.
182 Resolution: NOT DONE: SL and ODE define meter square as being at one corner with one diagional.
183Terrain as mesh. (Resolution: done)
184How are static linksets seen by the physics engine?
185 Resolution: they are not linked in physics. When moved, all the children are repositioned.
186Convert BSCharacter to use all API2 (Resolution: done)
187Avatar pushing difficult (too heavy?)
188Use asset service passed to BulletSim to get sculptie bodies, etc. (Resolution: done)
189Remove old code in DLL (all non-API2 stuff). (Resolution: done)
190Measurements of mega-physical prim performance (with graph) (Resolution: done, email)
191Debug Bullet internal stats output (why is timing all wrong?)
192 Resolution: Bullet stats logging only works with a single instance of Bullet (one region).
193Implement meshes or just verify that they work. (Resolution: they do!)
194Do prim hash codes work for sculpties and meshes? (Resolution: yes)
195Linkset implementation using compound shapes. (Resolution: implemented LinksetCompound)
196 Compound shapes will need the LocalID in the shapes and collision
197 processing to get it from there.
198Light cycle not banking (Resolution: It doesn't. Banking is roll adding yaw.)
199Package Bullet source mods for Bullet internal stats output
200 (Resolution: move code into WorldData.h rather than relying on patches)
201Single prim vehicles don't seem to properly vehiclize.
202 (Resolution: mass was not getting set properly for single prim linksets)
203Add material type linkage and input all the material property definitions.
204 Skeleton classes and table are in the sources but are not filled or used.
205 (Resolution:
206Neb vehicle taking > 25ms of physics time!!
207 (Resolution: compound linksets were being rebuild WAY too often)
208Avatar height off after unsitting (floats off ground)
209 Editting appearance then moving restores.
210 Must not be initializing height when recreating capsule after unsit.
211 (Resolution: confusion of scale vs size for native objects removed)
212Light cycle falling over when driving (Resolution: implemented angularMotorUp)
213Should vehicle angular/linear movement friction happen after all the components
214 or does it only apply to the basic movement?
215 (Resolution: friction added before returning newly computed motor value.
216 What is expected by some vehicles (turning up friction to moderate speed))
217Tune terrain/object friction to be closer to SL.
218 (Resolution: added material type with friction and resolution)
diff --git a/OpenSim/Region/Physics/ChOdePlugin/ODEPrim.cs b/OpenSim/Region/Physics/ChOdePlugin/ODEPrim.cs
index 8de70ef..ba24aa7 100644
--- a/OpenSim/Region/Physics/ChOdePlugin/ODEPrim.cs
+++ b/OpenSim/Region/Physics/ChOdePlugin/ODEPrim.cs
@@ -2190,7 +2190,7 @@ namespace OpenSim.Region.Physics.OdePlugin
2190 convex = false; 2190 convex = false;
2191 try 2191 try
2192 { 2192 {
2193 _mesh = _parent_scene.mesher.CreateMesh(m_primName, _pbs, _size, (int)LevelOfDetail.High, true,convex,false); 2193 _mesh = _parent_scene.mesher.CreateMesh(m_primName, _pbs, _size, (int)LevelOfDetail.High, true,false,convex,false);
2194 } 2194 }
2195 catch 2195 catch
2196 { 2196 {
@@ -2557,7 +2557,7 @@ namespace OpenSim.Region.Physics.OdePlugin
2557 2557
2558 try 2558 try
2559 { 2559 {
2560 mesh = _parent_scene.mesher.CreateMesh(oldname, _pbs, _size, (int)LevelOfDetail.High, true, convex,false); 2560 mesh = _parent_scene.mesher.CreateMesh(oldname, _pbs, _size, (int)LevelOfDetail.High, true, false,convex,false);
2561 } 2561 }
2562 catch 2562 catch
2563 { 2563 {
diff --git a/OpenSim/Region/Physics/Manager/IMesher.cs b/OpenSim/Region/Physics/Manager/IMesher.cs
index ecc2918..df980ab 100644
--- a/OpenSim/Region/Physics/Manager/IMesher.cs
+++ b/OpenSim/Region/Physics/Manager/IMesher.cs
@@ -37,7 +37,7 @@ namespace OpenSim.Region.Physics.Manager
37 { 37 {
38 IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod); 38 IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod);
39 IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical); 39 IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical);
40 IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex, bool forOde); 40 IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache, bool convex, bool forOde);
41 IMesh GetMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex); 41 IMesh GetMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex);
42 void ReleaseMesh(IMesh mesh); 42 void ReleaseMesh(IMesh mesh);
43 void ExpireReleaseMeshs(); 43 void ExpireReleaseMeshs();
diff --git a/OpenSim/Region/Physics/Manager/PhysicsActor.cs b/OpenSim/Region/Physics/Manager/PhysicsActor.cs
index 9338130..e2789d6 100644
--- a/OpenSim/Region/Physics/Manager/PhysicsActor.cs
+++ b/OpenSim/Region/Physics/Manager/PhysicsActor.cs
@@ -349,17 +349,20 @@ namespace OpenSim.Region.Physics.Manager
349 } 349 }
350 350
351 /// <summary> 351 /// <summary>
352 /// Velocity of this actor. 352 /// The desired velocity of this actor.
353 /// </summary> 353 /// </summary>
354 /// <remarks> 354 /// <remarks>
355 /// Setting this provides a target velocity for physics scene updates. 355 /// Setting this provides a target velocity for physics scene updates.
356 /// Getting this returns the velocity calculated by physics scene updates, using factors such as target velocity, 356 /// Getting this returns the last set target. Fetch Velocity to get the current velocity.
357 /// time to accelerate and collisions.
358 /// </remarks> 357 /// </remarks>
358 protected Vector3 m_targetVelocity;
359 public virtual Vector3 TargetVelocity 359 public virtual Vector3 TargetVelocity
360 { 360 {
361 get { return Velocity; } 361 get { return m_targetVelocity; }
362 set { Velocity = value; } 362 set {
363 m_targetVelocity = value;
364 Velocity = m_targetVelocity;
365 }
363 } 366 }
364 367
365 public abstract Vector3 Velocity { get; set; } 368 public abstract Vector3 Velocity { get; set; }
diff --git a/OpenSim/Region/Physics/Manager/ZeroMesher.cs b/OpenSim/Region/Physics/Manager/ZeroMesher.cs
index 16846e6..80ecf66 100644
--- a/OpenSim/Region/Physics/Manager/ZeroMesher.cs
+++ b/OpenSim/Region/Physics/Manager/ZeroMesher.cs
@@ -64,16 +64,21 @@ namespace OpenSim.Region.Physics.Manager
64 { 64 {
65 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod) 65 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod)
66 { 66 {
67 return CreateMesh(primName, primShape, size, lod, false); 67 return CreateMesh(primName, primShape, size, lod, false, false);
68 } 68 }
69 69
70 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex,bool forOde) 70 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache, bool convex,bool forOde)
71 { 71 {
72 return CreateMesh(primName, primShape, size, lod, false); 72 return CreateMesh(primName, primShape, size, lod, false);
73 } 73 }
74 74
75 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical) 75 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical)
76 { 76 {
77 return CreateMesh(primName, primShape, size, lod, false, false);
78 }
79
80 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache)
81 {
77 // Remove the reference to the encoded JPEG2000 data so it can be GCed 82 // Remove the reference to the encoded JPEG2000 data so it can be GCed
78 primShape.SculptData = OpenMetaverse.Utils.EmptyBytes; 83 primShape.SculptData = OpenMetaverse.Utils.EmptyBytes;
79 84
diff --git a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs
index f629c4d..d181b78 100644
--- a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs
+++ b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs
@@ -321,6 +321,9 @@ namespace OpenSim.Region.Physics.Meshing
321 321
322 if (primShape.SculptData.Length <= 0) 322 if (primShape.SculptData.Length <= 0)
323 { 323 {
324 // XXX: At the moment we can not log here since ODEPrim, for instance, ends up triggering this
325 // method twice - once before it has loaded sculpt data from the asset service and once afterwards.
326 // The first time will always call with unloaded SculptData if this needs to be uploaded.
324// m_log.ErrorFormat("[MESH]: asset data for {0} is zero length", primName); 327// m_log.ErrorFormat("[MESH]: asset data for {0} is zero length", primName);
325 return false; 328 return false;
326 } 329 }
@@ -699,16 +702,21 @@ namespace OpenSim.Region.Physics.Meshing
699 702
700 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod) 703 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod)
701 { 704 {
702 return CreateMesh(primName, primShape, size, lod, false); 705 return CreateMesh(primName, primShape, size, lod, false, true);
703 } 706 }
704 707
705 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex, bool forOde) 708 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache, bool convex, bool forOde)
706 { 709 {
707 return CreateMesh(primName, primShape, size, lod, false); 710 return CreateMesh(primName, primShape, size, lod, false);
708 } 711 }
709 712
710 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical) 713 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical)
711 { 714 {
715 return CreateMesh(primName, primShape, size, lod, isPhysical, true);
716 }
717
718 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache)
719 {
712#if SPAM 720#if SPAM
713 m_log.DebugFormat("[MESH]: Creating mesh for {0}", primName); 721 m_log.DebugFormat("[MESH]: Creating mesh for {0}", primName);
714#endif 722#endif
@@ -718,9 +726,12 @@ namespace OpenSim.Region.Physics.Meshing
718 726
719 // If this mesh has been created already, return it instead of creating another copy 727 // If this mesh has been created already, return it instead of creating another copy
720 // For large regions with 100k+ prims and hundreds of copies of each, this can save a GB or more of memory 728 // For large regions with 100k+ prims and hundreds of copies of each, this can save a GB or more of memory
721 key = primShape.GetMeshKey(size, lod); 729 if (shouldCache)
722 if (m_uniqueMeshes.TryGetValue(key, out mesh)) 730 {
723 return mesh; 731 key = primShape.GetMeshKey(size, lod);
732 if (m_uniqueMeshes.TryGetValue(key, out mesh))
733 return mesh;
734 }
724 735
725 if (size.X < 0.01f) size.X = 0.01f; 736 if (size.X < 0.01f) size.X = 0.01f;
726 if (size.Y < 0.01f) size.Y = 0.01f; 737 if (size.Y < 0.01f) size.Y = 0.01f;
@@ -743,7 +754,10 @@ namespace OpenSim.Region.Physics.Meshing
743 // trim the vertex and triangle lists to free up memory 754 // trim the vertex and triangle lists to free up memory
744 mesh.TrimExcess(); 755 mesh.TrimExcess();
745 756
746 m_uniqueMeshes.Add(key, mesh); 757 if (shouldCache)
758 {
759 m_uniqueMeshes.Add(key, mesh);
760 }
747 } 761 }
748 762
749 return mesh; 763 return mesh;
diff --git a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs
index a59f63f..d09aa62 100644
--- a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs
+++ b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs
@@ -3367,6 +3367,11 @@ Console.WriteLine(" JointCreateFixed");
3367 _pbs.SculptData = new byte[asset.Data.Length]; 3367 _pbs.SculptData = new byte[asset.Data.Length];
3368 asset.Data.CopyTo(_pbs.SculptData, 0); 3368 asset.Data.CopyTo(_pbs.SculptData, 0);
3369// m_assetFailed = false; 3369// m_assetFailed = false;
3370
3371// m_log.DebugFormat(
3372// "[ODE PRIM]: Received mesh/sculpt data asset {0} with {1} bytes for {2} at {3} in {4}",
3373// _pbs.SculptTexture, _pbs.SculptData.Length, Name, _position, _parent_scene.Name);
3374
3370 m_taintshape = true; 3375 m_taintshape = true;
3371 _parent_scene.AddPhysicsActorTaint(this); 3376 _parent_scene.AddPhysicsActorTaint(this);
3372 } 3377 }
diff --git a/OpenSim/Region/Physics/OdePlugin/Tests/ODETestClass.cs b/OpenSim/Region/Physics/OdePlugin/Tests/ODETestClass.cs
index cbc6b95..16404c6 100644
--- a/OpenSim/Region/Physics/OdePlugin/Tests/ODETestClass.cs
+++ b/OpenSim/Region/Physics/OdePlugin/Tests/ODETestClass.cs
@@ -32,13 +32,14 @@ using OpenMetaverse;
32using OpenSim.Framework; 32using OpenSim.Framework;
33using OpenSim.Region.Physics.Manager; 33using OpenSim.Region.Physics.Manager;
34using OpenSim.Region.Physics.OdePlugin; 34using OpenSim.Region.Physics.OdePlugin;
35using OpenSim.Tests.Common;
35using log4net; 36using log4net;
36using System.Reflection; 37using System.Reflection;
37 38
38namespace OpenSim.Region.Physics.OdePlugin.Tests 39namespace OpenSim.Region.Physics.OdePlugin.Tests
39{ 40{
40 [TestFixture] 41 [TestFixture]
41 public class ODETestClass 42 public class ODETestClass : OpenSimTestCase
42 { 43 {
43 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 44 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
44 45
diff --git a/OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs b/OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs
index 6e1a105..00cbfbd 100644
--- a/OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs
+++ b/OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs
@@ -1031,12 +1031,12 @@ namespace OpenSim.Region.Physics.Meshing
1031 1031
1032 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod) 1032 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod)
1033 { 1033 {
1034 return CreateMesh(primName, primShape, size, lod, false,false,false); 1034 return CreateMesh(primName, primShape, size, lod, false,false,false,false);
1035 } 1035 }
1036 1036
1037 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical) 1037 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical)
1038 { 1038 {
1039 return CreateMesh(primName, primShape, size, lod, false,false,false); 1039 return CreateMesh(primName, primShape, size, lod, false,false,false,false);
1040 } 1040 }
1041 1041
1042 public IMesh GetMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex) 1042 public IMesh GetMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex)
@@ -1080,7 +1080,7 @@ namespace OpenSim.Region.Physics.Meshing
1080 1080
1081 private static Vector3 m_MeshUnitSize = new Vector3(1.0f, 1.0f, 1.0f); 1081 private static Vector3 m_MeshUnitSize = new Vector3(1.0f, 1.0f, 1.0f);
1082 1082
1083 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex, bool forOde) 1083 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache, bool convex, bool forOde)
1084 { 1084 {
1085#if SPAM 1085#if SPAM
1086 m_log.DebugFormat("[MESH]: Creating mesh for {0}", primName); 1086 m_log.DebugFormat("[MESH]: Creating mesh for {0}", primName);
diff --git a/OpenSim/Region/Physics/UbitOdePlugin/ODEMeshWorker.cs b/OpenSim/Region/Physics/UbitOdePlugin/ODEMeshWorker.cs
index 5030cec..0df71eb 100644
--- a/OpenSim/Region/Physics/UbitOdePlugin/ODEMeshWorker.cs
+++ b/OpenSim/Region/Physics/UbitOdePlugin/ODEMeshWorker.cs
@@ -448,7 +448,7 @@ namespace OpenSim.Region.Physics.OdePlugin
448 else 448 else
449 { 449 {
450 repData.meshState = MeshState.needMesh; 450 repData.meshState = MeshState.needMesh;
451 mesh = m_mesher.CreateMesh(actor.Name, pbs, size, clod, true, convex, true); 451 mesh = m_mesher.CreateMesh(actor.Name, pbs, size, clod, true, false, convex, true);
452 if (mesh == null) 452 if (mesh == null)
453 { 453 {
454 repData.meshState = MeshState.MeshFailed; 454 repData.meshState = MeshState.MeshFailed;
@@ -513,7 +513,7 @@ namespace OpenSim.Region.Physics.OdePlugin
513 clod = (int)LevelOfDetail.Low; 513 clod = (int)LevelOfDetail.Low;
514 } 514 }
515 515
516 mesh = m_mesher.CreateMesh(actor.Name, pbs, size, clod, true, convex, true); 516 mesh = m_mesher.CreateMesh(actor.Name, pbs, size, clod, true, false, convex, true);
517 517
518 if (mesh == null) 518 if (mesh == null)
519 { 519 {
@@ -929,4 +929,4 @@ namespace OpenSim.Region.Physics.OdePlugin
929 repData.actor.Name); 929 repData.actor.Name);
930 } 930 }
931 } 931 }
932} \ No newline at end of file 932}