Improve targeting, minimap, and bridge collisions

This commit is contained in:
Kelsi 2026-02-07 20:51:53 -08:00
parent a7c0b4320b
commit 38c9fdad6b
10 changed files with 142 additions and 24 deletions

View file

@ -668,9 +668,15 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) {
(lowerName.find("stormwindplanter") != std::string::npos) ||
(lowerName.find("stormwindwindowplanter") != std::string::npos);
bool lowPlatformShape = (horiz > 1.8f && vert > 0.2f && vert < 1.8f);
bool bridgeName =
(lowerName.find("bridge") != std::string::npos) ||
(lowerName.find("plank") != std::string::npos) ||
(lowerName.find("walkway") != std::string::npos);
gpuModel.collisionSteppedLowPlatform = (!gpuModel.collisionSteppedFountain) &&
(knownStormwindPlanter ||
bridgeName ||
(likelyCurbName && (lowPlatformShape || lowWideShape)));
gpuModel.collisionBridge = bridgeName;
bool isPlanter = (lowerName.find("planter") != std::string::npos);
gpuModel.collisionPlanter = isPlanter;
@ -702,6 +708,7 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) {
(lowerName.find("vine") != std::string::npos) ||
(lowerName.find("lily") != std::string::npos) ||
(lowerName.find("weed") != std::string::npos) ||
(lowerName.find("wheat") != std::string::npos) ||
(lowerName.find("pumpkin") != std::string::npos) ||
(lowerName.find("firefly") != std::string::npos) ||
(lowerName.find("fireflies") != std::string::npos) ||
@ -2329,18 +2336,18 @@ std::optional<float> M2Renderer::getFloorHeight(float glX, float glY, float glZ)
continue;
}
if (glX < instance.worldBoundsMin.x || glX > instance.worldBoundsMax.x ||
glY < instance.worldBoundsMin.y || glY > instance.worldBoundsMax.y ||
glZ < instance.worldBoundsMin.z - 2.0f || glZ > instance.worldBoundsMax.z + 2.0f) {
continue;
}
auto it = models.find(instance.modelId);
if (it == models.end()) continue;
if (instance.scale <= 0.001f) continue;
const M2ModelGPU& model = it->second;
if (model.collisionNoBlock) continue;
float zMargin = model.collisionBridge ? 25.0f : 2.0f;
if (glX < instance.worldBoundsMin.x || glX > instance.worldBoundsMax.x ||
glY < instance.worldBoundsMin.y || glY > instance.worldBoundsMax.y ||
glZ < instance.worldBoundsMin.z - zMargin || glZ > instance.worldBoundsMax.z + zMargin) {
continue;
}
glm::vec3 localMin, localMax;
getTightCollisionBounds(model, localMin, localMax);
@ -2351,6 +2358,9 @@ std::optional<float> M2Renderer::getFloorHeight(float glX, float glY, float glZ)
float footprintPad = 0.0f;
if (model.collisionSteppedLowPlatform) {
footprintPad = model.collisionPlanter ? 0.22f : 0.16f;
if (model.collisionBridge) {
footprintPad = 0.35f;
}
}
if (localPos.x < localMin.x - footprintPad || localPos.x > localMax.x + footprintPad ||
localPos.y < localMin.y - footprintPad || localPos.y > localMax.y + footprintPad) {
@ -2372,6 +2382,9 @@ std::optional<float> M2Renderer::getFloorHeight(float glX, float glY, float glZ)
maxStepUp = 2.5f;
} else if (model.collisionSteppedLowPlatform) {
maxStepUp = model.collisionPlanter ? 3.0f : 2.4f;
if (model.collisionBridge) {
maxStepUp = 25.0f;
}
}
if (worldTop.z > glZ + maxStepUp) continue;
@ -2455,6 +2468,9 @@ bool M2Renderer::checkCollision(const glm::vec3& from, const glm::vec3& to,
maxStepUp = 2.5f;
} else if (model.collisionSteppedLowPlatform) {
maxStepUp = model.collisionPlanter ? 2.8f : 2.4f;
if (model.collisionBridge) {
maxStepUp = 25.0f;
}
}
bool stepableLowObject = (effectiveTop <= localFrom.z + maxStepUp);
bool climbingAttempt = (localPos.z > localFrom.z + 0.18f);
@ -2464,6 +2480,9 @@ bool M2Renderer::checkCollision(const glm::vec3& from, const glm::vec3& to,
// Let low curb/planter blocks be stepable without sticky side shoves.
climbAllowance = 1.00f;
}
if (model.collisionBridge) {
climbAllowance = 3.0f;
}
if (model.collisionSmallSolidProp) {
climbAllowance = 1.05f;
}

View file

