Merge branch 'master' into careminster
commit
c4c6121752
|
@ -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" />
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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); },
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.
46
prebuild.xml
46
prebuild.xml
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue