// When physical properties are changed the linkset needs to recalculate // its internal properties. // This is queued in the 'post taint' queue so the // refresh will happen once after all the other taints are applied. public override void Refresh(BSPrimLinkable requestor) { base.Refresh(requestor); if (HasAnyChildren && IsRoot(requestor)) { // Queue to happen after all the other taint processing PhysicsScene.PostTaintObject("BSLinksetContraints.Refresh", requestor.LocalID, delegate() { if (HasAnyChildren && IsRoot(requestor)) RecomputeLinksetConstraints(); }); } }
protected BSLinkset(BSScene scene, BSPrimLinkable parent) { // A simple linkset of one (no children) LinksetID = m_nextLinksetID++; // We create LOTS of linksets. if (m_nextLinksetID <= 0) m_nextLinksetID = 1; PhysicsScene = scene; LinksetRoot = parent; m_children = new HashSet<BSPrimLinkable>(); LinksetMass = parent.RawMass; Rebuilding = false; parent.ClearDisplacement(); }
// 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 override bool MakeDynamic(BSPrimLinkable child) { bool ret = false; DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child)); if (IsRoot(child)) { // The root is going dynamic. Rebuild the linkset so parts and mass get computed properly. ScheduleRebuild(LinksetRoot); } /*else { // The origional prims are removed from the world as the shape of the root compound // shape takes over. PhysicsScene.PE.AddToCollisionFlags(child.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); PhysicsScene.PE.ForceActivationState(child.PhysBody, ActivationState.DISABLE_SIMULATION); // We don't want collisions from the old linkset children. PhysicsScene.PE.RemoveFromCollisionFlags(child.PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); child.PhysBody.collisionType = CollisionType.LinksetChild; ret = true; }*/ return ret; }
// 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. // This doesn't normally happen -- OpenSim removes the objects from the physical // world if it is a static linkset. // Called at taint-time! public override bool MakeStatic(BSPrimLinkable child) { // What is done for each object in BSPrim is what we want. return false; }
// Remove the specified child from the linkset. // Safe to call even if the child is not really in my linkset. protected override void RemoveChildFromLinkset(BSPrimLinkable child) { if (m_children.Remove(child)) { BSPrimLinkable rootx = LinksetRoot; // capture the root and body as of now BSPrimLinkable childx = child; DetailLog("{0},BSLinksetConstraints.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", childx.LocalID, rootx.LocalID, rootx.PhysBody.AddrString, childx.LocalID, childx.PhysBody.AddrString); PhysicsScene.TaintedObject("BSLinksetConstraints.RemoveChildFromLinkset", delegate() { PhysicallyUnlinkAChildFromRoot(rootx, childx); }); // See that the linkset parameters are recomputed at the end of the taint time. Refresh(LinksetRoot); } else { // Non-fatal occurance. // PhysicsScene.Logger.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset", LogHeader); } return; }
public BSLinksetGroupCompound(BSScene scene, BSPrimLinkable parent) : base(scene, parent) { }
// Called when a parameter update comes from the physics engine for any object // of the linkset is received. // Passed flag is update came from physics engine (true) or the user (false). // Called at taint-time!! public abstract void UpdateProperties(UpdatedProperties whichUpdated, BSPrimLinkable physObject);
// ================================================================ // Add a new child to the linkset. // Called while LinkActivity is locked. protected override void AddChildToLinkset(BSPrimLinkable child, bool scheduleRebuild) { if (!HasChild(child)) { m_children.Add(child); DetailLog("{0},BSLinksetCompound.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID); // Rebuild the compound shape with the new child shape included if (scheduleRebuild) ScheduleRebuild(child); } return; }
// Schedule a refresh to happen after all the other taint processing. protected override void ScheduleRebuild(BSPrimLinkable requestor) { DetailLog("{0},BSLinksetCompound.ScheduleRebuild,,rebuilding={1},hasChildren={2},actuallyScheduling={3}", requestor.LocalID, Rebuilding, HasAnyChildren, (!Rebuilding && HasAnyChildren)); // When rebuilding, it is possible to set properties that would normally require a rebuild. // If already rebuilding, don't request another rebuild. // If a linkset with just a root prim (simple non-linked prim) don't bother rebuilding. if (!Rebuilding && HasAnyChildren) { PhysicsScene.PostTaintObject("BSLinksetCompound.ScheduleRebuild", LinksetRoot.LocalID, delegate() { if (HasAnyChildren) RecomputeLinksetCompound(); }); } }
// For compound implimented linksets, if there are children, use compound shape for the root. public override BSPhysicsShapeType PreferredPhysicalShape(BSPrimLinkable requestor) { // Returning 'unknown' means we don't have a preference. BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN; if (IsRoot(requestor) && HasAnyChildren && requestor.IsPhysical) { ret = BSPhysicsShapeType.SHAPE_COMPOUND; } // DetailLog("{0},BSLinksetCompound.PreferredPhysicalShape,call,shape={1}", LinksetRoot.LocalID, ret); return ret; }
// Routine called when rebuilding the body of some member of the linkset. // Since we don't keep in world relationships, do nothing unless it's a child changing. // Returns 'true' of something was actually removed and would need restoring // Called at taint-time!! public override bool RemoveBodyDependencies(BSPrimLinkable child) { bool ret = false; DetailLog("{0},BSLinksetCompound.RemoveBodyDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}", child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody, IsRoot(child)); if (!IsRoot(child)) { // Because it is a convenient time, recompute child world position and rotation based on // its position in the linkset. RecomputeChildWorldPosition(child, true /* inTaintTime */); child.LinksetInfo = null; } // Cannot schedule a refresh/rebuild here because this routine is called when // the linkset is being rebuilt. // InternalRefresh(LinksetRoot); return ret; }
// ================================================================ // Add a new child to the linkset. // Called while LinkActivity is locked. protected override void AddChildToLinkset(BSPrimLinkable child, bool scheduleRebuild) { if (!this.LinksetRoot.IsPhysical) return; if (!HasChild(child)) { m_children.Add(child); DetailLog("{0},BSLinksetConstraints.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID); // Cause constraints and assorted properties to be recomputed before the next simulation step. if(scheduleRebuild) Refresh(LinksetRoot); } return; }
// 'centerDisplacement' is the distance from the root the the center-of-mass (Bullet 'zero' of the shape) public BSLinksetCompoundInfo(int indx, BSPrimLinkable root, BSPrimLinkable child, OMV.Vector3 centerDisplacement) { // Each child position and rotation is given relative to the center-of-mass. OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(root.RawOrientation); OMV.Vector3 displacementFromRoot = (child.RawPosition - root.RawPosition) * invRootOrientation; OMV.Vector3 displacementFromCOM = displacementFromRoot - centerDisplacement; OMV.Quaternion displacementRot = child.RawOrientation * invRootOrientation; // Save relative position for recomputing child's world position after moving linkset. Index = indx; OffsetFromRoot = displacementFromRoot; OffsetFromCenterOfMass = displacementFromCOM; OffsetRot = displacementRot; }
// Called at taint-time!! public override void UpdateProperties(UpdatedProperties whichUpdated, BSPrimLinkable pObj) { // Nothing to do for constraints on property updates }
// Routine called when rebuilding the body of some member of the linkset. // Destroy all the constraints have have been made to root and set // up to rebuild the constraints before the next simulation step. // Returns 'true' of something was actually removed and would need restoring // Called at taint-time!! public override bool RemoveBodyDependencies(BSPrimLinkable child) { bool ret = false; DetailLog("{0},BSLinksetConstraint.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2}", child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.AddrString); lock (m_linksetActivityLock) { // Just undo all the constraints for this linkset. Rebuild at the end of the step. ret = PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot); // Cause the constraints, et al to be rebuilt before the next simulation step. Refresh(LinksetRoot); } return ret; }
// I am the root of a linkset and a new child is being added // Called while LinkActivity is locked. protected abstract void AddChildToLinkset(BSPrimLinkable child, bool scheduleRebuild);
protected abstract void ScheduleRebuild(BSPrimLinkable requestor);
// I am the root of a linkset and one of my children is being removed. // Safe to call even if the child is not really in my linkset. protected abstract void RemoveChildFromLinkset(BSPrimLinkable child);
// 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. // This doesn't normally happen -- OpenSim removes the objects from the physical // world if it is a static linkset. // Called at taint-time! public override bool MakeStatic(BSPrimLinkable child) { if (!LinksetRoot.IsPhysical) return false; bool ret = false; DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child)); if (IsRoot(child)) { ScheduleRebuild(LinksetRoot); } /*else { // The non-physical children can come back to life. PhysicsScene.PE.RemoveFromCollisionFlags(child.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); child.PhysBody.collisionType = CollisionType.LinksetChild; // Don't force activation so setting of DISABLE_SIMULATION can stay if used. PhysicsScene.PE.Activate(child.PhysBody, false); ret = true; }*/ return ret; }
protected override void ScheduleRebuild(BSPrimLinkable requestor) { Refresh(requestor); }
// When physical properties are changed the linkset needs to recalculate // its internal properties. public override void Refresh(BSPrimLinkable requestor) { base.Refresh(requestor); // Something changed so do the rebuilding thing // ScheduleRebuild(); }
private BSConstraint BuildConstraint(BSPrimLinkable rootPrim, BSPrimLinkable childPrim) { // Zero motion for children so they don't interpolate childPrim.ZeroMotion(true); // Relative position normalized to the root prim // Essentually a vector pointing from center of rootPrim to center of childPrim OMV.Vector3 childRelativePosition = childPrim.Position - rootPrim.Position; // real world coordinate of midpoint between the two objects OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2); DetailLog("{0},BSLinksetConstraint.BuildConstraint,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}", rootPrim.LocalID, rootPrim.LocalID, rootPrim.PhysBody.AddrString, childPrim.LocalID, childPrim.PhysBody.AddrString, 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 BSConstraint6Dof constrain = new BSConstraint6Dof( PhysicsScene.World, rootPrim.PhysBody, childPrim.PhysBody, midPoint, true, true ); // PhysicsScene.World, childPrim.BSBody, rootPrim.BSBody, midPoint, true, true ); /* 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 * of the objects. * Code left for future programmers. // ================================================================================== // relative position normalized to the root prim OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(rootPrim.Orientation); OMV.Vector3 childRelativePosition = (childPrim.Position - rootPrim.Position) * invThisOrientation; // relative rotation of the child to the parent OMV.Quaternion childRelativeRotation = invThisOrientation * childPrim.Orientation; OMV.Quaternion inverseChildRelativeRotation = OMV.Quaternion.Inverse(childRelativeRotation); DetailLog("{0},BSLinksetConstraint.PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID); BS6DofConstraint constrain = new BS6DofConstraint( PhysicsScene.World, rootPrim.Body, childPrim.Body, OMV.Vector3.Zero, OMV.Quaternion.Inverse(rootPrim.Orientation), OMV.Vector3.Zero, OMV.Quaternion.Inverse(childPrim.Orientation), true, true ); // ================================================================================== */ PhysicsScene.Constraints.AddConstraint(constrain); // zero linear and angular limits makes the objects unable to move in relation to each other constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); // tweek the constraint to increase stability constrain.UseFrameOffset(BSParam.LinkConstraintUseFrameOffset); constrain.TranslationalLimitMotor(BSParam.LinkConstraintEnableTransMotor, BSParam.LinkConstraintTransMotorMaxVel, BSParam.LinkConstraintTransMotorMaxForce); constrain.SetCFMAndERP(BSParam.LinkConstraintCFM, BSParam.LinkConstraintERP); if (BSParam.LinkConstraintSolverIterations != 0f) { constrain.SetSolverIterations(BSParam.LinkConstraintSolverIterations); } return constrain; }
// 'physicalUpdate' is true if these changes came directly from the physics engine. Don't need to rebuild then. // Called at taint-time. public override void UpdateProperties(UpdatedProperties whichUpdated, BSPrimLinkable updated) { if (!LinksetRoot.IsPhysical) return; // The user moving a child around requires the rebuilding of the linkset compound shape // One problem is this happens when a border is crossed -- the simulator implementation // stores the position into the group which causes the move of the object // but it also means all the child positions get updated. // What would cause an unnecessary rebuild so we make sure the linkset is in a // region before bothering to do a rebuild. if (!IsRoot(updated) && PhysicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition)) { // If a child of the linkset is updating only the position or rotation, that can be done // without rebuilding the linkset. // If a handle for the child can be fetch, we update the child here. If a rebuild was // scheduled by someone else, the rebuild will just replace this setting. bool updatedChild = false; // Anything other than updating position or orientation usually means a physical update // and that is caused by us updating the object. if ((whichUpdated & ~(UpdatedProperties.Position | UpdatedProperties.Orientation)) == 0) { // Find the physical instance of the child 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) { BulletShape linksetChildShape = PhysicsScene.PE.GetChildShapeFromCompoundShapeIndex(LinksetRoot.PhysShape, updated.LinksetChildIndex); if (linksetChildShape.HasPhysicalShape) { // Found the child shape within the compound shape PhysicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape, updated.LinksetChildIndex, updated.RawPosition - LinksetRoot.RawPosition, updated.RawOrientation * OMV.Quaternion.Inverse(LinksetRoot.RawOrientation), true /* shouldRecalculateLocalAabb */); updatedChild = true; DetailLog("{0},BSLinksetCompound.UpdateProperties,changeChildPosRot,whichUpdated={1},pos={2},rot={3}", updated.LocalID, whichUpdated, updated.RawPosition, updated.RawOrientation); } else // DEBUG DEBUG { // DEBUG DEBUG DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noChildShape,shape={1}", updated.LocalID, linksetChildShape); } // DEBUG DEBUG } else // DEBUG DEBUG { // DEBUG DEBUG // 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 } else // DEBUG DEBUG { // DEBUG DEBUG DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noBodyOrNotCompound", updated.LocalID); } // DEBUG DEBUG if (!updatedChild) { // If couldn't do the individual child, the linkset needs a rebuild to incorporate the new child info. // Note: there are several ways through this code that will not update the child if // the linkset is being rebuilt. In this case, scheduling a rebuild is a NOOP since // there will already be a rebuild scheduled. DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild.schedulingRebuild,whichUpdated={1}", updated.LocalID, whichUpdated); updated.LinksetInfo = null; // setting to 'null' causes relative position to be recomputed. ScheduleRebuild(updated); } } } }
// Create a constraint between me (root of linkset) and the passed prim (the child). // Called at taint time! private void PhysicallyLinkAChildToRoot(BSPrimLinkable rootPrim, BSPrimLinkable childPrim) { // Don't build the constraint when asked. Put it off until just before the simulation step. Refresh(rootPrim); }
// Remove the specified child from the linkset. // Safe to call even if the child is not really in the linkset. protected override void RemoveChildFromLinkset(BSPrimLinkable child) { child.ClearDisplacement(); if (m_children.Remove(child)) { DetailLog("{0},BSLinksetCompound.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.AddrString, child.LocalID, child.PhysBody.AddrString); // Cause the child's body to be rebuilt and thus restored to normal operation RecomputeChildWorldPosition(child, false); child.LinksetInfo = null; child.ForceBodyShapeRebuild(false); if (!HasAnyChildren) { // The linkset is now empty. The root needs rebuilding. LinksetRoot.ForceBodyShapeRebuild(false); } else { // Rebuild the compound shape with the child removed ScheduleRebuild(LinksetRoot); } } return; }
// Remove linkage between the linkset root and a particular child // The root and child bodies are passed in because we need to remove the constraint between // the bodies that were present at unlink time. // Called at taint time! private bool PhysicallyUnlinkAChildFromRoot(BSPrimLinkable rootPrim, BSPrimLinkable childPrim) { bool ret = false; DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}", rootPrim.LocalID, rootPrim.LocalID, rootPrim.PhysBody.AddrString, childPrim.LocalID, childPrim.PhysBody.AddrString); // Find the constraint for this link and get rid of it from the overall collection and from my list if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody, childPrim.PhysBody)) { // Make the child refresh its location PhysicsScene.PE.PushUpdate(childPrim.PhysBody); ret = true; } return ret; }
// When the linkset is built, the child shape is added to the compound shape relative to the // root shape. The linkset then moves around but this does not move the actual child // prim. The child prim's location must be recomputed based on the location of the root shape. private void RecomputeChildWorldPosition(BSPrimLinkable child, bool inTaintTime) { // For the moment (20130201), disable this computation (converting the child physical addr back to // a region address) until we have a good handle on center-of-mass offsets and what the physics // engine moving a child actually means. // The simulator keeps track of where children should be as the linkset moves. Setting // the pos/rot here does not effect that knowledge as there is no good way for the // physics engine to send the simulator an update for a child. /* BSLinksetCompoundInfo lci = child.LinksetInfo as BSLinksetCompoundInfo; if (lci != null) { if (inTaintTime) { OMV.Vector3 oldPos = child.RawPosition; child.ForcePosition = LinksetRoot.RawPosition + lci.OffsetFromRoot; child.ForceOrientation = LinksetRoot.RawOrientation * lci.OffsetRot; DetailLog("{0},BSLinksetCompound.RecomputeChildWorldPosition,oldPos={1},lci={2},newPos={3}", child.LocalID, oldPos, lci, child.RawPosition); } else { // TaintedObject is not used here so the raw position is set now and not at taint-time. child.Position = LinksetRoot.RawPosition + lci.OffsetFromRoot; child.Orientation = LinksetRoot.RawOrientation * lci.OffsetRot; } } else { // This happens when children have been added to the linkset but the linkset // has not been constructed yet. So like, at taint time, adding children to a linkset // and then changing properties of the children (makePhysical, for instance) // but the post-print action of actually rebuilding the linkset has not yet happened. // PhysicsScene.Logger.WarnFormat("{0} Restoring linkset child position failed because of no relative position computed. ID={1}", // LogHeader, child.LocalID); DetailLog("{0},BSLinksetCompound.recomputeChildWorldPosition,noRelativePositonInfo", child.LocalID); } */ }
// Remove linkage between myself and any possible children I might have. // Returns 'true' of any constraints were destroyed. // Called at taint time! private bool PhysicallyUnlinkAllChildrenFromRoot(BSPrimLinkable rootPrim) { DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID); return PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody); }
// private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINTS]"; public BSLinksetConstraints(BSScene scene, BSPrimLinkable parent) : base(scene, parent) { }
// Remove a child from a linkset. // Returns a new linkset for the child which is a linkset of one (just the // orphened child). // Called at runtime. public BSLinkset RemoveMeFromLinkset(BSPrimLinkable child) { lock (m_linksetActivityLock) { if (IsRoot(child)) { // Cannot remove the root from a linkset. return this; } RemoveChildFromLinkset(child); LinksetMass = ComputeLinksetMass(); } // The child is down to a linkset of just itself return BSLinkset.Factory(PhysicsScene, child); }