Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Rewrite CHUDCrosshair, reintegrate with CHUDTarget
- Now authoritative over both wireframe and texture crosshair rendering
- Takes a transform and projects itself into 3D with a customizable depth-size metric
- Performs vertex transformation to allow arbitrary 3D positioning and rotation
- Uses new HUD flag to switch between wireframe and shader modes
- Shader mode reinits whenever its shader or texture are changed
  • Loading branch information
ProfLander committed Apr 25, 2025
commit e7bdc5a09531e8093e295604c00a1b1306de9429
285 changes: 169 additions & 116 deletions src/xrGame/HUDCrosshair.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,161 +5,214 @@
#include "stdafx.h"

#include "HUDCrosshair.h"
//.#include "UIStaticItem.h"
#include "../xrEngine/CustomHUD.h"
#include "../xrEngine/igame_persistent.h"
#include "ui_base.h"

string32 crosshair_shader = "hud\\cursor";
string32 crosshair_texture = "ui\\cursor";
float crosshair_near_size = 1.f;

CHUDCrosshair::CHUDCrosshair()
{
hShader->create("hud\\crosshair");
radius = 0;
strcpy(lastCrosshairShader, "");
strcpy(lastCrosshairTexture, "");
shaderWire->create("hud\\crosshair");
transform = Fmatrix().identity();
minRadius = 0.001f;
maxRadius = 0.004f;
crossColor = 0;
}


CHUDCrosshair::~CHUDCrosshair()
{
}

void CHUDCrosshair::Load()
{
//��� ������� � ��������� �� ����� ������
//����� ��������
cross_length_perc = pSettings->r_float(HUD_CURSOR_SECTION, "cross_length");
min_radius_perc = pSettings->r_float(HUD_CURSOR_SECTION, "min_radius");
max_radius_perc = pSettings->r_float(HUD_CURSOR_SECTION, "max_radius");
cross_color = pSettings->r_fcolor(HUD_CURSOR_SECTION, "cross_color").get();
minRadius = pSettings->r_float(HUD_CURSOR_SECTION, "min_radius");
maxRadius = pSettings->r_float(HUD_CURSOR_SECTION, "max_radius");
crossColor = pSettings->r_fcolor(HUD_CURSOR_SECTION, "cross_color").get();
}

//���������� radius �� min_radius �� max_radius
void CHUDCrosshair::SetDispersion(float disp)
void CHUDCrosshair::SetTransform(const Fmatrix& m)
{
Fvector4 r;
Fvector R = {VIEWPORT_NEAR * _sin(disp), 0.f, VIEWPORT_NEAR};
Device.mProject.transform(r, R);

Fvector2 scr_size;
scr_size.set(float(::Render->getTarget()->get_width()), float(::Render->getTarget()->get_height()));
float radius_pixels = _abs(r.x) * scr_size.x / 2.0f;
target_radius = radius_pixels;
transform.set(m);
}

#ifdef DEBUG
void CHUDCrosshair::SetFirstBulletDispertion(float fbdisp)
void CHUDCrosshair::SetColor(u32 c)
{
Fvector4 r;
Fvector R = { VIEWPORT_NEAR*_sin(fbdisp), 0.f, VIEWPORT_NEAR };
Device.mProject.transform (r,R);
crossColor = c;
}

Fvector2 scr_size;
scr_size.set (float(::Render->getTarget()->get_width()), float(::Render->getTarget()->get_height()));
fb_radius = _abs(r.x)*scr_size.x/2.0f;
void CHUDCrosshair::SetDispersion(float d)
{
// Stubbed out for now, as the existing code did not use the provided dispersion values
// Potential for a proper treatment later down the line
}

BOOL g_bDrawFirstBulletCrosshair = FALSE;
extern ENGINE_API BOOL g_bRendering;

