Compare commits
No commits in common. "5da5a754fe88b925b17eeafd15e1b19b6b267fa9" and "fa3a4c66c9f1aac36120b4495f2ee286c4c03f1d" have entirely different histories.
5da5a754fe
...
fa3a4c66c9
@ -1,21 +0,0 @@
|
||||
{
|
||||
"enabled": true,
|
||||
|
||||
"referenceResolution": [1280, 720],
|
||||
|
||||
"color": [1.0, 1.0, 1.0],
|
||||
"cl_crosshairalpha": 1.0,
|
||||
"cl_crosshairthickness": 2.0,
|
||||
|
||||
"centerGapPx": 10.0,
|
||||
|
||||
"top": {
|
||||
"lengthPx": 14.0,
|
||||
"angleDeg": 90.0
|
||||
},
|
||||
|
||||
"arms": [
|
||||
{ "lengthPx": 20.0, "angleDeg": 210.0 },
|
||||
{ "lengthPx": 20.0, "angleDeg": 330.0 }
|
||||
]
|
||||
}
|
||||
290
src/Space.cpp
290
src/Space.cpp
@ -336,19 +336,6 @@ namespace ZL
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
crosshairCfgLoaded = loadCrosshairConfig("resources/config/crosshair_config.json");
|
||||
std::cerr << "[Crosshair] loaded=" << crosshairCfgLoaded
|
||||
<< " enabled=" << crosshairCfg.enabled
|
||||
<< " w=" << Environment::width << " h=" << Environment::height
|
||||
<< " alpha=" << crosshairCfg.alpha
|
||||
<< " thickness=" << crosshairCfg.thicknessPx
|
||||
<< " gap=" << crosshairCfg.gapPx << "\n";
|
||||
if (!crosshairCfgLoaded) {
|
||||
std::cerr << "Failed to load crosshair_config.json, using defaults\n";
|
||||
}
|
||||
|
||||
|
||||
|
||||
textRenderer = std::make_unique<ZL::TextRenderer>();
|
||||
if (!textRenderer->init(renderer, "resources/fonts/DroidSans.ttf", 32, CONST_ZIP_FILE)) {
|
||||
std::cerr << "Failed to init TextRenderer\n";
|
||||
@ -579,7 +566,6 @@ namespace ZL
|
||||
drawBoxesLabels();
|
||||
drawShip();
|
||||
|
||||
drawCrosshair();
|
||||
drawTargetHud();
|
||||
CheckGlError();
|
||||
}
|
||||
@ -707,230 +693,6 @@ namespace ZL
|
||||
//#endif
|
||||
}
|
||||
|
||||
// хелпер прицела: добавляет повернутую 2D-линию в меш прицела
|
||||
static void AppendRotatedRect2D(
|
||||
VertexDataStruct& out,
|
||||
float cx, float cy,
|
||||
float length, float thickness,
|
||||
float angleRad,
|
||||
float z,
|
||||
const Eigen::Vector3f& rgb)
|
||||
{
|
||||
// прямоугольник вдоль локальной оси +X: [-L/2..+L/2] и [-T/2..+T/2]
|
||||
float hl = length * 0.5f;
|
||||
float ht = thickness * 0.5f;
|
||||
|
||||
Eigen::Vector2f p0(-hl, -ht);
|
||||
Eigen::Vector2f p1(-hl, +ht);
|
||||
Eigen::Vector2f p2(+hl, +ht);
|
||||
Eigen::Vector2f p3(+hl, -ht);
|
||||
|
||||
float c = std::cos(angleRad);
|
||||
float s = std::sin(angleRad);
|
||||
|
||||
auto rot = [&](const Eigen::Vector2f& p) -> Vector3f {
|
||||
float rx = p.x() * c - p.y() * s;
|
||||
float ry = p.x() * s + p.y() * c;
|
||||
return Vector3f(cx + rx, cy + ry, z);
|
||||
};
|
||||
|
||||
Vector3f v0 = rot(p0);
|
||||
Vector3f v1 = rot(p1);
|
||||
Vector3f v2 = rot(p2);
|
||||
Vector3f v3 = rot(p3);
|
||||
|
||||
// 2 треугольника
|
||||
out.PositionData.push_back(v0);
|
||||
out.PositionData.push_back(v1);
|
||||
out.PositionData.push_back(v2);
|
||||
out.PositionData.push_back(v2);
|
||||
out.PositionData.push_back(v3);
|
||||
out.PositionData.push_back(v0);
|
||||
|
||||
for (int i = 0; i < 6; ++i) out.ColorData.push_back(rgb);
|
||||
}
|
||||
|
||||
bool Space::loadCrosshairConfig(const std::string& path)
|
||||
{
|
||||
using json = nlohmann::json;
|
||||
|
||||
std::string content;
|
||||
try {
|
||||
if (std::string(CONST_ZIP_FILE).empty()) content = readTextFile(path);
|
||||
else {
|
||||
auto buf = readFileFromZIP(path, CONST_ZIP_FILE);
|
||||
if (buf.empty()) return false;
|
||||
content.assign(buf.begin(), buf.end());
|
||||
}
|
||||
json j = json::parse(content);
|
||||
|
||||
if (j.contains("enabled")) crosshairCfg.enabled = j["enabled"].get<bool>();
|
||||
|
||||
if (j.contains("referenceResolution") && j["referenceResolution"].is_array() && j["referenceResolution"].size() == 2) {
|
||||
crosshairCfg.refW = j["referenceResolution"][0].get<int>();
|
||||
crosshairCfg.refH = j["referenceResolution"][1].get<int>();
|
||||
}
|
||||
|
||||
if (j.contains("scale")) crosshairCfg.scaleMul = j["scale"].get<float>();
|
||||
crosshairCfg.scaleMul = std::clamp(crosshairCfg.scaleMul, 0.1f, 3.0f);
|
||||
|
||||
if (j.contains("color") && j["color"].is_array() && j["color"].size() == 3) {
|
||||
crosshairCfg.color = Eigen::Vector3f(
|
||||
j["color"][0].get<float>(),
|
||||
j["color"][1].get<float>(),
|
||||
j["color"][2].get<float>()
|
||||
);
|
||||
}
|
||||
|
||||
if (j.contains("cl_crosshairalpha")) crosshairCfg.alpha = j["cl_crosshairalpha"].get<float>();
|
||||
if (j.contains("cl_crosshairthickness")) crosshairCfg.thicknessPx = j["cl_crosshairthickness"].get<float>();
|
||||
if (j.contains("centerGapPx")) crosshairCfg.gapPx = j["centerGapPx"].get<float>();
|
||||
|
||||
if (j.contains("top") && j["top"].is_object()) {
|
||||
auto t = j["top"];
|
||||
if (t.contains("lengthPx")) crosshairCfg.topLenPx = t["lengthPx"].get<float>();
|
||||
if (t.contains("angleDeg")) crosshairCfg.topAngleDeg = t["angleDeg"].get<float>();
|
||||
}
|
||||
|
||||
crosshairCfg.arms.clear();
|
||||
if (j.contains("arms") && j["arms"].is_array()) {
|
||||
for (auto& a : j["arms"]) {
|
||||
CrosshairConfig::Arm arm;
|
||||
arm.lenPx = a.value("lengthPx", 20.0f);
|
||||
arm.angleDeg = a.value("angleDeg", 210.0f);
|
||||
crosshairCfg.arms.push_back(arm);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// дефолт
|
||||
crosshairCfg.arms.push_back({ 20.0f, 210.0f });
|
||||
crosshairCfg.arms.push_back({ 20.0f, 330.0f });
|
||||
}
|
||||
|
||||
// clamp
|
||||
crosshairCfg.alpha = std::clamp(crosshairCfg.alpha, 0.0f, 1.0f);
|
||||
crosshairCfg.thicknessPx = max(0.5f, crosshairCfg.thicknessPx);
|
||||
crosshairCfg.gapPx = max(0.0f, crosshairCfg.gapPx);
|
||||
|
||||
crosshairMeshValid = false; // пересобрать
|
||||
return true;
|
||||
}
|
||||
catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// пересобирает mesh прицела при изменениях/ресайзе
|
||||
void Space::rebuildCrosshairMeshIfNeeded()
|
||||
{
|
||||
if (!crosshairCfg.enabled) return;
|
||||
|
||||
// если ничего не изменилось — не трогаем VBO
|
||||
if (crosshairMeshValid &&
|
||||
crosshairLastW == Environment::width &&
|
||||
crosshairLastH == Environment::height &&
|
||||
std::abs(crosshairLastAlpha - crosshairCfg.alpha) < 1e-6f &&
|
||||
std::abs(crosshairLastThickness - crosshairCfg.thicknessPx) < 1e-6f &&
|
||||
std::abs(crosshairLastGap - crosshairCfg.gapPx) < 1e-6f &&
|
||||
std::abs(crosshairLastScaleMul - crosshairCfg.scaleMul) < 1e-6f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
crosshairLastW = Environment::width;
|
||||
crosshairLastH = Environment::height;
|
||||
crosshairLastAlpha = crosshairCfg.alpha;
|
||||
crosshairLastThickness = crosshairCfg.thicknessPx;
|
||||
crosshairLastGap = crosshairCfg.gapPx;
|
||||
crosshairLastScaleMul = crosshairCfg.scaleMul;
|
||||
|
||||
float cx = Environment::width * 0.5f;
|
||||
float cy = Environment::height * 0.5f;
|
||||
|
||||
// масштаб от reference (стандартно: по высоте)
|
||||
float scale = (crosshairCfg.refH > 0) ? (Environment::height / (float)crosshairCfg.refH) : 1.0f;
|
||||
scale *= crosshairCfg.scaleMul;
|
||||
|
||||
float thickness = crosshairCfg.thicknessPx * scale;
|
||||
float gap = crosshairCfg.gapPx * scale;
|
||||
|
||||
VertexDataStruct v;
|
||||
v.PositionData.reserve(6 * (1 + (int)crosshairCfg.arms.size()));
|
||||
v.ColorData.reserve(6 * (1 + (int)crosshairCfg.arms.size()));
|
||||
|
||||
const float z = 0.0f;
|
||||
const Eigen::Vector3f rgb = crosshairCfg.color;
|
||||
|
||||
auto deg2rad = [](float d) { return d * 3.1415926535f / 180.0f; };
|
||||
|
||||
// TOP (короткая палочка сверху)
|
||||
{
|
||||
float len = crosshairCfg.topLenPx * scale;
|
||||
float ang = deg2rad(crosshairCfg.topAngleDeg);
|
||||
|
||||
// сдвигаем сегмент от центра на gap + len/2 по направлению
|
||||
float dx = std::cos(ang);
|
||||
float dy = std::sin(ang);
|
||||
float mx = cx + dx * (gap + len * 0.5f);
|
||||
float my = cy + dy * (gap + len * 0.5f);
|
||||
|
||||
AppendRotatedRect2D(v, mx, my, len, thickness, ang, z, rgb);
|
||||
}
|
||||
|
||||
// ARMS (2 луча вниз-влево и вниз-вправо)
|
||||
for (auto& a : crosshairCfg.arms)
|
||||
{
|
||||
float len = a.lenPx * scale;
|
||||
float ang = deg2rad(a.angleDeg);
|
||||
|
||||
float dx = std::cos(ang);
|
||||
float dy = std::sin(ang);
|
||||
float mx = cx + dx * (gap + len * 0.5f);
|
||||
float my = cy + dy * (gap + len * 0.5f);
|
||||
|
||||
AppendRotatedRect2D(v, mx, my, len, thickness, ang, z, rgb);
|
||||
}
|
||||
|
||||
crosshairMesh.AssignFrom(v);
|
||||
crosshairMesh.RefreshVBO();
|
||||
crosshairMeshValid = true;
|
||||
}
|
||||
|
||||
void Space::drawCrosshair()
|
||||
{
|
||||
if (!crosshairCfg.enabled) return;
|
||||
|
||||
rebuildCrosshairMeshIfNeeded();
|
||||
if (!crosshairMeshValid) return;
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
renderer.shaderManager.PushShader("defaultColor");
|
||||
renderer.PushProjectionMatrix((float)Environment::width, (float)Environment::height, 0.f, 1.f);
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
|
||||
renderer.EnableVertexAttribArray("vPosition");
|
||||
renderer.EnableVertexAttribArray("vColor");
|
||||
|
||||
Eigen::Vector4f uColor(crosshairCfg.color.x(), crosshairCfg.color.y(), crosshairCfg.color.z(), crosshairCfg.alpha);
|
||||
renderer.RenderUniform4fv("uColor", uColor.data());
|
||||
|
||||
renderer.DrawVertexRenderStruct(crosshairMesh);
|
||||
|
||||
renderer.DisableVertexAttribArray("vPosition");
|
||||
renderer.DisableVertexAttribArray("vColor");
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.shaderManager.PopShader();
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
int Space::pickTargetId() const
|
||||
{
|
||||
int bestId = -1;
|
||||
@ -1096,39 +858,42 @@ namespace ZL
|
||||
Vector3f shooterVel = ForwardFromRotation(Environment::shipState.rotation) * Environment::shipState.velocity;
|
||||
Vector3f targetVel = ForwardFromRotation(st.rotation) * st.velocity;
|
||||
|
||||
// условие "если враг не движется — круг не рисуем"
|
||||
const float minTargetSpeed = 0.5f; // подобрать (в твоих единицах)
|
||||
bool targetMoving = (targetVel.norm() > minTargetSpeed);
|
||||
|
||||
// альфа круга
|
||||
float leadAlpha = targetMoving ? 1.0f : 0.5f;
|
||||
|
||||
Vector3f leadWorld = shipWorld;
|
||||
bool haveLead = false;
|
||||
|
||||
// чтобы круг не улетал далеко: максимум 4 секунды (подстроить под игру)
|
||||
float distToTarget = (Environment::shipState.position - shipWorld).norm();
|
||||
float maxLeadTime = std::clamp((distToTarget / projectileSpeed) * 1.2f, 0.05f, 4.0f);
|
||||
//if (targetMoving) {
|
||||
// float tLead = 0.0f;
|
||||
// if (SolveLeadInterceptTime(shooterPos, shooterVel, shipWorld, targetVel, projectileSpeed, tLead)) {
|
||||
// // ограничим случаи, чтобы круг не улетал далеко
|
||||
// if (tLead > 0.0f && tLead < 8.0f) {
|
||||
// // подобрать максимум (сек)
|
||||
// leadWorld = shipWorld + targetVel * tLead;
|
||||
// haveLead = true;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
if (!targetMoving) {
|
||||
// Цель стоит: рисуем lead прямо на ней, но полупрозрачный
|
||||
leadWorld = shipWorld;
|
||||
haveLead = true;
|
||||
}
|
||||
else {
|
||||
if (targetMoving) {
|
||||
float tLead = 0.0f;
|
||||
float distToTarget = (Environment::shipState.position - shipWorld).norm();
|
||||
|
||||
// 1) Пытаемся “правильное” решение перехвата
|
||||
bool ok = SolveLeadInterceptTime(shooterPos, shooterVel, shipWorld, targetVel, projectileSpeed, tLead);
|
||||
const float leadMaxDist = 2500.0f; // максимум
|
||||
float allowedDist = min(distToTarget, leadMaxDist);
|
||||
|
||||
// 2) Если решения нет / оно плохое — fallback (чтобы круг не пропадал при пролёте "вбок")
|
||||
// Это ключевое изменение: lead всегда будет.
|
||||
if (!ok || !(tLead > 0.0f) || tLead > maxLeadTime) {
|
||||
tLead = std::clamp(distToTarget / projectileSpeed, 0.05f, maxLeadTime);
|
||||
}
|
||||
// + небольшой запас 10–20% чтобы не моргало на границе
|
||||
const float maxLeadTime = (allowedDist / projectileSpeed) * 1.2f;
|
||||
|
||||
if (SolveLeadInterceptTime(shooterPos, shooterVel, shipWorld, targetVel, projectileSpeed, tLead)) {
|
||||
if (tLead > 0.0f && tLead < maxLeadTime) {
|
||||
leadWorld = shipWorld + targetVel * tLead;
|
||||
haveLead = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2) проекция
|
||||
float ndcX, ndcY, ndcZ, clipW;
|
||||
@ -1200,10 +965,6 @@ namespace ZL
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
|
||||
// базовый цвет для HUD (скобки/стрелка) — непрозрачный
|
||||
Eigen::Vector4f hudColor = enemyColor;
|
||||
renderer.RenderUniform4fv("uColor", hudColor.data());
|
||||
|
||||
// рисуем кружок упреждения (только если есть решение)
|
||||
if (haveLead) {
|
||||
float leadNdcX, leadNdcY, leadNdcZ, leadClipW;
|
||||
@ -1220,16 +981,9 @@ namespace ZL
|
||||
float innerR = max(1.0f, r - thicknessPx);
|
||||
float outerR = r + thicknessPx;
|
||||
|
||||
// для lead indicator alpha: 1.0 если движется, 0.5 если стоит
|
||||
Eigen::Vector4f leadColor = enemyColor;
|
||||
leadColor.w() = leadAlpha;
|
||||
renderer.RenderUniform4fv("uColor", leadColor.data());
|
||||
|
||||
VertexDataStruct ring = MakeRing2D(lx, ly, innerR, outerR, 0.0f, 32, enemyColor);
|
||||
hudTempMesh.AssignFrom(ring);
|
||||
renderer.DrawVertexRenderStruct(hudTempMesh);
|
||||
|
||||
renderer.RenderUniform4fv("uColor", hudColor.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
36
src/Space.h
36
src/Space.h
@ -136,42 +136,6 @@ namespace ZL {
|
||||
int pickTargetId() const; // выбирает цель (пока: ближайший живой удаленный игрок)
|
||||
|
||||
void clearTextRendererCache();
|
||||
|
||||
// Crosshair HUD
|
||||
struct CrosshairConfig {
|
||||
bool enabled = true;
|
||||
int refW = 1280;
|
||||
int refH = 720;
|
||||
|
||||
float scaleMul = 1.0f;
|
||||
|
||||
Eigen::Vector3f color = { 1.f, 1.f, 1.f };
|
||||
float alpha = 1.0f; // cl_crosshairalpha
|
||||
float thicknessPx = 2.0f; // cl_crosshairthickness
|
||||
float gapPx = 10.0f;
|
||||
|
||||
float topLenPx = 14.0f;
|
||||
float topAngleDeg = 90.0f;
|
||||
|
||||
struct Arm { float lenPx; float angleDeg; };
|
||||
std::vector<Arm> arms;
|
||||
};
|
||||
|
||||
CrosshairConfig crosshairCfg;
|
||||
bool crosshairCfgLoaded = false;
|
||||
|
||||
// кеш геометрии
|
||||
VertexRenderStruct crosshairMesh;
|
||||
bool crosshairMeshValid = false;
|
||||
int crosshairLastW = 0, crosshairLastH = 0;
|
||||
float crosshairLastAlpha = -1.0f;
|
||||
float crosshairLastThickness = -1.0f;
|
||||
float crosshairLastGap = -1.0f;
|
||||
float crosshairLastScaleMul = -1.0f;
|
||||
|
||||
bool loadCrosshairConfig(const std::string& path);
|
||||
void rebuildCrosshairMeshIfNeeded();
|
||||
void drawCrosshair();
|
||||
};
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user