Replace the projected Setup-sphere rectangle and independent physics-wall ray with retail's render-coupled picker: only visible server-object parts participate, each exact drawing sphere broad-phases the camera-eye ray, and first-in-DAT-order visual polygon hits globally outrank sphere fallbacks. Replace the devtools-only procedural triangles with the retained gameplay VividTargetIndicator using retail client-enum surfaces 1..4, radar-blip colorization, Setup selection-sphere framing, and the exact eight-pixel viewport clamp. Release build succeeds with zero warnings and all 5,886 tests pass with five intentional skips. Co-authored-by: OpenAI Codex <codex@openai.com>
54 lines
2 KiB
C#
54 lines
2 KiB
C#
using System.Numerics;
|
|
|
|
namespace AcDream.Core.Selection;
|
|
|
|
/// <summary>
|
|
/// Pure mouse-pixel to world-ray conversion shared by retail selection paths.
|
|
/// </summary>
|
|
public static class WorldPicker
|
|
{
|
|
/// <summary>
|
|
/// Unprojects a pixel coordinate to a world-space ray using System.Numerics'
|
|
/// row-vector convention (<c>view * projection</c>).
|
|
/// </summary>
|
|
/// <returns>
|
|
/// The camera viewpoint and normalized direction, or two zero vectors
|
|
/// when the camera transform is singular.
|
|
/// </returns>
|
|
public static (Vector3 Origin, Vector3 Direction) BuildRay(
|
|
float mouseX,
|
|
float mouseY,
|
|
float viewportW,
|
|
float viewportH,
|
|
Matrix4x4 view,
|
|
Matrix4x4 projection)
|
|
{
|
|
float ndcX = (2f * mouseX) / viewportW - 1f;
|
|
float ndcY = 1f - (2f * mouseY) / viewportH;
|
|
|
|
Matrix4x4 vp = view * projection;
|
|
if (!Matrix4x4.Invert(vp, out Matrix4x4 invVp)
|
|
|| !Matrix4x4.Invert(view, out Matrix4x4 invView))
|
|
return (Vector3.Zero, Vector3.Zero);
|
|
|
|
Vector4 nearClip = new(ndcX, ndcY, -1f, 1f);
|
|
Vector4 farClip = new(ndcX, ndcY, 1f, 1f);
|
|
Vector4 near = Vector4.Transform(nearClip, invVp);
|
|
Vector4 far = Vector4.Transform(farClip, invVp);
|
|
if (near.W == 0f || far.W == 0f)
|
|
return (Vector3.Zero, Vector3.Zero);
|
|
|
|
Vector3 nearWorld = new Vector3(near.X, near.Y, near.Z) / near.W;
|
|
Vector3 farWorld = new Vector3(far.X, far.Y, far.Z) / far.W;
|
|
Vector3 direction = farWorld - nearWorld;
|
|
if (direction.LengthSquared() < 1e-10f)
|
|
return (Vector3.Zero, Vector3.Zero);
|
|
|
|
// Render::pick_ray @ 0x0054B610 stores a direction through the pixel;
|
|
// GfxObjUnderSelectionRay @ 0x0054C740 pairs it with Render::viewpoint,
|
|
// not the near-plane point. Inverse-view origin is that viewpoint in
|
|
// System.Numerics' row-vector convention.
|
|
Vector3 viewpoint = Vector3.Transform(Vector3.Zero, invView);
|
|
return (viewpoint, Vector3.Normalize(direction));
|
|
}
|
|
}
|