BulletSim: rename some constraint variables to be consistant with other name use.

Added callbacks for shape and body changes in GetBodyAndShape() so the linkset
    constraints can be picked up and restored. A better design might be to have
    a "prim shape changed" event. Think about that.
Added constraint types to general constraint class.
connector_plugin
Robert Adams 2012-09-27 19:57:35 -07:00
parent 7b65985047
commit 74dea4cfd5
7 changed files with 232 additions and 117 deletions

View File

@ -34,6 +34,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
public class BS6DofConstraint : BSConstraint public class BS6DofConstraint : BSConstraint
{ {
private static string LogHeader = "[BULLETSIM 6DOF CONSTRAINT]";
public override ConstraintType Type { get { return ConstraintType.D6_CONSTRAINT_TYPE; } }
// Create a btGeneric6DofConstraint // Create a btGeneric6DofConstraint
public BS6DofConstraint(BulletSim world, BulletBody obj1, BulletBody obj2, public BS6DofConstraint(BulletSim world, BulletBody obj1, BulletBody obj2,
Vector3 frame1, Quaternion frame1rot, Vector3 frame1, Quaternion frame1rot,
@ -49,6 +53,9 @@ public class BS6DofConstraint : BSConstraint
frame2, frame2rot, frame2, frame2rot,
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies)); useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
m_enabled = true; m_enabled = true;
world.physicsScene.DetailLog("{0},BS6DofConstraint,createFrame,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
BSScene.DetailLogZero, world.worldID,
obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X"));
} }
public BS6DofConstraint(BulletSim world, BulletBody obj1, BulletBody obj2, public BS6DofConstraint(BulletSim world, BulletBody obj1, BulletBody obj2,
@ -60,12 +67,13 @@ public class BS6DofConstraint : BSConstraint
m_body2 = obj2; m_body2 = obj2;
if (obj1.ptr == IntPtr.Zero || obj2.ptr == IntPtr.Zero) if (obj1.ptr == IntPtr.Zero || obj2.ptr == IntPtr.Zero)
{ {
world.scene.DetailLog("{0},BS6DOFConstraint,badBodyPtr,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}", world.physicsScene.DetailLog("{0},BS6DOFConstraint,badBodyPtr,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
"[BULLETSIM 6DOF CONSTRAINT]", world.worldID, BSScene.DetailLogZero, world.worldID,
obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X")); obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X"));
world.scene.Logger.ErrorFormat("{0} Attempt to build 6DOF constraint with missing bodies: wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}", world.physicsScene.Logger.ErrorFormat("{0} Attempt to build 6DOF constraint with missing bodies: wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
"[BULLETSIM 6DOF CONSTRAINT]", world.worldID, "[BULLETSIM 6DOF CONSTRAINT]", world.worldID,
obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X")); obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X"));
m_enabled = false;
} }
else else
{ {
@ -73,8 +81,20 @@ public class BS6DofConstraint : BSConstraint
BulletSimAPI.Create6DofConstraintToPoint2(m_world.ptr, m_body1.ptr, m_body2.ptr, BulletSimAPI.Create6DofConstraintToPoint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
joinPoint, joinPoint,
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies)); useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
world.physicsScene.DetailLog("{0},BS6DofConstraint,createMidPoint,wID={1}, csrt={2}, rID={3}, rBody={4}, cID={5}, cBody={6}",
BSScene.DetailLogZero, world.worldID, m_constraint.ptr.ToString("X"),
obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X"));
if (m_constraint.ptr == IntPtr.Zero)
{
world.physicsScene.Logger.ErrorFormat("{0} Failed creation of 6Dof constraint. rootID={1}, childID={2}",
LogHeader, obj1.ID, obj2.ID);
m_enabled = false;
}
else
{
m_enabled = true;
}
} }
m_enabled = true;
} }
public bool SetFrames(Vector3 frameA, Quaternion frameArot, Vector3 frameB, Quaternion frameBrot) public bool SetFrames(Vector3 frameA, Quaternion frameArot, Vector3 frameB, Quaternion frameBrot)
@ -82,7 +102,7 @@ public class BS6DofConstraint : BSConstraint
bool ret = false; bool ret = false;
if (m_enabled) if (m_enabled)
{ {
BulletSimAPI.SetFrames2(m_constraint.Ptr, frameA, frameArot, frameB, frameBrot); BulletSimAPI.SetFrames2(m_constraint.ptr, frameA, frameArot, frameB, frameBrot);
ret = true; ret = true;
} }
return ret; return ret;
@ -93,9 +113,9 @@ public class BS6DofConstraint : BSConstraint
bool ret = false; bool ret = false;
if (m_enabled) if (m_enabled)
{ {
BulletSimAPI.SetConstraintParam2(m_constraint.Ptr, ConstraintParams.BT_CONSTRAINT_STOP_CFM, cfm, ConstraintParamAxis.AXIS_ALL); BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
BulletSimAPI.SetConstraintParam2(m_constraint.Ptr, ConstraintParams.BT_CONSTRAINT_STOP_ERP, erp, ConstraintParamAxis.AXIS_ALL); BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_ERP, erp, ConstraintParamAxis.AXIS_ALL);
BulletSimAPI.SetConstraintParam2(m_constraint.Ptr, ConstraintParams.BT_CONSTRAINT_CFM, cfm, ConstraintParamAxis.AXIS_ALL); BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
ret = true; ret = true;
} }
return ret; return ret;
@ -106,7 +126,7 @@ public class BS6DofConstraint : BSConstraint
bool ret = false; bool ret = false;
float onOff = useOffset ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse; float onOff = useOffset ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse;
if (m_enabled) if (m_enabled)
ret = BulletSimAPI.UseFrameOffset2(m_constraint.Ptr, onOff); ret = BulletSimAPI.UseFrameOffset2(m_constraint.ptr, onOff);
return ret; return ret;
} }
@ -115,7 +135,7 @@ public class BS6DofConstraint : BSConstraint
bool ret = false; bool ret = false;
float onOff = enable ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse; float onOff = enable ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse;
if (m_enabled) if (m_enabled)
ret = BulletSimAPI.TranslationalLimitMotor2(m_constraint.Ptr, onOff, targetVelocity, maxMotorForce); ret = BulletSimAPI.TranslationalLimitMotor2(m_constraint.ptr, onOff, targetVelocity, maxMotorForce);
return ret; return ret;
} }
@ -123,7 +143,7 @@ public class BS6DofConstraint : BSConstraint
{ {
bool ret = false; bool ret = false;
if (m_enabled) if (m_enabled)
ret = BulletSimAPI.SetBreakingImpulseThreshold2(m_constraint.Ptr, threshold); ret = BulletSimAPI.SetBreakingImpulseThreshold2(m_constraint.ptr, threshold);
return ret; return ret;
} }
} }

