mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-04 16:23:52 +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
|
|
@ -111,9 +111,11 @@ std::optional<float> CameraController::getCachedFloorHeight(float x, float y, fl
|
|||
// Check cache validity (position within threshold and frame count)
|
||||
glm::vec2 queryPos(x, y);
|
||||
glm::vec2 cachedPos(lastFloorQueryPos.x, lastFloorQueryPos.y);
|
||||
float dist = glm::length(queryPos - cachedPos);
|
||||
glm::vec2 dq = queryPos - cachedPos;
|
||||
float distSq = glm::dot(dq, dq);
|
||||
constexpr float kFloorThresholdSq = FLOOR_QUERY_DISTANCE_THRESHOLD * FLOOR_QUERY_DISTANCE_THRESHOLD;
|
||||
|
||||
if (dist < FLOOR_QUERY_DISTANCE_THRESHOLD && floorQueryFrameCounter < FLOOR_QUERY_FRAME_INTERVAL) {
|
||||
if (distSq < kFloorThresholdSq && floorQueryFrameCounter < FLOOR_QUERY_FRAME_INTERVAL) {
|
||||
floorQueryFrameCounter++;
|
||||
return cachedFloorHeight;
|
||||
}
|
||||
|
|
@ -194,7 +196,7 @@ void CameraController::update(float deltaTime) {
|
|||
}
|
||||
|
||||
// Smooth camera position
|
||||
if (glm::length(smoothedCamPos) < 0.01f) {
|
||||
if (glm::dot(smoothedCamPos, smoothedCamPos) < 1e-4f) {
|
||||
smoothedCamPos = actualCam;
|
||||
}
|
||||
float camLerp = 1.0f - std::exp(-CAM_SMOOTH_SPEED * deltaTime);
|
||||
|
|
@ -516,9 +518,9 @@ void CameraController::update(float deltaTime) {
|
|||
};
|
||||
|
||||
glm::vec2 fwd2(forward.x, forward.y);
|
||||
float fwdLen = glm::length(fwd2);
|
||||
if (fwdLen > 1e-4f) {
|
||||
fwd2 /= fwdLen;
|
||||
float fwdLenSq = glm::dot(fwd2, fwd2);
|
||||
if (fwdLenSq > 1e-8f) {
|
||||
fwd2 *= glm::inversesqrt(fwdLenSq);
|
||||
std::optional<float> aheadFloor;
|
||||
const float probeZ = targetPos.z + 2.0f;
|
||||
const float dists[] = {0.45f, 0.90f, 1.25f};
|
||||
|
|
@ -566,7 +568,7 @@ void CameraController::update(float deltaTime) {
|
|||
} else {
|
||||
// Manual control: use camera's 3D direction (swim where you look)
|
||||
swimForward = glm::normalize(forward3D);
|
||||
if (glm::length(swimForward) < 1e-4f) {
|
||||
if (glm::dot(swimForward, swimForward) < 1e-8f) {
|
||||
swimForward = forward;
|
||||
}
|
||||
}
|
||||
|
|
@ -583,8 +585,9 @@ void CameraController::update(float deltaTime) {
|
|||
if (nowStrafeLeft) swimMove += swimRight;
|
||||
if (nowStrafeRight) swimMove -= swimRight;
|
||||
|
||||
if (glm::length(swimMove) > 0.001f) {
|
||||
swimMove = glm::normalize(swimMove);
|
||||
float swimMoveLenSq = glm::dot(swimMove, swimMove);
|
||||
if (swimMoveLenSq > 1e-6f) {
|
||||
swimMove *= glm::inversesqrt(swimMoveLenSq);
|
||||
// Use backward swim speed when moving backwards only (not when combining with strafe)
|
||||
float applySpeed = (nowBackward && !nowForward) ? swimBackSpeed : swimSpeed;
|
||||
targetPos += swimMove * applySpeed * physicsDeltaTime;
|
||||
|
|
@ -623,10 +626,12 @@ void CameraController::update(float deltaTime) {
|
|||
// Prevent sinking/clipping through world floor while swimming.
|
||||
// Cache floor queries (update every 3 frames or 1 unit movement)
|
||||
std::optional<float> floorH;
|
||||
float dist2D = glm::length(glm::vec2(targetPos.x - lastFloorQueryPos.x,
|
||||
targetPos.y - lastFloorQueryPos.y));
|
||||
float dx2D = targetPos.x - lastFloorQueryPos.x;
|
||||
float dy2D = targetPos.y - lastFloorQueryPos.y;
|
||||
float dist2DSq = dx2D * dx2D + dy2D * dy2D;
|
||||
constexpr float kFloorDistSq = FLOOR_QUERY_DISTANCE_THRESHOLD * FLOOR_QUERY_DISTANCE_THRESHOLD;
|
||||
bool updateFloorCache = (floorQueryFrameCounter++ >= FLOOR_QUERY_FRAME_INTERVAL) ||
|
||||
(dist2D > FLOOR_QUERY_DISTANCE_THRESHOLD);
|
||||
(dist2DSq > kFloorDistSq);
|
||||
|
||||
if (updateFloorCache) {
|
||||
floorQueryFrameCounter = 0;
|
||||
|
|
@ -685,10 +690,12 @@ void CameraController::update(float deltaTime) {
|
|||
{
|
||||
glm::vec3 swimFrom = *followTarget;
|
||||
glm::vec3 swimTo = targetPos;
|
||||
float swimMoveDist = glm::length(swimTo - swimFrom);
|
||||
glm::vec3 swimDelta = swimTo - swimFrom;
|
||||
float swimMoveDistSq = glm::dot(swimDelta, swimDelta);
|
||||
glm::vec3 stepPos = swimFrom;
|
||||
|
||||
if (swimMoveDist > 0.01f) {
|
||||
if (swimMoveDistSq > 1e-4f) {
|
||||
float swimMoveDist = std::sqrt(swimMoveDistSq);
|
||||
float swimStepSize = cachedInsideWMO ? 0.20f : 0.35f;
|
||||
int swimSteps = std::max(1, std::min(8, static_cast<int>(std::ceil(swimMoveDist / swimStepSize))));
|
||||
glm::vec3 stepDelta = (swimTo - swimFrom) / static_cast<float>(swimSteps);
|
||||
|
|
@ -746,7 +753,7 @@ void CameraController::update(float deltaTime) {
|
|||
|
||||
// Forward/back follows camera 3D direction (same as swim)
|
||||
glm::vec3 flyFwd = glm::normalize(forward3D);
|
||||
if (glm::length(flyFwd) < 1e-4f) flyFwd = forward;
|
||||
if (glm::dot(flyFwd, flyFwd) < 1e-8f) flyFwd = forward;
|
||||
glm::vec3 flyMove(0.0f);
|
||||
if (nowForward) flyMove += flyFwd;
|
||||
if (nowBackward) flyMove -= flyFwd;
|
||||
|
|
@ -756,8 +763,9 @@ void CameraController::update(float deltaTime) {
|
|||
bool flyDescend = !uiWantsKeyboard && xDown && mounted_;
|
||||
if (nowJump) flyMove.z += 1.0f;
|
||||
if (flyDescend) flyMove.z -= 1.0f;
|
||||
if (glm::length(flyMove) > 0.001f) {
|
||||
flyMove = glm::normalize(flyMove);
|
||||
float flyMoveLenSq = glm::dot(flyMove, flyMove);
|
||||
if (flyMoveLenSq > 1e-6f) {
|
||||
flyMove *= glm::inversesqrt(flyMoveLenSq);
|
||||
float flyFwdSpeed = (flightSpeedOverride_ > 0.0f && flightSpeedOverride_ < 200.0f
|
||||
&& !std::isnan(flightSpeedOverride_))
|
||||
? flightSpeedOverride_ : speed;
|
||||
|
|
@ -771,8 +779,9 @@ void CameraController::update(float deltaTime) {
|
|||
// Skip all ground physics — go straight to collision/WMO sections
|
||||
} else {
|
||||
|
||||
if (glm::length(movement) > 0.001f) {
|
||||
movement = glm::normalize(movement);
|
||||
float moveLenSq = glm::dot(movement, movement);
|
||||
if (moveLenSq > 1e-6f) {
|
||||
movement *= glm::inversesqrt(moveLenSq);
|
||||
targetPos += movement * speed * physicsDeltaTime;
|
||||
}
|
||||
|
||||
|
|
@ -784,7 +793,7 @@ void CameraController::update(float deltaTime) {
|
|||
float drag = std::exp(-KNOCKBACK_HORIZ_DRAG * physicsDeltaTime);
|
||||
knockbackHorizVel_ *= drag;
|
||||
// Once negligible, clear the flag so collision/grounding work normally.
|
||||
if (glm::length(knockbackHorizVel_) < 0.05f) {
|
||||
if (glm::dot(knockbackHorizVel_, knockbackHorizVel_) < 0.0025f) {
|
||||
knockbackActive_ = false;
|
||||
knockbackHorizVel_ = glm::vec2(0.0f);
|
||||
}
|
||||
|
|
@ -829,8 +838,9 @@ void CameraController::update(float deltaTime) {
|
|||
// Refresh inside-WMO state before collision/grounding so we don't use stale
|
||||
// terrain-first caches while entering enclosed tunnel/building spaces.
|
||||
if (wmoRenderer && !externalFollow_) {
|
||||
const float insideDist = glm::length(targetPos - lastInsideStateCheckPos_);
|
||||
if (++insideStateCheckCounter_ >= 2 || insideDist > 0.35f) {
|
||||
glm::vec3 insideDelta = targetPos - lastInsideStateCheckPos_;
|
||||
float insideDistSq = glm::dot(insideDelta, insideDelta);
|
||||
if (++insideStateCheckCounter_ >= 2 || insideDistSq > 0.1225f) {
|
||||
insideStateCheckCounter_ = 0;
|
||||
lastInsideStateCheckPos_ = targetPos;
|
||||
|
||||
|
|
@ -853,9 +863,11 @@ void CameraController::update(float deltaTime) {
|
|||
{
|
||||
glm::vec3 startPos = *followTarget;
|
||||
glm::vec3 desiredPos = targetPos;
|
||||
float moveDist = glm::length(desiredPos - startPos);
|
||||
glm::vec3 moveDelta = desiredPos - startPos;
|
||||
float moveDistSq = glm::dot(moveDelta, moveDelta);
|
||||
|
||||
if (moveDist > 0.01f) {
|
||||
if (moveDistSq > 1e-4f) {
|
||||
float moveDist = std::sqrt(moveDistSq);
|
||||
// Smaller step size when inside buildings for tighter collision
|
||||
float stepSize = cachedInsideWMO ? 0.20f : 0.35f;
|
||||
int sweepSteps = std::max(1, std::min(8, static_cast<int>(std::ceil(moveDist / stepSize))));
|
||||
|
|
@ -909,9 +921,11 @@ void CameraController::update(float deltaTime) {
|
|||
std::optional<float> centerM2H;
|
||||
{
|
||||
// Collision cache: skip expensive checks if barely moved (15cm threshold)
|
||||
float distMoved = glm::length(glm::vec2(targetPos.x, targetPos.y) -
|
||||
glm::vec2(lastCollisionCheckPos_.x, lastCollisionCheckPos_.y));
|
||||
bool useCached = grounded && hasCachedFloor_ && distMoved < COLLISION_CACHE_DISTANCE;
|
||||
float dmx = targetPos.x - lastCollisionCheckPos_.x;
|
||||
float dmy = targetPos.y - lastCollisionCheckPos_.y;
|
||||
float distMovedSq = dmx * dmx + dmy * dmy;
|
||||
constexpr float kCollisionCacheDistSq = COLLISION_CACHE_DISTANCE * COLLISION_CACHE_DISTANCE;
|
||||
bool useCached = grounded && hasCachedFloor_ && distMovedSq < kCollisionCacheDistSq;
|
||||
if (useCached) {
|
||||
// Never trust cached ground while actively descending or when
|
||||
// vertical drift from cached floor is meaningful.
|
||||
|
|
@ -1371,11 +1385,13 @@ void CameraController::update(float deltaTime) {
|
|||
float mountedOffset = mounted_ ? mountHeightOffset_ : 0.0f;
|
||||
float pivotLift = 0.0f;
|
||||
if (terrainManager && !externalFollow_ && !cachedInsideInteriorWMO) {
|
||||
float moved = glm::length(glm::vec2(targetPos.x - lastPivotLiftQueryPos_.x,
|
||||
targetPos.y - lastPivotLiftQueryPos_.y));
|
||||
float plx = targetPos.x - lastPivotLiftQueryPos_.x;
|
||||
float ply = targetPos.y - lastPivotLiftQueryPos_.y;
|
||||
float movedSq = plx * plx + ply * ply;
|
||||
constexpr float kPivotLiftPosSq = PIVOT_LIFT_POS_THRESHOLD * PIVOT_LIFT_POS_THRESHOLD;
|
||||
float distDelta = std::abs(currentDistance - lastPivotLiftDistance_);
|
||||
bool queryLift = (++pivotLiftQueryCounter_ >= PIVOT_LIFT_QUERY_INTERVAL) ||
|
||||
(moved >= PIVOT_LIFT_POS_THRESHOLD) ||
|
||||
(movedSq >= kPivotLiftPosSq) ||
|
||||
(distDelta >= PIVOT_LIFT_DIST_THRESHOLD);
|
||||
if (queryLift) {
|
||||
pivotLiftQueryCounter_ = 0;
|
||||
|
|
@ -1421,8 +1437,9 @@ void CameraController::update(float deltaTime) {
|
|||
// Limit max zoom when inside a WMO with a ceiling (building interior)
|
||||
// Throttle: only recheck every 10 frames or when position changes >2 units.
|
||||
if (wmoRenderer) {
|
||||
float distFromLastCheck = glm::length(targetPos - lastInsideWMOCheckPos);
|
||||
if (++insideWMOCheckCounter >= 10 || distFromLastCheck > 2.0f) {
|
||||
glm::vec3 wmoCheckDelta = targetPos - lastInsideWMOCheckPos;
|
||||
float distFromLastCheckSq = glm::dot(wmoCheckDelta, wmoCheckDelta);
|
||||
if (++insideWMOCheckCounter >= 10 || distFromLastCheckSq > 4.0f) {
|
||||
wmoRenderer->updateActiveGroup(targetPos.x, targetPos.y, targetPos.z + 1.0f);
|
||||
insideWMOCheckCounter = 0;
|
||||
lastInsideWMOCheckPos = targetPos;
|
||||
|
|
@ -1486,7 +1503,7 @@ void CameraController::update(float deltaTime) {
|
|||
}
|
||||
|
||||
// Smooth camera position to avoid jitter
|
||||
if (glm::length(smoothedCamPos) < 0.01f) {
|
||||
if (glm::dot(smoothedCamPos, smoothedCamPos) < 1e-4f) {
|
||||
smoothedCamPos = actualCam; // Initialize
|
||||
}
|
||||
float camLerp = 1.0f - std::exp(-CAM_SMOOTH_SPEED * deltaTime);
|
||||
|
|
@ -1503,9 +1520,10 @@ void CameraController::update(float deltaTime) {
|
|||
std::optional<float> camWmoH;
|
||||
if (wmoRenderer) {
|
||||
// Skip expensive WMO floor query if camera barely moved
|
||||
float camDelta = glm::length(glm::vec2(smoothedCamPos.x - lastCamFloorQueryPos.x,
|
||||
smoothedCamPos.y - lastCamFloorQueryPos.y));
|
||||
if (camDelta < 0.3f && hasCachedCamFloor) {
|
||||
float cdx = smoothedCamPos.x - lastCamFloorQueryPos.x;
|
||||
float cdy = smoothedCamPos.y - lastCamFloorQueryPos.y;
|
||||
float camDeltaSq = cdx * cdx + cdy * cdy;
|
||||
if (camDeltaSq < 0.09f && hasCachedCamFloor) {
|
||||
camWmoH = cachedCamWmoFloor;
|
||||
} else {
|
||||
float camFloorProbeZ = smoothedCamPos.z;
|
||||
|
|
@ -1618,8 +1636,9 @@ void CameraController::update(float deltaTime) {
|
|||
float waterSurfaceCamZ = waterH ? (*waterH - WATER_SURFACE_OFFSET + eyeHeight) : newPos.z;
|
||||
bool diveIntent = nowForward && (forward3D.z < -0.28f);
|
||||
|
||||
if (glm::length(movement) > 0.001f) {
|
||||
movement = glm::normalize(movement);
|
||||
float movLenSq = glm::dot(movement, movement);
|
||||
if (movLenSq > 1e-6f) {
|
||||
movement *= glm::inversesqrt(movLenSq);
|
||||
newPos += movement * swimSpeed * physicsDeltaTime;
|
||||
}
|
||||
|
||||
|
|
@ -1652,8 +1671,9 @@ void CameraController::update(float deltaTime) {
|
|||
} else {
|
||||
swimming = false;
|
||||
|
||||
if (glm::length(movement) > 0.001f) {
|
||||
movement = glm::normalize(movement);
|
||||
float movLenSq2 = glm::dot(movement, movement);
|
||||
if (movLenSq2 > 1e-6f) {
|
||||
movement *= glm::inversesqrt(movLenSq2);
|
||||
newPos += movement * speed * physicsDeltaTime;
|
||||
}
|
||||
|
||||
|
|
@ -1680,9 +1700,11 @@ void CameraController::update(float deltaTime) {
|
|||
if (wmoRenderer) {
|
||||
glm::vec3 startFeet = camera->getPosition() - glm::vec3(0, 0, eyeHeight);
|
||||
glm::vec3 desiredFeet = newPos - glm::vec3(0, 0, eyeHeight);
|
||||
float moveDist = glm::length(desiredFeet - startFeet);
|
||||
glm::vec3 feetDelta = desiredFeet - startFeet;
|
||||
float moveDistSq2 = glm::dot(feetDelta, feetDelta);
|
||||
|
||||
if (moveDist > 0.01f) {
|
||||
if (moveDistSq2 > 1e-4f) {
|
||||
float moveDist = std::sqrt(moveDistSq2);
|
||||
float stepSize = cachedInsideWMO ? 0.20f : 0.35f;
|
||||
int sweepSteps = std::max(1, std::min(8, static_cast<int>(std::ceil(moveDist / stepSize))));
|
||||
glm::vec3 stepPos = startFeet;
|
||||
|
|
|
|||
|
|
@ -319,8 +319,11 @@ void CharacterRenderer::shutdown() {
|
|||
" models=", models.size(), " override=", (void*)renderPassOverride_);
|
||||
|
||||
// Wait for any in-flight background normal map generation threads
|
||||
while (pendingNormalMapCount_.load(std::memory_order_relaxed) > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(normalMapResultsMutex_);
|
||||
normalMapDoneCV_.wait(lock, [this] {
|
||||
return pendingNormalMapCount_.load(std::memory_order_acquire) == 0;
|
||||
});
|
||||
}
|
||||
|
||||
vkDeviceWaitIdle(vkCtx_->getDevice());
|
||||
|
|
@ -407,8 +410,11 @@ void CharacterRenderer::clear() {
|
|||
" models=", models.size());
|
||||
|
||||
// Wait for any in-flight background normal map generation threads
|
||||
while (pendingNormalMapCount_.load(std::memory_order_relaxed) > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(normalMapResultsMutex_);
|
||||
normalMapDoneCV_.wait(lock, [this] {
|
||||
return pendingNormalMapCount_.load(std::memory_order_acquire) == 0;
|
||||
});
|
||||
}
|
||||
// Discard any completed results that haven't been uploaded
|
||||
{
|
||||
|
|
@ -731,7 +737,9 @@ VkTexture* CharacterRenderer::loadTexture(const std::string& path) {
|
|||
std::lock_guard<std::mutex> lock(self->normalMapResultsMutex_);
|
||||
self->completedNormalMaps_.push_back(std::move(result));
|
||||
}
|
||||
self->pendingNormalMapCount_.fetch_sub(1, std::memory_order_relaxed);
|
||||
if (self->pendingNormalMapCount_.fetch_sub(1, std::memory_order_release) == 1) {
|
||||
self->normalMapDoneCV_.notify_one();
|
||||
}
|
||||
}).detach();
|
||||
e.normalMapPending = true;
|
||||
}
|
||||
|
|
@ -2825,11 +2833,12 @@ void CharacterRenderer::moveInstanceTo(uint32_t instanceId, const glm::vec3& des
|
|||
return 0;
|
||||
};
|
||||
|
||||
float planarDist = glm::length(glm::vec2(destination.x - inst.position.x,
|
||||
destination.y - inst.position.y));
|
||||
float pdx = destination.x - inst.position.x;
|
||||
float pdy = destination.y - inst.position.y;
|
||||
float planarDistSq = pdx * pdx + pdy * pdy;
|
||||
bool synthesizedDuration = false;
|
||||
if (durationSeconds <= 0.0f) {
|
||||
if (planarDist < 0.01f) {
|
||||
if (planarDistSq < 1e-4f) {
|
||||
// Stop at current location.
|
||||
inst.position = destination;
|
||||
inst.isMoving = false;
|
||||
|
|
@ -2840,7 +2849,7 @@ void CharacterRenderer::moveInstanceTo(uint32_t instanceId, const glm::vec3& des
|
|||
}
|
||||
// Some cores send movement-only deltas without spline duration.
|
||||
// Synthesize a tiny duration so movement anim/rotation still updates.
|
||||
durationSeconds = std::clamp(planarDist / 7.0f, 0.05f, 0.20f);
|
||||
durationSeconds = std::clamp(std::sqrt(planarDistSq) / 7.0f, 0.05f, 0.20f);
|
||||
synthesizedDuration = true;
|
||||
}
|
||||
|
||||
|
|
@ -2852,14 +2861,14 @@ void CharacterRenderer::moveInstanceTo(uint32_t instanceId, const glm::vec3& des
|
|||
|
||||
// Face toward destination (yaw around Z axis since Z is up)
|
||||
glm::vec3 dir = destination - inst.position;
|
||||
if (glm::length(glm::vec2(dir.x, dir.y)) > 0.001f) {
|
||||
if (dir.x * dir.x + dir.y * dir.y > 1e-6f) {
|
||||
float angle = std::atan2(dir.y, dir.x);
|
||||
inst.rotation.z = angle;
|
||||
}
|
||||
|
||||
// Play movement animation while moving.
|
||||
// Prefer run only when speed is clearly above normal walk pace.
|
||||
float moveSpeed = planarDist / std::max(durationSeconds, 0.001f);
|
||||
float moveSpeed = std::sqrt(planarDistSq) / std::max(durationSeconds, 0.001f);
|
||||
bool preferRun = (!synthesizedDuration && moveSpeed >= 4.5f);
|
||||
uint32_t moveAnim = pickMoveAnim(preferRun);
|
||||
if (moveAnim != 0 && inst.currentAnimationId != moveAnim) {
|
||||
|
|
|
|||
|
|
@ -449,8 +449,9 @@ void ChargeEffect::emit(const glm::vec3& position, const glm::vec3& direction) {
|
|||
}
|
||||
|
||||
// Only add a new trail point if we've moved enough
|
||||
float dist = glm::length(position - lastEmitPos_);
|
||||
if (dist >= TRAIL_SPAWN_DIST || trail_.empty()) {
|
||||
glm::vec3 emitDelta = position - lastEmitPos_;
|
||||
float distSq = glm::dot(emitDelta, emitDelta);
|
||||
if (distSq >= TRAIL_SPAWN_DIST * TRAIL_SPAWN_DIST || trail_.empty()) {
|
||||
// Ribbon is vertical: side vector points straight up
|
||||
glm::vec3 side = glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
|
||||
|
|
@ -466,9 +467,10 @@ void ChargeEffect::emit(const glm::vec3& position, const glm::vec3& direction) {
|
|||
|
||||
// Spawn dust puffs at feet
|
||||
glm::vec3 horizDir = glm::vec3(direction.x, direction.y, 0.0f);
|
||||
float horizLen = glm::length(horizDir);
|
||||
if (horizLen < 0.001f) return;
|
||||
glm::vec3 backDir = -horizDir / horizLen;
|
||||
float horizLenSq = glm::dot(horizDir, horizDir);
|
||||
if (horizLenSq < 1e-6f) return;
|
||||
float invHorizLen = glm::inversesqrt(horizLenSq);
|
||||
glm::vec3 backDir = -horizDir * invHorizLen;
|
||||
glm::vec3 sideDir = glm::vec3(-backDir.y, backDir.x, 0.0f);
|
||||
|
||||
dustAccum_ += 30.0f * 0.016f;
|
||||
|
|
|
|||
|
|
@ -63,10 +63,11 @@ void Frustum::extractFromMatrix(const glm::mat4& vp) {
|
|||
}
|
||||
|
||||
void Frustum::normalizePlane(Plane& plane) {
|
||||
float length = glm::length(plane.normal);
|
||||
if (length > 0.0001f) {
|
||||
plane.normal /= length;
|
||||
plane.distance /= length;
|
||||
float lenSq = glm::dot(plane.normal, plane.normal);
|
||||
if (lenSq > 0.00000001f) {
|
||||
float invLen = glm::inversesqrt(lenSq);
|
||||
plane.normal *= invLen;
|
||||
plane.distance *= invLen;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -301,10 +301,11 @@ void LensFlare::render(VkCommandBuffer cmd, const Camera& camera, const glm::vec
|
|||
// Sun billboard rendering is sky-locked (view translation removed), so anchor
|
||||
// flare projection to camera position along sun direction to avoid parallax drift.
|
||||
glm::vec3 sunDir = sunPosition;
|
||||
if (glm::length(sunDir) < 0.0001f) {
|
||||
float sunDirLenSq = glm::dot(sunDir, sunDir);
|
||||
if (sunDirLenSq < 1e-8f) {
|
||||
return;
|
||||
}
|
||||
sunDir = glm::normalize(sunDir);
|
||||
sunDir *= glm::inversesqrt(sunDirLenSq);
|
||||
glm::vec3 anchoredSunPos = camera.getPosition() + sunDir * 800.0f;
|
||||
|
||||
// Calculate sun visibility
|
||||
|
|
|
|||
|
|
@ -305,8 +305,9 @@ void LightingManager::update(const glm::vec3& playerPos, uint32_t mapId,
|
|||
}
|
||||
|
||||
// Normalize blended direction
|
||||
if (glm::length(blendedDir) > 0.001f) {
|
||||
newParams.directionalDir = glm::normalize(blendedDir);
|
||||
float blendedDirLenSq = glm::dot(blendedDir, blendedDir);
|
||||
if (blendedDirLenSq > 1e-6f) {
|
||||
newParams.directionalDir = blendedDir * glm::inversesqrt(blendedDirLenSq);
|
||||
} else {
|
||||
// Fallback if all directions cancelled out
|
||||
newParams.directionalDir = glm::vec3(0.3f, -0.7f, 0.6f);
|
||||
|
|
@ -371,14 +372,16 @@ std::vector<LightingManager::WeightedVolume> LightingManager::findLightVolumes(c
|
|||
for (const auto& volume : volumes) {
|
||||
// Apply coordinate scaling (test with 1.0f, try 36.0f if distances are off)
|
||||
glm::vec3 scaledPos = volume.position * LIGHT_COORD_SCALE;
|
||||
float dist = glm::length(playerPos - scaledPos);
|
||||
glm::vec3 toPlayer = playerPos - scaledPos;
|
||||
float distSq = glm::dot(toPlayer, toPlayer);
|
||||
|
||||
float weight = 0.0f;
|
||||
if (dist <= volume.innerRadius) {
|
||||
if (distSq <= volume.innerRadius * volume.innerRadius) {
|
||||
// Inside inner radius: full weight
|
||||
weight = 1.0f;
|
||||
} else if (dist < volume.outerRadius) {
|
||||
// Between inner and outer: fade out with smoothstep
|
||||
} else if (distSq < volume.outerRadius * volume.outerRadius) {
|
||||
// Between inner and outer: fade out with smoothstep (sqrt needed for interpolation)
|
||||
float dist = std::sqrt(distSq);
|
||||
float t = (dist - volume.innerRadius) / (volume.outerRadius - volume.innerRadius);
|
||||
t = glm::clamp(t, 0.0f, 1.0f);
|
||||
weight = 1.0f - (t * t * (3.0f - 2.0f * t)); // Smoothstep
|
||||
|
|
@ -389,7 +392,7 @@ std::vector<LightingManager::WeightedVolume> LightingManager::findLightVolumes(c
|
|||
|
||||
// Debug logging for first few volumes
|
||||
if (weighted.size() <= 3) {
|
||||
LOG_INFO("Light volume ", volume.lightId, ": dist=", dist,
|
||||
LOG_INFO("Light volume ", volume.lightId, ": distSq=", distSq,
|
||||
" inner=", volume.innerRadius, " outer=", volume.outerRadius,
|
||||
" weight=", weight);
|
||||
}
|
||||
|
|
@ -400,15 +403,20 @@ std::vector<LightingManager::WeightedVolume> LightingManager::findLightVolumes(c
|
|||
return {};
|
||||
}
|
||||
|
||||
// Sort by weight descending
|
||||
std::sort(weighted.begin(), weighted.end(),
|
||||
[](const WeightedVolume& a, const WeightedVolume& b) {
|
||||
return a.weight > b.weight;
|
||||
});
|
||||
|
||||
// Keep top N volumes
|
||||
// Keep top N volumes by weight (partial sort is O(n) vs O(n log n) for full sort)
|
||||
if (weighted.size() > MAX_BLEND_VOLUMES) {
|
||||
std::partial_sort(weighted.begin(),
|
||||
weighted.begin() + MAX_BLEND_VOLUMES,
|
||||
weighted.end(),
|
||||
[](const WeightedVolume& a, const WeightedVolume& b) {
|
||||
return a.weight > b.weight;
|
||||
});
|
||||
weighted.resize(MAX_BLEND_VOLUMES);
|
||||
} else {
|
||||
std::sort(weighted.begin(), weighted.end(),
|
||||
[](const WeightedVolume& a, const WeightedVolume& b) {
|
||||
return a.weight > b.weight;
|
||||
});
|
||||
}
|
||||
|
||||
// Normalize weights to sum to 1.0
|
||||
|
|
|
|||
|
|
@ -947,6 +947,9 @@ void M2ModelGPU::CollisionMesh::getFloorTrisInRange(
|
|||
int cxMax = std::clamp(static_cast<int>((maxX - gridOrigin.x) / CELL_SIZE), 0, gridCellsX - 1);
|
||||
int cyMin = std::clamp(static_cast<int>((minY - gridOrigin.y) / CELL_SIZE), 0, gridCellsY - 1);
|
||||
int cyMax = std::clamp(static_cast<int>((maxY - gridOrigin.y) / CELL_SIZE), 0, gridCellsY - 1);
|
||||
const size_t cellCount = static_cast<size_t>(cxMax - cxMin + 1) *
|
||||
static_cast<size_t>(cyMax - cyMin + 1);
|
||||
out.reserve(cellCount * 8);
|
||||
for (int cy = cyMin; cy <= cyMax; cy++) {
|
||||
for (int cx = cxMin; cx <= cxMax; cx++) {
|
||||
const auto& cell = cellFloorTris[cy * gridCellsX + cx];
|
||||
|
|
@ -966,6 +969,9 @@ void M2ModelGPU::CollisionMesh::getWallTrisInRange(
|
|||
int cxMax = std::clamp(static_cast<int>((maxX - gridOrigin.x) / CELL_SIZE), 0, gridCellsX - 1);
|
||||
int cyMin = std::clamp(static_cast<int>((minY - gridOrigin.y) / CELL_SIZE), 0, gridCellsY - 1);
|
||||
int cyMax = std::clamp(static_cast<int>((maxY - gridOrigin.y) / CELL_SIZE), 0, gridCellsY - 1);
|
||||
const size_t cellCount = static_cast<size_t>(cxMax - cxMin + 1) *
|
||||
static_cast<size_t>(cyMax - cyMin + 1);
|
||||
out.reserve(cellCount * 8);
|
||||
for (int cy = cyMin; cy <= cyMax; cy++) {
|
||||
for (int cx = cxMin; cx <= cxMax; cx++) {
|
||||
const auto& cell = cellWallTris[cy * gridCellsX + cx];
|
||||
|
|
@ -3227,8 +3233,8 @@ void M2Renderer::emitParticles(M2Instance& inst, const M2ModelGPU& gpu, float dt
|
|||
dir.x += distN(particleRng_) * hRange;
|
||||
dir.y += distN(particleRng_) * hRange;
|
||||
dir.z += distN(particleRng_) * vRange;
|
||||
float len = glm::length(dir);
|
||||
if (len > 0.001f) dir /= len;
|
||||
float lenSq = glm::dot(dir, dir);
|
||||
if (lenSq > 0.001f * 0.001f) dir *= glm::inversesqrt(lenSq);
|
||||
|
||||
// Transform direction by bone + model orientation (rotation only)
|
||||
glm::mat3 rotMat = glm::mat3(inst.modelMatrix * boneXform);
|
||||
|
|
@ -4715,7 +4721,7 @@ float M2Renderer::raycastBoundingBoxes(const glm::vec3& origin, const glm::vec3&
|
|||
getTightCollisionBounds(model, localMin, localMax);
|
||||
// Skip tiny doodads for camera occlusion; they cause jitter and false hits.
|
||||
glm::vec3 extents = (localMax - localMin) * instance.scale;
|
||||
if (glm::length(extents) < 0.75f) continue;
|
||||
if (glm::dot(extents, extents) < 0.5625f) continue;
|
||||
|
||||
glm::vec3 localOrigin = glm::vec3(instance.invModelMatrix * glm::vec4(origin, 1.0f));
|
||||
glm::vec3 localDir = glm::normalize(glm::vec3(instance.invModelMatrix * glm::vec4(direction, 0.0f)));
|
||||
|
|
|
|||
|
|
@ -421,10 +421,11 @@ void Minimap::compositePass(VkCommandBuffer cmd, const glm::vec3& centerWorldPos
|
|||
const auto now = std::chrono::steady_clock::now();
|
||||
bool needsRefresh = !hasCachedFrame;
|
||||
if (!needsRefresh) {
|
||||
float moved = glm::length(glm::vec2(centerWorldPos.x - lastUpdatePos.x,
|
||||
centerWorldPos.y - lastUpdatePos.y));
|
||||
float mdx = centerWorldPos.x - lastUpdatePos.x;
|
||||
float mdy = centerWorldPos.y - lastUpdatePos.y;
|
||||
float movedSq = mdx * mdx + mdy * mdy;
|
||||
float elapsed = std::chrono::duration<float>(now - lastUpdateTime).count();
|
||||
needsRefresh = (moved >= updateDistance) || (elapsed >= updateIntervalSec);
|
||||
needsRefresh = (movedSq >= updateDistance * updateDistance) || (elapsed >= updateIntervalSec);
|
||||
}
|
||||
|
||||
// Also refresh if player crossed a tile boundary
|
||||
|
|
|
|||
|
|
@ -3242,7 +3242,7 @@ void Renderer::update(float deltaTime) {
|
|||
} else if (inCombat_ && targetPosition && !emoteActive && !isMounted()) {
|
||||
// Face target when in combat and idle
|
||||
glm::vec3 toTarget = *targetPosition - characterPosition;
|
||||
if (glm::length(glm::vec2(toTarget.x, toTarget.y)) > 0.1f) {
|
||||
if (toTarget.x * toTarget.x + toTarget.y * toTarget.y > 0.01f) {
|
||||
float targetYaw = glm::degrees(std::atan2(toTarget.y, toTarget.x));
|
||||
float diff = targetYaw - characterYaw;
|
||||
while (diff > 180.0f) diff -= 360.0f;
|
||||
|
|
@ -6222,8 +6222,9 @@ glm::mat4 Renderer::computeLightSpaceMatrix() {
|
|||
glm::vec3 sunDir = glm::normalize(glm::vec3(-0.3f, -0.7f, -0.6f));
|
||||
if (lightingManager) {
|
||||
const auto& lighting = lightingManager->getLightingParams();
|
||||
if (glm::length(lighting.directionalDir) > 0.001f) {
|
||||
sunDir = glm::normalize(-lighting.directionalDir);
|
||||
float ldirLenSq = glm::dot(lighting.directionalDir, lighting.directionalDir);
|
||||
if (ldirLenSq > 1e-6f) {
|
||||
sunDir = -lighting.directionalDir * glm::inversesqrt(ldirLenSq);
|
||||
}
|
||||
}
|
||||
// Shadow camera expects light rays pointing downward in render space (Z up).
|
||||
|
|
|
|||
|
|
@ -156,8 +156,9 @@ void SkySystem::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet,
|
|||
}
|
||||
|
||||
glm::vec3 SkySystem::getSunPosition(const SkyParams& params) const {
|
||||
glm::vec3 dir = glm::normalize(params.directionalDir);
|
||||
if (glm::length(dir) < 0.0001f) {
|
||||
float dirLenSq = glm::dot(params.directionalDir, params.directionalDir);
|
||||
glm::vec3 dir = (dirLenSq > 1e-8f) ? params.directionalDir * glm::inversesqrt(dirLenSq) : glm::vec3(0.0f);
|
||||
if (dirLenSq < 1e-8f) {
|
||||
dir = glm::vec3(0.0f, 0.0f, -1.0f);
|
||||
}
|
||||
glm::vec3 sunDir = -dir;
|
||||
|
|
|
|||
|
|
@ -542,7 +542,7 @@ void SwimEffects::update(const Camera& camera, const CameraController& cc,
|
|||
// Compute movement direction from camera yaw
|
||||
float yawRad = glm::radians(cc.getYaw());
|
||||
glm::vec3 moveDir(std::cos(yawRad), std::sin(yawRad), 0.0f);
|
||||
if (glm::length(glm::vec2(moveDir)) > 0.001f) {
|
||||
if (moveDir.x * moveDir.x + moveDir.y * moveDir.y > 1e-6f) {
|
||||
moveDir = glm::normalize(moveDir);
|
||||
}
|
||||
|
||||
|
|
@ -676,6 +676,7 @@ void SwimEffects::update(const Camera& camera, const CameraController& cc,
|
|||
|
||||
// --- Build vertex data ---
|
||||
rippleVertexData.clear();
|
||||
rippleVertexData.reserve(ripples.size() * 5);
|
||||
for (const auto& p : ripples) {
|
||||
rippleVertexData.push_back(p.position.x);
|
||||
rippleVertexData.push_back(p.position.y);
|
||||
|
|
@ -685,6 +686,7 @@ void SwimEffects::update(const Camera& camera, const CameraController& cc,
|
|||
}
|
||||
|
||||
bubbleVertexData.clear();
|
||||
bubbleVertexData.reserve(bubbles.size() * 5);
|
||||
for (const auto& p : bubbles) {
|
||||
bubbleVertexData.push_back(p.position.x);
|
||||
bubbleVertexData.push_back(p.position.y);
|
||||
|
|
@ -694,6 +696,7 @@ void SwimEffects::update(const Camera& camera, const CameraController& cc,
|
|||
}
|
||||
|
||||
insectVertexData.clear();
|
||||
insectVertexData.reserve(insects.size() * 5);
|
||||
for (const auto& p : insects) {
|
||||
insectVertexData.push_back(p.position.x);
|
||||
insectVertexData.push_back(p.position.y);
|
||||
|
|
|
|||
|
|
@ -922,7 +922,8 @@ void WaterRenderer::loadFromWMO([[maybe_unused]] const pipeline::WMOLiquid& liqu
|
|||
float stepXLen = glm::length(surface.stepX);
|
||||
float stepYLen = glm::length(surface.stepY);
|
||||
glm::vec3 planeN = glm::cross(surface.stepX, surface.stepY);
|
||||
float nz = (glm::length(planeN) > 1e-4f) ? std::abs(glm::normalize(planeN).z) : 0.0f;
|
||||
float planeNLenSq = glm::dot(planeN, planeN);
|
||||
float nz = (planeNLenSq > 1e-8f) ? std::abs(planeN.z * glm::inversesqrt(planeNLenSq)) : 0.0f;
|
||||
float spanX = stepXLen * static_cast<float>(surface.width);
|
||||
float spanY = stepYLen * static_cast<float>(surface.height);
|
||||
if (stepXLen < 0.2f || stepXLen > 12.0f ||
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ void Weather::update(const Camera& camera, float deltaTime) {
|
|||
|
||||
// Update position buffer
|
||||
particlePositions.clear();
|
||||
particlePositions.reserve(particles.size());
|
||||
for (const auto& particle : particles) {
|
||||
particlePositions.push_back(particle.position);
|
||||
}
|
||||
|
|
@ -232,9 +233,10 @@ void Weather::updateParticle(Particle& particle, const Camera& camera, float del
|
|||
|
||||
// Reset if lifetime exceeded or too far from camera
|
||||
glm::vec3 cameraPos = camera.getPosition();
|
||||
float distance = glm::length(particle.position - cameraPos);
|
||||
glm::vec3 toCamera = particle.position - cameraPos;
|
||||
float distSq = glm::dot(toCamera, toCamera);
|
||||
|
||||
if (particle.lifetime >= particle.maxLifetime || distance > SPAWN_VOLUME_SIZE ||
|
||||
if (particle.lifetime >= particle.maxLifetime || distSq > SPAWN_VOLUME_SIZE * SPAWN_VOLUME_SIZE ||
|
||||
particle.position.y < cameraPos.y - 20.0f) {
|
||||
// Respawn at top
|
||||
particle.position = getRandomPosition(cameraPos);
|
||||
|
|
|
|||
|
|
@ -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