void CHUDCrosshair::OnRenderFirstBulletDispertion()
static float lerp(float a, float b, float t)
{
VERIFY (g_bRendering);
Fvector2 center;
Fvector2 scr_size;
scr_size.set (float(::Render->getTarget()->get_width()), float(::Render->getTarget()->get_height()));
center.set (scr_size.x/2.0f, scr_size.y/2.0f);

UIRender->StartPrimitive (10, IUIRender::ptLineList, UI().m_currentPointType);

u32 fb_cross_color = color_rgba(255, 0, 0, 255); //red


float cross_length = /*cross_length_perc*/0.008f*scr_size.x;
float min_radius = min_radius_perc*scr_size.x;
float max_radius = max_radius_perc*scr_size.x;

clamp (target_radius , min_radius, max_radius);

float x_min = min_radius + fb_radius;
float x_max = x_min + cross_length;

float y_min = x_min;
float y_max = x_max;

// 0
UIRender->PushPoint(center.x, center.y + y_min, 0, fb_cross_color, 0,0);
UIRender->PushPoint(center.x, center.y + y_max, 0, fb_cross_color, 0,0);
// 1
UIRender->PushPoint(center.x, center.y - y_min, 0, fb_cross_color, 0,0);
UIRender->PushPoint(center.x, center.y - y_max, 0, fb_cross_color, 0,0);
// 2
UIRender->PushPoint(center.x + x_min, center.y, 0, fb_cross_color, 0,0);
UIRender->PushPoint(center.x + x_max, center.y, 0, fb_cross_color, 0,0);
// 3
UIRender->PushPoint(center.x - x_min, center.y, 0, fb_cross_color, 0,0);
UIRender->PushPoint(center.x - x_max, center.y, 0, fb_cross_color, 0,0);

// point
UIRender->PushPoint(center.x-0.5f, center.y, 0, fb_cross_color, 0,0);
UIRender->PushPoint(center.x+0.5f, center.y, 0, fb_cross_color, 0,0);


// render
UIRender->SetShader (*hShader);
UIRender->FlushPrimitive ();
return a * (1 - t) + b * t;
}
#endif

extern ENGINE_API BOOL g_bRendering;
void CHUDCrosshair::DeinitShaderCrosshair()
{
if (shaderCrosshair->inited())
{
shaderCrosshair->destroy();
}
}

void CHUDCrosshair::OnRender()
bool CHUDCrosshair::InitShaderCrosshair()
{
VERIFY(g_bRendering);
Fvector2 center;
Fvector2 scr_size;
scr_size.set(float(::Render->getTarget()->get_width()), float(::Render->getTarget()->get_height()));
center.set(scr_size.x / 2.0f, scr_size.y / 2.0f);
if (strcmp(lastCrosshairShader, crosshair_shader) || strcmp(lastCrosshairTexture, crosshair_texture))
{
DeinitShaderCrosshair();

UIRender->StartPrimitive(10, IUIRender::ptLineList, UI().m_currentPointType);
shaderCrosshair->create(crosshair_shader, crosshair_texture);

strcpy(lastCrosshairShader, crosshair_shader);
strcpy(lastCrosshairTexture, crosshair_texture);
}

float cross_length = cross_length_perc * scr_size.x;
float min_radius = min_radius_perc * scr_size.x;
float max_radius = max_radius_perc * scr_size.x;
return shaderCrosshair->inited();
}

clamp(target_radius, min_radius, max_radius);
void CHUDCrosshair::RenderShaderCrosshair()
{
UIRender->StartPrimitive(4, IUIRender::ptTriStrip, UI().m_currentPointType);

float x_min = min_radius + radius;
float x_max = x_min + cross_length;
Fvector4 pt;
Device.mFullTransform.transform(pt, transform.c);
pt.y = -pt.y;

float y_min = x_min;
float y_max = x_max;
Fvector2 scr_size = { float(Device.dwWidth), float(Device.dwHeight) };

// 0
UIRender->PushPoint(center.x, center.y + y_min, 0, cross_color, 0, 0);
UIRender->PushPoint(center.x, center.y + y_max, 0, cross_color, 0, 0);
// 1
UIRender->PushPoint(center.x, center.y - y_min, 0, cross_color, 0, 0);
UIRender->PushPoint(center.x, center.y - y_max, 0, cross_color, 0, 0);
// 2
UIRender->PushPoint(center.x + x_min, center.y, 0, cross_color, 0, 0);
UIRender->PushPoint(center.x + x_max, center.y, 0, cross_color, 0, 0);
// 3
UIRender->PushPoint(center.x - x_min, center.y, 0, cross_color, 0, 0);
UIRender->PushPoint(center.x - x_max, center.y, 0, cross_color, 0, 0);
Fvector verts[4] = {
{-maxRadius, -maxRadius},
{-maxRadius, maxRadius},
{maxRadius, -maxRadius},
{maxRadius, maxRadius},
};

// point
UIRender->PushPoint(center.x - 0.5f, center.y, 0, cross_color, 0, 0);
UIRender->PushPoint(center.x + 0.5f, center.y, 0, cross_color, 0, 0);
Fvector2 uvs[4] = {
{0, 1},
{0, 0},
{1, 1},
{1, 0},
};

// Bring our transform into view space
Fvector pos = Fvector();
transform.transform_tiny(pos);
Device.mView.transform_tiny(pos);

// render
UIRender->SetShader(*hShader);
UIRender->FlushPrimitive();
// Project without W-divide to retrieve linear depth
Fvector pos_ = Fvector().set(pos);
Device.mProject.transform_tiny(pos_);

// Calculate size from linear depth
float zNear = Device.ViewportNear;
float zFar = g_pGamePersistent->Environment().CurrentEnv->far_plane;
float size = lerp(zNear + (crosshair_near_size - 1), zFar, (pos_.z - zNear) / (zFar - zNear));

if (!fsimilar(target_radius, radius))
// Transform and push vertices
for (int i = 0; i < 4; i++)
{
//here was crosshair innertion emulation
radius = target_radius;
Fvector vert = verts[i];
vert.mul(size);
transform.transform_tiny(vert);
Device.mView.transform_tiny(vert);
Device.mProject.transform(vert);
vert.x = (vert.x + 1.f) * 0.5f * scr_size.x;
vert.y = (-vert.y + 1.f) * 0.5f * scr_size.y;
Fvector2 uv = uvs[i];
UIRender->PushPoint(vert.x, vert.y, 0, crossColor, uv.x, uv.y);
}

// Draw
UIRender->SetShader(*shaderCrosshair);
UIRender->FlushPrimitive();
}

void CHUDCrosshair::RenderWireCrosshair()
{
// Fetch the render target size
Fvector2 scr_size = {
float(::Render->getTarget()->get_width()),
float(::Render->getTarget()->get_height())
};
#ifdef DEBUG
if (g_bDrawFirstBulletCrosshair)
OnRenderFirstBulletDispertion();
#endif

// Create vertices from our size metrics
Fvector verts[8] = {
{ minRadius, 0 },
{ maxRadius, 0 },
{ -minRadius, 0 },
{ -maxRadius, 0 },
{ 0, minRadius },
{ 0, maxRadius },
{ 0, -minRadius },
{ 0, -maxRadius },
};

// Transform into view space
Fvector pos = Fvector();
transform.transform_tiny(pos);
Device.mView.transform_tiny(pos);

// Project without W-divide to retrieve linear depth
Fvector pos_ = Fvector().set(pos);
Device.mProject.transform_tiny(pos_);

// Calculate size from linear depth
float zNear = Device.ViewportNear;
float zFar = g_pGamePersistent->Environment().CurrentEnv->far_plane;
float size = lerp(zNear + (crosshair_near_size - 1), zFar, (pos_.z - zNear) / (zFar - zNear));

// Project into NDC with W-divide
Device.mProject.transform(pos);

// Project vertices for accurate scaling
UIRender->StartPrimitive(8, IUIRender::ptLineList, UI().m_currentPointType);
for (int i = 0; i < 8; i++)
{
Fvector vert = verts[i];
vert.mul(size);
transform.transform_tiny(vert);
Device.mView.transform_tiny(vert);
Device.mProject.transform(vert);
vert.x = (vert.x + 1.f) * 0.5f * scr_size.x;
vert.y = (-vert.y + 1.f) * 0.5f * scr_size.y;
UIRender->PushPoint(vert.x, vert.y, 0, crossColor, 0, 0);
}

// Apply perspective divide to aim point and transform into screen space
pos.x = (pos.x + 1.f) * 0.5f * scr_size.x;
pos.y = (-pos.y + 1.f) * 0.5f * scr_size.y;

// Render a 1px wide line for the center dot
UIRender->PushPoint(pos.x - 0.5f, pos.y, 0, crossColor, 0, 0);
UIRender->PushPoint(pos.x + 0.5f, pos.y, 0, crossColor, 0, 0);

UIRender->SetShader(*shaderWire);
UIRender->FlushPrimitive();
}

void CHUDCrosshair::OnRender()
{
VERIFY(g_bRendering);

if (psHUD_Flags.is(HUD_SHADER_CROSSHAIR))
{
if (InitShaderCrosshair())
RenderShaderCrosshair();
}
else
{
DeinitShaderCrosshair();
RenderWireCrosshair();
}
}
41 changes: 20 additions & 21 deletions src/xrGame/HUDCrosshair.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,29 @@
class CHUDCrosshair
{
private:
float cross_length_perc;
float min_radius_perc;
float max_radius_perc;

//������� ������ �������
float radius;
float target_radius;
#ifdef DEBUG
float fb_radius;
#endif
//ref_geom hGeomLine;
ui_shader hShader;
public:
u32 cross_color;
Fmatrix transform;
float minRadius;
float maxRadius;
u32 crossColor;

ui_shader shaderWire;
ui_shader shaderCrosshair;

string32 lastCrosshairShader;
string32 lastCrosshairTexture;

void DeinitShaderCrosshair();
bool InitShaderCrosshair();
void RenderShaderCrosshair();
void RenderWireCrosshair();

public:
CHUDCrosshair();
~CHUDCrosshair();

void OnRender();
void SetDispersion(float disp);
#ifdef DEBUG
void SetFirstBulletDispertion(float fbdisp);
void OnRenderFirstBulletDispertion();
#endif

void Load();
void SetTransform(const Fmatrix& m);
void SetColor(u32 c);
void SetDispersion(float d);
void OnRender();
};
Loading