View File

@ -49,20 +49,23 @@ public abstract class BSConstraint : IDisposable
if (m_enabled) if (m_enabled)
{ {
m_enabled = false; m_enabled = false;
bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.Ptr); bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr);
m_world.scene.DetailLog("{0},BSConstraint.Dispose,taint,body1={1},body2={2},success={3}", BSScene.DetailLogZero, m_body1.ID, m_body2.ID, success); m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,body1={1},body2={2},success={3}", BSScene.DetailLogZero, m_body1.ID, m_body2.ID, success);
m_constraint.Ptr = System.IntPtr.Zero; m_constraint.ptr = System.IntPtr.Zero;
} }
} }
public BulletBody Body1 { get { return m_body1; } } public BulletBody Body1 { get { return m_body1; } }
public BulletBody Body2 { get { return m_body2; } } public BulletBody Body2 { get { return m_body2; } }
public BulletConstraint Constraint { get { return m_constraint; } }
public abstract ConstraintType Type { get; }
public virtual bool SetLinearLimits(Vector3 low, Vector3 high) public virtual bool SetLinearLimits(Vector3 low, Vector3 high)
{ {
bool ret = false; bool ret = false;
if (m_enabled) if (m_enabled)
ret = BulletSimAPI.SetLinearLimits2(m_constraint.Ptr, low, high); ret = BulletSimAPI.SetLinearLimits2(m_constraint.ptr, low, high);
return ret; return ret;
} }
@ -70,7 +73,7 @@ public abstract class BSConstraint : IDisposable
{ {
bool ret = false; bool ret = false;
if (m_enabled) if (m_enabled)
ret = BulletSimAPI.SetAngularLimits2(m_constraint.Ptr, low, high); ret = BulletSimAPI.SetAngularLimits2(m_constraint.ptr, low, high);
return ret; return ret;
} }
@ -79,7 +82,7 @@ public abstract class BSConstraint : IDisposable
bool ret = false; bool ret = false;
if (m_enabled) if (m_enabled)
{ {
BulletSimAPI.SetConstraintNumSolverIterations2(m_constraint.Ptr, cnt); BulletSimAPI.SetConstraintNumSolverIterations2(m_constraint.ptr, cnt);
ret = true; ret = true;
} }
return ret; return ret;
@ -91,7 +94,7 @@ public abstract class BSConstraint : IDisposable
if (m_enabled) if (m_enabled)
{ {
// Recompute the internal transforms // Recompute the internal transforms
BulletSimAPI.CalculateTransforms2(m_constraint.Ptr); BulletSimAPI.CalculateTransforms2(m_constraint.ptr);
ret = true; ret = true;
} }
return ret; return ret;
@ -110,11 +113,11 @@ public abstract class BSConstraint : IDisposable
// Setting an object's mass to zero (making it static like when it's selected) // Setting an object's mass to zero (making it static like when it's selected)
// automatically disables the constraints. // automatically disables the constraints.
// If the link is enabled, be sure to set the constraint itself to enabled. // If the link is enabled, be sure to set the constraint itself to enabled.
BulletSimAPI.SetConstraintEnable2(m_constraint.Ptr, m_world.scene.NumericBool(true)); BulletSimAPI.SetConstraintEnable2(m_constraint.ptr, m_world.physicsScene.NumericBool(true));
} }
else else
{ {
m_world.scene.Logger.ErrorFormat("[BULLETSIM CONSTRAINT] CalculateTransforms failed. A={0}, B={1}", Body1.ID, Body2.ID); m_world.physicsScene.Logger.ErrorFormat("[BULLETSIM CONSTRAINT] CalculateTransforms failed. A={0}, B={1}", Body1.ID, Body2.ID);
} }
} }
return ret; return ret;

