mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-25 13:03:50 +00:00
perf: eliminate ~70 unnecessary sqrt ops per frame, optimize caches and threading
Squared distance optimizations across 30 files: - Convert glm::length() comparisons to glm::dot() (no sqrt) - Use glm::inversesqrt() for check-then-normalize patterns (1 rsqrt vs 2 sqrt) - Defer sqrt to after early-out checks in collision/movement code - Hottest paths: camera_controller (21), weather particles, WMO collision, transport movement, creature interpolation, nameplate culling Container and algorithm improvements: - std::map<string> → std::unordered_map for asset/DBC/MPQ/warden caches - std::mutex → std::shared_mutex for asset_manager and mpq_manager caches - std::sort → std::partial_sort in lighting_manager (top-2 of N volumes) - Double-lookup find()+operator[] → insert_or_assign in game_handler - Add reserve() for per-frame vectors: weather, swim_effects, WMO/M2 collision Threading and synchronization: - Replace 1ms busy-wait polling with condition_variable in character_renderer - Move timestamp capture before mutex in logger - Use memory_order_acquire/release for normal map completion signaling API additions: - DBC getStringView()/getStringViewByOffset() for zero-copy string access - Parse creature display IDs from SMSG_CREATURE_QUERY_SINGLE_RESPONSE
This commit is contained in:
parent
cf0e2aa240
commit
b0466e9029
29 changed files with 328 additions and 196 deletions
|
|
@ -2101,6 +2101,7 @@ void WMORenderer::getVisibleGroupsViaPortals(const ModelData& model,
|
|||
// BFS through portals from camera's group
|
||||
std::vector<bool> visited(model.groups.size(), false);
|
||||
std::vector<uint32_t> queue;
|
||||
queue.reserve(model.groups.size());
|
||||
queue.push_back(static_cast<uint32_t>(cameraGroup));
|
||||
visited[cameraGroup] = true;
|
||||
outVisibleGroups.insert(static_cast<uint32_t>(cameraGroup));
|
||||
|
|
@ -2731,6 +2732,11 @@ void WMORenderer::GroupResources::getTrianglesInRange(
|
|||
|
||||
if (cellMinX > cellMaxX || cellMinY > cellMaxY) return;
|
||||
|
||||
// Reserve estimate: cells queried * ~8 triangles per cell
|
||||
const size_t cellCount = static_cast<size_t>(cellMaxX - cellMinX + 1) *
|
||||
static_cast<size_t>(cellMaxY - cellMinY + 1);
|
||||
out.reserve(cellCount * 8);
|
||||
|
||||
// Collect unique triangle indices using visited bitset (O(n) dedup)
|
||||
bool multiCell = (cellMinX != cellMaxX || cellMinY != cellMaxY);
|
||||
if (multiCell && !triVisited.empty()) {
|
||||
|
|
@ -2776,6 +2782,10 @@ void WMORenderer::GroupResources::getFloorTrianglesInRange(
|
|||
|
||||
if (cellMinX > cellMaxX || cellMinY > cellMaxY) return;
|
||||
|
||||
const size_t cellCount = static_cast<size_t>(cellMaxX - cellMinX + 1) *
|
||||
static_cast<size_t>(cellMaxY - cellMinY + 1);
|
||||
out.reserve(cellCount * 8);
|
||||
|
||||
bool multiCell = (cellMinX != cellMaxX || cellMinY != cellMaxY);
|
||||
if (multiCell && !triVisited.empty()) {
|
||||
for (int cy = cellMinY; cy <= cellMaxY; ++cy) {
|
||||
|
|
@ -2819,6 +2829,10 @@ void WMORenderer::GroupResources::getWallTrianglesInRange(
|
|||
|
||||
if (cellMinX > cellMaxX || cellMinY > cellMaxY) return;
|
||||
|
||||
const size_t cellCount = static_cast<size_t>(cellMaxX - cellMinX + 1) *
|
||||
static_cast<size_t>(cellMaxY - cellMinY + 1);
|
||||
out.reserve(cellCount * 8);
|
||||
|
||||
bool multiCell = (cellMinX != cellMaxX || cellMinY != cellMaxY);
|
||||
if (multiCell && !triVisited.empty()) {
|
||||
for (int cy = cellMinY; cy <= cellMaxY; ++cy) {
|
||||
|
|
@ -3112,8 +3126,9 @@ bool WMORenderer::checkWallCollision(const glm::vec3& from, const glm::vec3& to,
|
|||
bool blocked = false;
|
||||
|
||||
glm::vec3 moveDir = to - from;
|
||||
float moveDist = glm::length(moveDir);
|
||||
if (moveDist < 0.001f) return false;
|
||||
float moveDistSq = glm::dot(moveDir, moveDir);
|
||||
if (moveDistSq < 1e-6f) return false;
|
||||
float moveDist = std::sqrt(moveDistSq);
|
||||
|
||||
// Player collision parameters — WoW-style horizontal cylinder
|
||||
// Tighter radius when inside for more responsive indoor collision
|
||||
|
|
@ -3246,10 +3261,10 @@ bool WMORenderer::checkWallCollision(const glm::vec3& from, const glm::vec3& to,
|
|||
glm::vec3 safeLocal = hitPoint + normal * side * (PLAYER_RADIUS + 0.05f);
|
||||
glm::vec3 pushLocal(safeLocal.x - localTo.x, safeLocal.y - localTo.y, 0.0f);
|
||||
// Cap swept pushback so walls don't shove the player violently
|
||||
float pushLen = glm::length(glm::vec2(pushLocal.x, pushLocal.y));
|
||||
float pushLenSq = pushLocal.x * pushLocal.x + pushLocal.y * pushLocal.y;
|
||||
const float MAX_SWEPT_PUSH = insideWMO ? 0.45f : 0.25f;
|
||||
if (pushLen > MAX_SWEPT_PUSH) {
|
||||
float scale = MAX_SWEPT_PUSH / pushLen;
|
||||
if (pushLenSq > MAX_SWEPT_PUSH * MAX_SWEPT_PUSH) {
|
||||
float scale = MAX_SWEPT_PUSH * glm::inversesqrt(pushLenSq);
|
||||
pushLocal.x *= scale;
|
||||
pushLocal.y *= scale;
|
||||
}
|
||||
|
|
@ -3268,9 +3283,9 @@ bool WMORenderer::checkWallCollision(const glm::vec3& from, const glm::vec3& to,
|
|||
// Horizontal cylinder collision: closest point + horizontal distance
|
||||
glm::vec3 closest = closestPointOnTriangle(localTo, v0, v1, v2);
|
||||
glm::vec3 delta = localTo - closest;
|
||||
float horizDist = glm::length(glm::vec2(delta.x, delta.y));
|
||||
float horizDistSq = delta.x * delta.x + delta.y * delta.y;
|
||||
|
||||
if (horizDist <= PLAYER_RADIUS) {
|
||||
if (horizDistSq <= PLAYER_RADIUS * PLAYER_RADIUS) {
|
||||
// Skip floor-like surfaces — grounding handles them, not wall collision.
|
||||
// Threshold matches MAX_WALK_SLOPE (cos 50° ≈ 0.6428): surfaces steeper
|
||||
// than 50° from horizontal must be tested as walls to prevent clip-through.
|
||||
|
|
@ -3280,16 +3295,17 @@ bool WMORenderer::checkWallCollision(const glm::vec3& from, const glm::vec3& to,
|
|||
const float SKIN = 0.005f; // small separation so we don't re-collide immediately
|
||||
// Push must cover full penetration to prevent gradual clip-through
|
||||
const float MAX_PUSH = PLAYER_RADIUS;
|
||||
float horizDist = std::sqrt(horizDistSq);
|
||||
float penetration = (PLAYER_RADIUS - horizDist);
|
||||
float pushDist = glm::clamp(penetration + SKIN, 0.0f, MAX_PUSH);
|
||||
glm::vec2 pushDir2;
|
||||
if (horizDist > 1e-4f) {
|
||||
pushDir2 = glm::normalize(glm::vec2(delta.x, delta.y));
|
||||
if (horizDistSq > 1e-8f) {
|
||||
pushDir2 = glm::vec2(delta.x, delta.y) * (1.0f / horizDist);
|
||||
} else {
|
||||
glm::vec2 n2(normal.x, normal.y);
|
||||
float n2Len = glm::length(n2);
|
||||
if (n2Len < 1e-4f) continue;
|
||||
pushDir2 = n2 / n2Len;
|
||||
float n2LenSq = glm::dot(n2, n2);
|
||||
if (n2LenSq < 1e-8f) continue;
|
||||
pushDir2 = n2 * glm::inversesqrt(n2LenSq);
|
||||
}
|
||||
glm::vec3 pushLocal(pushDir2.x * pushDist, pushDir2.y * pushDist, 0.0f);
|
||||
|
||||
|
|
@ -3524,8 +3540,12 @@ float WMORenderer::raycastBoundingBoxes(const glm::vec3& origin, const glm::vec3
|
|||
}
|
||||
|
||||
glm::vec3 center = (instance.worldBoundsMin + instance.worldBoundsMax) * 0.5f;
|
||||
float radius = glm::length(instance.worldBoundsMax - center);
|
||||
if (glm::length(center - origin) > (maxDistance + radius + 1.0f)) {
|
||||
glm::vec3 halfExtent = instance.worldBoundsMax - center;
|
||||
float radiusSq = glm::dot(halfExtent, halfExtent);
|
||||
glm::vec3 toCenter = center - origin;
|
||||
float distSq = glm::dot(toCenter, toCenter);
|
||||
float maxR = maxDistance + std::sqrt(radiusSq) + 1.0f;
|
||||
if (distSq > maxR * maxR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue