aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim')
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs33
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs4
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs6
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs4
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs67
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSScene.cs28
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs75
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSShapes.cs18
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs170
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs298
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs256
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs75
12 files changed, 713 insertions, 321 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
index 3c48dcc..e2aa41e 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
@@ -82,7 +82,13 @@ public sealed class BSCharacter : BSPhysObject
82 { 82 {
83 _physicsActorType = (int)ActorTypes.Agent; 83 _physicsActorType = (int)ActorTypes.Agent;
84 _position = pos; 84 _position = pos;
85
86 // Old versions of ScenePresence passed only the height. If width and/or depth are zero,
87 // replace with the default values.
85 _size = size; 88 _size = size;
89 if (_size.X == 0f) _size.X = PhysicsScene.Params.avatarCapsuleDepth;
90 if (_size.Y == 0f) _size.Y = PhysicsScene.Params.avatarCapsuleWidth;
91
86 _flying = isFlying; 92 _flying = isFlying;
87 _orientation = OMV.Quaternion.Identity; 93 _orientation = OMV.Quaternion.Identity;
88 _velocity = OMV.Vector3.Zero; 94 _velocity = OMV.Vector3.Zero;
@@ -175,8 +181,7 @@ public sealed class BSCharacter : BSPhysObject
175 get 181 get
176 { 182 {
177 // Avatar capsule size is kept in the scale parameter. 183 // Avatar capsule size is kept in the scale parameter.
178 // return _size; 184 return _size;
179 return new OMV.Vector3(Scale.X * 2f, Scale.Y * 2f, Scale.Z);
180 } 185 }
181 186
182 set { 187 set {
@@ -184,8 +189,8 @@ public sealed class BSCharacter : BSPhysObject
184 _size = value; 189 _size = value;
185 ComputeAvatarScale(_size); 190 ComputeAvatarScale(_size);
186 ComputeAvatarVolumeAndMass(); 191 ComputeAvatarVolumeAndMass();
187 DetailLog("{0},BSCharacter.setSize,call,scale={1},density={2},volume={3},mass={4}", 192 DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}",
188 LocalID, Scale, _avatarDensity, _avatarVolume, RawMass); 193 LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass);
189 194
190 PhysicsScene.TaintedObject("BSCharacter.setSize", delegate() 195 PhysicsScene.TaintedObject("BSCharacter.setSize", delegate()
191 { 196 {
@@ -203,9 +208,9 @@ public sealed class BSCharacter : BSPhysObject
203 set { BaseShape = value; } 208 set { BaseShape = value; }
204 } 209 }
205 // I want the physics engine to make an avatar capsule 210 // I want the physics engine to make an avatar capsule
206 public override ShapeData.PhysicsShapeType PreferredPhysicalShape 211 public override PhysicsShapeType PreferredPhysicalShape
207 { 212 {
208 get {return ShapeData.PhysicsShapeType.SHAPE_AVATAR; } 213 get {return PhysicsShapeType.SHAPE_CAPSULE; }
209 } 214 }
210 215
211 public override bool Grabbed { 216 public override bool Grabbed {
@@ -614,13 +619,19 @@ public sealed class BSCharacter : BSPhysObject
614 // The 'size' given by the simulator is the mid-point of the avatar 619 // The 'size' given by the simulator is the mid-point of the avatar
615 // and X and Y are unspecified. 620 // and X and Y are unspecified.
616 621
617 OMV.Vector3 newScale = OMV.Vector3.Zero; 622 OMV.Vector3 newScale = size;
618 newScale.X = PhysicsScene.Params.avatarCapsuleRadius; 623 // newScale.X = PhysicsScene.Params.avatarCapsuleWidth;
619 newScale.Y = PhysicsScene.Params.avatarCapsuleRadius; 624 // newScale.Y = PhysicsScene.Params.avatarCapsuleDepth;
620 625
621 // From the total height, remove the capsule half spheres that are at each end 626 // From the total height, remove the capsule half spheres that are at each end
622 newScale.Z = size.Z - (newScale.X + newScale.Y); 627 // The 1.15f came from ODE. Not sure what this factors in.
623 Scale = newScale; 628 // newScale.Z = (size.Z * 1.15f) - (newScale.X + newScale.Y);
629
630 // The total scale height is the central cylindar plus the caps on the two ends.
631 newScale.Z = size.Z + (Math.Min(size.X, size.Y) * 2f);
632
633 // Convert diameters to radii and height to half height -- the way Bullet expects it.
634 Scale = newScale / 2f;
624 } 635 }
625 636
626 // set _avatarVolume and _mass based on capsule size, _density and Scale 637 // set _avatarVolume and _mass based on capsule size, _density and Scale
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
index 436e043..4ee047b 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
@@ -82,9 +82,9 @@ public abstract class BSLinkset
82 82
83 // Some linksets have a preferred physical shape. 83 // Some linksets have a preferred physical shape.
84 // Returns SHAPE_UNKNOWN if there is no preference. Causes the correct shape to be selected. 84 // Returns SHAPE_UNKNOWN if there is no preference. Causes the correct shape to be selected.
85 public virtual ShapeData.PhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor) 85 public virtual PhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
86 { 86 {
87 return ShapeData.PhysicsShapeType.SHAPE_UNKNOWN; 87 return PhysicsShapeType.SHAPE_UNKNOWN;
88 } 88 }
89 89
90 // Linksets move around the children so the linkset might need to compute the child position 90 // Linksets move around the children so the linkset might need to compute the child position
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs
index 3238c85..cb37840 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs
@@ -42,12 +42,12 @@ public sealed class BSLinksetCompound : BSLinkset
42 } 42 }
43 43
44 // For compound implimented linksets, if there are children, use compound shape for the root. 44 // For compound implimented linksets, if there are children, use compound shape for the root.
45 public override ShapeData.PhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor) 45 public override PhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
46 { 46 {
47 ShapeData.PhysicsShapeType ret = ShapeData.PhysicsShapeType.SHAPE_UNKNOWN; 47 PhysicsShapeType ret = PhysicsShapeType.SHAPE_UNKNOWN;
48 if (IsRoot(requestor) && HasAnyChildren) 48 if (IsRoot(requestor) && HasAnyChildren)
49 { 49 {
50 ret = ShapeData.PhysicsShapeType.SHAPE_COMPOUND; 50 ret = PhysicsShapeType.SHAPE_COMPOUND;
51 } 51 }
52 // DetailLog("{0},BSLinksetCompound.PreferredPhysicalShape,call,shape={1}", LinksetRoot.LocalID, ret); 52 // DetailLog("{0},BSLinksetCompound.PreferredPhysicalShape,call,shape={1}", LinksetRoot.LocalID, ret);
53 return ret; 53 return ret;
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
index 991e5b1..e68b167 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
@@ -94,9 +94,9 @@ public abstract class BSPhysObject : PhysicsActor
94 public PrimitiveBaseShape BaseShape { get; protected set; } 94 public PrimitiveBaseShape BaseShape { get; protected set; }
95 // Some types of objects have preferred physical representations. 95 // Some types of objects have preferred physical representations.
96 // Returns SHAPE_UNKNOWN if there is no preference. 96 // Returns SHAPE_UNKNOWN if there is no preference.
97 public virtual ShapeData.PhysicsShapeType PreferredPhysicalShape 97 public virtual PhysicsShapeType PreferredPhysicalShape
98 { 98 {
99 get { return ShapeData.PhysicsShapeType.SHAPE_UNKNOWN; } 99 get { return PhysicsShapeType.SHAPE_UNKNOWN; }
100 } 100 }
101 101
102 // When the physical properties are updated, an EntityProperty holds the update values. 102 // When the physical properties are updated, an EntityProperty holds the update values.
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
index 500c84a..5d16bbf 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
@@ -47,7 +47,6 @@ public sealed class BSPrim : BSPhysObject
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. 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 49 private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user
50 // private OMV.Vector3 _scale; // the multiplier for each mesh dimension for the mesh as created by the meshmerizer
51 50
52 private bool _grabbed; 51 private bool _grabbed;
53 private bool _isSelected; 52 private bool _isSelected;
@@ -94,7 +93,7 @@ public sealed class BSPrim : BSPhysObject
94 _physicsActorType = (int)ActorTypes.Prim; 93 _physicsActorType = (int)ActorTypes.Prim;
95 _position = pos; 94 _position = pos;
96 _size = size; 95 _size = size;
97 Scale = new OMV.Vector3(1f, 1f, 1f); // the scale will be set by CreateGeom depending on object type 96 Scale = size; // the scale will be set by CreateGeom depending on object type
98 _orientation = rotation; 97 _orientation = rotation;
99 _buoyancy = 1f; 98 _buoyancy = 1f;
100 _velocity = OMV.Vector3.Zero; 99 _velocity = OMV.Vector3.Zero;
@@ -155,6 +154,8 @@ public sealed class BSPrim : BSPhysObject
155 public override OMV.Vector3 Size { 154 public override OMV.Vector3 Size {
156 get { return _size; } 155 get { return _size; }
157 set { 156 set {
157 // 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.
158 _size = value; 159 _size = value;
159 ForceBodyShapeRebuild(false); 160 ForceBodyShapeRebuild(false);
160 } 161 }
@@ -170,7 +171,7 @@ public sealed class BSPrim : BSPhysObject
170 } 171 }
171 } 172 }
172 // Whatever the linkset wants is what I want. 173 // Whatever the linkset wants is what I want.
173 public override ShapeData.PhysicsShapeType PreferredPhysicalShape 174 public override PhysicsShapeType PreferredPhysicalShape
174 { get { return Linkset.PreferredPhysicalShape(this); } } 175 { get { return Linkset.PreferredPhysicalShape(this); } }
175 176
176 public override bool ForceBodyShapeRebuild(bool inTaintTime) 177 public override bool ForceBodyShapeRebuild(bool inTaintTime)
@@ -274,19 +275,19 @@ public sealed class BSPrim : BSPhysObject
274 if (!Linkset.IsRoot(this)) 275 if (!Linkset.IsRoot(this))
275 _position = Linkset.Position(this); 276 _position = Linkset.Position(this);
276 277
277 // don't do the GetObjectPosition for root elements because this function is called a zillion times 278 // don't do the GetObjectPosition for root elements because this function is called a zillion times.
278 // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr); 279 // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr);
279 return _position; 280 return _position;
280 } 281 }
281 set { 282 set {
282 // If you must push the position into the physics engine, use ForcePosition. 283 // If the position must be forced into the physics engine, use ForcePosition.
283 if (_position == value) 284 if (_position == value)
284 { 285 {
285 return; 286 return;
286 } 287 }
287 _position = value; 288 _position = value;
288 // TODO: what does it mean to set the position of a child prim?? Rebuild the constraint? 289 // TODO: what does it mean to set the position of a child prim?? Rebuild the constraint?
289 PositionSanityCheck(); 290 PositionSanityCheck(false);
290 PhysicsScene.TaintedObject("BSPrim.setPosition", delegate() 291 PhysicsScene.TaintedObject("BSPrim.setPosition", delegate()
291 { 292 {
292 // DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); 293 // DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
@@ -302,7 +303,7 @@ public sealed class BSPrim : BSPhysObject
302 } 303 }
303 set { 304 set {
304 _position = value; 305 _position = value;
305 PositionSanityCheck(); 306 // PositionSanityCheck(); // Don't do this! Causes a loop and caller should know better.
306 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 307 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
307 ActivateIfPhysical(false); 308 ActivateIfPhysical(false);
308 } 309 }
@@ -311,52 +312,43 @@ public sealed class BSPrim : BSPhysObject
311 // Check that the current position is sane and, if not, modify the position to make it so. 312 // Check that the current position is sane and, if not, modify the position to make it so.
312 // Check for being below terrain and being out of bounds. 313 // Check for being below terrain and being out of bounds.
313 // Returns 'true' of the position was made sane by some action. 314 // Returns 'true' of the position was made sane by some action.
314 private bool PositionSanityCheck() 315 private bool PositionSanityCheck(bool inTaintTime)
315 { 316 {
316 bool ret = false; 317 bool ret = false;
317 318
318 // If totally below the ground, move the prim up
319 // TODO: figure out the right solution for this... only for dynamic objects?
320 /*
321 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); 319 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
320 OMV.Vector3 upForce = OMV.Vector3.Zero;
322 if (Position.Z < terrainHeight) 321 if (Position.Z < terrainHeight)
323 { 322 {
324 DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight); 323 DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight);
325 _position.Z = terrainHeight + 2.0f; 324 float targetHeight = terrainHeight + (Size.Z / 2f);
325 // Upforce proportional to the distance away from the terrain. Correct the error in 1 sec.
326 upForce.Z = (terrainHeight - Position.Z) * 1f;
326 ret = true; 327 ret = true;
327 } 328 }
328 */ 329
329 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) 330 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
330 { 331 {
331 float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position); 332 float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position);
332 // TODO: a floating motor so object will bob in the water 333 // TODO: a floating motor so object will bob in the water
333 if (Position.Z < waterHeight) 334 if (Math.Abs(Position.Z - waterHeight) > 0.1f)
334 { 335 {
335 _position.Z = waterHeight; 336 // Upforce proportional to the distance away from the water. Correct the error in 1 sec.
337 upForce.Z = (waterHeight - Position.Z) * 1f;
336 ret = true; 338 ret = true;
337 } 339 }
338 } 340 }
339 341
340 // TODO: check for out of bounds 342 // TODO: check for out of bounds
341 return ret;
342 }
343 343
344 // A version of the sanity check that also makes sure a new position value is 344 // The above code computes a force to apply to correct any out-of-bounds problems. Apply same.
345 // pushed to the physics engine. This routine would be used by anyone 345 if (ret)
346 // who is not already pushing the value.
347 private bool PositionSanityCheck(bool inTaintTime)
348 {
349 bool ret = false;
350 if (PositionSanityCheck())
351 { 346 {
352 // The new position value must be pushed into the physics engine but we can't 347 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.PositionSanityCheck:belowTerrain", delegate()
353 // just assign to "Position" because of potential call loops.
354 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.PositionSanityCheck", delegate()
355 { 348 {
356 DetailLog("{0},BSPrim.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); 349 // Apply upforce and overcome gravity.
357 ForcePosition = _position; 350 ForceVelocity = ForceVelocity + upForce - PhysicsScene.DefaultGravity;
358 }); 351 });
359 ret = true;
360 } 352 }
361 return ret; 353 return ret;
362 } 354 }
@@ -940,6 +932,7 @@ public sealed class BSPrim : BSPhysObject
940 public override void AddForce(OMV.Vector3 force, bool pushforce) { 932 public override void AddForce(OMV.Vector3 force, bool pushforce) {
941 AddForce(force, pushforce, false); 933 AddForce(force, pushforce, false);
942 } 934 }
935 // Applying a force just adds this to the total force on the object.
943 public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { 936 public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) {
944 // for an object, doesn't matter if force is a pushforce or not 937 // for an object, doesn't matter if force is a pushforce or not
945 if (force.IsFinite()) 938 if (force.IsFinite())
@@ -971,6 +964,7 @@ public sealed class BSPrim : BSPhysObject
971 }); 964 });
972 } 965 }
973 966
967 // An impulse force is scaled by the mass of the object.
974 public void ApplyForceImpulse(OMV.Vector3 impulse, bool inTaintTime) 968 public void ApplyForceImpulse(OMV.Vector3 impulse, bool inTaintTime)
975 { 969 {
976 OMV.Vector3 applyImpulse = impulse; 970 OMV.Vector3 applyImpulse = impulse;
@@ -1423,7 +1417,7 @@ public sealed class BSPrim : BSPhysObject
1423 if (changed != 0) 1417 if (changed != 0)
1424 { 1418 {
1425 // Only update the position of single objects and linkset roots 1419 // Only update the position of single objects and linkset roots
1426 if (this._parentPrim == null) 1420 if (Linkset.IsRoot(this))
1427 { 1421 {
1428 base.RequestPhysicsterseUpdate(); 1422 base.RequestPhysicsterseUpdate();
1429 } 1423 }
@@ -1435,19 +1429,24 @@ public sealed class BSPrim : BSPhysObject
1435 // Updates only for individual prims and for the root object of a linkset. 1429 // Updates only for individual prims and for the root object of a linkset.
1436 if (Linkset.IsRoot(this)) 1430 if (Linkset.IsRoot(this))
1437 { 1431 {
1438 // Assign to the local variables so the normal set action does not happen 1432 // Assign directly to the local variables so the normal set action does not happen
1439 _position = entprop.Position; 1433 _position = entprop.Position;
1440 _orientation = entprop.Rotation; 1434 _orientation = entprop.Rotation;
1441 _velocity = entprop.Velocity; 1435 _velocity = entprop.Velocity;
1442 _acceleration = entprop.Acceleration; 1436 _acceleration = entprop.Acceleration;
1443 _rotationalVelocity = entprop.RotationalVelocity; 1437 _rotationalVelocity = entprop.RotationalVelocity;
1444 1438
1439 // The sanity check can change the velocity and/or position.
1440 if (PositionSanityCheck(true))
1441 {
1442 entprop.Position = _position;
1443 entprop.Velocity = _velocity;
1444 }
1445
1445 // remember the current and last set values 1446 // remember the current and last set values
1446 LastEntityProperties = CurrentEntityProperties; 1447 LastEntityProperties = CurrentEntityProperties;
1447 CurrentEntityProperties = entprop; 1448 CurrentEntityProperties = entprop;
1448 1449
1449 PositionSanityCheck(true);
1450
1451 OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation; 1450 OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation;
1452 DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}", 1451 DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}",
1453 LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity); 1452 LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity);
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
index 2fee95e..27a78d1 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
@@ -712,7 +712,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
712 // here just before the physics engine is called to step the simulation. 712 // here just before the physics engine is called to step the simulation.
713 public void ProcessTaints() 713 public void ProcessTaints()
714 { 714 {
715 InTaintTime = true; 715 InTaintTime = true; // Only used for debugging so locking is not necessary.
716 ProcessRegularTaints(); 716 ProcessRegularTaints();
717 ProcessPostTaintTaints(); 717 ProcessPostTaintTaints();
718 InTaintTime = false; 718 InTaintTime = false;
@@ -758,6 +758,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
758 DetailLog("{0},BSScene.ProcessTaints,leftTaintsOnList,numNotProcessed={1}", DetailLogZero, _taintOperations.Count); 758 DetailLog("{0},BSScene.ProcessTaints,leftTaintsOnList,numNotProcessed={1}", DetailLogZero, _taintOperations.Count);
759 } 759 }
760 */ 760 */
761
761 // swizzle a new list into the list location so we can process what's there 762 // swizzle a new list into the list location so we can process what's there
762 List<TaintCallbackEntry> oldList; 763 List<TaintCallbackEntry> oldList;
763 lock (_taintLock) 764 lock (_taintLock)
@@ -787,8 +788,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
787 // will replace any previous operation by the same object. 788 // will replace any previous operation by the same object.
788 public void PostTaintObject(String ident, uint ID, TaintCallback callback) 789 public void PostTaintObject(String ident, uint ID, TaintCallback callback)
789 { 790 {
790 if (!m_initialized) return;
791
792 string uniqueIdent = ident + "-" + ID.ToString(); 791 string uniqueIdent = ident + "-" + ID.ToString();
793 lock (_taintLock) 792 lock (_taintLock)
794 { 793 {
@@ -864,13 +863,14 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
864 } 863 }
865 } 864 }
866 865
866 // Only used for debugging. Does not change state of anything so locking is not necessary.
867 public bool AssertInTaintTime(string whereFrom) 867 public bool AssertInTaintTime(string whereFrom)
868 { 868 {
869 if (!InTaintTime) 869 if (!InTaintTime)
870 { 870 {
871 DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom); 871 DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom);
872 m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom); 872 m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom);
873 Util.PrintCallStack(); 873 Util.PrintCallStack(); // Prints the stack into the DEBUG log file.
874 } 874 }
875 return InTaintTime; 875 return InTaintTime;
876 } 876 }
@@ -1145,6 +1145,11 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1145 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); }, 1145 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); },
1146 (s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.PhysBody.ptr, v); } ), 1146 (s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.PhysBody.ptr, v); } ),
1147 1147
1148 new ParameterDefn("TerrainImplementation", "Type of shape to use for terrain (0=heightmap, 1=mesh)",
1149 (float)BSTerrainPhys.TerrainImplementation.Mesh,
1150 (s,cf,p,v) => { s.m_params[0].terrainImplementation = cf.GetFloat(p,v); },
1151 (s) => { return s.m_params[0].terrainImplementation; },
1152 (s,p,l,v) => { s.m_params[0].terrainImplementation = v; } ),
1148 new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , 1153 new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" ,
1149 0.5f, 1154 0.5f,
1150 (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); }, 1155 (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); },
@@ -1180,11 +1185,16 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1180 (s,cf,p,v) => { s.m_params[0].avatarRestitution = cf.GetFloat(p, v); }, 1185 (s,cf,p,v) => { s.m_params[0].avatarRestitution = cf.GetFloat(p, v); },
1181 (s) => { return s.m_params[0].avatarRestitution; }, 1186 (s) => { return s.m_params[0].avatarRestitution; },
1182 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarRestitution, p, l, v); } ), 1187 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarRestitution, p, l, v); } ),
1183 new ParameterDefn("AvatarCapsuleRadius", "Radius of space around an avatar", 1188 new ParameterDefn("AvatarCapsuleWidth", "The distance between the sides of the avatar capsule",
1184 0.37f, 1189 0.6f,
1185 (s,cf,p,v) => { s.m_params[0].avatarCapsuleRadius = cf.GetFloat(p, v); }, 1190 (s,cf,p,v) => { s.m_params[0].avatarCapsuleWidth = cf.GetFloat(p, v); },
1186 (s) => { return s.m_params[0].avatarCapsuleRadius; }, 1191 (s) => { return s.m_params[0].avatarCapsuleWidth; },
1187 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleRadius, p, l, v); } ), 1192 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleWidth, p, l, v); } ),
1193 new ParameterDefn("AvatarCapsuleDepth", "The distance between the front and back of the avatar capsule",
1194 0.45f,
1195 (s,cf,p,v) => { s.m_params[0].avatarCapsuleDepth = cf.GetFloat(p, v); },
1196 (s) => { return s.m_params[0].avatarCapsuleDepth; },
1197 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleDepth, p, l, v); } ),
1188 new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar", 1198 new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar",
1189 1.5f, 1199 1.5f,
1190 (s,cf,p,v) => { s.m_params[0].avatarCapsuleHeight = cf.GetFloat(p, v); }, 1200 (s,cf,p,v) => { s.m_params[0].avatarCapsuleHeight = cf.GetFloat(p, v); },
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
index 29a23c0..869735c 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
@@ -178,7 +178,7 @@ public sealed class BSShapeCollection : IDisposable
178 bool ret = false; 178 bool ret = false;
179 switch (shape.type) 179 switch (shape.type)
180 { 180 {
181 case ShapeData.PhysicsShapeType.SHAPE_MESH: 181 case PhysicsShapeType.SHAPE_MESH:
182 MeshDesc meshDesc; 182 MeshDesc meshDesc;
183 if (Meshes.TryGetValue(shape.shapeKey, out meshDesc)) 183 if (Meshes.TryGetValue(shape.shapeKey, out meshDesc))
184 { 184 {
@@ -201,7 +201,7 @@ public sealed class BSShapeCollection : IDisposable
201 meshDesc.lastReferenced = System.DateTime.Now; 201 meshDesc.lastReferenced = System.DateTime.Now;
202 Meshes[shape.shapeKey] = meshDesc; 202 Meshes[shape.shapeKey] = meshDesc;
203 break; 203 break;
204 case ShapeData.PhysicsShapeType.SHAPE_HULL: 204 case PhysicsShapeType.SHAPE_HULL:
205 HullDesc hullDesc; 205 HullDesc hullDesc;
206 if (Hulls.TryGetValue(shape.shapeKey, out hullDesc)) 206 if (Hulls.TryGetValue(shape.shapeKey, out hullDesc))
207 { 207 {
@@ -224,7 +224,7 @@ public sealed class BSShapeCollection : IDisposable
224 hullDesc.lastReferenced = System.DateTime.Now; 224 hullDesc.lastReferenced = System.DateTime.Now;
225 Hulls[shape.shapeKey] = hullDesc; 225 Hulls[shape.shapeKey] = hullDesc;
226 break; 226 break;
227 case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN: 227 case PhysicsShapeType.SHAPE_UNKNOWN:
228 break; 228 break;
229 default: 229 default:
230 // Native shapes are not tracked and they don't go into any list 230 // Native shapes are not tracked and they don't go into any list
@@ -255,16 +255,16 @@ public sealed class BSShapeCollection : IDisposable
255 { 255 {
256 switch (shape.type) 256 switch (shape.type)
257 { 257 {
258 case ShapeData.PhysicsShapeType.SHAPE_HULL: 258 case PhysicsShapeType.SHAPE_HULL:
259 DereferenceHull(shape, shapeCallback); 259 DereferenceHull(shape, shapeCallback);
260 break; 260 break;
261 case ShapeData.PhysicsShapeType.SHAPE_MESH: 261 case PhysicsShapeType.SHAPE_MESH:
262 DereferenceMesh(shape, shapeCallback); 262 DereferenceMesh(shape, shapeCallback);
263 break; 263 break;
264 case ShapeData.PhysicsShapeType.SHAPE_COMPOUND: 264 case PhysicsShapeType.SHAPE_COMPOUND:
265 DereferenceCompound(shape, shapeCallback); 265 DereferenceCompound(shape, shapeCallback);
266 break; 266 break;
267 case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN: 267 case PhysicsShapeType.SHAPE_UNKNOWN:
268 break; 268 break;
269 default: 269 default:
270 break; 270 break;
@@ -352,28 +352,28 @@ public sealed class BSShapeCollection : IDisposable
352 BulletShape shapeInfo = new BulletShape(cShape); 352 BulletShape shapeInfo = new BulletShape(cShape);
353 if (TryGetMeshByPtr(cShape, out meshDesc)) 353 if (TryGetMeshByPtr(cShape, out meshDesc))
354 { 354 {
355 shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_MESH; 355 shapeInfo.type = PhysicsShapeType.SHAPE_MESH;
356 shapeInfo.shapeKey = meshDesc.shapeKey; 356 shapeInfo.shapeKey = meshDesc.shapeKey;
357 } 357 }
358 else 358 else
359 { 359 {
360 if (TryGetHullByPtr(cShape, out hullDesc)) 360 if (TryGetHullByPtr(cShape, out hullDesc))
361 { 361 {
362 shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_HULL; 362 shapeInfo.type = PhysicsShapeType.SHAPE_HULL;
363 shapeInfo.shapeKey = hullDesc.shapeKey; 363 shapeInfo.shapeKey = hullDesc.shapeKey;
364 } 364 }
365 else 365 else
366 { 366 {
367 if (BulletSimAPI.IsCompound2(cShape)) 367 if (BulletSimAPI.IsCompound2(cShape))
368 { 368 {
369 shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_COMPOUND; 369 shapeInfo.type = PhysicsShapeType.SHAPE_COMPOUND;
370 } 370 }
371 else 371 else
372 { 372 {
373 if (BulletSimAPI.IsNativeShape2(cShape)) 373 if (BulletSimAPI.IsNativeShape2(cShape))
374 { 374 {
375 shapeInfo.isNativeShape = true; 375 shapeInfo.isNativeShape = true;
376 shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_BOX; // (technically, type doesn't matter) 376 shapeInfo.type = PhysicsShapeType.SHAPE_BOX; // (technically, type doesn't matter)
377 } 377 }
378 } 378 }
379 } 379 }
@@ -381,7 +381,7 @@ public sealed class BSShapeCollection : IDisposable
381 381
382 DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo); 382 DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo);
383 383
384 if (shapeInfo.type != ShapeData.PhysicsShapeType.SHAPE_UNKNOWN) 384 if (shapeInfo.type != PhysicsShapeType.SHAPE_UNKNOWN)
385 { 385 {
386 DereferenceShape(shapeInfo, true, null); 386 DereferenceShape(shapeInfo, true, null);
387 } 387 }
@@ -405,11 +405,11 @@ public sealed class BSShapeCollection : IDisposable
405 bool ret = false; 405 bool ret = false;
406 bool haveShape = false; 406 bool haveShape = false;
407 407
408 if (!haveShape && prim.PreferredPhysicalShape == ShapeData.PhysicsShapeType.SHAPE_AVATAR) 408 if (!haveShape && prim.PreferredPhysicalShape == PhysicsShapeType.SHAPE_CAPSULE)
409 { 409 {
410 // an avatar capsule is close to a native shape (it is not shared) 410 // an avatar capsule is close to a native shape (it is not shared)
411 ret = GetReferenceToNativeShape(prim, ShapeData.PhysicsShapeType.SHAPE_AVATAR, 411 ret = GetReferenceToNativeShape(prim, PhysicsShapeType.SHAPE_CAPSULE,
412 ShapeData.FixedShapeKey.KEY_CAPSULE, shapeCallback); 412 FixedShapeKey.KEY_CAPSULE, shapeCallback);
413 DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape); 413 DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape);
414 ret = true; 414 ret = true;
415 haveShape = true; 415 haveShape = true;
@@ -417,7 +417,7 @@ public sealed class BSShapeCollection : IDisposable
417 417
418 // Compound shapes are handled special as they are rebuilt from scratch. 418 // 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. 419 // This isn't too great a hardship since most of the child shapes will already been created.
420 if (!haveShape && prim.PreferredPhysicalShape == ShapeData.PhysicsShapeType.SHAPE_COMPOUND) 420 if (!haveShape && prim.PreferredPhysicalShape == PhysicsShapeType.SHAPE_COMPOUND)
421 { 421 {
422 ret = GetReferenceToCompoundShape(prim, shapeCallback); 422 ret = GetReferenceToCompoundShape(prim, shapeCallback);
423 DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape); 423 DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape);
@@ -460,11 +460,11 @@ public sealed class BSShapeCollection : IDisposable
460 haveShape = true; 460 haveShape = true;
461 if (forceRebuild 461 if (forceRebuild
462 || prim.Scale != prim.Size 462 || prim.Scale != prim.Size
463 || prim.PhysShape.type != ShapeData.PhysicsShapeType.SHAPE_SPHERE 463 || prim.PhysShape.type != PhysicsShapeType.SHAPE_SPHERE
464 ) 464 )
465 { 465 {
466 ret = GetReferenceToNativeShape(prim, ShapeData.PhysicsShapeType.SHAPE_SPHERE, 466 ret = GetReferenceToNativeShape(prim, PhysicsShapeType.SHAPE_SPHERE,
467 ShapeData.FixedShapeKey.KEY_SPHERE, shapeCallback); 467 FixedShapeKey.KEY_SPHERE, shapeCallback);
468 DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}", 468 DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}",
469 prim.LocalID, forceRebuild, prim.PhysShape); 469 prim.LocalID, forceRebuild, prim.PhysShape);
470 } 470 }
@@ -474,11 +474,11 @@ public sealed class BSShapeCollection : IDisposable
474 haveShape = true; 474 haveShape = true;
475 if (forceRebuild 475 if (forceRebuild
476 || prim.Scale != prim.Size 476 || prim.Scale != prim.Size
477 || prim.PhysShape.type != ShapeData.PhysicsShapeType.SHAPE_BOX 477 || prim.PhysShape.type != PhysicsShapeType.SHAPE_BOX
478 ) 478 )
479 { 479 {
480 ret = GetReferenceToNativeShape( prim, ShapeData.PhysicsShapeType.SHAPE_BOX, 480 ret = GetReferenceToNativeShape( prim, PhysicsShapeType.SHAPE_BOX,
481 ShapeData.FixedShapeKey.KEY_BOX, shapeCallback); 481 FixedShapeKey.KEY_BOX, shapeCallback);
482 DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}", 482 DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}",
483 prim.LocalID, forceRebuild, prim.PhysShape); 483 prim.LocalID, forceRebuild, prim.PhysShape);
484 } 484 }
@@ -519,15 +519,12 @@ public sealed class BSShapeCollection : IDisposable
519 // Creates a native shape and assignes it to prim.BSShape. 519 // Creates a native shape and assignes it to prim.BSShape.
520 // "Native" shapes are never shared. they are created here and destroyed in DereferenceShape(). 520 // "Native" shapes are never shared. they are created here and destroyed in DereferenceShape().
521 private bool GetReferenceToNativeShape(BSPhysObject prim, 521 private bool GetReferenceToNativeShape(BSPhysObject prim,
522 ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey, 522 PhysicsShapeType shapeType, FixedShapeKey shapeKey,
523 ShapeDestructionCallback shapeCallback) 523 ShapeDestructionCallback shapeCallback)
524 { 524 {
525 // release any previous shape 525 // release any previous shape
526 DereferenceShape(prim.PhysShape, true, shapeCallback); 526 DereferenceShape(prim.PhysShape, true, shapeCallback);
527 527
528 // Bullet native objects are scaled by the Bullet engine so pass the size in
529 prim.Scale = prim.Size;
530
531 BulletShape newShape = BuildPhysicalNativeShape(prim, shapeType, shapeKey); 528 BulletShape newShape = BuildPhysicalNativeShape(prim, shapeType, shapeKey);
532 529
533 // Don't need to do a 'ReferenceShape()' here because native shapes are not shared. 530 // Don't need to do a 'ReferenceShape()' here because native shapes are not shared.
@@ -538,8 +535,8 @@ public sealed class BSShapeCollection : IDisposable
538 return true; 535 return true;
539 } 536 }
540 537
541 private BulletShape BuildPhysicalNativeShape(BSPhysObject prim, ShapeData.PhysicsShapeType shapeType, 538 private BulletShape BuildPhysicalNativeShape(BSPhysObject prim, PhysicsShapeType shapeType,
542 ShapeData.FixedShapeKey shapeKey) 539 FixedShapeKey shapeKey)
543 { 540 {
544 BulletShape newShape; 541 BulletShape newShape;
545 // Need to make sure the passed shape information is for the native type. 542 // Need to make sure the passed shape information is for the native type.
@@ -547,12 +544,13 @@ public sealed class BSShapeCollection : IDisposable
547 nativeShapeData.Type = shapeType; 544 nativeShapeData.Type = shapeType;
548 nativeShapeData.ID = prim.LocalID; 545 nativeShapeData.ID = prim.LocalID;
549 nativeShapeData.Scale = prim.Scale; 546 nativeShapeData.Scale = prim.Scale;
550 nativeShapeData.Size = prim.Scale; 547 nativeShapeData.Size = prim.Scale; // unneeded, I think.
551 nativeShapeData.MeshKey = (ulong)shapeKey; 548 nativeShapeData.MeshKey = (ulong)shapeKey;
552 nativeShapeData.HullKey = (ulong)shapeKey; 549 nativeShapeData.HullKey = (ulong)shapeKey;
553 550
554 if (shapeType == ShapeData.PhysicsShapeType.SHAPE_AVATAR) 551 if (shapeType == PhysicsShapeType.SHAPE_CAPSULE)
555 { 552 {
553 // The proper scale has been calculated in the prim.
556 newShape = new BulletShape( 554 newShape = new BulletShape(
557 BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1f, 1f, prim.Scale) 555 BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1f, 1f, prim.Scale)
558 , shapeType); 556 , shapeType);
@@ -560,6 +558,9 @@ public sealed class BSShapeCollection : IDisposable
560 } 558 }
561 else 559 else
562 { 560 {
561 // Native shapes are scaled in Bullet so set the scaling to the size
562 prim.Scale = prim.Size;
563 nativeShapeData.Scale = prim.Scale;
563 newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, nativeShapeData), shapeType); 564 newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, nativeShapeData), shapeType);
564 } 565 }
565 if (newShape.ptr == IntPtr.Zero) 566 if (newShape.ptr == IntPtr.Zero)
@@ -585,7 +586,7 @@ public sealed class BSShapeCollection : IDisposable
585 System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod); 586 System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
586 587
587 // if this new shape is the same as last time, don't recreate the mesh 588 // if this new shape is the same as last time, don't recreate the mesh
588 if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == ShapeData.PhysicsShapeType.SHAPE_MESH) 589 if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == PhysicsShapeType.SHAPE_MESH)
589 return false; 590 return false;
590 591
591 DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2}", 592 DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2}",
@@ -643,7 +644,7 @@ public sealed class BSShapeCollection : IDisposable
643 indices.GetLength(0), indices, vertices.Count, verticesAsFloats); 644 indices.GetLength(0), indices, vertices.Count, verticesAsFloats);
644 } 645 }
645 } 646 }
646 BulletShape newShape = new BulletShape(meshPtr, ShapeData.PhysicsShapeType.SHAPE_MESH); 647 BulletShape newShape = new BulletShape(meshPtr, PhysicsShapeType.SHAPE_MESH);
647 newShape.shapeKey = newMeshKey; 648 newShape.shapeKey = newMeshKey;
648 649
649 return newShape; 650 return newShape;
@@ -659,7 +660,7 @@ public sealed class BSShapeCollection : IDisposable
659 System.UInt64 newHullKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod); 660 System.UInt64 newHullKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
660 661
661 // if the hull hasn't changed, don't rebuild it 662 // if the hull hasn't changed, don't rebuild it
662 if (newHullKey == prim.PhysShape.shapeKey && prim.PhysShape.type == ShapeData.PhysicsShapeType.SHAPE_HULL) 663 if (newHullKey == prim.PhysShape.shapeKey && prim.PhysShape.type == PhysicsShapeType.SHAPE_HULL)
663 return false; 664 return false;
664 665
665 DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}", 666 DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}",
@@ -780,7 +781,7 @@ public sealed class BSShapeCollection : IDisposable
780 } 781 }
781 } 782 }
782 783
783 BulletShape newShape = new BulletShape(hullPtr, ShapeData.PhysicsShapeType.SHAPE_HULL); 784 BulletShape newShape = new BulletShape(hullPtr, PhysicsShapeType.SHAPE_HULL);
784 newShape.shapeKey = newHullKey; 785 newShape.shapeKey = newHullKey;
785 786
786 return newShape; // 'true' means a new shape has been added to this prim 787 return newShape; // 'true' means a new shape has been added to this prim
@@ -803,7 +804,7 @@ public sealed class BSShapeCollection : IDisposable
803 // DereferenceShape(prim.PhysShape, true, shapeCallback); 804 // DereferenceShape(prim.PhysShape, true, shapeCallback);
804 805
805 BulletShape cShape = new BulletShape( 806 BulletShape cShape = new BulletShape(
806 BulletSimAPI.CreateCompoundShape2(PhysicsScene.World.ptr, false), ShapeData.PhysicsShapeType.SHAPE_COMPOUND); 807 BulletSimAPI.CreateCompoundShape2(PhysicsScene.World.ptr, false), PhysicsShapeType.SHAPE_COMPOUND);
807 808
808 // Create the shape for the root prim and add it to the compound shape. Cannot be a native shape. 809 // Create the shape for the root prim and add it to the compound shape. Cannot be a native shape.
809 CreateGeomMeshOrHull(prim, shapeCallback); 810 CreateGeomMeshOrHull(prim, shapeCallback);
@@ -894,7 +895,7 @@ public sealed class BSShapeCollection : IDisposable
894 895
895 // While we figure out the real problem, stick a simple native shape on the object. 896 // While we figure out the real problem, stick a simple native shape on the object.
896 BulletShape fillinShape = 897 BulletShape fillinShape =
897 BuildPhysicalNativeShape(prim, ShapeData.PhysicsShapeType.SHAPE_BOX, ShapeData.FixedShapeKey.KEY_BOX); 898 BuildPhysicalNativeShape(prim, PhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);
898 899
899 return fillinShape; 900 return fillinShape;
900 } 901 }
@@ -940,7 +941,7 @@ public sealed class BSShapeCollection : IDisposable
940 else 941 else
941 { 942 {
942 bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr, 943 bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
943 prim.LocalID, prim.ForcePosition, prim.ForceOrientation); 944 prim.LocalID, prim.RawPosition, prim.RawOrientation);
944 DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); 945 DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
945 } 946 }
946 aBody = new BulletBody(prim.LocalID, bodyPtr); 947 aBody = new BulletBody(prim.LocalID, bodyPtr);
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs
index d59a486..f2e62d9 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs
@@ -35,7 +35,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
35public abstract class BSShape 35public abstract class BSShape
36{ 36{
37 public IntPtr ptr { get; set; } 37 public IntPtr ptr { get; set; }
38 public ShapeData.PhysicsShapeType type { get; set; } 38 public PhysicsShapeType type { get; set; }
39 public System.UInt64 key { get; set; } 39 public System.UInt64 key { get; set; }
40 public int referenceCount { get; set; } 40 public int referenceCount { get; set; }
41 public DateTime lastReferenced { get; set; } 41 public DateTime lastReferenced { get; set; }
@@ -43,7 +43,7 @@ public abstract class BSShape
43 public BSShape() 43 public BSShape()
44 { 44 {
45 ptr = IntPtr.Zero; 45 ptr = IntPtr.Zero;
46 type = ShapeData.PhysicsShapeType.SHAPE_UNKNOWN; 46 type = PhysicsShapeType.SHAPE_UNKNOWN;
47 key = 0; 47 key = 0;
48 referenceCount = 0; 48 referenceCount = 0;
49 lastReferenced = DateTime.Now; 49 lastReferenced = DateTime.Now;
@@ -54,17 +54,17 @@ public abstract class BSShape
54 { 54 {
55 BSShape ret = null; 55 BSShape ret = null;
56 56
57 if (prim.PreferredPhysicalShape == ShapeData.PhysicsShapeType.SHAPE_AVATAR) 57 if (prim.PreferredPhysicalShape == PhysicsShapeType.SHAPE_CAPSULE)
58 { 58 {
59 // an avatar capsule is close to a native shape (it is not shared) 59 // an avatar capsule is close to a native shape (it is not shared)
60 ret = BSShapeNative.GetReference(physicsScene, prim, ShapeData.PhysicsShapeType.SHAPE_AVATAR, 60 ret = BSShapeNative.GetReference(physicsScene, prim, PhysicsShapeType.SHAPE_CAPSULE,
61 ShapeData.FixedShapeKey.KEY_CAPSULE); 61 FixedShapeKey.KEY_CAPSULE);
62 physicsScene.DetailLog("{0},BSShape.GetShapeReference,avatarCapsule,shape={1}", prim.LocalID, ret); 62 physicsScene.DetailLog("{0},BSShape.GetShapeReference,avatarCapsule,shape={1}", prim.LocalID, ret);
63 } 63 }
64 64
65 // Compound shapes are handled special as they are rebuilt from scratch. 65 // Compound shapes are handled special as they are rebuilt from scratch.
66 // This isn't too great a hardship since most of the child shapes will already been created. 66 // This isn't too great a hardship since most of the child shapes will already been created.
67 if (ret == null && prim.PreferredPhysicalShape == ShapeData.PhysicsShapeType.SHAPE_COMPOUND) 67 if (ret == null && prim.PreferredPhysicalShape == PhysicsShapeType.SHAPE_COMPOUND)
68 { 68 {
69 // Getting a reference to a compound shape gets you the compound shape with the root prim shape added 69 // Getting a reference to a compound shape gets you the compound shape with the root prim shape added
70 ret = BSShapeCompound.GetReference(prim); 70 ret = BSShapeCompound.GetReference(prim);
@@ -123,14 +123,14 @@ public class BSShapeNative : BSShape
123 { 123 {
124 } 124 }
125 public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim, 125 public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim,
126 ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey) 126 PhysicsShapeType shapeType, FixedShapeKey shapeKey)
127 { 127 {
128 // Native shapes are not shared and are always built anew. 128 // Native shapes are not shared and are always built anew.
129 return new BSShapeNative(physicsScene, prim, shapeType, shapeKey); 129 return new BSShapeNative(physicsScene, prim, shapeType, shapeKey);
130 } 130 }
131 131
132 private BSShapeNative(BSScene physicsScene, BSPhysObject prim, 132 private BSShapeNative(BSScene physicsScene, BSPhysObject prim,
133 ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey) 133 PhysicsShapeType shapeType, FixedShapeKey shapeKey)
134 { 134 {
135 ShapeData nativeShapeData = new ShapeData(); 135 ShapeData nativeShapeData = new ShapeData();
136 nativeShapeData.Type = shapeType; 136 nativeShapeData.Type = shapeType;
@@ -141,7 +141,7 @@ public class BSShapeNative : BSShape
141 nativeShapeData.HullKey = (ulong)shapeKey; 141 nativeShapeData.HullKey = (ulong)shapeKey;
142 142
143 143
144 if (shapeType == ShapeData.PhysicsShapeType.SHAPE_AVATAR) 144 if (shapeType == PhysicsShapeType.SHAPE_CAPSULE)
145 { 145 {
146 ptr = BulletSimAPI.BuildCapsuleShape2(physicsScene.World.ptr, 1f, 1f, prim.Scale); 146 ptr = BulletSimAPI.BuildCapsuleShape2(physicsScene.World.ptr, 1f, 1f, prim.Scale);
147 physicsScene.DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale); 147 physicsScene.DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale);
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs
new file mode 100755
index 0000000..8fc36d1
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs
@@ -0,0 +1,170 @@
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;
30
31using OpenSim.Framework;
32using OpenSim.Region.Framework;
33using OpenSim.Region.CoreModules;
34using OpenSim.Region.Physics.Manager;
35
36using Nini.Config;
37using log4net;
38
39using OpenMetaverse;
40
41namespace OpenSim.Region.Physics.BulletSPlugin
42{
43public sealed class BSTerrainHeightmap : BSTerrainPhys
44{
45 static string LogHeader = "[BULLETSIM TERRAIN HEIGHTMAP]";
46
47 BulletHeightMapInfo m_mapInfo = null;
48
49 // Constructor to build a default, flat heightmap terrain.
50 public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize)
51 : base(physicsScene, regionBase, id)
52 {
53 Vector3 minTerrainCoords = new Vector3(0f, 0f, BSTerrainManager.HEIGHT_INITIALIZATION - BSTerrainManager.HEIGHT_EQUAL_FUDGE);
54 Vector3 maxTerrainCoords = new Vector3(regionSize.X, regionSize.Y, BSTerrainManager.HEIGHT_INITIALIZATION);
55 int totalHeights = (int)maxTerrainCoords.X * (int)maxTerrainCoords.Y;
56 float[] initialMap = new float[totalHeights];
57 for (int ii = 0; ii < totalHeights; ii++)
58 {
59 initialMap[ii] = BSTerrainManager.HEIGHT_INITIALIZATION;
60 }
61 m_mapInfo = new BulletHeightMapInfo(id, initialMap, IntPtr.Zero);
62 m_mapInfo.minCoords = minTerrainCoords;
63 m_mapInfo.maxCoords = maxTerrainCoords;
64 m_mapInfo.terrainRegionBase = TerrainBase;
65 // Don't have to free any previous since we just got here.
66 BuildHeightmapTerrain();
67 }
68
69 // This minCoords and maxCoords passed in give the size of the terrain (min and max Z
70 // are the high and low points of the heightmap).
71 public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap,
72 Vector3 minCoords, Vector3 maxCoords)
73 : base(physicsScene, regionBase, id)
74 {
75 m_mapInfo = new BulletHeightMapInfo(id, initialMap, IntPtr.Zero);
76 m_mapInfo.minCoords = minCoords;
77 m_mapInfo.maxCoords = maxCoords;
78 m_mapInfo.minZ = minCoords.Z;
79 m_mapInfo.maxZ = maxCoords.Z;
80 m_mapInfo.terrainRegionBase = TerrainBase;
81
82 // Don't have to free any previous since we just got here.
83 BuildHeightmapTerrain();
84 }
85
86 public override void Dispose()
87 {
88 ReleaseHeightMapTerrain();
89 }
90
91 // Using the information in m_mapInfo, create the physical representation of the heightmap.
92 private void BuildHeightmapTerrain()
93 {
94 m_mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, m_mapInfo.ID,
95 m_mapInfo.minCoords, m_mapInfo.maxCoords,
96 m_mapInfo.heightMap, BSTerrainManager.TERRAIN_COLLISION_MARGIN);
97
98 // Create the terrain shape from the mapInfo
99 m_mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(m_mapInfo.Ptr),
100 PhysicsShapeType.SHAPE_TERRAIN);
101
102 // The terrain object initial position is at the center of the object
103 Vector3 centerPos;
104 centerPos.X = m_mapInfo.minCoords.X + (m_mapInfo.sizeX / 2f);
105 centerPos.Y = m_mapInfo.minCoords.Y + (m_mapInfo.sizeY / 2f);
106 centerPos.Z = m_mapInfo.minZ + ((m_mapInfo.maxZ - m_mapInfo.minZ) / 2f);
107
108 m_mapInfo.terrainBody = new BulletBody(m_mapInfo.ID,
109 BulletSimAPI.CreateBodyWithDefaultMotionState2(m_mapInfo.terrainShape.ptr,
110 m_mapInfo.ID, centerPos, Quaternion.Identity));
111
112 // Set current terrain attributes
113 BulletSimAPI.SetFriction2(m_mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainFriction);
114 BulletSimAPI.SetHitFraction2(m_mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainHitFraction);
115 BulletSimAPI.SetRestitution2(m_mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainRestitution);
116 BulletSimAPI.SetCollisionFlags2(m_mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
117
118 // Return the new terrain to the world of physical objects
119 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
120
121 // redo its bounding box now that it is in the world
122 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
123
124 BulletSimAPI.SetCollisionFilterMask2(m_mapInfo.terrainBody.ptr,
125 (uint)CollisionFilterGroups.TerrainFilter,
126 (uint)CollisionFilterGroups.TerrainMask);
127
128 // Make it so the terrain will not move or be considered for movement.
129 BulletSimAPI.ForceActivationState2(m_mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
130
131 return;
132 }
133
134 // If there is information in m_mapInfo pointing to physical structures, release same.
135 private void ReleaseHeightMapTerrain()
136 {
137 if (m_mapInfo != null)
138 {
139 if (m_mapInfo.terrainBody.ptr != IntPtr.Zero)
140 {
141 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
142 // Frees both the body and the shape.
143 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
144 BulletSimAPI.ReleaseHeightMapInfo2(m_mapInfo.Ptr);
145 }
146 }
147 m_mapInfo = null;
148 }
149
150 // The passed position is relative to the base of the region.
151 public override float GetHeightAtXYZ(Vector3 pos)
152 {
153 float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
154
155 int mapIndex = (int)pos.Y * (int)m_mapInfo.sizeY + (int)pos.X;
156 try
157 {
158 ret = m_mapInfo.heightMap[mapIndex];
159 }
160 catch
161 {
162 // Sometimes they give us wonky values of X and Y. Give a warning and return something.
163 PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}",
164 LogHeader, m_mapInfo.terrainRegionBase, pos);
165 ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
166 }
167 return ret;
168 }
169}
170}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
index 7c34af2..71fca33 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
@@ -40,6 +40,32 @@ using OpenMetaverse;
40 40
41namespace OpenSim.Region.Physics.BulletSPlugin 41namespace OpenSim.Region.Physics.BulletSPlugin
42{ 42{
43
44// The physical implementation of the terrain is wrapped in this class.
45public abstract class BSTerrainPhys : IDisposable
46{
47 public enum TerrainImplementation
48 {
49 Heightmap = 0,
50 Mesh = 1
51 }
52
53 public BSScene PhysicsScene { get; private set; }
54 // Base of the region in world coordinates. Coordinates inside the region are relative to this.
55 public Vector3 TerrainBase { get; private set; }
56 public uint ID { get; private set; }
57
58 public BSTerrainPhys(BSScene physicsScene, Vector3 regionBase, uint id)
59 {
60 PhysicsScene = physicsScene;
61 TerrainBase = regionBase;
62 ID = id;
63 }
64 public abstract void Dispose();
65 public abstract float GetHeightAtXYZ(Vector3 pos);
66}
67
68// ==========================================================================================
43public sealed class BSTerrainManager 69public sealed class BSTerrainManager
44{ 70{
45 static string LogHeader = "[BULLETSIM TERRAIN MANAGER]"; 71 static string LogHeader = "[BULLETSIM TERRAIN MANAGER]";
@@ -67,11 +93,10 @@ public sealed class BSTerrainManager
67 93
68 // If doing mega-regions, if we're region zero we will be managing multiple 94 // If doing mega-regions, if we're region zero we will be managing multiple
69 // region terrains since region zero does the physics for the whole mega-region. 95 // region terrains since region zero does the physics for the whole mega-region.
70 private Dictionary<Vector2, BulletHeightMapInfo> m_heightMaps; 96 private Dictionary<Vector3, BSTerrainPhys> m_terrains;
71 97
72 // True of the terrain has been modified. 98 // Flags used to know when to recalculate the height.
73 // Used to force recalculation of terrain height after terrain has been modified 99 private bool m_terrainModified = false;
74 private bool m_terrainModified;
75 100
76 // If we are doing mega-regions, terrains are added from TERRAIN_ID to m_terrainCount. 101 // If we are doing mega-regions, terrains are added from TERRAIN_ID to m_terrainCount.
77 // This is incremented before assigning to new region so it is the last ID allocated. 102 // This is incremented before assigning to new region so it is the last ID allocated.
@@ -89,8 +114,7 @@ public sealed class BSTerrainManager
89 public BSTerrainManager(BSScene physicsScene) 114 public BSTerrainManager(BSScene physicsScene)
90 { 115 {
91 PhysicsScene = physicsScene; 116 PhysicsScene = physicsScene;
92 m_heightMaps = new Dictionary<Vector2,BulletHeightMapInfo>(); 117 m_terrains = new Dictionary<Vector3,BSTerrainPhys>();
93 m_terrainModified = false;
94 118
95 // Assume one region of default size 119 // Assume one region of default size
96 m_worldOffset = Vector3.Zero; 120 m_worldOffset = Vector3.Zero;
@@ -99,9 +123,6 @@ public sealed class BSTerrainManager
99 } 123 }
100 124
101 // Create the initial instance of terrain and the underlying ground plane. 125 // Create the initial instance of terrain and the underlying ground plane.
102 // The objects are allocated in the unmanaged space and the pointers are tracked
103 // by the managed code.
104 // The terrains and the groundPlane are not added to the list of PhysObjects.
105 // This is called from the initialization routine so we presume it is 126 // This is called from the initialization routine so we presume it is
106 // safe to call Bullet in real time. We hope no one is moving prims around yet. 127 // safe to call Bullet in real time. We hope no one is moving prims around yet.
107 public void CreateInitialGroundPlaneAndTerrain() 128 public void CreateInitialGroundPlaneAndTerrain()
@@ -109,7 +130,7 @@ public sealed class BSTerrainManager
109 // The ground plane is here to catch things that are trying to drop to negative infinity 130 // The ground plane is here to catch things that are trying to drop to negative infinity
110 BulletShape groundPlaneShape = new BulletShape( 131 BulletShape groundPlaneShape = new BulletShape(
111 BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f, TERRAIN_COLLISION_MARGIN), 132 BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f, TERRAIN_COLLISION_MARGIN),
112 ShapeData.PhysicsShapeType.SHAPE_GROUNDPLANE); 133 PhysicsShapeType.SHAPE_GROUNDPLANE);
113 m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID, 134 m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID,
114 BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID, 135 BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID,
115 Vector3.Zero, Quaternion.Identity)); 136 Vector3.Zero, Quaternion.Identity));
@@ -121,15 +142,9 @@ public sealed class BSTerrainManager
121 BulletSimAPI.SetCollisionFilterMask2(m_groundPlane.ptr, 142 BulletSimAPI.SetCollisionFilterMask2(m_groundPlane.ptr,
122 (uint)CollisionFilterGroups.GroundPlaneFilter, (uint)CollisionFilterGroups.GroundPlaneMask); 143 (uint)CollisionFilterGroups.GroundPlaneFilter, (uint)CollisionFilterGroups.GroundPlaneMask);
123 144
124 Vector3 minTerrainCoords = new Vector3(0f, 0f, HEIGHT_INITIALIZATION - HEIGHT_EQUAL_FUDGE); 145 // Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain.
125 Vector3 maxTerrainCoords = new Vector3(DefaultRegionSize.X, DefaultRegionSize.Y, HEIGHT_INITIALIZATION); 146 BSTerrainPhys initialTerrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize);
126 int totalHeights = (int)maxTerrainCoords.X * (int)maxTerrainCoords.Y; 147 m_terrains.Add(Vector3.Zero, initialTerrain);
127 float[] initialMap = new float[totalHeights];
128 for (int ii = 0; ii < totalHeights; ii++)
129 {
130 initialMap[ii] = HEIGHT_INITIALIZATION;
131 }
132 UpdateOrCreateTerrain(BSScene.TERRAIN_ID, initialMap, minTerrainCoords, maxTerrainCoords, true);
133 } 148 }
134 149
135 // Release all the terrain structures we might have allocated 150 // Release all the terrain structures we might have allocated
@@ -150,15 +165,11 @@ public sealed class BSTerrainManager
150 // Release all the terrain we have allocated 165 // Release all the terrain we have allocated
151 public void ReleaseTerrain() 166 public void ReleaseTerrain()
152 { 167 {
153 foreach (KeyValuePair<Vector2, BulletHeightMapInfo> kvp in m_heightMaps) 168 foreach (KeyValuePair<Vector3, BSTerrainPhys> kvp in m_terrains)
154 { 169 {
155 if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, kvp.Value.terrainBody.ptr)) 170 kvp.Value.Dispose();
156 {
157 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, kvp.Value.terrainBody.ptr);
158 BulletSimAPI.ReleaseHeightMapInfo2(kvp.Value.Ptr);
159 }
160 } 171 }
161 m_heightMaps.Clear(); 172 m_terrains.Clear();
162 } 173 }
163 174
164 // The simulator wants to set a new heightmap for the terrain. 175 // The simulator wants to set a new heightmap for the terrain.
@@ -176,8 +187,9 @@ public sealed class BSTerrainManager
176 { 187 {
177 DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}", 188 DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}",
178 BSScene.DetailLogZero, m_worldOffset, m_worldMax); 189 BSScene.DetailLogZero, m_worldOffset, m_worldMax);
179 ((BSScene)MegaRegionParentPhysicsScene).TerrainManager.UpdateOrCreateTerrain(BSScene.CHILDTERRAIN_ID, 190 ((BSScene)MegaRegionParentPhysicsScene).TerrainManager.UpdateTerrain(
180 localHeightMap, m_worldOffset, m_worldOffset + DefaultRegionSize, true); 191 BSScene.CHILDTERRAIN_ID, localHeightMap,
192 m_worldOffset, m_worldOffset + DefaultRegionSize, true);
181 } 193 }
182 } 194 }
183 else 195 else
@@ -185,7 +197,7 @@ public sealed class BSTerrainManager
185 // If not doing the mega-prim thing, just change the terrain 197 // If not doing the mega-prim thing, just change the terrain
186 DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero); 198 DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero);
187 199
188 UpdateOrCreateTerrain(BSScene.TERRAIN_ID, localHeightMap, 200 UpdateTerrain(BSScene.TERRAIN_ID, localHeightMap,
189 m_worldOffset, m_worldOffset + DefaultRegionSize, true); 201 m_worldOffset, m_worldOffset + DefaultRegionSize, true);
190 } 202 }
191 }); 203 });
@@ -195,56 +207,63 @@ public sealed class BSTerrainManager
195 // based on the passed information. The 'id' should be either the terrain id or 207 // based on the passed information. The 'id' should be either the terrain id or
196 // BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used. 208 // BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used.
197 // The latter feature is for creating child terrains for mega-regions. 209 // The latter feature is for creating child terrains for mega-regions.
198 // If called with a mapInfo in m_heightMaps but the terrain has no body yet (mapInfo.terrainBody.Ptr == 0)
199 // then a new body and shape is created and the mapInfo is filled.
200 // This call is used for doing the initial terrain creation.
201 // If called with a mapInfo in m_heightMaps and there is an existing terrain body, a new 210 // If called with a mapInfo in m_heightMaps and there is an existing terrain body, a new
202 // terrain shape is created and added to the body. 211 // terrain shape is created and added to the body.
203 // This call is most often used to update the heightMap and parameters of the terrain. 212 // This call is most often used to update the heightMap and parameters of the terrain.
204 // (The above does suggest that some simplification/refactoring is in order.) 213 // (The above does suggest that some simplification/refactoring is in order.)
205 private void UpdateOrCreateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords, bool inTaintTime) 214 private void UpdateTerrain(uint id, float[] heightMap,
215 Vector3 minCoords, Vector3 maxCoords, bool inTaintTime)
206 { 216 {
207 DetailLog("{0},BSTerrainManager.UpdateOrCreateTerrain,call,minC={1},maxC={2},inTaintTime={3}", 217 DetailLog("{0},BSTerrainManager.UpdateTerrain,call,minC={1},maxC={2},inTaintTime={3}",
208 BSScene.DetailLogZero, minCoords, maxCoords, inTaintTime); 218 BSScene.DetailLogZero, minCoords, maxCoords, inTaintTime);
209 219
220 // Find high and low points of passed heightmap.
221 // 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
223 // terrain so we replace passed min and max Z with the actual terrain min/max Z.
210 float minZ = float.MaxValue; 224 float minZ = float.MaxValue;
211 float maxZ = float.MinValue; 225 float maxZ = float.MinValue;
212 Vector2 terrainRegionBase = new Vector2(minCoords.X, minCoords.Y); 226 foreach (float height in heightMap)
213
214 int heightMapSize = heightMap.Length;
215 for (int ii = 0; ii < heightMapSize; ii++)
216 { 227 {
217 float height = heightMap[ii];
218 if (height < minZ) minZ = height; 228 if (height < minZ) minZ = height;
219 if (height > maxZ) maxZ = height; 229 if (height > maxZ) maxZ = height;
220 } 230 }
221 231 if (minZ == maxZ)
222 // The shape of the terrain is from its base to its extents. 232 {
233 // If min and max are the same, reduce min a little bit so a good bounding box is created.
234 minZ -= BSTerrainManager.HEIGHT_EQUAL_FUDGE;
235 }
223 minCoords.Z = minZ; 236 minCoords.Z = minZ;
224 maxCoords.Z = maxZ; 237 maxCoords.Z = maxZ;
225 238
226 BulletHeightMapInfo mapInfo; 239 Vector3 terrainRegionBase = new Vector3(minCoords.X, minCoords.Y, 0f);
227 if (m_heightMaps.TryGetValue(terrainRegionBase, out mapInfo)) 240
241 BSTerrainPhys terrainPhys;
242 if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys))
228 { 243 {
229 // If this is terrain we know about, it's easy to update 244 // There is already a terrain in this spot. Free the old and build the new.
230 245 DetailLog("{0},UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}",
231 mapInfo.heightMap = heightMap; 246 BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords);
232 mapInfo.minCoords = minCoords; 247
233 mapInfo.maxCoords = maxCoords; 248 PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateTerrain:UpdateExisting", delegate()
234 mapInfo.minZ = minZ;
235 mapInfo.maxZ = maxZ;
236 mapInfo.sizeX = maxCoords.X - minCoords.X;
237 mapInfo.sizeY = maxCoords.Y - minCoords.Y;
238 DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,call,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}",
239 BSScene.DetailLogZero, terrainRegionBase, mapInfo.minCoords, mapInfo.maxCoords, mapInfo.sizeX, mapInfo.sizeY);
240
241 PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateOrCreateTerrain:UpdateExisting", delegate()
242 { 249 {
243 if (MegaRegionParentPhysicsScene != null) 250 // Remove old terrain from the collection
251 m_terrains.Remove(terrainRegionBase);
252 // Release any physical memory it may be using.
253 terrainPhys.Dispose();
254
255 if (MegaRegionParentPhysicsScene == null)
256 {
257 BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
258 m_terrains.Add(terrainRegionBase, newTerrainPhys);
259
260 m_terrainModified = true;
261 }
262 else
244 { 263 {
245 // It's possible that Combine() was called after this code was queued. 264 // It's possible that Combine() was called after this code was queued.
246 // If we are a child of combined regions, we don't create any terrain for us. 265 // If we are a child of combined regions, we don't create any terrain for us.
247 DetailLog("{0},UpdateOrCreateTerrain:AmACombineChild,taint", BSScene.DetailLogZero); 266 DetailLog("{0},BSTerrainManager.UpdateTerrain:AmACombineChild,taint", BSScene.DetailLogZero);
248 267
249 // Get rid of any terrain that may have been allocated for us. 268 // Get rid of any terrain that may have been allocated for us.
250 ReleaseGroundPlaneAndTerrain(); 269 ReleaseGroundPlaneAndTerrain();
@@ -252,91 +271,6 @@ public sealed class BSTerrainManager
252 // I hate doing this, but just bail 271 // I hate doing this, but just bail
253 return; 272 return;
254 } 273 }
255
256 if (mapInfo.terrainBody.ptr != IntPtr.Zero)
257 {
258 // Updating an existing terrain.
259 DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,taint,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}",
260 BSScene.DetailLogZero, terrainRegionBase, mapInfo.minCoords, mapInfo.maxCoords, mapInfo.sizeX, mapInfo.sizeY);
261
262 // Remove from the dynamics world because we're going to mangle this object
263 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
264
265 // Get rid of the old terrain
266 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
267 BulletSimAPI.ReleaseHeightMapInfo2(mapInfo.Ptr);
268 mapInfo.Ptr = IntPtr.Zero;
269
270 /*
271 // NOTE: This routine is half here because I can't get the terrain shape replacement
272 // to work. In the short term, the above three lines completely delete the old
273 // terrain and the code below recreates one from scratch.
274 // Hopefully the Bullet community will help me out on this one.
275
276 // First, release the old collision shape (there is only one terrain)
277 BulletSimAPI.DeleteCollisionShape2(m_physicsScene.World.Ptr, mapInfo.terrainShape.Ptr);
278
279 // Fill the existing height map info with the new location and size information
280 BulletSimAPI.FillHeightMapInfo2(m_physicsScene.World.Ptr, mapInfo.Ptr, mapInfo.ID,
281 mapInfo.minCoords, mapInfo.maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN);
282
283 // Create a terrain shape based on the new info
284 mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr));
285
286 // Stuff the shape into the existing terrain body
287 BulletSimAPI.SetBodyShape2(m_physicsScene.World.Ptr, mapInfo.terrainBody.Ptr, mapInfo.terrainShape.Ptr);
288 */
289 }
290 // else
291 {
292 // Creating a new terrain.
293 DetailLog("{0},UpdateOrCreateTerrain:CreateNewTerrain,taint,baseX={1},baseY={2},minZ={3},maxZ={4}",
294 BSScene.DetailLogZero, mapInfo.minCoords.X, mapInfo.minCoords.Y, minZ, maxZ);
295
296 mapInfo.ID = id;
297 mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, mapInfo.ID,
298 mapInfo.minCoords, mapInfo.maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN);
299
300 // Create the terrain shape from the mapInfo
301 mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr),
302 ShapeData.PhysicsShapeType.SHAPE_TERRAIN);
303
304 // The terrain object initial position is at the center of the object
305 Vector3 centerPos;
306 centerPos.X = minCoords.X + (mapInfo.sizeX / 2f);
307 centerPos.Y = minCoords.Y + (mapInfo.sizeY / 2f);
308 centerPos.Z = minZ + ((maxZ - minZ) / 2f);
309
310 mapInfo.terrainBody = new BulletBody(mapInfo.ID,
311 BulletSimAPI.CreateBodyWithDefaultMotionState2(mapInfo.terrainShape.ptr,
312 id, centerPos, Quaternion.Identity));
313 }
314
315 // Make sure the entry is in the heightmap table
316 m_heightMaps[terrainRegionBase] = mapInfo;
317
318 // Set current terrain attributes
319 BulletSimAPI.SetFriction2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainFriction);
320 BulletSimAPI.SetHitFraction2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainHitFraction);
321 BulletSimAPI.SetRestitution2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainRestitution);
322 BulletSimAPI.SetCollisionFlags2(mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
323
324 // Return the new terrain to the world of physical objects
325 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
326
327 // redo its bounding box now that it is in the world
328 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
329
330 BulletSimAPI.SetCollisionFilterMask2(mapInfo.terrainBody.ptr,
331 (uint)CollisionFilterGroups.TerrainFilter,
332 (uint)CollisionFilterGroups.TerrainMask);
333
334 // Make sure the new shape is processed.
335 // BulletSimAPI.Activate2(mapInfo.terrainBody.ptr, true);
336 // BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.ISLAND_SLEEPING);
337 BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
338
339 m_terrainModified = true;
340 }); 274 });
341 } 275 }
342 else 276 else
@@ -353,34 +287,51 @@ public sealed class BSTerrainManager
353 Vector3 minCoordsX = minCoords; 287 Vector3 minCoordsX = minCoords;
354 Vector3 maxCoordsX = maxCoords; 288 Vector3 maxCoordsX = maxCoords;
355 289
356 DetailLog("{0},UpdateOrCreateTerrain:NewTerrain,call,id={1}, minC={2}, maxC={3}", 290 DetailLog("{0},UpdateTerrain:NewTerrain,call,id={1}, minC={2}, maxC={3}",
357 BSScene.DetailLogZero, newTerrainID, minCoords, minCoords); 291 BSScene.DetailLogZero, newTerrainID, minCoords, minCoords);
358 292
359 // Code that must happen at taint-time 293 // Code that must happen at taint-time
360 PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateOrCreateTerrain:NewTerrain", delegate() 294 PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateTerrain:NewTerrain", delegate()
361 { 295 {
362 DetailLog("{0},UpdateOrCreateTerrain:NewTerrain,taint,baseX={1},baseY={2}", BSScene.DetailLogZero, minCoords.X, minCoords.Y); 296 DetailLog("{0},UpdateTerrain:NewTerrain,taint,baseX={1},baseY={2}",
363 // Create a new mapInfo that will be filled with the new info 297 BSScene.DetailLogZero, minCoordsX.X, minCoordsX.Y);
364 mapInfo = new BulletHeightMapInfo(id, heightMapX, 298 BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
365 BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, newTerrainID, 299 m_terrains.Add(terrainRegionBase, newTerrainPhys);
366 minCoordsX, maxCoordsX, heightMapX, TERRAIN_COLLISION_MARGIN));
367 // Put the unfilled heightmap info into the collection of same
368 m_heightMaps.Add(terrainRegionBase, mapInfo);
369 // Build the terrain
370 UpdateOrCreateTerrain(newTerrainID, heightMap, minCoords, maxCoords, true);
371 300
372 m_terrainModified = true; 301 m_terrainModified = true;
373 }); 302 });
374 } 303 }
375 } 304 }
376 305
377 // Someday we will have complex terrain with caves and tunnels 306 // TODO: redo terrain implementation selection to allow other base types than heightMap.
378 public float GetTerrainHeightAtXYZ(Vector3 loc) 307 private BSTerrainPhys BuildPhysicalTerrain(Vector3 terrainRegionBase, uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords)
379 { 308 {
380 // For the moment, it's flat and convex 309 PhysicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}",
381 return GetTerrainHeightAtXY(loc.X, loc.Y); 310 LogHeader, PhysicsScene.RegionName, terrainRegionBase,
311 (BSTerrainPhys.TerrainImplementation)PhysicsScene.Params.terrainImplementation);
312 BSTerrainPhys newTerrainPhys = null;
313 switch ((int)PhysicsScene.Params.terrainImplementation)
314 {
315 case (int)BSTerrainPhys.TerrainImplementation.Heightmap:
316 newTerrainPhys = new BSTerrainHeightmap(PhysicsScene, terrainRegionBase, id,
317 heightMap, minCoords, maxCoords);
318 break;
319 case (int)BSTerrainPhys.TerrainImplementation.Mesh:
320 newTerrainPhys = new BSTerrainMesh(PhysicsScene, terrainRegionBase, id,
321 heightMap, minCoords, maxCoords);
322 break;
323 default:
324 PhysicsScene.Logger.ErrorFormat("{0} Bad terrain implementation specified. Type={1}/{2},Region={3}/{4}",
325 LogHeader,
326 (int)PhysicsScene.Params.terrainImplementation,
327 PhysicsScene.Params.terrainImplementation,
328 PhysicsScene.RegionName, terrainRegionBase);
329 break;
330 }
331 return newTerrainPhys;
382 } 332 }
383 333
334
384 // Given an X and Y, find the height of the terrain. 335 // Given an X and Y, find the height of the terrain.
385 // Since we could be handling multiple terrains for a mega-region, 336 // Since we could be handling multiple terrains for a mega-region,
386 // the base of the region is calcuated assuming all regions are 337 // the base of the region is calcuated assuming all regions are
@@ -390,8 +341,10 @@ public sealed class BSTerrainManager
390 private float lastHeightTX = 999999f; 341 private float lastHeightTX = 999999f;
391 private float lastHeightTY = 999999f; 342 private float lastHeightTY = 999999f;
392 private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT; 343 private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT;
393 private float GetTerrainHeightAtXY(float tX, float tY) 344 public float GetTerrainHeightAtXYZ(Vector3 loc)
394 { 345 {
346 float tX = loc.X;
347 float tY = loc.Y;
395 // You'd be surprized at the number of times this routine is called 348 // You'd be surprized at the number of times this routine is called
396 // with the same parameters as last time. 349 // with the same parameters as last time.
397 if (!m_terrainModified && lastHeightTX == tX && lastHeightTY == tY) 350 if (!m_terrainModified && lastHeightTX == tX && lastHeightTY == tY)
@@ -403,27 +356,14 @@ public sealed class BSTerrainManager
403 356
404 int offsetX = ((int)(tX / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X; 357 int offsetX = ((int)(tX / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X;
405 int offsetY = ((int)(tY / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y; 358 int offsetY = ((int)(tY / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y;
406 Vector2 terrainBaseXY = new Vector2(offsetX, offsetY); 359 Vector3 terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f);
407 360
408 BulletHeightMapInfo mapInfo; 361 BSTerrainPhys physTerrain;
409 if (m_heightMaps.TryGetValue(terrainBaseXY, out mapInfo)) 362 if (m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain))
410 { 363 {
411 float regionX = tX - offsetX; 364 ret = physTerrain.GetHeightAtXYZ(loc - terrainBaseXYZ);
412 float regionY = tY - offsetY; 365 DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXYZ,loc={1},base={2},height={3}",
413 int mapIndex = (int)regionY * (int)mapInfo.sizeY + (int)regionX; 366 BSScene.DetailLogZero, loc, terrainBaseXYZ, ret);
414 try
415 {
416 ret = mapInfo.heightMap[mapIndex];
417 }
418 catch
419 {
420 // Sometimes they give us wonky values of X and Y. Give a warning and return something.
421 PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, x={2}, y={3}",
422 LogHeader, terrainBaseXY, regionX, regionY);
423 ret = HEIGHT_GETHEIGHT_RET;
424 }
425 // DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXY,bX={1},baseY={2},szX={3},szY={4},regX={5},regY={6},index={7},ht={8}",
426 // BSScene.DetailLogZero, offsetX, offsetY, mapInfo.sizeX, mapInfo.sizeY, regionX, regionY, mapIndex, ret);
427 } 367 }
428 else 368 else
429 { 369 {
@@ -466,7 +406,7 @@ public sealed class BSTerrainManager
466 // Unhook all the combining that I know about. 406 // Unhook all the combining that I know about.
467 public void UnCombine(PhysicsScene pScene) 407 public void UnCombine(PhysicsScene pScene)
468 { 408 {
469 // Just like ODE, for the moment a NOP 409 // Just like ODE, we don't do anything yet.
470 DetailLog("{0},BSTerrainManager.UnCombine", BSScene.DetailLogZero); 410 DetailLog("{0},BSTerrainManager.UnCombine", BSScene.DetailLogZero);
471 } 411 }
472 412
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs
new file mode 100755
index 0000000..3279b6f
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs
@@ -0,0 +1,256 @@
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;
30
31using OpenSim.Framework;
32using OpenSim.Region.Framework;
33using OpenSim.Region.CoreModules;
34using OpenSim.Region.Physics.Manager;
35
36using Nini.Config;
37using log4net;
38
39using OpenMetaverse;
40
41namespace OpenSim.Region.Physics.BulletSPlugin
42{
43public sealed class BSTerrainMesh : BSTerrainPhys
44{
45 static string LogHeader = "[BULLETSIM TERRAIN MESH]";
46
47 private float[] m_savedHeightMap;
48 int m_sizeX;
49 int m_sizeY;
50
51 BulletShape m_terrainShape;
52 BulletBody m_terrainBody;
53
54 public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize)
55 : base(physicsScene, regionBase, id)
56 {
57 }
58
59 public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id /* parameters for making mesh */)
60 : base(physicsScene, regionBase, id)
61 {
62 }
63
64 // Create terrain mesh from a heightmap.
65 public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap,
66 Vector3 minCoords, Vector3 maxCoords)
67 : base(physicsScene, regionBase, id)
68 {
69 int indicesCount;
70 int[] indices;
71 int verticesCount;
72 float[] vertices;
73
74 m_savedHeightMap = initialMap;
75
76 m_sizeX = (int)(maxCoords.X - minCoords.X);
77 m_sizeY = (int)(maxCoords.Y - minCoords.Y);
78
79 if (!BSTerrainMesh.ConvertHeightmapToMesh(PhysicsScene, initialMap,
80 m_sizeX, m_sizeY,
81 (float)m_sizeX, (float)m_sizeY,
82 Vector3.Zero, 1.0f,
83 out indicesCount, out indices, out verticesCount, out vertices))
84 {
85 // DISASTER!!
86 PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap", ID);
87 PhysicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh! base={1}", LogHeader, TerrainBase);
88 // Something is very messed up and a crash is in our future.
89 return;
90 }
91
92 m_terrainShape = new BulletShape(BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
93 indicesCount, indices, verticesCount, vertices),
94 PhysicsShapeType.SHAPE_MESH);
95 if (m_terrainShape.ptr == IntPtr.Zero)
96 {
97 // DISASTER!!
98 PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape", ID);
99 physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase);
100 // Something is very messed up and a crash is in our future.
101 return;
102 }
103
104 Vector3 pos = regionBase;
105 Quaternion rot = Quaternion.Identity;
106
107 m_terrainBody = new BulletBody(id, BulletSimAPI.CreateBodyWithDefaultMotionState2( m_terrainShape.ptr, ID, pos, rot));
108 if (m_terrainBody.ptr == IntPtr.Zero)
109 {
110 // DISASTER!!
111 physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase);
112 // Something is very messed up and a crash is in our future.
113 return;
114 }
115
116 // Set current terrain attributes
117 BulletSimAPI.SetFriction2(m_terrainBody.ptr, PhysicsScene.Params.terrainFriction);
118 BulletSimAPI.SetHitFraction2(m_terrainBody.ptr, PhysicsScene.Params.terrainHitFraction);
119 BulletSimAPI.SetRestitution2(m_terrainBody.ptr, PhysicsScene.Params.terrainRestitution);
120 BulletSimAPI.SetCollisionFlags2(m_terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
121
122 // Static objects are not very massive.
123 BulletSimAPI.SetMassProps2(m_terrainBody.ptr, 0f, Vector3.Zero);
124
125 // Return the new terrain to the world of physical objects
126 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr);
127
128 // redo its bounding box now that it is in the world
129 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_terrainBody.ptr);
130
131 BulletSimAPI.SetCollisionFilterMask2(m_terrainBody.ptr,
132 (uint)CollisionFilterGroups.TerrainFilter,
133 (uint)CollisionFilterGroups.TerrainMask);
134
135 // Make it so the terrain will not move or be considered for movement.
136 BulletSimAPI.ForceActivationState2(m_terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
137 }
138
139 public override void Dispose()
140 {
141 if (m_terrainBody.ptr != IntPtr.Zero)
142 {
143 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr);
144 // Frees both the body and the shape.
145 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_terrainBody.ptr);
146 }
147 }
148
149 public override float GetHeightAtXYZ(Vector3 pos)
150 {
151 // 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 float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
154
155 int mapIndex = (int)pos.Y * m_sizeY + (int)pos.X;
156 try
157 {
158 ret = m_savedHeightMap[mapIndex];
159 }
160 catch
161 {
162 // Sometimes they give us wonky values of X and Y. Give a warning and return something.
163 PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}",
164 LogHeader, TerrainBase, pos);
165 ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
166 }
167 return ret;
168 }
169
170 // Convert the passed heightmap to mesh information suitable for CreateMeshShape2().
171 // Return 'true' if successfully created.
172 public static bool ConvertHeightmapToMesh(
173 BSScene physicsScene,
174 float[] heightMap, int sizeX, int sizeY, // parameters of incoming heightmap
175 float extentX, float extentY, // zero based range for output vertices
176 Vector3 extentBase, // base to be added to all vertices
177 float magnification, // number of vertices to create between heightMap coords
178 out int indicesCountO, out int[] indicesO,
179 out int verticesCountO, out float[] verticesO)
180 {
181 bool ret = false;
182
183 int indicesCount = 0;
184 int verticesCount = 0;
185 int[] indices = new int[0];
186 float[] vertices = new float[0];
187
188 // Simple mesh creation which assumes magnification == 1.
189 // TODO: do a more general solution that scales, adds new vertices and smoothes the result.
190
191 try
192 {
193 // One vertice per heightmap value plus the vertices off the top and bottom edge.
194 int totalVertices = (sizeX + 1) * (sizeY + 1);
195 vertices = new float[totalVertices * 3];
196 int totalIndices = sizeX * sizeY * 6;
197 indices = new int[totalIndices];
198
199 float magX = (float)sizeX / extentX;
200 float magY = (float)sizeY / extentY;
201 physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,totVert={1},totInd={2},extentBase={3},magX={4},magY={5}",
202 BSScene.DetailLogZero, totalVertices, totalIndices, extentBase, magX, magY);
203 // 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++)
205 {
206 for (int xx = 0; xx <= sizeX; xx++) // Hint: the "<=" means we got through sizeX + 1 times
207 {
208 int offset = yy * sizeX + xx;
209 // Extend the height from the height from the last row or column
210 if (yy == sizeY) offset -= sizeX;
211 if (xx == sizeX) offset -= 1;
212 float height = heightMap[offset];
213 vertices[verticesCount + 0] = (float)xx * magX + extentBase.X;
214 vertices[verticesCount + 1] = (float)yy * magY + extentBase.Y;
215 vertices[verticesCount + 2] = height + extentBase.Z;
216 verticesCount += 3;
217 }
218 }
219 verticesCount = verticesCount / 3;
220 physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,completeVerts,verCount={1}",
221 BSScene.DetailLogZero, verticesCount);
222
223 for (int yy = 0; yy < sizeY; yy++)
224 {
225 for (int xx = 0; xx < sizeX; xx++)
226 {
227 int offset = yy * sizeX + xx;
228 // Each vertices is presumed to be the upper left corner of a box of two triangles
229 indices[indicesCount + 0] = offset;
230 indices[indicesCount + 1] = offset + 1;
231 indices[indicesCount + 2] = offset + sizeX + 1; // accounting for the extra column
232 indices[indicesCount + 3] = offset + 1;
233 indices[indicesCount + 4] = offset + sizeX + 2;
234 indices[indicesCount + 5] = offset + sizeX + 1;
235 indicesCount += 6;
236 }
237 }
238 physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,completeIndices,indCount={1}", // DEEBUG DEBUG DEBUG
239 LogHeader, indicesCount); // DEBUG
240 ret = true;
241 }
242 catch (Exception e)
243 {
244 physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh. For={1}/{2}, e={3}",
245 LogHeader, physicsScene.RegionName, extentBase, e);
246 }
247
248 indicesCountO = indicesCount;
249 indicesO = indices;
250 verticesCountO = verticesCount;
251 verticesO = vertices;
252
253 return ret;
254 }
255}
256}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
index 28fae13..4647c2d 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
@@ -88,11 +88,11 @@ public struct BulletShape
88 public BulletShape(IntPtr xx) 88 public BulletShape(IntPtr xx)
89 { 89 {
90 ptr = xx; 90 ptr = xx;
91 type=ShapeData.PhysicsShapeType.SHAPE_UNKNOWN; 91 type=PhysicsShapeType.SHAPE_UNKNOWN;
92 shapeKey = 0; 92 shapeKey = (System.UInt64)FixedShapeKey.KEY_NONE;
93 isNativeShape = false; 93 isNativeShape = false;
94 } 94 }
95 public BulletShape(IntPtr xx, ShapeData.PhysicsShapeType typ) 95 public BulletShape(IntPtr xx, PhysicsShapeType typ)
96 { 96 {
97 ptr = xx; 97 ptr = xx;
98 type = typ; 98 type = typ;
@@ -100,7 +100,7 @@ public struct BulletShape
100 isNativeShape = false; 100 isNativeShape = false;
101 } 101 }
102 public IntPtr ptr; 102 public IntPtr ptr;
103 public ShapeData.PhysicsShapeType type; 103 public PhysicsShapeType type;
104 public System.UInt64 shapeKey; 104 public System.UInt64 shapeKey;
105 public bool isNativeShape; 105 public bool isNativeShape;
106 public override string ToString() 106 public override string ToString()
@@ -152,7 +152,7 @@ public class BulletHeightMapInfo
152 ID = id; 152 ID = id;
153 Ptr = xx; 153 Ptr = xx;
154 heightMap = hm; 154 heightMap = hm;
155 terrainRegionBase = new Vector2(0f, 0f); 155 terrainRegionBase = Vector3.Zero;
156 minCoords = new Vector3(100f, 100f, 25f); 156 minCoords = new Vector3(100f, 100f, 25f);
157 maxCoords = new Vector3(101f, 101f, 26f); 157 maxCoords = new Vector3(101f, 101f, 26f);
158 minZ = maxZ = 0f; 158 minZ = maxZ = 0f;
@@ -161,7 +161,7 @@ public class BulletHeightMapInfo
161 public uint ID; 161 public uint ID;
162 public IntPtr Ptr; 162 public IntPtr Ptr;
163 public float[] heightMap; 163 public float[] heightMap;
164 public Vector2 terrainRegionBase; 164 public Vector3 terrainRegionBase;
165 public Vector3 minCoords; 165 public Vector3 minCoords;
166 public Vector3 maxCoords; 166 public Vector3 maxCoords;
167 public float sizeX, sizeY; 167 public float sizeX, sizeY;
@@ -178,24 +178,37 @@ public struct ConvexHull
178 int VertexCount; 178 int VertexCount;
179 Vector3[] Vertices; 179 Vector3[] Vertices;
180} 180}
181public enum PhysicsShapeType
182{
183 SHAPE_UNKNOWN = 0,
184 SHAPE_CAPSULE = 1,
185 SHAPE_BOX = 2,
186 SHAPE_CONE = 3,
187 SHAPE_CYLINDER = 4,
188 SHAPE_SPHERE = 5,
189 SHAPE_MESH = 6,
190 SHAPE_HULL = 7,
191 // following defined by BulletSim
192 SHAPE_GROUNDPLANE = 20,
193 SHAPE_TERRAIN = 21,
194 SHAPE_COMPOUND = 22,
195 SHAPE_HEIGHTMAP = 23,
196};
197
198// The native shapes have predefined shape hash keys
199public enum FixedShapeKey : ulong
200{
201 KEY_NONE = 0,
202 KEY_BOX = 1,
203 KEY_SPHERE = 2,
204 KEY_CONE = 3,
205 KEY_CYLINDER = 4,
206 KEY_CAPSULE = 5,
207}
208
181[StructLayout(LayoutKind.Sequential)] 209[StructLayout(LayoutKind.Sequential)]
182public struct ShapeData 210public struct ShapeData
183{ 211{
184 public enum PhysicsShapeType
185 {
186 SHAPE_UNKNOWN = 0,
187 SHAPE_AVATAR = 1,
188 SHAPE_BOX = 2,
189 SHAPE_CONE = 3,
190 SHAPE_CYLINDER = 4,
191 SHAPE_SPHERE = 5,
192 SHAPE_MESH = 6,
193 SHAPE_HULL = 7,
194 // following defined by BulletSim
195 SHAPE_GROUNDPLANE = 20,
196 SHAPE_TERRAIN = 21,
197 SHAPE_COMPOUND = 22,
198 };
199 public uint ID; 212 public uint ID;
200 public PhysicsShapeType Type; 213 public PhysicsShapeType Type;
201 public Vector3 Position; 214 public Vector3 Position;
@@ -216,16 +229,6 @@ public struct ShapeData
216 // note that bools are passed as floats since bool size changes by language and architecture 229 // note that bools are passed as floats since bool size changes by language and architecture
217 public const float numericTrue = 1f; 230 public const float numericTrue = 1f;
218 public const float numericFalse = 0f; 231 public const float numericFalse = 0f;
219
220 // The native shapes have predefined shape hash keys
221 public enum FixedShapeKey : ulong
222 {
223 KEY_BOX = 1,
224 KEY_SPHERE = 2,
225 KEY_CONE = 3,
226 KEY_CYLINDER = 4,
227 KEY_CAPSULE = 5,
228 }
229} 232}
230[StructLayout(LayoutKind.Sequential)] 233[StructLayout(LayoutKind.Sequential)]
231public struct SweepHit 234public struct SweepHit
@@ -280,6 +283,7 @@ public struct ConfigurationParameters
280 public float ccdSweptSphereRadius; 283 public float ccdSweptSphereRadius;
281 public float contactProcessingThreshold; 284 public float contactProcessingThreshold;
282 285
286 public float terrainImplementation;
283 public float terrainFriction; 287 public float terrainFriction;
284 public float terrainHitFraction; 288 public float terrainHitFraction;
285 public float terrainRestitution; 289 public float terrainRestitution;
@@ -287,7 +291,8 @@ public struct ConfigurationParameters
287 public float avatarStandingFriction; 291 public float avatarStandingFriction;
288 public float avatarDensity; 292 public float avatarDensity;
289 public float avatarRestitution; 293 public float avatarRestitution;
290 public float avatarCapsuleRadius; 294 public float avatarCapsuleWidth;
295 public float avatarCapsuleDepth;
291 public float avatarCapsuleHeight; 296 public float avatarCapsuleHeight;
292 public float avatarContactProcessingThreshold; 297 public float avatarContactProcessingThreshold;
293 298
@@ -388,13 +393,13 @@ public enum CollisionFilterGroups : uint
388 ObjectFilter = BSolidFilter, 393 ObjectFilter = BSolidFilter,
389 ObjectMask = BAllFilter, 394 ObjectMask = BAllFilter,
390 StaticObjectFilter = BStaticFilter, 395 StaticObjectFilter = BStaticFilter,
391 StaticObjectMask = BAllFilter, 396 StaticObjectMask = BAllFilter & ~BStaticFilter, // static objects don't collide with each other
392 LinksetFilter = BLinksetFilter, 397 LinksetFilter = BLinksetFilter,
393 LinksetMask = BAllFilter & ~BLinksetFilter, 398 LinksetMask = BAllFilter & ~BLinksetFilter, // linkset objects don't collide with each other
394 VolumeDetectFilter = BSensorTrigger, 399 VolumeDetectFilter = BSensorTrigger,
395 VolumeDetectMask = ~BSensorTrigger, 400 VolumeDetectMask = ~BSensorTrigger,
396 TerrainFilter = BTerrainFilter, 401 TerrainFilter = BTerrainFilter,
397 TerrainMask = BAllFilter & ~BStaticFilter, 402 TerrainMask = BAllFilter & ~BStaticFilter, // static objects on the ground don't collide
398 GroundPlaneFilter = BGroundPlaneFilter, 403 GroundPlaneFilter = BGroundPlaneFilter,
399 GroundPlaneMask = BAllFilter 404 GroundPlaneMask = BAllFilter
400 405