Fixing script

This commit is contained in:
Vladislav Khorev committed 2026-11-09 19:56:28 +06:00
1 parent 3fe21230cc
commit 423d8b8f52
1 file changed
+50 -13
+50 -13
View File
@@ -890,14 +890,16 @@ namespace FRG
}
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
// 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_HEIGHT = 1.85f;
static constexpr float NPC_CLICK_RADIUS = 0.25f;
static constexpr float NPC_CLICK_HEIGHT = 1.6f;
static constexpr float NPC_PICK_EPSILON = 1e-6f;
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;
@@ -908,8 +910,11 @@ namespace FRG
continue;
}
if (!npcState->enabled) continue;
const float dx = rayOrigin.x() - npcState->position.x();
const float dz = rayOrigin.z() - npcState->position.z();
// Use the presentation position so the clickable volume follows the
// 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();
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 tFar = (-b + sqrtDisc) / (2.0f * a);
float entryT = max(tNear, 0.1f);
float entryT = (std::max)(tNear, 0.1f);
float exitT = tFar;
// 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;
if (std::abs(rayDir.y()) > 1e-6f) {
const float tYa = (yMin - rayOrigin.y()) / rayDir.y();
const float tYb = (yMax - rayOrigin.y()) / rayDir.y();
entryT = max(entryT, min(tYa, tYb));
exitT = min(exitT, max(tYa, tYb));
entryT = (std::max)(entryT, (std::min)(tYa, tYb));
exitT = (std::min)(exitT, (std::max)(tYa, tYb));
}
else if (rayOrigin.y() < yMin || rayOrigin.y() > yMax) {
continue; // horizontal ray passing above or below the cylinder
}
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) {
closestDist = entryT;
const bool betterAngularMatch =
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();
}
}