map: fix contact-blip projection -- enemy now plots inside the radar FOV wedge
The previous commit found+drew contacts but they landed off-radar. Debugged with BT_MAP_LOG entity/blip tracing: the reads were CORRECT all along -- the enemy mech (cls 0xBB9, own=0) sits at dsq=14400 (exactly 120u) from the player (own=1, dsq=0), and the grid also carries ~311 cls-0x5E vehicle props per cycle (the map's scattered vehicles) which the 0xBB9 blip filter correctly skips. The bug was the PROJECTION: worldToView is a camera (view->world) matrix and Point3D::Multiply bakes a scale that is wrong for a point transform -- a 120u contact at ppm=0.366 projected to |167px| instead of |44px| (~3.8x), and with a spurious rotation. (The earlier "377-828m" reading was this wrong scale amplifying the varying player-drift distance, not a distance error.) Fixed by projecting delta = (entity - viewpoint) DIRECTLY: rotate by -heading (from the orientation quaternion's yaw, top-down heading-up) and scale by pixelsPerMeter. Now |screen| == |delta|*ppm exactly (blip @(0,44) for the 120u enemy), on-radar inside the view wedge. Blip is a cross + box so it reads as a distinct contact marker. Verified live (BT_DEV_GAUGES): the enemy contact renders as a box marker INSIDE the FOV wedge (render-confirmed via a 4x radar crop); combat un-regressed (TARGET DESTROYED after 8 hits), heap-clean under BT_HEAPCHECK. BT_MAP_LOG traces blip coords + scale + delta. Remaining map polish: blip-vs-wedge rotation convention (the enemy reads ahead/in-wedge, which is correct); the authentic pip symbol/name infrastructure (still stubbed); SetTargetRange (radar zoom, 1km default). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
0bceb9a638
commit
fb3ed38fa7
@@ -837,6 +837,11 @@ void
|
||||
Entity *owner = ResolveOperatorEntity(renderer);
|
||||
Entity *target = GetTarget(owner); // owner->GetVideoObject()->target
|
||||
|
||||
const int mlog = getenv("BT_MAP_LOG") ? 1 : 0;
|
||||
if (mlog)
|
||||
DEBUG_STREAM << "[map] DrawMoving owner=" << (void*)owner
|
||||
<< " view=(" << viewingPosition.x << "," << viewingPosition.z << ")\n" << std::flush;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
Entity *entity = iter.ReadAndNext();
|
||||
@@ -844,6 +849,19 @@ void
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (mlog)
|
||||
{
|
||||
Point3D ep = EntityPosition(entity);
|
||||
Scalar dx = ep.x - viewingPosition.x, dz = ep.z - viewingPosition.z;
|
||||
DEBUG_STREAM << "[map] ent=" << (void*)entity
|
||||
<< " cls=0x" << std::hex << entity->GetClassID() << std::dec
|
||||
<< " own=" << (entity == owner ? 1 : 0)
|
||||
<< " pos=(" << ep.x << "," << ep.z << ")"
|
||||
<< " dsq=" << (dx*dx + dz*dz)
|
||||
<< "\n" << std::flush;
|
||||
}
|
||||
|
||||
if (entity == owner)
|
||||
{
|
||||
continue;
|
||||
@@ -880,25 +898,40 @@ void
|
||||
// carries don't clutter the display -- the real pip table would key the
|
||||
// symbol off the model class, which is the proper follow-up.
|
||||
//
|
||||
// Project the RELATIVE position (delta = entity - viewpoint): worldToView
|
||||
// is a camera matrix and Point3D::Multiply applies only rotation+scale
|
||||
// (not the translation), so feeding the absolute position leaves the
|
||||
// viewpoint un-subtracted and the blip lands off-screen.
|
||||
Point3D relative;
|
||||
relative.x = delta.x;
|
||||
relative.y = delta.y;
|
||||
relative.z = delta.z;
|
||||
Point3D screen;
|
||||
screen.Multiply(relative, worldToView); // FUN_00408b98 (rotate+scale)
|
||||
int sx = Round(screen.x);
|
||||
int sz = Round(screen.z);
|
||||
// Project delta = (entity - viewpoint) directly to radar pixels: the
|
||||
// worldToView camera matrix bakes a scale that is wrong for a point
|
||||
// transform (|blip| came out ~3.8x |delta|*ppm), so compute it here --
|
||||
// top-down, heading-up: rotate delta by -heading, then scale by
|
||||
// pixelsPerMeter. |screen| == |delta|*ppm, on-radar for in-range mechs.
|
||||
Scalar sinH = 0.0f, cosH = 1.0f;
|
||||
if (currentAngularPointer != NULL)
|
||||
{
|
||||
EulerAngles e;
|
||||
e = *currentAngularPointer; // Quaternion -> EulerAngles (operator=)
|
||||
SinCosPair scH;
|
||||
scH = Radian(e.yaw); // FUN_00408328
|
||||
sinH = scH.sine;
|
||||
cosH = scH.cosine;
|
||||
}
|
||||
Scalar rx = delta.x * cosH + delta.z * sinH;
|
||||
Scalar rz = -delta.x * sinH + delta.z * cosH;
|
||||
int sx = Round(rx * pixelsPerMeter);
|
||||
int sz = Round(rz * pixelsPerMeter);
|
||||
if (getenv("BT_MAP_LOG"))
|
||||
DEBUG_STREAM << "[map] blip mech @(" << sx << "," << sz << ")\n" << std::flush;
|
||||
DEBUG_STREAM << "[map] blip mech @(" << sx << "," << sz << ")"
|
||||
<< " scale=" << currentScale << " ppm=" << pixelsPerMeter
|
||||
<< " deltaXZ=(" << delta.x << "," << delta.z << ")\n" << std::flush;
|
||||
localView.SetColor((entity == target) ? boxColor : MapBlipColor());
|
||||
localView.MoveToAbsolute(sx - 4, sz);
|
||||
localView.DrawLineToAbsolute(sx + 4, sz);
|
||||
localView.MoveToAbsolute(sx, sz - 4);
|
||||
localView.DrawLineToAbsolute(sx, sz + 4);
|
||||
localView.MoveToAbsolute(sx - 5, sz);
|
||||
localView.DrawLineToAbsolute(sx + 5, sz);
|
||||
localView.MoveToAbsolute(sx, sz - 5);
|
||||
localView.DrawLineToAbsolute(sx, sz + 5);
|
||||
// small box around the blip so it reads as a distinct contact marker
|
||||
localView.MoveToAbsolute(sx - 4, sz - 4);
|
||||
localView.DrawLineToAbsolute(sx + 4, sz - 4);
|
||||
localView.DrawLineToAbsolute(sx + 4, sz + 4);
|
||||
localView.DrawLineToAbsolute(sx - 4, sz + 4);
|
||||
localView.DrawLineToAbsolute(sx - 4, sz - 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user