mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-23 07:40:14 +00:00
Fix M2 texture loading, /unstuckgy, and WMO floor detection
- Add mutex to AssetManager::loadTexture/loadDBC/fileExists to prevent StormLib thread-safety races that silently fail texture reads; stop caching texture load failures so transient errors are retried. - Replace /unstuckgy DBC lookup (which used wrong coordinate transform) with hardcoded safe locations per map. - Widen WMO floor raycast from single grid cell to ±1 unit range query to catch bridge/walkway triangles at cell boundaries. - Tighten swept collision hit threshold (0.5 → 0.15) and grid query margin (2.5 → 1.5) to prevent false-positive wall pushes. - Tighten post-wall-push Z snap lower bound (-1.0 → -0.3) to prevent gradual floor sinking.
This commit is contained in:
parent
387cc5ddf4
commit
046d4615ea
5 changed files with 43 additions and 48 deletions
|
|
@ -613,42 +613,22 @@ void Application::setupUICallbacks() {
|
|||
auto* ft = cc->getFollowTargetMutable();
|
||||
if (!ft) return;
|
||||
|
||||
auto wsl = assetManager->loadDBC("WorldSafeLocs.dbc");
|
||||
if (!wsl || !wsl->isLoaded()) {
|
||||
LOG_WARNING("WorldSafeLocs.dbc not available for /unstuckgy");
|
||||
return;
|
||||
}
|
||||
|
||||
// Use current map and position.
|
||||
// Hardcoded safe locations per map (canonical WoW coords)
|
||||
uint32_t mapId = gameHandler ? gameHandler->getCurrentMapId() : 0;
|
||||
glm::vec3 cur = *ft;
|
||||
float bestDist2 = std::numeric_limits<float>::max();
|
||||
glm::vec3 bestPos = cur;
|
||||
|
||||
for (uint32_t i = 0; i < wsl->getRecordCount(); i++) {
|
||||
uint32_t recMap = wsl->getUInt32(i, 1);
|
||||
if (recMap != mapId) continue;
|
||||
float x = wsl->getFloat(i, 2);
|
||||
float y = wsl->getFloat(i, 3);
|
||||
float z = wsl->getFloat(i, 4);
|
||||
glm::vec3 glPos = core::coords::adtToWorld(x, y, z);
|
||||
float dx = glPos.x - cur.x;
|
||||
float dy = glPos.y - cur.y;
|
||||
float dz = glPos.z - cur.z;
|
||||
float d2 = dx*dx + dy*dy + dz*dz;
|
||||
if (d2 < bestDist2) {
|
||||
bestDist2 = d2;
|
||||
bestPos = glPos;
|
||||
}
|
||||
glm::vec3 safeCanonical;
|
||||
switch (mapId) {
|
||||
case 0: safeCanonical = glm::vec3(-8833.38f, 628.63f, 94.0f); break; // Stormwind Trade District
|
||||
case 1: safeCanonical = glm::vec3(1629.36f, -4373.34f, 31.2f); break; // Orgrimmar
|
||||
case 530: safeCanonical = glm::vec3(-3961.64f, -13931.2f, 100.6f); break; // Shattrath
|
||||
case 571: safeCanonical = glm::vec3(5804.14f, 624.77f, 647.8f); break; // Dalaran
|
||||
default:
|
||||
LOG_WARNING("No hardcoded safe location for map ", mapId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bestDist2 == std::numeric_limits<float>::max()) {
|
||||
LOG_WARNING("No graveyard found on map ", mapId);
|
||||
return;
|
||||
}
|
||||
|
||||
*ft = bestPos;
|
||||
cc->setDefaultSpawn(bestPos, cc->getYaw(), cc->getPitch());
|
||||
glm::vec3 safePos = core::coords::canonicalToRender(safeCanonical);
|
||||
*ft = safePos;
|
||||
cc->setDefaultSpawn(safePos, cc->getYaw(), cc->getPitch());
|
||||
cc->reset();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -54,8 +54,12 @@ BLPImage AssetManager::loadTexture(const std::string& path) {
|
|||
|
||||
LOG_DEBUG("Loading texture: ", normalizedPath);
|
||||
|
||||
// Read BLP file from MPQ
|
||||
std::vector<uint8_t> blpData = mpqManager.readFile(normalizedPath);
|
||||
// Read BLP file from MPQ (must hold readMutex — StormLib is not thread-safe)
|
||||
std::vector<uint8_t> blpData;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(readMutex);
|
||||
blpData = mpqManager.readFile(normalizedPath);
|
||||
}
|
||||
if (blpData.empty()) {
|
||||
LOG_WARNING("Texture not found: ", normalizedPath);
|
||||
return BLPImage();
|
||||
|
|
@ -90,8 +94,12 @@ std::shared_ptr<DBCFile> AssetManager::loadDBC(const std::string& name) {
|
|||
// Construct DBC path (DBFilesClient directory)
|
||||
std::string dbcPath = "DBFilesClient\\" + name;
|
||||
|
||||
// Read DBC file from MPQ
|
||||
std::vector<uint8_t> dbcData = mpqManager.readFile(dbcPath);
|
||||
// Read DBC file from MPQ (must hold readMutex — StormLib is not thread-safe)
|
||||
std::vector<uint8_t> dbcData;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(readMutex);
|
||||
dbcData = mpqManager.readFile(dbcPath);
|
||||
}
|
||||
if (dbcData.empty()) {
|
||||
LOG_WARNING("DBC not found: ", dbcPath);
|
||||
return nullptr;
|
||||
|
|
@ -124,6 +132,7 @@ bool AssetManager::fileExists(const std::string& path) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(readMutex);
|
||||
return mpqManager.fileExists(normalizePath(path));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -481,7 +481,7 @@ void CameraController::update(float deltaTime) {
|
|||
candidate.y = adjusted.y;
|
||||
// Snap Z to floor at adjusted position to prevent fall-through
|
||||
auto adjFloor = wmoRenderer->getFloorHeight(adjusted.x, adjusted.y, feetZ + 2.5f);
|
||||
if (adjFloor && *adjFloor >= feetZ - 1.0f && *adjFloor <= feetZ + 1.6f) {
|
||||
if (adjFloor && *adjFloor >= feetZ - 0.3f && *adjFloor <= feetZ + 1.6f) {
|
||||
candidate.z = *adjFloor;
|
||||
}
|
||||
} else if (floorH && *floorH > candidate.z) {
|
||||
|
|
|
|||
|
|
@ -2307,7 +2307,8 @@ GLuint M2Renderer::loadTexture(const std::string& path) {
|
|||
pipeline::BLPImage blp = assetManager->loadTexture(path);
|
||||
if (!blp.isValid()) {
|
||||
LOG_WARNING("M2: Failed to load texture: ", path);
|
||||
textureCache[path] = whiteTexture;
|
||||
// Don't cache failures — transient StormLib thread contention can
|
||||
// cause reads to fail; next loadModel call will retry.
|
||||
return whiteTexture;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1736,10 +1736,15 @@ std::optional<float> WMORenderer::getFloorHeight(float glX, float glY, float glZ
|
|||
const auto& verts = group.collisionVertices;
|
||||
const auto& indices = group.collisionIndices;
|
||||
|
||||
// Use spatial grid to only test triangles near the query XY
|
||||
const auto* cellTris = group.getTrianglesAtLocal(localOrigin.x, localOrigin.y);
|
||||
if (cellTris) {
|
||||
for (uint32_t triStart : *cellTris) {
|
||||
// Use spatial grid to test triangles near the query XY.
|
||||
// Query a small range (±1 unit) to catch floor triangles at cell boundaries
|
||||
// (bridges, narrow walkways whose triangles may sit in adjacent cells).
|
||||
group.getTrianglesInRange(
|
||||
localOrigin.x - 1.0f, localOrigin.y - 1.0f,
|
||||
localOrigin.x + 1.0f, localOrigin.y + 1.0f,
|
||||
wallTriScratch);
|
||||
{
|
||||
for (uint32_t triStart : wallTriScratch) {
|
||||
const glm::vec3& v0 = verts[indices[triStart]];
|
||||
const glm::vec3& v1 = verts[indices[triStart + 1]];
|
||||
const glm::vec3& v2 = verts[indices[triStart + 2]];
|
||||
|
|
@ -1859,10 +1864,10 @@ bool WMORenderer::checkWallCollision(const glm::vec3& from, const glm::vec3& to,
|
|||
const auto& indices = group.collisionIndices;
|
||||
|
||||
// Use spatial grid: query range covering the movement segment + player radius
|
||||
float rangeMinX = std::min(localFrom.x, localTo.x) - PLAYER_RADIUS - 2.5f;
|
||||
float rangeMinY = std::min(localFrom.y, localTo.y) - PLAYER_RADIUS - 2.5f;
|
||||
float rangeMaxX = std::max(localFrom.x, localTo.x) + PLAYER_RADIUS + 2.5f;
|
||||
float rangeMaxY = std::max(localFrom.y, localTo.y) + PLAYER_RADIUS + 2.5f;
|
||||
float rangeMinX = std::min(localFrom.x, localTo.x) - PLAYER_RADIUS - 1.5f;
|
||||
float rangeMinY = std::min(localFrom.y, localTo.y) - PLAYER_RADIUS - 1.5f;
|
||||
float rangeMaxX = std::max(localFrom.x, localTo.x) + PLAYER_RADIUS + 1.5f;
|
||||
float rangeMaxY = std::max(localFrom.y, localTo.y) + PLAYER_RADIUS + 1.5f;
|
||||
group.getTrianglesInRange(rangeMinX, rangeMinY, rangeMaxX, rangeMaxY, wallTriScratch);
|
||||
|
||||
for (uint32_t triStart : wallTriScratch) {
|
||||
|
|
@ -1913,7 +1918,7 @@ bool WMORenderer::checkWallCollision(const glm::vec3& from, const glm::vec3& to,
|
|||
glm::vec3 hitPoint = localFrom + (localTo - localFrom) * tHit;
|
||||
glm::vec3 hitClosest = closestPointOnTriangle(hitPoint, v0, v1, v2);
|
||||
float hitErrSq = glm::dot(hitClosest - hitPoint, hitClosest - hitPoint);
|
||||
if (hitErrSq <= 0.5f * 0.5f) {
|
||||
if (hitErrSq <= 0.15f * 0.15f) {
|
||||
float side = fromDist > 0.0f ? 1.0f : -1.0f;
|
||||
glm::vec3 safeLocal = hitPoint + normal * side * (PLAYER_RADIUS + 0.05f);
|
||||
glm::vec3 safeWorld = glm::vec3(instance.modelMatrix * glm::vec4(safeLocal, 1.0f));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue