mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-08 10:03:51 +00:00
chore(refactor): god-object decomposition and mega-file splits
Split all mega-files by single-responsibility concern and partially extracting AudioCoordinator and OverlaySystem from the Renderer facade. No behavioral changes. Splits: - game_handler.cpp (5,247 LOC) → core + callbacks + packets (3 files) - world_packets.cpp (4,453 LOC) → economy/entity/social/world (4 files) - game_screen.cpp (5,786 LOC) → core + frames + hud + minimap (4 files) - m2_renderer.cpp (3,343 LOC) → core + instance + particles + render (4 files) - chat_panel.cpp (3,140 LOC) → core + commands + utils (3 files) - entity_spawner.cpp (2,750 LOC) → core + player + processing (3 files) Extractions: - AudioCoordinator: include/audio/ + src/audio/ (owned by Renderer) - OverlaySystem: include/rendering/ + src/rendering/overlay_system.* CMakeLists.txt: registered all 17 new translation units. Related handler/callback files: minor include fixups post-split.
This commit is contained in:
parent
6dcc06697b
commit
34c0e3ca28
49 changed files with 29113 additions and 28109 deletions
364
src/rendering/m2_renderer_internal.h
Normal file
364
src/rendering/m2_renderer_internal.h
Normal file
|
|
@ -0,0 +1,364 @@
|
|||
// m2_renderer_internal.h — shared helpers for the m2_renderer split files.
|
||||
// All functions are inline to allow inclusion in multiple translation units.
|
||||
#pragma once
|
||||
|
||||
#include "rendering/m2_renderer.hpp"
|
||||
#include "pipeline/m2_loader.hpp"
|
||||
#include "core/profiler.hpp"
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <random>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
namespace m2_internal {
|
||||
|
||||
// ---- RNG helpers ----
|
||||
inline std::mt19937& rng() {
|
||||
static std::mt19937 gen(std::random_device{}());
|
||||
return gen;
|
||||
}
|
||||
inline uint32_t randRange(uint32_t maxExclusive) {
|
||||
if (maxExclusive == 0) return 0;
|
||||
return std::uniform_int_distribution<uint32_t>(0, maxExclusive - 1)(rng());
|
||||
}
|
||||
inline float randFloat(float lo, float hi) {
|
||||
return std::uniform_real_distribution<float>(lo, hi)(rng());
|
||||
}
|
||||
|
||||
// ---- Constants ----
|
||||
inline const auto kLavaAnimStart = std::chrono::steady_clock::now();
|
||||
inline constexpr uint32_t kParticleFlagRandomized = 0x40;
|
||||
inline constexpr uint32_t kParticleFlagTiled = 0x80;
|
||||
inline constexpr float kSmokeEmitInterval = 1.0f / 48.0f;
|
||||
|
||||
// ---- Geometry / collision helpers ----
|
||||
|
||||
inline float computeGroundDetailDownOffset(const M2ModelGPU& model, float scale) {
|
||||
const float pivotComp = glm::clamp(std::max(0.0f, model.boundMin.z * scale), 0.0f, 0.10f);
|
||||
const float terrainSink = 0.03f;
|
||||
return pivotComp + terrainSink;
|
||||
}
|
||||
|
||||
inline void getTightCollisionBounds(const M2ModelGPU& model, glm::vec3& outMin, glm::vec3& outMax) {
|
||||
glm::vec3 center = (model.boundMin + model.boundMax) * 0.5f;
|
||||
glm::vec3 half = (model.boundMax - model.boundMin) * 0.5f;
|
||||
|
||||
if (model.collisionTreeTrunk) {
|
||||
float modelHoriz = std::max(model.boundMax.x - model.boundMin.x,
|
||||
model.boundMax.y - model.boundMin.y);
|
||||
float trunkHalf = std::clamp(modelHoriz * 0.05f, 0.5f, 5.0f);
|
||||
half.x = trunkHalf;
|
||||
half.y = trunkHalf;
|
||||
half.z = std::min(trunkHalf * 2.5f, 3.5f);
|
||||
center.z = model.boundMin.z + half.z;
|
||||
} else if (model.collisionNarrowVerticalProp) {
|
||||
half.x *= 0.30f;
|
||||
half.y *= 0.30f;
|
||||
half.z *= 0.96f;
|
||||
} else if (model.collisionSmallSolidProp) {
|
||||
half.x *= 1.00f;
|
||||
half.y *= 1.00f;
|
||||
half.z *= 1.00f;
|
||||
} else if (model.collisionSteppedLowPlatform) {
|
||||
half.x *= 0.98f;
|
||||
half.y *= 0.98f;
|
||||
half.z *= 0.52f;
|
||||
} else {
|
||||
half.x *= 0.66f;
|
||||
half.y *= 0.66f;
|
||||
half.z *= 0.76f;
|
||||
}
|
||||
|
||||
outMin = center - half;
|
||||
outMax = center + half;
|
||||
}
|
||||
|
||||
inline float getEffectiveCollisionTopLocal(const M2ModelGPU& model,
|
||||
const glm::vec3& localPos,
|
||||
const glm::vec3& localMin,
|
||||
const glm::vec3& localMax) {
|
||||
if (!model.collisionSteppedFountain && !model.collisionSteppedLowPlatform) {
|
||||
return localMax.z;
|
||||
}
|
||||
|
||||
glm::vec2 center((localMin.x + localMax.x) * 0.5f, (localMin.y + localMax.y) * 0.5f);
|
||||
glm::vec2 half((localMax.x - localMin.x) * 0.5f, (localMax.y - localMin.y) * 0.5f);
|
||||
if (half.x < 1e-4f || half.y < 1e-4f) {
|
||||
return localMax.z;
|
||||
}
|
||||
|
||||
float nx = (localPos.x - center.x) / half.x;
|
||||
float ny = (localPos.y - center.y) / half.y;
|
||||
float r = std::sqrt(nx * nx + ny * ny);
|
||||
|
||||
float h = localMax.z - localMin.z;
|
||||
if (model.collisionSteppedFountain) {
|
||||
if (r > 0.85f) return localMin.z + h * 0.18f;
|
||||
if (r > 0.65f) return localMin.z + h * 0.36f;
|
||||
if (r > 0.45f) return localMin.z + h * 0.54f;
|
||||
if (r > 0.28f) return localMin.z + h * 0.70f;
|
||||
if (r > 0.14f) return localMin.z + h * 0.84f;
|
||||
return localMin.z + h * 0.96f;
|
||||
}
|
||||
|
||||
float edge = std::max(std::abs(nx), std::abs(ny));
|
||||
if (edge > 0.92f) return localMin.z + h * 0.06f;
|
||||
if (edge > 0.72f) return localMin.z + h * 0.30f;
|
||||
return localMin.z + h * 0.62f;
|
||||
}
|
||||
|
||||
inline bool segmentIntersectsAABB(const glm::vec3& from, const glm::vec3& to,
|
||||
const glm::vec3& bmin, const glm::vec3& bmax,
|
||||
float& outEnterT) {
|
||||
glm::vec3 d = to - from;
|
||||
float tEnter = 0.0f;
|
||||
float tExit = 1.0f;
|
||||
|
||||
for (int axis = 0; axis < 3; axis++) {
|
||||
if (std::abs(d[axis]) < 1e-6f) {
|
||||
if (from[axis] < bmin[axis] || from[axis] > bmax[axis]) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
float inv = 1.0f / d[axis];
|
||||
float t0 = (bmin[axis] - from[axis]) * inv;
|
||||
float t1 = (bmax[axis] - from[axis]) * inv;
|
||||
if (t0 > t1) std::swap(t0, t1);
|
||||
|
||||
tEnter = std::max(tEnter, t0);
|
||||
tExit = std::min(tExit, t1);
|
||||
if (tEnter > tExit) return false;
|
||||
}
|
||||
|
||||
outEnterT = tEnter;
|
||||
return tExit >= 0.0f && tEnter <= 1.0f;
|
||||
}
|
||||
|
||||
inline void transformAABB(const glm::mat4& modelMatrix,
|
||||
const glm::vec3& localMin,
|
||||
const glm::vec3& localMax,
|
||||
glm::vec3& outMin,
|
||||
glm::vec3& outMax) {
|
||||
const glm::vec3 corners[8] = {
|
||||
{localMin.x, localMin.y, localMin.z},
|
||||
{localMin.x, localMin.y, localMax.z},
|
||||
{localMin.x, localMax.y, localMin.z},
|
||||
{localMin.x, localMax.y, localMax.z},
|
||||
{localMax.x, localMin.y, localMin.z},
|
||||
{localMax.x, localMin.y, localMax.z},
|
||||
{localMax.x, localMax.y, localMin.z},
|
||||
{localMax.x, localMax.y, localMax.z}
|
||||
};
|
||||
|
||||
outMin = glm::vec3(std::numeric_limits<float>::max());
|
||||
outMax = glm::vec3(-std::numeric_limits<float>::max());
|
||||
for (const auto& c : corners) {
|
||||
glm::vec3 wc = glm::vec3(modelMatrix * glm::vec4(c, 1.0f));
|
||||
outMin = glm::min(outMin, wc);
|
||||
outMax = glm::max(outMax, wc);
|
||||
}
|
||||
}
|
||||
|
||||
inline float pointAABBDistanceSq(const glm::vec3& p, const glm::vec3& bmin, const glm::vec3& bmax) {
|
||||
glm::vec3 q = glm::clamp(p, bmin, bmax);
|
||||
glm::vec3 d = p - q;
|
||||
return glm::dot(d, d);
|
||||
}
|
||||
|
||||
// Möller–Trumbore ray-triangle intersection.
|
||||
// Returns distance along ray if hit, negative if miss.
|
||||
inline float rayTriangleIntersect(const glm::vec3& origin, const glm::vec3& dir,
|
||||
const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2) {
|
||||
constexpr float EPSILON = 1e-6f;
|
||||
glm::vec3 e1 = v1 - v0;
|
||||
glm::vec3 e2 = v2 - v0;
|
||||
glm::vec3 h = glm::cross(dir, e2);
|
||||
float a = glm::dot(e1, h);
|
||||
if (a > -EPSILON && a < EPSILON) return -1.0f;
|
||||
float f = 1.0f / a;
|
||||
glm::vec3 s = origin - v0;
|
||||
float u = f * glm::dot(s, h);
|
||||
if (u < 0.0f || u > 1.0f) return -1.0f;
|
||||
glm::vec3 q = glm::cross(s, e1);
|
||||
float v = f * glm::dot(dir, q);
|
||||
if (v < 0.0f || u + v > 1.0f) return -1.0f;
|
||||
float t = f * glm::dot(e2, q);
|
||||
return t > EPSILON ? t : -1.0f;
|
||||
}
|
||||
|
||||
// Closest point on triangle to a point (Ericson, Real-Time Collision Detection §5.1.5).
|
||||
inline glm::vec3 closestPointOnTriangle(const glm::vec3& p,
|
||||
const glm::vec3& a, const glm::vec3& b, const glm::vec3& c) {
|
||||
glm::vec3 ab = b - a, ac = c - a, ap = p - a;
|
||||
float d1 = glm::dot(ab, ap), d2 = glm::dot(ac, ap);
|
||||
if (d1 <= 0.0f && d2 <= 0.0f) return a;
|
||||
glm::vec3 bp = p - b;
|
||||
float d3 = glm::dot(ab, bp), d4 = glm::dot(ac, bp);
|
||||
if (d3 >= 0.0f && d4 <= d3) return b;
|
||||
float vc = d1 * d4 - d3 * d2;
|
||||
if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f) {
|
||||
float v = d1 / (d1 - d3);
|
||||
return a + v * ab;
|
||||
}
|
||||
glm::vec3 cp = p - c;
|
||||
float d5 = glm::dot(ab, cp), d6 = glm::dot(ac, cp);
|
||||
if (d6 >= 0.0f && d5 <= d6) return c;
|
||||
float vb = d5 * d2 - d1 * d6;
|
||||
if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f) {
|
||||
float w = d2 / (d2 - d6);
|
||||
return a + w * ac;
|
||||
}
|
||||
float va = d3 * d6 - d5 * d4;
|
||||
if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f) {
|
||||
float w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
|
||||
return b + w * (c - b);
|
||||
}
|
||||
float denom = 1.0f / (va + vb + vc);
|
||||
float v = vb * denom;
|
||||
float w = vc * denom;
|
||||
return a + ab * v + ac * w;
|
||||
}
|
||||
|
||||
// ---- Thread-local scratch buffers for collision queries ----
|
||||
inline thread_local std::vector<size_t> tl_m2_candidateScratch;
|
||||
inline thread_local std::unordered_set<uint32_t> tl_m2_candidateIdScratch;
|
||||
inline thread_local std::vector<uint32_t> tl_m2_collisionTriScratch;
|
||||
|
||||
// ---- Bone animation helpers ----
|
||||
|
||||
inline int findKeyframeIndex(const std::vector<uint32_t>& timestamps, float time) {
|
||||
if (timestamps.empty()) return -1;
|
||||
if (timestamps.size() == 1) return 0;
|
||||
auto it = std::upper_bound(timestamps.begin(), timestamps.end(), time,
|
||||
[](float t, uint32_t ts) { return t < static_cast<float>(ts); });
|
||||
if (it == timestamps.begin()) return 0;
|
||||
size_t idx = static_cast<size_t>(it - timestamps.begin()) - 1;
|
||||
return static_cast<int>(std::min(idx, timestamps.size() - 2));
|
||||
}
|
||||
|
||||
inline void resolveTrackTime(const pipeline::M2AnimationTrack& track,
|
||||
int seqIdx, float time,
|
||||
const std::vector<uint32_t>& globalSeqDurations,
|
||||
int& outSeqIdx, float& outTime) {
|
||||
if (track.globalSequence >= 0 &&
|
||||
static_cast<size_t>(track.globalSequence) < globalSeqDurations.size()) {
|
||||
outSeqIdx = 0;
|
||||
float dur = static_cast<float>(globalSeqDurations[track.globalSequence]);
|
||||
if (dur > 0.0f) {
|
||||
outTime = time;
|
||||
while (outTime >= dur) {
|
||||
outTime -= dur;
|
||||
}
|
||||
} else {
|
||||
outTime = 0.0f;
|
||||
}
|
||||
} else {
|
||||
outSeqIdx = seqIdx;
|
||||
outTime = time;
|
||||
}
|
||||
}
|
||||
|
||||
inline glm::vec3 interpVec3(const pipeline::M2AnimationTrack& track,
|
||||
int seqIdx, float time, const glm::vec3& def,
|
||||
const std::vector<uint32_t>& globalSeqDurations) {
|
||||
if (!track.hasData()) return def;
|
||||
int si; float t;
|
||||
resolveTrackTime(track, seqIdx, time, globalSeqDurations, si, t);
|
||||
if (si < 0 || si >= static_cast<int>(track.sequences.size())) return def;
|
||||
const auto& keys = track.sequences[si];
|
||||
if (keys.timestamps.empty() || keys.vec3Values.empty()) return def;
|
||||
auto safe = [&](const glm::vec3& v) -> glm::vec3 {
|
||||
if (std::isnan(v.x) || std::isnan(v.y) || std::isnan(v.z)) return def;
|
||||
return v;
|
||||
};
|
||||
if (keys.vec3Values.size() == 1) return safe(keys.vec3Values[0]);
|
||||
int idx = findKeyframeIndex(keys.timestamps, t);
|
||||
if (idx < 0) return def;
|
||||
size_t i0 = static_cast<size_t>(idx);
|
||||
size_t i1 = std::min(i0 + 1, keys.vec3Values.size() - 1);
|
||||
if (i0 == i1) return safe(keys.vec3Values[i0]);
|
||||
float t0 = static_cast<float>(keys.timestamps[i0]);
|
||||
float t1 = static_cast<float>(keys.timestamps[i1]);
|
||||
float dur = t1 - t0;
|
||||
float frac = (dur > 0.0f) ? glm::clamp((t - t0) / dur, 0.0f, 1.0f) : 0.0f;
|
||||
return safe(glm::mix(keys.vec3Values[i0], keys.vec3Values[i1], frac));
|
||||
}
|
||||
|
||||
inline glm::quat interpQuat(const pipeline::M2AnimationTrack& track,
|
||||
int seqIdx, float time,
|
||||
const std::vector<uint32_t>& globalSeqDurations) {
|
||||
glm::quat identity(1.0f, 0.0f, 0.0f, 0.0f);
|
||||
if (!track.hasData()) return identity;
|
||||
int si; float t;
|
||||
resolveTrackTime(track, seqIdx, time, globalSeqDurations, si, t);
|
||||
if (si < 0 || si >= static_cast<int>(track.sequences.size())) return identity;
|
||||
const auto& keys = track.sequences[si];
|
||||
if (keys.timestamps.empty() || keys.quatValues.empty()) return identity;
|
||||
auto safe = [&](const glm::quat& q) -> glm::quat {
|
||||
float lenSq = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w;
|
||||
if (lenSq < 0.000001f || std::isnan(lenSq)) return identity;
|
||||
return q;
|
||||
};
|
||||
if (keys.quatValues.size() == 1) return safe(keys.quatValues[0]);
|
||||
int idx = findKeyframeIndex(keys.timestamps, t);
|
||||
if (idx < 0) return identity;
|
||||
size_t i0 = static_cast<size_t>(idx);
|
||||
size_t i1 = std::min(i0 + 1, keys.quatValues.size() - 1);
|
||||
if (i0 == i1) return safe(keys.quatValues[i0]);
|
||||
float t0 = static_cast<float>(keys.timestamps[i0]);
|
||||
float t1 = static_cast<float>(keys.timestamps[i1]);
|
||||
float dur = t1 - t0;
|
||||
float frac = (dur > 0.0f) ? glm::clamp((t - t0) / dur, 0.0f, 1.0f) : 0.0f;
|
||||
return glm::slerp(safe(keys.quatValues[i0]), safe(keys.quatValues[i1]), frac);
|
||||
}
|
||||
|
||||
inline void computeBoneMatrices(const M2ModelGPU& model, M2Instance& instance) {
|
||||
ZoneScopedN("M2::computeBoneMatrices");
|
||||
size_t numBones = std::min(model.bones.size(), size_t(128));
|
||||
if (numBones == 0) return;
|
||||
instance.boneMatrices.resize(numBones);
|
||||
const auto& gsd = model.globalSequenceDurations;
|
||||
|
||||
for (size_t i = 0; i < numBones; i++) {
|
||||
const auto& bone = model.bones[i];
|
||||
glm::vec3 trans = interpVec3(bone.translation, instance.currentSequenceIndex, instance.animTime, glm::vec3(0.0f), gsd);
|
||||
glm::quat rot = interpQuat(bone.rotation, instance.currentSequenceIndex, instance.animTime, gsd);
|
||||
glm::vec3 scl = interpVec3(bone.scale, instance.currentSequenceIndex, instance.animTime, glm::vec3(1.0f), gsd);
|
||||
|
||||
if (scl.x < 0.001f) scl.x = 1.0f;
|
||||
if (scl.y < 0.001f) scl.y = 1.0f;
|
||||
if (scl.z < 0.001f) scl.z = 1.0f;
|
||||
|
||||
glm::mat4 local = glm::translate(glm::mat4(1.0f), bone.pivot);
|
||||
local = glm::translate(local, trans);
|
||||
local *= glm::toMat4(rot);
|
||||
local = glm::scale(local, scl);
|
||||
local = glm::translate(local, -bone.pivot);
|
||||
|
||||
if (bone.parentBone >= 0 && static_cast<size_t>(bone.parentBone) < numBones) {
|
||||
instance.boneMatrices[i] = instance.boneMatrices[bone.parentBone] * local;
|
||||
} else {
|
||||
instance.boneMatrices[i] = local;
|
||||
}
|
||||
}
|
||||
instance.bonesDirty[0] = instance.bonesDirty[1] = true;
|
||||
}
|
||||
|
||||
} // namespace m2_internal
|
||||
|
||||
// Pull all symbols into the rendering namespace so existing code compiles unchanged
|
||||
using namespace m2_internal;
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
Loading…
Add table
Add a link
Reference in a new issue