Merge branch 'master' into careminster

avinationmerge
Melanie 2012-11-22 03:17:33 +00:00
commit c4c6121752
21 changed files with 892 additions and 324 deletions

View File

@ -132,6 +132,11 @@
</exec> </exec>
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.capabilities.handlers.tests)==0}" /> <fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.capabilities.handlers.tests)==0}" />
<exec program="${nunitcmd}" failonerror="true" resultproperty="testresult.opensim.server.handlers.tests">
<arg value="./bin/OpenSim.Server.Handlers.Tests.dll" />
</exec>
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.server.handlers.tests)==0}" />
<exec program="${nunitcmd}" failonerror="true" resultproperty="testresult.opensim.services.inventoryservice.tests"> <exec program="${nunitcmd}" failonerror="true" resultproperty="testresult.opensim.services.inventoryservice.tests">
<arg value="./bin/OpenSim.Services.InventoryService.Tests.dll" /> <arg value="./bin/OpenSim.Services.InventoryService.Tests.dll" />
</exec> </exec>
@ -240,6 +245,11 @@
<arg value="-xml=test-results/OpenSim.Capabilities.Handlers.Tests.dll-Results.xml" /> <arg value="-xml=test-results/OpenSim.Capabilities.Handlers.Tests.dll-Results.xml" />
</exec> </exec>
<exec program="${nunitcmd}" failonerror="false" resultproperty="testresult.opensim.server.handlers.tests">
<arg value="./bin/OpenSim.Server.Handlers.Tests.dll" />
<arg value="-xml=test-results/OpenSim.Server.Handlers.Tests.dll-Results.xml" />
</exec>
<exec program="${nunitcmd}" failonerror="false" resultproperty="testresult.opensim.services.inventoryservice.tests"> <exec program="${nunitcmd}" failonerror="false" resultproperty="testresult.opensim.services.inventoryservice.tests">
<arg value="./bin/OpenSim.Services.InventoryService.Tests.dll" /> <arg value="./bin/OpenSim.Services.InventoryService.Tests.dll" />
<arg value="-xml=test-results/OpenSim.Services.InventoryService.Tests.dll-Results.xml" /> <arg value="-xml=test-results/OpenSim.Services.InventoryService.Tests.dll-Results.xml" />

View File

@ -82,7 +82,13 @@ public sealed class BSCharacter : BSPhysObject
{ {
_physicsActorType = (int)ActorTypes.Agent; _physicsActorType = (int)ActorTypes.Agent;
_position = pos; _position = pos;
// Old versions of ScenePresence passed only the height. If width and/or depth are zero,
// replace with the default values.
_size = size; _size = size;
if (_size.X == 0f) _size.X = PhysicsScene.Params.avatarCapsuleDepth;
if (_size.Y == 0f) _size.Y = PhysicsScene.Params.avatarCapsuleWidth;
_flying = isFlying; _flying = isFlying;
_orientation = OMV.Quaternion.Identity; _orientation = OMV.Quaternion.Identity;
_velocity = OMV.Vector3.Zero; _velocity = OMV.Vector3.Zero;
@ -175,8 +181,7 @@ public sealed class BSCharacter : BSPhysObject
get get
{ {
// Avatar capsule size is kept in the scale parameter. // Avatar capsule size is kept in the scale parameter.
// return _size; return _size;
return new OMV.Vector3(Scale.X * 2f, Scale.Y * 2f, Scale.Z);
} }
set { set {
@ -184,8 +189,8 @@ public sealed class BSCharacter : BSPhysObject
_size = value; _size = value;
ComputeAvatarScale(_size); ComputeAvatarScale(_size);
ComputeAvatarVolumeAndMass(); ComputeAvatarVolumeAndMass();
DetailLog("{0},BSCharacter.setSize,call,scale={1},density={2},volume={3},mass={4}", DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}",
LocalID, Scale, _avatarDensity, _avatarVolume, RawMass); LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass);
PhysicsScene.TaintedObject("BSCharacter.setSize", delegate() PhysicsScene.TaintedObject("BSCharacter.setSize", delegate()
{ {
@ -203,9 +208,9 @@ public sealed class BSCharacter : BSPhysObject
set { BaseShape = value; } set { BaseShape = value; }
} }
// I want the physics engine to make an avatar capsule // I want the physics engine to make an avatar capsule
public override ShapeData.PhysicsShapeType PreferredPhysicalShape public override PhysicsShapeType PreferredPhysicalShape
{ {
get {return ShapeData.PhysicsShapeType.SHAPE_AVATAR; } get {return PhysicsShapeType.SHAPE_CAPSULE; }
} }
public override bool Grabbed { public override bool Grabbed {
@ -614,13 +619,19 @@ public sealed class BSCharacter : BSPhysObject
// The 'size' given by the simulator is the mid-point of the avatar // The 'size' given by the simulator is the mid-point of the avatar
// and X and Y are unspecified. // and X and Y are unspecified.
OMV.Vector3 newScale = OMV.Vector3.Zero; OMV.Vector3 newScale = size;
newScale.X = PhysicsScene.Params.avatarCapsuleRadius; // newScale.X = PhysicsScene.Params.avatarCapsuleWidth;
newScale.Y = PhysicsScene.Params.avatarCapsuleRadius; // newScale.Y = PhysicsScene.Params.avatarCapsuleDepth;
// From the total height, remove the capsule half spheres that are at each end // From the total height, remove the capsule half spheres that are at each end
newScale.Z = size.Z - (newScale.X + newScale.Y); // The 1.15f came from ODE. Not sure what this factors in.
Scale = newScale; // newScale.Z = (size.Z * 1.15f) - (newScale.X + newScale.Y);
// The total scale height is the central cylindar plus the caps on the two ends.
newScale.Z = size.Z + (Math.Min(size.X, size.Y) * 2f);
// Convert diameters to radii and height to half height -- the way Bullet expects it.
Scale = newScale / 2f;
} }
// set _avatarVolume and _mass based on capsule size, _density and Scale // set _avatarVolume and _mass based on capsule size, _density and Scale

View File

@ -82,9 +82,9 @@ public abstract class BSLinkset
// Some linksets have a preferred physical shape. // Some linksets have a preferred physical shape.
// Returns SHAPE_UNKNOWN if there is no preference. Causes the correct shape to be selected. // Returns SHAPE_UNKNOWN if there is no preference. Causes the correct shape to be selected.
public virtual ShapeData.PhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor) public virtual PhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
{ {
return ShapeData.PhysicsShapeType.SHAPE_UNKNOWN; return PhysicsShapeType.SHAPE_UNKNOWN;
} }
// Linksets move around the children so the linkset might need to compute the child position // Linksets move around the children so the linkset might need to compute the child position

View File

@ -42,12 +42,12 @@ public sealed class BSLinksetCompound : BSLinkset
} }
// For compound implimented linksets, if there are children, use compound shape for the root. // For compound implimented linksets, if there are children, use compound shape for the root.
public override ShapeData.PhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor) public override PhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
{ {
ShapeData.PhysicsShapeType ret = ShapeData.PhysicsShapeType.SHAPE_UNKNOWN; PhysicsShapeType ret = PhysicsShapeType.SHAPE_UNKNOWN;
if (IsRoot(requestor) && HasAnyChildren) if (IsRoot(requestor) && HasAnyChildren)
{ {
ret = ShapeData.PhysicsShapeType.SHAPE_COMPOUND; ret = PhysicsShapeType.SHAPE_COMPOUND;
} }
// DetailLog("{0},BSLinksetCompound.PreferredPhysicalShape,call,shape={1}", LinksetRoot.LocalID, ret); // DetailLog("{0},BSLinksetCompound.PreferredPhysicalShape,call,shape={1}", LinksetRoot.LocalID, ret);
return ret; return ret;

View File

@ -94,9 +94,9 @@ public abstract class BSPhysObject : PhysicsActor
public PrimitiveBaseShape BaseShape { get; protected set; } public PrimitiveBaseShape BaseShape { get; protected set; }
// Some types of objects have preferred physical representations. // Some types of objects have preferred physical representations.
// Returns SHAPE_UNKNOWN if there is no preference. // Returns SHAPE_UNKNOWN if there is no preference.
public virtual ShapeData.PhysicsShapeType PreferredPhysicalShape public virtual PhysicsShapeType PreferredPhysicalShape
{ {
get { return ShapeData.PhysicsShapeType.SHAPE_UNKNOWN; } get { return PhysicsShapeType.SHAPE_UNKNOWN; }
} }
// When the physical properties are updated, an EntityProperty holds the update values. // When the physical properties are updated, an EntityProperty holds the update values.

View File

@ -47,7 +47,6 @@ public sealed class BSPrim : BSPhysObject
// _size is what the user passed. Scale is what we pass to the physics engine with the mesh. // _size is what the user passed. Scale is what we pass to the physics engine with the mesh.
// Often Scale is unity because the meshmerizer will apply _size when creating the mesh. // Often Scale is unity because the meshmerizer will apply _size when creating the mesh.
private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user
// private OMV.Vector3 _scale; // the multiplier for each mesh dimension for the mesh as created by the meshmerizer
private bool _grabbed; private bool _grabbed;
private bool _isSelected; private bool _isSelected;
@ -94,7 +93,7 @@ public sealed class BSPrim : BSPhysObject
_physicsActorType = (int)ActorTypes.Prim; _physicsActorType = (int)ActorTypes.Prim;
_position = pos; _position = pos;
_size = size; _size = size;
Scale = new OMV.Vector3(1f, 1f, 1f); // the scale will be set by CreateGeom depending on object type Scale = size; // the scale will be set by CreateGeom depending on object type
_orientation = rotation; _orientation = rotation;
_buoyancy = 1f; _buoyancy = 1f;
_velocity = OMV.Vector3.Zero; _velocity = OMV.Vector3.Zero;
@ -155,6 +154,8 @@ public sealed class BSPrim : BSPhysObject
public override OMV.Vector3 Size { public override OMV.Vector3 Size {
get { return _size; } get { return _size; }
set { set {
// We presume the scale and size are the same. If scale must be changed for
// the physical shape, that is done when the geometry is built.
_size = value; _size = value;
ForceBodyShapeRebuild(false); ForceBodyShapeRebuild(false);
} }
@ -170,7 +171,7 @@ public sealed class BSPrim : BSPhysObject
} }
} }
// Whatever the linkset wants is what I want. // Whatever the linkset wants is what I want.
public override ShapeData.PhysicsShapeType PreferredPhysicalShape public override PhysicsShapeType PreferredPhysicalShape
{ get { return Linkset.PreferredPhysicalShape(this); } } { get { return Linkset.PreferredPhysicalShape(this); } }
public override bool ForceBodyShapeRebuild(bool inTaintTime) public override bool ForceBodyShapeRebuild(bool inTaintTime)
@ -274,19 +275,19 @@ public sealed class BSPrim : BSPhysObject
if (!Linkset.IsRoot(this)) if (!Linkset.IsRoot(this))
_position = Linkset.Position(this); _position = Linkset.Position(this);
// don't do the GetObjectPosition for root elements because this function is called a zillion times // don't do the GetObjectPosition for root elements because this function is called a zillion times.
// _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr); // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr);
return _position; return _position;
} }
set { set {
// If you must push the position into the physics engine, use ForcePosition. // If the position must be forced into the physics engine, use ForcePosition.
if (_position == value) if (_position == value)
{ {
return; return;
} }
_position = value; _position = value;
// TODO: what does it mean to set the position of a child prim?? Rebuild the constraint? // TODO: what does it mean to set the position of a child prim?? Rebuild the constraint?
PositionSanityCheck(); PositionSanityCheck(false);
PhysicsScene.TaintedObject("BSPrim.setPosition", delegate() PhysicsScene.TaintedObject("BSPrim.setPosition", delegate()
{ {
// DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); // DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
@ -302,7 +303,7 @@ public sealed class BSPrim : BSPhysObject
} }
set { set {
_position = value; _position = value;
PositionSanityCheck(); // PositionSanityCheck(); // Don't do this! Causes a loop and caller should know better.
BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
ActivateIfPhysical(false); ActivateIfPhysical(false);
} }
@ -311,52 +312,43 @@ public sealed class BSPrim : BSPhysObject
// Check that the current position is sane and, if not, modify the position to make it so. // Check that the current position is sane and, if not, modify the position to make it so.
// Check for being below terrain and being out of bounds. // Check for being below terrain and being out of bounds.
// Returns 'true' of the position was made sane by some action. // Returns 'true' of the position was made sane by some action.
private bool PositionSanityCheck() private bool PositionSanityCheck(bool inTaintTime)
{ {
bool ret = false; bool ret = false;
// If totally below the ground, move the prim up
// TODO: figure out the right solution for this... only for dynamic objects?
/*
float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
OMV.Vector3 upForce = OMV.Vector3.Zero;
if (Position.Z < terrainHeight) if (Position.Z < terrainHeight)
{ {
DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight); DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight);
_position.Z = terrainHeight + 2.0f; float targetHeight = terrainHeight + (Size.Z / 2f);
// Upforce proportional to the distance away from the terrain. Correct the error in 1 sec.
upForce.Z = (terrainHeight - Position.Z) * 1f;
ret = true; ret = true;
} }
*/
if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
{ {
float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position); float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position);
// TODO: a floating motor so object will bob in the water // TODO: a floating motor so object will bob in the water
if (Position.Z < waterHeight) if (Math.Abs(Position.Z - waterHeight) > 0.1f)
{ {
_position.Z = waterHeight; // Upforce proportional to the distance away from the water. Correct the error in 1 sec.
upForce.Z = (waterHeight - Position.Z) * 1f;
ret = true; ret = true;
} }
} }
// TODO: check for out of bounds // TODO: check for out of bounds
return ret;
}
// A version of the sanity check that also makes sure a new position value is // The above code computes a force to apply to correct any out-of-bounds problems. Apply same.
// pushed to the physics engine. This routine would be used by anyone if (ret)
// who is not already pushing the value.
private bool PositionSanityCheck(bool inTaintTime)
{ {
bool ret = false; PhysicsScene.TaintedObject(inTaintTime, "BSPrim.PositionSanityCheck:belowTerrain", delegate()
if (PositionSanityCheck())
{ {
// The new position value must be pushed into the physics engine but we can't // Apply upforce and overcome gravity.
// just assign to "Position" because of potential call loops. ForceVelocity = ForceVelocity + upForce - PhysicsScene.DefaultGravity;
PhysicsScene.TaintedObject(inTaintTime, "BSPrim.PositionSanityCheck", delegate()
{
DetailLog("{0},BSPrim.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
ForcePosition = _position;
}); });
ret = true;
} }
return ret; return ret;
} }
@ -940,6 +932,7 @@ public sealed class BSPrim : BSPhysObject
public override void AddForce(OMV.Vector3 force, bool pushforce) { public override void AddForce(OMV.Vector3 force, bool pushforce) {
AddForce(force, pushforce, false); AddForce(force, pushforce, false);
} }
// Applying a force just adds this to the total force on the object.
public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) {
// for an object, doesn't matter if force is a pushforce or not // for an object, doesn't matter if force is a pushforce or not
if (force.IsFinite()) if (force.IsFinite())
@ -971,6 +964,7 @@ public sealed class BSPrim : BSPhysObject
}); });
} }
// An impulse force is scaled by the mass of the object.
public void ApplyForceImpulse(OMV.Vector3 impulse, bool inTaintTime) public void ApplyForceImpulse(OMV.Vector3 impulse, bool inTaintTime)
{ {
OMV.Vector3 applyImpulse = impulse; OMV.Vector3 applyImpulse = impulse;
@ -1423,7 +1417,7 @@ public sealed class BSPrim : BSPhysObject
if (changed != 0) if (changed != 0)
{ {
// Only update the position of single objects and linkset roots // Only update the position of single objects and linkset roots
if (this._parentPrim == null) if (Linkset.IsRoot(this))
{ {
base.RequestPhysicsterseUpdate(); base.RequestPhysicsterseUpdate();
} }
@ -1435,19 +1429,24 @@ public sealed class BSPrim : BSPhysObject
// Updates only for individual prims and for the root object of a linkset. // Updates only for individual prims and for the root object of a linkset.
if (Linkset.IsRoot(this)) if (Linkset.IsRoot(this))
{ {
// Assign to the local variables so the normal set action does not happen // Assign directly to the local variables so the normal set action does not happen
_position = entprop.Position; _position = entprop.Position;
_orientation = entprop.Rotation; _orientation = entprop.Rotation;
_velocity = entprop.Velocity; _velocity = entprop.Velocity;
_acceleration = entprop.Acceleration; _acceleration = entprop.Acceleration;
_rotationalVelocity = entprop.RotationalVelocity; _rotationalVelocity = entprop.RotationalVelocity;
// The sanity check can change the velocity and/or position.
if (PositionSanityCheck(true))
{
entprop.Position = _position;
entprop.Velocity = _velocity;
}
// remember the current and last set values // remember the current and last set values
LastEntityProperties = CurrentEntityProperties; LastEntityProperties = CurrentEntityProperties;
CurrentEntityProperties = entprop; CurrentEntityProperties = entprop;
PositionSanityCheck(true);
OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation; OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation;
DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}", DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}",
LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity); LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity);

View File

@ -712,7 +712,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
// here just before the physics engine is called to step the simulation. // here just before the physics engine is called to step the simulation.
public void ProcessTaints() public void ProcessTaints()
{ {
InTaintTime = true; InTaintTime = true; // Only used for debugging so locking is not necessary.
ProcessRegularTaints(); ProcessRegularTaints();
ProcessPostTaintTaints(); ProcessPostTaintTaints();
InTaintTime = false; InTaintTime = false;
@ -758,6 +758,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
DetailLog("{0},BSScene.ProcessTaints,leftTaintsOnList,numNotProcessed={1}", DetailLogZero, _taintOperations.Count); DetailLog("{0},BSScene.ProcessTaints,leftTaintsOnList,numNotProcessed={1}", DetailLogZero, _taintOperations.Count);
} }
*/ */
// swizzle a new list into the list location so we can process what's there // swizzle a new list into the list location so we can process what's there
List<TaintCallbackEntry> oldList; List<TaintCallbackEntry> oldList;
lock (_taintLock) lock (_taintLock)
@ -787,8 +788,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
// will replace any previous operation by the same object. // will replace any previous operation by the same object.
public void PostTaintObject(String ident, uint ID, TaintCallback callback) public void PostTaintObject(String ident, uint ID, TaintCallback callback)
{ {
if (!m_initialized) return;
string uniqueIdent = ident + "-" + ID.ToString(); string uniqueIdent = ident + "-" + ID.ToString();
lock (_taintLock) lock (_taintLock)
{ {
@ -864,13 +863,14 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
} }
} }
// Only used for debugging. Does not change state of anything so locking is not necessary.
public bool AssertInTaintTime(string whereFrom) public bool AssertInTaintTime(string whereFrom)
{ {
if (!InTaintTime) if (!InTaintTime)
{ {
DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom); DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom);
m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom); m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom);
Util.PrintCallStack(); Util.PrintCallStack(); // Prints the stack into the DEBUG log file.
} }
return InTaintTime; return InTaintTime;
} }
@ -1145,6 +1145,11 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); }, (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); },
(s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.PhysBody.ptr, v); } ), (s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.PhysBody.ptr, v); } ),
new ParameterDefn("TerrainImplementation", "Type of shape to use for terrain (0=heightmap, 1=mesh)",
(float)BSTerrainPhys.TerrainImplementation.Mesh,
(s,cf,p,v) => { s.m_params[0].terrainImplementation = cf.GetFloat(p,v); },
(s) => { return s.m_params[0].terrainImplementation; },
(s,p,l,v) => { s.m_params[0].terrainImplementation = v; } ),
new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" ,
0.5f, 0.5f,
(s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); },
@ -1180,11 +1185,16 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
(s,cf,p,v) => { s.m_params[0].avatarRestitution = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].avatarRestitution = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].avatarRestitution; }, (s) => { return s.m_params[0].avatarRestitution; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarRestitution, p, l, v); } ), (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarRestitution, p, l, v); } ),
new ParameterDefn("AvatarCapsuleRadius", "Radius of space around an avatar", new ParameterDefn("AvatarCapsuleWidth", "The distance between the sides of the avatar capsule",
0.37f, 0.6f,
(s,cf,p,v) => { s.m_params[0].avatarCapsuleRadius = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].avatarCapsuleWidth = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].avatarCapsuleRadius; }, (s) => { return s.m_params[0].avatarCapsuleWidth; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleRadius, p, l, v); } ), (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleWidth, p, l, v); } ),
new ParameterDefn("AvatarCapsuleDepth", "The distance between the front and back of the avatar capsule",
0.45f,
(s,cf,p,v) => { s.m_params[0].avatarCapsuleDepth = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].avatarCapsuleDepth; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleDepth, p, l, v); } ),
new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar", new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar",
1.5f, 1.5f,
(s,cf,p,v) => { s.m_params[0].avatarCapsuleHeight = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].avatarCapsuleHeight = cf.GetFloat(p, v); },

View File

