BulletSim: Move construction of compound linkset from ShapeCollection

into LinksetCompound where it should be.
Create meshes for native shapes when part of a compound linkset because
    scale is currently per object and not per collision shape.
Don't schedule a LinksetCompound refresh if just changing properties.
integration
Robert Adams 2012-11-02 17:22:34 -07:00
parent 1dc23b2b97
commit 498ea76e63
3 changed files with 121 additions and 62 deletions

View File

@ -34,7 +34,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
{ {
public sealed class BSLinksetCompound : BSLinkset public sealed class BSLinksetCompound : BSLinkset
{ {
// private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINTS]"; private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]";
public BSLinksetCompound(BSScene scene, BSPhysObject parent) public BSLinksetCompound(BSScene scene, BSPhysObject parent)
{ {
@ -58,6 +58,12 @@ public sealed class BSLinksetCompound : BSLinkset
// This is queued in the 'post taint' queue so the // This is queued in the 'post taint' queue so the
// refresh will happen once after all the other taints are applied. // refresh will happen once after all the other taints are applied.
public override void Refresh(BSPhysObject requestor) public override void Refresh(BSPhysObject requestor)
{
// External request for Refresh (from BSPrim) is not necessary
// InternalRefresh(requestor);
}
private void InternalRefresh(BSPhysObject requestor)
{ {
DetailLog("{0},BSLinksetCompound.Refresh,schedulingRefresh,requestor={1}", LinksetRoot.LocalID, requestor.LocalID); DetailLog("{0},BSLinksetCompound.Refresh,schedulingRefresh,requestor={1}", LinksetRoot.LocalID, requestor.LocalID);
// Queue to happen after all the other taint processing // Queue to happen after all the other taint processing
@ -135,13 +141,13 @@ public sealed class BSLinksetCompound : BSLinkset
{ {
bool ret = false; bool ret = false;
DetailLog("{0},BSLinksetCompound.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2},isRoot={3}", DetailLog("{0},BSLinksetCompound.RemoveBodyDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}",
child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString("X"), IsRoot(child)); child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString("X"), IsRoot(child));
if (!IsRoot(child)) if (!IsRoot(child))
{ {
// Cause the current shape to be freed and the new one to be built. // Cause the current shape to be freed and the new one to be built.
Refresh(LinksetRoot); InternalRefresh(LinksetRoot);
ret = true; ret = true;
} }
@ -169,7 +175,7 @@ public sealed class BSLinksetCompound : BSLinkset
DetailLog("{0},BSLinksetCompound.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID); DetailLog("{0},BSLinksetCompound.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
// Cause constraints and assorted properties to be recomputed before the next simulation step. // Cause constraints and assorted properties to be recomputed before the next simulation step.
Refresh(LinksetRoot); InternalRefresh(LinksetRoot);
} }
return; return;
} }
@ -196,34 +202,68 @@ public sealed class BSLinksetCompound : BSLinkset
else else
{ {
// Schedule a rebuild of the linkset before the next simulation tick. // Schedule a rebuild of the linkset before the next simulation tick.
Refresh(LinksetRoot); InternalRefresh(LinksetRoot);
} }
} }
return; return;
} }
// Called before the simulation step to make sure the compound based linkset
// Call each of the constraints that make up this linkset and recompute the
// various transforms and variables. Create constraints of not created yet.
// Called before the simulation step to make sure the constraint based linkset
// is all initialized. // is all initialized.
// Constraint linksets are rebuilt every time.
// Note that this works for rebuilding just the root after a linkset is taken apart.
// Called at taint time!! // Called at taint time!!
private void RecomputeLinksetCompound() private void RecomputeLinksetCompound()
{ {
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},numChildren={2}", // Cause the root shape to be rebuilt as a compound object with just the root in it
LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString("X"), NumberOfChildren);
LinksetRoot.ForceBodyShapeRebuild(true); LinksetRoot.ForceBodyShapeRebuild(true);
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}",
LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren);
ForEachMember(delegate(BSPhysObject cPrim)
{
if (!IsRoot(cPrim))
{
OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation);
OMV.Vector3 displacementPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation;
OMV.Quaternion displacementRot = cPrim.RawOrientation * invRootOrientation;
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addMemberToShape,mID={1},mShape={2},dispPos={3},dispRot={4}",
LinksetRoot.LocalID, cPrim.LocalID, cPrim.PhysShape, displacementPos, displacementRot);
if (cPrim.PhysShape.isNativeShape)
{
// Native shapes are not shared so we need to create a new one.
// A mesh or hull is created because scale is not available on a native shape.
// (TODO: Bullet does have a btScaledCollisionShape. Can that be used?)
BulletShape saveShape = cPrim.PhysShape;
PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null);
BulletShape newShape = cPrim.PhysShape;
cPrim.PhysShape = saveShape;
BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, newShape.ptr, displacementPos, displacementRot);
}
else
{
// For the shared shapes (meshes and hulls) just use the shape in the child
if (PhysicsScene.Shapes.ReferenceShape(cPrim.PhysShape))
{
PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}",
LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape);
}
BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, cPrim.PhysShape.ptr, displacementPos, displacementRot);
}
}
return false;
});
float linksetMass = LinksetMass; float linksetMass = LinksetMass;
LinksetRoot.UpdatePhysicalMassProperties(linksetMass); LinksetRoot.UpdatePhysicalMassProperties(linksetMass);
// DEBUG: see of inter-linkset collisions are causing problems // DEBUG: see of inter-linkset collisions are causing problems
// BulletSimAPI.SetCollisionFilterMask2(LinksetRoot.BSBody.ptr, // BulletSimAPI.SetCollisionFilterMask2(LinksetRoot.BSBody.ptr,
// (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask); // (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,end,rBody={1},linksetMass={2}",
LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString("X"), linksetMass);
} }
} }

