Files
dt/Character.cpp
T
2026-11-02 19:35:36 -05:00

164 lines
5.5 KiB
C++

#include "Character.h"
#include "Animator.h"
#include "Ragdoll.h"
#include "tFunctions.h"
#include <cctype>
#include <cfloat>
#include <cmath>
#include <glm/gtc/quaternion.hpp>
// -------------------------------------------------------
// BodyComponent
// -------------------------------------------------------
BodyComponent* CharacterBody::GetComponentOfName(std::string name) {
for (BodyComponent* C : components) {
if (C->name == name)
return C;
}
return nullptr;
}
void CharacterBody::ExecuteAction(InputAction* action) {
for (BodyComponent* C : components) {
if (C->name == action->BodyComponentName) {
for (Action* a : C->Actions) {
if (a->Name == action->ActionName) {
a->FunctionPointer(this, C);
break;
}
}
break;
}
}
}
// -------------------------------------------------------
// CharacterMind
// -------------------------------------------------------
InputAction* CharacterMind::ProduceAction() {
// TODO: implement
return nullptr;
}
// -------------------------------------------------------
// Character
// -------------------------------------------------------
void Character::PressInput(char letter) {
letter = (char)std::toupper((unsigned char)letter);
if (letter == 'W' || letter == 'A' || letter == 'S' || letter == 'D')
inputLetters.insert(letter);
}
void Character::ReleaseInput(char letter) {
letter = (char)std::toupper((unsigned char)letter);
inputLetters.erase(letter);
}
void Character::ClearInput() {
inputLetters.clear();
}
glm::vec3 Character::GetMovementDirection(const glm::vec3& forward, const glm::vec3& right) const {
const glm::vec3 flatForward = glm::normalize(glm::vec3(forward.x, 0.0f, forward.z));
const glm::vec3 flatRight = glm::normalize(glm::vec3(right.x, 0.0f, right.z));
glm::vec3 direction(0.0f);
if (inputLetters.count('W')) direction += flatForward;
if (inputLetters.count('S')) direction -= flatForward;
if (inputLetters.count('A')) direction += flatRight;
if (inputLetters.count('D')) direction -= flatRight;
if (glm::length(direction) > 0.0f)
direction = glm::normalize(direction);
return direction;
}
void Character::SetTurnDirection(const glm::vec3& direction) {
const glm::vec3 flatDirection(direction.x, 0.0f, direction.z);
if (glm::length(flatDirection) > 0.0001f)
turnDirection = glm::normalize(flatDirection);
}
void Character::TurnToDirection(float dt) {
if (glm::length(turnDirection) <= 0.0001f)
return;
const glm::quat target = LookAt(-turnDirection);
const glm::quat current = body->t.GetRotationQuaternion();
const float blend = 1.0f - std::exp(-turnSpeed * dt);
body->t.RotateToQuaternion(glm::normalize(glm::slerp(current, target, blend)));
}
void Character::SetWalking(bool walking) {
if (body && body->animator)
body->animator->playbackSpeed = walking ? 1.0f : 0.0f;
}
glm::vec3 Character::GetForwardDirection() const {
if (!body)
return glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 forward = -body->t.GetFrontVector();
forward.y = 0.0f;
const float length = glm::length(forward);
if (length <= 0.0001f)
return glm::vec3(0.0f, 0.0f, -1.0f);
return forward / length;
}
void Character::EnableRagdoll(Physics& physics) {
if (!ragdoll)
ragdoll = new Ragdoll(*body, GetRigMatrix(), physics);
}
glm::mat4 Character::GetRigMatrix() const {
// body->t's scale is the hitbox only; the rig is drawn at visualScale so it fills that hitbox
return body->t.GetTranslationMatrix() * body->t.GetRotationMatrix()
* glm::scale(glm::mat4(1.0f), glm::vec3(visualScale));
}
bool Character::GetWorldBounds(BoundingBox& bounds) const {
if (!body)
return false;
if (ragdoll && !ragdoll->GetParts().empty()) {
bounds.min = glm::vec3(FLT_MAX);
bounds.max = glm::vec3(-FLT_MAX);
for (const Ragdoll::Part* part : ragdoll->GetParts()) {
const BoundingBox partBounds = part->transform.t.GetAABB();
bounds.min = glm::min(bounds.min, partBounds.min);
bounds.max = glm::max(bounds.max, partBounds.max);
}
return true;
}
// The rig is about 2 units across natively in every direction it reaches, arms out included.
// A cube that size around the body covers any pose and any turn.
const glm::vec3 reach(visualScale);
bounds.min = body->t.GetTranslation() - reach;
bounds.max = body->t.GetTranslation() + reach;
return true;
}
Character::Character() {
body = new CharacterBody();
mind = nullptr;
shadertype = ShaderType::RigShader;
}
void Character::Render(Shader& shader) {
if (!body || !body->rig || !body->animator) return;
const std::vector<glm::mat4> transforms = ragdoll ? ragdoll->GetBoneMatrices()
: body->animator->m_CurrentAnimation ? body->GetBlendedBoneMatrices()
: std::vector<glm::mat4>(100, glm::mat4(1.0f));
shader.SetMat4Array("finalBonesMatrices", transforms.data(), glm::min((int)transforms.size(), 100));
shader.Set4F("colorOverride", colorOverride);
surface.Apply(shader, 1, glm::vec3(visualScale));
shader.Set1F("metallic", metallic);
shader.Set1F("roughness", roughness);
shader.Set1F("ao", ao);
body->rig->Render(shader, ragdoll ? glm::mat4(1.0f) : GetRigMatrix()); // ragdoll bones are already in world space
shader.Set4F("colorOverride", glm::vec4(0.0f)); // don't leak into other rig draws
}