View File

@ -34,6 +34,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin
class BSHingeConstraint : BSConstraint class BSHingeConstraint : BSConstraint
{ {
public override ConstraintType Type { get { return ConstraintType.HINGE_CONSTRAINT_TYPE; } }
public BSHingeConstraint(BulletSim world, BulletBody obj1, BulletBody obj2, public BSHingeConstraint(BulletSim world, BulletBody obj1, BulletBody obj2,
Vector3 pivotInA, Vector3 pivotInB, Vector3 pivotInA, Vector3 pivotInB,
Vector3 axisInA, Vector3 axisInB, Vector3 axisInA, Vector3 axisInB,

View File

@ -88,6 +88,7 @@ public class BSLinkset
// Link to a linkset where the child knows the parent. // Link to a linkset where the child knows the parent.
// Parent changing should not happen so do some sanity checking. // Parent changing should not happen so do some sanity checking.
// We return the parent's linkset so the child can track its membership. // We return the parent's linkset so the child can track its membership.
// Called at runtime.
public BSLinkset AddMeToLinkset(BSPhysObject child) public BSLinkset AddMeToLinkset(BSPhysObject child)
{ {
lock (m_linksetActivityLock) lock (m_linksetActivityLock)
@ -102,6 +103,7 @@ public class BSLinkset
// Remove a child from a linkset. // Remove a child from a linkset.
// Returns a new linkset for the child which is a linkset of one (just the // Returns a new linkset for the child which is a linkset of one (just the
// orphened child). // orphened child).
// Called at runtime.
public BSLinkset RemoveMeFromLinkset(BSPhysObject child) public BSLinkset RemoveMeFromLinkset(BSPhysObject child)
{ {
lock (m_linksetActivityLock) lock (m_linksetActivityLock)
@ -113,27 +115,6 @@ public class BSLinkset
} }
RemoveChildFromLinkset(child); RemoveChildFromLinkset(child);
/* Alternate implementation that destroys the linkset of the root is removed.
* This fails because items are added and removed from linksets to build shapes.
* Code left for reference.
if (IsRoot(child))
{
// if root of linkset, take the linkset apart
while (m_children.Count > 0)
{
// Note that we don't do a foreach because the remove routine
// takes it out of the list.
RemoveChildFromOtherLinkset(m_children[0]);
}
m_children.Clear(); // just to make sure
}
else
{
// Just removing a child from an existing linkset
RemoveChildFromLinkset(child);
}
*/
} }
// The child is down to a linkset of just itself // The child is down to a linkset of just itself
@ -169,6 +150,106 @@ public class BSLinkset
return ret; return ret;
} }
// The object is going dynamic (physical). Do any setup necessary
// for a dynamic linkset.
// Only the state of the passed object can be modified. The rest of the linkset
// has not yet been fully constructed.
// Return 'true' if any properties updated on the passed object.
// Called at taint-time!
public bool MakeDynamic(BSPhysObject child)
{
// What is done for each object in BSPrim is what we want.
return false;
}
// The object is going static (non-physical). Do any setup necessary
// for a static linkset.
// Return 'true' if any properties updated on the passed object.
// Called at taint-time!
public bool MakeStatic(BSPhysObject child)
{
// What is done for each object in BSPrim is what we want.
return false;
}
// When physical properties are changed the linkset needs to recalculate
// its internal properties.
// Called at runtime.
public void Refresh(BSPhysObject requestor)
{
// If there are no children, there can't be any constraints to recompute
if (!HasAnyChildren)
return;
// Only the root does the recomputation
if (IsRoot(requestor))
{
PhysicsScene.TaintedObject("BSLinkSet.Refresh", delegate()
{
RecomputeLinksetConstraintVariables();
});
}
}
// Routine used when rebuilding the body of the root of the linkset
// Destroy all the constraints have have been made to root.
// This is called when the root body is changing.
// Called at taint-time!!
public void RemoveBodyDependencies(BSPrim child)
{
lock (m_linksetActivityLock)
{
if (IsRoot(child))
{
// If the one with the dependency is root, must undo all children
DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeChildrenForRoot,rID={1},numChild={2}",
LinksetRoot.LocalID, m_children.Count);
foreach (BSPhysObject bpo in m_children)
{
PhysicallyUnlinkAChildFromRoot(LinksetRoot, LinksetRoot.BSBody, bpo, bpo.BSBody);
}
}
else
{
DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeSingleChild,rID={1},rBody={2},cID={3},cBody={4}",
LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"),
child.LocalID, child.BSBody.ptr.ToString("X"));
// Remove the dependency on the body of this one
PhysicallyUnlinkAChildFromRoot(LinksetRoot, LinksetRoot.BSBody, child, child.BSBody);
}
}
}
// Routine used when rebuilding the body of the root of the linkset
// This is called after RemoveAllLinksToRoot() to restore all the constraints.
// This is called when the root body has been changed.
// Called at taint-time!!
public void RestoreBodyDependencies(BSPrim child)
{
lock (m_linksetActivityLock)
{
if (IsRoot(child))
{
DetailLog("{0},BSLinkset.RestoreBodyDependencies,restoreChildrenForRoot,rID={1},numChild={2}",
LinksetRoot.LocalID, m_children.Count);
foreach (BSPhysObject bpo in m_children)
{
PhysicallyLinkAChildToRoot(LinksetRoot, LinksetRoot.BSBody, bpo, bpo.BSBody);
}
}
else
{
DetailLog("{0},BSLinkset.RestoreBodyDependencies,restoreSingleChild,rID={1},rBody={2},cID={3},cBody={4}",
LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"),
child.LocalID, child.BSBody.ptr.ToString("X"));
PhysicallyLinkAChildToRoot(LinksetRoot, LinksetRoot.BSBody, child, child.BSBody);
}
}
}
// ================================================================
// Below this point is internal magic
private float ComputeLinksetMass() private float ComputeLinksetMass()
{ {
float mass; float mass;
@ -220,46 +301,6 @@ public class BSLinkset
return com; return com;
} }
// The object is going dynamic (physical). Do any setup necessary
// for a dynamic linkset.
// Only the state of the passed object can be modified. The rest of the linkset
// has not yet been fully constructed.
// Return 'true' if any properties updated on the passed object.
// Called at taint-time!
public bool MakeDynamic(BSPhysObject child)
{
// What is done for each object in BSPrim is what we want.
return false;
}
// The object is going static (non-physical). Do any setup necessary
// for a static linkset.
// Return 'true' if any properties updated on the passed object.
// Called at taint-time!
public bool MakeStatic(BSPhysObject child)
{
// What is done for each object in BSPrim is what we want.
return false;
}
// When physical properties are changed the linkset needs to recalculate
// its internal properties.
public void Refresh(BSPhysObject requestor)
{
// If there are no children, there can't be any constraints to recompute
if (!HasAnyChildren)
return;
// Only the root does the recomputation
if (IsRoot(requestor))
{
PhysicsScene.TaintedObject("BSLinkSet.Refresh", delegate()
{
RecomputeLinksetConstraintVariables();
});
}
}
// Call each of the constraints that make up this linkset and recompute the // Call each of the constraints that make up this linkset and recompute the
// various transforms and variables. Used when objects are added or removed // various transforms and variables. Used when objects are added or removed
// from a linkset to make sure the constraints know about the new mass and // from a linkset to make sure the constraints know about the new mass and
@ -327,6 +368,11 @@ public class BSLinkset
BSPhysObject childx = child; BSPhysObject childx = child;
BulletBody childBodyx = child.BSBody; BulletBody childBodyx = child.BSBody;
DetailLog("{0},AddChildToLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
rootx.LocalID,
rootx.LocalID, rootBodyx.ptr.ToString("X"),
childx.LocalID, childBodyx.ptr.ToString("X"));
PhysicsScene.TaintedObject("AddChildToLinkset", delegate() PhysicsScene.TaintedObject("AddChildToLinkset", delegate()
{ {
DetailLog("{0},AddChildToLinkset,taint,child={1}", LinksetRoot.LocalID, child.LocalID); DetailLog("{0},AddChildToLinkset,taint,child={1}", LinksetRoot.LocalID, child.LocalID);
@ -358,10 +404,14 @@ public class BSLinkset
BulletBody rootBodyx = LinksetRoot.BSBody; BulletBody rootBodyx = LinksetRoot.BSBody;
BSPhysObject childx = child; BSPhysObject childx = child;
BulletBody childBodyx = child.BSBody; BulletBody childBodyx = child.BSBody;
DetailLog("{0},RemoveChildFromLinkset,call,child={1}",
rootx.LocalID,
rootx.LocalID, rootBodyx.ptr.ToString("X"),
childx.LocalID, childBodyx.ptr.ToString("X"));
PhysicsScene.TaintedObject("RemoveChildFromLinkset", delegate() PhysicsScene.TaintedObject("RemoveChildFromLinkset", delegate()
{ {
DetailLog("{0},RemoveChildFromLinkset,taint,child={1}", LinksetRoot.LocalID, child.LocalID);
PhysicallyUnlinkAChildFromRoot(rootx, rootBodyx, childx, childBodyx); PhysicallyUnlinkAChildFromRoot(rootx, rootBodyx, childx, childBodyx);
RecomputeLinksetConstraintVariables(); RecomputeLinksetConstraintVariables();
}); });
@ -390,14 +440,15 @@ public class BSLinkset
// real world coordinate of midpoint between the two objects // real world coordinate of midpoint between the two objects
OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2); OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2);
// create a constraint that allows no freedom of movement between the two objects
// http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
DetailLog("{0},PhysicallyLinkAChildToRoot,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}", DetailLog("{0},PhysicallyLinkAChildToRoot,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}",
rootPrim.LocalID, rootPrim.LocalID,
rootPrim.LocalID, rootBody.ptr.ToString("X"), rootPrim.LocalID, rootBody.ptr.ToString("X"),
childPrim.LocalID, childBody.ptr.ToString("X"), childPrim.LocalID, childBody.ptr.ToString("X"),
rootPrim.Position, childPrim.Position, midPoint); rootPrim.Position, childPrim.Position, midPoint);
// create a constraint that allows no freedom of movement between the two objects
// http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
// There is great subtlty in these paramters. Notice the check for a ptr of zero. // There is great subtlty in these paramters. Notice the check for a ptr of zero.
// We pass the BulletBody structure into the taint in order to capture the pointer // We pass the BulletBody structure into the taint in order to capture the pointer
// of the body at the time of constraint creation. This doesn't work for the very first // of the body at the time of constraint creation. This doesn't work for the very first
@ -416,6 +467,7 @@ public class BSLinkset
true, true,
true true
); );
/* NOTE: below is an attempt to build constraint with full frame computation, etc. /* NOTE: below is an attempt to build constraint with full frame computation, etc.
* Using the midpoint is easier since it lets the Bullet code manipulate the transforms * Using the midpoint is easier since it lets the Bullet code manipulate the transforms
* of the objects. * of the objects.

View File

@ -1111,13 +1111,21 @@ public sealed class BSPrim : BSPhysObject
// Undo me from any possible linkset so, if body is rebuilt, the link will get restored. // Undo me from any possible linkset so, if body is rebuilt, the link will get restored.
// NOTE that the new linkset is not set. This saves the handle to the linkset // NOTE that the new linkset is not set. This saves the handle to the linkset
// so we can add ourselves back when shape mangling is complete. // so we can add ourselves back when shape mangling is complete.
Linkset.RemoveMeFromLinkset(this); bool needToRestoreLinkset = false;
// Create the correct physical representation for this type of object. // Create the correct physical representation for this type of object.
// Updates BSBody and BSShape with the new information. // Updates BSBody and BSShape with the new information.
PhysicsScene.Shapes.GetBodyAndShape(forceRebuild, PhysicsScene.World, this, shapeData, _pbs); PhysicsScene.Shapes.GetBodyAndShape(forceRebuild, PhysicsScene.World, this, shapeData, _pbs,
null, delegate(BulletBody dBody)
{
// Called if the current prim body is about to be destroyed.
// The problem is the constraints for Linksets which need to be updated for the new body.
Linkset.RemoveBodyDependencies(this);
needToRestoreLinkset = true;
});
Linkset = Linkset.AddMeToLinkset(this); if (needToRestoreLinkset)
Linkset.RestoreBodyDependencies(this);
// Make sure the properties are set on the new object // Make sure the properties are set on the new object
UpdatePhysicalParameters(); UpdatePhysicalParameters();

View File

@ -81,12 +81,21 @@ public class BSShapeCollection : IDisposable
// TODO!!!!!!!!! // TODO!!!!!!!!!
} }
// Callbacks called just before either the body or shape is destroyed.
// Mostly used for changing bodies out from under Linksets.
// Useful for other cases where parameters need saving.
// Passing 'null' says no callback.
public delegate void ShapeDestructionCallback(BulletShape shape);
public delegate void BodyDestructionCallback(BulletBody body);
// Called to update/change the body and shape for an object. // Called to update/change the body and shape for an object.
// First checks the shape and updates that if necessary then makes // First checks the shape and updates that if necessary then makes
// sure the body is of the right type. // sure the body is of the right type.
// Return 'true' if either the body or the shape changed. // Return 'true' if either the body or the shape changed.
// Called at taint-time!! // Called at taint-time!!
public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs) public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPrim prim,
ShapeData shapeData, PrimitiveBaseShape pbs,
ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback)
{ {
bool ret = false; bool ret = false;
@ -95,11 +104,11 @@ public class BSShapeCollection : IDisposable
{ {
// Do we have the correct geometry for this type of object? // Do we have the correct geometry for this type of object?
// Updates prim.BSShape with information/pointers to requested shape // Updates prim.BSShape with information/pointers to requested shape
bool newGeom = CreateGeom(forceRebuild, prim, shapeData, pbs); bool newGeom = CreateGeom(forceRebuild, prim, shapeData, pbs, shapeCallback);
// If we had to select a new shape geometry for the object, // If we had to select a new shape geometry for the object,
// rebuild the body around it. // rebuild the body around it.
// Updates prim.BSBody with information/pointers to requested body // Updates prim.BSBody with information/pointers to requested body
bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World, prim.BSShape, shapeData); bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World, prim.BSShape, shapeData, bodyCallback);
ret = newGeom || newBody; ret = newGeom || newBody;
} }
DetailLog("{0},BSShapeCollection.GetBodyAndShape,force={1},ret={2},body={3},shape={4}", DetailLog("{0},BSShapeCollection.GetBodyAndShape,force={1},ret={2},body={3},shape={4}",
@ -135,7 +144,7 @@ public class BSShapeCollection : IDisposable
// Release the usage of a body. // Release the usage of a body.
// Called when releasing use of a BSBody. BSShape is handled separately. // Called when releasing use of a BSBody. BSShape is handled separately.
public void DereferenceBody(BulletBody shape, bool inTaintTime) public void DereferenceBody(BulletBody shape, bool inTaintTime, BodyDestructionCallback bodyCallback )
{ {
if (shape.ptr == IntPtr.Zero) if (shape.ptr == IntPtr.Zero)
return; return;
@ -244,7 +253,7 @@ public class BSShapeCollection : IDisposable
// Release the usage of a shape. // Release the usage of a shape.
// The collisionObject is released since it is a copy of the real collision shape. // The collisionObject is released since it is a copy of the real collision shape.
private void DereferenceShape(BulletShape shape, bool atTaintTime) private void DereferenceShape(BulletShape shape, bool atTaintTime, ShapeDestructionCallback shapeCallback)
{ {
if (shape.ptr == IntPtr.Zero) if (shape.ptr == IntPtr.Zero)
return; return;
@ -254,10 +263,10 @@ public class BSShapeCollection : IDisposable
switch (shape.type) switch (shape.type)
{ {
case ShapeData.PhysicsShapeType.SHAPE_HULL: case ShapeData.PhysicsShapeType.SHAPE_HULL:
DereferenceHull(shape); DereferenceHull(shape, shapeCallback);
break; break;
case ShapeData.PhysicsShapeType.SHAPE_MESH: case ShapeData.PhysicsShapeType.SHAPE_MESH:
DereferenceMesh(shape); DereferenceMesh(shape, shapeCallback);
break; break;
case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN: case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN:
break; break;
@ -267,6 +276,7 @@ public class BSShapeCollection : IDisposable
{ {
DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}", DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}",
BSScene.DetailLogZero, shape.ptr.ToString("X"), atTaintTime); BSScene.DetailLogZero, shape.ptr.ToString("X"), atTaintTime);
if (shapeCallback != null) shapeCallback(shape);
BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr); BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
} }
break; break;
@ -287,13 +297,14 @@ public class BSShapeCollection : IDisposable
// Count down the reference count for a mesh shape // Count down the reference count for a mesh shape
// Called at taint-time. // Called at taint-time.
private void DereferenceMesh(BulletShape shape) private void DereferenceMesh(BulletShape shape, ShapeDestructionCallback shapeCallback)
{ {
MeshDesc meshDesc; MeshDesc meshDesc;
if (Meshes.TryGetValue(shape.shapeKey, out meshDesc)) if (Meshes.TryGetValue(shape.shapeKey, out meshDesc))
{ {
meshDesc.referenceCount--; meshDesc.referenceCount--;
// TODO: release the Bullet storage // TODO: release the Bullet storage
if (shapeCallback != null) shapeCallback(shape);
meshDesc.lastReferenced = System.DateTime.Now; meshDesc.lastReferenced = System.DateTime.Now;
Meshes[shape.shapeKey] = meshDesc; Meshes[shape.shapeKey] = meshDesc;
DetailLog("{0},BSShapeCollection.DereferenceMesh,key={1},refCnt={2}", DetailLog("{0},BSShapeCollection.DereferenceMesh,key={1},refCnt={2}",
@ -304,13 +315,14 @@ public class BSShapeCollection : IDisposable
// Count down the reference count for a hull shape // Count down the reference count for a hull shape
// Called at taint-time. // Called at taint-time.
private void DereferenceHull(BulletShape shape) private void DereferenceHull(BulletShape shape, ShapeDestructionCallback shapeCallback)
{ {
HullDesc hullDesc; HullDesc hullDesc;
if (Hulls.TryGetValue(shape.shapeKey, out hullDesc)) if (Hulls.TryGetValue(shape.shapeKey, out hullDesc))
{ {
hullDesc.referenceCount--; hullDesc.referenceCount--;
// TODO: release the Bullet storage (aging old entries?) // TODO: release the Bullet storage (aging old entries?)
if (shapeCallback != null) shapeCallback(shape);
hullDesc.lastReferenced = System.DateTime.Now; hullDesc.lastReferenced = System.DateTime.Now;
Hulls[shape.shapeKey] = hullDesc; Hulls[shape.shapeKey] = hullDesc;
DetailLog("{0},BSShapeCollection.DereferenceHull,key={1},refCnt={2}", DetailLog("{0},BSShapeCollection.DereferenceHull,key={1},refCnt={2}",
@ -324,7 +336,8 @@ public class BSShapeCollection : IDisposable
// if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used. // if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used.
// Returns 'true' if the geometry was rebuilt. // Returns 'true' if the geometry was rebuilt.
// Called at taint-time! // Called at taint-time!
private bool CreateGeom(bool forceRebuild, BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs) private bool CreateGeom(bool forceRebuild, BSPrim prim, ShapeData shapeData,
PrimitiveBaseShape pbs, ShapeDestructionCallback shapeCallback)
{ {
bool ret = false; bool ret = false;
bool haveShape = false; bool haveShape = false;
@ -349,8 +362,8 @@ public class BSShapeCollection : IDisposable
|| prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_SPHERE || prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_SPHERE
) )
{ {
ret = GetReferenceToNativeShape(prim, shapeData, ret = GetReferenceToNativeShape(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_SPHERE,
ShapeData.PhysicsShapeType.SHAPE_SPHERE, ShapeData.FixedShapeKey.KEY_SPHERE); ShapeData.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.BSShape); prim.LocalID, forceRebuild, prim.BSShape);
} }
@ -363,8 +376,8 @@ public class BSShapeCollection : IDisposable
|| prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_BOX || prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_BOX
) )
{ {
ret = GetReferenceToNativeShape( ret = GetReferenceToNativeShape( prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_BOX,
prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_BOX, ShapeData.FixedShapeKey.KEY_BOX); ShapeData.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.BSShape); prim.LocalID, forceRebuild, prim.BSShape);
} }
@ -378,13 +391,13 @@ public class BSShapeCollection : IDisposable
if (prim.IsPhysical) if (prim.IsPhysical)
{ {
// Update prim.BSShape to reference a hull of this shape. // Update prim.BSShape to reference a hull of this shape.
ret = GetReferenceToHull(prim, shapeData, pbs); ret = GetReferenceToHull(prim, shapeData, pbs, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}", DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}",
shapeData.ID, prim.BSShape, prim.BSShape.shapeKey.ToString("X")); shapeData.ID, prim.BSShape, prim.BSShape.shapeKey.ToString("X"));
} }
else else
{ {
ret = GetReferenceToMesh(prim, shapeData, pbs); ret = GetReferenceToMesh(prim, shapeData, pbs, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}", DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}",
shapeData.ID, prim.BSShape, prim.BSShape.shapeKey.ToString("X")); shapeData.ID, prim.BSShape, prim.BSShape.shapeKey.ToString("X"));
} }
@ -394,7 +407,8 @@ public class BSShapeCollection : IDisposable
// Creates a native shape and assignes it to prim.BSShape // Creates a native shape and assignes it to prim.BSShape
private bool GetReferenceToNativeShape( BSPrim prim, ShapeData shapeData, private bool GetReferenceToNativeShape( BSPrim prim, ShapeData shapeData,
ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey) ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey,
ShapeDestructionCallback shapeCallback)
{ {
BulletShape newShape; BulletShape newShape;
@ -404,7 +418,7 @@ public class BSShapeCollection : IDisposable
shapeData.Scale = shapeData.Size; shapeData.Scale = shapeData.Size;
// release any previous shape // release any previous shape
DereferenceShape(prim.BSShape, true); DereferenceShape(prim.BSShape, true, shapeCallback);
// Native shapes are always built independently. // Native shapes are always built independently.
newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, shapeData), shapeType); newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, shapeData), shapeType);
@ -422,7 +436,8 @@ public class BSShapeCollection : IDisposable
// Dereferences previous shape in BSShape and adds a reference for this new shape. // Dereferences previous shape in BSShape and adds a reference for this new shape.
// Returns 'true' of a mesh was actually built. Otherwise . // Returns 'true' of a mesh was actually built. Otherwise .
// Called at taint-time! // Called at taint-time!
private bool GetReferenceToMesh(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs) private bool GetReferenceToMesh(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs,
ShapeDestructionCallback shapeCallback)
{ {
BulletShape newShape = new BulletShape(IntPtr.Zero); BulletShape newShape = new BulletShape(IntPtr.Zero);
@ -436,7 +451,7 @@ public class BSShapeCollection : IDisposable
prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newMeshKey.ToString("X")); prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newMeshKey.ToString("X"));
// Since we're recreating new, get rid of the reference to the previous shape // Since we're recreating new, get rid of the reference to the previous shape
DereferenceShape(prim.BSShape, true); DereferenceShape(prim.BSShape, true, shapeCallback);
newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, pbs, shapeData.Size, lod); newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, pbs, shapeData.Size, lod);
@ -490,7 +505,8 @@ public class BSShapeCollection : IDisposable
// See that hull shape exists in the physical world and update prim.BSShape. // See that hull shape exists in the physical world and update prim.BSShape.
// We could be creating the hull because scale changed or whatever. // We could be creating the hull because scale changed or whatever.
private bool GetReferenceToHull(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs) private bool GetReferenceToHull(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs,
ShapeDestructionCallback shapeCallback)
{ {
BulletShape newShape; BulletShape newShape;
@ -505,7 +521,7 @@ public class BSShapeCollection : IDisposable
prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newHullKey.ToString("X")); prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newHullKey.ToString("X"));
// Remove usage of the previous shape. Also removes reference to underlying mesh if it is a hull. // Remove usage of the previous shape. Also removes reference to underlying mesh if it is a hull.
DereferenceShape(prim.BSShape, true); DereferenceShape(prim.BSShape, true, shapeCallback);
newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, pbs, shapeData.Size, lod); newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, pbs, shapeData.Size, lod);
@ -656,7 +672,8 @@ public class BSShapeCollection : IDisposable
// Updates prim.BSBody with the information about the new body if one is created. // Updates prim.BSBody with the information about the new body if one is created.
// Returns 'true' if an object was actually created. // Returns 'true' if an object was actually created.
// Called at taint-time. // Called at taint-time.
private bool CreateBody(bool forceRebuild, BSPrim prim, BulletSim sim, BulletShape shape, ShapeData shapeData) private bool CreateBody(bool forceRebuild, BSPrim prim, BulletSim sim, BulletShape shape,
ShapeData shapeData, BodyDestructionCallback bodyCallback)
{ {
bool ret = false; bool ret = false;
@ -679,7 +696,7 @@ public class BSShapeCollection : IDisposable
if (mustRebuild || forceRebuild) if (mustRebuild || forceRebuild)
{ {
DereferenceBody(prim.BSBody, true); DereferenceBody(prim.BSBody, true, bodyCallback);
BulletBody aBody; BulletBody aBody;
IntPtr bodyPtr = IntPtr.Zero; IntPtr bodyPtr = IntPtr.Zero;

View File

@ -42,12 +42,12 @@ public struct BulletSim
{ {
ptr = xx; ptr = xx;
worldID = worldId; worldID = worldId;
scene = bss; physicsScene = bss;
} }
public IntPtr ptr; public IntPtr ptr;
public uint worldID; public uint worldID;
// The scene is only in here so very low level routines have a handle to print debug/error messages // The scene is only in here so very low level routines have a handle to print debug/error messages
public BSScene scene; public BSScene physicsScene;
} }
// An allocated Bullet btRigidBody // An allocated Bullet btRigidBody
@ -120,14 +120,27 @@ public struct BulletShape
} }
} }
// Constraint type values as defined by Bullet
public enum ConstraintType : int
{
POINT2POINT_CONSTRAINT_TYPE = 3,
HINGE_CONSTRAINT_TYPE,
CONETWIST_CONSTRAINT_TYPE,
D6_CONSTRAINT_TYPE,
SLIDER_CONSTRAINT_TYPE,
CONTACT_CONSTRAINT_TYPE,
D6_SPRING_CONSTRAINT_TYPE,
MAX_CONSTRAINT_TYPE
}
// An allocated Bullet btConstraint // An allocated Bullet btConstraint
public struct BulletConstraint public struct BulletConstraint
{ {
public BulletConstraint(IntPtr xx) public BulletConstraint(IntPtr xx)
{ {
Ptr = xx; ptr = xx;
} }
public IntPtr Ptr; public IntPtr ptr;
} }
// An allocated HeightMapThing which holds various heightmap info. // An allocated HeightMapThing which holds various heightmap info.