View File

@ -116,6 +116,10 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
// True if initialized and ready to do simulation steps // True if initialized and ready to do simulation steps
private bool m_initialized = false; private bool m_initialized = false;
// Flag which is true when processing taints.
// Not guaranteed to be correct all the time (don't depend on this) but good for debugging.
public bool InTaintTime { get; private set; }
// Pinned memory used to pass step information between managed and unmanaged // Pinned memory used to pass step information between managed and unmanaged
private int m_maxCollisionsPerFrame; private int m_maxCollisionsPerFrame;
private CollisionDesc[] m_collisionArray; private CollisionDesc[] m_collisionArray;
@ -270,6 +274,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
TerrainManager = new BSTerrainManager(this); TerrainManager = new BSTerrainManager(this);
TerrainManager.CreateInitialGroundPlaneAndTerrain(); TerrainManager.CreateInitialGroundPlaneAndTerrain();
m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)Params.linksetImplementation);
InTaintTime = false;
m_initialized = true; m_initialized = true;
} }
@ -707,8 +714,10 @@ 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;
ProcessRegularTaints(); ProcessRegularTaints();
ProcessPostTaintTaints(); ProcessPostTaintTaints();
InTaintTime = false;
} }
private void ProcessRegularTaints() private void ProcessRegularTaints()
@ -851,6 +860,17 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
} }
} }
public bool AssertInTaintTime(string whereFrom)
{
if (!InTaintTime)
{
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);
Util.PrintCallStack();
}
return InTaintTime;
}
#endregion // Taints #endregion // Taints
#region Vehicles #region Vehicles
@ -1214,8 +1234,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
(s) => { return s.m_params[0].numberOfSolverIterations; }, (s) => { return s.m_params[0].numberOfSolverIterations; },
(s,p,l,v) => { s.m_params[0].numberOfSolverIterations = v; } ), (s,p,l,v) => { s.m_params[0].numberOfSolverIterations = v; } ),
new ParameterDefn("LinksetImplementation", "Type of linkset implementation (0=Constraint, 1=Compound)", new ParameterDefn("LinksetImplementation", "Type of linkset implementation (0=Constraint, 1=Compound, 2=Manual)",
(float)BSLinkset.LinksetImplementation.Constraint, (float)BSLinkset.LinksetImplementation.Compound,
(s,cf,p,v) => { s.m_params[0].linksetImplementation = cf.GetFloat(p,v); }, (s,cf,p,v) => { s.m_params[0].linksetImplementation = cf.GetFloat(p,v); },
(s) => { return s.m_params[0].linksetImplementation; }, (s) => { return s.m_params[0].linksetImplementation; },
(s,p,l,v) => { s.m_params[0].linksetImplementation = v; } ), (s,p,l,v) => { s.m_params[0].linksetImplementation = v; } ),