@ -178,7 +178,7 @@ public sealed class BSShapeCollection : IDisposable
bool ret = false; bool ret = false;
switch (shape.type) switch (shape.type)
{ {
case ShapeData.PhysicsShapeType.SHAPE_MESH: case PhysicsShapeType.SHAPE_MESH:
MeshDesc meshDesc; MeshDesc meshDesc;
if (Meshes.TryGetValue(shape.shapeKey, out meshDesc)) if (Meshes.TryGetValue(shape.shapeKey, out meshDesc))
{ {
@ -201,7 +201,7 @@ public sealed class BSShapeCollection : IDisposable
meshDesc.lastReferenced = System.DateTime.Now; meshDesc.lastReferenced = System.DateTime.Now;
Meshes[shape.shapeKey] = meshDesc; Meshes[shape.shapeKey] = meshDesc;
break; break;
case ShapeData.PhysicsShapeType.SHAPE_HULL: case PhysicsShapeType.SHAPE_HULL:
HullDesc hullDesc; HullDesc hullDesc;
if (Hulls.TryGetValue(shape.shapeKey, out hullDesc)) if (Hulls.TryGetValue(shape.shapeKey, out hullDesc))
{ {
@ -224,7 +224,7 @@ public sealed class BSShapeCollection : IDisposable
hullDesc.lastReferenced = System.DateTime.Now; hullDesc.lastReferenced = System.DateTime.Now;
Hulls[shape.shapeKey] = hullDesc; Hulls[shape.shapeKey] = hullDesc;
break; break;
case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN: case PhysicsShapeType.SHAPE_UNKNOWN:
break; break;
default: default:
// Native shapes are not tracked and they don't go into any list // Native shapes are not tracked and they don't go into any list
@ -255,16 +255,16 @@ public sealed class BSShapeCollection : IDisposable
{ {
switch (shape.type) switch (shape.type)
{ {
case ShapeData.PhysicsShapeType.SHAPE_HULL: case PhysicsShapeType.SHAPE_HULL:
DereferenceHull(shape, shapeCallback); DereferenceHull(shape, shapeCallback);
break; break;
case ShapeData.PhysicsShapeType.SHAPE_MESH: case PhysicsShapeType.SHAPE_MESH:
DereferenceMesh(shape, shapeCallback); DereferenceMesh(shape, shapeCallback);
break; break;
case ShapeData.PhysicsShapeType.SHAPE_COMPOUND: case PhysicsShapeType.SHAPE_COMPOUND:
DereferenceCompound(shape, shapeCallback); DereferenceCompound(shape, shapeCallback);
break; break;
case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN: case PhysicsShapeType.SHAPE_UNKNOWN:
break; break;
default: default:
break; break;
@ -352,28 +352,28 @@ public sealed class BSShapeCollection : IDisposable
BulletShape shapeInfo = new BulletShape(cShape); BulletShape shapeInfo = new BulletShape(cShape);
if (TryGetMeshByPtr(cShape, out meshDesc)) if (TryGetMeshByPtr(cShape, out meshDesc))
{ {
shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_MESH; shapeInfo.type = PhysicsShapeType.SHAPE_MESH;
shapeInfo.shapeKey = meshDesc.shapeKey; shapeInfo.shapeKey = meshDesc.shapeKey;
} }
else else
{ {
if (TryGetHullByPtr(cShape, out hullDesc)) if (TryGetHullByPtr(cShape, out hullDesc))
{ {
shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_HULL; shapeInfo.type = PhysicsShapeType.SHAPE_HULL;
shapeInfo.shapeKey = hullDesc.shapeKey; shapeInfo.shapeKey = hullDesc.shapeKey;
} }
else else
{ {
if (BulletSimAPI.IsCompound2(cShape)) if (BulletSimAPI.IsCompound2(cShape))
{ {
shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_COMPOUND; shapeInfo.type = PhysicsShapeType.SHAPE_COMPOUND;
} }
else else
{ {
if (BulletSimAPI.IsNativeShape2(cShape)) if (BulletSimAPI.IsNativeShape2(cShape))
{ {
shapeInfo.isNativeShape = true; shapeInfo.isNativeShape = true;
shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_BOX; // (technically, type doesn't matter) shapeInfo.type = PhysicsShapeType.SHAPE_BOX; // (technically, type doesn't matter)
} }
} }
} }
@ -381,7 +381,7 @@ public sealed class BSShapeCollection : IDisposable
DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo); DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo);
if (shapeInfo.type != ShapeData.PhysicsShapeType.SHAPE_UNKNOWN) if (shapeInfo.type != PhysicsShapeType.SHAPE_UNKNOWN)
{ {
DereferenceShape(shapeInfo, true, null); DereferenceShape(shapeInfo, true, null);
} }
@ -405,11 +405,11 @@ public sealed class BSShapeCollection : IDisposable
bool ret = false; bool ret = false;
bool haveShape = false; bool haveShape = false;
if (!haveShape && prim.PreferredPhysicalShape == ShapeData.PhysicsShapeType.SHAPE_AVATAR) if (!haveShape && prim.PreferredPhysicalShape == PhysicsShapeType.SHAPE_CAPSULE)
{ {
// an avatar capsule is close to a native shape (it is not shared) // an avatar capsule is close to a native shape (it is not shared)
ret = GetReferenceToNativeShape(prim, ShapeData.PhysicsShapeType.SHAPE_AVATAR, ret = GetReferenceToNativeShape(prim, PhysicsShapeType.SHAPE_CAPSULE,
ShapeData.FixedShapeKey.KEY_CAPSULE, shapeCallback); FixedShapeKey.KEY_CAPSULE, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape); DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape);
ret = true; ret = true;
haveShape = true; haveShape = true;
@ -417,7 +417,7 @@ public sealed class BSShapeCollection : IDisposable
// Compound shapes are handled special as they are rebuilt from scratch. // Compound shapes are handled special as they are rebuilt from scratch.
// This isn't too great a hardship since most of the child shapes will already been created. // This isn't too great a hardship since most of the child shapes will already been created.
if (!haveShape && prim.PreferredPhysicalShape == ShapeData.PhysicsShapeType.SHAPE_COMPOUND) if (!haveShape && prim.PreferredPhysicalShape == PhysicsShapeType.SHAPE_COMPOUND)
{ {
ret = GetReferenceToCompoundShape(prim, shapeCallback); ret = GetReferenceToCompoundShape(prim, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape); DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape);
@ -460,11 +460,11 @@ public sealed class BSShapeCollection : IDisposable
haveShape = true; haveShape = true;
if (forceRebuild if (forceRebuild
|| prim.Scale != prim.Size || prim.Scale != prim.Size
|| prim.PhysShape.type != ShapeData.PhysicsShapeType.SHAPE_SPHERE || prim.PhysShape.type != PhysicsShapeType.SHAPE_SPHERE
) )
{ {
ret = GetReferenceToNativeShape(prim, ShapeData.PhysicsShapeType.SHAPE_SPHERE, ret = GetReferenceToNativeShape(prim, PhysicsShapeType.SHAPE_SPHERE,
ShapeData.FixedShapeKey.KEY_SPHERE, shapeCallback); FixedShapeKey.KEY_SPHERE, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}", DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}",
prim.LocalID, forceRebuild, prim.PhysShape); prim.LocalID, forceRebuild, prim.PhysShape);
} }
@ -474,11 +474,11 @@ public sealed class BSShapeCollection : IDisposable
haveShape = true; haveShape = true;
if (forceRebuild if (forceRebuild
|| prim.Scale != prim.Size || prim.Scale != prim.Size
|| prim.PhysShape.type != ShapeData.PhysicsShapeType.SHAPE_BOX || prim.PhysShape.type != PhysicsShapeType.SHAPE_BOX
) )
{ {
ret = GetReferenceToNativeShape( prim, ShapeData.PhysicsShapeType.SHAPE_BOX, ret = GetReferenceToNativeShape( prim, PhysicsShapeType.SHAPE_BOX,
ShapeData.FixedShapeKey.KEY_BOX, shapeCallback); FixedShapeKey.KEY_BOX, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}", DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}",
prim.LocalID, forceRebuild, prim.PhysShape); prim.LocalID, forceRebuild, prim.PhysShape);
} }
@ -519,15 +519,12 @@ public sealed class BSShapeCollection : IDisposable
// Creates a native shape and assignes it to prim.BSShape. // Creates a native shape and assignes it to prim.BSShape.
// "Native" shapes are never shared. they are created here and destroyed in DereferenceShape(). // "Native" shapes are never shared. they are created here and destroyed in DereferenceShape().
private bool GetReferenceToNativeShape(BSPhysObject prim, private bool GetReferenceToNativeShape(BSPhysObject prim,
ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey, PhysicsShapeType shapeType, FixedShapeKey shapeKey,
ShapeDestructionCallback shapeCallback) ShapeDestructionCallback shapeCallback)
{ {
// release any previous shape // release any previous shape
DereferenceShape(prim.PhysShape, true, shapeCallback); DereferenceShape(prim.PhysShape, true, shapeCallback);
// Bullet native objects are scaled by the Bullet engine so pass the size in
prim.Scale = prim.Size;
BulletShape newShape = BuildPhysicalNativeShape(prim, shapeType, shapeKey); BulletShape newShape = BuildPhysicalNativeShape(prim, shapeType, shapeKey);
// Don't need to do a 'ReferenceShape()' here because native shapes are not shared. // Don't need to do a 'ReferenceShape()' here because native shapes are not shared.
@ -538,8 +535,8 @@ public sealed class BSShapeCollection : IDisposable
return true; return true;
} }
private BulletShape BuildPhysicalNativeShape(BSPhysObject prim, ShapeData.PhysicsShapeType shapeType, private BulletShape BuildPhysicalNativeShape(BSPhysObject prim, PhysicsShapeType shapeType,
ShapeData.FixedShapeKey shapeKey) FixedShapeKey shapeKey)
{ {
BulletShape newShape; BulletShape newShape;
// Need to make sure the passed shape information is for the native type. // Need to make sure the passed shape information is for the native type.
@ -547,12 +544,13 @@ public sealed class BSShapeCollection : IDisposable
nativeShapeData.Type = shapeType; nativeShapeData.Type = shapeType;
nativeShapeData.ID = prim.LocalID; nativeShapeData.ID = prim.LocalID;
nativeShapeData.Scale = prim.Scale; nativeShapeData.Scale = prim.Scale;
nativeShapeData.Size = prim.Scale; nativeShapeData.Size = prim.Scale; // unneeded, I think.
nativeShapeData.MeshKey = (ulong)shapeKey; nativeShapeData.MeshKey = (ulong)shapeKey;
nativeShapeData.HullKey = (ulong)shapeKey; nativeShapeData.HullKey = (ulong)shapeKey;
if (shapeType == ShapeData.PhysicsShapeType.SHAPE_AVATAR) if (shapeType == PhysicsShapeType.SHAPE_CAPSULE)
{ {
// The proper scale has been calculated in the prim.
newShape = new BulletShape( newShape = new BulletShape(
BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1f, 1f, prim.Scale) BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1f, 1f, prim.Scale)
, shapeType); , shapeType);
@ -560,6 +558,9 @@ public sealed class BSShapeCollection : IDisposable
} }
else else
{ {
// Native shapes are scaled in Bullet so set the scaling to the size
prim.Scale = prim.Size;
nativeShapeData.Scale = prim.Scale;
newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, nativeShapeData), shapeType); newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, nativeShapeData), shapeType);
} }
if (newShape.ptr == IntPtr.Zero) if (newShape.ptr == IntPtr.Zero)
@ -585,7 +586,7 @@ public sealed class BSShapeCollection : IDisposable
System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod); System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
// if this new shape is the same as last time, don't recreate the mesh // if this new shape is the same as last time, don't recreate the mesh
if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == ShapeData.PhysicsShapeType.SHAPE_MESH) if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == PhysicsShapeType.SHAPE_MESH)
return false; return false;
DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2}", DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2}",
@ -643,7 +644,7 @@ public sealed class BSShapeCollection : IDisposable
indices.GetLength(0), indices, vertices.Count, verticesAsFloats); indices.GetLength(0), indices, vertices.Count, verticesAsFloats);
} }
} }
BulletShape newShape = new BulletShape(meshPtr, ShapeData.PhysicsShapeType.SHAPE_MESH); BulletShape newShape = new BulletShape(meshPtr, PhysicsShapeType.SHAPE_MESH);
newShape.shapeKey = newMeshKey; newShape.shapeKey = newMeshKey;
return newShape; return newShape;
@ -659,7 +660,7 @@ public sealed class BSShapeCollection : IDisposable
System.UInt64 newHullKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod); System.UInt64 newHullKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
// if the hull hasn't changed, don't rebuild it // if the hull hasn't changed, don't rebuild it
if (newHullKey == prim.PhysShape.shapeKey && prim.PhysShape.type == ShapeData.PhysicsShapeType.SHAPE_HULL) if (newHullKey == prim.PhysShape.shapeKey && prim.PhysShape.type == PhysicsShapeType.SHAPE_HULL)
return false; return false;
DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}", DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}",
@ -780,7 +781,7 @@ public sealed class BSShapeCollection : IDisposable
} }
} }
BulletShape newShape = new BulletShape(hullPtr, ShapeData.PhysicsShapeType.SHAPE_HULL); BulletShape newShape = new BulletShape(hullPtr, PhysicsShapeType.SHAPE_HULL);
newShape.shapeKey = newHullKey; newShape.shapeKey = newHullKey;
return newShape; // 'true' means a new shape has been added to this prim return newShape; // 'true' means a new shape has been added to this prim
@ -803,7 +804,7 @@ public sealed class BSShapeCollection : IDisposable
// DereferenceShape(prim.PhysShape, true, shapeCallback); // DereferenceShape(prim.PhysShape, true, shapeCallback);
BulletShape cShape = new BulletShape( BulletShape cShape = new BulletShape(
BulletSimAPI.CreateCompoundShape2(PhysicsScene.World.ptr, false), ShapeData.PhysicsShapeType.SHAPE_COMPOUND); BulletSimAPI.CreateCompoundShape2(PhysicsScene.World.ptr, false), PhysicsShapeType.SHAPE_COMPOUND);
// Create the shape for the root prim and add it to the compound shape. Cannot be a native shape. // Create the shape for the root prim and add it to the compound shape. Cannot be a native shape.
CreateGeomMeshOrHull(prim, shapeCallback); CreateGeomMeshOrHull(prim, shapeCallback);
@ -894,7 +895,7 @@ public sealed class BSShapeCollection : IDisposable
// While we figure out the real problem, stick a simple native shape on the object. // While we figure out the real problem, stick a simple native shape on the object.
BulletShape fillinShape = BulletShape fillinShape =
BuildPhysicalNativeShape(prim, ShapeData.PhysicsShapeType.SHAPE_BOX, ShapeData.FixedShapeKey.KEY_BOX); BuildPhysicalNativeShape(prim, PhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);
return fillinShape; return fillinShape;
} }
@ -940,7 +941,7 @@ public sealed class BSShapeCollection : IDisposable
else else
{ {
bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr, bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
prim.LocalID, prim.ForcePosition, prim.ForceOrientation); prim.LocalID, prim.RawPosition, prim.RawOrientation);
DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
} }
aBody = new BulletBody(prim.LocalID, bodyPtr); aBody = new BulletBody(prim.LocalID, bodyPtr);