@ -141,6 +141,7 @@ bool Minimap::initialize(int size) {
uniform sampler2D uComposite;
uniform vec2 uPlayerUV;
uniform float uRotation;
uniform float uArrowRotation;
uniform float uZoomRadius;
out vec4 FragColor;
@ -158,6 +159,12 @@ bool Minimap::initialize(int size) {
return (u >= 0.0) && (v >= 0.0) && (u + v <= 1.0);
}
vec2 rot2(vec2 v, float ang) {
float c = cos(ang);
float s = sin(ang);
return vec2(v.x * c - v.y * s, v.x * s + v.y * c);
}
void main() {
vec2 centered = TexCoord - 0.5;
float dist = length(centered);
@ -185,7 +192,7 @@ bool Minimap::initialize(int size) {
}
// Player arrow at center (always points up = forward)
vec2 ap = centered;
vec2 ap = rot2(centered, -uArrowRotation);
vec2 tip = vec2(0.0, 0.035);
vec2 lt = vec2(-0.018, -0.016);
vec2 rt = vec2(0.018, -0.016);
@ -490,9 +497,18 @@ void Minimap::renderQuad(const Camera& playerCamera, const glm::vec3& centerWorl
// renderX = wowY (west), renderY = wowX (north)
// Facing north: fwd=(0,1,0) → bearing=0
// Facing east: fwd=(-1,0,0) → bearing=π/2
glm::vec3 fwd = playerCamera.getForward();
float rotation = std::atan2(-fwd.x, fwd.y);
float rotation = 0.0f;
if (rotateWithCamera) {
glm::vec3 fwd = playerCamera.getForward();
rotation = std::atan2(-fwd.x, fwd.y);
}
quadShader->setUniform("uRotation", rotation);
float arrowRotation = 0.0f;
if (!rotateWithCamera) {
glm::vec3 fwd = playerCamera.getForward();
arrowRotation = std::atan2(-fwd.x, fwd.y);
}
quadShader->setUniform("uArrowRotation", arrowRotation);
quadShader->setUniform("uComposite", 0);
glActiveTexture(GL_TEXTURE0);

View file

@ -272,6 +272,12 @@ bool WMORenderer::loadModel(const pipeline::WMOModel& model, uint32_t id) {
modelData.id = id;
modelData.boundingBoxMin = model.boundingBoxMin;
modelData.boundingBoxMax = model.boundingBoxMax;
{
glm::vec3 ext = model.boundingBoxMax - model.boundingBoxMin;
float horiz = std::max(ext.x, ext.y);
float vert = ext.z;
modelData.isLowPlatform = (vert < 6.0f && horiz > 20.0f);
}
core::Logger::getInstance().info(" WMO bounds: min=(", model.boundingBoxMin.x, ", ", model.boundingBoxMin.y, ", ", model.boundingBoxMin.z,
") max=(", model.boundingBoxMax.x, ", ", model.boundingBoxMax.y, ", ", model.boundingBoxMax.z, ")");
@ -1637,6 +1643,7 @@ std::optional<float> WMORenderer::getFloorHeight(float glX, float glY, float glZ
QueryTimer timer(&queryTimeMs, &queryCallCount);
std::optional<float> bestFloor;
bool bestFromLowPlatform = false;
// World-space ray: from high above, pointing straight down
glm::vec3 worldOrigin(glX, glY, glZ + 500.0f);
@ -1653,17 +1660,19 @@ std::optional<float> WMORenderer::getFloorHeight(float glX, float glY, float glZ
continue;
}
// Broad-phase reject in world space to avoid expensive matrix transforms.
if (glX < instance.worldBoundsMin.x || glX > instance.worldBoundsMax.x ||
glY < instance.worldBoundsMin.y || glY > instance.worldBoundsMax.y ||
glZ < instance.worldBoundsMin.z - 2.0f || glZ > instance.worldBoundsMax.z + 4.0f) {
continue;
}
auto it = loadedModels.find(instance.modelId);
if (it == loadedModels.end()) continue;
const ModelData& model = it->second;
float zMarginDown = model.isLowPlatform ? 20.0f : 2.0f;
float zMarginUp = model.isLowPlatform ? 20.0f : 4.0f;
// Broad-phase reject in world space to avoid expensive matrix transforms.
if (glX < instance.worldBoundsMin.x || glX > instance.worldBoundsMax.x ||
glY < instance.worldBoundsMin.y || glY > instance.worldBoundsMax.y ||
glZ < instance.worldBoundsMin.z - zMarginDown || glZ > instance.worldBoundsMax.z + zMarginUp) {
continue;
}
// World-space pre-pass: check which groups' world XY bounds contain
// the query point. For a vertical ray this eliminates most groups
@ -1720,9 +1729,11 @@ std::optional<float> WMORenderer::getFloorHeight(float glX, float glY, float glZ
glm::vec3 hitLocal = localOrigin + localDir * t;
glm::vec3 hitWorld = glm::vec3(instance.modelMatrix * glm::vec4(hitLocal, 1.0f));
if (hitWorld.z <= glZ + 0.5f) {
float allowAbove = model.isLowPlatform ? 12.0f : 0.5f;
if (hitWorld.z <= glZ + allowAbove) {
if (!bestFloor || hitWorld.z > *bestFloor) {
bestFloor = hitWorld.z;
bestFromLowPlatform = model.isLowPlatform;
}
}
}
@ -1735,7 +1746,10 @@ std::optional<float> WMORenderer::getFloorHeight(float glX, float glY, float glZ
// Only update cache if we found a floor that's close to query height,
// to avoid caching wrong floors when player is on different stories.
if (bestFloor && *bestFloor >= glZ - 6.0f) {
precomputedFloorGrid[gridKey] = *bestFloor;
float cacheAbove = bestFromLowPlatform ? 12.0f : 2.0f;
if (*bestFloor <= glZ + cacheAbove) {
precomputedFloorGrid[gridKey] = *bestFloor;
}
}
return bestFloor;