All content below is for educational purposes only
Original linkVisibility checks are interesting features in ArmA series because of the kind of the game (long range shots, buildings, trees …).
I am going to show you how to implement one of the multiple ways of doing this kind of stuff.
There is two major functions for doing visibility checks :
enum class Functions
{
Landscape__IntersectWithGroundOrSea = 0x0044F3B0,
Landscape__ObjectCollisionLine = 0x004458E0,
};
And two other functions in charge of allocationg & freeing the collision buffer in charge of storing collision results :
enum class Functions
{
CollisionBuffer__Alloc = 0x0044FEF0,
CollisionBuffer__Free = 0x0044FF60,
};
typedef double (__thiscall *fnLandscape__IntersectWithGroundOrSea_t)(
unsigned int this_,
Math::Vector3 *ret,
Math::Vector3 *from,
Math::Vector3 *dir,
float minDist,
float maxDist
);
typedef int (__thiscall *fnLandscape__ObjectCollisionLine_t)(
unsigned int this_,
float age,
CollisionBuffer *res,
IntersectionFilter *f,
Math::Vector3 *beg,
Math::Vector3 *end,
float radius,
ObjIntersect a8,
ObjIntersect a9
);
typedef CollisionBuffer *(__thiscall *fnCollisionBuffer__Alloc_t)(CollisionBuffer *this_);
typedef void(__thiscall *fnCollisionBuffer__Free_t)(CollisionBuffer *this_);
So now we still need some elements in order to make this working, the Landscape class pointer, the CollisionBuffer
struct and the IntersectionFilter struct.
As said earlier the CollisionBuffer
struct is in charge of storing collisions results while the IntersectionFilter
struct is in charge of filtering units we dont want to get the collisions recorded (in this case it will be our player and our target.
struct CollisionInfo
{
void *texture;
void *surface;
Math::Vector3 pos;
Math::Vector3 dirOut;
Math::Vector3 dirOutNotNorm;
int hierLevel;
Object *object;
Object *parentObject;
float under;
int component;
bool entry;
bool exit;
int geomLevel;
};
struct CollisionBuffer
{
CollisionInfo *_data;
int _n;
BYTE gap0[1]; //alignment ??
unsigned int _oldSP;
void *_buffer;
bool _memused;
int maxN;
};
struct IntersectionFilterVtbl
{
bool(__thiscall *Filter)(void *this_, void *object);
};
struct IntersectionFilter
{
IntersectionFilterVtbl *vfptr;
};
struct Landscape__FilterIgnoreTwo : IntersectionFilter
{
Object *_obj1;
Object *_obj2;
};
You need to initialize the vfptr field with the according filter pointer :
enum class VirtualTables
{
Landscape__FilterIgnoreTwo = 0x00C36BE4,
};
The Landscape class pointer can be grabbed in two ways :
Through his static pointer :
enum class Statics
{
Landscape = 0x00DBE1C0,
};
uintptr_t landscape = *reinterpret_cast<uintptr_t*>(static_cast<uintptr_t>(Offsets::Statics::Landscape));
Or the Scene static pointer
enum class Statics
{
Scene = 0x00DD32F4,
};
enum class Scene
{
Landscape = 0x574,
};
uintptr_t landscape = *reinterpret_cast<uintptr_t*>(*reinterpret_cast<uintptr_t*>(Offsets::Statics::Scene) + static_cast<uintptr_t>(Offsets::Scene::Landscape));
How to check visibility ?
Quick overview :
bool Engine::TraceLineCollision(const Object& target)
{
if (!ObjectManager::GetInstance().IsSceneReady())
return false;
uintptr_t landscape = *reinterpret_cast<uintptr_t*>(*reinterpret_cast<uintptr_t*>(Offsets::Statics::Scene) + static_cast<uintptr_t>(Offsets::Scene::Landscape));
if (landscape == 0)
return false;
Object& localPlayer = ObjectManager::GetInstance().GetLocalPlayer();
Math::Vector3 from = ObjectManager::GetInstance().GetCamera().GetPosition();
Math::Vector3 to = target.GetAimingPosition();
Math::Vector3 direction = to - from;
float dirSize = direction.Size();
Math::Vector3 normalizedDir = direction.Normalize();
float t = fnLandscape__IntersectWithGroundOrSea(
landscape,
nullptr,
&from,
&normalizedDir,
0.0f,
dirSize
);
if (dirSize > t) //we hit terrain
return true;
CollisionBuffer colResults;
fnCollisionBuffer__Alloc(&colResults);
Landscape__FilterIgnoreTwo filter;
filter.vfptr = reinterpret_cast<IntersectionFilterVtbl *>(Offsets::VirtualTables::Landscape__FilterIgnoreTwo);
filter._obj1 = &localPlayer;
filter._obj2 = ⌖
fnLandscape__ObjectCollisionLine(
landscape,
0.0f,
&colResults,
&filter,
&from,
&to,
0.0f,
1,
4
);
bool hit = colResults._n != 0; //we hit objects
fnCollisionBuffer__Free(&colResults);
return hit;
}
You can also get collisions positions through CollisionBuffer._data.