diff --git a/rwengine/include/engine/Animator.hpp b/rwengine/include/engine/Animator.hpp index 0d8fbd46..db504e2c 100644 --- a/rwengine/include/engine/Animator.hpp +++ b/rwengine/include/engine/Animator.hpp @@ -45,6 +45,9 @@ public: */ void setAnimation(Animation* animation, bool repeat = true); + Animation* getAnimation() const + { return animation; } + void setModel(Model* model); /** diff --git a/rwengine/include/objects/GTACharacter.hpp b/rwengine/include/objects/GTACharacter.hpp index ad8ec99d..abc5da5b 100644 --- a/rwengine/include/objects/GTACharacter.hpp +++ b/rwengine/include/objects/GTACharacter.hpp @@ -10,6 +10,27 @@ class GTAVehicle; class GameWorld; +struct AnimationGroup +{ + Animation* idle; + Animation* walk; + Animation* walk_start; + Animation* run; + + Animation* jump_start; + Animation* jump_glide; + Animation* jump_land; + + Animation* car_sit; + Animation* car_sit_low; + + AnimationGroup() + : idle(nullptr), walk(nullptr), walk_start(nullptr), run(nullptr), + jump_start(nullptr), jump_glide(nullptr), jump_land(nullptr), + car_sit(nullptr), car_sit_low(nullptr) + {} +}; + /** * @brief The GTACharacter struct * Stores data relating to an instance of a "pedestrian". @@ -48,6 +69,8 @@ public: GTAAIController* controller; + AnimationGroup animations; + /** * @brief GTACharacter Constructs a Character * @param pos @@ -63,7 +86,7 @@ public: Activity currentActivity; - void changeAction(Activity newAction); + void enterActivity(Activity act); void tick(float dt); diff --git a/rwengine/src/ai/GTADefaultAIController.cpp b/rwengine/src/ai/GTADefaultAIController.cpp index 45fc1969..d223ec8e 100644 --- a/rwengine/src/ai/GTADefaultAIController.cpp +++ b/rwengine/src/ai/GTADefaultAIController.cpp @@ -13,14 +13,14 @@ void GTADefaultAIController::update(float dt) else { getUpTime -= dt; if(getUpTime < 0.f) { - character->changeAction(GTACharacter::GettingUp); + character->enterActivity(GTACharacter::GettingUp); getUpTime = -1.f; } } } else if (character->currentActivity == GTACharacter::GettingUp ) { if( character->animator->isCompleted() ) { - character->changeAction(GTACharacter::Idle); + character->enterActivity(GTACharacter::Idle); } } else { @@ -43,10 +43,10 @@ void GTADefaultAIController::update(float dt) } if( targetNode == nullptr ) { - character->changeAction(GTACharacter::Idle); + character->enterActivity(GTACharacter::Idle); } else { - character->changeAction(GTACharacter::Walk); + character->enterActivity(GTACharacter::Walk); // Choose a new random margin if(character->getCurrentVehicle()) { std::uniform_real_distribution dist(2.f, 2.5f); @@ -71,14 +71,14 @@ void GTADefaultAIController::update(float dt) } else { targetNode = nullptr; - character->changeAction(GTACharacter::Idle); + character->enterActivity(GTACharacter::Idle); } } else { // Ensure the AI doesn't get stuck in idle with a target node. if(character->currentActivity == GTACharacter::Idle) { - character->changeAction(GTACharacter::Walk); + character->enterActivity(GTACharacter::Walk); } } } diff --git a/rwengine/src/ai/GTAPlayerAIController.cpp b/rwengine/src/ai/GTAPlayerAIController.cpp index aaca801e..60f364b6 100644 --- a/rwengine/src/ai/GTAPlayerAIController.cpp +++ b/rwengine/src/ai/GTAPlayerAIController.cpp @@ -65,10 +65,10 @@ void GTAPlayerAIController::update(float dt) if( character->currentActivity != GTACharacter::Jump ) { if( glm::length(direction) > 0.001f ) { - character->changeAction(running ? GTACharacter::Run : GTACharacter::Walk); + character->enterActivity(running ? GTACharacter::Run : GTACharacter::Walk); } else { - character->changeAction(GTACharacter::Idle); + character->enterActivity(GTACharacter::Idle); } if( character->getCurrentVehicle() ) { @@ -90,7 +90,6 @@ glm::vec3 GTAPlayerAIController::getTargetPosition() void GTAPlayerAIController::jump() { - character->changeAction(GTACharacter::Jump); character->jump(); } diff --git a/rwengine/src/engine/Animator.cpp b/rwengine/src/engine/Animator.cpp index c7946341..db868dfb 100644 --- a/rwengine/src/engine/Animator.cpp +++ b/rwengine/src/engine/Animator.cpp @@ -13,6 +13,7 @@ Animator::Animator() void Animator::reset() { time = 0.f; + serverTime = 0.f; lastServerTime= 0.f; if(model && animation) { @@ -56,7 +57,19 @@ void Animator::tick(float dt) glm::vec3 Animator::getRootTranslation() const { - if( model && model->rootFrameIdx != -1 ) { + // This is a pretty poor assumption. + if(model->frames[model->rootFrameIdx]->getChildren().size() > 0) { + ModelFrame* realRoot = model->frames[model->rootFrameIdx]->getChildren()[0]; + auto it = animation->bones.find(realRoot->getName()); + if(it != animation->bones.end()) { + float df = fmod(lastServerTime, this->animation->duration); + float rt = getAnimationTime(); + if(df < rt) { + auto lastKF = it->second->getInterpolatedKeyframe(df); + auto KF = it->second->getInterpolatedKeyframe(rt); + return KF.position - lastKF.position; + } + } } return glm::vec3(); @@ -73,7 +86,7 @@ glm::mat4 Animator::getFrameMatrix(ModelFrame* frame, float alpha) const if(it != animation->bones.end()) { auto kf = it->second->getInterpolatedKeyframe(getAnimationTime(alpha)); glm::mat4 m; - if(it->second->type == AnimationBone::R00) { + if(it->second->type == AnimationBone::R00 || frame->getName() == "swaist") { m = glm::translate(m, frame->getDefaultTranslation()); m = m * glm::mat4_cast(kf.rotation); } diff --git a/rwengine/src/objects/GTACharacter.cpp b/rwengine/src/objects/GTACharacter.cpp index 9df46c4d..c66485f9 100644 --- a/rwengine/src/objects/GTACharacter.cpp +++ b/rwengine/src/objects/GTACharacter.cpp @@ -11,12 +11,26 @@ GTACharacter::GTACharacter(GameWorld* engine, const glm::vec3& pos, const glm::q controller(nullptr), currentActivity(None) { mHealth = 100.f; + + // TODO move AnimationGroup creation somewhere else. + animations.idle = engine->gameData.animations["idle_stance"]; + animations.walk = engine->gameData.animations["walk_player"]; + animations.walk_start = engine->gameData.animations["walk_start"]; + animations.run = engine->gameData.animations["run_player"]; + + animations.jump_start = engine->gameData.animations["jump_launch"]; + animations.jump_glide = engine->gameData.animations["jump_glide"]; + animations.jump_land = engine->gameData.animations["jump_land"]; + + animations.car_sit = engine->gameData.animations["car_sit"]; + animations.car_sit_low = engine->gameData.animations["car_lsit"]; + if(model) { animator = new Animator(); animator->setModel(model); createActor(); - changeAction(Idle); + enterActivity(Idle); } } @@ -25,6 +39,13 @@ GTACharacter::~GTACharacter() destroyActor(); } +void GTACharacter::enterActivity(GTACharacter::Activity act) +{ + if(currentActivity != act) { + currentActivity = act; + } +} + void GTACharacter::createActor(const glm::vec3& size) { if(physCharacter) { @@ -64,49 +85,56 @@ void GTACharacter::destroyActor() } } -void GTACharacter::changeAction(Activity newAction) -{ - if(currentActivity != newAction) { - currentActivity = newAction; - if(currentVehicle == nullptr) { - switch( currentActivity ) { - default: - case Idle: - animator->setAnimation(engine->gameData.animations.at("idle_stance")); - break; - case Walk: - animator->setAnimation(engine->gameData.animations.at("walk_civi")); - break; - case Run: - animator->setAnimation(engine->gameData.animations.at("run_civi")); - break; - case KnockedDown: - // Change body shape. - position += glm::vec3(0.f, 0.f, 0.5f); - createActor(glm::vec3(0.5f, 0.5f, 0.1f)); - animator->setAnimation(engine->gameData.animations.at("kd_front"), false); - break; - case GettingUp: - // Change body shape back to normal. - createActor(); - animator->setAnimation(engine->gameData.animations.at("getup"), false); - break; - case VehicleDrive: - animator->setAnimation(engine->gameData.animations.at("car_sit")); - break; - } - } - else { - animator->setAnimation(engine->gameData.animations.at("car_sit")); - } - } -} - void GTACharacter::tick(float dt) { if(controller) { controller->update(dt); } + + if(currentVehicle) { + enterActivity(VehicleSit); + } + + switch(currentActivity) { + case Idle: { + if(animator->getAnimation() != animations.idle) { + animator->setAnimation(animations.idle); + } + } break; + case Walk: { + if(animator->getAnimation() != animations.walk) { + if(animator->getAnimation() != animations.walk_start) { + animator->setAnimation(animations.walk_start, false); + } + else if(animator->isCompleted()) { + animator->setAnimation(animations.walk); + } + } + } break; + case Run: { + if(animator->getAnimation() != animations.run) { + animator->setAnimation(animations.run); + } + } break; + case Jump: { + if(animator->getAnimation() != animations.jump_start) { + if(animator->getAnimation() != animations.jump_glide) { + animator->setAnimation(animations.jump_start, false); + } + else if(animator->isCompleted()) { + animator->setAnimation(animations.jump_glide); + } + } + } break; + case VehicleSit: { + if(animator->getAnimation() != animations.car_sit) { + animator->setAnimation(animations.car_sit); + } + } break; + default: break; + }; + + animator->tick(dt); updateCharacter(); @@ -153,7 +181,7 @@ void GTACharacter::updateCharacter() if(object->type() == Vehicle) { GTAVehicle* vehicle = static_cast(object); if(vehicle->physBody->getLinearVelocity().length() > 0.1f) { - changeAction(KnockedDown); + enterActivity(KnockedDown); } } } @@ -166,7 +194,7 @@ void GTACharacter::updateCharacter() { if(physCharacter->onGround()) { - changeAction(GTACharacter::Idle); + enterActivity(GTACharacter::Idle); } } else @@ -211,6 +239,7 @@ bool GTACharacter::enterVehicle(GTAVehicle* vehicle, size_t seat) enterVehicle(nullptr, 0); vehicle->setOccupant(seat, this); setCurrentVehicle(vehicle, seat); + enterActivity(VehicleSit); return true; } } @@ -257,7 +286,7 @@ bool GTACharacter::takeDamage(const GameObject::DamageInfo& dmg) void GTACharacter::jump() { physCharacter->jump(); - changeAction(GTACharacter::Jump); + enterActivity(GTACharacter::Jump); } void GTACharacter::resetToAINode() diff --git a/rwgame/main.cpp b/rwgame/main.cpp index 086c323a..03a8593c 100644 --- a/rwgame/main.cpp +++ b/rwgame/main.cpp @@ -148,7 +148,7 @@ std::map> Commands = { {"knock-down", [&](std::string) { for(auto it = gta->pedestrians.begin(); it != gta->pedestrians.end(); ++it) { - (*it)->changeAction(GTACharacter::KnockedDown); + (*it)->enterActivity(GTACharacter::KnockedDown); } } },