Files
coah80-LegacyVulkEdition/Minecraft.Client/CrossPlatform/NkHUD.cpp

321 lines
11 KiB
C++

#define NK_INCLUDE_FIXED_TYPES
#define NK_INCLUDE_STANDARD_IO
#define NK_INCLUDE_STANDARD_VARARGS
#define NK_INCLUDE_DEFAULT_ALLOCATOR
#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
#define NK_INCLUDE_FONT_BAKING
#define NK_INCLUDE_DEFAULT_FONT
#include "nuklear.h"
#include "NkHUD.h"
#include "NkCommon.h"
#include "NuklearBridge.h"
#include <cstdio>
#include <cstring>
#include <cmath>
static const float BASE_W = 1280.0f;
static const float BASE_H = 720.0f;
static const float HUD_SCALE = 3.0f;
static const float CROSSHAIR_NATIVE = 15.0f;
static const float HOLDER_X = 366.0f;
static const float HOLDER_Y = 480.0f;
static const float HOTBAR_W = 182.0f;
static const float HOTBAR_H = 22.0f;
static const int HOTBAR_SLOTS = 9;
static const float HOTBAR_SLOT_PITCH = 20.0f;
static const float HOTBAR_SLOT_PAD = 3.0f;
static const float HOTBAR_SELECTED_SIZE = 24.0f;
static const float ICON_SIZE = 9.0f;
static const float ICON_SPACING = 8.0f;
static const int ICON_ROW_COUNT = 10;
static const float XP_BAR_W = 182.0f;
static const float XP_BAR_H = 5.0f;
static const float ARMOR_Y_OFF = 0.0f;
static const float HEALTH_Y_OFF = 10.0f;
static const float XP_Y_OFF = 20.0f;
static const float HOTBAR_Y_OFF = 26.0f;
static const float LEFT_X_OFF = 0.0f;
static const float RIGHT_X_OFF = 101.0f;
static const float HOTBAR_X_OFF = 0.0f;
static const float XP_LEVEL_X_OFF = 81.0f;
static const float XP_LEVEL_Y_OFF = 10.0f;
static const float XP_LEVEL_W = 20.0f;
static struct nk_image imageFromHUD(const lve::ui::HUDTextureSlot &slot)
{
return nk_image_id(slot.id);
}
static lve::ui::HUDTextureSlot loadHUDSlot(const char *path)
{
int w = 0, h = 0;
struct nk_image img = lve::ui::loadTextureWithSize(path, &w, &h);
return {img.handle.id, w, h};
}
namespace lve { namespace ui {
bool initHUDTextures(HUDTextures &tex)
{
const char *base = "../Assets/ui/skinHDGraphicsHud/";
char path[256];
auto load = [&](const char *file) -> HUDTextureSlot {
snprintf(path, sizeof(path), "%s%s", base, file);
return loadHUDSlot(path);
};
tex.crosshair = load("27_HUD_Crosshair.png");
tex.hotbarBack = load("29_hotbar_item_back.png");
tex.hotbarSelected = load("28_hotbar_item_selected.png");
tex.healthFull = load("34_Health_Full.png");
tex.healthHalf = load("30_Health_Half.png");
tex.healthBg = load("38_Health_Background.png");
tex.foodFull = load("44_HUD_Food_Full.png");
tex.foodHalf = load("40_HUD_Food_Half.png");
tex.foodBg = load("48_HUD_Food_Background.png");
tex.armorFull = load("54_HUD_Armour_Full.png");
tex.armorHalf = load("53_HUD_Armour_Half.png");
tex.armorEmpty = load("55_HUD_Armour_Empty.png");
tex.airBubble = load("57_HUD_Air_Bubble.png");
tex.airPop = load("56_HUD_Air_Pop.png");
tex.xpBarFull = load("51_experience_bar_full.png");
tex.xpBarEmpty = load("52_experience_bar_empty.png");
tex.loaded = (tex.crosshair.w > 0 && tex.hotbarBack.w > 0 &&
tex.healthFull.w > 0 && tex.foodFull.w > 0 &&
tex.xpBarEmpty.w > 0);
if (!tex.loaded)
fprintf(stderr, "[NkHUD] WARNING: some HUD textures failed to load\n");
return tex.loaded;
}
static void drawCrosshair(struct nk_command_buffer *canvas, int winW, int winH,
const HUDTextures &tex, float sx, float sy)
{
if (tex.crosshair.w <= 0) return;
float w = CROSSHAIR_NATIVE * HUD_SCALE * sx;
float h = CROSSHAIR_NATIVE * HUD_SCALE * sy;
float x = (float)winW * 0.5f - w * 0.5f;
float y = (float)winH * 0.5f - h * 0.5f;
struct nk_image img = imageFromHUD(tex.crosshair);
nk_draw_image(canvas, nk_rect(x, y, w, h), &img, nk_rgba(255, 255, 255, 255));
}
static void drawHotbar(struct nk_command_buffer *canvas, float hx, float hy,
float sx, float sy, const HUDTextures &tex, int selected)
{
if (tex.hotbarBack.w <= 0) return;
float barX = hx + HOTBAR_X_OFF * HUD_SCALE * sx;
float barY = hy + HOTBAR_Y_OFF * HUD_SCALE * sy;
float barW = HOTBAR_W * HUD_SCALE * sx;
float barH = HOTBAR_H * HUD_SCALE * sy;
struct nk_image back = imageFromHUD(tex.hotbarBack);
nk_draw_image(canvas, nk_rect(barX, barY, barW, barH), &back, nk_rgba(255, 255, 255, 255));
if (tex.hotbarSelected.w <= 0 || selected < 0 || selected >= HOTBAR_SLOTS) return;
float selW = HOTBAR_SELECTED_SIZE * HUD_SCALE * sx;
float selH = HOTBAR_SELECTED_SIZE * HUD_SCALE * sy;
float pitch = HOTBAR_SLOT_PITCH * HUD_SCALE * sx;
float padX = HOTBAR_SLOT_PAD * HUD_SCALE * sx;
float selX = barX + padX - (2.0f * HUD_SCALE * sx) + (float)selected * pitch;
float selY = barY - (1.0f * HUD_SCALE * sy);
struct nk_image sel = imageFromHUD(tex.hotbarSelected);
nk_draw_image(canvas, nk_rect(selX, selY, selW, selH), &sel, nk_rgba(255, 255, 255, 255));
}
static void drawIconRow(struct nk_command_buffer *canvas,
float baseX, float baseY, float sx, float sy,
const HUDTextureSlot &bg, const HUDTextureSlot &full,
const HUDTextureSlot &half, int value, bool mirror)
{
float iw = ICON_SIZE * HUD_SCALE * sx;
float ih = ICON_SIZE * HUD_SCALE * sy;
float step = ICON_SPACING * HUD_SCALE * sx;
for (int i = 0; i < ICON_ROW_COUNT; i++)
{
float ix = mirror
? baseX + (float)(ICON_ROW_COUNT - 1 - i) * step
: baseX + (float)i * step;
if (bg.w > 0)
{
struct nk_image bgImg = nk_image_id(bg.id);
nk_draw_image(canvas, nk_rect(ix, baseY, iw, ih), &bgImg, nk_rgba(255, 255, 255, 255));
}
int halfPoints = i * 2;
if (halfPoints + 1 < value && full.w > 0)
{
struct nk_image fImg = nk_image_id(full.id);
nk_draw_image(canvas, nk_rect(ix, baseY, iw, ih), &fImg, nk_rgba(255, 255, 255, 255));
}
else if (halfPoints < value && half.w > 0)
{
struct nk_image hImg = nk_image_id(half.id);
nk_draw_image(canvas, nk_rect(ix, baseY, iw, ih), &hImg, nk_rgba(255, 255, 255, 255));
}
}
}
static void drawAirBubbles(struct nk_command_buffer *canvas,
float baseX, float baseY, float sx, float sy,
const HUDTextures &tex, int bubbles)
{
float iw = ICON_SIZE * HUD_SCALE * sx;
float ih = ICON_SIZE * HUD_SCALE * sy;
float step = ICON_SPACING * HUD_SCALE * sx;
for (int i = 0; i < ICON_ROW_COUNT; i++)
{
float ix = baseX + (float)(ICON_ROW_COUNT - 1 - i) * step;
if (i < bubbles && tex.airBubble.w > 0)
{
struct nk_image img = imageFromHUD(tex.airBubble);
nk_draw_image(canvas, nk_rect(ix, baseY, iw, ih), &img, nk_rgba(255, 255, 255, 255));
}
else if (tex.airPop.w > 0)
{
struct nk_image img = imageFromHUD(tex.airPop);
nk_draw_image(canvas, nk_rect(ix, baseY, iw, ih), &img, nk_rgba(255, 255, 255, 255));
}
}
}
static void drawXPBar(struct nk_command_buffer *canvas, float hx, float hy,
float sx, float sy, const HUDTextures &tex,
float progress, int level)
{
float barX = hx + LEFT_X_OFF * HUD_SCALE * sx;
float barY = hy + XP_Y_OFF * HUD_SCALE * sy;
float barW = XP_BAR_W * HUD_SCALE * sx;
float barH = XP_BAR_H * HUD_SCALE * sy;
if (tex.xpBarEmpty.w > 0)
{
struct nk_image empty = imageFromHUD(tex.xpBarEmpty);
nk_draw_image(canvas, nk_rect(barX, barY, barW, barH), &empty, nk_rgba(255, 255, 255, 255));
}
if (tex.xpBarFull.w > 0 && progress > 0.0f)
{
float fillW = barW * progress;
struct nk_image sub = nk_subimage_id(tex.xpBarFull.id,
(unsigned short)tex.xpBarFull.w, (unsigned short)tex.xpBarFull.h,
nk_rect(0, 0, (float)tex.xpBarFull.w * progress, (float)tex.xpBarFull.h));
nk_draw_image(canvas, nk_rect(barX, barY, fillW, barH), &sub, nk_rgba(255, 255, 255, 255));
}
if (level <= 0) return;
nk_context *ctx = getNkContext();
const struct nk_user_font *font = ctx->style.font;
if (font == nullptr) return;
char buf[16];
snprintf(buf, sizeof(buf), "%d", level);
int len = (int)strlen(buf);
float tw = font->width(font->userdata, font->height, buf, len);
float levelX = hx + XP_LEVEL_X_OFF * HUD_SCALE * sx;
float levelW = XP_LEVEL_W * HUD_SCALE * sx;
float tx = levelX + (levelW - tw) * 0.5f;
float ty = hy + XP_LEVEL_Y_OFF * HUD_SCALE * sy;
nk_draw_text(canvas, nk_rect(tx + 1, ty + 1, tw + 2, font->height),
buf, len, font, nk_rgba(0, 0, 0, 0), nk_rgba(0, 0, 0, 255));
nk_draw_text(canvas, nk_rect(tx, ty, tw, font->height),
buf, len, font, nk_rgba(0, 0, 0, 0), nk_rgba(128, 255, 32, 255));
}
void renderHUD(nk_context *ctx, int winW, int winH, HUDTextures &tex)
{
if (!tex.loaded) return;
float sx = (float)winW / BASE_W;
float sy = (float)winH / BASE_H;
pushCleanStyle(ctx);
if (nk_begin(ctx, "HUD", nk_rect(0, 0, (float)winW, (float)winH),
NK_WINDOW_NO_SCROLLBAR | NK_WINDOW_NO_INPUT))
{
struct nk_command_buffer *canvas = nk_window_get_canvas(ctx);
if (canvas == nullptr)
{
nk_end(ctx);
popCleanStyle(ctx);
return;
}
drawCrosshair(canvas, winW, winH, tex, sx, sy);
float hx = HOLDER_X * sx;
float hy = HOLDER_Y * sy;
int selectedSlot = 0;
int health = 20;
int food = 20;
int armor = 20;
int air = 10;
float xpProgress = 0.45f;
int xpLevel = 7;
bool showArmor = true;
bool showAir = false;
drawIconRow(canvas,
hx + LEFT_X_OFF * HUD_SCALE * sx,
hy + HEALTH_Y_OFF * HUD_SCALE * sy,
sx, sy, tex.healthBg, tex.healthFull, tex.healthHalf, health, false);
drawIconRow(canvas,
hx + RIGHT_X_OFF * HUD_SCALE * sx,
hy + HEALTH_Y_OFF * HUD_SCALE * sy,
sx, sy, tex.foodBg, tex.foodFull, tex.foodHalf, food, true);
if (showArmor)
{
drawIconRow(canvas,
hx + LEFT_X_OFF * HUD_SCALE * sx,
hy + ARMOR_Y_OFF * HUD_SCALE * sy,
sx, sy, tex.armorEmpty, tex.armorFull, tex.armorHalf, armor, false);
}
if (showAir)
{
drawAirBubbles(canvas,
hx + RIGHT_X_OFF * HUD_SCALE * sx,
hy + ARMOR_Y_OFF * HUD_SCALE * sy,
sx, sy, tex, air);
}
drawXPBar(canvas, hx, hy, sx, sy, tex, xpProgress, xpLevel);
drawHotbar(canvas, hx, hy, sx, sy, tex, selectedSlot);
}
nk_end(ctx);
popCleanStyle(ctx);
}
}}