mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Improve rendering distances, camera collision, and spawn point
- Increase WMO render distance from 1500 to 3000 units for better city loading - Increase M2 render distance from 500 to 1000 units - Increase terrain load radius from 4 to 6 tiles (~3200 units) - Add raycast-based camera collision that zooms in when obstructed by walls/objects - Move spawn point outside chapel to road near Stormwind gate - Add ground height smoothing to prevent stumbling on uneven terrain
This commit is contained in:
parent
76a16a214e
commit
a8cf17e7e5
7 changed files with 142 additions and 12 deletions
|
|
@ -126,9 +126,9 @@ private:
|
|||
static constexpr float WOW_GRAVITY = -19.29f;
|
||||
static constexpr float WOW_JUMP_VELOCITY = 7.96f;
|
||||
|
||||
// Default spawn position (in front of Stormwind gate)
|
||||
glm::vec3 defaultPosition = glm::vec3(-8900.0f, -170.0f, 150.0f);
|
||||
float defaultYaw = 0.0f; // Look north toward Stormwind gate
|
||||
// Default spawn position (on the road outside Stormwind)
|
||||
glm::vec3 defaultPosition = glm::vec3(-8830.0f, -150.0f, 82.0f);
|
||||
float defaultYaw = 180.0f; // Look south toward Stormwind gate
|
||||
float defaultPitch = -5.0f;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -144,6 +144,15 @@ public:
|
|||
bool checkCollision(const glm::vec3& from, const glm::vec3& to,
|
||||
glm::vec3& adjustedPos, float playerRadius = 0.5f) const;
|
||||
|
||||
/**
|
||||
* Raycast against M2 bounding boxes for camera collision
|
||||
* @param origin Ray origin (e.g., character head position)
|
||||
* @param direction Ray direction (normalized)
|
||||
* @param maxDistance Maximum ray distance to check
|
||||
* @return Distance to first intersection, or maxDistance if no hit
|
||||
*/
|
||||
float raycastBoundingBoxes(const glm::vec3& origin, const glm::vec3& direction, float maxDistance) const;
|
||||
|
||||
// Stats
|
||||
uint32_t getModelCount() const { return static_cast<uint32_t>(models.size()); }
|
||||
uint32_t getInstanceCount() const { return static_cast<uint32_t>(instances.size()); }
|
||||
|
|
|
|||
|
|
@ -240,8 +240,8 @@ private:
|
|||
|
||||
// Streaming parameters
|
||||
bool streamingEnabled = true;
|
||||
int loadRadius = 4; // Load tiles within this radius (9x9 grid, ~2133 units)
|
||||
int unloadRadius = 6; // Unload tiles beyond this radius (~3200 units, past far clip)
|
||||
int loadRadius = 6; // Load tiles within this radius (13x13 grid, ~3200 units)
|
||||
int unloadRadius = 8; // Unload tiles beyond this radius (~4266 units)
|
||||
float updateInterval = 0.1f; // Check streaming every 0.1 seconds
|
||||
float timeSinceLastUpdate = 0.0f;
|
||||
|
||||
|
|
|
|||
|
|
@ -149,6 +149,15 @@ public:
|
|||
*/
|
||||
bool isInsideWMO(float glX, float glY, float glZ, uint32_t* outModelId = nullptr) const;
|
||||
|
||||
/**
|
||||
* Raycast against WMO bounding boxes for camera collision
|
||||
* @param origin Ray origin (e.g., character head position)
|
||||
* @param direction Ray direction (normalized)
|
||||
* @param maxDistance Maximum ray distance to check
|
||||
* @return Distance to first intersection, or maxDistance if no hit
|
||||
*/
|
||||
float raycastBoundingBoxes(const glm::vec3& origin, const glm::vec3& direction, float maxDistance) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* WMO group GPU resources
|
||||
|
|
|
|||
|
|
@ -201,9 +201,18 @@ void CameraController::update(float deltaTime) {
|
|||
}
|
||||
|
||||
if (groundH) {
|
||||
lastGroundZ = *groundH;
|
||||
if (targetPos.z <= *groundH) {
|
||||
targetPos.z = *groundH;
|
||||
// Smooth ground height to prevent stumbling on uneven terrain
|
||||
float groundDiff = *groundH - lastGroundZ;
|
||||
if (std::abs(groundDiff) < 2.0f) {
|
||||
// Small height difference - smooth it
|
||||
lastGroundZ += groundDiff * std::min(1.0f, deltaTime * 15.0f);
|
||||
} else {
|
||||
// Large height difference (stairs, ledges) - snap
|
||||
lastGroundZ = *groundH;
|
||||
}
|
||||
|
||||
if (targetPos.z <= lastGroundZ + 0.1f) {
|
||||
targetPos.z = lastGroundZ;
|
||||
verticalVelocity = 0.0f;
|
||||
grounded = true;
|
||||
swimming = false; // Touching ground = wading, not swimming
|
||||
|
|
@ -223,7 +232,30 @@ void CameraController::update(float deltaTime) {
|
|||
|
||||
// Compute camera position orbiting behind the character
|
||||
glm::vec3 lookAtPoint = targetPos + glm::vec3(0.0f, 0.0f, eyeHeight);
|
||||
glm::vec3 camPos = lookAtPoint - forward3D * orbitDistance;
|
||||
|
||||
// Camera collision detection - raycast from character head to desired camera position
|
||||
glm::vec3 rayDir = -forward3D; // Direction from character toward camera
|
||||
float desiredDist = orbitDistance;
|
||||
float actualDist = desiredDist;
|
||||
const float cameraOffset = 0.3f; // Small offset to not clip into walls
|
||||
|
||||
// Raycast against WMO bounding boxes
|
||||
if (wmoRenderer) {
|
||||
float wmoHit = wmoRenderer->raycastBoundingBoxes(lookAtPoint, rayDir, desiredDist);
|
||||
if (wmoHit < actualDist) {
|
||||
actualDist = std::max(minOrbitDistance, wmoHit - cameraOffset);
|
||||
}
|
||||
}
|
||||
|
||||
// Raycast against M2 bounding boxes (larger objects only affect camera)
|
||||
if (m2Renderer) {
|
||||
float m2Hit = m2Renderer->raycastBoundingBoxes(lookAtPoint, rayDir, desiredDist);
|
||||
if (m2Hit < actualDist) {
|
||||
actualDist = std::max(minOrbitDistance, m2Hit - cameraOffset);
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 camPos = lookAtPoint + rayDir * actualDist;
|
||||
|
||||
// Clamp camera above terrain/WMO floor
|
||||
{
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ void M2Renderer::render(const Camera& camera, const glm::mat4& view, const glm::
|
|||
lastDrawCallCount = 0;
|
||||
|
||||
// Distance-based culling threshold for M2 models
|
||||
const float maxRenderDistance = 500.0f; // Don't render small doodads beyond this
|
||||
const float maxRenderDistance = 1000.0f; // Don't render small doodads beyond this
|
||||
const float maxRenderDistanceSq = maxRenderDistance * maxRenderDistance;
|
||||
const glm::vec3 camPos = camera.getPosition();
|
||||
|
||||
|
|
@ -392,7 +392,7 @@ void M2Renderer::render(const Camera& camera, const glm::mat4& view, const glm::
|
|||
|
||||
shader->setUniform("uModel", instance.modelMatrix);
|
||||
shader->setUniform("uTime", instance.animTime);
|
||||
shader->setUniform("uAnimScale", 1.0f); // Enable animation for all M2s
|
||||
shader->setUniform("uAnimScale", 0.0f); // Disabled - proper M2 animation needs bone/particle systems
|
||||
|
||||
glBindVertexArray(model.vao);
|
||||
|
||||
|
|
@ -587,5 +587,46 @@ bool M2Renderer::checkCollision(const glm::vec3& from, const glm::vec3& to,
|
|||
return collided;
|
||||
}
|
||||
|
||||
float M2Renderer::raycastBoundingBoxes(const glm::vec3& origin, const glm::vec3& direction, float maxDistance) const {
|
||||
float closestHit = maxDistance;
|
||||
|
||||
for (const auto& instance : instances) {
|
||||
auto it = models.find(instance.modelId);
|
||||
if (it == models.end()) continue;
|
||||
|
||||
const M2ModelGPU& model = it->second;
|
||||
|
||||
// Transform model bounds to world space (approximate with scaled AABB)
|
||||
glm::vec3 worldMin = instance.position + model.boundMin * instance.scale;
|
||||
glm::vec3 worldMax = instance.position + model.boundMax * instance.scale;
|
||||
|
||||
// Ensure min/max are correct
|
||||
glm::vec3 actualMin = glm::min(worldMin, worldMax);
|
||||
glm::vec3 actualMax = glm::max(worldMin, worldMax);
|
||||
|
||||
// Ray-AABB intersection (slab method)
|
||||
glm::vec3 invDir = 1.0f / direction;
|
||||
glm::vec3 tMin = (actualMin - origin) * invDir;
|
||||
glm::vec3 tMax = (actualMax - origin) * invDir;
|
||||
|
||||
// Handle negative direction components
|
||||
glm::vec3 t1 = glm::min(tMin, tMax);
|
||||
glm::vec3 t2 = glm::max(tMin, tMax);
|
||||
|
||||
float tNear = std::max({t1.x, t1.y, t1.z});
|
||||
float tFar = std::min({t2.x, t2.y, t2.z});
|
||||
|
||||
// Check if ray intersects the box
|
||||
if (tNear <= tFar && tFar > 0.0f) {
|
||||
float hitDist = tNear > 0.0f ? tNear : tFar;
|
||||
if (hitDist > 0.0f && hitDist < closestHit) {
|
||||
closestHit = hitDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closestHit;
|
||||
}
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
|
|
|
|||
|
|
@ -354,7 +354,7 @@ void WMORenderer::render(const Camera& camera, const glm::mat4& view, const glm:
|
|||
|
||||
// Render all instances with instance-level culling
|
||||
const glm::vec3 camPos = camera.getPosition();
|
||||
const float maxRenderDistance = 1500.0f; // Don't render WMOs beyond this distance
|
||||
const float maxRenderDistance = 3000.0f; // Don't render WMOs beyond this distance
|
||||
const float maxRenderDistanceSq = maxRenderDistance * maxRenderDistance;
|
||||
|
||||
for (const auto& instance : instances) {
|
||||
|
|
@ -883,5 +883,44 @@ bool WMORenderer::isInsideWMO(float glX, float glY, float glZ, uint32_t* outMode
|
|||
return false;
|
||||
}
|
||||
|
||||
float WMORenderer::raycastBoundingBoxes(const glm::vec3& origin, const glm::vec3& direction, float maxDistance) const {
|
||||
float closestHit = maxDistance;
|
||||
|
||||
for (const auto& instance : instances) {
|
||||
auto it = loadedModels.find(instance.modelId);
|
||||
if (it == loadedModels.end()) continue;
|
||||
|
||||
const ModelData& model = it->second;
|
||||
|
||||
// Transform ray into local space
|
||||
glm::mat4 invModel = glm::inverse(instance.modelMatrix);
|
||||
glm::vec3 localOrigin = glm::vec3(invModel * glm::vec4(origin, 1.0f));
|
||||
glm::vec3 localDir = glm::normalize(glm::vec3(invModel * glm::vec4(direction, 0.0f)));
|
||||
|
||||
for (const auto& group : model.groups) {
|
||||
// Ray-AABB intersection (slab method)
|
||||
glm::vec3 tMin = (group.boundingBoxMin - localOrigin) / localDir;
|
||||
glm::vec3 tMax = (group.boundingBoxMax - localOrigin) / localDir;
|
||||
|
||||
// Handle negative direction components
|
||||
glm::vec3 t1 = glm::min(tMin, tMax);
|
||||
glm::vec3 t2 = glm::max(tMin, tMax);
|
||||
|
||||
float tNear = std::max({t1.x, t1.y, t1.z});
|
||||
float tFar = std::min({t2.x, t2.y, t2.z});
|
||||
|
||||
// Check if ray intersects the box
|
||||
if (tNear <= tFar && tFar > 0.0f) {
|
||||
float hitDist = tNear > 0.0f ? tNear : tFar;
|
||||
if (hitDist > 0.0f && hitDist < closestHit) {
|
||||
closestHit = hitDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closestHit;
|
||||
}
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue