More work on states, added bot

This commit is contained in:
Vladislav Khorev committed 2026-10-29 22:37:30 +06:00
1 parent 66e93d46f4
commit 5e9a663812
25 files changed
+1595 -740

No files matched your search

+235 -138
View File
@@ -1,4 +1,4 @@
#include "Character.h"
#include "Character.h"
#include "render/TextRenderer.h"
#include <cmath>
#include <iostream>
@@ -19,6 +19,44 @@ namespace FRG {
Dialogue::TranslationDatabase Character::nameTranslations;
std::shared_ptr<const CharacterState> Character::stateSnapshot() const {
auto snapshot = gameServer_.snapshot();
if (!snapshot) return {};
auto player = snapshot->players.find(entityId_);
if (player != snapshot->players.end()) {
return std::shared_ptr<const CharacterState>(
std::move(snapshot), &player->second.state);
}
auto location = snapshot->locations.find(locationId_);
if (location == snapshot->locations.end()) return {};
auto npc = location->second.npcs.find(entityId_);
if (npc == location->second.npcs.end()) return {};
return std::shared_ptr<const CharacterState>(
std::move(snapshot), &npc->second.state);
}
bool Character::isMoving() const {
auto state = stateSnapshot();
return state && state->isMoving();
}
Eigen::Vector3f Character::getRequestedWalkTarget() const {
auto state = stateSnapshot();
return state ? state->getRequestedWalkTarget() : Eigen::Vector3f::Zero();
}
Eigen::Vector3f Character::getCurrentNavigationTarget() const {
auto state = stateSnapshot();
return state ? state->getCurrentNavigationTarget() : Eigen::Vector3f::Zero();
}
float Character::getHp() const {
auto state = stateSnapshot();
return state ? state->getHp() : 0.f;
}
void Character::loadAnimation(AnimationState animState, const std::string& filename, const std::string& zipFile) {
auto& data = animations[animState];
data.model.LoadFromFile(filename, zipFile);
@@ -46,7 +84,7 @@ void Character::loadBinaryAnimation(AnimationState animState, const std::string&
std::unique_ptr<Character> Character::createFromState(const CharacterState& st, IGameServer& server,
const std::string& locationId, const std::string& entityId,
Renderer& renderer, const char* zipFile) {
auto c = std::make_unique<Character>(st, server, locationId, entityId);
auto c = std::make_unique<Character>(server, locationId, entityId);
const std::string zip = zipFile ? zipFile : "";
@@ -62,7 +100,7 @@ std::unique_ptr<Character> Character::createFromState(const CharacterState& st,
}
};
for (const auto& [animState, path] : c->characterState.creationInfo.animationPaths)
for (const auto& [animState, path] : st.creationInfo.animationPaths)
loadAnim(animState, path);
auto loadTex = [&](const std::string& path) -> std::shared_ptr<Texture> {
@@ -76,27 +114,27 @@ std::unique_ptr<Character> Character::createFromState(const CharacterState& st,
}
};
if (!c->characterState.creationInfo.singleTexturePath.empty()) {
auto tex = loadTex(c->characterState.creationInfo.singleTexturePath);
if (!st.creationInfo.singleTexturePath.empty()) {
auto tex = loadTex(st.creationInfo.singleTexturePath);
if (tex) c->setTexture(tex);
}
for (const auto& [meshName, texPath] : c->characterState.creationInfo.meshTexturePaths) {
for (const auto& [meshName, texPath] : st.creationInfo.meshTexturePaths) {
auto tex = loadTex(texPath);
if (tex) c->setTexture(meshName, tex);
}
if (!c->characterState.creationInfo.weaponMeshPath.empty()) {
const std::string& wp = c->characterState.creationInfo.weaponMeshPath;
if (!st.creationInfo.weaponMeshPath.empty()) {
const std::string& wp = st.creationInfo.weaponMeshPath;
try {
if (wp.size() >= 4 && wp.substr(wp.size() - 4) == ".bin")
c->weaponMesh.data = LoadModelFromBinFile(wp, zip);
else
c->weaponMesh.data = LoadFromTextFile02(wp, zip);
if (c->characterState.creationInfo.weaponMeshMove != Eigen::Vector3f::Zero())
c->weaponMesh.data.Move(c->characterState.creationInfo.weaponMeshMove);
if (c->characterState.creationInfo.weaponMeshScale != 1.0f)
c->weaponMesh.data.Scale(c->characterState.creationInfo.weaponMeshScale);
if (st.creationInfo.weaponMeshMove != Eigen::Vector3f::Zero())
c->weaponMesh.data.Move(st.creationInfo.weaponMeshMove);
if (st.creationInfo.weaponMeshScale != 1.0f)
c->weaponMesh.data.Scale(st.creationInfo.weaponMeshScale);
c->weaponMesh.RefreshVBO();
} catch (const std::exception& e) {
@@ -104,10 +142,10 @@ std::unique_ptr<Character> Character::createFromState(const CharacterState& st,
}
}
c->weaponTexture = loadTex(c->characterState.creationInfo.weaponTexturePath);
c->weaponAttachBoneName = c->characterState.creationInfo.weaponAttachBoneName;
c->weaponInitialPosition = c->characterState.creationInfo.weaponInitialPosition;
c->weaponInitialRotation = c->characterState.creationInfo.weaponInitialRotation.toRotationMatrix();
c->weaponTexture = loadTex(st.creationInfo.weaponTexturePath);
c->weaponAttachBoneName = st.creationInfo.weaponAttachBoneName;
c->weaponInitialPosition = st.creationInfo.weaponInitialPosition;
c->weaponInitialRotation = st.creationInfo.weaponInitialRotation.toRotationMatrix();
return c;
}
@@ -124,8 +162,8 @@ void Character::setTexture(const std::string& meshName, std::shared_ptr<Texture>
meshTextures[meshName] = std::move(tex);
}
AnimationState Character::resolveActiveState() const {
if (animations.count(characterState.currentState)) return characterState.currentState;
AnimationState Character::resolveActiveState(const CharacterState& state) const {
if (animations.count(state.currentState)) return state.currentState;
return AnimationState::STAND;
}
@@ -134,29 +172,33 @@ void Character::update(int64_t deltaMs, const CharacterUpdateEvents* events) {
hitSparkEmitter.update(static_cast<float>(deltaMs));
}
auto stateSnapshot = this->stateSnapshot();
if (!stateSnapshot) return;
const CharacterState& state = *stateSnapshot;
if (events) {
if (events->deathAnimationComplete && onDeathAnimComplete)
onDeathAnimComplete();
if (events->attackImpact && audioPlayer) {
audioPlayer->playSoundAsync(characterState.isPlayer
audioPlayer->playSoundAsync(state.isPlayer
? "resources/audio/547041__cogfirestudios__hit-swing-sword-small-3.ogg"
: "resources/audio/13925__adcbicycle__21.ogg");
}
}
if (!characterState.enabled) return;
auto it = animations.find(characterState.currentState);
if (!state.enabled) return;
auto it = animations.find(state.currentState);
if (it == animations.end()) return;
auto& anim = it->second;
anim.currentFrame = (std::min)(
characterState.animationFrame, static_cast<float>((std::max)(0, anim.totalFrames - 1)));
state.animationFrame, static_cast<float>((std::max)(0, anim.totalFrames - 1)));
if (static_cast<int>(anim.currentFrame) != anim.lastFrame) {
anim.gpuSkinningShaderData.ComputeSkinningMatrices(
anim.model.startBones, anim.model.animations[0].keyFrames,
static_cast<int>(anim.currentFrame));
if (!characterState.useGpuSkinning) {
if (!state.useGpuSkinning) {
anim.model.Interpolate(static_cast<int>(anim.currentFrame));
}
anim.lastFrame = static_cast<int>(anim.currentFrame);
@@ -171,14 +213,17 @@ void Character::draw(Renderer& renderer) {
renderer.RenderUniform1f("uAlpha", 1.0f);
glDisable(GL_BLEND);
if (!characterState.enabled) return;
auto stateSnapshot = this->stateSnapshot();
if (!stateSnapshot) return;
const CharacterState& state = *stateSnapshot;
if (!state.enabled) return;
if (characterState.useGpuSkinning) {
drawGpuSkinning(renderer);
if (state.useGpuSkinning) {
drawGpuSkinning(renderer, state);
return;
}
AnimationState drawState = resolveActiveState();
AnimationState drawState = resolveActiveState(state);
auto it = animations.find(drawState);
if (it == animations.end()) return;
@@ -192,10 +237,10 @@ void Character::draw(Renderer& renderer) {
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
renderer.PushMatrix();
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(characterState.modelScale);
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(state.modelScale);
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
auto& anim = it->second;
for (const auto& name : anim.model.meshNamesOrdered) {
@@ -211,14 +256,14 @@ void Character::draw(Renderer& renderer) {
renderer.PopMatrix();
drawAttachedWeapon(renderer);
drawAttachedWeapon(renderer, state);
renderer.PopProjectionMatrix();
renderer.shaderManager.PopShader();
}
void Character::drawGpuSkinning(Renderer& renderer) {
AnimationState drawState = resolveActiveState();
void Character::drawGpuSkinning(Renderer& renderer, const CharacterState& state) {
AnimationState drawState = resolveActiveState(state);
auto it = animations.find(drawState);
if (it == animations.end()) return;
@@ -246,10 +291,10 @@ void Character::drawGpuSkinning(Renderer& renderer) {
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
renderer.PushMatrix();
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(characterState.modelScale);
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(state.modelScale);
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
renderer.RenderUniformMatrix4fvArray(boneMatricesUniform,
static_cast<int>(anim.gpuSkinningShaderData.skinningMatrices.size()), false,
@@ -269,15 +314,15 @@ void Character::drawGpuSkinning(Renderer& renderer) {
renderer.shaderManager.PushShader("fog");
renderer.RenderUniform1i(textureUniformName, 0);
renderer.RenderUniform3fv("uPlayerEyePos", playerEyePosSkin);
drawAttachedWeapon(renderer);
drawAttachedWeapon(renderer, state);
renderer.shaderManager.PopShader();
renderer.PopProjectionMatrix();
renderer.shaderManager.PopShader();
}
bool Character::prepareGpuSkinning() {
AnimationState drawState = resolveActiveState();
bool Character::prepareGpuSkinning(const CharacterState& state) {
AnimationState drawState = resolveActiveState(state);
auto it = animations.find(drawState);
if (it == animations.end()) return false;
@@ -293,11 +338,11 @@ bool Character::prepareGpuSkinning() {
return true;
}
void Character::drawAttachedWeapon(Renderer& renderer) {
if (!characterState.showWeapon) return;
void Character::drawAttachedWeapon(Renderer& renderer, const CharacterState& state) {
if (!state.showWeapon) return;
if (weaponMesh.data.PositionData.size() == 0 || !weaponTexture) return;
auto it = animations.find(resolveActiveState());
auto it = animations.find(resolveActiveState(state));
if (it == animations.end()) return;
auto& anim = it->second;
@@ -310,10 +355,10 @@ void Character::drawAttachedWeapon(Renderer& renderer) {
Eigen::Matrix4f currentBone = anim.gpuSkinningShaderData.skinningMatrices[boneIdx] * bindBone;
renderer.PushMatrix();
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(characterState.modelScale);
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(state.modelScale);
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
renderer.TranslateMatrix(Eigen::Vector3f(currentBone.block<3, 1>(0, 3)));
renderer.RotateMatrix(Eigen::Matrix3f(currentBone.block<3, 3>(0, 0)));
renderer.TranslateMatrix(weaponInitialPosition);
@@ -327,24 +372,27 @@ void Character::drawAttachedWeapon(Renderer& renderer) {
// ==================== Shadow depth pass ====================
void Character::drawShadowDepth(Renderer& renderer) {
if (!characterState.enabled) return;
if (characterState.useGpuSkinning) {
drawShadowDepthGpuSkinning(renderer);
auto stateSnapshot = this->stateSnapshot();
if (!stateSnapshot) return;
const CharacterState& state = *stateSnapshot;
if (!state.enabled) return;
if (state.useGpuSkinning) {
drawShadowDepthGpuSkinning(renderer, state);
} else {
drawShadowDepthCpu(renderer);
drawShadowDepthCpu(renderer, state);
}
}
void Character::drawShadowDepthCpu(Renderer& renderer) {
AnimationState drawState = resolveActiveState();
void Character::drawShadowDepthCpu(Renderer& renderer, const CharacterState& state) {
AnimationState drawState = resolveActiveState(state);
auto it = animations.find(drawState);
if (it == animations.end()) return;
renderer.PushMatrix();
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(characterState.modelScale);
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(state.modelScale);
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
auto& anim = it->second;
for (const auto& name : anim.model.meshNamesOrdered) {
@@ -358,17 +406,17 @@ void Character::drawShadowDepthCpu(Renderer& renderer) {
}
renderer.PopMatrix();
drawAttachedWeapon(renderer);
drawAttachedWeapon(renderer, state);
}
void Character::drawShadowDepthGpuSkinning(Renderer& renderer) {
void Character::drawShadowDepthGpuSkinning(Renderer& renderer, const CharacterState& state) {
CheckGlError(__FILE__, __LINE__);
AnimationState drawState = resolveActiveState();
AnimationState drawState = resolveActiveState(state);
auto it = animations.find(drawState);
if (it == animations.end()) return;
CheckGlError(__FILE__, __LINE__);
if (!prepareGpuSkinning()) return;
if (!prepareGpuSkinning(state)) return;
CheckGlError(__FILE__, __LINE__);
static const std::string shadowSkinningShader = "shadow_depth_skinning";
@@ -377,10 +425,10 @@ void Character::drawShadowDepthGpuSkinning(Renderer& renderer) {
renderer.shaderManager.PushShader(shadowSkinningShader);
renderer.PushMatrix();
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(characterState.modelScale);
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(state.modelScale);
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
CheckGlError(__FILE__, __LINE__);
renderer.RenderUniformMatrix4fvArray(boneMatricesUniform,
@@ -400,14 +448,17 @@ void Character::drawShadowDepthGpuSkinning(Renderer& renderer) {
renderer.PopMatrix();
renderer.shaderManager.PopShader();
drawAttachedWeapon(renderer);
drawAttachedWeapon(renderer, state);
CheckGlError(__FILE__, __LINE__);
}
// ==================== Main pass with shadows ====================
void Character::drawWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera) {
if (!characterState.enabled) return;
auto stateSnapshot = this->stateSnapshot();
if (!stateSnapshot) return;
const CharacterState& state = *stateSnapshot;
if (!state.enabled) return;
if (hitSparkEmitter.isConfigured() && hitSparkEmitter.getActiveParticleCount() > 0) {
hitSparkEmitter.draw(renderer, 1.0f, Environment::width, Environment::height, false);
}
@@ -415,15 +466,15 @@ void Character::drawWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightF
renderer.RenderUniform1f("uAlpha", 1.0f);
glDisable(GL_BLEND);
if (characterState.useGpuSkinning) {
drawGpuSkinningWithShadow(renderer, lightFromCamera, shadowMapTex, lightDirCamera);
if (state.useGpuSkinning) {
drawGpuSkinningWithShadow(renderer, state, lightFromCamera, shadowMapTex, lightDirCamera);
} else {
drawCpuWithShadow(renderer, lightFromCamera, shadowMapTex, lightDirCamera);
drawCpuWithShadow(renderer, state, lightFromCamera, shadowMapTex, lightDirCamera);
}
}
void Character::drawCpuWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera) {
AnimationState drawState = resolveActiveState();
void Character::drawCpuWithShadow(Renderer& renderer, const CharacterState& state, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera) {
AnimationState drawState = resolveActiveState(state);
auto it = animations.find(drawState);
if (it == animations.end()) return;
@@ -446,10 +497,10 @@ void Character::drawCpuWithShadow(Renderer& renderer, const Eigen::Matrix4f& lig
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
renderer.PushMatrix();
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(characterState.modelScale);
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(state.modelScale);
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
auto& anim = it->second;
for (const auto& name : anim.model.meshNamesOrdered) {
@@ -468,20 +519,20 @@ void Character::drawCpuWithShadow(Renderer& renderer, const Eigen::Matrix4f& lig
renderer.shaderManager.PushShader("fog");
renderer.RenderUniform1i(textureUniformName, 0);
renderer.RenderUniform3fv("uPlayerEyePos", playerEyePosShadow);
drawAttachedWeapon(renderer);
drawAttachedWeapon(renderer, state);
renderer.shaderManager.PopShader();
renderer.PopProjectionMatrix();
renderer.shaderManager.PopShader();
}
void Character::drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera) {
AnimationState drawState = resolveActiveState();
void Character::drawGpuSkinningWithShadow(Renderer& renderer, const CharacterState& state, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera) {
AnimationState drawState = resolveActiveState(state);
auto it = animations.find(drawState);
if (it == animations.end()) return;
CheckGlError(__FILE__, __LINE__);
if (!prepareGpuSkinning()) return;
if (!prepareGpuSkinning(state)) return;
CheckGlError(__FILE__, __LINE__);
static const std::string skinningShadowShader = "fog_skinning_shadow";
@@ -508,10 +559,10 @@ void Character::drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matri
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
renderer.PushMatrix();
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(characterState.modelScale);
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(state.modelScale);
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
CheckGlError(__FILE__, __LINE__);
renderer.RenderUniformMatrix4fvArray(boneMatricesUniform,
@@ -534,7 +585,7 @@ void Character::drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matri
renderer.shaderManager.PushShader("fog");
renderer.RenderUniform1i(textureUniformName, 0);
renderer.RenderUniform3fv("uPlayerEyePos", playerEyePosSkinShadow);
drawAttachedWeapon(renderer);
drawAttachedWeapon(renderer, state);
renderer.shaderManager.PopShader();
CheckGlError(__FILE__, __LINE__);
@@ -554,7 +605,10 @@ void Character::drawNight(Renderer& renderer,
const Eigen::Matrix4f& cameraViewInverse,
const float* ambientColor, const float* fogColor)
{
if (!characterState.enabled) return;
auto stateSnapshot = this->stateSnapshot();
if (!stateSnapshot) return;
const CharacterState& state = *stateSnapshot;
if (!state.enabled) return;
if (hitSparkEmitter.isConfigured() && hitSparkEmitter.getActiveParticleCount() > 0) {
hitSparkEmitter.draw(renderer, 1.0f, Environment::width, Environment::height, false);
}
@@ -562,25 +616,26 @@ void Character::drawNight(Renderer& renderer,
renderer.RenderUniform1f("uAlpha", 1.0f);
glDisable(GL_BLEND);
if (characterState.useGpuSkinning) {
drawNightGpuSkinning(renderer, pointLightPosEye, pointLightDirEye, pointLightColors, pointLightCount,
if (state.useGpuSkinning) {
drawNightGpuSkinning(renderer, state, pointLightPosEye, pointLightDirEye, pointLightColors, pointLightCount,
pointLightWorldXZ, pointLightLimitXZ, cameraViewInverse, ambientColor, fogColor);
} else {
drawNightCpu(renderer, pointLightPosEye, pointLightDirEye, pointLightColors, pointLightCount,
drawNightCpu(renderer, state, pointLightPosEye, pointLightDirEye, pointLightColors, pointLightCount,
pointLightWorldXZ, pointLightLimitXZ, cameraViewInverse, ambientColor, fogColor);
}
}
void Character::drawNightGpuSkinning(Renderer& renderer,
const CharacterState& state,
const float* plPosEye, const float* plDirEye, const float* plColors, int plCount,
const float* plWorldXZ, const float* plLimitXZ, const Eigen::Matrix4f& cameraViewInverse,
const float* ambientColor, const float* fogColor)
{
AnimationState drawState = resolveActiveState();
AnimationState drawState = resolveActiveState(state);
auto it = animations.find(drawState);
if (it == animations.end()) return;
if (!prepareGpuSkinning()) return;
if (!prepareGpuSkinning(state)) return;
static const std::string nightSkinningShader = "night_fog_skinning";
static const std::string boneMatricesUniform = "uBoneMatrices[0]";
@@ -606,10 +661,10 @@ void Character::drawNightGpuSkinning(Renderer& renderer,
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
renderer.PushMatrix();
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(characterState.modelScale);
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(state.modelScale);
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
renderer.RenderUniformMatrix4fvArray(boneMatricesUniform,
static_cast<int>(it->second.gpuSkinningShaderData.skinningMatrices.size()), false,
@@ -639,7 +694,7 @@ void Character::drawNightGpuSkinning(Renderer& renderer,
renderer.RenderUniform2fvArray("uPointLightWorldXZ[0]", plCount, plWorldXZ);
renderer.RenderUniform2fvArray("uPointLightLimitXZ[0]", plCount, plLimitXZ);
}
drawAttachedWeapon(renderer);
drawAttachedWeapon(renderer, state);
renderer.shaderManager.PopShader();
renderer.PopProjectionMatrix();
@@ -647,11 +702,12 @@ void Character::drawNightGpuSkinning(Renderer& renderer,
}
void Character::drawNightCpu(Renderer& renderer,
const CharacterState& state,
const float* plPosEye, const float* plDirEye, const float* plColors, int plCount,
const float* plWorldXZ, const float* plLimitXZ, const Eigen::Matrix4f& cameraViewInverse,
const float* ambientColor, const float* fogColor)
{
AnimationState drawState = resolveActiveState();
AnimationState drawState = resolveActiveState(state);
auto it = animations.find(drawState);
if (it == animations.end()) return;
@@ -677,10 +733,10 @@ void Character::drawNightCpu(Renderer& renderer,
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
renderer.PushMatrix();
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(characterState.modelScale);
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(state.modelScale);
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
auto& anim = it->second;
for (const auto& name : anim.model.meshNamesOrdered) {
@@ -697,7 +753,7 @@ void Character::drawNightCpu(Renderer& renderer,
renderer.PopMatrix();
renderer.RenderUniform3fv("uPlayerEyePos", playerEyePosNight);
drawAttachedWeapon(renderer);
drawAttachedWeapon(renderer, state);
renderer.PopProjectionMatrix();
renderer.shaderManager.PopShader();
@@ -712,35 +768,39 @@ void Character::drawNightWithShadow(Renderer& renderer,
const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex,
const float* ambientColor, const float* fogColor)
{
if (!characterState.enabled) return;
auto stateSnapshot = this->stateSnapshot();
if (!stateSnapshot) return;
const CharacterState& state = *stateSnapshot;
if (!state.enabled) return;
if (hitSparkEmitter.isConfigured() && hitSparkEmitter.getActiveParticleCount() > 0) {
hitSparkEmitter.draw(renderer, 1.0f, Environment::width, Environment::height, false);
}
renderer.RenderUniform1f("uAlpha", 1.0f);
glDisable(GL_BLEND);
if (characterState.useGpuSkinning) {
drawNightGpuSkinningWithShadow(renderer, plPosEye, plDirEye, plColors, plCount,
if (state.useGpuSkinning) {
drawNightGpuSkinningWithShadow(renderer, state, plPosEye, plDirEye, plColors, plCount,
plWorldXZ, plLimitXZ, cameraViewInverse,
lightFromCamera, shadowMapTex, ambientColor, fogColor);
} else {
drawNightCpuWithShadow(renderer, plPosEye, plDirEye, plColors, plCount,
drawNightCpuWithShadow(renderer, state, plPosEye, plDirEye, plColors, plCount,
plWorldXZ, plLimitXZ, cameraViewInverse,
lightFromCamera, shadowMapTex, ambientColor, fogColor);
}
}
void Character::drawNightGpuSkinningWithShadow(Renderer& renderer,
const CharacterState& state,
const float* plPosEye, const float* plDirEye, const float* plColors, int plCount,
const float* plWorldXZ, const float* plLimitXZ,
const Eigen::Matrix4f& cameraViewInverse,
const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex,
const float* ambientColor, const float* fogColor)
{
AnimationState drawState = resolveActiveState();
AnimationState drawState = resolveActiveState(state);
auto it = animations.find(drawState);
if (it == animations.end()) return;
if (!prepareGpuSkinning()) return;
if (!prepareGpuSkinning(state)) return;
static const std::string nightShadowSkinningShader = "night_fog_skinning_shadow";
static const std::string boneMatricesUniform = "uBoneMatrices[0]";
@@ -774,10 +834,10 @@ void Character::drawNightGpuSkinningWithShadow(Renderer& renderer,
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
renderer.PushMatrix();
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(characterState.modelScale);
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(state.modelScale);
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
renderer.RenderUniformMatrix4fvArray(boneMatricesUniform,
static_cast<int>(it->second.gpuSkinningShaderData.skinningMatrices.size()), false,
@@ -810,7 +870,7 @@ void Character::drawNightGpuSkinningWithShadow(Renderer& renderer,
}
renderer.RenderUniform2fvArray("uPointLightWorldXZ[0]", plCount, plWorldXZ);
renderer.RenderUniform2fvArray("uPointLightLimitXZ[0]", plCount, plLimitXZ);
drawAttachedWeapon(renderer);
drawAttachedWeapon(renderer, state);
renderer.shaderManager.PopShader();
renderer.PopProjectionMatrix();
@@ -818,13 +878,14 @@ void Character::drawNightGpuSkinningWithShadow(Renderer& renderer,
}
void Character::drawNightCpuWithShadow(Renderer& renderer,
const CharacterState& state,
const float* plPosEye, const float* plDirEye, const float* plColors, int plCount,
const float* plWorldXZ, const float* plLimitXZ,
const Eigen::Matrix4f& cameraViewInverse,
const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex,
const float* ambientColor, const float* fogColor)
{
AnimationState drawState = resolveActiveState();
AnimationState drawState = resolveActiveState(state);
auto it = animations.find(drawState);
if (it == animations.end()) return;
@@ -858,10 +919,10 @@ void Character::drawNightCpuWithShadow(Renderer& renderer,
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
renderer.PushMatrix();
renderer.TranslateMatrix({ characterState.position.x(), characterState.position.y(), characterState.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-characterState.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(characterState.modelScale);
renderer.RotateMatrix(characterState.modelCorrectionRotation.toRotationMatrix());
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
renderer.ScaleMatrix(state.modelScale);
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
auto& anim = it->second;
for (const auto& name : anim.model.meshNamesOrdered) {
@@ -878,7 +939,7 @@ void Character::drawNightCpuWithShadow(Renderer& renderer,
renderer.PopMatrix();
renderer.RenderUniform3fv("uPlayerEyePos", playerEyePosNightShadow);
drawAttachedWeapon(renderer);
drawAttachedWeapon(renderer, state);
renderer.PopProjectionMatrix();
renderer.shaderManager.PopShader();
@@ -906,8 +967,11 @@ void Character::presentDamage(float hp, float maxHp,
onHpChanged(hp, maxHp, attackerId);
if (!hitSparkEmitter.isConfigured()) return;
auto stateSnapshot = this->stateSnapshot();
if (!stateSnapshot) return;
const CharacterState& state = *stateSnapshot;
Eigen::Vector3f emitPos = characterState.position + Eigen::Vector3f(0.f, 1.f, 0.f);
Eigen::Vector3f emitPos = state.position + Eigen::Vector3f(0.f, 1.f, 0.f);
hitSparkEmitter.resetEmissionPoints({ emitPos });
hitSparkEmitter.setEmissionDirection(attackDirection);
@@ -924,16 +988,18 @@ void Character::prepareHitSparksForDraw(const Eigen::Matrix4f& cameraViewMatrix)
hitSparkEmitter.prepareForDraw(cameraViewMatrix);
}
void Character::drawName(TextRenderer& textRenderer,
const Eigen::Matrix4f& cameraViewMatrix,
const Eigen::Matrix4f& projectionMatrix)
static void drawCharacterNameLabel(
TextRenderer& textRenderer,
const Eigen::Matrix4f& cameraViewMatrix,
const Eigen::Matrix4f& projectionMatrix,
const CharacterState& state,
const std::string& displayName,
const std::array<float, 4>& color)
{
if (characterState.isPlayer) return;
if (!characterState.enabled) return;
if (characterState.hp <= 0.f) return;
if (characterState.npcName.empty()) return;
if (!state.enabled || state.hp <= 0.f || displayName.empty()) return;
Eigen::Vector4f worldPos(characterState.position.x(), characterState.position.y() + 1.83f, characterState.position.z(), 1.f);
Eigen::Vector4f worldPos(
state.position.x(), state.position.y() + 1.83f, state.position.z(), 1.f);
Eigen::Vector4f clip = projectionMatrix * cameraViewMatrix * worldPos;
if (clip.w() <= 0.f) return;
@@ -943,26 +1009,57 @@ void Character::drawName(TextRenderer& textRenderer,
float screenX = (ndcX * 0.5f + 0.5f) * Environment::projectionWidth;
float screenY = (ndcY * 0.5f + 0.5f) * Environment::projectionHeight;
textRenderer.drawText(displayName, screenX, screenY, 1.0f, true, color);
}
std::array<float, 4> color = characterState.canAttack
void Character::drawName(TextRenderer& textRenderer,
const Eigen::Matrix4f& cameraViewMatrix,
const Eigen::Matrix4f& projectionMatrix)
{
auto stateSnapshot = this->stateSnapshot();
if (!stateSnapshot) return;
const CharacterState& state = *stateSnapshot;
if (state.isPlayer) return;
if (state.npcName.empty()) return;
std::array<float, 4> color = state.canAttack
? std::array<float, 4>{ 1.f, 0.f, 0.f, 1.f }
: std::array<float, 4>{ 0.f, 1.f, 0.f, 1.f };
const std::string& translatedName = Character::nameTranslations.translateRef(characterState.npcName, g_currentLanguage);
const std::string& translatedName = Character::nameTranslations.translateRef(state.npcName, g_currentLanguage);
textRenderer.drawText(translatedName, screenX, screenY, 1.0f, true, color);
drawCharacterNameLabel(
textRenderer, cameraViewMatrix, projectionMatrix,
state, translatedName, color);
}
void Character::drawPlayerName(
TextRenderer& textRenderer,
const Eigen::Matrix4f& cameraViewMatrix,
const Eigen::Matrix4f& projectionMatrix,
const std::string& displayName)
{
auto snapshot = stateSnapshot();
if (!snapshot) return;
static const std::array<float, 4> color = {0.f, 1.f, 0.f, 1.f};
drawCharacterNameLabel(
textRenderer, cameraViewMatrix, projectionMatrix,
*snapshot, displayName, color);
}
void Character::drawHealthBar(Renderer& renderer,
const Eigen::Matrix4f& cameraViewMatrix,
const Eigen::Matrix4f& projectionMatrix)
{
if (characterState.isPlayer || !characterState.canAttack) return;
if (!characterState.enabled) return;
if (characterState.hp <= 0.f) return;
if (characterState.initialHp <= 0.f) return;
auto stateSnapshot = this->stateSnapshot();
if (!stateSnapshot) return;
const CharacterState& state = *stateSnapshot;
if (state.isPlayer || !state.canAttack) return;
if (!state.enabled) return;
if (state.hp <= 0.f) return;
if (state.initialHp <= 0.f) return;
Eigen::Vector4f worldPos(characterState.position.x(), characterState.position.y() + 1.75f, characterState.position.z(), 1.f);
Eigen::Vector4f worldPos(state.position.x(), state.position.y() + 1.75f, state.position.z(), 1.f);
Eigen::Vector4f clip = projectionMatrix * cameraViewMatrix * worldPos;
if (clip.w() <= 0.f) return;
float ndcX = clip.x() / clip.w();
@@ -980,7 +1077,7 @@ void Character::drawHealthBar(Renderer& renderer,
const float bottom = sy - barH * 0.5f;
const float top = sy + barH * 0.5f;
float frac = characterState.hp / characterState.initialHp;
float frac = state.hp / state.initialHp;
if (frac < 0.f) frac = 0.f;
if (frac > 1.f) frac = 1.f;
const float split = left + barW * frac;