#ifndef CHARACTER_CLASS #define CHARACTER_CLASS #include #include #include #include #include #include "t.h" #include "Rig.h" #include "Object.h" #include "Animator.h" class Character; class CharacterManager; class CharacterBody; class BodyComponent; class Ragdoll; class Physics; class Action { public: std::string Name; std::function FunctionPointer; }; class InputAction { public: std::string BodyComponentName; std::string ActionName; }; class BodyComponent { public: std::string name; BodyComponent* ParentComponent = nullptr; std::vector Actions; }; class CharacterBody : public t_package { public: Rig* rig = nullptr; std::vector animators; Animator* animator = nullptr; // back-compat: base animator at weight 0 std::vector components; BodyComponent* RootComponent = nullptr; CharacterBody() { animator = AddAnimator(0); animator->blendWeight = 1.0f; // base always fully visible } Animator* AddAnimator(int weight) { Animator* a = new Animator(); a->weight = weight; if (rig) a->SetRig(rig); animators.push_back(a); std::sort(animators.begin(), animators.end(), [](Animator* a, Animator* b) { return a->weight < b->weight; }); 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 GetBlendedBoneMatrices() const { // Start from the base animator std::vector 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; } void SetRig(Rig* r) { rig = r; for (Animator* a : animators) a->SetRig(r); } void SetAnimation(Animation* anim) { animator->SetAnimation(anim); animator->PlayAnimation(); 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); } BodyComponent* GetComponentOfName(std::string name); void ExecuteAction(InputAction* action); }; class CharacterMind { public: InputAction* ProduceAction(); }; class Character : public Object, public Renderable { public: CharacterMind* mind = nullptr; 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 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; void EnableRagdoll(Physics& physics); 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 inputLetters; glm::vec3 turnDirection = glm::vec3(0.0f); }; #endif