View File

@ -92,6 +92,8 @@ public sealed class BSShapeCollection : IDisposable
public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim, public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim,
ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback) ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback)
{ {
PhysicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape");
bool ret = false; bool ret = false;
// This lock could probably be pushed down lower but building shouldn't take long // This lock could probably be pushed down lower but building shouldn't take long
@ -121,7 +123,7 @@ public sealed class BSShapeCollection : IDisposable
{ {
lock (m_collectionActivityLock) lock (m_collectionActivityLock)
{ {
DetailLog("{0},BSShapeCollection.ReferenceBody,newBody", body.ID, body); DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body);
PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.ReferenceBody", delegate() PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.ReferenceBody", delegate()
{ {
if (!BulletSimAPI.IsInWorld2(body.ptr)) if (!BulletSimAPI.IsInWorld2(body.ptr))
@ -165,7 +167,7 @@ public sealed class BSShapeCollection : IDisposable
// Meshes and hulls for the same shape have the same hash key. // Meshes and hulls for the same shape have the same hash key.
// NOTE that native shapes are not added to the mesh list or removed. // NOTE that native shapes are not added to the mesh list or removed.
// Returns 'true' if this is the initial reference to the shape. Otherwise reused. // Returns 'true' if this is the initial reference to the shape. Otherwise reused.
private bool ReferenceShape(BulletShape shape) public bool ReferenceShape(BulletShape shape)
{ {
bool ret = false; bool ret = false;
switch (shape.type) switch (shape.type)
@ -276,8 +278,8 @@ public sealed class BSShapeCollection : IDisposable
if (shapeCallback != null) shapeCallback(shape); 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,shape={1},refCnt={2}",
BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); BSScene.DetailLogZero, shape, meshDesc.referenceCount);
} }
} }
@ -297,8 +299,8 @@ public sealed class BSShapeCollection : IDisposable
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,shape={1},refCnt={2}",
BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); BSScene.DetailLogZero, shape, hullDesc.referenceCount);
} }
} }
@ -319,8 +321,11 @@ public sealed class BSShapeCollection : IDisposable
BSScene.DetailLogZero, shape.type, shape.ptr.ToString("X")); BSScene.DetailLogZero, shape.type, shape.ptr.ToString("X"));
return; return;
} }
int numChildren = BulletSimAPI.GetNumberOfCompoundChildren2(shape.ptr); int numChildren = BulletSimAPI.GetNumberOfCompoundChildren2(shape.ptr);
for (int ii = 0; ii < numChildren; ii++) DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", BSScene.DetailLogZero, shape, numChildren);
for (int ii = numChildren - 1; ii >= 0; ii--)
{ {
IntPtr childShape = BulletSimAPI.RemoveChildShapeFromCompoundShapeIndex2(shape.ptr, ii); IntPtr childShape = BulletSimAPI.RemoveChildShapeFromCompoundShapeIndex2(shape.ptr, ii);
DereferenceAnonCollisionShape(childShape); DereferenceAnonCollisionShape(childShape);
@ -343,6 +348,7 @@ public sealed class BSShapeCollection : IDisposable
shapeInfo.isNativeShape = true; shapeInfo.isNativeShape = true;
shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_BOX; // (technically, type doesn't matter) shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_BOX; // (technically, type doesn't matter)
} }
DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo);
DereferenceShape(shapeInfo, true, null); DereferenceShape(shapeInfo, true, null);
} }
@ -440,23 +446,32 @@ public sealed class BSShapeCollection : IDisposable
} }
// If a simple shape is not happening, create a mesh and possibly a hull. // If a simple shape is not happening, create a mesh and possibly a hull.
// Note that if it's a native shape, the check for physical/non-physical is not
// made. Native shapes work in either case.
if (!haveShape && pbs != null) if (!haveShape && pbs != null)
{ {
if (prim.IsPhysical && PhysicsScene.ShouldUseHullsForPhysicalObjects) ret = CreateGeomMeshOrHull(prim, shapeCallback);
{ }
// Update prim.BSShape to reference a hull of this shape.
ret = GetReferenceToHull(prim,shapeCallback); return ret;
DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}", }
prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X"));
} public bool CreateGeomMeshOrHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
else {
{
ret = GetReferenceToMesh(prim, shapeCallback); bool ret = false;
DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}", // Note that if it's a native shape, the check for physical/non-physical is not
prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X")); // made. Native shapes work in either case.
} if (prim.IsPhysical && PhysicsScene.ShouldUseHullsForPhysicalObjects)
{
// Update prim.BSShape to reference a hull of this shape.
ret = GetReferenceToHull(prim,shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}",
prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X"));
}
else
{
ret = GetReferenceToMesh(prim, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}",
prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X"));
} }
return ret; return ret;
} }
@ -743,32 +758,16 @@ public sealed class BSShapeCollection : IDisposable
// This shouldn't be to bad since most of the parts will be meshes that had been built previously. // This shouldn't be to bad since most of the parts will be meshes that had been built previously.
private bool GetReferenceToCompoundShape(BSPhysObject prim, ShapeDestructionCallback shapeCallback) private bool GetReferenceToCompoundShape(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
{ {
// Remove reference to the old shape
// Don't need to do this as the shape is freed when we create the new root shape below.
// DereferenceShape(prim.PhysShape, true, shapeCallback);
BulletShape cShape = new BulletShape( BulletShape cShape = new BulletShape(
BulletSimAPI.CreateCompoundShape2(PhysicsScene.World.ptr), ShapeData.PhysicsShapeType.SHAPE_COMPOUND); BulletSimAPI.CreateCompoundShape2(PhysicsScene.World.ptr), ShapeData.PhysicsShapeType.SHAPE_COMPOUND);
// The prim's linkset is the source of the children. // Create the shape for the root prim and add it to the compound shape
// TODO: there is too much knowledge here about the internals of linksets and too much
// dependency on the relationship of compound shapes and linksets (what if we want to use
// compound shapes for something else?). Think through this and clean up so the
// appropriate knowledge is used at the correct software levels.
// Recreate the geometry of the root prim (might have been a linkset root in the past)
CreateGeomNonSpecial(true, prim, null); CreateGeomNonSpecial(true, prim, null);
BulletSimAPI.AddChildShapeToCompoundShape2(cShape.ptr, prim.PhysShape.ptr, OMV.Vector3.Zero, OMV.Quaternion.Identity);
BSPhysObject rootPrim = prim.Linkset.LinksetRoot;
prim.Linkset.ForEachMember(delegate(BSPhysObject cPrim)
{
OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(rootPrim.RawOrientation);
OMV.Vector3 displacementPos = (cPrim.RawPosition - rootPrim.RawPosition) * invRootOrientation;
OMV.Quaternion displacementRot = cPrim.RawOrientation * invRootOrientation;
DetailLog("{0},BSShapeCollection.GetReferenceToCompoundShape,addMemberToShape,mID={1},mShape={2},dispPos={3},dispRot={4}",
prim.LocalID, cPrim.LocalID, cPrim.PhysShape.ptr.ToString("X"), displacementPos, displacementRot);
BulletSimAPI.AddChildShapeToCompoundShape2(cShape.ptr, cPrim.PhysShape.ptr, displacementPos, displacementRot);
return false;
});
prim.PhysShape = cShape; prim.PhysShape = cShape;