BulletSim: More work on center-of-mass. Remove linksetinfo and rely on simulator to update info.

user_profiles
Robert Adams 2013-02-10 09:51:34 -08:00
parent 992ef9e971
commit fb903ff490
8 changed files with 163 additions and 125 deletions

View File

@ -225,9 +225,10 @@ public enum CollisionFlags : uint
CF_DISABLE_VISUALIZE_OBJECT = 1 << 5, CF_DISABLE_VISUALIZE_OBJECT = 1 << 5,
CF_DISABLE_SPU_COLLISION_PROCESS = 1 << 6, CF_DISABLE_SPU_COLLISION_PROCESS = 1 << 6,
// Following used by BulletSim to control collisions and updates // Following used by BulletSim to control collisions and updates
BS_SUBSCRIBE_COLLISION_EVENTS = 1 << 10, BS_SUBSCRIBE_COLLISION_EVENTS = 1 << 10, // return collision events from unmanaged to managed
BS_FLOATS_ON_WATER = 1 << 11, BS_FLOATS_ON_WATER = 1 << 11, // the object should float at water level
BS_VEHICLE_COLLISIONS = 1 << 12, BS_VEHICLE_COLLISIONS = 1 << 12, // return collisions for vehicle ground checking
BS_RETURN_ROOT_COMPOUND_SHAPE = 1 << 13, // return the pos/rot of the root shape in a compound shape
BS_NONE = 0, BS_NONE = 0,
BS_ALL = 0xFFFFFFFF BS_ALL = 0xFFFFFFFF
}; };

View File

@ -274,7 +274,7 @@ public sealed class BSCharacter : BSPhysObject
// This test is done if moving forward, not flying and is colliding with something. // This test is done if moving forward, not flying and is colliding with something.
// DetailLog("{0},BSCharacter.WalkUpStairs,IsColliding={1},flying={2},targSpeed={3},collisions={4}", // DetailLog("{0},BSCharacter.WalkUpStairs,IsColliding={1},flying={2},targSpeed={3},collisions={4}",
// LocalID, IsColliding, Flying, TargetSpeed, CollisionsLastTick.Count); // LocalID, IsColliding, Flying, TargetSpeed, CollisionsLastTick.Count);
if (IsColliding && !Flying && TargetSpeed > 0.1f /* && ForwardSpeed < 0.1f */) if (IsColliding && !Flying && TargetVelocitySpeed > 0.1f /* && ForwardSpeed < 0.1f */)
{ {
// The range near the character's feet where we will consider stairs // The range near the character's feet where we will consider stairs
float nearFeetHeightMin = RawPosition.Z - (Size.Z / 2f) + 0.05f; float nearFeetHeightMin = RawPosition.Z - (Size.Z / 2f) + 0.05f;

View File

@ -127,6 +127,8 @@ public abstract class BSLinkset
m_children = new HashSet<BSPrimLinkable>(); m_children = new HashSet<BSPrimLinkable>();
LinksetMass = parent.RawMass; LinksetMass = parent.RawMass;
Rebuilding = false; Rebuilding = false;
parent.ClearDisplacement();
} }
// Link to a linkset where the child knows the parent. // Link to a linkset where the child knows the parent.
@ -280,6 +282,7 @@ public abstract class BSLinkset
return mass; return mass;
} }
// Computes linkset's center of mass in world coordinates.
protected virtual OMV.Vector3 ComputeLinksetCenterOfMass() protected virtual OMV.Vector3 ComputeLinksetCenterOfMass()
{ {
OMV.Vector3 com; OMV.Vector3 com;

View File

@ -93,7 +93,8 @@ public sealed class BSLinksetCompound : BSLinkset
{ {
private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]"; private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]";
public BSLinksetCompound(BSScene scene, BSPrimLinkable parent) : base(scene, parent) public BSLinksetCompound(BSScene scene, BSPrimLinkable parent)
: base(scene, parent)
{ {
} }
@ -217,59 +218,45 @@ public sealed class BSLinksetCompound : BSLinkset
// and that is caused by us updating the object. // and that is caused by us updating the object.
if ((whichUpdated & ~(UpdatedProperties.Position | UpdatedProperties.Orientation)) == 0) if ((whichUpdated & ~(UpdatedProperties.Position | UpdatedProperties.Orientation)) == 0)
{ {
// Gather the child info. It might not be there if the linkset is in transition.
BSLinksetCompoundInfo lsi = updated.LinksetInfo as BSLinksetCompoundInfo;
if (lsi != null)
{
// Since the child moved or rotationed, it needs a new relative position within the linkset
BSLinksetCompoundInfo newLsi = new BSLinksetCompoundInfo(lsi.Index, LinksetRoot, updated, OMV.Vector3.Zero);
updated.LinksetInfo = newLsi;
// Find the physical instance of the child // Find the physical instance of the child
if (LinksetRoot.PhysShape.HasPhysicalShape && PhysicsScene.PE.IsCompound(LinksetRoot.PhysShape)) if (LinksetRoot.PhysShape.HasPhysicalShape && PhysicsScene.PE.IsCompound(LinksetRoot.PhysShape))
{
// It is possible that the linkset is still under construction and the child is not yet
// inserted into the compound shape. A rebuild of the linkset in a pre-step action will
// build the whole thing with the new position or rotation.
// The index must be checked because Bullet references the child array but does no validity
// checking of the child index passed.
int numLinksetChildren = PhysicsScene.PE.GetNumberOfCompoundChildren(LinksetRoot.PhysShape);
if (updated.LinksetChildIndex < numLinksetChildren)
{ {
// It is possible that the linkset is still under construction and the child is not yet BulletShape linksetChildShape = PhysicsScene.PE.GetChildShapeFromCompoundShapeIndex(LinksetRoot.PhysShape, updated.LinksetChildIndex);
// inserted into the compound shape. A rebuild of the linkset in a pre-step action will if (linksetChildShape.HasPhysicalShape)
// build the whole thing with the new position or rotation.
// The index must be checked because Bullet references the child array but does no validity
// checking of the child index passed.
int numLinksetChildren = PhysicsScene.PE.GetNumberOfCompoundChildren(LinksetRoot.PhysShape);
if (lsi.Index < numLinksetChildren)
{ {
BulletShape linksetChildShape = PhysicsScene.PE.GetChildShapeFromCompoundShapeIndex(LinksetRoot.PhysShape, lsi.Index); // Found the child shape within the compound shape
if (linksetChildShape.HasPhysicalShape) PhysicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape, updated.LinksetChildIndex,
{ updated.RawPosition - LinksetRoot.RawPosition,
// Found the child shape within the compound shape updated.RawOrientation * OMV.Quaternion.Inverse(LinksetRoot.RawOrientation),
PhysicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape, lsi.Index, true /* shouldRecalculateLocalAabb */);
newLsi.OffsetFromCenterOfMass, updatedChild = true;
newLsi.OffsetRot, DetailLog("{0},BSLinksetCompound.UpdateProperties,changeChildPosRot,whichUpdated={1},pos={2},rot={3}",
true /* shouldRecalculateLocalAabb */); updated.LocalID, whichUpdated, updated.RawPosition, updated.RawOrientation);
updatedChild = true;
DetailLog("{0},BSLinksetCompound.UpdateProperties,changeChildPosRot,whichUpdated={1},newLsi={2}",
updated.LocalID, whichUpdated, newLsi);
}
else // DEBUG DEBUG
{ // DEBUG DEBUG
DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noChildShape,shape={1}",
updated.LocalID, linksetChildShape);
} // DEBUG DEBUG
} }
else // DEBUG DEBUG else // DEBUG DEBUG
{ // DEBUG DEBUG { // DEBUG DEBUG
// the child is not yet in the compound shape. This is non-fatal. DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noChildShape,shape={1}",
DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,childNotInCompoundShape,numChildren={1},index={2}", updated.LocalID, linksetChildShape);
updated.LocalID, numLinksetChildren, lsi.Index);
} // DEBUG DEBUG } // DEBUG DEBUG
} }
else // DEBUG DEBUG else // DEBUG DEBUG
{ // DEBUG DEBUG { // DEBUG DEBUG
DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noBodyOrNotCompound", updated.LocalID); // the child is not yet in the compound shape. This is non-fatal.
DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,childNotInCompoundShape,numChildren={1},index={2}",
updated.LocalID, numLinksetChildren, updated.LinksetChildIndex);
} // DEBUG DEBUG } // DEBUG DEBUG
} }
else // DEBUG DEBUG else // DEBUG DEBUG
{ // DEBUG DEBUG { // DEBUG DEBUG
DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noLinkSetInfo,rootPhysShape={1}", DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noBodyOrNotCompound", updated.LocalID);
updated.LocalID, LinksetRoot.PhysShape);
} // DEBUG DEBUG } // DEBUG DEBUG
if (!updatedChild) if (!updatedChild)
@ -379,6 +366,8 @@ public sealed class BSLinksetCompound : BSLinkset
// Safe to call even if the child is not really in the linkset. // Safe to call even if the child is not really in the linkset.
protected override void RemoveChildFromLinkset(BSPrimLinkable child) protected override void RemoveChildFromLinkset(BSPrimLinkable child)
{ {
child.ClearDisplacement();
if (m_children.Remove(child)) if (m_children.Remove(child))
{ {
DetailLog("{0},BSLinksetCompound.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", DetailLog("{0},BSLinksetCompound.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
@ -410,7 +399,7 @@ public sealed class BSLinksetCompound : BSLinkset
// Constraint linksets are rebuilt every time. // Constraint linksets are rebuilt every time.
// Note that this works for rebuilding just the root after a linkset is taken apart. // Note that this works for rebuilding just the root after a linkset is taken apart.
// Called at taint time!! // Called at taint time!!
private bool disableCOM = true; // DEBUG DEBUG: disable until we get this debugged private bool disableCOM = false; // DEBUG DEBUG: disable until we get this debugged
private void RecomputeLinksetCompound() private void RecomputeLinksetCompound()
{ {
try try
@ -424,30 +413,31 @@ public sealed class BSLinksetCompound : BSLinkset
// The center of mass for the linkset is the geometric center of the group. // The center of mass for the linkset is the geometric center of the group.
// Compute a displacement for each component so it is relative to the center-of-mass. // Compute a displacement for each component so it is relative to the center-of-mass.
// Bullet presumes an object's origin (relative <0,0,0>) is its center-of-mass // Bullet presumes an object's origin (relative <0,0,0>) is its center-of-mass
OMV.Vector3 centerOfMass; OMV.Vector3 centerOfMassW = LinksetRoot.RawPosition;
OMV.Vector3 centerDisplacement = OMV.Vector3.Zero; if (!disableCOM) // DEBUG DEBUG
if (disableCOM) // DEBUG DEBUG
{ // DEBUG DEBUG
centerOfMass = LinksetRoot.RawPosition; // DEBUG DEBUG
// LinksetRoot.PositionDisplacement = OMV.Vector3.Zero;
} // DEBUG DEBUG
else
{ {
centerOfMass = ComputeLinksetCenterOfMass(); // Compute a center-of-mass in world coordinates.
// 'centerDisplacement' is the value to *add* to all the shape offsets centerOfMassW = ComputeLinksetCenterOfMass();
centerDisplacement = LinksetRoot.RawPosition - centerOfMass;
// Since we're displacing the center of the shape, we need to move the body in the world
// LinksetRoot.PositionDisplacement = centerDisplacement;
// This causes the root prim position to be set properly based on the new PositionDisplacement
LinksetRoot.ForcePosition = LinksetRoot.RawPosition;
// Update the local transform for the root child shape so it is offset from the <0,0,0> which is COM
PhysicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape, 0, -centerDisplacement, OMV.Quaternion.Identity, false);
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,COM,com={1},rootPos={2},centerDisp={3}",
LinksetRoot.LocalID, centerOfMass, LinksetRoot.RawPosition, centerDisplacement);
} }
OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation);
// 'centerDisplacement' is the value to subtract from children to give physical offset position
OMV.Vector3 centerDisplacement = (centerOfMassW - LinksetRoot.RawPosition) * invRootOrientation;
LinksetRoot.SetEffectiveCenterOfMassW(centerDisplacement);
// This causes the physical position of the root prim to be offset to accomodate for the displacements
LinksetRoot.ForcePosition = LinksetRoot.RawPosition;
// Update the local transform for the root child shape so it is offset from the <0,0,0> which is COM
PhysicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape, 0,
-centerDisplacement,
LinksetRoot.RawOrientation,
false /* shouldRecalculateLocalAabb */);
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,COM,com={1},rootPos={2},centerDisp={3}",
LinksetRoot.LocalID, centerOfMassW, LinksetRoot.RawPosition, centerDisplacement);
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}", DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}",
LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren); LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren);
@ -455,29 +445,20 @@ public sealed class BSLinksetCompound : BSLinkset
int memberIndex = 1; int memberIndex = 1;
ForEachMember(delegate(BSPrimLinkable cPrim) ForEachMember(delegate(BSPrimLinkable cPrim)
{ {
if (!IsRoot(cPrim)) if (IsRoot(cPrim))
{ {
// Compute the displacement of the child from the root of the linkset. cPrim.LinksetChildIndex = 0;
// This info is saved in the child prim so the relationship does not }
// change over time and the new child position can be computed else
// when the linkset is being disassembled (the linkset may have moved). {
BSLinksetCompoundInfo lci = cPrim.LinksetInfo as BSLinksetCompoundInfo; cPrim.LinksetChildIndex = memberIndex;
if (lci == null)
{
lci = new BSLinksetCompoundInfo(memberIndex, LinksetRoot, cPrim, centerDisplacement);
cPrim.LinksetInfo = lci;
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,creatingRelPos,lci={1}", cPrim.LocalID, lci);
}
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addMemberToShape,mID={1},mShape={2},lci={3}",
LinksetRoot.LocalID, cPrim.LocalID, cPrim.PhysShape, lci);
if (cPrim.PhysShape.isNativeShape) if (cPrim.PhysShape.isNativeShape)
{ {
// A native shape is turned into a hull collision shape because native // A native shape is turned into a hull collision shape because native
// shapes are not shared so we have to hullify it so it will be tracked // shapes are not shared so we have to hullify it so it will be tracked
// and freed at the correct time. This also solves the scaling problem // and freed at the correct time. This also solves the scaling problem
// (native shapes scaled but hull/meshes are assumed to not be). // (native shapes scale but hull/meshes are assumed to not be).
// TODO: decide of the native shape can just be used in the compound shape. // TODO: decide of the native shape can just be used in the compound shape.
// Use call to CreateGeomNonSpecial(). // Use call to CreateGeomNonSpecial().
BulletShape saveShape = cPrim.PhysShape; BulletShape saveShape = cPrim.PhysShape;
@ -486,7 +467,10 @@ public sealed class BSLinksetCompound : BSLinkset
PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null); PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null);
BulletShape newShape = cPrim.PhysShape; BulletShape newShape = cPrim.PhysShape;
cPrim.PhysShape = saveShape; cPrim.PhysShape = saveShape;
PhysicsScene.PE.AddChildShapeToCompoundShape(LinksetRoot.PhysShape, newShape, lci.OffsetFromCenterOfMass, lci.OffsetRot);
OMV.Vector3 offsetPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation - centerDisplacement;
OMV.Quaternion offsetRot = cPrim.RawOrientation * invRootOrientation;
PhysicsScene.PE.AddChildShapeToCompoundShape(LinksetRoot.PhysShape, newShape, offsetPos, offsetRot);
} }
else else
{ {
@ -498,9 +482,10 @@ public sealed class BSLinksetCompound : BSLinkset
PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}", PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}",
LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape); LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape);
} }
PhysicsScene.PE.AddChildShapeToCompoundShape(LinksetRoot.PhysShape, cPrim.PhysShape, lci.OffsetFromCenterOfMass, lci.OffsetRot); OMV.Vector3 offsetPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation - centerDisplacement;
OMV.Quaternion offsetRot = cPrim.RawOrientation * invRootOrientation;
PhysicsScene.PE.AddChildShapeToCompoundShape(LinksetRoot.PhysShape, cPrim.PhysShape, offsetPos, offsetRot);
} }
lci.Index = memberIndex;
memberIndex++; memberIndex++;
} }
return false; // 'false' says to move onto the next child in the list return false; // 'false' says to move onto the next child in the list
@ -509,6 +494,9 @@ public sealed class BSLinksetCompound : BSLinkset
// With all of the linkset packed into the root prim, it has the mass of everyone. // With all of the linkset packed into the root prim, it has the mass of everyone.
LinksetMass = ComputeLinksetMass(); LinksetMass = ComputeLinksetMass();
LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true); LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true);
// Enable the physical position updator to return the position and rotation of the root shape
PhysicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody, CollisionFlags.BS_RETURN_ROOT_COMPOUND_SHAPE);
} }
finally finally
{ {

View File

@ -136,6 +136,7 @@ public abstract class BSPhysObject : PhysicsActor
// The objects base shape information. Null if not a prim type shape. // The objects base shape information. Null if not a prim type shape.
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 BSPhysicsShapeType PreferredPhysicalShape public virtual BSPhysicsShapeType PreferredPhysicalShape
@ -150,15 +151,17 @@ public abstract class BSPhysObject : PhysicsActor
public EntityProperties LastEntityProperties { get; set; } public EntityProperties LastEntityProperties { get; set; }
public virtual OMV.Vector3 Scale { get; set; } public virtual OMV.Vector3 Scale { get; set; }
public abstract bool IsSolid { get; }
public abstract bool IsStatic { get; }
public abstract bool IsSelected { get; }
// It can be confusing for an actor to know if it should move or update an object // It can be confusing for an actor to know if it should move or update an object
// depeneding on the setting of 'selected', 'physical, ... // depeneding on the setting of 'selected', 'physical, ...
// This flag is the true test -- if true, the object is being acted on in the physical world // This flag is the true test -- if true, the object is being acted on in the physical world
public abstract bool IsPhysicallyActive { get; } public abstract bool IsPhysicallyActive { get; }
// Detailed state of the object.
public abstract bool IsSolid { get; }
public abstract bool IsStatic { get; }
public abstract bool IsSelected { get; }
// Materialness // Materialness
public MaterialAttributes.Material Material { get; private set; } public MaterialAttributes.Material Material { get; private set; }
public override void SetMaterial(int material) public override void SetMaterial(int material)
@ -185,14 +188,6 @@ public abstract class BSPhysObject : PhysicsActor
public abstract OMV.Quaternion RawOrientation { get; set; } public abstract OMV.Quaternion RawOrientation { get; set; }
public abstract OMV.Quaternion ForceOrientation { get; set; } public abstract OMV.Quaternion ForceOrientation { get; set; }
public virtual float TargetSpeed
{
get
{
OMV.Vector3 characterOrientedVelocity = TargetVelocity * OMV.Quaternion.Inverse(OMV.Quaternion.Normalize(RawOrientation));
return characterOrientedVelocity.X;
}
}
public abstract OMV.Vector3 RawVelocity { get; set; } public abstract OMV.Vector3 RawVelocity { get; set; }
public abstract OMV.Vector3 ForceVelocity { get; set; } public abstract OMV.Vector3 ForceVelocity { get; set; }
@ -202,6 +197,7 @@ public abstract class BSPhysObject : PhysicsActor
public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; } public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; }
// The current velocity forward
public virtual float ForwardSpeed public virtual float ForwardSpeed
{ {
get get
@ -210,6 +206,19 @@ public abstract class BSPhysObject : PhysicsActor
return characterOrientedVelocity.X; return characterOrientedVelocity.X;
} }
} }
// The forward speed we are trying to achieve (TargetVelocity)
public virtual float TargetVelocitySpeed
{
get
{
OMV.Vector3 characterOrientedVelocity = TargetVelocity * OMV.Quaternion.Inverse(OMV.Quaternion.Normalize(RawOrientation));
return characterOrientedVelocity.X;
}
}
// The user can optionally set the center of mass. The user's setting will override any
// computed center-of-mass (like in linksets).
public OMV.Vector3? UserSetCenterOfMass { get; set; }
#region Collisions #region Collisions

