aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs97
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs4
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSConstraint6Dof.cs4
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs987
-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.cs324
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs37
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs272
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSScene.cs110
-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.txt204
-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, 2332 insertions, 1122 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
index 4c195e1..4dd6264 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
@@ -105,12 +105,12 @@ public sealed class BSCharacter : BSPhysObject
105 DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}", 105 DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}",
106 LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass); 106 LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass);
107 107
108 // do actual create at taint time 108 // do actual creation in taint time
109 PhysicsScene.TaintedObject("BSCharacter.create", delegate() 109 PhysicsScene.TaintedObject("BSCharacter.create", delegate()
110 { 110 {
111 DetailLog("{0},BSCharacter.create,taint", LocalID); 111 DetailLog("{0},BSCharacter.create,taint", LocalID);
112 // New body and shape into PhysBody and PhysShape 112 // New body and shape into PhysBody and PhysShape
113 PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this, null, null); 113 PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this);
114 114
115 SetPhysicalProperties(); 115 SetPhysicalProperties();
116 }); 116 });
@@ -124,7 +124,9 @@ public sealed class BSCharacter : BSPhysObject
124 PhysicsScene.TaintedObject("BSCharacter.destroy", delegate() 124 PhysicsScene.TaintedObject("BSCharacter.destroy", delegate()
125 { 125 {
126 PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null); 126 PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null);
127 PhysBody.Clear();
127 PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null); 128 PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null);
129 PhysShape.Clear();
128 }); 130 });
129 } 131 }
130 132
@@ -165,9 +167,8 @@ public sealed class BSCharacter : BSPhysObject
165 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr); 167 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr);
166 168
167 // Do this after the object has been added to the world 169 // Do this after the object has been added to the world
168 BulletSimAPI.SetCollisionFilterMask2(PhysBody.ptr, 170 PhysBody.collisionType = CollisionType.Avatar;
169 (uint)CollisionFilterGroups.AvatarFilter, 171 PhysBody.ApplyCollisionMask();
170 (uint)CollisionFilterGroups.AvatarMask);
171 } 172 }
172 173
173 public override void RequestPhysicsterseUpdate() 174 public override void RequestPhysicsterseUpdate()
@@ -187,6 +188,11 @@ public sealed class BSCharacter : BSPhysObject
187 set { 188 set {
188 // When an avatar's size is set, only the height is changed. 189 // When an avatar's size is set, only the height is changed.
189 _size = value; 190 _size = value;
191 // Old versions of ScenePresence passed only the height. If width and/or depth are zero,
192 // replace with the default values.
193 if (_size.X == 0f) _size.X = PhysicsScene.Params.avatarCapsuleDepth;
194 if (_size.Y == 0f) _size.Y = PhysicsScene.Params.avatarCapsuleWidth;
195
190 ComputeAvatarScale(_size); 196 ComputeAvatarScale(_size);
191 ComputeAvatarVolumeAndMass(); 197 ComputeAvatarVolumeAndMass();
192 DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}", 198 DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}",
@@ -194,15 +200,18 @@ public sealed class BSCharacter : BSPhysObject
194 200
195 PhysicsScene.TaintedObject("BSCharacter.setSize", delegate() 201 PhysicsScene.TaintedObject("BSCharacter.setSize", delegate()
196 { 202 {
197 BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale); 203 if (PhysBody.HasPhysicalBody && PhysShape.HasPhysicalShape)
198 UpdatePhysicalMassProperties(RawMass); 204 {
205 BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale);
206 UpdatePhysicalMassProperties(RawMass);
207 // Make sure this change appears as a property update event
208 BulletSimAPI.PushUpdate2(PhysBody.ptr);
209 }
199 }); 210 });
200 211
201 } 212 }
202 } 213 }
203 214
204 public override OMV.Vector3 Scale { get; set; }
205
206 public override PrimitiveBaseShape Shape 215 public override PrimitiveBaseShape Shape
207 { 216 {
208 set { BaseShape = value; } 217 set { BaseShape = value; }
@@ -236,7 +245,8 @@ public sealed class BSCharacter : BSPhysObject
236 // Zero some other properties directly into the physics engine 245 // Zero some other properties directly into the physics engine
237 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() 246 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
238 { 247 {
239 BulletSimAPI.ClearAllForces2(PhysBody.ptr); 248 if (PhysBody.HasPhysicalBody)
249 BulletSimAPI.ClearAllForces2(PhysBody.ptr);
240 }); 250 });
241 } 251 }
242 public override void ZeroAngularMotion(bool inTaintTime) 252 public override void ZeroAngularMotion(bool inTaintTime)
@@ -245,10 +255,13 @@ public sealed class BSCharacter : BSPhysObject
245 255
246 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() 256 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
247 { 257 {
248 BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); 258 if (PhysBody.HasPhysicalBody)
249 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); 259 {
250 // The next also get rid of applied linear force but the linear velocity is untouched. 260 BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
251 BulletSimAPI.ClearForces2(PhysBody.ptr); 261 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
262 // The next also get rid of applied linear force but the linear velocity is untouched.
263 BulletSimAPI.ClearForces2(PhysBody.ptr);
264 }
252 }); 265 });
253 } 266 }
254 267
@@ -273,7 +286,8 @@ public sealed class BSCharacter : BSPhysObject
273 PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate() 286 PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate()
274 { 287 {
275 DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); 288 DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
276 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 289 if (PhysBody.HasPhysicalBody)
290 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
277 }); 291 });
278 } 292 }
279 } 293 }
@@ -297,6 +311,15 @@ public sealed class BSCharacter : BSPhysObject
297 { 311 {
298 bool ret = false; 312 bool ret = false;
299 313
314 // TODO: check for out of bounds
315 if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(_position))
316 {
317 // The character is out of the known/simulated area.
318 // Upper levels of code will handle the transition to other areas so, for
319 // the time, we just ignore the position.
320 return ret;
321 }
322
300 // If below the ground, move the avatar up 323 // If below the ground, move the avatar up
301 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); 324 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
302 if (Position.Z < terrainHeight) 325 if (Position.Z < terrainHeight)
@@ -307,7 +330,7 @@ public sealed class BSCharacter : BSPhysObject
307 } 330 }
308 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) 331 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
309 { 332 {
310 float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position); 333 float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position);
311 if (Position.Z < waterHeight) 334 if (Position.Z < waterHeight)
312 { 335 {
313 _position.Z = waterHeight; 336 _position.Z = waterHeight;
@@ -315,7 +338,6 @@ public sealed class BSCharacter : BSPhysObject
315 } 338 }
316 } 339 }
317 340
318 // TODO: check for out of bounds
319 return ret; 341 return ret;
320 } 342 }
321 343
@@ -332,7 +354,8 @@ public sealed class BSCharacter : BSPhysObject
332 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate() 354 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate()
333 { 355 {
334 DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); 356 DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
335 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 357 if (PhysBody.HasPhysicalBody)
358 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
336 }); 359 });
337 ret = true; 360 ret = true;
338 } 361 }
@@ -359,7 +382,8 @@ public sealed class BSCharacter : BSPhysObject
359 PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate() 382 PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate()
360 { 383 {
361 DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force); 384 DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force);
362 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force); 385 if (PhysBody.HasPhysicalBody)
386 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
363 }); 387 });
364 } 388 }
365 } 389 }
@@ -398,7 +422,8 @@ public sealed class BSCharacter : BSPhysObject
398 if (_currentFriction != PhysicsScene.Params.avatarStandingFriction) 422 if (_currentFriction != PhysicsScene.Params.avatarStandingFriction)
399 { 423 {
400 _currentFriction = PhysicsScene.Params.avatarStandingFriction; 424 _currentFriction = PhysicsScene.Params.avatarStandingFriction;
401 BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction); 425 if (PhysBody.HasPhysicalBody)
426 BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction);
402 } 427 }
403 } 428 }
404 else 429 else
@@ -406,7 +431,8 @@ public sealed class BSCharacter : BSPhysObject
406 if (_currentFriction != PhysicsScene.Params.avatarFriction) 431 if (_currentFriction != PhysicsScene.Params.avatarFriction)
407 { 432 {
408 _currentFriction = PhysicsScene.Params.avatarFriction; 433 _currentFriction = PhysicsScene.Params.avatarFriction;
409 BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction); 434 if (PhysBody.HasPhysicalBody)
435 BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction);
410 } 436 }
411 } 437 }
412 _velocity = value; 438 _velocity = value;
@@ -443,8 +469,11 @@ public sealed class BSCharacter : BSPhysObject
443 // m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation); 469 // m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation);
444 PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate() 470 PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate()
445 { 471 {
446 // _position = BulletSimAPI.GetPosition2(BSBody.ptr); 472 if (PhysBody.HasPhysicalBody)
447 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 473 {
474 // _position = BulletSimAPI.GetPosition2(BSBody.ptr);
475 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
476 }
448 }); 477 });
449 } 478 }
450 } 479 }
@@ -517,10 +546,13 @@ public sealed class BSCharacter : BSPhysObject
517 _floatOnWater = value; 546 _floatOnWater = value;
518 PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate() 547 PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate()
519 { 548 {
520 if (_floatOnWater) 549 if (PhysBody.HasPhysicalBody)
521 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); 550 {
522 else 551 if (_floatOnWater)
523 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); 552 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
553 else
554 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
555 }
524 }); 556 });
525 } 557 }
526 } 558 }
@@ -553,7 +585,8 @@ public sealed class BSCharacter : BSPhysObject
553 DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); 585 DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
554 // Buoyancy is faked by changing the gravity applied to the object 586 // Buoyancy is faked by changing the gravity applied to the object
555 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy); 587 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
556 BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav)); 588 if (PhysBody.HasPhysicalBody)
589 BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav));
557 } 590 }
558 } 591 }
559 592
@@ -599,7 +632,8 @@ public sealed class BSCharacter : BSPhysObject
599 PhysicsScene.TaintedObject("BSCharacter.AddForce", delegate() 632 PhysicsScene.TaintedObject("BSCharacter.AddForce", delegate()
600 { 633 {
601 DetailLog("{0},BSCharacter.setAddForce,taint,addedForce={1}", LocalID, _force); 634 DetailLog("{0},BSCharacter.setAddForce,taint,addedForce={1}", LocalID, _force);
602 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force); 635 if (PhysBody.HasPhysicalBody)
636 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
603 }); 637 });
604 } 638 }
605 else 639 else
@@ -616,9 +650,6 @@ public sealed class BSCharacter : BSPhysObject
616 650
617 private void ComputeAvatarScale(OMV.Vector3 size) 651 private void ComputeAvatarScale(OMV.Vector3 size)
618 { 652 {
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; 653 OMV.Vector3 newScale = size;
623 // newScale.X = PhysicsScene.Params.avatarCapsuleWidth; 654 // newScale.X = PhysicsScene.Params.avatarCapsuleWidth;
624 // newScale.Y = PhysicsScene.Params.avatarCapsuleDepth; 655 // newScale.Y = PhysicsScene.Params.avatarCapsuleDepth;
@@ -677,7 +708,7 @@ public sealed class BSCharacter : BSPhysObject
677 } 708 }
678 709
679 // Tell the linkset about value changes 710 // Tell the linkset about value changes
680 Linkset.UpdateProperties(this); 711 Linkset.UpdateProperties(this, true);
681 712
682 // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop. 713 // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop.
683 // base.RequestPhysicsterseUpdate(); 714 // 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..5887249 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,13 +80,14 @@ 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
@@ -124,8 +112,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin
124 // Therefore only m_VehicleBuoyancy=1 (0g) will use the script-requested .Z velocity. 112 // Therefore only m_VehicleBuoyancy=1 (0g) will use the script-requested .Z velocity.
125 113
126 //Attractor properties 114 //Attractor properties
127 private float m_verticalAttractionEfficiency = 1.0f; // damped 115 private BSVMotor m_verticalAttractionMotor = new BSVMotor("VerticalAttraction");
128 private float m_verticalAttractionTimescale = 500f; // Timescale > 300 means no vert attractor. 116 private float m_verticalAttractionEfficiency = 1.0f; // damped
117 private float m_verticalAttractionCutoff = 500f; // per the documentation
118 // Timescale > cutoff means no vert attractor.
119 private float m_verticalAttractionTimescale = 510f;
129 120
130 public BSDynamics(BSScene myScene, BSPrim myPrim) 121 public BSDynamics(BSScene myScene, BSPrim myPrim)
131 { 122 {
@@ -137,7 +128,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
137 // Return 'true' if this vehicle is doing vehicle things 128 // Return 'true' if this vehicle is doing vehicle things
138 public bool IsActive 129 public bool IsActive
139 { 130 {
140 get { return Type != Vehicle.TYPE_NONE; } 131 get { return Type != Vehicle.TYPE_NONE && Prim.IsPhysical; }
141 } 132 }
142 133
143 internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue) 134 internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue)
@@ -152,13 +143,15 @@ namespace OpenSim.Region.Physics.BulletSPlugin
152 m_angularDeflectionTimescale = Math.Max(pValue, 0.01f); 143 m_angularDeflectionTimescale = Math.Max(pValue, 0.01f);
153 break; 144 break;
154 case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE: 145 case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE:
155 m_angularMotorDecayTimescale = Math.Max(pValue, 0.01f); 146 m_angularMotorDecayTimescale = ClampInRange(0.01f, pValue, 120);
147 m_angularMotor.TargetValueDecayTimeScale = m_angularMotorDecayTimescale;
156 break; 148 break;
157 case Vehicle.ANGULAR_MOTOR_TIMESCALE: 149 case Vehicle.ANGULAR_MOTOR_TIMESCALE:
158 m_angularMotorTimescale = Math.Max(pValue, 0.01f); 150 m_angularMotorTimescale = Math.Max(pValue, 0.01f);
151 m_angularMotor.TimeScale = m_angularMotorTimescale;
159 break; 152 break;
160 case Vehicle.BANKING_EFFICIENCY: 153 case Vehicle.BANKING_EFFICIENCY:
161 m_bankingEfficiency = Math.Max(-1f, Math.Min(pValue, 1f)); 154 m_bankingEfficiency = ClampInRange(-1f, pValue, 1f);
162 break; 155 break;
163 case Vehicle.BANKING_MIX: 156 case Vehicle.BANKING_MIX:
164 m_bankingMix = Math.Max(pValue, 0.01f); 157 m_bankingMix = Math.Max(pValue, 0.01f);
@@ -167,10 +160,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
167 m_bankingTimescale = Math.Max(pValue, 0.01f); 160 m_bankingTimescale = Math.Max(pValue, 0.01f);
168 break; 161 break;
169 case Vehicle.BUOYANCY: 162 case Vehicle.BUOYANCY:
170 m_VehicleBuoyancy = Math.Max(-1f, Math.Min(pValue, 1f)); 163 m_VehicleBuoyancy = ClampInRange(-1f, pValue, 1f);
171 break; 164 break;
172 case Vehicle.HOVER_EFFICIENCY: 165 case Vehicle.HOVER_EFFICIENCY:
173 m_VhoverEfficiency = Math.Max(0f, Math.Min(pValue, 1f)); 166 m_VhoverEfficiency = ClampInRange(0f, pValue, 1f);
174 break; 167 break;
175 case Vehicle.HOVER_HEIGHT: 168 case Vehicle.HOVER_HEIGHT:
176 m_VhoverHeight = pValue; 169 m_VhoverHeight = pValue;
@@ -185,33 +178,40 @@ namespace OpenSim.Region.Physics.BulletSPlugin
185 m_linearDeflectionTimescale = Math.Max(pValue, 0.01f); 178 m_linearDeflectionTimescale = Math.Max(pValue, 0.01f);
186 break; 179 break;
187 case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE: 180 case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE:
188 m_linearMotorDecayTimescale = Math.Max(pValue, 0.01f); 181 m_linearMotorDecayTimescale = ClampInRange(0.01f, pValue, 120);
182 m_linearMotor.TargetValueDecayTimeScale = m_linearMotorDecayTimescale;
189 break; 183 break;
190 case Vehicle.LINEAR_MOTOR_TIMESCALE: 184 case Vehicle.LINEAR_MOTOR_TIMESCALE:
191 m_linearMotorTimescale = Math.Max(pValue, 0.01f); 185 m_linearMotorTimescale = Math.Max(pValue, 0.01f);
186 m_linearMotor.TimeScale = m_linearMotorTimescale;
192 break; 187 break;
193 case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY: 188 case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY:
194 m_verticalAttractionEfficiency = Math.Max(0.1f, Math.Min(pValue, 1f)); 189 m_verticalAttractionEfficiency = ClampInRange(0.1f, pValue, 1f);
190 m_verticalAttractionMotor.Efficiency = m_verticalAttractionEfficiency;
195 break; 191 break;
196 case Vehicle.VERTICAL_ATTRACTION_TIMESCALE: 192 case Vehicle.VERTICAL_ATTRACTION_TIMESCALE:
197 m_verticalAttractionTimescale = Math.Max(pValue, 0.01f); 193 m_verticalAttractionTimescale = Math.Max(pValue, 0.01f);
194 m_verticalAttractionMotor.TimeScale = m_verticalAttractionTimescale;
198 break; 195 break;
199 196
200 // These are vector properties but the engine lets you use a single float value to 197 // 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 198 // set all of the components to the same value
202 case Vehicle.ANGULAR_FRICTION_TIMESCALE: 199 case Vehicle.ANGULAR_FRICTION_TIMESCALE:
203 m_angularFrictionTimescale = new Vector3(pValue, pValue, pValue); 200 m_angularFrictionTimescale = new Vector3(pValue, pValue, pValue);
201 m_angularMotor.FrictionTimescale = m_angularFrictionTimescale;
204 break; 202 break;
205 case Vehicle.ANGULAR_MOTOR_DIRECTION: 203 case Vehicle.ANGULAR_MOTOR_DIRECTION:
206 m_angularMotorDirection = new Vector3(pValue, pValue, pValue); 204 m_angularMotorDirection = new Vector3(pValue, pValue, pValue);
207 // m_angularMotorApply = 100; 205 m_angularMotor.SetTarget(m_angularMotorDirection);
208 break; 206 break;
209 case Vehicle.LINEAR_FRICTION_TIMESCALE: 207 case Vehicle.LINEAR_FRICTION_TIMESCALE:
210 m_linearFrictionTimescale = new Vector3(pValue, pValue, pValue); 208 m_linearFrictionTimescale = new Vector3(pValue, pValue, pValue);
209 m_linearMotor.FrictionTimescale = m_linearFrictionTimescale;
211 break; 210 break;
212 case Vehicle.LINEAR_MOTOR_DIRECTION: 211 case Vehicle.LINEAR_MOTOR_DIRECTION:
213 m_linearMotorDirection = new Vector3(pValue, pValue, pValue); 212 m_linearMotorDirection = new Vector3(pValue, pValue, pValue);
214 m_linearMotorDirectionLASTSET = new Vector3(pValue, pValue, pValue); 213 m_linearMotorDirectionLASTSET = new Vector3(pValue, pValue, pValue);
214 m_linearMotor.SetTarget(m_linearMotorDirection);
215 break; 215 break;
216 case Vehicle.LINEAR_MOTOR_OFFSET: 216 case Vehicle.LINEAR_MOTOR_OFFSET:
217 m_linearMotorOffset = new Vector3(pValue, pValue, pValue); 217 m_linearMotorOffset = new Vector3(pValue, pValue, pValue);
@@ -227,21 +227,24 @@ namespace OpenSim.Region.Physics.BulletSPlugin
227 { 227 {
228 case Vehicle.ANGULAR_FRICTION_TIMESCALE: 228 case Vehicle.ANGULAR_FRICTION_TIMESCALE:
229 m_angularFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); 229 m_angularFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z);
230 m_angularMotor.FrictionTimescale = m_angularFrictionTimescale;
230 break; 231 break;
231 case Vehicle.ANGULAR_MOTOR_DIRECTION: 232 case Vehicle.ANGULAR_MOTOR_DIRECTION:
232 // Limit requested angular speed to 2 rps= 4 pi rads/sec 233 // Limit requested angular speed to 2 rps= 4 pi rads/sec
233 pValue.X = Math.Max(-12.56f, Math.Min(pValue.X, 12.56f)); 234 pValue.X = ClampInRange(-12.56f, pValue.X, 12.56f);
234 pValue.Y = Math.Max(-12.56f, Math.Min(pValue.Y, 12.56f)); 235 pValue.Y = ClampInRange(-12.56f, pValue.Y, 12.56f);
235 pValue.Z = Math.Max(-12.56f, Math.Min(pValue.Z, 12.56f)); 236 pValue.Z = ClampInRange(-12.56f, pValue.Z, 12.56f);
236 m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); 237 m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z);
237 // m_angularMotorApply = 100; 238 m_angularMotor.SetTarget(m_angularMotorDirection);
238 break; 239 break;
239 case Vehicle.LINEAR_FRICTION_TIMESCALE: 240 case Vehicle.LINEAR_FRICTION_TIMESCALE:
240 m_linearFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); 241 m_linearFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z);
242 m_linearMotor.FrictionTimescale = m_linearFrictionTimescale;
241 break; 243 break;
242 case Vehicle.LINEAR_MOTOR_DIRECTION: 244 case Vehicle.LINEAR_MOTOR_DIRECTION:
243 m_linearMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); 245 m_linearMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z);
244 m_linearMotorDirectionLASTSET = new Vector3(pValue.X, pValue.Y, pValue.Z); 246 m_linearMotorDirectionLASTSET = new Vector3(pValue.X, pValue.Y, pValue.Z);
247 m_linearMotor.SetTarget(m_linearMotorDirection);
245 break; 248 break;
246 case Vehicle.LINEAR_MOTOR_OFFSET: 249 case Vehicle.LINEAR_MOTOR_OFFSET:
247 m_linearMotorOffset = new Vector3(pValue.X, pValue.Y, pValue.Z); 250 m_linearMotorOffset = new Vector3(pValue.X, pValue.Y, pValue.Z);
@@ -303,7 +306,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
303 m_VhoverEfficiency = 0; 306 m_VhoverEfficiency = 0;
304 m_VhoverTimescale = 0; 307 m_VhoverTimescale = 0;
305 m_VehicleBuoyancy = 0; 308 m_VehicleBuoyancy = 0;
306 309
307 m_linearDeflectionEfficiency = 1; 310 m_linearDeflectionEfficiency = 1;
308 m_linearDeflectionTimescale = 1; 311 m_linearDeflectionTimescale = 1;
309 312
@@ -319,6 +322,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
319 322
320 m_referenceFrame = Quaternion.Identity; 323 m_referenceFrame = Quaternion.Identity;
321 m_flags = (VehicleFlag)0; 324 m_flags = (VehicleFlag)0;
325
322 break; 326 break;
323 327
324 case Vehicle.TYPE_SLED: 328 case Vehicle.TYPE_SLED:
@@ -351,10 +355,14 @@ namespace OpenSim.Region.Physics.BulletSPlugin
351 m_bankingMix = 1; 355 m_bankingMix = 1;
352 356
353 m_referenceFrame = Quaternion.Identity; 357 m_referenceFrame = Quaternion.Identity;
354 m_flags |= (VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_ROLL_ONLY | VehicleFlag.LIMIT_MOTOR_UP); 358 m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
355 m_flags &= 359 | VehicleFlag.HOVER_TERRAIN_ONLY
356 ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | 360 | VehicleFlag.HOVER_GLOBAL_HEIGHT
357 VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY); 361 | VehicleFlag.HOVER_UP_ONLY);
362 m_flags |= (VehicleFlag.NO_DEFLECTION_UP
363 | VehicleFlag.LIMIT_ROLL_ONLY
364 | VehicleFlag.LIMIT_MOTOR_UP);
365
358 break; 366 break;
359 case Vehicle.TYPE_CAR: 367 case Vehicle.TYPE_CAR:
360 m_linearMotorDirection = Vector3.Zero; 368 m_linearMotorDirection = Vector3.Zero;
@@ -498,6 +506,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
498 m_bankingEfficiency = 0; 506 m_bankingEfficiency = 0;
499 m_bankingMix = 0.7f; 507 m_bankingMix = 0.7f;
500 m_bankingTimescale = 5; 508 m_bankingTimescale = 5;
509
501 m_referenceFrame = Quaternion.Identity; 510 m_referenceFrame = Quaternion.Identity;
502 511
503 m_referenceFrame = Quaternion.Identity; 512 m_referenceFrame = Quaternion.Identity;
@@ -510,6 +519,26 @@ namespace OpenSim.Region.Physics.BulletSPlugin
510 | VehicleFlag.HOVER_GLOBAL_HEIGHT); 519 | VehicleFlag.HOVER_GLOBAL_HEIGHT);
511 break; 520 break;
512 } 521 }
522
523 // Update any physical parameters based on this type.
524 Refresh();
525
526 m_linearMotor = new BSVMotor("LinearMotor", m_linearMotorTimescale,
527 m_linearMotorDecayTimescale, m_linearFrictionTimescale,
528 1f);
529 m_linearMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
530
531 m_angularMotor = new BSVMotor("AngularMotor", m_angularMotorTimescale,
532 m_angularMotorDecayTimescale, m_angularFrictionTimescale,
533 1f);
534 m_angularMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
535
536 m_verticalAttractionMotor = new BSVMotor("VerticalAttraction", m_verticalAttractionTimescale,
537 BSMotor.Infinite, BSMotor.InfiniteVector,
538 m_verticalAttractionEfficiency);
539 // Z goes away and we keep X and Y
540 m_verticalAttractionMotor.FrictionTimescale = new Vector3(BSMotor.Infinite, BSMotor.Infinite, 0.1f);
541 m_verticalAttractionMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
513 } 542 }
514 543
515 // Some of the properties of this prim may have changed. 544 // Some of the properties of this prim may have changed.
@@ -518,13 +547,35 @@ namespace OpenSim.Region.Physics.BulletSPlugin
518 { 547 {
519 if (IsActive) 548 if (IsActive)
520 { 549 {
521 // Friction effects are handled by this vehicle code 550 // Remember the mass so we don't have to fetch it every step
522 BulletSimAPI.SetFriction2(Prim.PhysBody.ptr, 0f); 551 m_vehicleMass = Prim.Linkset.LinksetMass;
523 BulletSimAPI.SetHitFraction2(Prim.PhysBody.ptr, 0f); 552
524 553 // Friction affects are handled by this vehicle code
525 // BulletSimAPI.SetAngularDamping2(Prim.PhysBody.ptr, 0.8f); 554 float friction = 0f;
526 555 BulletSimAPI.SetFriction2(Prim.PhysBody.ptr, friction);
527 VDetailLog("{0},BSDynamics.Refresh,zeroingFriction and adding damping", Prim.LocalID); 556
557 // Moderate angular movement introduced by Bullet.
558 // TODO: possibly set AngularFactor and LinearFactor for the type of vehicle.
559 // Maybe compute linear and angular factor and damping from params.
560 float angularDamping = PhysicsScene.Params.vehicleAngularDamping;
561 BulletSimAPI.SetAngularDamping2(Prim.PhysBody.ptr, angularDamping);
562
563 // Vehicles report collision events so we know when it's on the ground
564 BulletSimAPI.AddToCollisionFlags2(Prim.PhysBody.ptr, CollisionFlags.BS_VEHICLE_COLLISIONS);
565
566 // DEBUG DEBUG DEBUG: use uniform inertia to smooth movement added by Bullet
567 // Vector3 localInertia = new Vector3(1f, 1f, 1f);
568 // Vector3 localInertia = new Vector3(m_vehicleMass, m_vehicleMass, m_vehicleMass);
569 Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(Prim.PhysShape.ptr, m_vehicleMass);
570 BulletSimAPI.SetMassProps2(Prim.PhysBody.ptr, m_vehicleMass, localInertia);
571 BulletSimAPI.UpdateInertiaTensor2(Prim.PhysBody.ptr);
572
573 VDetailLog("{0},BSDynamics.Refresh,mass={1},frict={2},inert={3},aDamp={4}",
574 Prim.LocalID, m_vehicleMass, friction, localInertia, angularDamping);
575 }
576 else
577 {
578 BulletSimAPI.RemoveFromCollisionFlags2(Prim.PhysBody.ptr, CollisionFlags.BS_VEHICLE_COLLISIONS);
528 } 579 }
529 } 580 }
530 581
@@ -546,116 +597,294 @@ namespace OpenSim.Region.Physics.BulletSPlugin
546 Refresh(); 597 Refresh();
547 } 598 }
548 599
600 #region Known vehicle value functions
601 // Vehicle physical parameters that we buffer from constant getting and setting.
602 // The "m_known*" variables are initialized to 'null', fetched only if referenced
603 // and stored back into the physics engine only if updated.
604 // This does two things: 1) saves continuious calls into unmanaged code, and
605 // 2) signals when a physics property update must happen back to the simulator
606 // to update values modified for the vehicle.
607 private int m_knownChanged;
608 private float? m_knownTerrainHeight;
609 private float? m_knownWaterLevel;
610 private Vector3? m_knownPosition;
611 private Vector3? m_knownVelocity;
612 private Vector3 m_knownForce;
613 private Quaternion? m_knownOrientation;
614 private Vector3? m_knownRotationalVelocity;
615 private Vector3 m_knownRotationalForce;
616 private float? m_knownForwardSpeed;
617
618 private const int m_knownChangedPosition = 1 << 0;
619 private const int m_knownChangedVelocity = 1 << 1;
620 private const int m_knownChangedForce = 1 << 2;
621 private const int m_knownChangedOrientation = 1 << 3;
622 private const int m_knownChangedRotationalVelocity = 1 << 4;
623 private const int m_knownChangedRotationalForce = 1 << 5;
624
625 private void ForgetKnownVehicleProperties()
626 {
627 m_knownTerrainHeight = null;
628 m_knownWaterLevel = null;
629 m_knownPosition = null;
630 m_knownVelocity = null;
631 m_knownForce = Vector3.Zero;
632 m_knownOrientation = null;
633 m_knownRotationalVelocity = null;
634 m_knownRotationalForce = Vector3.Zero;
635 m_knownForwardSpeed = null;
636 m_knownChanged = 0;
637 }
638 private void PushKnownChanged()
639 {
640 if (m_knownChanged != 0)
641 {
642 if ((m_knownChanged & m_knownChangedPosition) != 0)
643 Prim.ForcePosition = VehiclePosition;
644 if ((m_knownChanged & m_knownChangedOrientation) != 0)
645 Prim.ForceOrientation = VehicleOrientation;
646 if ((m_knownChanged & m_knownChangedVelocity) != 0)
647 {
648 Prim.ForceVelocity = VehicleVelocity;
649 BulletSimAPI.SetInterpolationLinearVelocity2(Prim.PhysBody.ptr, VehicleVelocity);
650 }
651 if ((m_knownChanged & m_knownChangedForce) != 0)
652 Prim.AddForce((Vector3)m_knownForce, false, true);
653
654 if ((m_knownChanged & m_knownChangedRotationalVelocity) != 0)
655 {
656 Prim.ForceRotationalVelocity = VehicleRotationalVelocity;
657 // Fake out Bullet by making it think the velocity is the same as last time.
658 BulletSimAPI.SetInterpolationAngularVelocity2(Prim.PhysBody.ptr, VehicleRotationalVelocity);
659 }
660 if ((m_knownChanged & m_knownChangedRotationalForce) != 0)
661 Prim.AddAngularForce((Vector3)m_knownRotationalForce, false, true);
662
663 // If we set one of the values (ie, the physics engine didn't do it) we must force
664 // an UpdateProperties event to send the changes up to the simulator.
665 BulletSimAPI.PushUpdate2(Prim.PhysBody.ptr);
666 }
667 m_knownChanged = 0;
668 }
669
670 // Since the computation of terrain height can be a little involved, this routine
671 // is used ot fetch the height only once for each vehicle simulation step.
672 private float GetTerrainHeight(Vector3 pos)
673 {
674 if (m_knownTerrainHeight == null)
675 m_knownTerrainHeight = Prim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos);
676 return (float)m_knownTerrainHeight;
677 }
678
679 // Since the computation of water level can be a little involved, this routine
680 // is used ot fetch the level only once for each vehicle simulation step.
681 private float GetWaterLevel(Vector3 pos)
682 {
683 if (m_knownWaterLevel == null)
684 m_knownWaterLevel = Prim.PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(pos);
685 return (float)m_knownWaterLevel;
686 }
687
688 private Vector3 VehiclePosition
689 {
690 get
691 {
692 if (m_knownPosition == null)
693 m_knownPosition = Prim.ForcePosition;
694 return (Vector3)m_knownPosition;
695 }
696 set
697 {
698 m_knownPosition = value;
699 m_knownChanged |= m_knownChangedPosition;
700 }
701 }
702
703 private Quaternion VehicleOrientation
704 {
705 get
706 {
707 if (m_knownOrientation == null)
708 m_knownOrientation = Prim.ForceOrientation;
709 return (Quaternion)m_knownOrientation;
710 }
711 set
712 {
713 m_knownOrientation = value;
714 m_knownChanged |= m_knownChangedOrientation;
715 }
716 }
717
718 private Vector3 VehicleVelocity
719 {
720 get
721 {
722 if (m_knownVelocity == null)
723 m_knownVelocity = Prim.ForceVelocity;
724 return (Vector3)m_knownVelocity;
725 }
726 set
727 {
728 m_knownVelocity = value;
729 m_knownChanged |= m_knownChangedVelocity;
730 }
731 }
732
733 private void VehicleAddForce(Vector3 aForce)
734 {
735 m_knownForce += aForce;
736 m_knownChanged |= m_knownChangedForce;
737 }
738
739 private Vector3 VehicleRotationalVelocity
740 {
741 get
742 {
743 if (m_knownRotationalVelocity == null)
744 m_knownRotationalVelocity = Prim.ForceRotationalVelocity;
745 return (Vector3)m_knownRotationalVelocity;
746 }
747 set
748 {
749 m_knownRotationalVelocity = value;
750 m_knownChanged |= m_knownChangedRotationalVelocity;
751 }
752 }
753 private void VehicleAddAngularForce(Vector3 aForce)
754 {
755 m_knownRotationalForce += aForce;
756 m_knownChanged |= m_knownChangedRotationalForce;
757 }
758 private float VehicleForwardSpeed
759 {
760 get
761 {
762 if (m_knownForwardSpeed == null)
763 m_knownForwardSpeed = (VehicleVelocity * Quaternion.Inverse(VehicleOrientation)).X;
764 return (float)m_knownForwardSpeed;
765 }
766 }
767
768 #endregion // Known vehicle value functions
769
549 // One step of the vehicle properties for the next 'pTimestep' seconds. 770 // One step of the vehicle properties for the next 'pTimestep' seconds.
550 internal void Step(float pTimestep) 771 internal void Step(float pTimestep)
551 { 772 {
552 if (!IsActive) return; 773 if (!IsActive) return;
553 774
554 // DEBUG 775 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 776
565 MoveLinear(pTimestep); 777 MoveLinear(pTimestep);
566 // Commented out for debug
567 MoveAngular(pTimestep); 778 MoveAngular(pTimestep);
568 // Prim.ApplyTorqueImpulse(-Prim.RotationalVelocity * m_vehicleMass, false); // DEBUG DEBUG
569 // Prim.ForceRotationalVelocity = -Prim.RotationalVelocity; // DEBUG DEBUG
570 779
571 LimitRotation(pTimestep); 780 LimitRotation(pTimestep);
572 781
573 // remember the position so next step we can limit absolute movement effects 782 // remember the position so next step we can limit absolute movement effects
574 m_lastPositionVector = Prim.ForcePosition; 783 m_lastPositionVector = VehiclePosition;
575 784
576 VDetailLog("{0},BSDynamics.Step,frict={1},grav={2},inertia={3},mass={4}", // DEBUG DEBUG 785 // If we forced the changing of some vehicle parameters, update the values and
577 Prim.LocalID, 786 // for the physics engine to note the changes so an UpdateProperties event will happen.
578 BulletSimAPI.GetFriction2(Prim.PhysBody.ptr), 787 PushKnownChanged();
579 BulletSimAPI.GetGravity2(Prim.PhysBody.ptr), 788
580 Prim.Inertia,
581 m_vehicleMass
582 );
583 VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}", 789 VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}",
584 Prim.LocalID, Prim.ForcePosition, Prim.Force, Prim.ForceVelocity, Prim.RotationalVelocity); 790 Prim.LocalID, VehiclePosition, Prim.Force, VehicleVelocity, VehicleRotationalVelocity);
585 }// end Step 791 }
586 792
587 // Apply the effect of the linear motor. 793 // 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) 794 private void MoveLinear(float pTimestep)
590 { 795 {
591 // m_linearMotorDirection is the target direction we are moving relative to the vehicle coordinates 796 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 797
600 // Add (desiredVelocity - lastAppliedVelocity) / howLongItShouldTakeToComplete 798 // The movement computed in the linear motor is relative to the vehicle
601 Vector3 addAmount = (m_linearMotorDirection - m_lastLinearVelocityVector)/(m_linearMotorTimescale) * pTimestep; 799 // coordinates. Rotate the movement to world coordinates.
602 m_lastLinearVelocityVector += addAmount; 800 linearMotorContribution *= VehicleOrientation;
603 801
604 float decayFactor = (1.0f / m_linearMotorDecayTimescale) * pTimestep; 802 // ==================================================================
605 m_linearMotorDirection *= (1f - decayFactor); 803 // Buoyancy: force to overcome gravity.
804 // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g;
805 // So, if zero, don't change anything (let gravity happen). If one, negate the effect of gravity.
806 Vector3 buoyancyContribution = Prim.PhysicsScene.DefaultGravity * m_VehicleBuoyancy;
606 807
607 // Rotate new object velocity from vehicle relative to world coordinates 808 Vector3 terrainHeightContribution = ComputeLinearTerrainHeightCorrection(pTimestep);
608 m_newVelocity = m_lastLinearVelocityVector * Prim.ForceOrientation;
609 809
610 // Apply friction for next time 810 Vector3 hoverContribution = ComputeLinearHover(pTimestep);
611 Vector3 frictionFactor = (Vector3.One / m_linearFrictionTimescale) * pTimestep;
612 m_lastLinearVelocityVector *= (Vector3.One - frictionFactor);
613 811
614 VDetailLog("{0},MoveLinear,nonZero,origlmDir={1},origlvVel={2},vehVel={3},add={4},decay={5},frict={6},lmDir={7},lvVec={8},newVel={9}", 812 ComputeLinearBlockingEndPoint(pTimestep);
615 Prim.LocalID, origDir, origVel, vehicleVelocity, addAmount, decayFactor, frictionFactor,
616 m_linearMotorDirection, m_lastLinearVelocityVector, m_newVelocity);
617 }
618 else
619 {
620 // if what remains of direction is very small, zero it.
621 m_linearMotorDirection = Vector3.Zero;
622 m_lastLinearVelocityVector = Vector3.Zero;
623 m_newVelocity = Vector3.Zero;
624 813
625 VDetailLog("{0},MoveLinear,zeroed", Prim.LocalID); 814 Vector3 limitMotorUpContribution = ComputeLinearMotorUp(pTimestep);
626 }
627 815
628 // m_newVelocity is velocity computed from linear motor in world coordinates 816 // ==================================================================
817 Vector3 newVelocity = linearMotorContribution
818 + terrainHeightContribution
819 + hoverContribution
820 + limitMotorUpContribution;
629 821
630 // Gravity and Buoyancy 822 Vector3 newForce = buoyancyContribution;
631 // There is some gravity, make a gravity force vector that is applied after object velocity. 823
632 // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g; 824 // If not changing some axis, reduce out velocity
633 Vector3 grav = Prim.PhysicsScene.DefaultGravity * (1f - m_VehicleBuoyancy); 825 if ((m_flags & (VehicleFlag.NO_X)) != 0)
826 newVelocity.X = 0;
827 if ((m_flags & (VehicleFlag.NO_Y)) != 0)
828 newVelocity.Y = 0;
829 if ((m_flags & (VehicleFlag.NO_Z)) != 0)
830 newVelocity.Z = 0;
634 831
635 /* 832 // ==================================================================
636 * RA: Not sure why one would do this unless we are hoping external forces are doing gravity, ... 833 // Clamp high or low velocities
637 // Preserve the current Z velocity 834 float newVelocityLengthSq = newVelocity.LengthSquared();
638 Vector3 vel_now = m_prim.Velocity; 835 // if (newVelocityLengthSq > 1e6f)
639 m_dir.Z = vel_now.Z; // Preserve the accumulated falling velocity 836 if (newVelocityLengthSq > 1000f)
640 */ 837 {
838 newVelocity /= newVelocity.Length();
839 newVelocity *= 1000f;
840 }
841 // else if (newVelocityLengthSq < 1e-6f)
842 else if (newVelocityLengthSq < 0.001f)
843 newVelocity = Vector3.Zero;
844
845 // ==================================================================
846 // Stuff new linear velocity into the vehicle.
847 // Since the velocity is just being set, it is not scaled by pTimeStep. Bullet will do that for us.
848 VehicleVelocity = newVelocity;
849
850 // Other linear forces are applied as forces.
851 Vector3 totalDownForce = newForce * m_vehicleMass;
852 if (!totalDownForce.ApproxEquals(Vector3.Zero, 0.01f))
853 {
854 VehicleAddForce(totalDownForce);
855 }
856
857 VDetailLog("{0}, MoveLinear,done,newVel={1},totDown={2},IsColliding={3}",
858 Prim.LocalID, newVelocity, totalDownForce, Prim.IsColliding);
859 VDetailLog("{0}, MoveLinear,done,linContrib={1},terrContrib={2},hoverContrib={3},limitContrib={4},buoyContrib={5}",
860 Prim.LocalID,
861 linearMotorContribution, terrainHeightContribution, hoverContribution,
862 limitMotorUpContribution, buoyancyContribution
863 );
641 864
642 Vector3 pos = Prim.ForcePosition; 865 } // 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 866
867 public Vector3 ComputeLinearTerrainHeightCorrection(float pTimestep)
868 {
869 Vector3 ret = Vector3.Zero;
645 // If below the terrain, move us above the ground a little. 870 // If below the terrain, move us above the ground a little.
646 float terrainHeight = Prim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos); 871 // 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. 872 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 { 873 {
653 pos.Z = terrainHeight + 2; 874 // TODO: correct position by applying force rather than forcing position.
654 Prim.ForcePosition = pos; 875 Vector3 newPosition = VehiclePosition;
655 VDetailLog("{0},MoveLinear,terrainHeight,terrainHeight={1},pos={2}", Prim.LocalID, terrainHeight, pos); 876 newPosition.Z = GetTerrainHeight(VehiclePosition) + 1f;
877 VehiclePosition = newPosition;
878 VDetailLog("{0}, MoveLinear,terrainHeight,terrainHeight={1},pos={2}",
879 Prim.LocalID, GetTerrainHeight(VehiclePosition), VehiclePosition);
656 } 880 }
881 return ret;
882 }
883
884 public Vector3 ComputeLinearHover(float pTimestep)
885 {
886 Vector3 ret = Vector3.Zero;
657 887
658 // Check if hovering
659 // m_VhoverEfficiency: 0=bouncy, 1=totally damped 888 // m_VhoverEfficiency: 0=bouncy, 1=totally damped
660 // m_VhoverTimescale: time to achieve height 889 // m_VhoverTimescale: time to achieve height
661 if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0) 890 if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0)
@@ -663,11 +892,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin
663 // We should hover, get the target height 892 // We should hover, get the target height
664 if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0) 893 if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0)
665 { 894 {
666 m_VhoverTargetHeight = Prim.PhysicsScene.GetWaterLevelAtXYZ(pos) + m_VhoverHeight; 895 m_VhoverTargetHeight = GetWaterLevel(VehiclePosition) + m_VhoverHeight;
667 } 896 }
668 if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0) 897 if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0)
669 { 898 {
670 m_VhoverTargetHeight = terrainHeight + m_VhoverHeight; 899 m_VhoverTargetHeight = GetTerrainHeight(VehiclePosition) + m_VhoverHeight;
671 } 900 }
672 if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0) 901 if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0)
673 { 902 {
@@ -677,45 +906,47 @@ namespace OpenSim.Region.Physics.BulletSPlugin
677 if ((m_flags & VehicleFlag.HOVER_UP_ONLY) != 0) 906 if ((m_flags & VehicleFlag.HOVER_UP_ONLY) != 0)
678 { 907 {
679 // If body is already heigher, use its height as target height 908 // If body is already heigher, use its height as target height
680 if (pos.Z > m_VhoverTargetHeight) 909 if (VehiclePosition.Z > m_VhoverTargetHeight)
681 m_VhoverTargetHeight = pos.Z; 910 m_VhoverTargetHeight = VehiclePosition.Z;
682 } 911 }
912
683 if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0) 913 if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0)
684 { 914 {
685 if (Math.Abs(pos.Z - m_VhoverTargetHeight) > 0.2f) 915 if (Math.Abs(VehiclePosition.Z - m_VhoverTargetHeight) > 0.2f)
686 { 916 {
917 Vector3 pos = VehiclePosition;
687 pos.Z = m_VhoverTargetHeight; 918 pos.Z = m_VhoverTargetHeight;
688 Prim.ForcePosition = pos; 919 VehiclePosition = pos;
689 } 920 }
690 } 921 }
691 else 922 else
692 { 923 {
693 float verticalError = pos.Z - m_VhoverTargetHeight; 924 // Error is positive if below the target and negative if above.
694 // RA: where does the 50 come from? 925 float verticalError = m_VhoverTargetHeight - VehiclePosition.Z;
695 float verticalCorrectionVelocity = pTimestep * ((verticalError * 50.0f) / m_VhoverTimescale); 926 float verticalCorrectionVelocity = verticalError / m_VhoverTimescale;
696 // Replace Vertical speed with correction figure if significant 927
697 if (Math.Abs(verticalError) > 0.01f) 928 // TODO: implement m_VhoverEfficiency correctly
698 { 929 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 { 930 {
708 m_newVelocity.Z = 0f; 931 ret = new Vector3(0f, 0f, verticalCorrectionVelocity);
709 } 932 }
710 } 933 }
711 934
712 VDetailLog("{0},MoveLinear,hover,pos={1},dir={2},height={3},target={4}", Prim.LocalID, pos, m_newVelocity, m_VhoverHeight, m_VhoverTargetHeight); 935 VDetailLog("{0}, MoveLinear,hover,pos={1},ret={2},hoverTS={3},height={4},target={5}",
936 Prim.LocalID, VehiclePosition, ret, m_VhoverTimescale, m_VhoverHeight, m_VhoverTargetHeight);
713 } 937 }
714 938
939 return ret;
940 }
941
942 public bool ComputeLinearBlockingEndPoint(float pTimestep)
943 {
944 bool changed = false;
945
946 Vector3 pos = VehiclePosition;
715 Vector3 posChange = pos - m_lastPositionVector; 947 Vector3 posChange = pos - m_lastPositionVector;
716 if (m_BlockingEndPoint != Vector3.Zero) 948 if (m_BlockingEndPoint != Vector3.Zero)
717 { 949 {
718 bool changed = false;
719 if (pos.X >= (m_BlockingEndPoint.X - (float)1)) 950 if (pos.X >= (m_BlockingEndPoint.X - (float)1))
720 { 951 {
721 pos.X -= posChange.X + 1; 952 pos.X -= posChange.X + 1;
@@ -743,233 +974,110 @@ namespace OpenSim.Region.Physics.BulletSPlugin
743 } 974 }
744 if (changed) 975 if (changed)
745 { 976 {
746 Prim.ForcePosition = pos; 977 VehiclePosition = pos;
747 VDetailLog("{0},MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}", 978 VDetailLog("{0}, MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}",
748 Prim.LocalID, m_BlockingEndPoint, posChange, pos); 979 Prim.LocalID, m_BlockingEndPoint, posChange, pos);
749 } 980 }
750 } 981 }
982 return changed;
983 }
751 984
752 #region downForce 985 // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags :
753 Vector3 downForce = Vector3.Zero; 986 // Prevent ground vehicles from motoring into the sky. This flag has a subtle effect when
987 // used with conjunction with banking: the strength of the banking will decay when the
988 // vehicle no longer experiences collisions. The decay timescale is the same as
989 // VEHICLE_BANKING_TIMESCALE. This is to help prevent ground vehicles from steering
990 // when they are in mid jump.
991 // TODO: this code is wrong. Also, what should it do for boats (height from water)?
992 // This is just using the ground and a general collision check. Should really be using
993 // a downward raycast to find what is below.
994 public Vector3 ComputeLinearMotorUp(float pTimestep)
995 {
996 Vector3 ret = Vector3.Zero;
997 float distanceAboveGround = 0f;
754 998
755 if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0) 999 if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0)
756 { 1000 {
757 // If the vehicle is motoring into the sky, get it going back down. 1001 float targetHeight = Type == Vehicle.TYPE_BOAT ? GetWaterLevel(VehiclePosition) : GetTerrainHeight(VehiclePosition);
758 // Is this an angular force or both linear and angular?? 1002 distanceAboveGround = VehiclePosition.Z - targetHeight;
759 float distanceAboveGround = pos.Z - terrainHeight; 1003 // Not colliding if the vehicle is off the ground
760 if (distanceAboveGround > 2f) 1004 if (!Prim.IsColliding)
761 { 1005 {
762 // downForce = new Vector3(0, 0, (-distanceAboveGround / m_bankingTimescale) * pTimestep); 1006 // downForce = new Vector3(0, 0, (-distanceAboveGround / m_bankingTimescale) * pTimestep);
763 // downForce = new Vector3(0, 0, -distanceAboveGround / m_bankingTimescale); 1007 // downForce = new Vector3(0, 0, -distanceAboveGround / m_bankingTimescale);
764 downForce = new Vector3(0, 0, -distanceAboveGround); 1008 ret = new Vector3(0, 0, -distanceAboveGround);
765 } 1009 }
766 // TODO: this calculation is all wrong. From the description at 1010 // TODO: this calculation is wrong. From the description at
767 // (http://wiki.secondlife.com/wiki/Category:LSL_Vehicle), the downForce 1011 // (http://wiki.secondlife.com/wiki/Category:LSL_Vehicle), the downForce
768 // has a decay factor. This says this force should 1012 // has a decay factor. This says this force should
769 // be computed with a motor. 1013 // be computed with a motor.
770 VDetailLog("{0},MoveLinear,limitMotorUp,distAbove={1},downForce={2}", 1014 // 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 } 1015 }
789 else if (m_newVelocity.LengthSquared() < 1e-6f) 1016 VDetailLog("{0}, MoveLinear,limitMotorUp,distAbove={1},colliding={2},ret={3}",
790 m_newVelocity = Vector3.Zero; 1017 Prim.LocalID, distanceAboveGround, Prim.IsColliding, ret);
791 1018 return ret;
792 // Stuff new linear velocity into the vehicle 1019 }
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 }
802
803 VDetailLog("{0},MoveLinear,done,lmDir={1},lmVel={2},newVel={3},primVel={4},totalDown={5}",
804 Prim.LocalID, m_linearMotorDirection, m_lastLinearVelocityVector, m_newVelocity, Prim.Velocity, totalDownForce);
805
806 } // end MoveLinear()
807 1020
808 // ======================================================================= 1021 // =======================================================================
1022 // =======================================================================
809 // Apply the effect of the angular motor. 1023 // Apply the effect of the angular motor.
1024 // The 'contribution' is how much angular correction velocity each function wants.
1025 // All the contributions are added together and the resulting velocity is
1026 // set directly on the vehicle.
810 private void MoveAngular(float pTimestep) 1027 private void MoveAngular(float pTimestep)
811 { 1028 {
812 // m_angularMotorDirection // angular velocity requested by LSL motor 1029 // The user wants how many radians per second angular change?
813 // m_angularMotorApply // application frame counter 1030 Vector3 angularMotorContribution = m_angularMotor.Step(pTimestep);
814 // m_angularMotorVelocity // current angular motor velocity (ramps up and down) 1031
815 // m_angularMotorTimescale // motor angular velocity ramp up rate 1032 // ==================================================================
816 // m_angularMotorDecayTimescale // motor angular velocity decay rate 1033 // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags :
817 // m_angularFrictionTimescale // body angular velocity decay rate 1034 // This flag prevents linear deflection parallel to world z-axis. This is useful
818 // m_lastAngularVelocity // what was last applied to body 1035 // for preventing ground vehicles with large linear deflection, like bumper cars,
819 1036 // from climbing their linear deflection into the sky.
820 if (m_angularMotorDirection.LengthSquared() > 0.0001) 1037 // That is, NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement
821 { 1038 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 { 1039 {
836 m_angularMotorVelocity = Vector3.Zero; 1040 angularMotorContribution.X = 0f;
1041 angularMotorContribution.Y = 0f;
1042 VDetailLog("{0}, MoveAngular,noDeflectionUp,angularMotorContrib={1}", Prim.LocalID, angularMotorContribution);
837 } 1043 }
838 1044
839 #region Vertical attactor 1045 Vector3 verticalAttractionContribution = ComputeAngularVerticalAttraction();
840
841 Vector3 vertattr = Vector3.Zero;
842 Vector3 deflection = Vector3.Zero;
843 Vector3 banking = Vector3.Zero;
844
845 // If vertical attaction timescale is reasonable and we applied an angular force last time...
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
871 // As the body rotates around the X axis, then verticalError.Y increases; Rotated around Y
872 // then .X increases, so change Body angular velocity X based on Y, and Y based on X.
873 // Z is not changed.
874 vertattr.X = verticalError.Y;
875 vertattr.Y = - verticalError.X;
876 vertattr.Z = 0f;
877 1046
878 // scaling appears better usingsquare-law 1047 Vector3 deflectionContribution = ComputeAngularDeflection();
879 Vector3 angularVelocity = Prim.ForceRotationalVelocity;
880 float bounce = 1.0f - (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency);
881 vertattr.X += bounce * angularVelocity.X;
882 vertattr.Y += bounce * angularVelocity.Y;
883 1048
884 VDetailLog("{0},MoveAngular,verticalAttraction,VAservo={1},effic={2},verticalError={3},bounce={4},vertattr={5}", 1049 Vector3 bankingContribution = ComputeAngularBanking();
885 Prim.LocalID, VAservo, m_verticalAttractionEfficiency, verticalError, bounce, vertattr);
886 1050
887 } 1051 // ==================================================================
888 #endregion // Vertical attactor 1052 m_lastVertAttractor = verticalAttractionContribution;
889 1053
890 #region Deflection 1054 m_lastAngularVelocity = angularMotorContribution
1055 + verticalAttractionContribution
1056 + deflectionContribution
1057 + bankingContribution;
891 1058
892 if (m_angularDeflectionEfficiency != 0) 1059 // ==================================================================
1060 // Apply the correction velocity.
1061 // TODO: Should this be applied as an angular force (torque)?
1062 if (!m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f))
893 { 1063 {
894 // Compute a scaled vector that points in the preferred axis (X direction) 1064 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
912 #region Banking
913 1065
914 if (m_bankingEfficiency != 0) 1066 VDetailLog("{0}, MoveAngular,done,nonZero,angMotorContrib={1},vertAttrContrib={2},bankContrib={3},deflectContrib={4},totalContrib={5}",
1067 Prim.LocalID,
1068 angularMotorContribution, verticalAttractionContribution,
1069 bankingContribution, deflectionContribution,
1070 m_lastAngularVelocity
1071 );
1072 }
1073 else
915 { 1074 {
916 Vector3 dir = Vector3.One * Prim.ForceOrientation; 1075 // The vehicle is not adding anything angular wise.
917 float mult = (m_bankingMix*m_bankingMix)*-1*(m_bankingMix < 0 ? -1 : 1); 1076 VehicleRotationalVelocity = Vector3.Zero;
918 //Changes which way it banks in and out of turns 1077 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 } 1078 }
963 1079
964 #endregion 1080 // ==================================================================
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 1081 //Offset section
974 if (m_linearMotorOffset != Vector3.Zero) 1082 if (m_linearMotorOffset != Vector3.Zero)
975 { 1083 {
@@ -985,8 +1093,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin
985 // 1093 //
986 // The torque created is the linear velocity crossed with the offset 1094 // The torque created is the linear velocity crossed with the offset
987 1095
988 // NOTE: this computation does should be in the linear section 1096 // TODO: this computation should be in the linear section
989 // because there we know the impulse being applied. 1097 // because that is where we know the impulse being applied.
990 Vector3 torqueFromOffset = Vector3.Zero; 1098 Vector3 torqueFromOffset = Vector3.Zero;
991 // torqueFromOffset = Vector3.Cross(m_linearMotorOffset, appliedImpulse); 1099 // torqueFromOffset = Vector3.Cross(m_linearMotorOffset, appliedImpulse);
992 if (float.IsNaN(torqueFromOffset.X)) 1100 if (float.IsNaN(torqueFromOffset.X))
@@ -995,47 +1103,175 @@ namespace OpenSim.Region.Physics.BulletSPlugin
995 torqueFromOffset.Y = 0; 1103 torqueFromOffset.Y = 0;
996 if (float.IsNaN(torqueFromOffset.Z)) 1104 if (float.IsNaN(torqueFromOffset.Z))
997 torqueFromOffset.Z = 0; 1105 torqueFromOffset.Z = 0;
998 torqueFromOffset *= m_vehicleMass; 1106
999 Prim.ApplyTorqueImpulse(torqueFromOffset, true); 1107 VehicleAddAngularForce(torqueFromOffset * m_vehicleMass);
1000 VDetailLog("{0},BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", Prim.LocalID, torqueFromOffset); 1108 VDetailLog("{0}, BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", Prim.LocalID, torqueFromOffset);
1001 } 1109 }
1002 1110
1003 #endregion 1111 }
1112 // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
1113 // Some vehicles, like boats, should always keep their up-side up. This can be done by
1114 // enabling the "vertical attractor" behavior that springs the vehicle's local z-axis to
1115 // the world z-axis (a.k.a. "up"). To take advantage of this feature you would set the
1116 // VEHICLE_VERTICAL_ATTRACTION_TIMESCALE to control the period of the spring frequency,
1117 // and then set the VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY to control the damping. An
1118 // efficiency of 0.0 will cause the spring to wobble around its equilibrium, while an
1119 // efficiency of 1.0 will cause the spring to reach its equilibrium with exponential decay.
1120 public Vector3 ComputeAngularVerticalAttraction()
1121 {
1122 Vector3 ret = Vector3.Zero;
1004 1123
1005 if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0) 1124 // If vertical attaction timescale is reasonable
1125 if (m_verticalAttractionTimescale < m_verticalAttractionCutoff)
1006 { 1126 {
1007 m_lastAngularVelocity.X = 0; 1127 // Take a vector pointing up and convert it from world to vehicle relative coords.
1008 m_lastAngularVelocity.Y = 0; 1128 Vector3 verticalError = Vector3.UnitZ * VehicleOrientation;
1009 VDetailLog("{0},MoveAngular,noDeflectionUp,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity); 1129
1130 // If vertical attraction correction is needed, the vector that was pointing up (UnitZ)
1131 // is now:
1132 // leaning to one side: rotated around the X axis with the Y value going
1133 // from zero (nearly straight up) to one (completely to the side)) or
1134 // leaning front-to-back: rotated around the Y axis with the value of X being between
1135 // zero and one.
1136 // The value of Z is how far the rotation is off with 1 meaning none and 0 being 90 degrees.
1137
1138 // If verticalError.Z is negative, the vehicle is upside down. Add additional push.
1139 if (verticalError.Z < 0f)
1140 {
1141 verticalError.X = 2f - verticalError.X;
1142 verticalError.Y = 2f - verticalError.Y;
1143 }
1144
1145 // Y error means needed rotation around X axis and visa versa.
1146 ret.X = verticalError.Y;
1147 ret.Y = - verticalError.X;
1148 ret.Z = 0f;
1149
1150 // Scale the correction force by how far we're off from vertical.
1151 // Z error of one says little error. As Z gets smaller, the vehicle is leaning farther over.
1152 float clampedSqrZError = ClampInRange(0.01f, verticalError.Z * verticalError.Z, 1f);
1153 float vertForce = 1f / clampedSqrZError;
1154
1155 ret *= vertForce;
1156
1157 // Correction happens over a number of seconds.
1158 Vector3 unscaledContrib = ret;
1159 ret /= m_verticalAttractionTimescale;
1160
1161 VDetailLog("{0}, MoveAngular,verticalAttraction,,verticalError={1},unscaled={2},vertForce={3},eff={4},vertAttr={5}",
1162 Prim.LocalID, verticalError, unscaledContrib, vertForce, m_verticalAttractionEfficiency, ret);
1010 } 1163 }
1164 return ret;
1165 }
1166
1167 // Return the angular correction to correct the direction the vehicle is pointing to be
1168 // the direction is should want to be pointing.
1169 // The vehicle is moving in some direction and correct its orientation to it is pointing
1170 // in that direction.
1171 // TODO: implement reference frame.
1172 public Vector3 ComputeAngularDeflection()
1173 {
1174 Vector3 ret = Vector3.Zero;
1175 return ret; // DEBUG DEBUG DEBUG debug one force at a time
1011 1176
1012 if (m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f)) 1177 if (m_angularDeflectionEfficiency != 0)
1013 { 1178 {
1014 m_lastAngularVelocity = Vector3.Zero; // Reduce small value to zero. 1179 // The direction the vehicle is moving
1015 Prim.ZeroAngularMotion(true); 1180 Vector3 movingDirection = VehicleVelocity;
1016 VDetailLog("{0},MoveAngular,zeroAngularMotion,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity); 1181 movingDirection.Normalize();
1182
1183 // The direction the vehicle is pointing
1184 Vector3 pointingDirection = Vector3.UnitX * VehicleOrientation;
1185 pointingDirection.Normalize();
1186
1187 // The difference between what is and what should be
1188 Vector3 deflectionError = movingDirection - pointingDirection;
1189
1190 // Scale the correction by recovery timescale and efficiency
1191 ret = (-deflectionError * VehicleForwardSpeed) * m_angularDeflectionEfficiency;
1192 ret /= m_angularDeflectionTimescale;
1193
1194 VDetailLog("{0}, MoveAngular,Deflection,movingDir={1},pointingDir={2},deflectError={3},ret={4}",
1195 Prim.LocalID, movingDirection, pointingDirection, deflectionError, ret);
1017 } 1196 }
1018 else 1197 return ret;
1198 }
1199
1200 // Return an angular change to rotate the vehicle around the Z axis when the vehicle
1201 // is tipped around the X axis.
1202 // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
1203 // The vertical attractor feature must be enabled in order for the banking behavior to
1204 // function. The way banking works is this: a rotation around the vehicle's roll-axis will
1205 // produce a angular velocity around the yaw-axis, causing the vehicle to turn. The magnitude
1206 // of the yaw effect will be proportional to the
1207 // VEHICLE_BANKING_EFFICIENCY, the angle of the roll rotation, and sometimes the vehicle's
1208 // velocity along its preferred axis of motion.
1209 // The VEHICLE_BANKING_EFFICIENCY can vary between -1 and +1. When it is positive then any
1210 // positive rotation (by the right-hand rule) about the roll-axis will effect a
1211 // (negative) torque around the yaw-axis, making it turn to the right--that is the
1212 // vehicle will lean into the turn, which is how real airplanes and motorcycle's work.
1213 // Negating the banking coefficient will make it so that the vehicle leans to the
1214 // outside of the turn (not very "physical" but might allow interesting vehicles so why not?).
1215 // The VEHICLE_BANKING_MIX is a fake (i.e. non-physical) parameter that is useful for making
1216 // banking vehicles do what you want rather than what the laws of physics allow.
1217 // For example, consider a real motorcycle...it must be moving forward in order for
1218 // it to turn while banking, however video-game motorcycles are often configured
1219 // to turn in place when at a dead stop--because they are often easier to control
1220 // that way using the limited interface of the keyboard or game controller. The
1221 // VEHICLE_BANKING_MIX enables combinations of both realistic and non-realistic
1222 // banking by functioning as a slider between a banking that is correspondingly
1223 // totally static (0.0) and totally dynamic (1.0). By "static" we mean that the
1224 // banking effect depends only on the vehicle's rotation about its roll-axis compared
1225 // to "dynamic" where the banking is also proportional to its velocity along its
1226 // roll-axis. Finding the best value of the "mixture" will probably require trial and error.
1227 // The time it takes for the banking behavior to defeat a preexisting angular velocity about the
1228 // world z-axis is determined by the VEHICLE_BANKING_TIMESCALE. So if you want the vehicle to
1229 // bank quickly then give it a banking timescale of about a second or less, otherwise you can
1230 // make a sluggish vehicle by giving it a timescale of several seconds.
1231 public Vector3 ComputeAngularBanking()
1232 {
1233 Vector3 ret = Vector3.Zero;
1234
1235 if (m_bankingEfficiency != 0 && m_verticalAttractionTimescale < m_verticalAttractionCutoff)
1019 { 1236 {
1020 // Apply to the body. 1237 // 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. 1238 // 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 1239 // up to one for full over).
1023 // velocity needs to be scaled by the timestep. 1240 Vector3 rollComponents = Vector3.UnitZ * VehicleOrientation;
1024 Vector3 applyAngularForce = ((m_lastAngularVelocity * pTimestep) - Prim.ForceRotationalVelocity); 1241
1025 Prim.ForceRotationalVelocity = applyAngularForce; 1242 // Figure out the yaw value for this much roll.
1026 1243 float turnComponent = rollComponents.Y * rollComponents.Y * m_bankingEfficiency;
1027 // Decay the angular movement for next time 1244 // Keep the sign
1028 Vector3 decayamount = (Vector3.One / m_angularFrictionTimescale) * pTimestep; 1245 if (rollComponents.Y < 0f)
1029 m_lastAngularVelocity *= Vector3.One - decayamount; 1246 turnComponent = -turnComponent;
1030 1247
1031 VDetailLog("{0},MoveAngular,done,newRotVel={1},decay={2},lastAngular={3}", 1248 // TODO: there must be a better computation of the banking force.
1032 Prim.LocalID, applyAngularForce, decayamount, m_lastAngularVelocity); 1249 float bankingTurnForce = turnComponent;
1250
1251 // actual error = static turn error + dynamic turn error
1252 float mixedBankingError = bankingTurnForce * (1f - m_bankingMix) + bankingTurnForce * m_bankingMix * VehicleForwardSpeed;
1253 // TODO: the banking effect should not go to infinity but what to limit it to?
1254 mixedBankingError = ClampInRange(-20f, mixedBankingError, 20f);
1255
1256 // Build the force vector to change rotation from what it is to what it should be
1257 ret.Z = -mixedBankingError;
1258
1259 // Don't do it all at once.
1260 ret /= m_bankingTimescale;
1261
1262 VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},turnComp={3},bankErr={4},mixedBankErr={5},ret={6}",
1263 Prim.LocalID, rollComponents, VehicleForwardSpeed, turnComponent, bankingTurnForce, mixedBankingError, ret);
1033 } 1264 }
1034 } //end MoveAngular 1265 return ret;
1266 }
1035 1267
1268 // This is from previous instantiations of XXXDynamics.cs.
1269 // Applies roll reference frame.
1270 // TODO: is this the right way to separate the code to do this operation?
1271 // Should this be in MoveAngular()?
1036 internal void LimitRotation(float timestep) 1272 internal void LimitRotation(float timestep)
1037 { 1273 {
1038 Quaternion rotq = Prim.ForceOrientation; 1274 Quaternion rotq = VehicleOrientation;
1039 Quaternion m_rot = rotq; 1275 Quaternion m_rot = rotq;
1040 if (m_RollreferenceFrame != Quaternion.Identity) 1276 if (m_RollreferenceFrame != Quaternion.Identity)
1041 { 1277 {
@@ -1063,12 +1299,17 @@ namespace OpenSim.Region.Physics.BulletSPlugin
1063 } 1299 }
1064 if (rotq != m_rot) 1300 if (rotq != m_rot)
1065 { 1301 {
1066 Prim.ForceOrientation = m_rot; 1302 VehicleOrientation = m_rot;
1067 VDetailLog("{0},LimitRotation,done,orig={1},new={2}", Prim.LocalID, rotq, m_rot); 1303 VDetailLog("{0}, LimitRotation,done,orig={1},new={2}", Prim.LocalID, rotq, m_rot);
1068 } 1304 }
1069 1305
1070 } 1306 }
1071 1307
1308 private float ClampInRange(float low, float val, float high)
1309 {
1310 return Math.Max(low, Math.Min(val, high));
1311 }
1312
1072 // Invoke the detailed logger and output something if it's enabled. 1313 // Invoke the detailed logger and output something if it's enabled.
1073 private void VDetailLog(string msg, params Object[] args) 1314 private void VDetailLog(string msg, params Object[] args)
1074 { 1315 {
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..e0faf4e 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs
@@ -1,104 +1,220 @@
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; 32
33 CurrentValueReductionTimescale = frictionTimeScale; 33namespace OpenSim.Region.Physics.BulletSPlugin
34 Efficiency = efficiency; 34{
35 } 35public abstract class BSMotor
36 public void SetCurrent(Vector3 current) 36{
37 { 37 // Timescales and other things can be turned off by setting them to 'infinite'.
38 CurrentValue = current; 38 public const float Infinite = 12345f;
39 } 39 public readonly static Vector3 InfiniteVector = new Vector3(BSMotor.Infinite, BSMotor.Infinite, BSMotor.Infinite);
40 public void SetTarget(Vector3 target) 40
41 { 41 public BSMotor(string useName)
42 TargetValue = target; 42 {
43 } 43 UseName = useName;
44 public Vector3 Step(float timeStep) 44 PhysicsScene = null;
45 { 45 }
46 if (CurrentValue.LengthSquared() > 0.001f) 46 public virtual void Reset() { }
47 { 47 public virtual void Zero() { }
48 // Vector3 origDir = Target; // DEBUG 48
49 // Vector3 origVel = CurrentValue; // DEBUG 49 // A name passed at motor creation for easily identifyable debugging messages.
50 50 public string UseName { get; private set; }
51 // Add (desiredVelocity - currentAppliedVelocity) / howLongItShouldTakeToComplete 51
52 Vector3 addAmount = (TargetValue - CurrentValue)/(TargetValue) * timeStep; 52 // Used only for outputting debug information. Might not be set so check for null.
53 CurrentValue += addAmount; 53 public BSScene PhysicsScene { get; set; }
54 54 protected void MDetailLog(string msg, params Object[] parms)
55 float decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep; 55 {
56 TargetValue *= (1f - decayFactor); 56 if (PhysicsScene != null)
57 57 {
58 Vector3 frictionFactor = (Vector3.One / CurrentValueReductionTimescale) * timeStep; 58 if (PhysicsScene.VehicleLoggingEnabled)
59 CurrentValue *= (Vector3.One - frictionFactor); 59 {
60 } 60 PhysicsScene.DetailLog(msg, parms);
61 else 61 }
62 { 62 }
63 // if what remains of direction is very small, zero it. 63 }
64 TargetValue = Vector3.Zero; 64}
65 CurrentValue = Vector3.Zero; 65// Can all the incremental stepping be replaced with motor classes?
66 66
67 // VDetailLog("{0},MoveLinear,zeroed", Prim.LocalID); 67// Motor which moves CurrentValue to TargetValue over TimeScale seconds.
68 } 68// The TargetValue decays in TargetValueDecayTimeScale and
69 return CurrentValue; 69// the CurrentValue will be held back by FrictionTimeScale.
70 } 70// TimeScale and TargetDelayTimeScale may be 'infinite' which means go decay.
71} 71
72 72// For instance, if something is moving at speed X and the desired speed is Y,
73public class BSFMotor : BSMotor 73// CurrentValue is X and TargetValue is Y. As the motor is stepped, new
74{ 74// values of CurrentValue are returned that approach the TargetValue.
75 public float TimeScale { get; set; } 75// The feature of decaying TargetValue is so vehicles will eventually
76 public float DecayTimeScale { get; set; } 76// come to a stop rather than run forever. This can be disabled by
77 public float Friction { get; set; } 77// setting TargetValueDecayTimescale to 'infinite'.
78 public float Efficiency { get; set; } 78// The change from CurrentValue to TargetValue is linear over TimeScale seconds.
79 79public class BSVMotor : BSMotor
80 public float Target { get; private set; } 80{
81 public float CurrentValue { get; private set; } 81 // public Vector3 FrameOfReference { get; set; }
82 82 // public Vector3 Offset { get; set; }
83 BSFMotor(float timeScale, float decayTimescale, float friction, float efficiency) 83
84 { 84 public float TimeScale { get; set; }
85 } 85 public float TargetValueDecayTimeScale { get; set; }
86 public void SetCurrent(float target) 86 public Vector3 FrictionTimescale { get; set; }
87 { 87 public float Efficiency { get; set; }
88 } 88
89 public void SetTarget(float target) 89 public Vector3 TargetValue { get; private set; }
90 { 90 public Vector3 CurrentValue { get; private set; }
91 } 91
92 public float Step(float timeStep) 92 public BSVMotor(string useName)
93 { 93 : base(useName)
94 return 0f; 94 {
95 } 95 TimeScale = TargetValueDecayTimeScale = BSMotor.Infinite;
96} 96 Efficiency = 1f;
97public class BSPIDMotor : BSMotor 97 FrictionTimescale = BSMotor.InfiniteVector;
98{ 98 CurrentValue = TargetValue = Vector3.Zero;
99 // TODO: write and use this one 99 }
100 BSPIDMotor() 100 public BSVMotor(string useName, float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency)
101 { 101 : this(useName)
102 } 102 {
103} 103 TimeScale = timeScale;
104} 104 TargetValueDecayTimeScale = decayTimeScale;
105 FrictionTimescale = frictionTimeScale;
106 Efficiency = efficiency;
107 CurrentValue = TargetValue = Vector3.Zero;
108 }
109 public void SetCurrent(Vector3 current)
110 {
111 CurrentValue = current;
112 }
113 public void SetTarget(Vector3 target)
114 {
115 TargetValue = target;
116 }
117
118 // A form of stepping that does not take the time quantum into account.
119 // The caller must do the right thing later.
120 public Vector3 Step()
121 {
122 return Step(1f);
123 }
124
125 public Vector3 Step(float timeStep)
126 {
127 Vector3 returnCurrent = Vector3.Zero;
128 if (!CurrentValue.ApproxEquals(TargetValue, 0.01f))
129 {
130 Vector3 origTarget = TargetValue; // DEBUG
131 Vector3 origCurrVal = CurrentValue; // DEBUG
132
133 // Addition = (desiredVector - currentAppliedVector) / secondsItShouldTakeToComplete
134 Vector3 addAmount = (TargetValue - CurrentValue)/TimeScale * timeStep;
135 CurrentValue += addAmount;
136
137 // The desired value reduces to zero which also reduces the difference with current.
138 // If the decay time is infinite, don't decay at all.
139 float decayFactor = 0f;
140 if (TargetValueDecayTimeScale != BSMotor.Infinite)
141 {
142 decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep;
143 TargetValue *= (1f - decayFactor);
144 }
145
146 Vector3 frictionFactor = Vector3.Zero;
147 if (FrictionTimescale != BSMotor.InfiniteVector)
148 {
149 // frictionFactor = (Vector3.One / FrictionTimescale) * timeStep;
150 // Individual friction components can be 'infinite' so compute each separately.
151 frictionFactor.X = FrictionTimescale.X == BSMotor.Infinite ? 0f : (1f / FrictionTimescale.X) * timeStep;
152 frictionFactor.Y = FrictionTimescale.Y == BSMotor.Infinite ? 0f : (1f / FrictionTimescale.Y) * timeStep;
153 frictionFactor.Z = FrictionTimescale.Z == BSMotor.Infinite ? 0f : (1f / FrictionTimescale.Z) * timeStep;
154 CurrentValue *= (Vector3.One - frictionFactor);
155 }
156
157 returnCurrent = CurrentValue;
158
159 MDetailLog("{0}, BSVMotor.Step,nonZero,{1},origCurr={2},origTarget={3},timeStep={4},timeScale={5},addAmnt={6},targetDecay={7},decayFact={8},fricTS={9},frictFact={10}",
160 BSScene.DetailLogZero, UseName, origCurrVal, origTarget,
161 timeStep, TimeScale, addAmount,
162 TargetValueDecayTimeScale, decayFactor,
163 FrictionTimescale, frictionFactor);
164 MDetailLog("{0}, BSVMotor.Step,nonZero,{1},curr={2},target={3},add={4},decay={5},frict={6},ret={7}",
165 BSScene.DetailLogZero, UseName, CurrentValue, TargetValue,
166 addAmount, decayFactor, frictionFactor, returnCurrent);
167 }
168 else
169 {
170 // Difference between what we have and target is small. Motor is done.
171 CurrentValue = Vector3.Zero;
172 TargetValue = Vector3.Zero;
173
174 MDetailLog("{0}, BSVMotor.Step,zero,{1},curr={2},target={3},ret={4}",
175 BSScene.DetailLogZero, UseName, TargetValue, CurrentValue, returnCurrent);
176
177 }
178 return returnCurrent;
179 }
180 public override string ToString()
181 {
182 return String.Format("<{0},curr={1},targ={2},decayTS={3},frictTS={4}>",
183 UseName, CurrentValue, TargetValue, TargetValueDecayTimeScale, FrictionTimescale);
184 }
185}
186
187public class BSFMotor : BSMotor
188{
189 public float TimeScale { get; set; }
190 public float DecayTimeScale { get; set; }
191 public float Friction { get; set; }
192 public float Efficiency { get; set; }
193
194 public float Target { get; private set; }
195 public float CurrentValue { get; private set; }
196
197 public BSFMotor(string useName, float timeScale, float decayTimescale, float friction, float efficiency)
198 : base(useName)
199 {
200 }
201 public void SetCurrent(float target)
202 {
203 }
204 public void SetTarget(float target)
205 {
206 }
207 public float Step(float timeStep)
208 {
209 return 0f;
210 }
211}
212public class BSPIDMotor : BSMotor
213{
214 // TODO: write and use this one
215 public BSPIDMotor(string useName)
216 : base(useName)
217 {
218 }
219}
220}
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..758d92b 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;
@@ -108,8 +107,8 @@ public sealed class BSPrim : BSPhysObject
108 _mass = CalculateMass(); 107 _mass = CalculateMass();
109 108
110 // No body or shape yet 109 // No body or shape yet
111 PhysBody = new BulletBody(LocalID, IntPtr.Zero); 110 PhysBody = new BulletBody(LocalID);
112 PhysShape = new BulletShape(IntPtr.Zero); 111 PhysShape = new BulletShape();
113 112
114 DetailLog("{0},BSPrim.constructor,call", LocalID); 113 DetailLog("{0},BSPrim.constructor,call", LocalID);
115 // do the actual object creation at taint time 114 // do the actual object creation at taint time
@@ -143,7 +142,9 @@ public sealed class BSPrim : BSPhysObject
143 DetailLog("{0},BSPrim.Destroy,taint,", LocalID); 142 DetailLog("{0},BSPrim.Destroy,taint,", LocalID);
144 // If there are physical body and shape, release my use of same. 143 // If there are physical body and shape, release my use of same.
145 PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null); 144 PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null);
145 PhysBody.Clear();
146 PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null); 146 PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null);
147 PhysShape.Clear();
147 }); 148 });
148 } 149 }
149 150
@@ -157,12 +158,10 @@ public sealed class BSPrim : BSPhysObject
157 // We presume the scale and size are the same. If scale must be changed for 158 // 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. 159 // the physical shape, that is done when the geometry is built.
159 _size = value; 160 _size = value;
161 Scale = _size;
160 ForceBodyShapeRebuild(false); 162 ForceBodyShapeRebuild(false);
161 } 163 }
162 } 164 }
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 165
167 public override PrimitiveBaseShape Shape { 166 public override PrimitiveBaseShape Shape {
168 set { 167 set {
@@ -189,13 +188,17 @@ public sealed class BSPrim : BSPhysObject
189 } 188 }
190 } 189 }
191 public override bool Selected { 190 public override bool Selected {
192 set { 191 set
193 _isSelected = value; 192 {
194 PhysicsScene.TaintedObject("BSPrim.setSelected", delegate() 193 if (value != _isSelected)
195 { 194 {
196 DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected); 195 _isSelected = value;
197 SetObjectDynamic(false); 196 PhysicsScene.TaintedObject("BSPrim.setSelected", delegate()
198 }); 197 {
198 DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected);
199 SetObjectDynamic(false);
200 });
201 }
199 } 202 }
200 } 203 }
201 public override void CrossingFailure() { return; } 204 public override void CrossingFailure() { return; }
@@ -244,7 +247,8 @@ public sealed class BSPrim : BSPhysObject
244 // Zero some other properties in the physics engine 247 // Zero some other properties in the physics engine
245 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() 248 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate()
246 { 249 {
247 BulletSimAPI.ClearAllForces2(PhysBody.ptr); 250 if (PhysBody.HasPhysicalBody)
251 BulletSimAPI.ClearAllForces2(PhysBody.ptr);
248 }); 252 });
249 } 253 }
250 public override void ZeroAngularMotion(bool inTaintTime) 254 public override void ZeroAngularMotion(bool inTaintTime)
@@ -253,8 +257,12 @@ public sealed class BSPrim : BSPhysObject
253 // Zero some other properties in the physics engine 257 // Zero some other properties in the physics engine
254 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() 258 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate()
255 { 259 {
256 BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); 260 // DetailLog("{0},BSPrim.ZeroAngularMotion,call,rotVel={1}", LocalID, _rotationalVelocity);
257 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); 261 if (PhysBody.HasPhysicalBody)
262 {
263 BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
264 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
265 }
258 }); 266 });
259 } 267 }
260 268
@@ -271,9 +279,12 @@ public sealed class BSPrim : BSPhysObject
271 } 279 }
272 public override OMV.Vector3 Position { 280 public override OMV.Vector3 Position {
273 get { 281 get {
282 /* NOTE: this refetch is not necessary. The simulator knows about linkset children
283 * 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 284 // child prims move around based on their parent. Need to get the latest location
275 if (!Linkset.IsRoot(this)) 285 if (!Linkset.IsRoot(this))
276 _position = Linkset.Position(this); 286 _position = Linkset.PositionGet(this);
287 */
277 288
278 // don't do the GetObjectPosition for root elements because this function is called a zillion times. 289 // 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); 290 // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr);
@@ -281,18 +292,22 @@ public sealed class BSPrim : BSPhysObject
281 } 292 }
282 set { 293 set {
283 // If the position must be forced into the physics engine, use ForcePosition. 294 // If the position must be forced into the physics engine, use ForcePosition.
295 // All positions are given in world positions.
284 if (_position == value) 296 if (_position == value)
285 { 297 {
298 DetailLog("{0},BSPrim.setPosition,taint,positionNotChanging,pos={1},orient={2}", LocalID, _position, _orientation);
286 return; 299 return;
287 } 300 }
288 _position = value; 301 _position = value;
289 // TODO: what does it mean to set the position of a child prim?? Rebuild the constraint?
290 PositionSanityCheck(false); 302 PositionSanityCheck(false);
303
304 // A linkset might need to know if a component information changed.
305 Linkset.UpdateProperties(this, false);
306
291 PhysicsScene.TaintedObject("BSPrim.setPosition", delegate() 307 PhysicsScene.TaintedObject("BSPrim.setPosition", delegate()
292 { 308 {
293 // DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); 309 DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
294 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 310 ForcePosition = _position;
295 ActivateIfPhysical(false);
296 }); 311 });
297 } 312 }
298 } 313 }
@@ -303,9 +318,11 @@ public sealed class BSPrim : BSPhysObject
303 } 318 }
304 set { 319 set {
305 _position = value; 320 _position = value;
306 // PositionSanityCheck(); // Don't do this! Causes a loop and caller should know better. 321 if (PhysBody.HasPhysicalBody)
307 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 322 {
308 ActivateIfPhysical(false); 323 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
324 ActivateIfPhysical(false);
325 }
309 } 326 }
310 } 327 }
311 328
@@ -316,39 +333,46 @@ public sealed class BSPrim : BSPhysObject
316 { 333 {
317 bool ret = false; 334 bool ret = false;
318 335
336 if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(_position))
337 {
338 // The physical object is out of the known/simulated area.
339 // Upper levels of code will handle the transition to other areas so, for
340 // the time, we just ignore the position.
341 return ret;
342 }
343
319 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); 344 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
320 OMV.Vector3 upForce = OMV.Vector3.Zero; 345 OMV.Vector3 upForce = OMV.Vector3.Zero;
321 if (Position.Z < terrainHeight) 346 if (RawPosition.Z < terrainHeight)
322 { 347 {
323 DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight); 348 DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight);
324 float targetHeight = terrainHeight + (Size.Z / 2f); 349 float targetHeight = terrainHeight + (Size.Z / 2f);
325 // Upforce proportional to the distance away from the terrain. Correct the error in 1 sec. 350 // Upforce proportional to the distance away from the terrain. Correct the error in 1 sec.
326 upForce.Z = (terrainHeight - Position.Z) * 1f; 351 upForce.Z = (terrainHeight - RawPosition.Z) * 1f;
327 ret = true; 352 ret = true;
328 } 353 }
329 354
330 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) 355 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
331 { 356 {
332 float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position); 357 float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position);
333 // TODO: a floating motor so object will bob in the water 358 // TODO: a floating motor so object will bob in the water
334 if (Math.Abs(Position.Z - waterHeight) > 0.1f) 359 if (Math.Abs(RawPosition.Z - waterHeight) > 0.1f)
335 { 360 {
336 // Upforce proportional to the distance away from the water. Correct the error in 1 sec. 361 // Upforce proportional to the distance away from the water. Correct the error in 1 sec.
337 upForce.Z = (waterHeight - Position.Z) * 1f; 362 upForce.Z = (waterHeight - RawPosition.Z) * 1f;
338 ret = true; 363 ret = true;
339 } 364 }
340 } 365 }
341 366
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. 367 // The above code computes a force to apply to correct any out-of-bounds problems. Apply same.
368 // TODO: This should be intergrated with a geneal physics action mechanism.
369 // TODO: This should be moderated with PID'ness.
345 if (ret) 370 if (ret)
346 { 371 {
347 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.PositionSanityCheck:belowTerrain", delegate() 372 // Apply upforce and overcome gravity.
348 { 373 OMV.Vector3 correctionForce = upForce - PhysicsScene.DefaultGravity;
349 // Apply upforce and overcome gravity. 374 DetailLog("{0},BSPrim.PositionSanityCheck,applyForce,pos={1},upForce={2},correctionForce={3}", LocalID, _position, upForce, correctionForce);
350 ForceVelocity = ForceVelocity + upForce - PhysicsScene.DefaultGravity; 375 AddForce(correctionForce, false, inTaintTime);
351 });
352 } 376 }
353 return ret; 377 return ret;
354 } 378 }
@@ -408,7 +432,8 @@ public sealed class BSPrim : BSPhysObject
408 PhysicsScene.TaintedObject("BSPrim.setForce", delegate() 432 PhysicsScene.TaintedObject("BSPrim.setForce", delegate()
409 { 433 {
410 // DetailLog("{0},BSPrim.setForce,taint,force={1}", LocalID, _force); 434 // DetailLog("{0},BSPrim.setForce,taint,force={1}", LocalID, _force);
411 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force); 435 if (PhysBody.HasPhysicalBody)
436 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
412 }); 437 });
413 } 438 }
414 } 439 }
@@ -502,7 +527,8 @@ public sealed class BSPrim : BSPhysObject
502 PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate() 527 PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate()
503 { 528 {
504 // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity); 529 // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity);
505 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity); 530 if (PhysBody.HasPhysicalBody)
531 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
506 }); 532 });
507 } 533 }
508 } 534 }
@@ -537,23 +563,32 @@ public sealed class BSPrim : BSPhysObject
537 } 563 }
538 public override OMV.Quaternion Orientation { 564 public override OMV.Quaternion Orientation {
539 get { 565 get {
566 /* NOTE: this refetch is not necessary. The simulator knows about linkset children
567 * 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. 568 // Children move around because tied to parent. Get a fresh value.
541 if (!Linkset.IsRoot(this)) 569 if (!Linkset.IsRoot(this))
542 { 570 {
543 _orientation = Linkset.Orientation(this); 571 _orientation = Linkset.OrientationGet(this);
544 } 572 }
573 */
545 return _orientation; 574 return _orientation;
546 } 575 }
547 set { 576 set {
548 if (_orientation == value) 577 if (_orientation == value)
549 return; 578 return;
550 _orientation = value; 579 _orientation = value;
551 // TODO: what does it mean if a child in a linkset changes its orientation? Rebuild the constraint? 580
581 // A linkset might need to know if a component information changed.
582 Linkset.UpdateProperties(this, false);
583
552 PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate() 584 PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate()
553 { 585 {
554 // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr); 586 if (PhysBody.HasPhysicalBody)
555 // DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation); 587 {
556 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 588 // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr);
589 // DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation);
590 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
591 }
557 }); 592 });
558 } 593 }
559 } 594 }
@@ -644,10 +679,7 @@ public sealed class BSPrim : BSPhysObject
644 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr); 679 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr);
645 680
646 // Collision filter can be set only when the object is in the world 681 // Collision filter can be set only when the object is in the world
647 if (PhysBody.collisionFilter != 0 || PhysBody.collisionMask != 0) 682 PhysBody.ApplyCollisionMask();
648 {
649 BulletSimAPI.SetCollisionFilterMask2(PhysBody.ptr, (uint)PhysBody.collisionFilter, (uint)PhysBody.collisionMask);
650 }
651 683
652 // Recompute any linkset parameters. 684 // Recompute any linkset parameters.
653 // When going from non-physical to physical, this re-enables the constraints that 685 // When going from non-physical to physical, this re-enables the constraints that
@@ -672,8 +704,12 @@ public sealed class BSPrim : BSPhysObject
672 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT); 704 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
673 // Stop all movement 705 // Stop all movement
674 ZeroMotion(true); 706 ZeroMotion(true);
675 // Center of mass is at the center of the object 707
676 // DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.PhysBody.ptr, _position, _orientation); 708 // Set various physical properties so other object interact properly
709 MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, false);
710 BulletSimAPI.SetFriction2(PhysBody.ptr, matAttrib.friction);
711 BulletSimAPI.SetRestitution2(PhysBody.ptr, matAttrib.restitution);
712
677 // Mass is zero which disables a bunch of physics stuff in Bullet 713 // Mass is zero which disables a bunch of physics stuff in Bullet
678 UpdatePhysicalMassProperties(0f); 714 UpdatePhysicalMassProperties(0f);
679 // Set collision detection parameters 715 // Set collision detection parameters
@@ -682,24 +718,27 @@ public sealed class BSPrim : BSPhysObject
682 BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, PhysicsScene.Params.ccdMotionThreshold); 718 BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, PhysicsScene.Params.ccdMotionThreshold);
683 BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius); 719 BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius);
684 } 720 }
685 // There can be special things needed for implementing linksets 721
686 Linkset.MakeStatic(this);
687 // The activation state is 'disabled' so Bullet will not try to act on it. 722 // The activation state is 'disabled' so Bullet will not try to act on it.
688 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_SIMULATION); 723 // BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_SIMULATION);
689 // Start it out sleeping and physical actions could wake it up. 724 // Start it out sleeping and physical actions could wake it up.
690 // BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ISLAND_SLEEPING); 725 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ISLAND_SLEEPING);
726
727 // This collides like a static object
728 PhysBody.collisionType = CollisionType.Static;
691 729
692 PhysBody.collisionFilter = CollisionFilterGroups.StaticObjectFilter; 730 // There can be special things needed for implementing linksets
693 PhysBody.collisionMask = CollisionFilterGroups.StaticObjectMask; 731 Linkset.MakeStatic(this);
694 } 732 }
695 else 733 else
696 { 734 {
697 // Not a Bullet static object 735 // Not a Bullet static object
698 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT); 736 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
699 737
700 // Set various physical properties so internal dynamic properties will get computed correctly as they are set 738 // Set various physical properties so other object interact properly
701 BulletSimAPI.SetFriction2(PhysBody.ptr, PhysicsScene.Params.defaultFriction); 739 MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, true);
702 BulletSimAPI.SetRestitution2(PhysBody.ptr, PhysicsScene.Params.defaultRestitution); 740 BulletSimAPI.SetFriction2(PhysBody.ptr, matAttrib.friction);
741 BulletSimAPI.SetRestitution2(PhysBody.ptr, matAttrib.restitution);
703 742
704 // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382 743 // 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 744 // Since this can be called multiple times, only zero forces when becoming physical
@@ -727,16 +766,15 @@ public sealed class BSPrim : BSPhysObject
727 BulletSimAPI.SetSleepingThresholds2(PhysBody.ptr, PhysicsScene.Params.linearSleepingThreshold, PhysicsScene.Params.angularSleepingThreshold); 766 BulletSimAPI.SetSleepingThresholds2(PhysBody.ptr, PhysicsScene.Params.linearSleepingThreshold, PhysicsScene.Params.angularSleepingThreshold);
728 BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, PhysicsScene.Params.contactProcessingThreshold); 767 BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, PhysicsScene.Params.contactProcessingThreshold);
729 768
730 // There might be special things needed for implementing linksets. 769 // This collides like an object.
731 Linkset.MakeDynamic(this); 770 PhysBody.collisionType = CollisionType.Dynamic;
732 771
733 // Force activation of the object so Bullet will act on it. 772 // Force activation of the object so Bullet will act on it.
734 // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects. 773 // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects.
735 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ACTIVE_TAG); 774 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ACTIVE_TAG);
736 // BulletSimAPI.Activate2(BSBody.ptr, true);
737 775
738 PhysBody.collisionFilter = CollisionFilterGroups.ObjectFilter; 776 // There might be special things needed for implementing linksets.
739 PhysBody.collisionMask = CollisionFilterGroups.ObjectMask; 777 Linkset.MakeDynamic(this);
740 } 778 }
741 } 779 }
742 780
@@ -763,8 +801,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); 801 m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType);
764 } 802 }
765 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); 803 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
766 PhysBody.collisionFilter = CollisionFilterGroups.VolumeDetectFilter; 804
767 PhysBody.collisionMask = CollisionFilterGroups.VolumeDetectMask; 805 // Change collision info from a static object to a ghosty collision object
806 PhysBody.collisionType = CollisionType.VolumeDetect;
768 } 807 }
769 } 808 }
770 809
@@ -839,15 +878,6 @@ public sealed class BSPrim : BSPhysObject
839 } 878 }
840 public override OMV.Vector3 RotationalVelocity { 879 public override OMV.Vector3 RotationalVelocity {
841 get { 880 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; 881 return _rotationalVelocity;
852 } 882 }
853 set { 883 set {
@@ -856,7 +886,8 @@ public sealed class BSPrim : BSPhysObject
856 PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate() 886 PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate()
857 { 887 {
858 DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity); 888 DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity);
859 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity); 889 if (PhysBody.HasPhysicalBody)
890 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
860 }); 891 });
861 } 892 }
862 } 893 }
@@ -891,8 +922,11 @@ public sealed class BSPrim : BSPhysObject
891 _buoyancy = value; 922 _buoyancy = value;
892 // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); 923 // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
893 // Buoyancy is faked by changing the gravity applied to the object 924 // Buoyancy is faked by changing the gravity applied to the object
894 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy); 925 if (PhysBody.HasPhysicalBody)
895 BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav)); 926 {
927 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
928 BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav));
929 }
896 } 930 }
897 } 931 }
898 932
@@ -960,7 +994,8 @@ public sealed class BSPrim : BSPhysObject
960 } 994 }
961 DetailLog("{0},BSPrim.AddForce,taint,force={1}", LocalID, fSum); 995 DetailLog("{0},BSPrim.AddForce,taint,force={1}", LocalID, fSum);
962 if (fSum != OMV.Vector3.Zero) 996 if (fSum != OMV.Vector3.Zero)
963 BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, fSum); 997 if (PhysBody.HasPhysicalBody)
998 BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, fSum);
964 }); 999 });
965 } 1000 }
966 1001
@@ -971,7 +1006,8 @@ public sealed class BSPrim : BSPhysObject
971 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyForceImpulse", delegate() 1006 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyForceImpulse", delegate()
972 { 1007 {
973 DetailLog("{0},BSPrim.ApplyForceImpulse,taint,tImpulse={1}", LocalID, applyImpulse); 1008 DetailLog("{0},BSPrim.ApplyForceImpulse,taint,tImpulse={1}", LocalID, applyImpulse);
974 BulletSimAPI.ApplyCentralImpulse2(PhysBody.ptr, applyImpulse); 1009 if (PhysBody.HasPhysicalBody)
1010 BulletSimAPI.ApplyCentralImpulse2(PhysBody.ptr, applyImpulse);
975 }); 1011 });
976 } 1012 }
977 1013
@@ -1007,18 +1043,23 @@ public sealed class BSPrim : BSPhysObject
1007 DetailLog("{0},BSPrim.AddAngularForce,taint,aForce={1}", LocalID, fSum); 1043 DetailLog("{0},BSPrim.AddAngularForce,taint,aForce={1}", LocalID, fSum);
1008 if (fSum != OMV.Vector3.Zero) 1044 if (fSum != OMV.Vector3.Zero)
1009 { 1045 {
1010 BulletSimAPI.ApplyTorque2(PhysBody.ptr, fSum); 1046 if (PhysBody.HasPhysicalBody)
1047 BulletSimAPI.ApplyTorque2(PhysBody.ptr, fSum);
1011 _torque = fSum; 1048 _torque = fSum;
1012 } 1049 }
1013 }); 1050 });
1014 } 1051 }
1015 // A torque impulse. 1052 // A torque impulse.
1053 // ApplyTorqueImpulse adds torque directly to the angularVelocity.
1054 // AddAngularForce accumulates the force and applied it to the angular velocity all at once.
1055 // Computed as: angularVelocity += impulse * inertia;
1016 public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime) 1056 public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime)
1017 { 1057 {
1018 OMV.Vector3 applyImpulse = impulse; 1058 OMV.Vector3 applyImpulse = impulse;
1019 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate() 1059 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate()
1020 { 1060 {
1021 BulletSimAPI.ApplyTorqueImpulse2(PhysBody.ptr, applyImpulse); 1061 if (PhysBody.HasPhysicalBody)
1062 BulletSimAPI.ApplyTorqueImpulse2(PhysBody.ptr, applyImpulse);
1022 }); 1063 });
1023 } 1064 }
1024 1065
@@ -1326,7 +1367,7 @@ public sealed class BSPrim : BSPhysObject
1326 // Rebuild the geometry and object. 1367 // Rebuild the geometry and object.
1327 // This is called when the shape changes so we need to recreate the mesh/hull. 1368 // This is called when the shape changes so we need to recreate the mesh/hull.
1328 // Called at taint-time!!! 1369 // Called at taint-time!!!
1329 private void CreateGeomAndObject(bool forceRebuild) 1370 public void CreateGeomAndObject(bool forceRebuild)
1330 { 1371 {
1331 // If this prim is part of a linkset, we must remove and restore the physical 1372 // If this prim is part of a linkset, we must remove and restore the physical
1332 // links if the body is rebuilt. 1373 // links if the body is rebuilt.
@@ -1336,12 +1377,11 @@ public sealed class BSPrim : BSPhysObject
1336 // Create the correct physical representation for this type of object. 1377 // Create the correct physical representation for this type of object.
1337 // Updates PhysBody and PhysShape with the new information. 1378 // Updates PhysBody and PhysShape with the new information.
1338 // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary. 1379 // 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) 1380 PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, null, delegate(BulletBody dBody)
1341 { 1381 {
1342 // Called if the current prim body is about to be destroyed. 1382 // Called if the current prim body is about to be destroyed.
1343 // Remove all the physical dependencies on the old body. 1383 // Remove all the physical dependencies on the old body.
1344 // (Maybe someday make the changing of BSShape an event handled by BSLinkset.) 1384 // (Maybe someday make the changing of BSShape an event to be subscribed to by BSLinkset, ...)
1345 needToRestoreLinkset = Linkset.RemoveBodyDependencies(this); 1385 needToRestoreLinkset = Linkset.RemoveBodyDependencies(this);
1346 needToRestoreVehicle = _vehicle.RemoveBodyDependencies(this); 1386 needToRestoreVehicle = _vehicle.RemoveBodyDependencies(this);
1347 }); 1387 });
@@ -1381,54 +1421,16 @@ public sealed class BSPrim : BSPhysObject
1381 1421
1382 public override void UpdateProperties(EntityProperties entprop) 1422 public override void UpdateProperties(EntityProperties entprop)
1383 { 1423 {
1384 /* 1424 // Updates only for individual prims and for the root object of a linkset.
1385 UpdatedProperties changed = 0; 1425 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 { 1426 {
1419 // Only update the position of single objects and linkset roots 1427 // A temporary kludge to suppress the rotational effects introduced on vehicles by Bullet
1420 if (Linkset.IsRoot(this)) 1428 // TODO: handle physics introduced by Bullet with computed vehicle physics.
1429 if (_vehicle.IsActive)
1421 { 1430 {
1422 base.RequestPhysicsterseUpdate(); 1431 entprop.RotationalVelocity = OMV.Vector3.Zero;
1423 } 1432 }
1424 }
1425 */
1426
1427 // Don't check for damping here -- it's done in BulletSim and SceneObjectPart.
1428 1433
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 1434 // Assign directly to the local variables so the normal set action does not happen
1433 _position = entprop.Position; 1435 _position = entprop.Position;
1434 _orientation = entprop.Rotation; 1436 _orientation = entprop.Rotation;
@@ -1437,21 +1439,19 @@ public sealed class BSPrim : BSPhysObject
1437 _rotationalVelocity = entprop.RotationalVelocity; 1439 _rotationalVelocity = entprop.RotationalVelocity;
1438 1440
1439 // The sanity check can change the velocity and/or position. 1441 // The sanity check can change the velocity and/or position.
1440 if (PositionSanityCheck(true)) 1442 if (IsPhysical && PositionSanityCheck(true))
1441 { 1443 {
1442 entprop.Position = _position; 1444 entprop.Position = _position;
1443 entprop.Velocity = _velocity; 1445 entprop.Velocity = _velocity;
1444 } 1446 }
1445 1447
1446 // remember the current and last set values 1448 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}", 1449 DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}",
1452 LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity); 1450 LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity);
1453 1451
1454 // BulletSimAPI.DumpRigidBody2(PhysicsScene.World.ptr, BSBody.ptr); // DEBUG DEBUG DEBUG 1452 // remember the current and last set values
1453 LastEntityProperties = CurrentEntityProperties;
1454 CurrentEntityProperties = entprop;
1455 1455
1456 base.RequestPhysicsterseUpdate(); 1456 base.RequestPhysicsterseUpdate();
1457 } 1457 }
@@ -1466,7 +1466,7 @@ public sealed class BSPrim : BSPhysObject
1466 */ 1466 */
1467 1467
1468 // The linkset implimentation might want to know about this. 1468 // The linkset implimentation might want to know about this.
1469 Linkset.UpdateProperties(this); 1469 Linkset.UpdateProperties(this, true);
1470 } 1470 }
1471} 1471}
1472} 1472}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
index 27a78d1..069cb0d 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,9 @@ 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 public delegate void PreStepAction(float timeStep);
100 public event PreStepAction BeforeStep;
101
112 // A value of the time now so all the collision and update routines do not have to get their own 102 // 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 103 // Set to 'now' just before all the prims and actors are called for collisions and updates
114 public int SimulationNowTime { get; private set; } 104 public int SimulationNowTime { get; private set; }
@@ -140,7 +130,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
140 public const uint GROUNDPLANE_ID = 1; 130 public const uint GROUNDPLANE_ID = 1;
141 public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here 131 public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here
142 132
143 private float m_waterLevel; 133 public float SimpleWaterLevel { get; set; }
144 public BSTerrainManager TerrainManager { get; private set; } 134 public BSTerrainManager TerrainManager { get; private set; }
145 135
146 public ConfigurationParameters Params 136 public ConfigurationParameters Params
@@ -195,8 +185,10 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
195 private string m_physicsLoggingDir; 185 private string m_physicsLoggingDir;
196 private string m_physicsLoggingPrefix; 186 private string m_physicsLoggingPrefix;
197 private int m_physicsLoggingFileMinutes; 187 private int m_physicsLoggingFileMinutes;
188 private bool m_physicsLoggingDoFlush;
198 // 'true' of the vehicle code is to log lots of details 189 // 'true' of the vehicle code is to log lots of details
199 public bool VehicleLoggingEnabled { get; private set; } 190 public bool VehicleLoggingEnabled { get; private set; }
191 public bool VehiclePhysicalLoggingEnabled { get; private set; }
200 192
201 #region Construction and Initialization 193 #region Construction and Initialization
202 public BSScene(string identifier) 194 public BSScene(string identifier)
@@ -234,6 +226,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
234 if (m_physicsLoggingEnabled) 226 if (m_physicsLoggingEnabled)
235 { 227 {
236 PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes); 228 PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes);
229 PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output error messages.
237 } 230 }
238 else 231 else
239 { 232 {
@@ -302,12 +295,22 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
302 m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", "."); 295 m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", ".");
303 m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-"); 296 m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-");
304 m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5); 297 m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5);
298 m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false);
305 // Very detailed logging for vehicle debugging 299 // Very detailed logging for vehicle debugging
306 VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false); 300 VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
301 VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false);
307 302
308 // Do any replacements in the parameters 303 // Do any replacements in the parameters
309 m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName); 304 m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
310 } 305 }
306
307 // The material characteristics.
308 BSMaterials.InitializeFromDefaults(Params);
309 if (pConfig != null)
310 {
311 // Let the user add new and interesting material property values.
312 BSMaterials.InitializefromParameters(pConfig);
313 }
311 } 314 }
312 } 315 }
313 316
@@ -345,8 +348,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
345 // make sure no stepping happens while we're deleting stuff 348 // make sure no stepping happens while we're deleting stuff
346 m_initialized = false; 349 m_initialized = false;
347 350
348 TerrainManager.ReleaseGroundPlaneAndTerrain();
349
350 foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects) 351 foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
351 { 352 {
352 kvp.Value.Destroy(); 353 kvp.Value.Destroy();
@@ -366,6 +367,13 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
366 Shapes = null; 367 Shapes = null;
367 } 368 }
368 369
370 if (TerrainManager != null)
371 {
372 TerrainManager.ReleaseGroundPlaneAndTerrain();
373 TerrainManager.Dispose();
374 TerrainManager = null;
375 }
376
369 // Anything left in the unmanaged code should be cleaned out 377 // Anything left in the unmanaged code should be cleaned out
370 BulletSimAPI.Shutdown2(World.ptr); 378 BulletSimAPI.Shutdown2(World.ptr);
371 379
@@ -490,8 +498,10 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
490 ProcessTaints(); 498 ProcessTaints();
491 499
492 // Some of the prims operate with special vehicle properties 500 // Some of the prims operate with special vehicle properties
493 ProcessVehicles(timeStep); 501 DoPreStepActions(timeStep);
494 ProcessTaints(); // the vehicles might have added taints 502
503 // the prestep actions might have added taints
504 ProcessTaints();
495 505
496 // step the physical world one interval 506 // step the physical world one interval
497 m_simulationStep++; 507 m_simulationStep++;
@@ -499,16 +509,17 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
499 509
500 try 510 try
501 { 511 {
502 if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG 512 if (VehiclePhysicalLoggingEnabled) DumpVehicles(); // DEBUG
503 if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount(); 513 if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount();
504 514
505 numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep, 515 numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep,
506 out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr); 516 out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr);
507 517
508 if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime); 518 if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime);
509 DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}", 519 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); 520 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps,
511 if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG 521 updatedEntityCount, collidersCount, ObjectsWithCollisions.Count);
522 if (VehiclePhysicalLoggingEnabled) DumpVehicles(); // DEBUG
512 } 523 }
513 catch (Exception e) 524 catch (Exception e)
514 { 525 {
@@ -520,9 +531,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
520 collidersCount = 0; 531 collidersCount = 0;
521 } 532 }
522 533
523 // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in 534 // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in.
524 535
525 // Get a value for 'now' so all the collision and update routines don't have to get their own 536 // Get a value for 'now' so all the collision and update routines don't have to get their own.
526 SimulationNowTime = Util.EnvironmentTickCount(); 537 SimulationNowTime = Util.EnvironmentTickCount();
527 538
528 // If there were collisions, process them by sending the event to the prim. 539 // If there were collisions, process them by sending the event to the prim.
@@ -562,12 +573,16 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
562 573
563 // Objects that are done colliding are removed from the ObjectsWithCollisions list. 574 // Objects that are done colliding are removed from the ObjectsWithCollisions list.
564 // Not done above because it is inside an iteration of ObjectWithCollisions. 575 // Not done above because it is inside an iteration of ObjectWithCollisions.
576 // This complex collision processing is required to create an empty collision
577 // event call after all collisions have happened on an object. This enables
578 // the simulator to generate the 'collision end' event.
565 if (ObjectsWithNoMoreCollisions.Count > 0) 579 if (ObjectsWithNoMoreCollisions.Count > 0)
566 { 580 {
567 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions) 581 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
568 ObjectsWithCollisions.Remove(po); 582 ObjectsWithCollisions.Remove(po);
569 ObjectsWithNoMoreCollisions.Clear(); 583 ObjectsWithNoMoreCollisions.Clear();
570 } 584 }
585 // Done with collisions.
571 586
572 // If any of the objects had updated properties, tell the object it has been changed by the physics engine 587 // If any of the objects had updated properties, tell the object it has been changed by the physics engine
573 if (updatedEntityCount > 0) 588 if (updatedEntityCount > 0)
@@ -585,15 +600,14 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
585 600
586 ProcessPostStepTaints(); 601 ProcessPostStepTaints();
587 602
588 // This causes the unmanaged code to output ALL the values found in ALL the objects in the world. 603 // 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. 604 // Only enable this in a limited test world with few objects.
590 // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG 605 // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG
591 606
592 // The physics engine returns the number of milliseconds it simulated this call. 607 // 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. 608 // 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). 609 // Multiply by 55 to give a nominal frame rate of 55.
595 return numSubSteps * m_fixedTimeStep * 1000 * 55; 610 return (float)numSubSteps * m_fixedTimeStep * 1000f * 55f;
596 // return timeStep * 1000 * 55;
597 } 611 }
598 612
599 // Something has collided 613 // Something has collided
@@ -639,12 +653,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
639 653
640 public override void SetWaterLevel(float baseheight) 654 public override void SetWaterLevel(float baseheight)
641 { 655 {
642 m_waterLevel = baseheight; 656 SimpleWaterLevel = baseheight;
643 }
644 // Someday....
645 public float GetWaterLevelAtXYZ(Vector3 loc)
646 {
647 return m_waterLevel;
648 } 657 }
649 658
650 public override void DeleteTerrain() 659 public override void DeleteTerrain()
@@ -915,6 +924,16 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
915 } 924 }
916 } 925 }
917 926
927 private void DoPreStepActions(float timeStep)
928 {
929 ProcessVehicles(timeStep);
930
931 PreStepAction actions = BeforeStep;
932 if (actions != null)
933 actions(timeStep);
934
935 }
936
918 // Some prims have extra vehicle actions 937 // Some prims have extra vehicle actions
919 // Called at taint time! 938 // Called at taint time!
920 private void ProcessVehicles(float timeStep) 939 private void ProcessVehicles(float timeStep)
@@ -979,6 +998,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
979 // Should handle fetching the right type from the ini file and converting it. 998 // Should handle fetching the right type from the ini file and converting it.
980 // -- a delegate for getting the value as a float 999 // -- a delegate for getting the value as a float
981 // -- a delegate for setting the value from a float 1000 // -- a delegate for setting the value from a float
1001 // -- an optional delegate to update the value in the world. Most often used to
1002 // push the new value to an in-world object.
982 // 1003 //
983 // The single letter parameters for the delegates are: 1004 // The single letter parameters for the delegates are:
984 // s = BSScene 1005 // s = BSScene
@@ -1069,7 +1090,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1069 (s,p,l,v) => { s.PID_P = v; } ), 1090 (s,p,l,v) => { s.PID_P = v; } ),
1070 1091
1071 new ParameterDefn("DefaultFriction", "Friction factor used on new objects", 1092 new ParameterDefn("DefaultFriction", "Friction factor used on new objects",
1072 0.5f, 1093 0.2f,
1073 (s,cf,p,v) => { s.m_params[0].defaultFriction = cf.GetFloat(p, v); }, 1094 (s,cf,p,v) => { s.m_params[0].defaultFriction = cf.GetFloat(p, v); },
1074 (s) => { return s.m_params[0].defaultFriction; }, 1095 (s) => { return s.m_params[0].defaultFriction; },
1075 (s,p,l,v) => { s.m_params[0].defaultFriction = v; } ), 1096 (s,p,l,v) => { s.m_params[0].defaultFriction = v; } ),
@@ -1084,7 +1105,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1084 (s) => { return s.m_params[0].defaultRestitution; }, 1105 (s) => { return s.m_params[0].defaultRestitution; },
1085 (s,p,l,v) => { s.m_params[0].defaultRestitution = v; } ), 1106 (s,p,l,v) => { s.m_params[0].defaultRestitution = v; } ),
1086 new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)", 1107 new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)",
1087 0f, 1108 0.04f,
1088 (s,cf,p,v) => { s.m_params[0].collisionMargin = cf.GetFloat(p, v); }, 1109 (s,cf,p,v) => { s.m_params[0].collisionMargin = cf.GetFloat(p, v); },
1089 (s) => { return s.m_params[0].collisionMargin; }, 1110 (s) => { return s.m_params[0].collisionMargin; },
1090 (s,p,l,v) => { s.m_params[0].collisionMargin = v; } ), 1111 (s,p,l,v) => { s.m_params[0].collisionMargin = v; } ),
@@ -1151,7 +1172,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1151 (s) => { return s.m_params[0].terrainImplementation; }, 1172 (s) => { return s.m_params[0].terrainImplementation; },
1152 (s,p,l,v) => { s.m_params[0].terrainImplementation = v; } ), 1173 (s,p,l,v) => { s.m_params[0].terrainImplementation = v; } ),
1153 new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , 1174 new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" ,
1154 0.5f, 1175 0.3f,
1155 (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); }, 1176 (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); },
1156 (s) => { return s.m_params[0].terrainFriction; }, 1177 (s) => { return s.m_params[0].terrainFriction; },
1157 (s,p,l,v) => { s.m_params[0].terrainFriction = v; /* TODO: set on real terrain */} ), 1178 (s,p,l,v) => { s.m_params[0].terrainFriction = v; /* TODO: set on real terrain */} ),
@@ -1165,13 +1186,19 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1165 (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); }, 1186 (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); },
1166 (s) => { return s.m_params[0].terrainRestitution; }, 1187 (s) => { return s.m_params[0].terrainRestitution; },
1167 (s,p,l,v) => { s.m_params[0].terrainRestitution = v; /* TODO: set on real terrain */ } ), 1188 (s,p,l,v) => { s.m_params[0].terrainRestitution = v; /* TODO: set on real terrain */ } ),
1189 new ParameterDefn("TerrainCollisionMargin", "Margin where collision checking starts" ,
1190 0.04f,
1191 (s,cf,p,v) => { s.m_params[0].terrainCollisionMargin = cf.GetFloat(p, v); },
1192 (s) => { return s.m_params[0].terrainCollisionMargin; },
1193 (s,p,l,v) => { s.m_params[0].terrainCollisionMargin = v; /* TODO: set on real terrain */ } ),
1194
1168 new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.", 1195 new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.",
1169 0.2f, 1196 0.2f,
1170 (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); }, 1197 (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); },
1171 (s) => { return s.m_params[0].avatarFriction; }, 1198 (s) => { return s.m_params[0].avatarFriction; },
1172 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ), 1199 (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.", 1200 new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.",
1174 10f, 1201 10.0f,
1175 (s,cf,p,v) => { s.m_params[0].avatarStandingFriction = cf.GetFloat(p, v); }, 1202 (s,cf,p,v) => { s.m_params[0].avatarStandingFriction = cf.GetFloat(p, v); },
1176 (s) => { return s.m_params[0].avatarStandingFriction; }, 1203 (s) => { return s.m_params[0].avatarStandingFriction; },
1177 (s,p,l,v) => { s.m_params[0].avatarStandingFriction = v; } ), 1204 (s,p,l,v) => { s.m_params[0].avatarStandingFriction = v; } ),
@@ -1206,6 +1233,11 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1206 (s) => { return s.m_params[0].avatarContactProcessingThreshold; }, 1233 (s) => { return s.m_params[0].avatarContactProcessingThreshold; },
1207 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ), 1234 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ),
1208 1235
1236 new ParameterDefn("VehicleAngularDamping", "Factor to damp vehicle angular movement per second (0.0 - 1.0)",
1237 0.95f,
1238 (s,cf,p,v) => { s.m_params[0].vehicleAngularDamping = cf.GetFloat(p, v); },
1239 (s) => { return s.m_params[0].vehicleAngularDamping; },
1240 (s,p,l,v) => { s.m_params[0].vehicleAngularDamping = v; } ),
1209 1241
1210 new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)", 1242 new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)",
1211 0f, 1243 0f,
@@ -1487,7 +1519,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1487 { 1519 {
1488 PhysicsLogging.Write(msg, args); 1520 PhysicsLogging.Write(msg, args);
1489 // Add the Flush() if debugging crashes. Gets all the messages written out. 1521 // Add the Flush() if debugging crashes. Gets all the messages written out.
1490 // PhysicsLogging.Flush(); 1522 if (m_physicsLoggingDoFlush) PhysicsLogging.Flush();
1491 } 1523 }
1492 // Used to fill in the LocalID when there isn't one. It's the correct number of characters. 1524 // Used to fill in the LocalID when there isn't one. It's the correct number of characters.
1493 public const string DetailLogZero = "0000000000"; 1525 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..0d9a156
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt
@@ -0,0 +1,204 @@
1CURRENT PRIORITIES
2=================================================
3Eliminate all crashes (DONEish)
4 Editing/deleting physical linkset (DONE)
5 Border crossing of physical linkset (DONE)
6Enable vehicle border crossings (at least as poorly as ODE)
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)
9Calibrate turning radius
10limitMotorUp calibration (more down?)
11study PID motors (include 'efficiency' implementation
12 Add to avatar movement
13
14CRASHES
15=================================================
1620121129.1411: editting/moving phys object across region boundries causes crash
17 getPos-> btRigidBody::upcast -> getBodyType -> BOOM
1820121128.1600: mesh object not rezzing (no physics mesh).
19 Causes many errors. Doesn't stop after first error with box shape.
20 Eventually crashes when deleting the object.
2120121206.1434: rez Sam-pan into OSGrid BulletSim11 region
22 Immediate simulator crash. Mono does not output any stacktrace and
23 log just stops after reporting taint-time linking of the linkset.
24
25VEHICLES TODO LIST:
26=================================================
27Border crossing with linked vehicle causes crash
28Neb vehicle taking > 25ms of physics time!!
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.
37Light cycle falling over when driving
38Implement referenceFrame for all the motion routines.
39Angular motion around Z moves the vehicle in world Z and not vehicle Z in ODE.
40 Verify that angular motion specified around Z moves in the vehicle coordinates.
41Verify llGetVel() is returning a smooth and good value for vehicle movement.
42llGetVel() should return the root's velocity if requested in a child prim.
43Implement function efficiency for lineaar and angular motion.
44Should vehicle angular/linear movement friction happen after all the components
45 or does it only apply to the basic movement?
46After getting off a vehicle, the root prim is phantom (can be walked through)
47 Need to force a position update for the root prim after compound shape destruction
48Linkset explosion after three "rides" on Nebadon lite vehicle (LinksetConstraint)
49For limitMotorUp, use raycast down to find if vehicle is in the air.
50Remove vehicle angular velocity zeroing in BSPrim.UpdateProperties().
51 A kludge that isn't fixing the real problem of Bullet adding extra motion.
52
53BULLETSIM TODO LIST:
54=================================================
55Revisit CollisionMargin. Builders notice the 0.04 spacing between prims.
56Avatar height off after unsitting (floats off ground)
57 Editting appearance then moving restores.
58 Must not be initializing height when recreating capsule after unsit.
59Duplicating a physical prim causes old prim to jump away
60 Dup a phys prim and the original become unselected and thus interacts w/ selected prim.
61Scenes with hundred of thousands of static objects take a lot of physics CPU time.
62BSPrim.Force should set a continious force on the prim. The force should be
63 applied each tick. Some limits?
64Gun sending shooter flying.
65Collision margin (gap between physical objects lying on each other)
66Boundry checking (crashes related to crossing boundry)
67 Add check for border edge position for avatars and objects.
68 Verify the events are created for border crossings.
69Avatar rotation (check out changes to ScenePresence for physical rotation)
70Avatar running (what does phys engine need to do?)
71Small physical objects do not interact correctly
72 Create chain of .5x.5x.1 torui and make all but top physical so to hang.
73 The chain will fall apart and pairs will dance around on ground
74 Chains of 1x1x.2 will stay connected but will dance.
75 Chains above 2x2x.4 are move stable and get stablier as torui get larger.
76Add PID motor for avatar movement (slow to stop, ...)
77setForce should set a constant force. Different than AddImpulse.
78Implement raycast.
79Implement ShapeCollection.Dispose()
80Implement water as a plain so raycasting and collisions can happen with same.
81Add osGetPhysicsEngineName() so scripters can tell whether BulletSim or ODE
82 Also osGetPhysicsEngineVerion() maybe.
83Linkset.Position and Linkset.Orientation requre rewrite to properly return
84 child position. LinksetConstraint acts like it's at taint time!!
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
103Eliminate collisions between objects in a linkset. (LinksetConstraint)
104 Have UserPointer point to struct with localID and linksetID?
105 Objects in original linkset still collide with each other?
106
107MORE
108======================================================
109Find/remove avatar collision with ID=0.
110Test avatar walking up stairs. How does compare with SL.
111 Radius of the capsule affects ability to climb edges.
112Tune terrain/object friction to be closer to SL.
113Debounce avatar contact so legs don't keep folding up when standing.
114Implement LSL physics controls. Like STATUS_ROTATE_X.
115Add border extensions to terrain to help region crossings and objects leaving region.
116
117Performance test with lots of avatars. Can BulletSim support a thousand?
118Optimize collisions in C++: only send up to the object subscribed to collisions.
119 Use collision subscription and remove the collsion(A,B) and collision(B,A)
120Check whether SimMotionState needs large if statement (see TODO).
121
122Implement 'top colliders' info.
123Avatar jump
124Performance measurement and changes to make quicker.
125Implement detailed physics stats (GetStats()).
126
127Measure performance improvement from hulls
128Test not using ghost objects for volume detect implementation.
129Performance of closures and delegates for taint processing
130 Are there faster ways?
131 Is any slowdown introduced by the existing implementation significant?
132Is there are more efficient method of implementing pre and post step actions?
133 See http://www.codeproject.com/Articles/29922/Weak-Events-in-C
134
135Physics Arena central pyramid: why is one side permiable?
136
137INTERNAL IMPROVEMENT/CLEANUP
138=================================================
139Consider moving prim/character body and shape destruction in destroy()
140 to postTimeTime rather than protecting all the potential sets that
141 might have been queued up.
142Remove unused fields from ShapeData (not used in API2)
143Breakout code for mesh/hull/compound/native into separate BSShape* classes
144 Standardize access to building and reference code.
145 The skeleton classes are in the sources but are not complete or linked in.
146Make BSBody and BSShape real classes to centralize creation/changin/destruction
147 Convert state and parameter calls from BulletSimAPI direct calls to
148 calls on BSBody and BSShape
149Generalize Dynamics and PID with standardized motors.
150Generalize Linkset and vehicles into PropertyManagers
151 Methods for Refresh, RemoveBodyDependencies, RestoreBodyDependencies
152 Potentially add events for shape destruction, etc.
153Complete implemention of preStepActions
154 Replace vehicle step call with prestep event.
155 Is there a need for postStepActions? postStepTaints?
156Implement linkset by setting position of children when root updated. (LinksetManual)
157 Linkset implementation using manual prim movement.
158LinkablePrim class? Would that simplify/centralize the linkset logic?
159BSScene.UpdateParameterSet() is broken. How to set params on objects?
160Remove HeightmapInfo from terrain specification
161 Since C++ code does not need terrain height, this structure et al are not needed.
162Add floating motor for BS_FLOATS_ON_WATER so prim and avatar will
163 bob at the water level. BSPrim.PositionSanityCheck().
164Should taints check for existance or activeness of target?
165 When destroying linksets/etc, taints can be generated for objects that are
166 actually gone when the taint happens. Crashes don't happen because the taint closure
167 keeps the object from being freed, but that is just an accident.
168 Possibly have and 'active' flag that is checked by the taint processor?
169
170THREADING
171=================================================
172Do taint action immediately if not actually executing Bullet.
173 Add lock around Bullet execution and just do taint actions if simulation is not happening.
174
175DONE DONE DONE DONE
176=================================================
177Cleanup code in BSDynamics by using motors. (Resolution: started)
178Consider implementing terrain with a mesh rather than heightmap. (Resolution: done)
179 Would have better and adjustable resolution.
180Build terrain mesh so heighmap is height of the center of the square meter.
181 Resolution: NOT DONE: SL and ODE define meter square as being at one corner with one diagional.
182Terrain as mesh. (Resolution: done)
183How are static linksets seen by the physics engine?
184 Resolution: they are not linked in physics. When moved, all the children are repositioned.
185Convert BSCharacter to use all API2 (Resolution: done)
186Avatar pushing difficult (too heavy?)
187Use asset service passed to BulletSim to get sculptie bodies, etc. (Resolution: done)
188Remove old code in DLL (all non-API2 stuff). (Resolution: done)
189Measurements of mega-physical prim performance (with graph) (Resolution: done, email)
190Debug Bullet internal stats output (why is timing all wrong?)
191 Resolution: Bullet stats logging only works with a single instance of Bullet (one region).
192Implement meshes or just verify that they work. (Resolution: they do!)
193Do prim hash codes work for sculpties and meshes? (Resolution: yes)
194Linkset implementation using compound shapes. (Resolution: implemented LinksetCompound)
195 Compound shapes will need the LocalID in the shapes and collision
196 processing to get it from there.
197Light cycle not banking (Resolution: It doesn't. Banking is roll adding yaw.)
198Package Bullet source mods for Bullet internal stats output
199 (Resolution: move code into WorldData.h rather than relying on patches)
200Single prim vehicles don't seem to properly vehiclize.
201 (Resolution: mass was not getting set properly for single prim linksets)
202Add material type linkage and input all the material property definitions.
203 Skeleton classes and table are in the sources but are not filled or used.
204 (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}