/* ================ idPhysics_RigidBody::DropToFloorAndRest Drops the object straight down to the floor and verifies if the object is at rest on the floor. ================ */ void idPhysics_RigidBody::DropToFloorAndRest( void ) { idVec3 down; trace_t tr; if( testSolid ) { testSolid = false; if( gameLocal.clip.Contents( current.i.position, clipModel, current.i.orientation, clipMask, self ) ) { gameLocal.DWarning( "rigid body in solid for entity '%s' type '%s' at (%s)", self->name.c_str(), self->GetType()->classname, current.i.position.ToString( 0 ) ); Rest(); dropToFloor = false; return; } } // put the body on the floor down = current.i.position + gravityNormal * 128.0f; gameLocal.clip.Translation( tr, current.i.position, down, clipModel, current.i.orientation, clipMask, self ); current.i.position = tr.endpos; clipModel->Link( gameLocal.clip, self, clipModel->GetId(), tr.endpos, current.i.orientation ); // if on the floor already if( tr.fraction == 0.0f ) { // test if we are really at rest EvaluateContacts(); if( !TestIfAtRest() ) { gameLocal.DWarning( "rigid body not at rest for entity '%s' type '%s' at (%s)", self->name.c_str(), self->GetType()->classname, current.i.position.ToString( 0 ) ); } Rest(); dropToFloor = false; } else if( IsOutsideWorld() ) { gameLocal.Warning( "rigid body outside world bounds for entity '%s' type '%s' at (%s)", self->name.c_str(), self->GetType()->classname, current.i.position.ToString( 0 ) ); Rest(); dropToFloor = false; } }
bool PhysicalObj::PutOutOfGround() { if (IsOutsideWorld(Point2i(0, 0))) return false; if (IsInVacuum(Point2i(0, 0))) return true; bool left,right,top,bottom; left = GetWorld().IsInVacuum_left(*this, 0, 0); right = GetWorld().IsInVacuum_right(*this, 0, 0); top = GetWorld().IsInVacuum_top(*this, 0, 0); bottom = GetWorld().IsInVacuum_bottom(*this, 0, 0); int dx = (int)GetTestRect().GetSizeX() * (right-left); int dy = (int)GetTestRect().GetSizeY() * (top-bottom); if (!dx && !dy) return false; //->Don't know in which direction we should go... Point2i b(dx, dy); Double dir = b.ComputeAngle(); return PutOutOfGround(dir); }
/* ================ rvPhysics_Particle::DropToFloorAndRest Drops the object straight down to the floor ================ */ void rvPhysics_Particle::DropToFloorAndRest( void ) { idVec3 down; trace_t tr; if ( testSolid ) { testSolid = false; if ( gameLocal.Contents( self, current.origin, clipModel, clipModel->GetAxis(), clipMask, self ) ) { gameLocal.Warning( "entity in solid '%s' type '%s' at (%s)", self->name.c_str(), self->GetType()->classname, current.origin.ToString(0) ); PutToRest(); dropToFloor = false; return; } } // put the body on the floor down = current.origin + gravityNormal * 128.0f; // RAVEN BEGIN // ddynerman: multiple clip worlds gameLocal.Translation( self, tr, current.origin, down, clipModel, clipModel->GetAxis(), clipMask, self ); // RAVEN END current.origin = tr.endpos; // RAVEN BEGIN // ddynerman: multiple clip worlds clipModel->Link( self, clipModel->GetId(), tr.endpos, clipModel->GetAxis() ); // RAVEN END // if on the floor already if ( tr.fraction == 0.0f ) { PutToRest(); EvaluateContacts();//Do a final contact check. Items that drop to floor never do this check otherwise dropToFloor = false; } else if ( IsOutsideWorld() ) { gameLocal.Warning( "entity outside world bounds '%s' type '%s' at (%s)", self->name.c_str(), self->GetType()->classname, current.origin.ToString(0) ); PutToRest(); dropToFloor = false; } }
bool PhysicalObj::PutOutOfGround(Double direction, Double max_distance) { if (IsOutsideWorld(Point2i(0, 0))) return false; if (IsInVacuum(Point2i(0, 0), false)) return true; Double dx = cos(direction); Double dy = sin(direction); // (dx,dy) is a normal vector (cos^2+sin^2==1) Double step=1; while (step<max_distance && !IsInVacuum(Point2i(dx * step, dy * step), false)) step+=1.0; if (step<max_distance) SetXY(Point2i(dx*step + GetX(), dy*step + GetY())); else return false; //Can't put the object out of the ground return true; }
/* ================ idPhysics_Monster::Evaluate ================ */ bool idPhysics_Monster::Evaluate(int timeStepMSec, int endTimeMSec) { idVec3 masterOrigin, oldOrigin; idMat3 masterAxis; float timeStep; timeStep = MS2SEC(timeStepMSec); moveResult = MM_OK; blockingEntity = NULL; oldOrigin = current.origin; // if bound to a master if (masterEntity) { self->GetMasterPosition(masterOrigin, masterAxis); current.origin = masterOrigin + current.localOrigin * masterAxis; clipModel->Link(gameLocal.clip, self, 0, current.origin, clipModel->GetAxis()); current.velocity = (current.origin - oldOrigin) / timeStep; masterDeltaYaw = masterYaw; masterYaw = masterAxis[0].ToYaw(); masterDeltaYaw = masterYaw - masterDeltaYaw; return true; } // if the monster is at rest if (current.atRest >= 0) { return false; } ActivateContactEntities(); // move the monster velocity into the frame of a pusher current.velocity -= current.pushVelocity; clipModel->Unlink(); // check if on the ground idPhysics_Monster::CheckGround(current); // if not on the ground or moving upwards float upspeed; if (gravityNormal != vec3_zero) { upspeed = -(current.velocity * gravityNormal); } else { upspeed = current.velocity.z; } if (fly || (!forceDeltaMove && (!current.onGround || upspeed > 1.0f))) { if (upspeed < 0.0f) { moveResult = MM_FALLING; } else { current.onGround = false; moveResult = MM_OK; } delta = current.velocity * timeStep; if (delta != vec3_origin) { moveResult = idPhysics_Monster::SlideMove(current.origin, current.velocity, delta); delta.Zero(); } if (!fly) { current.velocity += gravityVector * timeStep; } } else { if (useVelocityMove) { delta = current.velocity * timeStep; } else { current.velocity = delta / timeStep; } current.velocity -= (current.velocity * gravityNormal) * gravityNormal; if (delta == vec3_origin) { Rest(); } else { // try moving into the desired direction moveResult = idPhysics_Monster::StepMove(current.origin, current.velocity, delta); delta.Zero(); } } clipModel->Link(gameLocal.clip, self, 0, current.origin, clipModel->GetAxis()); // get all the ground contacts EvaluateContacts(); // move the monster velocity back into the world frame current.velocity += current.pushVelocity; current.pushVelocity.Zero(); if (IsOutsideWorld()) { gameLocal.Warning("clip model outside world bounds for entity '%s' at (%s)", self->name.c_str(), current.origin.ToString(0)); Rest(); } return (current.origin != oldOrigin); }
/* ================ idPhysics_RigidBody::Evaluate Evaluate the impulse based rigid body physics. When a collision occurs an impulse is applied at the moment of impact but the remaining time after the collision is ignored. ================ */ bool idPhysics_RigidBody::Evaluate( int timeStepMSec, int endTimeMSec ) { rigidBodyPState_t next; idAngles angles; trace_t collision; idVec3 impulse; idEntity *ent; idVec3 oldOrigin, masterOrigin; idMat3 oldAxis, masterAxis; float timeStep; bool collided, cameToRest = false; timeStep = MS2SEC( timeStepMSec ); current.lastTimeStep = timeStep; if ( hasMaster ) { oldOrigin = current.i.position; oldAxis = current.i.orientation; self->GetMasterPosition( masterOrigin, masterAxis ); current.i.position = masterOrigin + current.localOrigin * masterAxis; if ( isOrientated ) { current.i.orientation = current.localAxis * masterAxis; } else { current.i.orientation = current.localAxis; } clipModel->Link( gameLocal.clip, self, clipModel->GetId(), current.i.position, current.i.orientation ); current.i.linearMomentum = mass * ( ( current.i.position - oldOrigin ) / timeStep ); current.i.angularMomentum = inertiaTensor * ( ( current.i.orientation * oldAxis.Transpose() ).ToAngularVelocity() / timeStep ); current.externalForce.Zero(); current.externalTorque.Zero(); return ( current.i.position != oldOrigin || current.i.orientation != oldAxis ); } // if the body is at rest if ( current.atRest >= 0 || timeStep <= 0.0f ) { DebugDraw(); return false; } // if putting the body to rest if ( dropToFloor ) { DropToFloorAndRest(); current.externalForce.Zero(); current.externalTorque.Zero(); return true; } #ifdef RB_TIMINGS timer_total.Start(); #endif // move the rigid body velocity into the frame of a pusher // current.i.linearMomentum -= current.pushVelocity.SubVec3( 0 ) * mass; // current.i.angularMomentum -= current.pushVelocity.SubVec3( 1 ) * inertiaTensor; clipModel->Unlink(); next = current; // calculate next position and orientation Integrate( timeStep, next ); #ifdef RB_TIMINGS timer_collision.Start(); #endif // check for collisions from the current to the next state collided = CheckForCollisions( timeStep, next, collision ); #ifdef RB_TIMINGS timer_collision.Stop(); #endif // set the new state current = next; if ( collided ) { // apply collision impulse if ( CollisionImpulse( collision, impulse ) ) { current.atRest = gameLocal.time; } } // update the position of the clip model clipModel->Link( gameLocal.clip, self, clipModel->GetId(), current.i.position, current.i.orientation ); DebugDraw(); if ( !noContact ) { #ifdef RB_TIMINGS timer_collision.Start(); #endif // get contacts EvaluateContacts(); #ifdef RB_TIMINGS timer_collision.Stop(); #endif // check if the body has come to rest if ( TestIfAtRest() ) { // put to rest Rest(); cameToRest = true; } else { // apply contact friction ContactFriction( timeStep ); } } if ( current.atRest < 0 ) { ActivateContactEntities(); } if ( collided ) { // if the rigid body didn't come to rest or the other entity is not at rest ent = gameLocal.entities[collision.c.entityNum]; if ( ent && ( !cameToRest || !ent->IsAtRest() ) ) { // apply impact to other entity ent->ApplyImpulse( self, collision.c.id, collision.c.point, -impulse ); } } // move the rigid body velocity back into the world frame // current.i.linearMomentum += current.pushVelocity.SubVec3( 0 ) * mass; // current.i.angularMomentum += current.pushVelocity.SubVec3( 1 ) * inertiaTensor; current.pushVelocity.Zero(); current.lastTimeStep = timeStep; current.externalForce.Zero(); current.externalTorque.Zero(); if ( IsOutsideWorld() ) { gameLocal.Warning( "rigid body moved outside world bounds for entity '%s' type '%s' at (%s)", self->name.c_str(), self->GetType()->classname, current.i.position.ToString(0) ); Rest(); } #ifdef RB_TIMINGS timer_total.Stop(); if ( rb_showTimings->integer == 1 ) { gameLocal.Printf( "%12s: t %u cd %u\n", self->name.c_str(), timer_total.Milliseconds(), timer_collision.Milliseconds() ); lastTimerReset = 0; } else if ( rb_showTimings->integer == 2 ) { numRigidBodies++; if ( endTimeMSec > lastTimerReset ) { gameLocal.Printf( "rb %d: t %u cd %u\n", numRigidBodies, timer_total.Milliseconds(), timer_collision.Milliseconds() ); } } if ( endTimeMSec > lastTimerReset ) { lastTimerReset = endTimeMSec; numRigidBodies = 0; timer_total.Clear(); timer_collision.Clear(); } #endif return true; }
/* ================ rvPhysics_Particle::Evaluate Evaluate the impulse based rigid body physics. When a collision occurs an impulse is applied at the moment of impact but the remaining time after the collision is ignored. ================ */ bool rvPhysics_Particle::Evaluate( int timeStepMSec, int endTimeMSec ) { particlePState_t next; float timeStep; float upspeed; timeStep = MS2SEC( timeStepMSec ); // if bound to a master if ( hasMaster ) { idVec3 masterOrigin; idMat3 masterAxis; idVec3 oldOrigin; oldOrigin = current.origin; self->GetMasterPosition( masterOrigin, masterAxis ); current.origin = masterOrigin + current.localOrigin * masterAxis; // RAVEN BEGIN // ddynerman: multiple clip worlds clipModel->Link( self, clipModel->GetId(), current.origin, current.localAxis * masterAxis ); // RAVEN END trace_t tr; gameLocal.Translation( self, tr, oldOrigin, current.origin, clipModel, clipModel->GetAxis(), clipMask, self ); if ( tr.fraction < 1.0f ) { self->Collide ( tr, current.origin - oldOrigin ); } DebugDraw(); return true; } // if the body is at rest if ( current.atRest >= 0 || timeStep <= 0.0f ) { DebugDraw(); return false; } // if putting the body to rest if ( dropToFloor ) { DropToFloorAndRest(); return true; } clipModel->Unlink(); // Determine if currently on the ground CheckGround ( ); // Determine the current upward velocity if ( gravityNormal != vec3_zero ) { upspeed = -( current.velocity * gravityNormal ); } else { upspeed = current.velocity.z; } // If not on the ground, or moving upwards, or bouncing and moving toward gravity then do a straight // forward slide move and gravity. if ( !current.onGround || upspeed > 1.0f || (bouncyness > 0.0f && upspeed < -PRT_BOUNCESTOP && !current.inWater) ) { // Force ground off when moving upward if ( upspeed > 0.0f ) { current.onGround = false; } SlideMove( current.origin, current.velocity, current.velocity * timeStep ); if ( current.onGround && upspeed < PRT_BOUNCESTOP ) { current.velocity -= ( current.velocity * gravityNormal ) * gravityNormal; } else { current.velocity += (gravityVector * timeStep); } } else { idVec3 delta; // Slow down due to friction ApplyFriction ( timeStep ); delta = current.velocity * timeStep; current.velocity -= ( current.velocity * gravityNormal ) * gravityNormal; if ( delta == vec3_origin ) { PutToRest( ); } else { SlideMove( current.origin, current.velocity, delta ); } } // update the position of the clip model // RAVEN BEGIN // ddynerman: multiple clip worlds clipModel->Link( self, clipModel->GetId(), current.origin, clipModel->GetAxis() ); // RAVEN END DebugDraw(); // get all the ground contacts EvaluateContacts(); current.pushVelocity.Zero(); if ( IsOutsideWorld() ) { gameLocal.Warning( "clip model outside world bounds for entity '%s' at (%s)", self->name.c_str(), current.origin.ToString(0) ); PutToRest(); } return true; }