Working on interpolation
This commit is contained in:
1 parent
23fef6f349
commit
28846b9f25
8 files changed
+231
-51
No files matched your search
+98
-26
@@ -52,6 +52,11 @@ Eigen::Vector3f Character::getCurrentNavigationTarget() const {
|
||||
return state ? state->getCurrentNavigationTarget() : Eigen::Vector3f::Zero();
|
||||
}
|
||||
|
||||
Eigen::Vector3f Character::renderPosition() const {
|
||||
auto state = stateSnapshot();
|
||||
return state ? interpolatedState(*state).position : Eigen::Vector3f::Zero();
|
||||
}
|
||||
|
||||
float Character::getHp() const {
|
||||
auto state = stateSnapshot();
|
||||
return state ? state->getHp() : 0.f;
|
||||
@@ -163,6 +168,62 @@ AnimationState Character::resolveActiveState(const CharacterState& state) const
|
||||
return AnimationState::STAND;
|
||||
}
|
||||
|
||||
CharacterState Character::interpolatedState(const CharacterState& state) const {
|
||||
if (!hasPresentationSample_ || !currentPresentationSample_.enabled || !state.enabled) {
|
||||
return state;
|
||||
}
|
||||
|
||||
constexpr float kSimulationTickMs = 100.0f;
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const float elapsedMs = static_cast<float>(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - lastPresentationUpdate_).count());
|
||||
const float alpha = (std::max)(0.0f, (std::min)(1.0f, elapsedMs / kSimulationTickMs));
|
||||
|
||||
CharacterState result = state;
|
||||
result.position = previousPresentationSample_.position +
|
||||
alpha * (currentPresentationSample_.position - previousPresentationSample_.position);
|
||||
|
||||
constexpr float kPi = 3.14159265358979323846f;
|
||||
constexpr float kTwoPi = 2.0f * kPi;
|
||||
float angleDelta = currentPresentationSample_.facingAngle -
|
||||
previousPresentationSample_.facingAngle;
|
||||
while (angleDelta > kPi) angleDelta -= kTwoPi;
|
||||
while (angleDelta < -kPi) angleDelta += kTwoPi;
|
||||
result.facingAngle = previousPresentationSample_.facingAngle + alpha * angleDelta;
|
||||
if (previousPresentationSample_.currentState == state.currentState &&
|
||||
currentPresentationSample_.currentState == state.currentState) {
|
||||
float previousFrame = previousPresentationSample_.animationFrame;
|
||||
float currentFrame = currentPresentationSample_.animationFrame;
|
||||
const auto animationIndex = static_cast<std::size_t>(state.currentState);
|
||||
const int frameCount = animationIndex < state.animationFrameCounts.size()
|
||||
? state.animationFrameCounts[animationIndex] : 1;
|
||||
if (frameCount > 1 && currentFrame < previousFrame)
|
||||
currentFrame += static_cast<float>(frameCount);
|
||||
result.animationFrame = previousFrame + alpha * (currentFrame - previousFrame);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Character::updateAnimationForRender(const CharacterState& state) {
|
||||
auto it = animations.find(state.currentState);
|
||||
if (it == animations.end()) return;
|
||||
|
||||
auto& anim = it->second;
|
||||
anim.currentFrame = (std::min)(
|
||||
state.animationFrame, static_cast<float>((std::max)(0, anim.totalFrames)));
|
||||
if (std::abs(anim.currentFrame - anim.lastFrame) <= 0.0001f) return;
|
||||
|
||||
if (!anim.model.animations.empty() && !anim.model.animations[0].keyFrames.empty()) {
|
||||
anim.gpuSkinningShaderData.ComputeSkinningMatrices(
|
||||
anim.model.startBones, anim.model.animations[0].keyFrames,
|
||||
anim.currentFrame);
|
||||
if (!state.useGpuSkinning)
|
||||
anim.model.Interpolate(anim.currentFrame);
|
||||
}
|
||||
anim.lastFrame = anim.currentFrame;
|
||||
}
|
||||
|
||||
void Character::update(int64_t deltaMs, const CharacterUpdateEvents* events) {
|
||||
if (hitSparkEmitter.isConfigured()) {
|
||||
hitSparkEmitter.update(static_cast<float>(deltaMs));
|
||||
@@ -172,6 +233,26 @@ void Character::update(int64_t deltaMs, const CharacterUpdateEvents* events) {
|
||||
if (!stateSnapshot) return;
|
||||
const CharacterState& state = *stateSnapshot;
|
||||
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
PresentationSample sample;
|
||||
sample.position = state.position;
|
||||
sample.facingAngle = state.facingAngle;
|
||||
sample.animationFrame = state.animationFrame;
|
||||
sample.currentState = state.currentState;
|
||||
sample.enabled = state.enabled;
|
||||
const bool largeGap = hasPresentationSample_ &&
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - lastPresentationUpdate_).count() > 250;
|
||||
if (!hasPresentationSample_ || largeGap ||
|
||||
currentPresentationSample_.enabled != sample.enabled) {
|
||||
previousPresentationSample_ = sample;
|
||||
} else {
|
||||
previousPresentationSample_ = currentPresentationSample_;
|
||||
}
|
||||
currentPresentationSample_ = sample;
|
||||
lastPresentationUpdate_ = now;
|
||||
hasPresentationSample_ = true;
|
||||
|
||||
if (events) {
|
||||
if (events->deathAnimationComplete && onDeathAnimComplete)
|
||||
onDeathAnimComplete();
|
||||
@@ -183,22 +264,7 @@ void Character::update(int64_t deltaMs, const CharacterUpdateEvents* events) {
|
||||
}
|
||||
|
||||
if (!state.enabled) return;
|
||||
auto it = animations.find(state.currentState);
|
||||
if (it == animations.end()) return;
|
||||
|
||||
auto& anim = it->second;
|
||||
anim.currentFrame = (std::min)(
|
||||
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 (!state.useGpuSkinning) {
|
||||
anim.model.Interpolate(static_cast<int>(anim.currentFrame));
|
||||
}
|
||||
anim.lastFrame = static_cast<int>(anim.currentFrame);
|
||||
}
|
||||
updateAnimationForRender(state);
|
||||
}
|
||||
|
||||
void Character::draw(Renderer& renderer) {
|
||||
@@ -211,8 +277,9 @@ void Character::draw(Renderer& renderer) {
|
||||
|
||||
auto stateSnapshot = this->stateSnapshot();
|
||||
if (!stateSnapshot) return;
|
||||
const CharacterState& state = *stateSnapshot;
|
||||
CharacterState state = interpolatedState(*stateSnapshot);
|
||||
if (!state.enabled) return;
|
||||
updateAnimationForRender(state);
|
||||
|
||||
if (state.useGpuSkinning) {
|
||||
drawGpuSkinning(renderer, state);
|
||||
@@ -270,7 +337,7 @@ void Character::drawGpuSkinning(Renderer& renderer, const CharacterState& state)
|
||||
if (anim.model.animations.empty() || anim.model.animations[0].keyFrames.empty()) return;
|
||||
anim.gpuSkinningShaderData.ComputeSkinningMatrices(
|
||||
anim.model.startBones, anim.model.animations[0].keyFrames,
|
||||
static_cast<int>(anim.currentFrame));
|
||||
anim.currentFrame);
|
||||
if (anim.gpuSkinningShaderData.skinningMatrices.empty()) return;
|
||||
}
|
||||
|
||||
@@ -328,7 +395,7 @@ bool Character::prepareGpuSkinning(const CharacterState& state) {
|
||||
if (anim.model.animations.empty() || anim.model.animations[0].keyFrames.empty()) return false;
|
||||
anim.gpuSkinningShaderData.ComputeSkinningMatrices(
|
||||
anim.model.startBones, anim.model.animations[0].keyFrames,
|
||||
static_cast<int>(anim.currentFrame));
|
||||
anim.currentFrame);
|
||||
if (anim.gpuSkinningShaderData.skinningMatrices.empty()) return false;
|
||||
}
|
||||
return true;
|
||||
@@ -370,8 +437,9 @@ void Character::drawAttachedWeapon(Renderer& renderer, const CharacterState& sta
|
||||
void Character::drawShadowDepth(Renderer& renderer) {
|
||||
auto stateSnapshot = this->stateSnapshot();
|
||||
if (!stateSnapshot) return;
|
||||
const CharacterState& state = *stateSnapshot;
|
||||
CharacterState state = interpolatedState(*stateSnapshot);
|
||||
if (!state.enabled) return;
|
||||
updateAnimationForRender(state);
|
||||
if (state.useGpuSkinning) {
|
||||
drawShadowDepthGpuSkinning(renderer, state);
|
||||
} else {
|
||||
@@ -453,8 +521,9 @@ void Character::drawShadowDepthGpuSkinning(Renderer& renderer, const CharacterSt
|
||||
void Character::drawWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera) {
|
||||
auto stateSnapshot = this->stateSnapshot();
|
||||
if (!stateSnapshot) return;
|
||||
const CharacterState& state = *stateSnapshot;
|
||||
CharacterState state = interpolatedState(*stateSnapshot);
|
||||
if (!state.enabled) return;
|
||||
updateAnimationForRender(state);
|
||||
if (hitSparkEmitter.isConfigured() && hitSparkEmitter.getActiveParticleCount() > 0) {
|
||||
hitSparkEmitter.draw(renderer, 1.0f, Environment::width, Environment::height, false);
|
||||
}
|
||||
@@ -603,8 +672,9 @@ void Character::drawNight(Renderer& renderer,
|
||||
{
|
||||
auto stateSnapshot = this->stateSnapshot();
|
||||
if (!stateSnapshot) return;
|
||||
const CharacterState& state = *stateSnapshot;
|
||||
CharacterState state = interpolatedState(*stateSnapshot);
|
||||
if (!state.enabled) return;
|
||||
updateAnimationForRender(state);
|
||||
if (hitSparkEmitter.isConfigured() && hitSparkEmitter.getActiveParticleCount() > 0) {
|
||||
hitSparkEmitter.draw(renderer, 1.0f, Environment::width, Environment::height, false);
|
||||
}
|
||||
@@ -766,8 +836,9 @@ void Character::drawNightWithShadow(Renderer& renderer,
|
||||
{
|
||||
auto stateSnapshot = this->stateSnapshot();
|
||||
if (!stateSnapshot) return;
|
||||
const CharacterState& state = *stateSnapshot;
|
||||
CharacterState state = interpolatedState(*stateSnapshot);
|
||||
if (!state.enabled) return;
|
||||
updateAnimationForRender(state);
|
||||
if (hitSparkEmitter.isConfigured() && hitSparkEmitter.getActiveParticleCount() > 0) {
|
||||
hitSparkEmitter.draw(renderer, 1.0f, Environment::width, Environment::height, false);
|
||||
}
|
||||
@@ -1014,7 +1085,7 @@ void Character::drawName(TextRenderer& textRenderer,
|
||||
{
|
||||
auto stateSnapshot = this->stateSnapshot();
|
||||
if (!stateSnapshot) return;
|
||||
const CharacterState& state = *stateSnapshot;
|
||||
CharacterState state = interpolatedState(*stateSnapshot);
|
||||
if (state.isPlayer) return;
|
||||
if (state.npcName.empty()) return;
|
||||
|
||||
@@ -1037,10 +1108,11 @@ void Character::drawPlayerName(
|
||||
{
|
||||
auto snapshot = stateSnapshot();
|
||||
if (!snapshot) return;
|
||||
CharacterState state = interpolatedState(*snapshot);
|
||||
static const std::array<float, 4> color = {0.f, 1.f, 0.f, 1.f};
|
||||
drawCharacterNameLabel(
|
||||
textRenderer, cameraViewMatrix, projectionMatrix,
|
||||
*snapshot, displayName, color);
|
||||
state, displayName, color);
|
||||
}
|
||||
|
||||
void Character::drawHealthBar(Renderer& renderer,
|
||||
@@ -1049,7 +1121,7 @@ void Character::drawHealthBar(Renderer& renderer,
|
||||
{
|
||||
auto stateSnapshot = this->stateSnapshot();
|
||||
if (!stateSnapshot) return;
|
||||
const CharacterState& state = *stateSnapshot;
|
||||
CharacterState state = interpolatedState(*stateSnapshot);
|
||||
if (state.isPlayer || !state.canAttack) return;
|
||||
if (!state.enabled) return;
|
||||
if (state.hp <= 0.f) return;
|
||||
|
||||
Reference in new issue
Block a user