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:
parent
3a156063d8
commit
9f257ea4c4
@ -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
|
||||
|
@ -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; }
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user