mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-17 09:33:51 +00:00
refactor: decompose TransportManager and upgrade Entity to CatmullRom splines
TransportManager decomposition: - Extract TransportClockSync: server clock offset, yaw flip detection, velocity bootstrap, client/server mode switching - Extract TransportAnimator: spline evaluation, Z clamping, orientation from server yaw or spline tangent - Slim TransportManager to thin orchestrator delegating to ClockSync and Animator; add pushTransform() helper to deduplicate WMO/M2 renderer calls - Remove legacy orientationFromSplineTangent (now uses CatmullRomSpline::orientationFromTangent) Entity path following upgrade: - Replace pathPoints_/pathSegDists_ linear lerp with std::optional<CatmullRomSpline> activeSpline_ - startMoveAlongPath builds SplineKeys with distance-proportional timing - updateMovement evaluates CatmullRomSpline for smooth Catmull-Rom interpolation matching server-side creature movement - Reset activeSpline_ on setPosition/startMoveTo to prevent stale state Tests: - Add test_transport_components (9 cases): ClockSync client/server/reverse modes, yaw flip detection, Animator position eval, server yaw, Z clamping - Link spline.cpp into test_entity for CatmullRomSpline dependency Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
This commit is contained in:
parent
de0383aa6b
commit
39719cac82
10 changed files with 704 additions and 337 deletions
|
|
@ -8,6 +8,8 @@
|
|||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include "math/spline.hpp"
|
||||
|
||||
namespace wowee {
|
||||
namespace game {
|
||||
|
|
@ -80,31 +82,41 @@ public:
|
|||
orientation = o;
|
||||
isMoving_ = false; // Instant position set cancels interpolation
|
||||
usePathMode_ = false;
|
||||
activeSpline_.reset();
|
||||
}
|
||||
|
||||
// Multi-segment path movement
|
||||
// Multi-segment path movement (Catmull-Rom spline interpolation)
|
||||
void startMoveAlongPath(const std::vector<std::array<float, 3>>& path, float destO, float totalDuration) {
|
||||
if (path.empty()) return;
|
||||
if (path.size() == 1 || totalDuration <= 0.0f) {
|
||||
startMoveTo(path.back()[0], path.back()[1], path.back()[2], destO, totalDuration);
|
||||
return;
|
||||
}
|
||||
// Compute cumulative distances for proportional segment timing
|
||||
pathPoints_ = path;
|
||||
pathSegDists_.resize(path.size());
|
||||
pathSegDists_[0] = 0.0f;
|
||||
// Build cumulative distances for proportional time assignment
|
||||
std::vector<float> cumDist(path.size(), 0.0f);
|
||||
float totalDist = 0.0f;
|
||||
for (size_t i = 1; i < path.size(); i++) {
|
||||
float dx = path[i][0] - path[i - 1][0];
|
||||
float dy = path[i][1] - path[i - 1][1];
|
||||
float dz = path[i][2] - path[i - 1][2];
|
||||
totalDist += std::sqrt(dx * dx + dy * dy + dz * dz);
|
||||
pathSegDists_[i] = totalDist;
|
||||
cumDist[i] = totalDist;
|
||||
}
|
||||
if (totalDist < 0.001f) {
|
||||
startMoveTo(path.back()[0], path.back()[1], path.back()[2], destO, totalDuration);
|
||||
return;
|
||||
}
|
||||
// Build SplineKeys with distance-proportional time
|
||||
uint32_t durationMs = static_cast<uint32_t>(totalDuration * 1000.0f);
|
||||
std::vector<math::SplineKey> keys(path.size());
|
||||
for (size_t i = 0; i < path.size(); i++) {
|
||||
float fraction = cumDist[i] / totalDist;
|
||||
keys[i].timeMs = static_cast<uint32_t>(fraction * durationMs);
|
||||
keys[i].position = {path[i][0], path[i][1], path[i][2]};
|
||||
}
|
||||
activeSpline_.emplace(std::move(keys), /*timeClosed=*/false);
|
||||
splineDurationMs_ = durationMs;
|
||||
|
||||
// Snap position if in overrun phase
|
||||
if (isMoving_ && moveElapsed_ >= moveDuration_) {
|
||||
x = moveEndX_; y = moveEndY_; z = moveEndZ_;
|
||||
|
|
@ -130,6 +142,7 @@ public:
|
|||
// Movement interpolation (syncs entity position with renderer during movement)
|
||||
void startMoveTo(float destX, float destY, float destZ, float destO, float durationSec) {
|
||||
usePathMode_ = false;
|
||||
activeSpline_.reset();
|
||||
if (durationSec <= 0.0f) {
|
||||
setPosition(destX, destY, destZ, destO);
|
||||
return;
|
||||
|
|
@ -172,24 +185,14 @@ public:
|
|||
if (!isMoving_) return;
|
||||
moveElapsed_ += deltaTime;
|
||||
if (moveElapsed_ < moveDuration_) {
|
||||
if (usePathMode_ && pathPoints_.size() > 1) {
|
||||
// Multi-segment path interpolation
|
||||
float totalDist = pathSegDists_.back();
|
||||
float t = moveElapsed_ / moveDuration_;
|
||||
float targetDist = t * totalDist;
|
||||
// Find the segment containing targetDist
|
||||
size_t seg = 1;
|
||||
while (seg < pathSegDists_.size() - 1 && pathSegDists_[seg] < targetDist)
|
||||
seg++;
|
||||
float segStart = pathSegDists_[seg - 1];
|
||||
float segEnd = pathSegDists_[seg];
|
||||
float segLen = segEnd - segStart;
|
||||
float segT = (segLen > 0.001f) ? (targetDist - segStart) / segLen : 0.0f;
|
||||
const auto& p0 = pathPoints_[seg - 1];
|
||||
const auto& p1 = pathPoints_[seg];
|
||||
x = p0[0] + (p1[0] - p0[0]) * segT;
|
||||
y = p0[1] + (p1[1] - p0[1]) * segT;
|
||||
z = p0[2] + (p1[2] - p0[2]) * segT;
|
||||
if (usePathMode_ && activeSpline_) {
|
||||
// Catmull-Rom spline interpolation
|
||||
uint32_t pathTimeMs = static_cast<uint32_t>(moveElapsed_ * 1000.0f);
|
||||
if (pathTimeMs >= splineDurationMs_) pathTimeMs = splineDurationMs_ - 1;
|
||||
glm::vec3 pos = activeSpline_->evaluatePosition(pathTimeMs);
|
||||
x = pos.x;
|
||||
y = pos.y;
|
||||
z = pos.z;
|
||||
} else {
|
||||
// Single-segment linear interpolation
|
||||
float t = moveElapsed_ / moveDuration_;
|
||||
|
|
@ -277,9 +280,9 @@ protected:
|
|||
float moveDuration_ = 0;
|
||||
float moveElapsed_ = 0;
|
||||
float velX_ = 0, velY_ = 0, velZ_ = 0; // Smoothed velocity for dead reckoning
|
||||
// Multi-segment path data
|
||||
std::vector<std::array<float, 3>> pathPoints_;
|
||||
std::vector<float> pathSegDists_; // Cumulative distances for each waypoint
|
||||
// CatmullRom spline for multi-segment path movement (replaces linear pathPoints_/pathSegDists_)
|
||||
std::optional<math::CatmullRomSpline> activeSpline_;
|
||||
uint32_t splineDurationMs_ = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
33
include/game/transport_animator.hpp
Normal file
33
include/game/transport_animator.hpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// include/game/transport_animator.hpp
|
||||
// Path evaluation, Z clamping, and orientation for transports.
|
||||
// Extracted from TransportManager (Phase 3b of spline refactoring).
|
||||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <cstdint>
|
||||
|
||||
namespace wowee::math { class CatmullRomSpline; }
|
||||
|
||||
namespace wowee::game {
|
||||
|
||||
struct ActiveTransport;
|
||||
struct PathEntry;
|
||||
|
||||
/// Evaluates a transport's position and orientation from a spline path and path time.
|
||||
class TransportAnimator {
|
||||
public:
|
||||
/// Evaluate the spline at pathTimeMs, apply Z clamping, and update
|
||||
/// transport.position and transport.rotation in-place.
|
||||
void evaluateAndApply(
|
||||
ActiveTransport& transport,
|
||||
const PathEntry& pathEntry,
|
||||
uint32_t pathTimeMs) const;
|
||||
|
||||
private:
|
||||
/// Guard against bad fallback Z offsets on non-world-coordinate paths.
|
||||
static float clampZOffset(float z, bool worldCoords, bool clientAnim,
|
||||
int serverUpdateCount, bool hasServerClock);
|
||||
};
|
||||
|
||||
} // namespace wowee::game
|
||||
49
include/game/transport_clock_sync.hpp
Normal file
49
include/game/transport_clock_sync.hpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// include/game/transport_clock_sync.hpp
|
||||
// Clock synchronization and yaw correction for transports.
|
||||
// Extracted from TransportManager (Phase 3a of spline refactoring).
|
||||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <cstdint>
|
||||
|
||||
namespace wowee::math { class CatmullRomSpline; }
|
||||
|
||||
namespace wowee::game {
|
||||
|
||||
struct ActiveTransport;
|
||||
struct PathEntry;
|
||||
|
||||
/// Manages clock sync, server-yaw correction, and velocity bootstrap for a single transport.
|
||||
class TransportClockSync {
|
||||
public:
|
||||
/// Compute pathTimeMs for a transport given current elapsed time and deltaTime.
|
||||
/// Returns false if the transport should use raw server position (no interpolation).
|
||||
[[nodiscard]] bool computePathTime(
|
||||
ActiveTransport& transport,
|
||||
const math::CatmullRomSpline& spline,
|
||||
double elapsedTime,
|
||||
float deltaTime,
|
||||
uint32_t& outPathTimeMs) const;
|
||||
|
||||
/// Process a server position update: update clock offset, detect yaw flips,
|
||||
/// bootstrap velocity, and switch between client/server driven modes.
|
||||
void processServerUpdate(
|
||||
ActiveTransport& transport,
|
||||
const PathEntry* pathEntry,
|
||||
const glm::vec3& position,
|
||||
float orientation,
|
||||
double elapsedTime);
|
||||
|
||||
private:
|
||||
/// Detect and apply 180-degree yaw correction based on movement vs heading alignment.
|
||||
void updateYawAlignment(
|
||||
ActiveTransport& transport,
|
||||
const glm::vec3& velocity) const;
|
||||
|
||||
/// Bootstrap velocity from nearest DBC path segment on first authoritative sample.
|
||||
void bootstrapVelocityFromPath(
|
||||
ActiveTransport& transport,
|
||||
const PathEntry& pathEntry) const;
|
||||
};
|
||||
|
||||
} // namespace wowee::game
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "game/transport_path_repository.hpp"
|
||||
#include "game/transport_clock_sync.hpp"
|
||||
#include "game/transport_animator.hpp"
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
|
@ -124,10 +126,11 @@ public:
|
|||
private:
|
||||
void updateTransportMovement(ActiveTransport& transport, float deltaTime);
|
||||
void updateTransformMatrices(ActiveTransport& transport);
|
||||
/// Legacy transport orientation from tangent (preserves original cross-product order).
|
||||
static glm::quat orientationFromSplineTangent(const glm::vec3& tangent);
|
||||
void pushTransform(ActiveTransport& transport);
|
||||
|
||||
TransportPathRepository pathRepo_;
|
||||
TransportClockSync clockSync_;
|
||||
TransportAnimator animator_;
|
||||
std::unordered_map<uint64_t, ActiveTransport> transports_;
|
||||
rendering::WMORenderer* wmoRenderer_ = nullptr;
|
||||
rendering::M2Renderer* m2Renderer_ = nullptr;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue