diff options
Diffstat (limited to '')
| -rw-r--r-- | src/vischeck.cpp | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/src/vischeck.cpp b/src/vischeck.cpp new file mode 100644 index 0000000..92d194b --- /dev/null +++ b/src/vischeck.cpp | |||
| @@ -0,0 +1,98 @@ | |||
| 1 | // vischeck.cpp — world-only line-of-sight test via EngineTraceClient003. | ||
| 2 | // | ||
| 3 | // IEngineTrace::TraceRay (vtable slot 4) is given a ray from the eye to the | ||
| 4 | // aim point and a TRACE_WORLD_ONLY filter. MASK_VISIBLE ignores glass/windows | ||
| 5 | // (you can see through them) while still hitting opaque world geometry. | ||
| 6 | // Per the SDK, TRACE_WORLD_ONLY does not test static props; that is the | ||
| 7 | // deliberate trade-off for this build. | ||
| 8 | |||
| 9 | #include "hl2h.h" | ||
| 10 | #include <cstddef> | ||
| 11 | #include <cstdint> | ||
| 12 | |||
| 13 | namespace { | ||
| 14 | |||
| 15 | struct alignas(16) VectorAlignedCompat { | ||
| 16 | float x, y, z, w; | ||
| 17 | }; | ||
| 18 | |||
| 19 | // Ray_t: four VectorAligned (16 bytes each) followed by two bools. | ||
| 20 | struct alignas(16) RayCompat { | ||
| 21 | VectorAlignedCompat start; | ||
| 22 | VectorAlignedCompat delta; | ||
| 23 | VectorAlignedCompat startOffset; | ||
| 24 | VectorAlignedCompat extents; | ||
| 25 | bool isRay; | ||
| 26 | bool isSwept; | ||
| 27 | }; | ||
| 28 | |||
| 29 | static_assert(sizeof(VectorAlignedCompat) == 16, "VectorAligned layout"); | ||
| 30 | static_assert(offsetof(RayCompat, delta) == 16, "Ray_t delta offset"); | ||
| 31 | static_assert(offsetof(RayCompat, isRay) == 64, "Ray_t isRay offset"); | ||
| 32 | |||
| 33 | // trace_t / CBaseTrace prefix. cplane_t is Vector + float + type + signbits | ||
| 34 | // + 2 pad bytes, so fraction lands at 0x2C and startsolid at 0x37. | ||
| 35 | struct TraceCompat { | ||
| 36 | Vector startpos; | ||
| 37 | Vector endpos; | ||
| 38 | Vector normal; | ||
| 39 | float dist; | ||
| 40 | uint8_t type; | ||
| 41 | uint8_t signbits; | ||
| 42 | uint8_t pad[2]; | ||
| 43 | float fraction; | ||
| 44 | int contents; | ||
| 45 | uint16_t dispFlags; | ||
| 46 | bool allsolid; | ||
| 47 | bool startsolid; | ||
| 48 | uint8_t rest[128]; // CGameTrace tail (surface, ent, hitbox, ...) | ||
| 49 | }; | ||
| 50 | |||
| 51 | static_assert(offsetof(TraceCompat, fraction) == 44, "trace_t fraction"); | ||
| 52 | static_assert(offsetof(TraceCompat, startsolid) == 55, "trace_t startsolid"); | ||
| 53 | |||
| 54 | // ITraceFilter has no virtual destructor: vtable[0] = ShouldHitEntity, | ||
| 55 | // vtable[1] = GetTraceType. | ||
| 56 | struct ITraceFilterCompat { | ||
| 57 | virtual bool ShouldHitEntity(void *pEntity, int contentsMask) = 0; | ||
| 58 | virtual int GetTraceType() const = 0; | ||
| 59 | }; | ||
| 60 | |||
| 61 | struct WorldOnlyFilter : ITraceFilterCompat { | ||
| 62 | bool ShouldHitEntity(void *, int) override { return false; } | ||
| 63 | int GetTraceType() const override { return 1; } // TRACE_WORLD_ONLY | ||
| 64 | }; | ||
| 65 | |||
| 66 | // MASK_VISIBLE = CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_OPAQUE | | ||
| 67 | // CONTENTS_IGNORE_NODRAW_OPAQUE | ||
| 68 | constexpr unsigned MASK_VISIBLE = 0x1u | 0x4000u | 0x80u | 0x2000u; | ||
| 69 | |||
| 70 | } // namespace | ||
| 71 | |||
| 72 | bool is_visible(const Vector &eye, const Vector &point) { | ||
| 73 | static bool warned = false; | ||
| 74 | if (!g_iface.enginetrace) { | ||
| 75 | if (!warned) { | ||
| 76 | warned = true; | ||
| 77 | logf("vischeck: EngineTraceClient003 unavailable; failing open"); | ||
| 78 | } | ||
| 79 | return true; | ||
| 80 | } | ||
| 81 | |||
| 82 | float dx = point.x - eye.x, dy = point.y - eye.y, dz = point.z - eye.z; | ||
| 83 | RayCompat ray{}; | ||
| 84 | ray.start = {eye.x, eye.y, eye.z, 0.0f}; | ||
| 85 | ray.delta = {dx, dy, dz, 0.0f}; | ||
| 86 | ray.startOffset = {0.0f, 0.0f, 0.0f, 0.0f}; | ||
| 87 | ray.extents = {0.0f, 0.0f, 0.0f, 0.0f}; | ||
| 88 | ray.isRay = true; | ||
| 89 | ray.isSwept = (dx * dx + dy * dy + dz * dz) > 0.0f; | ||
| 90 | |||
| 91 | TraceCompat tr{}; | ||
| 92 | WorldOnlyFilter filter; | ||
| 93 | using TraceRayFn = void (*)(void *, const RayCompat *, unsigned, void *, | ||
| 94 | TraceCompat *); | ||
| 95 | vfunc<TraceRayFn>(g_iface.enginetrace, 4)(g_iface.enginetrace, &ray, | ||
| 96 | MASK_VISIBLE, &filter, &tr); | ||
| 97 | return tr.fraction > 0.97f && !tr.startsolid; | ||
| 98 | } | ||