View File

@ -35,7 +35,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
public abstract class BSShape public abstract class BSShape
{ {
public IntPtr ptr { get; set; } public IntPtr ptr { get; set; }
public ShapeData.PhysicsShapeType type { get; set; } public PhysicsShapeType type { get; set; }
public System.UInt64 key { get; set; } public System.UInt64 key { get; set; }
public int referenceCount { get; set; } public int referenceCount { get; set; }
public DateTime lastReferenced { get; set; } public DateTime lastReferenced { get; set; }
@ -43,7 +43,7 @@ public abstract class BSShape
public BSShape() public BSShape()
{ {
ptr = IntPtr.Zero; ptr = IntPtr.Zero;
type = ShapeData.PhysicsShapeType.SHAPE_UNKNOWN; type = PhysicsShapeType.SHAPE_UNKNOWN;
key = 0; key = 0;
referenceCount = 0; referenceCount = 0;
lastReferenced = DateTime.Now; lastReferenced = DateTime.Now;
@ -54,17 +54,17 @@ public abstract class BSShape
{ {
BSShape ret = null; BSShape ret = null;
if (prim.PreferredPhysicalShape == ShapeData.PhysicsShapeType.SHAPE_AVATAR) if (prim.PreferredPhysicalShape == PhysicsShapeType.SHAPE_CAPSULE)
{ {
// an avatar capsule is close to a native shape (it is not shared) // an avatar capsule is close to a native shape (it is not shared)
ret = BSShapeNative.GetReference(physicsScene, prim, ShapeData.PhysicsShapeType.SHAPE_AVATAR, ret = BSShapeNative.GetReference(physicsScene, prim, PhysicsShapeType.SHAPE_CAPSULE,
ShapeData.FixedShapeKey.KEY_CAPSULE); FixedShapeKey.KEY_CAPSULE);
physicsScene.DetailLog("{0},BSShape.GetShapeReference,avatarCapsule,shape={1}", prim.LocalID, ret); physicsScene.DetailLog("{0},BSShape.GetShapeReference,avatarCapsule,shape={1}", prim.LocalID, ret);
} }
// Compound shapes are handled special as they are rebuilt from scratch. // Compound shapes are handled special as they are rebuilt from scratch.
// This isn't too great a hardship since most of the child shapes will already been created. // This isn't too great a hardship since most of the child shapes will already been created.
if (ret == null && prim.PreferredPhysicalShape == ShapeData.PhysicsShapeType.SHAPE_COMPOUND) if (ret == null && prim.PreferredPhysicalShape == PhysicsShapeType.SHAPE_COMPOUND)
{ {
// Getting a reference to a compound shape gets you the compound shape with the root prim shape added // Getting a reference to a compound shape gets you the compound shape with the root prim shape added
ret = BSShapeCompound.GetReference(prim); ret = BSShapeCompound.GetReference(prim);
@ -123,14 +123,14 @@ public class BSShapeNative : BSShape
{ {
} }
public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim, public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim,
ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey) PhysicsShapeType shapeType, FixedShapeKey shapeKey)
{ {
// Native shapes are not shared and are always built anew. // Native shapes are not shared and are always built anew.
return new BSShapeNative(physicsScene, prim, shapeType, shapeKey); return new BSShapeNative(physicsScene, prim, shapeType, shapeKey);
} }
private BSShapeNative(BSScene physicsScene, BSPhysObject prim, private BSShapeNative(BSScene physicsScene, BSPhysObject prim,
ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey) PhysicsShapeType shapeType, FixedShapeKey shapeKey)
{ {
ShapeData nativeShapeData = new ShapeData(); ShapeData nativeShapeData = new ShapeData();
nativeShapeData.Type = shapeType; nativeShapeData.Type = shapeType;
@ -141,7 +141,7 @@ public class BSShapeNative : BSShape
nativeShapeData.HullKey = (ulong)shapeKey; nativeShapeData.HullKey = (ulong)shapeKey;
if (shapeType == ShapeData.PhysicsShapeType.SHAPE_AVATAR) if (shapeType == PhysicsShapeType.SHAPE_CAPSULE)
{ {
ptr = BulletSimAPI.BuildCapsuleShape2(physicsScene.World.ptr, 1f, 1f, prim.Scale); ptr = BulletSimAPI.BuildCapsuleShape2(physicsScene.World.ptr, 1f, 1f, prim.Scale);
physicsScene.DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale); physicsScene.DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale);

View File

@ -0,0 +1,170 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyrightD
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Text;
using OpenSim.Framework;
using OpenSim.Region.Framework;
using OpenSim.Region.CoreModules;
using OpenSim.Region.Physics.Manager;
using Nini.Config;
using log4net;
using OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin
{
public sealed class BSTerrainHeightmap : BSTerrainPhys
{
static string LogHeader = "[BULLETSIM TERRAIN HEIGHTMAP]";
BulletHeightMapInfo m_mapInfo = null;
// Constructor to build a default, flat heightmap terrain.
public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize)
: base(physicsScene, regionBase, id)
{
Vector3 minTerrainCoords = new Vector3(0f, 0f, BSTerrainManager.HEIGHT_INITIALIZATION - BSTerrainManager.HEIGHT_EQUAL_FUDGE);
Vector3 maxTerrainCoords = new Vector3(regionSize.X, regionSize.Y, BSTerrainManager.HEIGHT_INITIALIZATION);
int totalHeights = (int)maxTerrainCoords.X * (int)maxTerrainCoords.Y;
float[] initialMap = new float[totalHeights];
for (int ii = 0; ii < totalHeights; ii++)
{
initialMap[ii] = BSTerrainManager.HEIGHT_INITIALIZATION;
}
m_mapInfo = new BulletHeightMapInfo(id, initialMap, IntPtr.Zero);
m_mapInfo.minCoords = minTerrainCoords;
m_mapInfo.maxCoords = maxTerrainCoords;
m_mapInfo.terrainRegionBase = TerrainBase;
// Don't have to free any previous since we just got here.
BuildHeightmapTerrain();
}
// This minCoords and maxCoords passed in give the size of the terrain (min and max Z
// are the high and low points of the heightmap).
public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap,
Vector3 minCoords, Vector3 maxCoords)
: base(physicsScene, regionBase, id)
{
m_mapInfo = new BulletHeightMapInfo(id, initialMap, IntPtr.Zero);
m_mapInfo.minCoords = minCoords;
m_mapInfo.maxCoords = maxCoords;
m_mapInfo.minZ = minCoords.Z;
m_mapInfo.maxZ = maxCoords.Z;
m_mapInfo.terrainRegionBase = TerrainBase;
// Don't have to free any previous since we just got here.
BuildHeightmapTerrain();
}
public override void Dispose()
{
ReleaseHeightMapTerrain();
}
// Using the information in m_mapInfo, create the physical representation of the heightmap.
private void BuildHeightmapTerrain()
{
m_mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, m_mapInfo.ID,
m_mapInfo.minCoords, m_mapInfo.maxCoords,
m_mapInfo.heightMap, BSTerrainManager.TERRAIN_COLLISION_MARGIN);
// Create the terrain shape from the mapInfo
m_mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(m_mapInfo.Ptr),
PhysicsShapeType.SHAPE_TERRAIN);
// The terrain object initial position is at the center of the object
Vector3 centerPos;
centerPos.X = m_mapInfo.minCoords.X + (m_mapInfo.sizeX / 2f);
centerPos.Y = m_mapInfo.minCoords.Y + (m_mapInfo.sizeY / 2f);
centerPos.Z = m_mapInfo.minZ + ((m_mapInfo.maxZ - m_mapInfo.minZ) / 2f);
m_mapInfo.terrainBody = new BulletBody(m_mapInfo.ID,
BulletSimAPI.CreateBodyWithDefaultMotionState2(m_mapInfo.terrainShape.ptr,
m_mapInfo.ID, centerPos, Quaternion.Identity));
// Set current terrain attributes
BulletSimAPI.SetFriction2(m_mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainFriction);
BulletSimAPI.SetHitFraction2(m_mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainHitFraction);
BulletSimAPI.SetRestitution2(m_mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainRestitution);
BulletSimAPI.SetCollisionFlags2(m_mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
// Return the new terrain to the world of physical objects
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
// redo its bounding box now that it is in the world
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
BulletSimAPI.SetCollisionFilterMask2(m_mapInfo.terrainBody.ptr,
(uint)CollisionFilterGroups.TerrainFilter,
(uint)CollisionFilterGroups.TerrainMask);
// Make it so the terrain will not move or be considered for movement.
BulletSimAPI.ForceActivationState2(m_mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
return;
}
// If there is information in m_mapInfo pointing to physical structures, release same.
private void ReleaseHeightMapTerrain()
{
if (m_mapInfo != null)
{
if (m_mapInfo.terrainBody.ptr != IntPtr.Zero)
{
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
// Frees both the body and the shape.
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
BulletSimAPI.ReleaseHeightMapInfo2(m_mapInfo.Ptr);
}
}
m_mapInfo = null;
}
// The passed position is relative to the base of the region.
public override float GetHeightAtXYZ(Vector3 pos)
{
float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
int mapIndex = (int)pos.Y * (int)m_mapInfo.sizeY + (int)pos.X;
try
{
ret = m_mapInfo.heightMap[mapIndex];
}
catch
{
// Sometimes they give us wonky values of X and Y. Give a warning and return something.
PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}",
LogHeader, m_mapInfo.terrainRegionBase, pos);
ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
}
return ret;
}
}
}

View File

@ -40,6 +40,32 @@ using OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin namespace OpenSim.Region.Physics.BulletSPlugin
{ {
// The physical implementation of the terrain is wrapped in this class.
public abstract class BSTerrainPhys : IDisposable
{
public enum TerrainImplementation
{
Heightmap = 0,
Mesh = 1
}
public BSScene PhysicsScene { get; private set; }
// Base of the region in world coordinates. Coordinates inside the region are relative to this.
public Vector3 TerrainBase { get; private set; }
public uint ID { get; private set; }
public BSTerrainPhys(BSScene physicsScene, Vector3 regionBase, uint id)
{
PhysicsScene = physicsScene;
TerrainBase = regionBase;
ID = id;
}
public abstract void Dispose();
public abstract float GetHeightAtXYZ(Vector3 pos);
}
// ==========================================================================================
public sealed class BSTerrainManager public sealed class BSTerrainManager
{ {
static string LogHeader = "[BULLETSIM TERRAIN MANAGER]"; static string LogHeader = "[BULLETSIM TERRAIN MANAGER]";
@ -67,11 +93,10 @@ public sealed class BSTerrainManager
// If doing mega-regions, if we're region zero we will be managing multiple // If doing mega-regions, if we're region zero we will be managing multiple
// region terrains since region zero does the physics for the whole mega-region. // region terrains since region zero does the physics for the whole mega-region.
private Dictionary<Vector2, BulletHeightMapInfo> m_heightMaps; private Dictionary<Vector3, BSTerrainPhys> m_terrains;
// True of the terrain has been modified. // Flags used to know when to recalculate the height.
// Used to force recalculation of terrain height after terrain has been modified private bool m_terrainModified = false;
private bool m_terrainModified;
// If we are doing mega-regions, terrains are added from TERRAIN_ID to m_terrainCount. // If we are doing mega-regions, terrains are added from TERRAIN_ID to m_terrainCount.
// This is incremented before assigning to new region so it is the last ID allocated. // This is incremented before assigning to new region so it is the last ID allocated.
@ -89,8 +114,7 @@ public sealed class BSTerrainManager
public BSTerrainManager(BSScene physicsScene) public BSTerrainManager(BSScene physicsScene)
{ {
PhysicsScene = physicsScene; PhysicsScene = physicsScene;
m_heightMaps = new Dictionary<Vector2,BulletHeightMapInfo>(); m_terrains = new Dictionary<Vector3,BSTerrainPhys>();
m_terrainModified = false;
// Assume one region of default size // Assume one region of default size
m_worldOffset = Vector3.Zero; m_worldOffset = Vector3.Zero;
@ -99,9 +123,6 @@ public sealed class BSTerrainManager
} }
// Create the initial instance of terrain and the underlying ground plane. // Create the initial instance of terrain and the underlying ground plane.
// The objects are allocated in the unmanaged space and the pointers are tracked
// by the managed code.
// The terrains and the groundPlane are not added to the list of PhysObjects.
// This is called from the initialization routine so we presume it is // This is called from the initialization routine so we presume it is
// safe to call Bullet in real time. We hope no one is moving prims around yet. // safe to call Bullet in real time. We hope no one is moving prims around yet.
public void CreateInitialGroundPlaneAndTerrain() public void CreateInitialGroundPlaneAndTerrain()
@ -109,7 +130,7 @@ public sealed class BSTerrainManager
// The ground plane is here to catch things that are trying to drop to negative infinity // The ground plane is here to catch things that are trying to drop to negative infinity
BulletShape groundPlaneShape = new BulletShape( BulletShape groundPlaneShape = new BulletShape(
BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f, TERRAIN_COLLISION_MARGIN), BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f, TERRAIN_COLLISION_MARGIN),
ShapeData.PhysicsShapeType.SHAPE_GROUNDPLANE); PhysicsShapeType.SHAPE_GROUNDPLANE);
m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID, m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID,
BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID, BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID,
Vector3.Zero, Quaternion.Identity)); Vector3.Zero, Quaternion.Identity));
@ -121,15 +142,9 @@ public sealed class BSTerrainManager
BulletSimAPI.SetCollisionFilterMask2(m_groundPlane.ptr, BulletSimAPI.SetCollisionFilterMask2(m_groundPlane.ptr,
(uint)CollisionFilterGroups.GroundPlaneFilter, (uint)CollisionFilterGroups.GroundPlaneMask); (uint)CollisionFilterGroups.GroundPlaneFilter, (uint)CollisionFilterGroups.GroundPlaneMask);
Vector3 minTerrainCoords = new Vector3(0f, 0f, HEIGHT_INITIALIZATION - HEIGHT_EQUAL_FUDGE); // Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain.
Vector3 maxTerrainCoords = new Vector3(DefaultRegionSize.X, DefaultRegionSize.Y, HEIGHT_INITIALIZATION); BSTerrainPhys initialTerrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize);
int totalHeights = (int)maxTerrainCoords.X * (int)maxTerrainCoords.Y; m_terrains.Add(Vector3.Zero, initialTerrain);
float[] initialMap = new float[totalHeights];
for (int ii = 0; ii < totalHeights; ii++)
{
initialMap[ii] = HEIGHT_INITIALIZATION;
}
UpdateOrCreateTerrain(BSScene.TERRAIN_ID, initialMap, minTerrainCoords, maxTerrainCoords, true);
} }
// Release all the terrain structures we might have allocated // Release all the terrain structures we might have allocated
@ -150,15 +165,11 @@ public sealed class BSTerrainManager
// Release all the terrain we have allocated // Release all the terrain we have allocated
public void ReleaseTerrain() public void ReleaseTerrain()
{ {
foreach (KeyValuePair<Vector2, BulletHeightMapInfo> kvp in m_heightMaps) foreach (KeyValuePair<Vector3, BSTerrainPhys> kvp in m_terrains)
{ {
if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, kvp.Value.terrainBody.ptr)) kvp.Value.Dispose();
{
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, kvp.Value.terrainBody.ptr);
BulletSimAPI.ReleaseHeightMapInfo2(kvp.Value.Ptr);
} }
} m_terrains.Clear();
m_heightMaps.Clear();
} }
// The simulator wants to set a new heightmap for the terrain. // The simulator wants to set a new heightmap for the terrain.
@ -176,8 +187,9 @@ public sealed class BSTerrainManager
{ {
DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}", DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}",
BSScene.DetailLogZero, m_worldOffset, m_worldMax); BSScene.DetailLogZero, m_worldOffset, m_worldMax);
((BSScene)MegaRegionParentPhysicsScene).TerrainManager.UpdateOrCreateTerrain(BSScene.CHILDTERRAIN_ID, ((BSScene)MegaRegionParentPhysicsScene).TerrainManager.UpdateTerrain(
localHeightMap, m_worldOffset, m_worldOffset + DefaultRegionSize, true); BSScene.CHILDTERRAIN_ID, localHeightMap,
m_worldOffset, m_worldOffset + DefaultRegionSize, true);
} }
} }
else else
@ -185,7 +197,7 @@ public sealed class BSTerrainManager
// If not doing the mega-prim thing, just change the terrain // If not doing the mega-prim thing, just change the terrain
DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero); DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero);
UpdateOrCreateTerrain(BSScene.TERRAIN_ID, localHeightMap, UpdateTerrain(BSScene.TERRAIN_ID, localHeightMap,
m_worldOffset, m_worldOffset + DefaultRegionSize, true); m_worldOffset, m_worldOffset + DefaultRegionSize, true);
} }
}); });
@ -195,56 +207,63 @@ public sealed class BSTerrainManager
// based on the passed information. The 'id' should be either the terrain id or // based on the passed information. The 'id' should be either the terrain id or
// BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used. // BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used.
// The latter feature is for creating child terrains for mega-regions. // The latter feature is for creating child terrains for mega-regions.
// If called with a mapInfo in m_heightMaps but the terrain has no body yet (mapInfo.terrainBody.Ptr == 0)
// then a new body and shape is created and the mapInfo is filled.
// This call is used for doing the initial terrain creation.
// If called with a mapInfo in m_heightMaps and there is an existing terrain body, a new // If called with a mapInfo in m_heightMaps and there is an existing terrain body, a new
// terrain shape is created and added to the body. // terrain shape is created and added to the body.
// This call is most often used to update the heightMap and parameters of the terrain. // This call is most often used to update the heightMap and parameters of the terrain.
// (The above does suggest that some simplification/refactoring is in order.) // (The above does suggest that some simplification/refactoring is in order.)
private void UpdateOrCreateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords, bool inTaintTime) private void UpdateTerrain(uint id, float[] heightMap,
Vector3 minCoords, Vector3 maxCoords, bool inTaintTime)
{ {
DetailLog("{0},BSTerrainManager.UpdateOrCreateTerrain,call,minC={1},maxC={2},inTaintTime={3}", DetailLog("{0},BSTerrainManager.UpdateTerrain,call,minC={1},maxC={2},inTaintTime={3}",
BSScene.DetailLogZero, minCoords, maxCoords, inTaintTime); BSScene.DetailLogZero, minCoords, maxCoords, inTaintTime);
// Find high and low points of passed heightmap.
// The min and max passed in is usually the area objects can be in (maximum
// object height, for instance). The terrain wants the bounding box for the
// terrain so we replace passed min and max Z with the actual terrain min/max Z.
float minZ = float.MaxValue; float minZ = float.MaxValue;
float maxZ = float.MinValue; float maxZ = float.MinValue;
Vector2 terrainRegionBase = new Vector2(minCoords.X, minCoords.Y); foreach (float height in heightMap)
int heightMapSize = heightMap.Length;
for (int ii = 0; ii < heightMapSize; ii++)
{ {
float height = heightMap[ii];
if (height < minZ) minZ = height; if (height < minZ) minZ = height;
if (height > maxZ) maxZ = height; if (height > maxZ) maxZ = height;
} }
if (minZ == maxZ)
// The shape of the terrain is from its base to its extents. {
// If min and max are the same, reduce min a little bit so a good bounding box is created.
minZ -= BSTerrainManager.HEIGHT_EQUAL_FUDGE;
}
minCoords.Z = minZ; minCoords.Z = minZ;
maxCoords.Z = maxZ; maxCoords.Z = maxZ;
BulletHeightMapInfo mapInfo; Vector3 terrainRegionBase = new Vector3(minCoords.X, minCoords.Y, 0f);
if (m_heightMaps.TryGetValue(terrainRegionBase, out mapInfo))
{
// If this is terrain we know about, it's easy to update
mapInfo.heightMap = heightMap; BSTerrainPhys terrainPhys;
mapInfo.minCoords = minCoords; if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys))
mapInfo.maxCoords = maxCoords;
mapInfo.minZ = minZ;
mapInfo.maxZ = maxZ;
mapInfo.sizeX = maxCoords.X - minCoords.X;
mapInfo.sizeY = maxCoords.Y - minCoords.Y;
DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,call,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}",
BSScene.DetailLogZero, terrainRegionBase, mapInfo.minCoords, mapInfo.maxCoords, mapInfo.sizeX, mapInfo.sizeY);
PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateOrCreateTerrain:UpdateExisting", delegate()
{ {
if (MegaRegionParentPhysicsScene != null) // There is already a terrain in this spot. Free the old and build the new.
DetailLog("{0},UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}",
BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords);
PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateTerrain:UpdateExisting", delegate()
{
// Remove old terrain from the collection
m_terrains.Remove(terrainRegionBase);
// Release any physical memory it may be using.
terrainPhys.Dispose();
if (MegaRegionParentPhysicsScene == null)
{
BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
m_terrains.Add(terrainRegionBase, newTerrainPhys);
m_terrainModified = true;
}
else
{ {
// It's possible that Combine() was called after this code was queued. // It's possible that Combine() was called after this code was queued.
// If we are a child of combined regions, we don't create any terrain for us. // If we are a child of combined regions, we don't create any terrain for us.
DetailLog("{0},UpdateOrCreateTerrain:AmACombineChild,taint", BSScene.DetailLogZero); DetailLog("{0},BSTerrainManager.UpdateTerrain:AmACombineChild,taint", BSScene.DetailLogZero);
// Get rid of any terrain that may have been allocated for us. // Get rid of any terrain that may have been allocated for us.
ReleaseGroundPlaneAndTerrain(); ReleaseGroundPlaneAndTerrain();
@ -252,91 +271,6 @@ public sealed class BSTerrainManager
// I hate doing this, but just bail // I hate doing this, but just bail
return; return;
} }
if (mapInfo.terrainBody.ptr != IntPtr.Zero)
{
// Updating an existing terrain.
DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,taint,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}",
BSScene.DetailLogZero, terrainRegionBase, mapInfo.minCoords, mapInfo.maxCoords, mapInfo.sizeX, mapInfo.sizeY);
// Remove from the dynamics world because we're going to mangle this object
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
// Get rid of the old terrain
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
BulletSimAPI.ReleaseHeightMapInfo2(mapInfo.Ptr);
mapInfo.Ptr = IntPtr.Zero;
/*
// NOTE: This routine is half here because I can't get the terrain shape replacement
// to work. In the short term, the above three lines completely delete the old
// terrain and the code below recreates one from scratch.
// Hopefully the Bullet community will help me out on this one.
// First, release the old collision shape (there is only one terrain)
BulletSimAPI.DeleteCollisionShape2(m_physicsScene.World.Ptr, mapInfo.terrainShape.Ptr);
// Fill the existing height map info with the new location and size information
BulletSimAPI.FillHeightMapInfo2(m_physicsScene.World.Ptr, mapInfo.Ptr, mapInfo.ID,
mapInfo.minCoords, mapInfo.maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN);
// Create a terrain shape based on the new info
mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr));
// Stuff the shape into the existing terrain body
BulletSimAPI.SetBodyShape2(m_physicsScene.World.Ptr, mapInfo.terrainBody.Ptr, mapInfo.terrainShape.Ptr);
*/
}
// else
{
// Creating a new terrain.
DetailLog("{0},UpdateOrCreateTerrain:CreateNewTerrain,taint,baseX={1},baseY={2},minZ={3},maxZ={4}",
BSScene.DetailLogZero, mapInfo.minCoords.X, mapInfo.minCoords.Y, minZ, maxZ);
mapInfo.ID = id;
mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, mapInfo.ID,
mapInfo.minCoords, mapInfo.maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN);
// Create the terrain shape from the mapInfo
mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr),
ShapeData.PhysicsShapeType.SHAPE_TERRAIN);
// The terrain object initial position is at the center of the object
Vector3 centerPos;
centerPos.X = minCoords.X + (mapInfo.sizeX / 2f);
centerPos.Y = minCoords.Y + (mapInfo.sizeY / 2f);
centerPos.Z = minZ + ((maxZ - minZ) / 2f);
mapInfo.terrainBody = new BulletBody(mapInfo.ID,
BulletSimAPI.CreateBodyWithDefaultMotionState2(mapInfo.terrainShape.ptr,
id, centerPos, Quaternion.Identity));
}
// Make sure the entry is in the heightmap table
m_heightMaps[terrainRegionBase] = mapInfo;
// Set current terrain attributes
BulletSimAPI.SetFriction2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainFriction);
BulletSimAPI.SetHitFraction2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainHitFraction);
BulletSimAPI.SetRestitution2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainRestitution);
BulletSimAPI.SetCollisionFlags2(mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
// Return the new terrain to the world of physical objects
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
// redo its bounding box now that it is in the world
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
BulletSimAPI.SetCollisionFilterMask2(mapInfo.terrainBody.ptr,
(uint)CollisionFilterGroups.TerrainFilter,
(uint)CollisionFilterGroups.TerrainMask);
// Make sure the new shape is processed.
// BulletSimAPI.Activate2(mapInfo.terrainBody.ptr, true);
// BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.ISLAND_SLEEPING);
BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
m_terrainModified = true;
}); });
} }
else else
@ -353,33 +287,50 @@ public sealed class BSTerrainManager
Vector3 minCoordsX = minCoords; Vector3 minCoordsX = minCoords;
Vector3 maxCoordsX = maxCoords; Vector3 maxCoordsX = maxCoords;
DetailLog("{0},UpdateOrCreateTerrain:NewTerrain,call,id={1}, minC={2}, maxC={3}", DetailLog("{0},UpdateTerrain:NewTerrain,call,id={1}, minC={2}, maxC={3}",
BSScene.DetailLogZero, newTerrainID, minCoords, minCoords); BSScene.DetailLogZero, newTerrainID, minCoords, minCoords);
// Code that must happen at taint-time // Code that must happen at taint-time
PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateOrCreateTerrain:NewTerrain", delegate() PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateTerrain:NewTerrain", delegate()
{ {
DetailLog("{0},UpdateOrCreateTerrain:NewTerrain,taint,baseX={1},baseY={2}", BSScene.DetailLogZero, minCoords.X, minCoords.Y); DetailLog("{0},UpdateTerrain:NewTerrain,taint,baseX={1},baseY={2}",
// Create a new mapInfo that will be filled with the new info BSScene.DetailLogZero, minCoordsX.X, minCoordsX.Y);
mapInfo = new BulletHeightMapInfo(id, heightMapX, BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, newTerrainID, m_terrains.Add(terrainRegionBase, newTerrainPhys);
minCoordsX, maxCoordsX, heightMapX, TERRAIN_COLLISION_MARGIN));
// Put the unfilled heightmap info into the collection of same
m_heightMaps.Add(terrainRegionBase, mapInfo);
// Build the terrain
UpdateOrCreateTerrain(newTerrainID, heightMap, minCoords, maxCoords, true);
m_terrainModified = true; m_terrainModified = true;
}); });
} }
} }
// Someday we will have complex terrain with caves and tunnels // TODO: redo terrain implementation selection to allow other base types than heightMap.
public float GetTerrainHeightAtXYZ(Vector3 loc) private BSTerrainPhys BuildPhysicalTerrain(Vector3 terrainRegionBase, uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords)
{ {
// For the moment, it's flat and convex PhysicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}",
return GetTerrainHeightAtXY(loc.X, loc.Y); LogHeader, PhysicsScene.RegionName, terrainRegionBase,
(BSTerrainPhys.TerrainImplementation)PhysicsScene.Params.terrainImplementation);
BSTerrainPhys newTerrainPhys = null;
switch ((int)PhysicsScene.Params.terrainImplementation)
{
case (int)BSTerrainPhys.TerrainImplementation.Heightmap:
newTerrainPhys = new BSTerrainHeightmap(PhysicsScene, terrainRegionBase, id,
heightMap, minCoords, maxCoords);
break;
case (int)BSTerrainPhys.TerrainImplementation.Mesh:
newTerrainPhys = new BSTerrainMesh(PhysicsScene, terrainRegionBase, id,
heightMap, minCoords, maxCoords);
break;
default:
PhysicsScene.Logger.ErrorFormat("{0} Bad terrain implementation specified. Type={1}/{2},Region={3}/{4}",
LogHeader,
(int)PhysicsScene.Params.terrainImplementation,
PhysicsScene.Params.terrainImplementation,
PhysicsScene.RegionName, terrainRegionBase);
break;
} }
return newTerrainPhys;
}
// Given an X and Y, find the height of the terrain. // Given an X and Y, find the height of the terrain.
// Since we could be handling multiple terrains for a mega-region, // Since we could be handling multiple terrains for a mega-region,
@ -390,8 +341,10 @@ public sealed class BSTerrainManager
private float lastHeightTX = 999999f; private float lastHeightTX = 999999f;
private float lastHeightTY = 999999f; private float lastHeightTY = 999999f;
private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT; private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT;
private float GetTerrainHeightAtXY(float tX, float tY) public float GetTerrainHeightAtXYZ(Vector3 loc)
{ {
float tX = loc.X;
float tY = loc.Y;
// You'd be surprized at the number of times this routine is called // You'd be surprized at the number of times this routine is called
// with the same parameters as last time. // with the same parameters as last time.
if (!m_terrainModified && lastHeightTX == tX && lastHeightTY == tY) if (!m_terrainModified && lastHeightTX == tX && lastHeightTY == tY)
@ -403,27 +356,14 @@ public sealed class BSTerrainManager
int offsetX = ((int)(tX / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X; int offsetX = ((int)(tX / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X;
int offsetY = ((int)(tY / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y; int offsetY = ((int)(tY / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y;
Vector2 terrainBaseXY = new Vector2(offsetX, offsetY); Vector3 terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f);
BulletHeightMapInfo mapInfo; BSTerrainPhys physTerrain;
if (m_heightMaps.TryGetValue(terrainBaseXY, out mapInfo)) if (m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain))
{ {
float regionX = tX - offsetX; ret = physTerrain.GetHeightAtXYZ(loc - terrainBaseXYZ);
float regionY = tY - offsetY; DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXYZ,loc={1},base={2},height={3}",
int mapIndex = (int)regionY * (int)mapInfo.sizeY + (int)regionX; BSScene.DetailLogZero, loc, terrainBaseXYZ, ret);
try
{
ret = mapInfo.heightMap[mapIndex];
}
catch
{
// Sometimes they give us wonky values of X and Y. Give a warning and return something.
PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, x={2}, y={3}",
LogHeader, terrainBaseXY, regionX, regionY);
ret = HEIGHT_GETHEIGHT_RET;
}
// DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXY,bX={1},baseY={2},szX={3},szY={4},regX={5},regY={6},index={7},ht={8}",
// BSScene.DetailLogZero, offsetX, offsetY, mapInfo.sizeX, mapInfo.sizeY, regionX, regionY, mapIndex, ret);
} }
else else
{ {
@ -466,7 +406,7 @@ public sealed class BSTerrainManager
// Unhook all the combining that I know about. // Unhook all the combining that I know about.
public void UnCombine(PhysicsScene pScene) public void UnCombine(PhysicsScene pScene)
{ {
// Just like ODE, for the moment a NOP // Just like ODE, we don't do anything yet.
DetailLog("{0},BSTerrainManager.UnCombine", BSScene.DetailLogZero); DetailLog("{0},BSTerrainManager.UnCombine", BSScene.DetailLogZero);
} }

View File

@ -0,0 +1,256 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyrightD
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Text;
using OpenSim.Framework;
using OpenSim.Region.Framework;
using OpenSim.Region.CoreModules;
using OpenSim.Region.Physics.Manager;
using Nini.Config;
using log4net;
using OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin
{
public sealed class BSTerrainMesh : BSTerrainPhys
{
static string LogHeader = "[BULLETSIM TERRAIN MESH]";
private float[] m_savedHeightMap;
int m_sizeX;
int m_sizeY;
BulletShape m_terrainShape;
BulletBody m_terrainBody;
public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize)
: base(physicsScene, regionBase, id)
{
}
public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id /* parameters for making mesh */)
: base(physicsScene, regionBase, id)
{
}
// Create terrain mesh from a heightmap.
public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap,
Vector3 minCoords, Vector3 maxCoords)
: base(physicsScene, regionBase, id)
{
int indicesCount;
int[] indices;
int verticesCount;
float[] vertices;
m_savedHeightMap = initialMap;
m_sizeX = (int)(maxCoords.X - minCoords.X);
m_sizeY = (int)(maxCoords.Y - minCoords.Y);
if (!BSTerrainMesh.ConvertHeightmapToMesh(PhysicsScene, initialMap,
m_sizeX, m_sizeY,
(float)m_sizeX, (float)m_sizeY,
Vector3.Zero, 1.0f,
out indicesCount, out indices, out verticesCount, out vertices))
{
// DISASTER!!
PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap", ID);
PhysicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh! base={1}", LogHeader, TerrainBase);
// Something is very messed up and a crash is in our future.
return;
}
m_terrainShape = new BulletShape(BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
indicesCount, indices, verticesCount, vertices),
PhysicsShapeType.SHAPE_MESH);
if (m_terrainShape.ptr == IntPtr.Zero)
{
// DISASTER!!
PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape", ID);
physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase);
// Something is very messed up and a crash is in our future.
return;
}
Vector3 pos = regionBase;
Quaternion rot = Quaternion.Identity;
m_terrainBody = new BulletBody(id, BulletSimAPI.CreateBodyWithDefaultMotionState2( m_terrainShape.ptr, ID, pos, rot));
if (m_terrainBody.ptr == IntPtr.Zero)
{
// DISASTER!!
physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase);
// Something is very messed up and a crash is in our future.
return;
}
// Set current terrain attributes
BulletSimAPI.SetFriction2(m_terrainBody.ptr, PhysicsScene.Params.terrainFriction);
BulletSimAPI.SetHitFraction2(m_terrainBody.ptr, PhysicsScene.Params.terrainHitFraction);
BulletSimAPI.SetRestitution2(m_terrainBody.ptr, PhysicsScene.Params.terrainRestitution);
BulletSimAPI.SetCollisionFlags2(m_terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
// Static objects are not very massive.
BulletSimAPI.SetMassProps2(m_terrainBody.ptr, 0f, Vector3.Zero);
// Return the new terrain to the world of physical objects
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr);
// redo its bounding box now that it is in the world
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_terrainBody.ptr);
BulletSimAPI.SetCollisionFilterMask2(m_terrainBody.ptr,
(uint)CollisionFilterGroups.TerrainFilter,
(uint)CollisionFilterGroups.TerrainMask);
// Make it so the terrain will not move or be considered for movement.
BulletSimAPI.ForceActivationState2(m_terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
}
public override void Dispose()
{
if (m_terrainBody.ptr != IntPtr.Zero)
{
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr);
// Frees both the body and the shape.
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_terrainBody.ptr);
}
}
public override float GetHeightAtXYZ(Vector3 pos)
{
// For the moment use the saved heightmap to get the terrain height.
// TODO: raycast downward to find the true terrain below the position.
float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
int mapIndex = (int)pos.Y * m_sizeY + (int)pos.X;
try
{
ret = m_savedHeightMap[mapIndex];
}
catch
{
// Sometimes they give us wonky values of X and Y. Give a warning and return something.
PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}",
LogHeader, TerrainBase, pos);
ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
}
return ret;
}
// Convert the passed heightmap to mesh information suitable for CreateMeshShape2().
// Return 'true' if successfully created.
public static bool ConvertHeightmapToMesh(
BSScene physicsScene,
float[] heightMap, int sizeX, int sizeY, // parameters of incoming heightmap
float extentX, float extentY, // zero based range for output vertices
Vector3 extentBase, // base to be added to all vertices
float magnification, // number of vertices to create between heightMap coords
out int indicesCountO, out int[] indicesO,
out int verticesCountO, out float[] verticesO)
{
bool ret = false;
int indicesCount = 0;
int verticesCount = 0;
int[] indices = new int[0];
float[] vertices = new float[0];
// Simple mesh creation which assumes magnification == 1.
// TODO: do a more general solution that scales, adds new vertices and smoothes the result.
try
{
// One vertice per heightmap value plus the vertices off the top and bottom edge.
int totalVertices = (sizeX + 1) * (sizeY + 1);
vertices = new float[totalVertices * 3];
int totalIndices = sizeX * sizeY * 6;
indices = new int[totalIndices];
float magX = (float)sizeX / extentX;
float magY = (float)sizeY / extentY;
physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,totVert={1},totInd={2},extentBase={3},magX={4},magY={5}",
BSScene.DetailLogZero, totalVertices, totalIndices, extentBase, magX, magY);
// Note that sizeX+1 vertices are created since there is land between this and the next region.
for (int yy = 0; yy <= sizeY; yy++)
{
for (int xx = 0; xx <= sizeX; xx++) // Hint: the "<=" means we got through sizeX + 1 times
{
int offset = yy * sizeX + xx;
// Extend the height from the height from the last row or column
if (yy == sizeY) offset -= sizeX;
if (xx == sizeX) offset -= 1;
float height = heightMap[offset];
vertices[verticesCount + 0] = (float)xx * magX + extentBase.X;
vertices[verticesCount + 1] = (float)yy * magY + extentBase.Y;
vertices[verticesCount + 2] = height + extentBase.Z;
verticesCount += 3;
}
}
verticesCount = verticesCount / 3;
physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,completeVerts,verCount={1}",
BSScene.DetailLogZero, verticesCount);
for (int yy = 0; yy < sizeY; yy++)
{
for (int xx = 0; xx < sizeX; xx++)
{
int offset = yy * sizeX + xx;
// Each vertices is presumed to be the upper left corner of a box of two triangles
indices[indicesCount + 0] = offset;
indices[indicesCount + 1] = offset + 1;
indices[indicesCount + 2] = offset + sizeX + 1; // accounting for the extra column
indices[indicesCount + 3] = offset + 1;
indices[indicesCount + 4] = offset + sizeX + 2;
indices[indicesCount + 5] = offset + sizeX + 1;
indicesCount += 6;
}
}
physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,completeIndices,indCount={1}", // DEEBUG DEBUG DEBUG
LogHeader, indicesCount); // DEBUG
ret = true;
}
catch (Exception e)
{
physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh. For={1}/{2}, e={3}",
LogHeader, physicsScene.RegionName, extentBase, e);
}
indicesCountO = indicesCount;
indicesO = indices;
verticesCountO = verticesCount;
verticesO = vertices;
return ret;
}
}
}

