// vischeck.cpp — world-only line-of-sight test via EngineTraceClient003. // // IEngineTrace::TraceRay (vtable slot 4) is given a ray from the eye to the // aim point and a TRACE_WORLD_ONLY filter. MASK_VISIBLE ignores glass/windows // (you can see through them) while still hitting opaque world geometry. // Per the SDK, TRACE_WORLD_ONLY does not test static props; that is the // deliberate trade-off for this build. #include "hl2h.h" #include #include namespace { struct alignas(16) VectorAlignedCompat { float x, y, z, w; }; // Ray_t: four VectorAligned (16 bytes each) followed by two bools. struct alignas(16) RayCompat { VectorAlignedCompat start; VectorAlignedCompat delta; VectorAlignedCompat startOffset; VectorAlignedCompat extents; bool isRay; bool isSwept; }; static_assert(sizeof(VectorAlignedCompat) == 16, "VectorAligned layout"); static_assert(offsetof(RayCompat, delta) == 16, "Ray_t delta offset"); static_assert(offsetof(RayCompat, isRay) == 64, "Ray_t isRay offset"); // trace_t / CBaseTrace prefix. cplane_t is Vector + float + type + signbits // + 2 pad bytes, so fraction lands at 0x2C and startsolid at 0x37. struct TraceCompat { Vector startpos; Vector endpos; Vector normal; float dist; uint8_t type; uint8_t signbits; uint8_t pad[2]; float fraction; int contents; uint16_t dispFlags; bool allsolid; bool startsolid; uint8_t rest[128]; // CGameTrace tail (surface, ent, hitbox, ...) }; static_assert(offsetof(TraceCompat, fraction) == 44, "trace_t fraction"); static_assert(offsetof(TraceCompat, startsolid) == 55, "trace_t startsolid"); // ITraceFilter has no virtual destructor: vtable[0] = ShouldHitEntity, // vtable[1] = GetTraceType. struct ITraceFilterCompat { virtual bool ShouldHitEntity(void *pEntity, int contentsMask) = 0; virtual int GetTraceType() const = 0; }; struct WorldOnlyFilter : ITraceFilterCompat { bool ShouldHitEntity(void *, int) override { return false; } int GetTraceType() const override { return 1; } // TRACE_WORLD_ONLY }; // MASK_VISIBLE = CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_OPAQUE | // CONTENTS_IGNORE_NODRAW_OPAQUE constexpr unsigned MASK_VISIBLE = 0x1u | 0x4000u | 0x80u | 0x2000u; } // namespace bool is_visible(const Vector &eye, const Vector &point) { static bool warned = false; if (!g_iface.enginetrace) { if (!warned) { warned = true; logf("vischeck: EngineTraceClient003 unavailable; failing open"); } return true; } float dx = point.x - eye.x, dy = point.y - eye.y, dz = point.z - eye.z; RayCompat ray{}; ray.start = {eye.x, eye.y, eye.z, 0.0f}; ray.delta = {dx, dy, dz, 0.0f}; ray.startOffset = {0.0f, 0.0f, 0.0f, 0.0f}; ray.extents = {0.0f, 0.0f, 0.0f, 0.0f}; ray.isRay = true; ray.isSwept = (dx * dx + dy * dy + dz * dz) > 0.0f; TraceCompat tr{}; WorldOnlyFilter filter; using TraceRayFn = void (*)(void *, const RayCompat *, unsigned, void *, TraceCompat *); vfunc(g_iface.enginetrace, 4)(g_iface.enginetrace, &ray, MASK_VISIBLE, &filter, &tr); return tr.fraction > 0.97f && !tr.startsolid; }