Fixing script
This commit is contained in:
1 parent
3fe21230cc
commit
423d8b8f52
1 file changed
+50
-13
+50
-13
@@ -890,14 +890,16 @@ namespace FRG
|
|||||||
}
|
}
|
||||||
|
|
||||||
Character* Location::raycastNpcs(const Eigen::Vector3f& rayOrigin, const Eigen::Vector3f& rayDir, float maxDistance) {
|
Character* Location::raycastNpcs(const Eigen::Vector3f& rayOrigin, const Eigen::Vector3f& rayDir, float maxDistance) {
|
||||||
// Every NPC is treated as a vertical cylinder: radius 1.0m, height 1.85m,
|
// Every NPC is treated as a vertical cylinder: radius 0.25m, height 1.6m,
|
||||||
// base at npc->position (the model's foot). Intersection = circle hit in the
|
// base at npc->position (the model's foot). Intersection = circle hit in the
|
||||||
// XZ plane, then clip the entry/exit t against the [yFoot, yFoot+height] slab.
|
// XZ plane, then clip the entry/exit t against the [yFoot, yFoot+height] slab.
|
||||||
static constexpr float NPC_CLICK_RADIUS = 1.0f;
|
static constexpr float NPC_CLICK_RADIUS = 0.25f;
|
||||||
static constexpr float NPC_CLICK_HEIGHT = 1.85f;
|
static constexpr float NPC_CLICK_HEIGHT = 1.6f;
|
||||||
|
static constexpr float NPC_PICK_EPSILON = 1e-6f;
|
||||||
|
|
||||||
Character* closestNpc = nullptr;
|
Character* closestNpc = nullptr;
|
||||||
float closestDist = maxDistance;
|
float closestAngularDistanceSq = (std::numeric_limits<float>::max)();
|
||||||
|
float closestEntryDistance = maxDistance;
|
||||||
|
|
||||||
//logger() << "[RAYCAST_NPC] Starting raycast with " << npcs.size() << " npcs" << std::endl;
|
//logger() << "[RAYCAST_NPC] Starting raycast with " << npcs.size() << " npcs" << std::endl;
|
||||||
|
|
||||||
@@ -908,8 +910,11 @@ namespace FRG
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!npcState->enabled) continue;
|
if (!npcState->enabled) continue;
|
||||||
const float dx = rayOrigin.x() - npcState->position.x();
|
// Use the presentation position so the clickable volume follows the
|
||||||
const float dz = rayOrigin.z() - npcState->position.z();
|
// rendered character while it is being interpolated between ticks.
|
||||||
|
const Eigen::Vector3f npcPosition = npc->renderPosition();
|
||||||
|
const float dx = rayOrigin.x() - npcPosition.x();
|
||||||
|
const float dz = rayOrigin.z() - npcPosition.z();
|
||||||
const float a = rayDir.x() * rayDir.x() + rayDir.z() * rayDir.z();
|
const float a = rayDir.x() * rayDir.x() + rayDir.z() * rayDir.z();
|
||||||
if (a < 1e-6f) continue; // purely-vertical ray; not a real click case
|
if (a < 1e-6f) continue; // purely-vertical ray; not a real click case
|
||||||
|
|
||||||
@@ -922,28 +927,60 @@ namespace FRG
|
|||||||
const float tNear = (-b - sqrtDisc) / (2.0f * a);
|
const float tNear = (-b - sqrtDisc) / (2.0f * a);
|
||||||
const float tFar = (-b + sqrtDisc) / (2.0f * a);
|
const float tFar = (-b + sqrtDisc) / (2.0f * a);
|
||||||
|
|
||||||
float entryT = max(tNear, 0.1f);
|
float entryT = (std::max)(tNear, 0.1f);
|
||||||
float exitT = tFar;
|
float exitT = tFar;
|
||||||
|
|
||||||
// Clip against the cylinder's vertical extent.
|
// Clip against the cylinder's vertical extent.
|
||||||
const float yMin = npcState->position.y();
|
const float yMin = npcPosition.y();
|
||||||
const float yMax = yMin + NPC_CLICK_HEIGHT;
|
const float yMax = yMin + NPC_CLICK_HEIGHT;
|
||||||
if (std::abs(rayDir.y()) > 1e-6f) {
|
if (std::abs(rayDir.y()) > 1e-6f) {
|
||||||
const float tYa = (yMin - rayOrigin.y()) / rayDir.y();
|
const float tYa = (yMin - rayOrigin.y()) / rayDir.y();
|
||||||
const float tYb = (yMax - rayOrigin.y()) / rayDir.y();
|
const float tYb = (yMax - rayOrigin.y()) / rayDir.y();
|
||||||
entryT = max(entryT, min(tYa, tYb));
|
entryT = (std::max)(entryT, (std::min)(tYa, tYb));
|
||||||
exitT = min(exitT, max(tYa, tYb));
|
exitT = (std::min)(exitT, (std::max)(tYa, tYb));
|
||||||
}
|
}
|
||||||
else if (rayOrigin.y() < yMin || rayOrigin.y() > yMax) {
|
else if (rayOrigin.y() < yMin || rayOrigin.y() > yMax) {
|
||||||
continue; // horizontal ray passing above or below the cylinder
|
continue; // horizontal ray passing above or below the cylinder
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entryT > exitT) continue;
|
if (entryT > exitT) continue;
|
||||||
|
if (entryT > maxDistance) continue;
|
||||||
|
|
||||||
//logger() << "[RAYCAST_NPC] " << npc->entityId() << " hit at t=" << entryT << std::endl;
|
// All candidates above are under the cursor. Resolve overlaps by
|
||||||
|
// projected cursor proximity rather than by whichever cylinder is
|
||||||
|
// entered first along the ray. The normalized perpendicular distance
|
||||||
|
// is an angular/screen-space approximation that remains comparable
|
||||||
|
// for NPCs at different depths.
|
||||||
|
const Eigen::Vector3f bodyCenter =
|
||||||
|
npcPosition + Eigen::Vector3f(0.0f, NPC_CLICK_HEIGHT * 0.5f, 0.0f);
|
||||||
|
const Eigen::Vector3f toBody = bodyCenter - rayOrigin;
|
||||||
|
const float bodyDepth = toBody.dot(rayDir);
|
||||||
|
if (bodyDepth <= NPC_PICK_EPSILON) continue;
|
||||||
|
const float perpendicularDistanceSq =
|
||||||
|
(toBody - rayDir * bodyDepth).squaredNorm();
|
||||||
|
const float angularDistanceSq =
|
||||||
|
perpendicularDistanceSq /
|
||||||
|
(std::max)(bodyDepth * bodyDepth, NPC_PICK_EPSILON);
|
||||||
|
|
||||||
if (entryT < closestDist) {
|
const bool betterAngularMatch =
|
||||||
closestDist = entryT;
|
angularDistanceSq <
|
||||||
|
closestAngularDistanceSq - NPC_PICK_EPSILON;
|
||||||
|
const bool equalAngularMatch =
|
||||||
|
std::abs(angularDistanceSq - closestAngularDistanceSq) <=
|
||||||
|
NPC_PICK_EPSILON;
|
||||||
|
const bool betterDepthTieBreak =
|
||||||
|
equalAngularMatch &&
|
||||||
|
(entryT < closestEntryDistance - NPC_PICK_EPSILON);
|
||||||
|
const bool equalDepthTieBreak =
|
||||||
|
equalAngularMatch &&
|
||||||
|
std::abs(entryT - closestEntryDistance) <= NPC_PICK_EPSILON;
|
||||||
|
const bool betterStableTieBreak =
|
||||||
|
equalDepthTieBreak &&
|
||||||
|
(!closestNpc || npc->entityId() < closestNpc->entityId());
|
||||||
|
|
||||||
|
if (betterAngularMatch || betterDepthTieBreak || betterStableTieBreak) {
|
||||||
|
closestEntryDistance = entryT;
|
||||||
|
closestAngularDistanceSq = angularDistanceSq;
|
||||||
closestNpc = npc.get();
|
closestNpc = npc.get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user