#include "Character.h" #include "Animator.h" #include "Ragdoll.h" #include "tFunctions.h" #include #include #include #include // ------------------------------------------------------- // 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 transforms = ragdoll ? ragdoll->GetBoneMatrices() : body->animator->m_CurrentAnimation ? body->GetBlendedBoneMatrices() : std::vector(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 }