1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-09-15 06:52:34 +02:00

Animator update and vehicle frame damage

Refactor Animator to handle frame visibility, removing isFrameVisible()
Make frames be removed if they are damaged due to physics.
This commit is contained in:
Daniel Evans 2014-06-18 05:08:15 +01:00
parent 3a156063d8
commit 9f257ea4c4
8 changed files with 153 additions and 62 deletions

View File

@ -27,13 +27,17 @@ class Animator
*/
Model* model;
struct BonePair {
struct FrameInstanceData {
bool visible;
AnimationBone* bone;
// Used if bone is non-null.
AnimationKeyframe first;
AnimationKeyframe second;
// Used if bone is null and entry exists.
glm::quat orientation;
};
std::map<ModelFrame*, BonePair> _boneMatrices;
std::map<ModelFrame*, FrameInstanceData> _frameInstances;
// Used in determining how far the skeleton being animated has moved
// From it's local origin.
@ -72,6 +76,9 @@ public:
void setModel(Model* model);
void setFrameVisibility(ModelFrame* frame, bool visible);
bool getFrameVisibility(ModelFrame* frame) const;
/**
* @brief getFrameMatrix returns the matrix for frame at the given time
* @param t

View File

@ -99,8 +99,6 @@ struct GameObject
virtual bool takeDamage(const DamageInfo& damage) { return false; }
virtual bool isFrameVisible(ModelFrame* frame, float distance) const { return true; }
virtual bool isAnimationFixed() const { return true; }
virtual bool isInWater() const { return _inWater; }

View File

@ -102,8 +102,6 @@ public:
void setPartDamaged(unsigned int flag, bool damaged);
virtual bool isFrameVisible(ModelFrame *frame, float distance) const;
void applyWaterFloat(const glm::vec3& relPt);
};

View File

@ -15,7 +15,7 @@ void Animator::reset()
serverTime = 0.f;
lastServerTime = 0.f;
if( _boneMatrices.empty() ) {
if( _frameInstances.empty() ) {
if( ! getAnimation() || ! model ) return;
for( ModelFrame* f : model->frames ) {
@ -23,7 +23,7 @@ void Animator::reset()
if( it == getAnimation()->bones.end() ) continue;
auto A = getKeyframeAt(f, 0.f);
_boneMatrices[f] = { it->second, A, A };
_frameInstances[f] = { true, it->second, A, A };
}
}
}
@ -38,7 +38,7 @@ void Animator::setAnimation(Animation *animation, bool repeat)
queueAnimation(animation);
this->repeat = repeat;
_boneMatrices.clear();
_frameInstances.clear();
reset();
}
@ -62,11 +62,38 @@ void Animator::setModel(Model *model)
this->model = model;
_boneMatrices.clear();
_frameInstances.clear();
reset();
}
void Animator::setFrameVisibility(ModelFrame *frame, bool visible)
{
auto fit = _frameInstances.find(frame);
if( fit != _frameInstances.end() ) {
fit->second.visible = visible;
}
else {
_frameInstances.insert({
frame,
{
visible,
nullptr,
{}, {}
}
});
}
}
bool Animator::getFrameVisibility(ModelFrame *frame) const
{
auto fit = _frameInstances.find(frame);
if( fit != _frameInstances.end() ) {
return fit->second.visible;
}
return true;
}
void Animator::tick(float dt)
{
if( model == nullptr || _animations.empty() ) {
@ -76,7 +103,7 @@ void Animator::tick(float dt)
lastServerTime = serverTime;
serverTime += dt;
for( auto& p : _boneMatrices ) {
for( auto& p : _frameInstances ) {
p.second.second = p.second.first;
float t = getAnimationTime();
p.second.first = getKeyframeAt(p.first, t);
@ -153,8 +180,8 @@ AnimationKeyframe Animator::getKeyframeAt(ModelFrame *frame, float time) const
glm::mat4 Animator::getFrameMatrix(ModelFrame *frame, float alpha, bool ignoreRoot) const
{
auto it = _boneMatrices.find( frame );
if( it != _boneMatrices.end() ) {
auto it = _frameInstances.find( frame );
if( it != _frameInstances.end() && it->second.bone ) {
const AnimationKeyframe& S = it->second.first;
const AnimationKeyframe& F = it->second.second;

View File

@ -412,6 +412,31 @@ int GameWorld::getMinute()
return fmod(gameTime, 60.f);
}
void handleVehicleResponse(GameObject* object, btManifoldPoint& mp, bool isA)
{
bool isVehicle = object->type() == GameObject::Vehicle;
if(! isVehicle) return;
if( mp.getAppliedImpulse() <= 100.f ) return;
btVector3 src, dmg;
if(isA) {
src = mp.getPositionWorldOnB();
dmg = mp.getPositionWorldOnA();
}
else {
src = mp.getPositionWorldOnA();
dmg = mp.getPositionWorldOnB();
}
object->takeDamage({
{dmg.x(), dmg.y(), dmg.z()},
{src.x(), src.y(), src.z()},
0.f,
GameObject::DamageInfo::Physics,
mp.getAppliedImpulse()
});
}
bool GameWorld::ContactProcessedCallback(btManifoldPoint &mp, void *body0, void *body1)
{
auto obA = static_cast<btCollisionObject*>(body0);
@ -433,37 +458,44 @@ bool GameWorld::ContactProcessedCallback(btManifoldPoint &mp, void *body0, void
InstanceObject* dynInst = nullptr;
const btRigidBody* instBody = nullptr, * otherBody = nullptr;
btVector3 src, dmg;
if( valA ) {
dynInst = static_cast<InstanceObject*>(a);
instBody = static_cast<const btRigidBody*>(obA);
otherBody = static_cast<const btRigidBody*>(obB);
src = mp.getPositionWorldOnB();
dmg = mp.getPositionWorldOnA();
}
else {
dynInst = static_cast<InstanceObject*>(b);
instBody = static_cast<const btRigidBody*>(obB);
otherBody = static_cast<const btRigidBody*>(obA);
src = mp.getPositionWorldOnA();
dmg = mp.getPositionWorldOnB();
}
if( dynInst->dynamics == nullptr || ! instBody->isStaticObject() ) {
return false;
}
if( dynInst->dynamics != nullptr && instBody->isStaticObject() ) {
// Attempt to determine relative velocity.
auto dV = (otherBody->getLinearVelocity());
auto impulse = dV.length()/ (otherBody->getInvMass());
// Attempt to determine relative velocity.
auto dV = (otherBody->getLinearVelocity());
auto impulse = dV.length()/ (otherBody->getInvMass());
auto src = otherBody->getWorldTransform().getOrigin();
// Ignore collision if the object is about to be uprooted.
if( dynInst->dynamics->uprootForce <= impulse ) {
dynInst->takeDamage({
dynInst->getPosition(),
{src.x(), src.y(), src.z()},
0.f,
GameObject::DamageInfo::Physics,
impulse
});
if( dynInst->dynamics->uprootForce <= impulse ) {
dynInst->takeDamage({
{dmg.x(), dmg.y(), dmg.z()},
{src.x(), src.y(), src.z()},
0.f,
GameObject::DamageInfo::Physics,
impulse
});
}
}
}
// Handle vehicles
if(a) handleVehicleResponse(a, mp, true);
if(b) handleVehicleResponse(b, mp, false);
return true;
}

View File

@ -5,6 +5,7 @@
#include <sys/stat.h>
#include <data/CollisionModel.hpp>
#include <render/Model.hpp>
#include <engine/Animator.hpp>
VehicleObject::VehicleObject(GameWorld* engine, const glm::vec3& pos, const glm::quat& rot, ModelHandle* model, VehicleDataHandle data, VehicleInfoHandle info, const glm::vec3& prim, const glm::vec3& sec)
: GameObject(engine, pos, rot, model),
@ -113,6 +114,22 @@ VehicleObject::VehicleObject(GameWorld* engine, const glm::vec3& pos, const glm:
}
}
// Hide all LOD and damage frames.
animator = new Animator;
animator->setModel(model->model);
for(ModelFrame* f : model->model->frames) {
auto& name = f->getName();
bool isDam = name.find("_dam") != name.npos;
bool isLod = name.find("lo") != name.npos;
bool isDum = name.find("_dummy") != name.npos;
bool isOk = name.find("_ok") != name.npos;
if(isDam || isLod || isDum ) {
animator->setFrameVisibility(f, false);
}
}
}
VehicleObject::~VehicleObject()
@ -122,6 +139,7 @@ VehicleObject::~VehicleObject()
delete physBody;
delete physVehicle;
delete physRaycaster;
delete animator;
ejectAll();
}
@ -375,6 +393,40 @@ void VehicleObject::setOccupant(size_t seat, GameObject* occupant)
bool VehicleObject::takeDamage(const GameObject::DamageInfo& dmg)
{
mHealth -= dmg.hitpoints;
const float frameDamageThreshold = 1500.f;
if( dmg.impulse >= frameDamageThreshold ) {
auto dpoint = dmg.damageLocation;
dpoint -= getPosition();
dpoint = glm::inverse(getRotation()) * dpoint;
float d = std::numeric_limits<float>::max();
ModelFrame* nearest = nullptr;
// find nearest "_ok" frame.
for(ModelFrame* f : model->model->frames) {
auto& name = f->getName();
if( name.find("_ok") != name.npos ) {
auto pp = f->getMatrix() * glm::vec4(0.f, 0.f, 0.f, 1.f);
float td = glm::distance(glm::vec3(pp), dpoint);
if( td < d ) {
d = td;
nearest = f;
}
}
}
if( nearest && animator->getFrameVisibility(nearest) ) {
animator->setFrameVisibility(nearest, false);
// find damaged
auto name = nearest->getName();
name.replace(name.find("_ok"), 3, "_dam");
auto damage = model->model->findFrame(name);
animator->setFrameVisibility(damage, true);
}
}
return true;
}
@ -406,30 +458,6 @@ unsigned int nameToDamageFlag(const std::string& name)
return 0;
}
bool VehicleObject::isFrameVisible(ModelFrame *frame, float distance) const
{
auto& name = frame->getName();
bool isDam = name.find("_dam") != name.npos;
bool isOk = name.find("_ok") != name.npos;
if(distance >= 150.f) {
return name.find("lo") != name.npos;
}
if(name.find("lo") != name.npos
|| name.find("_dummy") != name.npos) return false;
if(isDam || isOk) {
unsigned int dft = nameToDamageFlag(name);
if(isDam) {
return (damageFlags & dft) == dft;
}
else {
return (damageFlags & dft) == 0;
}
}
return true;
}
void VehicleObject::applyWaterFloat(const glm::vec3 &relPt)
{
auto ws = getPosition() + relPt;

View File

@ -525,9 +525,9 @@ bool GameRenderer::renderFrame(Model* m, ModelFrame* f, const glm::mat4& matrix,
localmatrix *= f->getTransform();
}
float distance = glm::distance(camera.worldPos, glm::vec3(localmatrix[3]));
bool vis = (object == nullptr || object->animator == nullptr) ||
object->animator->getFrameVisibility(f);
bool vis = object == nullptr || object->isFrameVisible(f, distance);
for(size_t g : f->getGeometries()) {
if(!vis ) continue;

View File

@ -2,6 +2,7 @@
#include "test_globals.hpp"
#include <objects/VehicleObject.hpp>
#include <render/Model.hpp>
#include <engine/Animator.hpp>
BOOST_AUTO_TEST_SUITE(VehicleTests)
@ -36,18 +37,18 @@ BOOST_AUTO_TEST_CASE(vehicle_frame_flags)
BOOST_REQUIRE(bonnet_ok != nullptr);
BOOST_REQUIRE(bonnet_dam != nullptr);
BOOST_CHECK(vehicle->isFrameVisible(bonnet_ok, 0.f));
BOOST_CHECK(!vehicle->isFrameVisible(bonnet_dam, 0.f));
BOOST_CHECK(vehicle->animator->getFrameVisibility(bonnet_ok));
BOOST_CHECK(!vehicle->animator->getFrameVisibility(bonnet_dam));
vehicle->setPartDamaged(VehicleObject::DF_Bonnet, true);
BOOST_CHECK(!vehicle->isFrameVisible(bonnet_ok, 0.f));
BOOST_CHECK(vehicle->isFrameVisible(bonnet_dam, 0.f));
BOOST_CHECK(!vehicle->animator->getFrameVisibility(bonnet_ok));
BOOST_CHECK(vehicle->animator->getFrameVisibility(bonnet_dam));
vehicle->setPartDamaged(VehicleObject::DF_Bonnet, false);
BOOST_CHECK(vehicle->isFrameVisible(bonnet_ok, 0.f));
BOOST_CHECK(!vehicle->isFrameVisible(bonnet_dam, 0.f));
BOOST_CHECK(vehicle->animator->getFrameVisibility(bonnet_ok));
BOOST_CHECK(!vehicle->animator->getFrameVisibility(bonnet_dam));
Global::get().e->destroyObject(vehicle);
}