base
This commit is contained in:
+81
-80
@@ -2,49 +2,37 @@
|
||||
#define CHARACTER_CLASS
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include "t.h"
|
||||
#include "Rig.h"
|
||||
#include "Object.h"
|
||||
#include "Animator.h"
|
||||
#include "CharacterAction.h"
|
||||
|
||||
class Character;
|
||||
class CharacterManager;
|
||||
class CharacterBody;
|
||||
class BodyComponent;
|
||||
class CharacterMind;
|
||||
class Ragdoll;
|
||||
class Physics;
|
||||
class p;
|
||||
|
||||
class Action {
|
||||
public:
|
||||
std::string Name;
|
||||
std::function<void(CharacterBody*, BodyComponent*)> FunctionPointer;
|
||||
};
|
||||
|
||||
class InputAction {
|
||||
public:
|
||||
std::string BodyComponentName;
|
||||
std::string ActionName;
|
||||
};
|
||||
|
||||
class BodyComponent {
|
||||
public:
|
||||
std::string name;
|
||||
BodyComponent* ParentComponent = nullptr;
|
||||
std::vector<Action*> Actions;
|
||||
};
|
||||
|
||||
// The physical side of a character: where it is, its physics body, its animation,
|
||||
// and carrying out whatever actions its mind sends it.
|
||||
class CharacterBody : public t_package {
|
||||
public:
|
||||
Rig* rig = nullptr;
|
||||
p* physics = nullptr; // made by Character::CreatePhysicsBody, its box is t's scale
|
||||
|
||||
std::vector<Animator*> animators;
|
||||
Animator* animator = nullptr; // back-compat: base animator at weight 0
|
||||
Animator* animator = nullptr; // the base layer, always fully on: standing idle
|
||||
Animator* walkAnimator = nullptr; // on top of the idle, faded in while walking
|
||||
Animator* actionAnimator = nullptr; // one-shots on top of both, see PlayAction
|
||||
|
||||
std::vector<BodyComponent*> components;
|
||||
BodyComponent* RootComponent = nullptr;
|
||||
float walkSpeed = 2.34f; // m/s on flat ground
|
||||
float turnSpeed = 12.0f; // how quickly it turns to face where it's walking
|
||||
float walkFadeSpeed = 8.0f; // how quickly it fades between idle and walking
|
||||
|
||||
CharacterBody() {
|
||||
animator = AddAnimator(0);
|
||||
@@ -61,31 +49,11 @@ public:
|
||||
return a;
|
||||
}
|
||||
|
||||
// Returns weighted blend of all playing animators.
|
||||
// Base (weight 0) is always the foundation; higher-weight animators
|
||||
// blend in on top based on their blendWeight (0?1).
|
||||
std::vector<glm::mat4> GetBlendedBoneMatrices() const {
|
||||
// Start from the base animator
|
||||
std::vector<glm::mat4> result = animator->GetFinalBoneMatrices();
|
||||
const int count = (int)result.size();
|
||||
|
||||
// Blend each higher-priority playing animator on top
|
||||
for (int i = 1; i < (int)animators.size(); i++) {
|
||||
Animator* a = animators[i];
|
||||
if (!a->isPlaying || !a->m_CurrentAnimation || a->blendWeight <= 0.0f) continue;
|
||||
const auto& matrices = a->GetFinalBoneMatrices();
|
||||
for (int j = 0; j < count && j < (int)matrices.size(); j++)
|
||||
result[j] = glm::mix(result[j], matrices[j], a->blendWeight);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns the highest-weight playing animator (for back-compat)
|
||||
Animator* GetActiveAnimator() const {
|
||||
for (int i = (int)animators.size() - 1; i >= 0; i--)
|
||||
if (animators[i]->isPlaying && animators[i]->m_CurrentAnimation)
|
||||
return animators[i];
|
||||
return animator;
|
||||
// The skinning matrices for this frame, from every animator layered together. Worked out in Step.
|
||||
// Base (weight 0) is the foundation; each higher-weight animator blends in by its blendWeight,
|
||||
// but only on the bones its animation has tracks for, so an arm-only clip leaves the legs walking.
|
||||
const std::vector<glm::mat4>& GetBlendedBoneMatrices() const {
|
||||
return blendedBones.empty() ? animator->GetFinalBoneMatrices() : blendedBones;
|
||||
}
|
||||
|
||||
void SetRig(Rig* r) {
|
||||
@@ -97,64 +65,97 @@ public:
|
||||
void SetAnimation(Animation* anim) {
|
||||
animator->SetAnimation(anim);
|
||||
animator->PlayAnimation();
|
||||
animator->blendWeight = 1.0f; // base never blends � always instant
|
||||
animator->blendWeight = 1.0f; // base never blends, always instant
|
||||
}
|
||||
|
||||
void LoadAnimation(Animation* anim) { SetAnimation(anim); }
|
||||
|
||||
void Step(float dt) {
|
||||
for (Animator* a : animators)
|
||||
a->Step(dt);
|
||||
// Plays an animation once on top of everything else. Starts over if it's already playing.
|
||||
void PlayAction(Animation* anim) {
|
||||
if (!anim)
|
||||
return;
|
||||
if (!actionAnimator) {
|
||||
actionAnimator = AddAnimator(2);
|
||||
actionAnimator->blendDuration = 0.1f;
|
||||
}
|
||||
actionAnimator->loops = false;
|
||||
actionAnimator->SetAnimation(anim);
|
||||
actionAnimator->PlayAnimation();
|
||||
}
|
||||
|
||||
BodyComponent* GetComponentOfName(std::string name);
|
||||
void ExecuteAction(InputAction* action);
|
||||
};
|
||||
// The loop played while walking. It keeps running underneath so it picks up mid-stride.
|
||||
void SetWalkAnimation(Animation* anim) {
|
||||
if (!walkAnimator)
|
||||
walkAnimator = AddAnimator(1);
|
||||
walkAnimator->loops = true;
|
||||
walkAnimator->SetAnimation(anim);
|
||||
walkAnimator->PlayAnimation();
|
||||
walkAnimator->blendWeight = walkBlend;
|
||||
}
|
||||
|
||||
class CharacterMind {
|
||||
public:
|
||||
InputAction* ProduceAction();
|
||||
// Carries out one action. Movement actions only count for the frame they arrive in.
|
||||
void Perform(const CharacterAction& action);
|
||||
|
||||
// Pushes the physics body toward walking speed along wherever it faces, from this frame's Move action.
|
||||
// speedFactor scales it, 0 stops it.
|
||||
void ApplyMovement(bool grounded, float speedFactor, float dt, float referenceDt);
|
||||
|
||||
// Turning and animation
|
||||
void Step(float dt);
|
||||
|
||||
void SetWalking(bool walking);
|
||||
glm::vec3 GetForwardDirection() const;
|
||||
|
||||
private:
|
||||
glm::vec3 wishDirection = glm::vec3(0.0f); // from this frame's Move action
|
||||
glm::vec3 turnDirection = glm::vec3(0.0f); // where it's turning to face, kept after it stops
|
||||
bool walking = false;
|
||||
float walkBlend = 0.0f; // 0 = idle, 1 = walking
|
||||
std::vector<glm::mat4> blendedBones;
|
||||
|
||||
// Per (base clip, layer clip): bone name -> what turns the layer's track into one relative to the bone's
|
||||
// real parent. Only bones whose parent differs between the two files have an entry.
|
||||
std::map<std::pair<const Animation*, const Animation*>, std::unordered_map<std::string, glm::mat4>> parentCorrections;
|
||||
|
||||
void BlendBones(const AssimpNodeData& node, const glm::mat4& parentTransform);
|
||||
const std::unordered_map<std::string, glm::mat4>& GetParentCorrections(const Animation* layer);
|
||||
};
|
||||
|
||||
class Character : public Object, public Renderable {
|
||||
public:
|
||||
CharacterMind* mind = nullptr;
|
||||
CharacterMind* mind = nullptr; // what decides its actions, nullptr does nothing
|
||||
CharacterBody* body = nullptr;
|
||||
Ragdoll* ragdoll = nullptr; // when set, physics drives the rig instead of the animators
|
||||
SurfaceTexture surface;
|
||||
glm::vec4 colorOverride = glm::vec4(0.0f); // rgb + strength (0 = keep the rig's own texture)
|
||||
float visualScale = 1.4f; // rig is ~1.8 tall natively; hitbox is 2.5
|
||||
float visualScale = 1.4f; // rig is ~1.8 tall natively; its physics box is 2.5
|
||||
float metallic = 0.0f;
|
||||
float roughness = 0.7f;
|
||||
float ao = 1.0f;
|
||||
float turnSpeed = 12.0f;
|
||||
|
||||
void PressInput(char letter);
|
||||
void ReleaseInput(char letter);
|
||||
void ClearInput();
|
||||
glm::vec3 GetMovementDirection(const glm::vec3& forward, const glm::vec3& right) const;
|
||||
void SetTurnDirection(const glm::vec3& direction);
|
||||
const glm::vec3& GetTurnDirection() const { return turnDirection; }
|
||||
void SetWalking(bool walking);
|
||||
glm::vec3 GetForwardDirection() const;
|
||||
// Asks the mind for this frame's actions and hands them to the body
|
||||
void Think();
|
||||
|
||||
glm::vec3 GetForwardDirection() const { return body->GetForwardDirection(); }
|
||||
void EnableRagdoll(Physics& physics);
|
||||
|
||||
// The box physics moves the character around with, sized by body->t's scale
|
||||
void CreatePhysicsBody(Physics& physics, float massKg);
|
||||
|
||||
// Its physics body's group, so it never collides with anything else of its own
|
||||
int GetCollisionGroup();
|
||||
|
||||
Character();
|
||||
void Render(Shader& shader) override;
|
||||
bool GetWorldBounds(BoundingBox& bounds) const override;
|
||||
|
||||
void Step(float dt) {
|
||||
if (ragdoll) return;
|
||||
TurnToDirection(dt);
|
||||
body->Step(dt);
|
||||
}
|
||||
|
||||
private:
|
||||
void TurnToDirection(float dt);
|
||||
glm::mat4 GetRigMatrix() const;
|
||||
|
||||
std::set<char> inputLetters;
|
||||
glm::vec3 turnDirection = glm::vec3(0.0f);
|
||||
int collisionGroup = 0;
|
||||
std::vector<CharacterAction> pendingActions;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user