public BSActorAvatarMove(BSScene physicsScene, BSPhysObject pObj, string actorName) : base(physicsScene, pObj, actorName) { m_velocityMotor = null; m_walkingUpStairs = 0; m_physicsScene.DetailLog("{0},BSActorAvatarMove,constructor", m_controllingPrim.LocalID); }
public BSActorLockAxis(BSScene physicsScene, BSPhysObject pObj, string actorName) : base(physicsScene, pObj, actorName) { m_physicsScene.DetailLog("{0},BSActorLockAxis,constructor", m_controllingPrim.LocalID); LockAxisConstraint = null; HaveRegisteredForBeforeStepCallback = false; }
public BSActor(BSScene physicsScene, BSPhysObject pObj, string actorName) { m_physicsScene = physicsScene; m_controllingPrim = pObj; ActorName = actorName; Enabled = true; }
// Get a reference to a physical shape. Create if it doesn't exist public static BSShape GetShapeReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) { BSShape ret = null; if (prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE) { // an avatar capsule is close to a native shape (it is not shared) ret = BSShapeNative.GetReference(physicsScene, prim, BSPhysicsShapeType.SHAPE_CAPSULE, FixedShapeKey.KEY_CAPSULE); physicsScene.DetailLog("{0},BSShape.GetShapeReference,avatarCapsule,shape={1}", prim.LocalID, ret); } // Compound shapes are handled special as they are rebuilt from scratch. // This isn't too great a hardship since most of the child shapes will have already been created. if (ret == null && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND) { // Getting a reference to a compound shape gets you the compound shape with the root prim shape added ret = BSShapeCompound.GetReference(prim); physicsScene.DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, ret); } // Avatars have their own unique shape if (ret == null && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_AVATAR) { // Getting a reference to a compound shape gets you the compound shape with the root prim shape added ret = BSShapeAvatar.GetReference(prim); physicsScene.DetailLog("{0},BSShapeCollection.CreateGeom,avatarShape,shape={1}", prim.LocalID, ret); } if (ret == null) ret = GetShapeReferenceNonSpecial(physicsScene, forceRebuild, prim); return ret; }
// Called to update/change the body and shape for an object. // The object has some shape and body on it. Here we decide if that is the correct shape // for the current state of the object (static/dynamic/...). // If bodyCallback is not null, it is called if either the body or the shape are changed // so dependencies (like constraints) can be removed before the physical object is dereferenced. // Return 'true' if either the body or the shape changed. // Called at taint-time. public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim, PhysicalDestructionCallback bodyCallback) { m_physicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape"); bool ret = false; // This lock could probably be pushed down lower but building shouldn't take long lock (m_collectionActivityLock) { // Do we have the correct geometry for this type of object? // Updates prim.BSShape with information/pointers to shape. // Returns 'true' of BSShape is changed to a new shape. bool newGeom = CreateGeom(forceRebuild, prim, bodyCallback); // If we had to select a new shape geometry for the object, // rebuild the body around it. // Updates prim.BSBody with information/pointers to requested body // Returns 'true' if BSBody was changed. bool newBody = CreateBody((newGeom || forceRebuild), prim, m_physicsScene.World, bodyCallback); ret = newGeom || newBody; } DetailLog("{0},BSShapeCollection.GetBodyAndShape,taintExit,force={1},ret={2},body={3},shape={4}", prim.LocalID, forceRebuild, ret, prim.PhysBody, prim.PhysShape); return ret; }
public BSActorLockAxis(BSScene physicsScene, BSPhysObject pObj, string actorName) : base(physicsScene, pObj, actorName) { m_physicsScene.DetailLog("{0},BSActorLockAxis,constructor", m_controllingPrim.LocalID); LockAxisConstraint = null; // we place our constraint just before the simulation step to make sure the linkset is complete m_physicsScene.BeforeStep += PhysicsScene_BeforeStep; }
// For compound implimented linksets, if there are children, use compound shape for the root. public override BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor) { // Returning 'unknown' means we don't have a preference. BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN; if (IsRoot(requestor) && HasAnyChildren) { ret = BSPhysicsShapeType.SHAPE_COMPOUND; } // DetailLog("{0},BSLinksetCompound.PreferredPhysicalShape,call,shape={1}", LinksetRoot.LocalID, ret); return ret; }
public BSLinkset(BSScene scene, BSPhysObject 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 List<BSPhysObject>(); m_taintChildren = new List<BSPhysObject>(); m_mass = parent.MassRaw; }
// 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(BSPhysObject 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(); }); } }
// private static string LogHeader = "[BULLETSIM LINKSET]"; // Create the correct type of linkset for this child public static BSLinkset Factory(BSScene physScene, BSPhysObject parent) { BSLinkset ret = null; /* if (parent.IsPhysical) ret = new BSLinksetConstraints(physScene, parent); else ret = new BSLinksetManual(physScene, parent); */ // at the moment, there is only one ret = new BSLinksetConstraints(physScene, parent); return ret; }
// When physical properties are changed the linkset needs to recalculate // its internal properties. // May be called at runtime or taint-time (just pass the appropriate flag). public override void Refresh(BSPhysObject requestor, bool inTaintTime) { // If there are no children or not root, I am not the one that recomputes the constraints if (!HasAnyChildren || !IsRoot(requestor)) return; BSScene.TaintCallback refreshOperation = delegate() { RecomputeLinksetConstraintVariables(); DetailLog("{0},BSLinkset.Refresh,complete,rBody={1}", LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X")); }; if (inTaintTime) refreshOperation(); else PhysicsScene.TaintedObject("BSLinkSet.Refresh", refreshOperation); }
// Create the correct type of linkset for this child public static BSLinkset Factory(BSScene physScene, BSPhysObject parent) { BSLinkset ret = null; switch ((int)BSParam.LinksetImplementation) { case (int)LinksetImplementation.Constraint: ret = new BSLinksetConstraints(physScene, parent); break; case (int)LinksetImplementation.Compound: ret = new BSLinksetCompound(physScene, parent); break; case (int)LinksetImplementation.Manual: // ret = new BSLinksetManual(physScene, parent); break; default: ret = new BSLinksetCompound(physScene, parent); break; } return ret; }
// Forcefully removing a child from a linkset. // This is not being called by the child so we have to make sure the child doesn't think // it's still connected to the linkset. // Normal OpenSimulator operation will never do this because other SceneObjectPart information // also has to be updated (like pointer to prim's parent). private void RemoveChildFromOtherLinkset(BSPhysObject pchild) { pchild.Linkset = new BSLinkset(PhysicsScene, pchild); RemoveChildFromLinkset(pchild); }
// Return 'true' if this child is in this linkset public bool HasChild(BSPhysObject child) { bool ret = false; lock (m_linksetActivityLock) { foreach (BSPhysObject bp in m_children) { if (child.LocalID == bp.LocalID) { ret = true; break; } } } return ret; }
// 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(); }); } }
public BSActorLockAxis(BSScene physicsScene, BSPhysObject pObj, string actorName) : base(physicsScene, pObj, actorName) { m_physicsScene.DetailLog("{0},BSActorLockAxis,constructor", m_controllingPrim.LocalID); LockAxisConstraint = null; }
// 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(BSPhysObject child) { lock (m_linksetActivityLock) { if (IsRoot(child)) { // Cannot remove the root from a linkset. return this; } RemoveChildFromLinkset(child); } // The child is down to a linkset of just itself return new BSLinkset(PhysicsScene, child); }
public override bool Collide(uint collidingWith, BSPhysObject collidee, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth) { // prims in the same linkset cannot collide with each other BSPrimLinkable convCollidee = collidee as BSPrimLinkable; if (convCollidee != null && (this.Linkset.LinksetID == convCollidee.Linkset.LinksetID)) { return false; } // TODO: handle collisions of other objects with with children of linkset. // This is a problem for LinksetCompound since the children are packed into the root. return base.Collide(collidingWith, collidee, contactPoint, contactNormal, pentrationDepth); }
public BSActorSetTorque(BSScene physicsScene, BSPhysObject pObj, string actorName) : base(physicsScene, pObj, actorName) { m_torqueMotor = null; m_physicsScene.DetailLog("{0},BSActorSetTorque,constructor", m_controllingPrim.LocalID); }
public BSActorMoveToTarget(BSScene physicsScene, BSPhysObject pObj, string actorName) : base(physicsScene, pObj, actorName) { m_targetMotor = null; m_physicsScene.DetailLog("{0},BSActorMoveToTarget,constructor", m_controllingPrim.LocalID); }
public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim) { return(GetBodyAndShape(forceRebuild, sim, prim, null, null)); }
// Create a constraint between me (root of linkset) and the passed prim (the child). // Called at taint time! private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BulletBody rootBody, BSPhysObject childPrim, BulletBody childBody) { // Zero motion for children so they don't interpolate childPrim.ZeroMotion(); // 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},PhysicallyLinkAChildToRoot,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}", rootPrim.LocalID, rootPrim.LocalID, rootBody.ptr.ToString("X"), childPrim.LocalID, childBody.ptr.ToString("X"), 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. // 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 // construction because there is no body yet. The body // is constructed later at taint time. Thus we use the body address at time of the // taint creation but, if it is zero, use what's in the prim at the moment. // There is a possible race condition since shape can change without a taint call // (like changing to a mesh that is already constructed). The fix for that would be // to only change BSShape at taint time thus syncronizing these operations at // the cost of efficiency and lag. BS6DofConstraint constrain = new BS6DofConstraint( PhysicsScene.World, rootBody.ptr == IntPtr.Zero ? rootPrim.BSBody : rootBody, childBody.ptr == IntPtr.Zero ? childPrim.BSBody : childBody, 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 as a warning to 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); * * // 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},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), * // A point half way between the parent and child * // childRelativePosition/2, * // childRelativeRotation, * // childRelativePosition/2, * // inverseChildRelativeRotation, * 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(PhysicsScene.BoolNumeric(PhysicsScene.Params.linkConstraintUseFrameOffset)); constrain.TranslationalLimitMotor(PhysicsScene.BoolNumeric(PhysicsScene.Params.linkConstraintEnableTransMotor), PhysicsScene.Params.linkConstraintTransMotorMaxVel, PhysicsScene.Params.linkConstraintTransMotorMaxForce); constrain.SetCFMAndERP(PhysicsScene.Params.linkConstraintCFM, PhysicsScene.Params.linkConstraintERP); if (PhysicsScene.Params.linkConstraintSolverIterations != 0f) { constrain.SetSolverIterations(PhysicsScene.Params.linkConstraintSolverIterations); } RecomputeLinksetConstraintVariables(); }
// 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); }
// Return 'true' if the passed object is the root object of this linkset public bool IsRoot(BSPhysObject requestor) { return(requestor.LocalID == LinksetRoot.LocalID); }
public BSActorAvatarMove(BSScene physicsScene, BSPhysObject pObj, string actorName) : base(physicsScene, pObj, actorName) { m_velocityMotor = null; m_physicsScene.DetailLog("{0},BSActorAvatarMove,constructor", m_controllingPrim.LocalID); }
public static BSShape GetReference(BSPhysObject prim) { return new BSShapeNull(); }
// Create a mesh/hull shape or a native shape if 'nativeShapePossible' is 'true'. public bool CreateGeomNonSpecial(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback) { bool ret = false; bool haveShape = false; bool nativeShapePossible = true; PrimitiveBaseShape pbs = prim.BaseShape; // If the prim attributes are simple, this could be a simple Bullet native shape if (!haveShape && pbs != null && nativeShapePossible && ((pbs.SculptEntry && !BSParam.ShouldMeshSculptedPrim) || (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 && pbs.ProfileHollow == 0 && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0 && pbs.PathBegin == 0 && pbs.PathEnd == 0 && pbs.PathTaperX == 0 && pbs.PathTaperY == 0 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100 && pbs.PathShearX == 0 && pbs.PathShearY == 0))) { // Get the scale of any existing shape so we can see if the new shape is same native type and same size. OMV.Vector3 scaleOfExistingShape = OMV.Vector3.Zero; if (prim.PhysShape.HasPhysicalShape) { scaleOfExistingShape = PhysicsScene.PE.GetLocalScaling(prim.PhysShape); } if (DDetail) { DetailLog("{0},BSShapeCollection.CreateGeom,maybeNative,force={1},primScale={2},primSize={3},primShape={4}", prim.LocalID, forceRebuild, prim.Scale, prim.Size, prim.PhysShape.type); } // It doesn't look like Bullet scales spheres so make sure the scales are all equal if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1) && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z) { haveShape = true; if (forceRebuild || prim.Scale != scaleOfExistingShape || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_SPHERE ) { ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_SPHERE, FixedShapeKey.KEY_SPHERE, shapeCallback); if (DDetail) { DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}", prim.LocalID, forceRebuild, prim.PhysShape); } } } if (!haveShape && pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight) { haveShape = true; if (forceRebuild || prim.Scale != scaleOfExistingShape || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_BOX ) { ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX, shapeCallback); if (DDetail) { DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}", prim.LocalID, forceRebuild, prim.PhysShape); } } } } // If a simple shape is not happening, create a mesh and possibly a hull. if (!haveShape && pbs != null) { ret = CreateGeomMeshOrHull(prim, shapeCallback); } return(ret); }
// The creation of a mesh or hull can fail if an underlying asset is not available. // There are two cases: 1) the asset is not in the cache and it needs to be fetched; // and 2) the asset cannot be converted (like failed decompression of JPEG2000s). // The first case causes the asset to be fetched. The second case requires // us to not loop forever. // Called after creating a physical mesh or hull. If the physical shape was created, // just return. public static BulletShape VerifyMeshCreated(BSScene physicsScene, BulletShape newShape, BSPhysObject prim) { // If the shape was successfully created, nothing more to do if (newShape.HasPhysicalShape) { return(newShape); } // VerifyMeshCreated is called after trying to create the mesh. If we think the asset had been // fetched but we end up here again, the meshing of the asset must have failed. // Prevent trying to keep fetching the mesh by declaring failure. if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched) { prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; physicsScene.Logger.WarnFormat("{0} Fetched asset would not mesh. {1}, texture={2}", LogHeader, prim.PhysObjectName, prim.BaseShape.SculptTexture); physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,setFailed,objNam={1},tex={2}", prim.LocalID, prim.PhysObjectName, prim.BaseShape.SculptTexture); } else { // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset if (prim.BaseShape.SculptEntry && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Failed && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Waiting && prim.BaseShape.SculptTexture != OMV.UUID.Zero ) { physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,fetchAsset,objNam={1},tex={2}", prim.LocalID, prim.PhysObjectName, prim.BaseShape.SculptTexture); // Multiple requestors will know we're waiting for this asset prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Waiting; BSPhysObject xprim = prim; Util.FireAndForget(delegate { // physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,inFireAndForget", xprim.LocalID); RequestAssetDelegate assetProvider = physicsScene.RequestAssetMethod; if (assetProvider != null) { BSPhysObject yprim = xprim; // probably not necessary, but, just in case. assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset) { // physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,assetProviderCallback", xprim.LocalID); bool assetFound = false; string mismatchIDs = String.Empty; // DEBUG DEBUG if (asset != null && yprim.BaseShape.SculptEntry) { if (yprim.BaseShape.SculptTexture.ToString() == asset.ID) { yprim.BaseShape.SculptData = asset.Data; // This will cause the prim to see that the filler shape is not the right // one and try again to build the object. // No race condition with the normal shape setting since the rebuild is at taint time. yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Fetched; yprim.ForceBodyShapeRebuild(false /* inTaintTime */); assetFound = true; } else { mismatchIDs = yprim.BaseShape.SculptTexture.ToString() + "/" + asset.ID; } } if (!assetFound) { yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; } physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,fetchAssetCallback,found={1},isSculpt={2},ids={3}", yprim.LocalID, assetFound, yprim.BaseShape.SculptEntry, mismatchIDs); }); } else { xprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; physicsScene.Logger.ErrorFormat("{0} Physical object requires asset but no asset provider. Name={1}", LogHeader, physicsScene.Name); } }); } else { if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Failed) { physicsScene.Logger.WarnFormat("{0} Mesh failed to fetch asset. obj={1}, texture={2}", LogHeader, prim.PhysObjectName, prim.BaseShape.SculptTexture); physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,wasFailed,objNam={1},tex={2}", prim.LocalID, prim.PhysObjectName, prim.BaseShape.SculptTexture); } } } // While we wait for the mesh defining asset to be loaded, stick in a simple box for the object. BSShape fillShape = BSShapeNative.GetReference(physicsScene, prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,boxTempShape", prim.LocalID); return(fillShape.physShapeInfo); }
// Create a constraint between me (root of linkset) and the passed prim (the child). // Called at taint time! private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BulletBody rootBody, BSPhysObject childPrim, BulletBody childBody) { // Zero motion for children so they don't interpolate childPrim.ZeroMotion(); // 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},PhysicallyLinkAChildToRoot,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}", rootPrim.LocalID, rootPrim.LocalID, rootBody.ptr.ToString("X"), childPrim.LocalID, childBody.ptr.ToString("X"), 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. // 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 // construction because there is no body yet. The body // is constructed later at taint time. Thus we use the body address at time of the // taint creation but, if it is zero, use what's in the prim at the moment. // There is a possible race condition since shape can change without a taint call // (like changing to a mesh that is already constructed). The fix for that would be // to only change BSShape at taint time thus syncronizing these operations at // the cost of efficiency and lag. BS6DofConstraint constrain = new BS6DofConstraint( PhysicsScene.World, rootBody.ptr == IntPtr.Zero ? rootPrim.BSBody : rootBody, childBody.ptr == IntPtr.Zero ? childPrim.BSBody : childBody, 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 as a warning to 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); // 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},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), // A point half way between the parent and child // childRelativePosition/2, // childRelativeRotation, // childRelativePosition/2, // inverseChildRelativeRotation, 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(PhysicsScene.BoolNumeric(PhysicsScene.Params.linkConstraintUseFrameOffset)); constrain.TranslationalLimitMotor(PhysicsScene.BoolNumeric(PhysicsScene.Params.linkConstraintEnableTransMotor), PhysicsScene.Params.linkConstraintTransMotorMaxVel, PhysicsScene.Params.linkConstraintTransMotorMaxForce); constrain.SetCFMAndERP(PhysicsScene.Params.linkConstraintCFM, PhysicsScene.Params.linkConstraintERP); if (PhysicsScene.Params.linkConstraintSolverIterations != 0f) { constrain.SetSolverIterations(PhysicsScene.Params.linkConstraintSolverIterations); } RecomputeLinksetConstraintVariables(); }
public virtual bool Collide(uint collidingWith, BSPhysObject collidee, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth) { bool ret = false; // The following lines make IsColliding(), CollidingGround() and CollidingObj work CollidingStep = PhysScene.SimulationStep; if (collidingWith <= PhysScene.TerrainManager.HighestTerrainID) { CollidingGroundStep = PhysScene.SimulationStep; } else { CollidingObjectStep = PhysScene.SimulationStep; } CollisionAccumulation++; // For movement tests, remember if we are colliding with an object that is moving. ColliderIsMoving = collidee != null ? (collidee.RawVelocity != OMV.Vector3.Zero) : false; ColliderIsVolumeDetect = collidee != null ? (collidee.IsVolumeDetect) : false; // Make a collection of the collisions that happened the last simulation tick. // This is different than the collection created for sending up to the simulator as it is cleared every tick. if (CollisionsLastTickStep != PhysScene.SimulationStep) { CollisionsLastTick = new CollisionEventUpdate(); CollisionsLastTickStep = PhysScene.SimulationStep; } CollisionsLastTick.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); // If someone has subscribed for collision events log the collision so it will be reported up if (SubscribedEvents()) { lock (PhysScene.CollisionLock) { CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); } DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5},colliderMoving={6}", LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth, ColliderIsMoving); ret = true; } return ret; }
public BSLinksetCompound(BSScene scene, BSPhysObject parent) : base(scene, parent) { }
// The simulation step is telling this object about a collision. // Return 'true' if a collision was processed and should be sent up. // Return 'false' if this object is not enabled/subscribed/appropriate for or has already seen this collision. // Called at taint time from within the Step() function public virtual bool Collide(uint collidingWith, BSPhysObject collidee, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth) { bool ret = false; // The following lines make IsColliding(), CollidingGround() and CollidingObj work CollidingStep = PhysicsScene.SimulationStep; if (collidingWith <= PhysicsScene.TerrainManager.HighestTerrainID) { CollidingGroundStep = PhysicsScene.SimulationStep; } else { CollidingObjectStep = PhysicsScene.SimulationStep; } CollisionAccumulation++; // For movement tests, remember if we are colliding with an object that is moving. ColliderIsMoving = collidee != null ? (collidee.RawVelocity != OMV.Vector3.Zero) : false; // If someone has subscribed for collision events log the collision so it will be reported up if (SubscribedEvents()) { CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5},colliderMoving={6}", LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth, ColliderIsMoving); ret = true; } return ret; }
public static BSShape GetShapeReferenceNonSpecial(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) { return(null); }
// Link to a linkset where the child knows the parent. // Parent changing should not happen so do some sanity checking. // We return the parent's linkset so the child can track its membership. // Called at runtime. public BSLinkset AddMeToLinkset(BSPhysObject child) { lock (m_linksetActivityLock) { // Don't add the root to its own linkset if (!IsRoot(child)) AddChildToLinkset(child); } return this; }
// return 'true' if the prim's shape was changed. private bool CreateGeomMeshOrHull(BSPhysObject prim, PhysicalDestructionCallback shapeCallback) { bool ret = false; // Note that if it's a native shape, the check for physical/non-physical is not // made. Native shapes work in either case. if (prim.IsPhysical && BSParam.ShouldUseHullsForPhysicalObjects) { // Use a simple, single mesh convex hull shape if the object is simple enough BSShape potentialHull = null; PrimitiveBaseShape pbs = prim.BaseShape; // Use a simple, one section convex shape for prims that are probably convex (no cuts or twists) if (BSParam.ShouldUseSingleConvexHullForPrims && pbs != null && !pbs.SculptEntry && PrimHasNoCuts(pbs) ) { potentialHull = BSShapeConvexHull.GetReference(m_physicsScene, false /* forceRebuild */, prim); } // Use the GImpact shape if it is a prim that has some concaveness if (potentialHull == null && BSParam.ShouldUseGImpactShapeForPrims && pbs != null && !pbs.SculptEntry ) { potentialHull = BSShapeGImpact.GetReference(m_physicsScene, false /* forceRebuild */, prim); } // If not any of the simple cases, just make a hull if (potentialHull == null) { potentialHull = BSShapeHull.GetReference(m_physicsScene, false /*forceRebuild*/, prim); } // If the current shape is not what is on the prim at the moment, time to change. if (!prim.PhysShape.HasPhysicalShape || potentialHull.ShapeType != prim.PhysShape.ShapeType || potentialHull.physShapeInfo.shapeKey != prim.PhysShape.physShapeInfo.shapeKey) { DereferenceExistingShape(prim, shapeCallback); prim.PhysShape = potentialHull; ret = true; } else { // The current shape on the prim is the correct one. We don't need the potential reference. potentialHull.Dereference(m_physicsScene); } if (DDetail) { DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1}", prim.LocalID, prim.PhysShape); } } else { // Non-physical objects should be just meshes. BSShape potentialMesh = BSShapeMesh.GetReference(m_physicsScene, false /*forceRebuild*/, prim); // If the current shape is not what is on the prim at the moment, time to change. if (!prim.PhysShape.HasPhysicalShape || potentialMesh.ShapeType != prim.PhysShape.ShapeType || potentialMesh.physShapeInfo.shapeKey != prim.PhysShape.physShapeInfo.shapeKey) { DereferenceExistingShape(prim, shapeCallback); prim.PhysShape = potentialMesh; ret = true; } else { // We don't need this reference to the mesh that is already being using. potentialMesh.Dereference(m_physicsScene); } if (DDetail) { DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1}", prim.LocalID, prim.PhysShape); } } return(ret); }
// Return 'true' if the passed object is the root object of this linkset public bool IsRoot(BSPhysObject requestor) { return (requestor.LocalID == LinksetRoot.LocalID); }
// Get a reference to a physical shape. Create if it doesn't exist public static BSShape GetShapeReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) { BSShape ret = null; if (prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE) { // an avatar capsule is close to a native shape (it is not shared) ret = BSShapeNative.GetReference(physicsScene, prim, BSPhysicsShapeType.SHAPE_CAPSULE, FixedShapeKey.KEY_CAPSULE); physicsScene.DetailLog("{0},BSShape.GetShapeReference,avatarCapsule,shape={1}", prim.LocalID, ret); } // Compound shapes are handled special as they are rebuilt from scratch. // This isn't too great a hardship since most of the child shapes will have already been created. if (ret == null && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND) { // Getting a reference to a compound shape gets you the compound shape with the root prim shape added ret = BSShapeCompound.GetReference(prim); physicsScene.DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, ret); } // Avatars have their own unique shape if (ret == null && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_AVATAR) { // Getting a reference to a compound shape gets you the compound shape with the root prim shape added ret = BSShapeAvatar.GetReference(prim); physicsScene.DetailLog("{0},BSShapeCollection.CreateGeom,avatarShape,shape={1}", prim.LocalID, ret); } if (ret == null) { ret = GetShapeReferenceNonSpecial(physicsScene, forceRebuild, prim); } 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. // Called at taint-time! public bool MakeStatic(BSPhysObject child) { // What is done for each object in BSPrim is what we want. return false; }
// Get another reference to this shape. public abstract BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim);
// I am the root of a linkset and a new child is being added // Called while LinkActivity is locked. private void AddChildToLinkset(BSPhysObject child) { if (!HasChild(child)) { m_children.Add(child); BSPhysObject rootx = LinksetRoot; // capture the root and body as of now BulletBody rootBodyx = LinksetRoot.BSBody; BSPhysObject childx = child; 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() { DetailLog("{0},AddChildToLinkset,taint,child={1}", LinksetRoot.LocalID, child.LocalID); // build the physical binding between me and the child m_taintChildren.Add(childx); PhysicallyLinkAChildToRoot(rootx, rootBodyx, childx, childBodyx); }); } return; }
public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) { return(new BSShapeNull()); }
// 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. private void RemoveChildFromLinkset(BSPhysObject child) { if (m_children.Remove(child)) { BSPhysObject rootx = LinksetRoot; // capture the root and body as of now BulletBody rootBodyx = LinksetRoot.BSBody; BSPhysObject childx = child; BulletBody childBodyx = child.BSBody; DetailLog("{0},RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", childx.LocalID, rootx.LocalID, rootBodyx.ptr.ToString("X"), childx.LocalID, childBodyx.ptr.ToString("X")); PhysicsScene.TaintedObject("RemoveChildFromLinkset", delegate() { if (m_taintChildren.Contains(childx)) m_taintChildren.Remove(childx); PhysicallyUnlinkAChildFromRoot(rootx, rootBodyx, childx, childBodyx); RecomputeLinksetConstraintVariables(); }); } else { // This will happen if we remove the root of the linkset first. Non-fatal occurance. // PhysicsScene.Logger.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset", LogHeader); } return; }
public static BSShape GetShapeReferenceNonNative(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) { return null; }
// Remove linkage between myself and a particular child // The root and child bodies are passed in because we need to remove the constraint between // the bodies that were at unlink time. // Called at taint time! private void PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BulletBody rootBody, BSPhysObject childPrim, BulletBody childBody) { DetailLog("{0},PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}", rootPrim.LocalID, rootPrim.LocalID, rootBody.ptr.ToString("X"), childPrim.LocalID, childBody.ptr.ToString("X")); // Find the constraint for this link and get rid of it from the overall collection and from my list PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootBody, childBody); // Make the child refresh its location BulletSimAPI.PushUpdate2(childPrim.BSBody.ptr); }
// The simulation step is telling this object about a collision. // Return 'true' if a collision was processed and should be sent up. // Called at taint time from within the Step() function public virtual bool Collide(uint collidingWith, BSPhysObject collidee, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth) { bool ret = false; // The following lines make IsColliding(), CollidingGround() and CollidingObj work CollidingStep = PhysicsScene.SimulationStep; if (collidingWith <= PhysicsScene.TerrainManager.HighestTerrainID) { CollidingGroundStep = PhysicsScene.SimulationStep; } else { CollidingObjectStep = PhysicsScene.SimulationStep; } // prims in the same linkset cannot collide with each other if (collidee != null && (this.Linkset.LinksetID == collidee.Linkset.LinksetID)) { return ret; } // if someone has subscribed for collision events.... if (SubscribedEvents()) { CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}", LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth); ret = true; } return ret; }
public BSActorSetForce(BSScene physicsScene, BSPhysObject pObj, string actorName) : base(physicsScene, pObj, actorName) { m_forceMotor = null; m_physicsScene.DetailLog("{0},BSActorSetForce,constructor", m_controllingPrim.LocalID); }
// Called by a BSPhysObject to note that it has changed properties and this information // should be passed up to the simulator at the proper time. // Note: this is called by the BSPhysObject from invocation via DoPhysicsStep() above so // this is is under UpdateLock. public void PostUpdate(BSPhysObject updatee) { ObjectsWithUpdates.Add(updatee); }
// Create the geometry information in Bullet for later use. // The objects needs a hull if it's physical otherwise a mesh is enough. // if 'forceRebuild' is true, the geometry is unconditionally rebuilt. For meshes and hulls, // shared geometries will be used. If the parameters of the existing shape are the same // as this request, the shape is not rebuilt. // Info in prim.BSShape is updated to the new shape. // Returns 'true' if the geometry was rebuilt. // Called at taint-time! private bool CreateGeom(bool forceRebuild, BSPhysObject prim, PhysicalDestructionCallback shapeCallback) { bool ret = false; bool haveShape = false; bool nativeShapePossible = true; PrimitiveBaseShape pbs = prim.BaseShape; // Kludge to create the capsule for the avatar. // TDOD: Remove/redo this when BSShapeAvatar is working!! BSCharacter theChar = prim as BSCharacter; if (theChar != null) { DereferenceExistingShape(prim, shapeCallback); prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim, BSPhysicsShapeType.SHAPE_CAPSULE, FixedShapeKey.KEY_CAPSULE); ret = true; haveShape = true; } // If the prim attributes are simple, this could be a simple Bullet native shape // Native shapes work whether to object is static or physical. if (!haveShape && nativeShapePossible && pbs != null && PrimHasNoCuts(pbs) && (!pbs.SculptEntry || (pbs.SculptEntry && !BSParam.ShouldMeshSculptedPrim)) ) { // Get the scale of any existing shape so we can see if the new shape is same native type and same size. OMV.Vector3 scaleOfExistingShape = OMV.Vector3.Zero; if (prim.PhysShape.HasPhysicalShape) { scaleOfExistingShape = m_physicsScene.PE.GetLocalScaling(prim.PhysShape.physShapeInfo); } if (DDetail) { DetailLog("{0},BSShapeCollection.CreateGeom,maybeNative,force={1},primScale={2},primSize={3},primShape={4}", prim.LocalID, forceRebuild, prim.Scale, prim.Size, prim.PhysShape.physShapeInfo.shapeType); } // It doesn't look like Bullet scales native spheres so make sure the scales are all equal if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1) && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z) { haveShape = true; if (forceRebuild || prim.PhysShape.ShapeType != BSPhysicsShapeType.SHAPE_SPHERE ) { DereferenceExistingShape(prim, shapeCallback); prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim, BSPhysicsShapeType.SHAPE_SPHERE, FixedShapeKey.KEY_SPHERE); ret = true; } if (DDetail) { DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},rebuilt={2},shape={3}", prim.LocalID, forceRebuild, ret, prim.PhysShape); } } // If we didn't make a sphere, maybe a box will work. if (!haveShape && pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight) { haveShape = true; if (forceRebuild || prim.Scale != scaleOfExistingShape || prim.PhysShape.ShapeType != BSPhysicsShapeType.SHAPE_BOX ) { DereferenceExistingShape(prim, shapeCallback); prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); ret = true; } if (DDetail) { DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},rebuilt={2},shape={3}", prim.LocalID, forceRebuild, ret, prim.PhysShape); } } } // If a simple shape is not happening, create a mesh and possibly a hull. if (!haveShape && pbs != null) { ret = CreateGeomMeshOrHull(prim, shapeCallback); } return(ret); }