private BSShapeNative(BSScene physicsScene, BSPhysObject prim, BSPhysicsShapeType shapeType, FixedShapeKey shapeKey) { ShapeData nativeShapeData = new ShapeData(); nativeShapeData.Type = shapeType; nativeShapeData.ID = prim.LocalID; nativeShapeData.Scale = prim.Scale; nativeShapeData.Size = prim.Scale; nativeShapeData.MeshKey = (ulong)shapeKey; nativeShapeData.HullKey = (ulong)shapeKey; if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE) { ptr = BulletSimAPI.BuildCapsuleShape2(physicsScene.World.ptr, 1f, 1f, prim.Scale); physicsScene.DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale); } else { ptr = BulletSimAPI.BuildNativeShape2(physicsScene.World.ptr, nativeShapeData); } if (ptr == null) { physicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}", LogHeader, prim.LocalID, shapeType); } type = shapeType; key = (UInt64)shapeKey; }
// Remove a reference to a compound shape. // Taking a compound shape apart is a little tricky because if you just delete the // physical shape, it will free all the underlying children. We can't do that because // they could be shared. So, this removes each of the children from the compound and // dereferences them separately before destroying the compound collision object itself. // Called at taint-time. private void DereferenceCompound(BulletShape shape, ShapeDestructionCallback shapeCallback) { if (!BulletSimAPI.IsCompound2(shape.ptr)) { // Failed the sanity check!! PhysicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}", LogHeader, shape.type, shape.ptr.ToString()); if (DDetail) { DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}", BSScene.DetailLogZero, shape.type, shape.ptr.ToString()); } return; } int numChildren = BulletSimAPI.GetNumberOfCompoundChildren2(shape.ptr); if (DDetail) { DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", BSScene.DetailLogZero, shape, numChildren); } for (int ii = numChildren - 1; ii >= 0; ii--) { Object childShape = BulletSimAPI.RemoveChildShapeFromCompoundShapeIndex2(shape.ptr, ii); DereferenceAnonCollisionShape(childShape); } BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr); }
// 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(BSPhysObject child) { bool ret = false; DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child)); if (IsRoot(child)) { // The root is going dynamic. Make sure mass is properly set. ScheduleRebuild(LinksetRoot); } else { // The origional prims are removed from the world as the shape of the root compound // shape takes over. BulletSimAPI.AddToCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); BulletSimAPI.ForceActivationState2(child.PhysBody.ptr, ActivationState.DISABLE_SIMULATION); // We don't want collisions from the old linkset children. BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); child.PhysBody.collisionType = CollisionType.LinksetChild; ret = true; } return(ret); }
// Make this reference to the physical shape go away since native shapes are not shared. public override void Dereference(BSScene physicsScene) { // Native shapes are not tracked and are released immediately physicsScene.DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this); BulletSimAPI.DeleteCollisionShape2(physicsScene.World.ptr, ptr); ptr = null; // Garbage collection will free up this instance. }
// Apply the specificed collision mask into the physical world public void ApplyCollisionMask() { // Should assert the body has been added to the physical world. // (The collision masks are stored in the collision proxy cache which only exists for // a collision body that is in the world.) BulletSimAPI.SetCollisionGroupMask2(ptr, BulletSimData.CollisionTypeMasks[collisionType].group, BulletSimData.CollisionTypeMasks[collisionType].mask); }
// Create a body object in Bullet. // Updates prim.BSBody with the information about the new body if one is created. // Returns 'true' if an object was actually created. // Called at taint-time. private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletWorld sim, BulletShape shape, BodyDestructionCallback bodyCallback) { bool ret = false; // the mesh, hull or native shape must have already been created in Bullet bool mustRebuild = !prim.PhysBody.HasPhysicalBody; // If there is an existing body, verify it's of an acceptable type. // If not a solid object, body is a GhostObject. Otherwise a RigidBody. if (!mustRebuild) { CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(prim.PhysBody.ptr); if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY || !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT) { // If the collisionObject is not the correct type for solidness, rebuild what's there mustRebuild = true; } } if (mustRebuild || forceRebuild) { // Free any old body DereferenceBody(prim.PhysBody, true, bodyCallback); BulletBody aBody; Object bodyPtr = null; if (prim.IsSolid) { bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr, prim.LocalID, prim.RawPosition, prim.RawOrientation); if (DDetail) { DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString()); } } else { bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr, prim.LocalID, prim.RawPosition, prim.RawOrientation); if (DDetail) { DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString()); } } aBody = new BulletBody(prim.LocalID, bodyPtr); ReferenceBody(aBody, true); prim.PhysBody = aBody; ret = true; } return(ret); }
public override void Dispose() { if (m_terrainBody.HasPhysicalBody) { BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr); // Frees both the body and the shape. BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_terrainBody.ptr); } }
public virtual bool SetAngularLimits(Vector3 low, Vector3 high) { bool ret = false; if (m_enabled) { ret = BulletSimAPI.SetAngularLimits2(m_constraint.ptr, low, high); } return(ret); }
public bool SetBreakingImpulseThreshold(float threshold) { bool ret = false; if (m_enabled) { ret = BulletSimAPI.SetBreakingImpulseThreshold2(m_constraint.ptr, threshold); } return(ret); }
public bool SetFrames(Vector3 frameA, Quaternion frameArot, Vector3 frameB, Quaternion frameBrot) { bool ret = false; if (m_enabled) { BulletSimAPI.SetFrames2(m_constraint.ptr, frameA, frameArot, frameB, frameBrot); ret = true; } return(ret); }
public bool UseFrameOffset(bool useOffset) { bool ret = false; float onOff = useOffset ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse; if (m_enabled) { ret = BulletSimAPI.UseFrameOffset2(m_constraint.ptr, onOff); } return(ret); }
public virtual bool SetSolverIterations(float cnt) { bool ret = false; if (m_enabled) { BulletSimAPI.SetConstraintNumSolverIterations2(m_constraint.ptr, cnt); ret = true; } return(ret); }
// The physics engine says that properties have updated. Update same and inform // the world that things have changed. public override void UpdateProperties(EntityProperties entprop) { _position = entprop.Position; _orientation = entprop.Rotation; _velocity = entprop.Velocity; _acceleration = entprop.Acceleration; _rotationalVelocity = entprop.RotationalVelocity; // Do some sanity checking for the avatar. Make sure it's above ground and inbounds. PositionSanityCheck(true); if (_velocityMotor.Enabled) { // TODO: Decide if the step parameters should be changed depending on the avatar's // state (flying, colliding, ...). OMV.Vector3 stepVelocity = _velocityMotor.Step(PhysicsScene.LastTimeStep); // If falling, we keep the world's downward vector no matter what the other axis specify. if (!Flying && !IsColliding) { stepVelocity.Z = entprop.Velocity.Z; DetailLog("{0},BSCharacter.UpdateProperties,taint,overrideStepZWithWorldZ,stepVel={1}", LocalID, stepVelocity); } // If the user has said stop and we've stopped applying velocity correction, // the motor can be turned off. Set the velocity to zero so the zero motion is sent to the viewer. if (_velocityMotor.TargetValue.ApproxEquals(OMV.Vector3.Zero, 0.01f) && _velocityMotor.ErrorIsZero) { ZeroMotion(true); stepVelocity = OMV.Vector3.Zero; _velocityMotor.Enabled = false; DetailLog("{0},BSCharacter.UpdateProperties,taint,disableVelocityMotor,m={1}", LocalID, _velocityMotor); } _velocity = stepVelocity; entprop.Velocity = _velocity; BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity); } // remember the current and last set values LastEntityProperties = CurrentEntityProperties; CurrentEntityProperties = entprop; // Tell the linkset about value changes Linkset.UpdateProperties(this, true); // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop. // base.RequestPhysicsterseUpdate(); DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}", LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity); }
public virtual bool CalculateTransforms() { bool ret = false; if (m_enabled) { // Recompute the internal transforms BulletSimAPI.CalculateTransforms2(m_constraint.ptr); ret = true; } return(ret); }
public bool SetCFMAndERP(float cfm, float erp) { bool ret = false; if (m_enabled) { BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_CFM, cfm, ConstraintParamAxis.AXIS_ALL); BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_ERP, erp, ConstraintParamAxis.AXIS_ALL); BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_CFM, cfm, ConstraintParamAxis.AXIS_ALL); ret = true; } return(ret); }
public override void UnSubscribeEvents() { // DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName); SubscribedEventsMs = 0; PhysicsScene.TaintedObject(TypeName + ".UnSubscribeEvents", delegate() { // Make sure there is a body there because sometimes destruction happens in an un-ideal order. if (PhysBody.HasPhysicalBody) { CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); } }); }
public bool TranslationalLimitMotor(bool enable, float targetVelocity, float maxMotorForce) { bool ret = false; float onOff = enable ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse; if (m_enabled) { ret = BulletSimAPI.TranslationalLimitMotor2(m_constraint.ptr, onOff, targetVelocity, maxMotorForce); m_world.physicsScene.DetailLog("{0},BS6DOFConstraint,TransLimitMotor,enable={1},vel={2},maxForce={3}", BSScene.DetailLogZero, enable, targetVelocity, maxMotorForce); } return(ret); }
// Release all the terrain structures we might have allocated public void ReleaseGroundPlaneAndTerrain() { if (m_groundPlane.HasPhysicalBody) { if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr)) { BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_groundPlane.ptr); } m_groundPlane.Clear(); } ReleaseTerrain(); }
// Release the usage of a shape. public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback) { if (!shape.HasPhysicalShape) { return; } PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceShape", delegate() { if (shape.HasPhysicalShape) { if (shape.isNativeShape) { // Native shapes are not tracked and are released immediately if (DDetail) { DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}", BSScene.DetailLogZero, shape.ptr.ToString(), inTaintTime); } if (shapeCallback != null) { shapeCallback(shape); } BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr); } else { switch (shape.type) { case BSPhysicsShapeType.SHAPE_HULL: DereferenceHull(shape, shapeCallback); break; case BSPhysicsShapeType.SHAPE_MESH: DereferenceMesh(shape, shapeCallback); break; case BSPhysicsShapeType.SHAPE_COMPOUND: DereferenceCompound(shape, shapeCallback); break; case BSPhysicsShapeType.SHAPE_UNKNOWN: break; default: break; } } } }); }
// Sometimes we have a pointer to a collision shape but don't know what type it is. // Figure out type and call the correct dereference routine. // Called at taint-time. private void DereferenceAnonCollisionShape(Object cShape) { MeshDesc meshDesc; HullDesc hullDesc; BulletShape shapeInfo = new BulletShape(cShape); if (TryGetMeshByPtr(cShape, out meshDesc)) { shapeInfo.type = BSPhysicsShapeType.SHAPE_MESH; shapeInfo.shapeKey = meshDesc.shapeKey; } else { if (TryGetHullByPtr(cShape, out hullDesc)) { shapeInfo.type = BSPhysicsShapeType.SHAPE_HULL; shapeInfo.shapeKey = hullDesc.shapeKey; } else { if (BulletSimAPI.IsCompound2(cShape)) { shapeInfo.type = BSPhysicsShapeType.SHAPE_COMPOUND; } else { if (BulletSimAPI.IsNativeShape2(cShape)) { shapeInfo.isNativeShape = true; shapeInfo.type = BSPhysicsShapeType.SHAPE_BOX; // (technically, type doesn't matter) } } } } if (DDetail) { DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo); } if (shapeInfo.type != BSPhysicsShapeType.SHAPE_UNKNOWN) { DereferenceShape(shapeInfo, true, null); } else { PhysicsScene.Logger.ErrorFormat("{0} Could not decypher shape type. Region={1}, addr={2}", LogHeader, PhysicsScene.RegionName, cShape.ToString()); } }
// If there is information in m_mapInfo pointing to physical structures, release same. private void ReleaseHeightMapTerrain() { if (m_mapInfo != null) { if (m_mapInfo.terrainBody.HasPhysicalBody) { BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr); // Frees both the body and the shape. BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr); BulletSimAPI.ReleaseHeightMapInfo2(m_mapInfo.Ptr); } } m_mapInfo = null; }
// Set motion values to zero. // Do it to the properties so the values get set in the physics engine. // Push the setting of the values to the viewer. // Called at taint time! public override void ZeroMotion(bool inTaintTime) { _velocity = OMV.Vector3.Zero; _acceleration = OMV.Vector3.Zero; _rotationalVelocity = OMV.Vector3.Zero; // Zero some other properties directly into the physics engine PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() { if (PhysBody.HasPhysicalBody) { BulletSimAPI.ClearAllForces2(PhysBody.ptr); } }); }
public BSConstraintHinge(BulletWorld world, BulletBody obj1, BulletBody obj2, Vector3 pivotInA, Vector3 pivotInB, Vector3 axisInA, Vector3 axisInB, bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) { m_world = world; m_body1 = obj1; m_body2 = obj2; m_constraint = new BulletConstraint( BulletSimAPI.CreateHingeConstraint2(m_world.ptr, m_body1.ptr, m_body2.ptr, pivotInA, pivotInB, axisInA, axisInB, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies)); m_enabled = true; }
public override void ZeroAngularMotion(bool inTaintTime) { _rotationalVelocity = OMV.Vector3.Zero; PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() { if (PhysBody.HasPhysicalBody) { BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); // The next also get rid of applied linear force but the linear velocity is untouched. BulletSimAPI.ClearForces2(PhysBody.ptr); } }); }
public virtual void Dispose() { if (m_enabled) { m_enabled = false; if (m_constraint.HasPhysicalConstraint) { bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr); m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,id1={1},body1={2},id2={3},body2={4},success={5}", BSScene.DetailLogZero, m_body1.ID, m_body1.ptr.ToString(), m_body2.ID, m_body2.ptr.ToString(), success); m_constraint.Clear(); } } }
private void SetPhysicalProperties() { BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, PhysBody.ptr); ZeroMotion(true); ForcePosition = _position; // Set the velocity and compute the proper friction ForceVelocity = _velocity; // Setting the current and target in the motor will cause it to start computing any deceleration. _velocityMotor.Reset(); _velocityMotor.SetCurrent(_velocity); _velocityMotor.SetTarget(_velocity); _velocityMotor.Enabled = false; // This will enable or disable the flying buoyancy of the avatar. // Needs to be reset especially when an avatar is recreated after crossing a region boundry. Flying = _flying; BulletSimAPI.SetRestitution2(PhysBody.ptr, BSParam.AvatarRestitution); BulletSimAPI.SetMargin2(PhysShape.ptr, PhysicsScene.Params.collisionMargin); BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale); BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, BSParam.ContactProcessingThreshold); if (BSParam.CcdMotionThreshold > 0f) { BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, BSParam.CcdMotionThreshold); BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, BSParam.CcdSweptSphereRadius); } UpdatePhysicalMassProperties(RawMass, false); // Make so capsule does not fall over BulletSimAPI.SetAngularFactorV2(PhysBody.ptr, OMV.Vector3.Zero); BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_CHARACTER_OBJECT); BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, PhysBody.ptr, _position, _orientation); // BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ACTIVE_TAG); BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_DEACTIVATION); BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr); // Do this after the object has been added to the world PhysBody.collisionType = CollisionType.Avatar; PhysBody.ApplyCollisionMask(); }
// Create a btGeneric6DofConstraint public BSConstraint6Dof(BulletWorld world, BulletBody obj1, BulletBody obj2, Vector3 frame1, Quaternion frame1rot, Vector3 frame2, Quaternion frame2rot, bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) { m_world = world; m_body1 = obj1; m_body2 = obj2; m_constraint = new BulletConstraint( BulletSimAPI.Create6DofConstraint2(m_world.ptr, m_body1.ptr, m_body2.ptr, frame1, frame1rot, frame2, frame2rot, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies)); m_enabled = true; world.physicsScene.DetailLog("{0},BS6DofConstraint,createFrame,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}", BSScene.DetailLogZero, world.worldID, obj1.ID, obj1.ptr.ToString(), obj2.ID, obj2.ptr.ToString()); }
// 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(BSPhysObject rootPrim, BSPhysObject childPrim) { bool ret = false; DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}", rootPrim.LocalID, rootPrim.LocalID, rootPrim.PhysBody.ptr.ToString(), childPrim.LocalID, childPrim.PhysBody.ptr.ToString()); // 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 BulletSimAPI.PushUpdate2(childPrim.PhysBody.ptr); ret = true; } return(ret); }
private BulletShape CreatePhysicalMesh(string objName, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) { IMesh meshData = null; Object meshPtr = null; MeshDesc meshDesc; if (Meshes.TryGetValue(newMeshKey, out meshDesc)) { // If the mesh has already been built just use it. meshPtr = meshDesc.ptr; } else { meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true, false); if (meshData != null) { int[] indices = meshData.getIndexListAsInt(); List <OMV.Vector3> vertices = meshData.getVertexList(); float[] verticesAsFloats = new float[vertices.Count * 3]; int vi = 0; foreach (OMV.Vector3 vv in vertices) { verticesAsFloats[vi++] = vv.X; verticesAsFloats[vi++] = vv.Y; verticesAsFloats[vi++] = vv.Z; } // m_log.DebugFormat("{0}: BSShapeCollection.CreatePhysicalMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}", // LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count); meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr, indices.GetLength(0), indices, vertices.Count, verticesAsFloats); } } BulletShape newShape = new BulletShape(meshPtr, BSPhysicsShapeType.SHAPE_MESH); newShape.shapeKey = newMeshKey; return(newShape); }
// A version of the sanity check that also makes sure a new position value is // pushed back to the physics engine. This routine would be used by anyone // who is not already pushing the value. private bool PositionSanityCheck(bool inTaintTime) { bool ret = false; if (PositionSanityCheck()) { // The new position value must be pushed into the physics engine but we can't // just assign to "Position" because of potential call loops. PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate() { DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); if (PhysBody.HasPhysicalBody) { BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); } }); ret = true; } return(ret); }