View File

@ -442,7 +442,7 @@ public class BSPrim : BSPhysObject
RegisterPreStepAction("BSPrim.setForce", LocalID, RegisterPreStepAction("BSPrim.setForce", LocalID,
delegate(float timeStep) delegate(float timeStep)
{ {
if (!IsPhysicallyActive) if (!IsPhysicallyActive || _force == OMV.Vector3.Zero)
{ {
UnRegisterPreStepAction("BSPrim.setForce", LocalID); UnRegisterPreStepAction("BSPrim.setForce", LocalID);
return; return;
@ -647,7 +647,7 @@ public class BSPrim : BSPhysObject
RegisterPreStepAction("BSPrim.setTorque", LocalID, RegisterPreStepAction("BSPrim.setTorque", LocalID,
delegate(float timeStep) delegate(float timeStep)
{ {
if (!IsPhysicallyActive) if (!IsPhysicallyActive || _torque == OMV.Vector3.Zero)
{ {
UnRegisterPreStepAction("BSPrim.setTorque", LocalID); UnRegisterPreStepAction("BSPrim.setTorque", LocalID);
return; return;

View File

@ -44,72 +44,107 @@ namespace OpenSim.Region.Physics.BulletSPlugin
{ {
public class BSPrimDisplaced : BSPrim public class BSPrimDisplaced : BSPrim
{ {
// 'Position' and 'Orientation' is what the simulator thinks the positions of the prim is. // The purpose of this module is to do any mapping between what the simulator thinks
// Because Bullet needs the zero coordinate to be the center of mass of the linkset, // the prim position and orientation is and what the physical position/orientation.
// sometimes it is necessary to displace the position the physics engine thinks // This difference happens because Bullet assumes the center-of-mass is the <0,0,0>
// the position is. PositionDisplacement must be added and removed from the // of the prim/linkset. The simulator tracks the location of the prim/linkset by
// position as the simulator position is stored and fetched from the physics // the location of the root prim. So, if center-of-mass is anywhere but the origin
// engine. Similar to OrientationDisplacement. // of the root prim, the physical origin is displaced from the simulator origin.
//
// This routine works by capturing the Force* setting of position/orientation/... and
// adjusting the simulator values (being set) into the physical values.
// The conversion is also done in the opposite direction (physical origin -> simulator origin).
//
// The updateParameter call is also captured and the values from the physics engine
// are converted into simulator origin values before being passed to the base
// class.
public virtual OMV.Vector3 PositionDisplacement { get; set; } public virtual OMV.Vector3 PositionDisplacement { get; set; }
public virtual OMV.Quaternion OrientationDisplacement { get; set; } public virtual OMV.Quaternion OrientationDisplacement { get; set; }
public virtual OMV.Vector3 CenterOfMassLocation { get; set; }
public virtual OMV.Vector3 GeometricCenterLocation { get; set; }
public BSPrimDisplaced(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, public BSPrimDisplaced(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size,
OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical) OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical)
: base(localID, primName, parent_scene, pos, size, rotation, pbs, pisPhysical) : base(localID, primName, parent_scene, pos, size, rotation, pbs, pisPhysical)
{ {
CenterOfMassLocation = RawPosition; ClearDisplacement();
GeometricCenterLocation = RawPosition; }
public void ClearDisplacement()
{
PositionDisplacement = OMV.Vector3.Zero;
OrientationDisplacement = OMV.Quaternion.Identity;
}
// Set this sets and computes the displacement from the passed prim to the center-of-mass.
// A user set value for center-of-mass overrides whatever might be passed in here.
// The displacement is in local coordinates (relative to root prim in linkset oriented coordinates).
public virtual void SetEffectiveCenterOfMassW(Vector3 centerOfMassDisplacement)
{
Vector3 comDisp;
if (UserSetCenterOfMass.HasValue)
comDisp = (OMV.Vector3)UserSetCenterOfMass;
else
comDisp = centerOfMassDisplacement;
if (comDisp == Vector3.Zero)
{
// If there is no diplacement. Things get reset.
PositionDisplacement = OMV.Vector3.Zero;
OrientationDisplacement = OMV.Quaternion.Identity;
}
else
{
// Remember the displacement from root as well as the origional rotation of the
// new center-of-mass.
PositionDisplacement = comDisp;
OrientationDisplacement = OMV.Quaternion.Identity;
}
} }
public override Vector3 ForcePosition public override Vector3 ForcePosition
{ {
get get { return base.ForcePosition; }
{
return base.ForcePosition;
}
set set
{ {
base.ForcePosition = value; if (PositionDisplacement != OMV.Vector3.Zero)
CenterOfMassLocation = RawPosition; base.ForcePosition = value - (PositionDisplacement * RawOrientation);
GeometricCenterLocation = RawPosition; else
base.ForcePosition = value;
} }
} }
public override Quaternion ForceOrientation public override Quaternion ForceOrientation
{ {
get get { return base.ForceOrientation; }
{
return base.ForceOrientation;
}
set set
{ {
base.ForceOrientation = value; base.ForceOrientation = value;
} }
} }
// TODO: decide if this is the right place for these variables.
// Somehow incorporate the optional settability by the user.
// Is this used? // Is this used?
public override OMV.Vector3 CenterOfMass public override OMV.Vector3 CenterOfMass
{ {
get { return CenterOfMassLocation; } get { return RawPosition; }
} }
// Is this used? // Is this used?
public override OMV.Vector3 GeometricCenter public override OMV.Vector3 GeometricCenter
{ {
get { return GeometricCenterLocation; } get { return RawPosition; }
} }
public override void UpdateProperties(EntityProperties entprop) public override void UpdateProperties(EntityProperties entprop)
{ {
// Undo any center-of-mass displacement that might have been done. // Undo any center-of-mass displacement that might have been done.
if (PositionDisplacement != OMV.Vector3.Zero) if (PositionDisplacement != OMV.Vector3.Zero || OrientationDisplacement != OMV.Quaternion.Identity)
{ {
// Correct for any rotation around the center-of-mass // Correct for any rotation around the center-of-mass
// TODO!!! // TODO!!!
entprop.Position -= PositionDisplacement; entprop.Position = entprop.Position + (PositionDisplacement * entprop.Rotation);
entprop.Rotation = something;
} }
base.UpdateProperties(entprop); base.UpdateProperties(entprop);

View File

@ -38,6 +38,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin
public class BSPrimLinkable : BSPrimDisplaced public class BSPrimLinkable : BSPrimDisplaced
{ {
public BSLinkset Linkset { get; set; } public BSLinkset Linkset { get; set; }
// The index of this child prim.
public int LinksetChildIndex { get; set; }
public BSLinksetInfo LinksetInfo { get; set; } public BSLinksetInfo LinksetInfo { get; set; }
public BSPrimLinkable(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, public BSPrimLinkable(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size,
@ -90,7 +93,6 @@ public class BSPrimLinkable : BSPrimDisplaced
DetailLog("{0},BSPrimLinkset.delink,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}, ", DetailLog("{0},BSPrimLinkset.delink,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}, ",
LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren); LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren);
return; return;
base.delink();
} }
// When simulator changes position, this might be moving a child of the linkset. // When simulator changes position, this might be moving a child of the linkset.