public override void UpdateProperties(EntityProperties entprop) { // Updates only for individual prims and for the root object of a linkset. if (Linkset.IsRoot(this)) { // A temporary kludge to suppress the rotational effects introduced on vehicles by Bullet // TODO: handle physics introduced by Bullet with computed vehicle physics. if (_vehicle.IsActive) { entprop.RotationalVelocity = OMV.Vector3.Zero; } // Assign directly to the local variables so the normal set action does not happen _position = entprop.Position; _orientation = entprop.Rotation; _velocity = entprop.Velocity; _acceleration = entprop.Acceleration; _rotationalVelocity = entprop.RotationalVelocity; // The sanity check can change the velocity and/or position. if (IsPhysical && PositionSanityCheck(true)) { entprop.Position = _position; entprop.Velocity = _velocity; } OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation; // DEBUG DEBUG DEBUG DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}", LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity); // remember the current and last set values LastEntityProperties = CurrentEntityProperties; CurrentEntityProperties = entprop; base.RequestPhysicsterseUpdate(); } /* else { // For debugging, report the movement of children DetailLog("{0},BSPrim.UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}", LocalID, entprop.Position, entprop.Rotation, entprop.Velocity, entprop.Acceleration, entprop.RotationalVelocity); } */ // The linkset implimentation might want to know about this. Linkset.UpdateProperties(this, true); }
// Update the physical location and motion of the object. Called with data from Bullet. public abstract void UpdateProperties(EntityProperties entprop);
// Simulate one timestep public override float Simulate(float timeStep) { // prevent simulation until we've been initialized if (!m_initialized) return 5.0f; LastTimeStep = timeStep; int updatedEntityCount = 0; //Object updatedEntitiesPtr; int collidersCount = 0; //Object collidersPtr; int beforeTime = 0; int simTime = 0; // update the prim states while we know the physics engine is not busy int numTaints = _taintOperations.Count; InTaintTime = true; // Only used for debugging so locking is not necessary. ProcessTaints(); // Some of the physical objects requre individual, pre-step calls TriggerPreStepEvent(timeStep); // the prestep actions might have added taints ProcessTaints(); InTaintTime = false; // Only used for debugging so locking is not necessary. // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world. // Only enable this in a limited test world with few objects. // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG // step the physical world one interval m_simulationStep++; int numSubSteps = 0; try { if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount(); numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out m_updateArray, out collidersCount, out m_collisionArray); if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime); DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}", DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount, ObjectsWithCollisions.Count); } catch (Exception e) { m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}", LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e); DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}", DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount); updatedEntityCount = 0; collidersCount = 0; } // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in. // Get a value for 'now' so all the collision and update routines don't have to get their own. SimulationNowTime = Util.EnvironmentTickCount(); // If there were collisions, process them by sending the event to the prim. // Collisions must be processed before updates. if (collidersCount > 0) { for (int ii = 0; ii < collidersCount; ii++) { uint cA = m_collisionArray[ii].aID; uint cB = m_collisionArray[ii].bID; Vector3 point = new Vector3(m_collisionArray[ii].point.X, m_collisionArray[ii].point.Y, m_collisionArray[ii].point.Z); Vector3 normal = new Vector3(m_collisionArray[ii].normal.X, m_collisionArray[ii].normal.Y, m_collisionArray[ii].normal.Z); SendCollision(cA, cB, point, normal, 0.01f); SendCollision(cB, cA, point, -normal, 0.01f); } } // The above SendCollision's batch up the collisions on the objects. // Now push the collisions into the simulator. if (ObjectsWithCollisions.Count > 0) { foreach (BSPhysObject bsp in ObjectsWithCollisions) if (!bsp.SendCollisions()) { // If the object is done colliding, see that it's removed from the colliding list ObjectsWithNoMoreCollisions.Add(bsp); } } // This is a kludge to get avatar movement updates. // The simulator expects collisions for avatars even if there are have been no collisions. // The event updates avatar animations and stuff. // If you fix avatar animation updates, remove this overhead and let normal collision processing happen. foreach (BSPhysObject bsp in m_avatars) if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice bsp.SendCollisions(); // Objects that are done colliding are removed from the ObjectsWithCollisions list. // Not done above because it is inside an iteration of ObjectWithCollisions. // This complex collision processing is required to create an empty collision // event call after all collisions have happened on an object. This enables // the simulator to generate the 'collision end' event. if (ObjectsWithNoMoreCollisions.Count > 0) { foreach (BSPhysObject po in ObjectsWithNoMoreCollisions) ObjectsWithCollisions.Remove(po); ObjectsWithNoMoreCollisions.Clear(); } // Done with collisions. // If any of the objects had updated properties, tell the object it has been changed by the physics engine if (updatedEntityCount > 0) { for (int ii = 0; ii < updatedEntityCount; ii++) { BulletXNA.EntityProperties entprop = m_updateArray[ii]; BSPhysObject pobj; if (PhysObjects.TryGetValue(entprop.ID, out pobj)) { EntityProperties prop = new EntityProperties() { Acceleration = new Vector3(entprop.Acceleration.X, entprop.Acceleration.Y, entprop.Acceleration.Z), ID = entprop.ID, Position = new Vector3(entprop.Position.X,entprop.Position.Y,entprop.Position.Z), Rotation = new Quaternion(entprop.Rotation.X,entprop.Rotation.Y,entprop.Rotation.Z,entprop.Rotation.W), RotationalVelocity = new Vector3(entprop.AngularVelocity.X,entprop.AngularVelocity.Y,entprop.AngularVelocity.Z), Velocity = new Vector3(entprop.Velocity.X,entprop.Velocity.Y,entprop.Velocity.Z) }; //m_log.Debug(pobj.Name + ":" + prop.ToString() + "\n"); pobj.UpdateProperties(prop); } } } TriggerPostStepEvent(timeStep); // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world. // Only enable this in a limited test world with few objects. // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG // The physics engine returns the number of milliseconds it simulated this call. // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS. // Multiply by 55 to give a nominal frame rate of 55. return (float)numSubSteps * m_fixedTimeStep * 1000f * 55f; }
// 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); }