1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-11-07 03:12:36 +01:00

Improved vehicle door dynamics with opening doors

This commit is contained in:
Daniel Evans 2014-12-14 20:45:58 +00:00
parent 25a3a0183b
commit 77f2277a68
4 changed files with 137 additions and 8 deletions

View File

@ -38,6 +38,8 @@ public:
ModelFrame* damaged;
btRigidBody* body;
btHingeConstraint* constraint;
bool holdAngle;
float targetAngle;
};
std::map<std::string, Part> dynamicParts;
@ -95,6 +97,8 @@ public:
return getPosition() + getRotation() * pos;
}
Part* getSeatEntryDoor(size_t seat);
virtual bool takeDamage(const DamageInfo& damage);
enum FrameState {
@ -103,9 +107,11 @@ public:
BROKEN
};
void setPartState(Part* part, FrameState state);
void setPartState(Part* part, FrameState state);
void setPartLocked(Part* part, bool locked);
void setPartLocked(Part* part, bool locked);
void setPartTarget(Part* part, bool enable, float target);
Part* getPart(const std::string& name);

View File

@ -218,12 +218,18 @@ bool Activities::EnterVehicle::update(CharacterObject *character, CharacterContr
auto anm_open = character->animations.car_open_lhs;
auto anm_enter = character->animations.car_getin_lhs;
auto entryDoor = vehicle->getSeatEntryDoor(seat);
if( entering ) {
if( character->animator->getAnimation() == anm_open ) {
if( character->animator->isCompleted() ) {
character->playAnimation(anm_enter, false);
character->enterVehicle(vehicle, seat);
}
else if( entryDoor && character->animator->getAnimationTime() >= 0.5f )
{
vehicle->setPartTarget(entryDoor, true, 1.f);
}
else {
//character->setPosition(vehicle->getSeatEntryPosition(seat));
character->rotation = vehicle->getRotation();
@ -231,6 +237,11 @@ bool Activities::EnterVehicle::update(CharacterObject *character, CharacterContr
}
else if( character->animator->getAnimation() == anm_enter ) {
if( character->animator->isCompleted() ) {
if( entryDoor )
{
vehicle->setPartTarget(entryDoor, true, 0.f);
}
// VehicleGetIn is over, finish activity
return true;
}
@ -248,7 +259,17 @@ bool Activities::EnterVehicle::update(CharacterObject *character, CharacterContr
// Warp character to vehicle orientation
character->controller->setRawMovement({0.f, 0.f, 0.f});
character->rotation = vehicle->getRotation();
character->playAnimation(anm_open, false);
// Determine if the door open animation should be skipped.
if( entryDoor == nullptr || (entryDoor->constraint != nullptr && entryDoor->constraint->getHingeAngle() >= 0.6f ) )
{
character->playAnimation(anm_enter, false);
character->enterVehicle(vehicle, seat);
}
else
{
character->playAnimation(anm_open, false);
}
}
else if( targetDistance > 15.f ) {
return true; // Give up if the vehicle is too far away.
@ -289,6 +310,12 @@ bool Activities::ExitVehicle::update(CharacterObject *character, CharacterContro
}
else {
character->playAnimation(anm_exit, false);
auto door = vehicle->getSeatEntryDoor(character->getCurrentSeat());
if( door )
{
vehicle->setPartTarget(door, true, 1.f);
}
}
return false;

View File

@ -8,6 +8,8 @@
#include <render/Model.hpp>
#include <engine/Animator.hpp>
#define PART_CLOSE_VELOCITY 0.5f
VehicleObject::VehicleObject(GameWorld* engine, const glm::vec3& pos, const glm::quat& rot, ModelHandle* model, VehicleDataHandle data, VehicleInfoHandle info, const glm::u8vec3& prim, const glm::u8vec3& sec)
: GameObject(engine, pos, rot, model),
steerAngle(0.f), throttle(0.f), brake(0.f), handbrake(false),
@ -131,6 +133,7 @@ glm::quat VehicleObject::getRotation() const
}
#include <glm/gtc/type_ptr.hpp>
#include <boost/concept_check.hpp>
void VehicleObject::tick(float dt)
{
@ -279,7 +282,7 @@ void VehicleObject::tickPhysics(float dt)
_lastHeight = vH;
// Update hinge object rotations
for(auto it : dynamicParts) {
for(auto& it : dynamicParts) {
if(it.second.body == nullptr) continue;
auto inv = glm::inverse(getRotation());
auto rot = it.second.body->getWorldTransform().getRotation();
@ -289,6 +292,22 @@ void VehicleObject::tickPhysics(float dt)
auto next = prev;
next.rotation = r2;
skeleton->setData(it.second.dummy->getIndex(), { next, prev, true } );
if( it.second.holdAngle )
{
it.second.constraint->setMotorTarget(it.second.targetAngle, 0.1f);
}
// If the part is moving quite fast and near the limit, lock it.
/// @TODO not all parts rotate in the z axis.
if(it.second.body->getAngularVelocity().getZ() >= PART_CLOSE_VELOCITY)
{
auto d = it.second.constraint->getHingeAngle() - it.second.constraint->getLowerLimit();
if( std::abs(d) < 0.01f )
{
setPartLocked(&(it.second), true);
}
}
}
}
}
@ -371,6 +390,23 @@ void VehicleObject::setOccupant(size_t seat, GameObject* occupant)
}
}
VehicleObject::Part* VehicleObject::getSeatEntryDoor(size_t seat)
{
auto pos = info->seats.at(seat).offset + glm::vec3(0.f, 0.5f, 0.f);
Part* nearestDoor = nullptr;
float d = std::numeric_limits<float>::max();
for(auto& p : dynamicParts)
{
float partDist = glm::distance(p.second.dummy->getDefaultTranslation(), pos);
if( partDist < d && p.second.dummy->getName().substr(0, 5) == "door_" )
{
d = partDist;
nearestDoor = &p.second;
}
}
return nearestDoor;
}
bool VehicleObject::takeDamage(const GameObject::DamageInfo& dmg)
{
mHealth -= dmg.hitpoints;
@ -448,6 +484,39 @@ void VehicleObject::setPartLocked(VehicleObject::Part* part, bool locked)
else if( part->body != nullptr && locked == true )
{
destroyObjectHinge(part);
// Restore default bone transform
auto dt = part->dummy->getDefaultTranslation();
auto dr = glm::quat_cast(part->dummy->getDefaultRotation());
Skeleton::FrameTransform tf { dt, dr };
skeleton->setData(part->dummy->getIndex(), { tf, tf, true });
}
}
void VehicleObject::setPartTarget(VehicleObject::Part* part, bool enable, float target)
{
if( enable )
{
if( part->body == nullptr )
{
setPartLocked(part, false);
}
part->targetAngle = target;
part->holdAngle = true;
part->constraint->enableMotor(true);
part->body->activate(true);
}
else
{
part->targetAngle = target;
part->holdAngle = false;
if( part->constraint )
{
part->constraint->enableMotor(false);
}
}
}
@ -492,7 +561,8 @@ void VehicleObject::registerPart(ModelFrame* mf)
mf,
normal,
damage,
nullptr, nullptr
nullptr, nullptr,
false, 0.f
}
});
}
@ -582,14 +652,18 @@ void VehicleObject::createObjectHinge(btTransform& local, Part *part)
void VehicleObject::destroyObjectHinge(Part* part)
{
if( part->body ) {
engine->dynamicsWorld->removeRigidBody(part->body);
if( part->body != nullptr ) {
engine->dynamicsWorld->removeConstraint(part->constraint);
engine->dynamicsWorld->removeRigidBody(part->body);
delete part->body;
delete part->constraint;
part->body = nullptr;
part->constraint = nullptr;
// Reset target.
part->holdAngle = false;
}
}

View File

@ -105,6 +105,28 @@ BOOST_AUTO_TEST_CASE(test_hinges)
BOOST_CHECK_EQUAL( part->constraint, nullptr );
BOOST_CHECK_EQUAL( part->body, nullptr );
Global::get().e->destroyObject(vehicle);
}
BOOST_AUTO_TEST_CASE(test_open_part)
{
VehicleObject* vehicle = Global::get().e->createVehicle(90u, glm::vec3(10.f, 0.f, 0.f), glm::quat());
BOOST_REQUIRE( vehicle != nullptr );
VehicleObject::Part* part = vehicle->getPart("door_lf_dummy");
BOOST_REQUIRE( part != nullptr );
BOOST_CHECK_EQUAL( part->body, nullptr );
vehicle->setPartLocked(part, true);
vehicle->setPartTarget(part, true, 1.f);
/// @todo a reasonable test
Global::get().e->destroyObject(vehicle);
}
BOOST_AUTO_TEST_SUITE_END()