View File

@ -88,11 +88,11 @@ public struct BulletShape
public BulletShape(IntPtr xx) public BulletShape(IntPtr xx)
{ {
ptr = xx; ptr = xx;
type=ShapeData.PhysicsShapeType.SHAPE_UNKNOWN; type=PhysicsShapeType.SHAPE_UNKNOWN;
shapeKey = 0; shapeKey = (System.UInt64)FixedShapeKey.KEY_NONE;
isNativeShape = false; isNativeShape = false;
} }
public BulletShape(IntPtr xx, ShapeData.PhysicsShapeType typ) public BulletShape(IntPtr xx, PhysicsShapeType typ)
{ {
ptr = xx; ptr = xx;
type = typ; type = typ;
@ -100,7 +100,7 @@ public struct BulletShape
isNativeShape = false; isNativeShape = false;
} }
public IntPtr ptr; public IntPtr ptr;
public ShapeData.PhysicsShapeType type; public PhysicsShapeType type;
public System.UInt64 shapeKey; public System.UInt64 shapeKey;
public bool isNativeShape; public bool isNativeShape;
public override string ToString() public override string ToString()
@ -152,7 +152,7 @@ public class BulletHeightMapInfo
ID = id; ID = id;
Ptr = xx; Ptr = xx;
heightMap = hm; heightMap = hm;
terrainRegionBase = new Vector2(0f, 0f); terrainRegionBase = Vector3.Zero;
minCoords = new Vector3(100f, 100f, 25f); minCoords = new Vector3(100f, 100f, 25f);
maxCoords = new Vector3(101f, 101f, 26f); maxCoords = new Vector3(101f, 101f, 26f);
minZ = maxZ = 0f; minZ = maxZ = 0f;
@ -161,7 +161,7 @@ public class BulletHeightMapInfo
public uint ID; public uint ID;
public IntPtr Ptr; public IntPtr Ptr;
public float[] heightMap; public float[] heightMap;
public Vector2 terrainRegionBase; public Vector3 terrainRegionBase;
public Vector3 minCoords; public Vector3 minCoords;
public Vector3 maxCoords; public Vector3 maxCoords;
public float sizeX, sizeY; public float sizeX, sizeY;
@ -178,13 +178,10 @@ public struct ConvexHull
int VertexCount; int VertexCount;
Vector3[] Vertices; Vector3[] Vertices;
} }
[StructLayout(LayoutKind.Sequential)] public enum PhysicsShapeType
public struct ShapeData
{ {
public enum PhysicsShapeType
{
SHAPE_UNKNOWN = 0, SHAPE_UNKNOWN = 0,
SHAPE_AVATAR = 1, SHAPE_CAPSULE = 1,
SHAPE_BOX = 2, SHAPE_BOX = 2,
SHAPE_CONE = 3, SHAPE_CONE = 3,
SHAPE_CYLINDER = 4, SHAPE_CYLINDER = 4,
@ -195,7 +192,23 @@ public struct ShapeData
SHAPE_GROUNDPLANE = 20, SHAPE_GROUNDPLANE = 20,
SHAPE_TERRAIN = 21, SHAPE_TERRAIN = 21,
SHAPE_COMPOUND = 22, SHAPE_COMPOUND = 22,
}; SHAPE_HEIGHTMAP = 23,
};
// The native shapes have predefined shape hash keys
public enum FixedShapeKey : ulong
{
KEY_NONE = 0,
KEY_BOX = 1,
KEY_SPHERE = 2,
KEY_CONE = 3,
KEY_CYLINDER = 4,
KEY_CAPSULE = 5,
}
[StructLayout(LayoutKind.Sequential)]
public struct ShapeData
{
public uint ID; public uint ID;
public PhysicsShapeType Type; public PhysicsShapeType Type;
public Vector3 Position; public Vector3 Position;
@ -216,16 +229,6 @@ public struct ShapeData
// note that bools are passed as floats since bool size changes by language and architecture // note that bools are passed as floats since bool size changes by language and architecture
public const float numericTrue = 1f; public const float numericTrue = 1f;
public const float numericFalse = 0f; public const float numericFalse = 0f;
// The native shapes have predefined shape hash keys
public enum FixedShapeKey : ulong
{
KEY_BOX = 1,
KEY_SPHERE = 2,
KEY_CONE = 3,
KEY_CYLINDER = 4,
KEY_CAPSULE = 5,
}
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct SweepHit public struct SweepHit
@ -280,6 +283,7 @@ public struct ConfigurationParameters
public float ccdSweptSphereRadius; public float ccdSweptSphereRadius;
public float contactProcessingThreshold; public float contactProcessingThreshold;
public float terrainImplementation;
public float terrainFriction; public float terrainFriction;
public float terrainHitFraction; public float terrainHitFraction;
public float terrainRestitution; public float terrainRestitution;
@ -287,7 +291,8 @@ public struct ConfigurationParameters
public float avatarStandingFriction; public float avatarStandingFriction;
public float avatarDensity; public float avatarDensity;
public float avatarRestitution; public float avatarRestitution;
public float avatarCapsuleRadius; public float avatarCapsuleWidth;
public float avatarCapsuleDepth;
public float avatarCapsuleHeight; public float avatarCapsuleHeight;
public float avatarContactProcessingThreshold; public float avatarContactProcessingThreshold;
@ -388,13 +393,13 @@ public enum CollisionFilterGroups : uint
ObjectFilter = BSolidFilter, ObjectFilter = BSolidFilter,
ObjectMask = BAllFilter, ObjectMask = BAllFilter,
StaticObjectFilter = BStaticFilter, StaticObjectFilter = BStaticFilter,
StaticObjectMask = BAllFilter, StaticObjectMask = BAllFilter & ~BStaticFilter, // static objects don't collide with each other
LinksetFilter = BLinksetFilter, LinksetFilter = BLinksetFilter,
LinksetMask = BAllFilter & ~BLinksetFilter, LinksetMask = BAllFilter & ~BLinksetFilter, // linkset objects don't collide with each other
VolumeDetectFilter = BSensorTrigger, VolumeDetectFilter = BSensorTrigger,
VolumeDetectMask = ~BSensorTrigger, VolumeDetectMask = ~BSensorTrigger,
TerrainFilter = BTerrainFilter, TerrainFilter = BTerrainFilter,
TerrainMask = BAllFilter & ~BStaticFilter, TerrainMask = BAllFilter & ~BStaticFilter, // static objects on the ground don't collide
GroundPlaneFilter = BGroundPlaneFilter, GroundPlaneFilter = BGroundPlaneFilter,
GroundPlaneMask = BAllFilter GroundPlaneMask = BAllFilter

View File

@ -57,14 +57,23 @@ namespace OpenSim.Server.Handlers.Asset
public override byte[] Handle(string path, Stream request, public override byte[] Handle(string path, Stream request,
IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{ {
AssetBase asset;
XmlSerializer xs = new XmlSerializer(typeof (AssetBase)); XmlSerializer xs = new XmlSerializer(typeof (AssetBase));
AssetBase asset = (AssetBase) xs.Deserialize(request);
try
{
asset = (AssetBase)xs.Deserialize(request);
}
catch (XmlException)
{
httpResponse.StatusCode = (int)HttpStatusCode.BadRequest;
return null;
}
string[] p = SplitParams(path); string[] p = SplitParams(path);
if (p.Length > 1) if (p.Length > 1)
{ {
bool result = bool result = m_AssetService.UpdateContent(p[1], asset.Data);
m_AssetService.UpdateContent(p[1], asset.Data);
xs = new XmlSerializer(typeof(bool)); xs = new XmlSerializer(typeof(bool));
return ServerUtils.SerializeResult(xs, result); return ServerUtils.SerializeResult(xs, result);

View File

@ -0,0 +1,110 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Nini.Config;
using NUnit.Framework;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Server.Handlers.Asset;
using OpenSim.Services.AssetService;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Server.Handlers.Asset.Test
{
[TestFixture]
public class AssetServerPostHandlerTests : OpenSimTestCase
{
[Test]
public void TestGoodAssetStoreRequest()
{
TestHelpers.InMethod();
UUID assetId = TestHelpers.ParseTail(0x1);
IConfigSource config = new IniConfigSource();
config.AddConfig("AssetService");
config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll");
AssetService assetService = new AssetService(config);
AssetServerPostHandler asph = new AssetServerPostHandler(assetService);
AssetBase asset = AssetHelpers.CreateNotecardAsset(assetId, "Hello World");
MemoryStream buffer = new MemoryStream();
XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = Encoding.UTF8;
using (XmlWriter writer = XmlWriter.Create(buffer, settings))
{
XmlSerializer serializer = new XmlSerializer(typeof(AssetBase));
serializer.Serialize(writer, asset);
writer.Flush();
}
buffer.Position = 0;
asph.Handle(null, buffer, null, null);
AssetBase retrievedAsset = assetService.Get(assetId.ToString());
Assert.That(retrievedAsset, Is.Not.Null);
}
[Test]
public void TestBadXmlAssetStoreRequest()
{
TestHelpers.InMethod();
IConfigSource config = new IniConfigSource();
config.AddConfig("AssetService");
config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll");
AssetService assetService = new AssetService(config);
AssetServerPostHandler asph = new AssetServerPostHandler(assetService);
MemoryStream buffer = new MemoryStream();
byte[] badData = new byte[] { 0x48, 0x65, 0x6c, 0x6c, 0x6f };
buffer.Write(badData, 0, badData.Length);
buffer.Position = 0;
TestOSHttpResponse response = new TestOSHttpResponse();
asph.Handle(null, buffer, null, response);
Assert.That(response.StatusCode, Is.EqualTo((int)HttpStatusCode.BadRequest));
}
}
}

View File

@ -909,7 +909,8 @@
AvatarFriction = 0.2 AvatarFriction = 0.2
AvatarRestitution = 0.0 AvatarRestitution = 0.0
AvatarDensity = 60.0 AvatarDensity = 60.0
AvatarCapsuleRadius = 0.37 AvatarCapsuleWidth = 0.6
AvatarCapsuleDepth = 0.45
AvatarCapsuleHeight = 1.5 AvatarCapsuleHeight = 1.5
AvatarContactProcessingThreshold = 0.1 AvatarContactProcessingThreshold = 0.1

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -3421,6 +3421,52 @@
</Files> </Files>
</Project> </Project>
<Project frameworkVersion="v3_5" name="OpenSim.Server.Handlers.Tests" path="OpenSim/Server/Handlers" type="Library">
<Configuration name="Debug">
<Options>
<OutputPath>../../../bin/</OutputPath>
</Options>
</Configuration>
<Configuration name="Release">
<Options>
<OutputPath>../../../bin/</OutputPath>
</Options>
</Configuration>
<ReferencePath>../../../bin/</ReferencePath>
<Reference name="System"/>
<Reference name="System.Xml"/>
<Reference name="System.Web"/>
<Reference name="log4net" path="../../../bin/"/>
<Reference name="Nini" path="../../../bin/"/>
<Reference name="nunit.framework" path="../../../bin/"/>
<Reference name="OpenMetaverseTypes" path="../../../bin/"/>
<Reference name="OpenMetaverse" path="../../../bin/"/>
<Reference name="OpenMetaverse.StructuredData" path="../../../bin/"/>
<Reference name="XMLRPC" path="../../../bin/"/>
<Reference name="OpenSim.Framework"/>
<Reference name="OpenSim.Framework.Console"/>
<Reference name="OpenSim.Framework.Servers.HttpServer"/>
<Reference name="OpenSim.Server.Base"/>
<Reference name="OpenSim.Server.Handlers"/>
<Reference name="OpenSim.Services.AssetService"/>
<Reference name="OpenSim.Services.Base"/>
<Reference name="OpenSim.Services.Interfaces"/>
<Reference name="OpenSim.Services.UserAccountService"/>
<Reference name="OpenSim.Tests.Common"/>
<!--
TODO: this is kind of lame, we basically build a duplicate
assembly but with tests added in, just so that we don't
need to hard code in a bunch of Test directories here. If
pattern="Tests/*.cs" worked, we wouldn't need this.
-->
<Files>
<!-- SADLY the way this works means you need to keep adding these paths -->
<Match path="Asset/Tests" pattern="*.cs" recurse="true"/>
</Files>
</Project>
<Project frameworkVersion="v3_5" name="OpenSim.Tests.Stress" path="OpenSim/Tests/Stress" type="Library"> <Project frameworkVersion="v3_5" name="OpenSim.Tests.Stress" path="OpenSim/Tests/Stress" type="Library">
<Configuration name="Debug"> <Configuration name="Debug">
<Options> <Options>