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

363 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
#define NK_IMPLEMENTATION
#include "nuklear.h"
#include "NuklearBridge.h"
#include "VulkanBootstrapApp.h"
#include "4J_Render.h"
#include <GLFW/glfw3.h>
#include <cstring>
#include <cstdio>
#include <vector>
#include "stb_image.h"
extern VulkanBootstrapApp g_vulkanBackend;
extern C4JRender RenderManager;
extern int g_iScreenWidth;
extern int g_iScreenHeight;
static struct nk_context g_nkCtx;
static struct nk_font_atlas g_nkAtlas;
static struct nk_font *g_nkFontSmall = nullptr;
static struct nk_font *g_nkFont = nullptr;
static struct nk_font *g_nkFontTitle = nullptr;
static struct nk_buffer g_nkCmds;
static struct nk_buffer g_nkVerts;
static struct nk_buffer g_nkIdx;
static int g_nkFontTexture = -1;
static const float FONT_SIZE_SMALL = 14.0f;
static const float FONT_SIZE_NORMAL = 20.0f;
static const float FONT_SIZE_TITLE = 32.0f;
struct NkVertex {
float pos[2];
float uv[2];
nk_byte col[4];
};
static void nk_glfw_char_callback(GLFWwindow *, unsigned int codepoint)
{
nk_input_unicode(&g_nkCtx, codepoint);
}
static void nk_glfw_scroll_callback(GLFWwindow *, double xoff, double yoff)
{
nk_input_scroll(&g_nkCtx, nk_vec2((float)xoff, (float)yoff));
}
namespace lve { namespace ui {
static bool loadFileIntoBuffer(const char *path, std::vector<unsigned char> &out);
bool initNuklear()
{
nk_init_default(&g_nkCtx, 0);
nk_buffer_init_default(&g_nkCmds);
nk_buffer_init_default(&g_nkVerts);
nk_buffer_init_default(&g_nkIdx);
nk_font_atlas_init_default(&g_nkAtlas);
nk_font_atlas_begin(&g_nkAtlas);
std::vector<unsigned char> fontData;
if (loadFileIntoBuffer("../Assets/fonts/Mojangles.ttf", fontData))
{
fprintf(stderr, "[NuklearBridge] Loaded Mojangles.ttf (%zu bytes)\n", fontData.size());
struct nk_font_config cfgSmall = nk_font_config(FONT_SIZE_SMALL);
cfgSmall.oversample_h = 2;
cfgSmall.oversample_v = 2;
cfgSmall.merge_mode = nk_false;
g_nkFontSmall = nk_font_atlas_add_from_memory(&g_nkAtlas,
fontData.data(), (nk_size)fontData.size(), FONT_SIZE_SMALL, &cfgSmall);
struct nk_font_config cfgNormal = nk_font_config(FONT_SIZE_NORMAL);
cfgNormal.oversample_h = 2;
cfgNormal.oversample_v = 2;
cfgNormal.merge_mode = nk_false;
g_nkFont = nk_font_atlas_add_from_memory(&g_nkAtlas,
fontData.data(), (nk_size)fontData.size(), FONT_SIZE_NORMAL, &cfgNormal);
struct nk_font_config cfgTitle = nk_font_config(FONT_SIZE_TITLE);
cfgTitle.oversample_h = 2;
cfgTitle.oversample_v = 2;
cfgTitle.merge_mode = nk_false;
g_nkFontTitle = nk_font_atlas_add_from_memory(&g_nkAtlas,
fontData.data(), (nk_size)fontData.size(), FONT_SIZE_TITLE, &cfgTitle);
}
else
{
fprintf(stderr, "[NuklearBridge] Mojangles.ttf not found, falling back to default\n");
g_nkFontSmall = nk_font_atlas_add_default(&g_nkAtlas, FONT_SIZE_SMALL, 0);
g_nkFont = nk_font_atlas_add_default(&g_nkAtlas, FONT_SIZE_NORMAL, 0);
g_nkFontTitle = nk_font_atlas_add_default(&g_nkAtlas, FONT_SIZE_TITLE, 0);
}
int atlasW, atlasH;
const void *atlasData = nk_font_atlas_bake(&g_nkAtlas, &atlasW, &atlasH, NK_FONT_ATLAS_RGBA32);
g_nkFontTexture = RenderManager.TextureCreate();
RenderManager.TextureBind(g_nkFontTexture);
RenderManager.TextureData(atlasW, atlasH, (void *)atlasData, 0);
RenderManager.TextureSetParam(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
RenderManager.TextureSetParam(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
nk_font_atlas_end(&g_nkAtlas, nk_handle_id(g_nkFontTexture), 0);
nk_style_set_font(&g_nkCtx, &g_nkFont->handle);
GLFWwindow *win = g_vulkanBackend.getWindow();
glfwSetCharCallback(win, nk_glfw_char_callback);
glfwSetScrollCallback(win, nk_glfw_scroll_callback);
return true;
}
void shutdownNuklear()
{
nk_font_atlas_clear(&g_nkAtlas);
if (g_nkFontTexture >= 0)
RenderManager.TextureFree(g_nkFontTexture);
nk_buffer_free(&g_nkIdx);
nk_buffer_free(&g_nkVerts);
nk_buffer_free(&g_nkCmds);
nk_free(&g_nkCtx);
}
void nuklearNewFrame()
{
GLFWwindow *win = g_vulkanBackend.getWindow();
double x, y;
glfwGetCursorPos(win, &x, &y);
nk_input_begin(&g_nkCtx);
nk_input_motion(&g_nkCtx, (int)x, (int)y);
nk_input_button(&g_nkCtx, NK_BUTTON_LEFT, (int)x, (int)y,
glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS);
nk_input_button(&g_nkCtx, NK_BUTTON_RIGHT, (int)x, (int)y,
glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS);
nk_input_button(&g_nkCtx, NK_BUTTON_MIDDLE, (int)x, (int)y,
glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS);
nk_bool escDown = glfwGetKey(win, GLFW_KEY_ESCAPE) == GLFW_PRESS;
nk_bool bsDown = glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS;
nk_input_key(&g_nkCtx, NK_KEY_BACKSPACE, escDown || bsDown);
nk_input_key(&g_nkCtx, NK_KEY_ENTER,
glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS);
nk_input_key(&g_nkCtx, NK_KEY_DEL,
glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS);
nk_input_key(&g_nkCtx, NK_KEY_TAB,
glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS);
nk_input_key(&g_nkCtx, NK_KEY_UP,
glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS);
nk_input_key(&g_nkCtx, NK_KEY_DOWN,
glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS);
nk_input_key(&g_nkCtx, NK_KEY_LEFT,
glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS);
nk_input_key(&g_nkCtx, NK_KEY_RIGHT,
glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS);
nk_input_key(&g_nkCtx, NK_KEY_SHIFT,
glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS ||
glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS);
nk_input_key(&g_nkCtx, NK_KEY_CTRL,
glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS ||
glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS);
nk_input_end(&g_nkCtx);
}
void nuklearRender()
{
int winW = g_iScreenWidth;
int winH = g_iScreenHeight;
if (winW <= 0 || winH <= 0) return;
static const struct nk_draw_vertex_layout_element vertex_layout[] = {
{NK_VERTEX_POSITION, NK_FORMAT_FLOAT, offsetof(NkVertex, pos)},
{NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, offsetof(NkVertex, uv)},
{NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, offsetof(NkVertex, col)},
{NK_VERTEX_LAYOUT_END}
};
struct nk_convert_config config = {};
config.vertex_layout = vertex_layout;
config.vertex_size = sizeof(NkVertex);
config.vertex_alignment = NK_ALIGNOF(NkVertex);
config.tex_null.texture = nk_handle_id(g_nkFontTexture);
config.tex_null.uv = nk_vec2(0, 0);
config.circle_segment_count = 22;
config.curve_segment_count = 22;
config.arc_segment_count = 22;
config.global_alpha = 1.0f;
config.shape_AA = NK_ANTI_ALIASING_ON;
config.line_AA = NK_ANTI_ALIASING_ON;
nk_buffer_clear(&g_nkVerts);
nk_buffer_clear(&g_nkIdx);
nk_convert(&g_nkCtx, &g_nkCmds, &g_nkVerts, &g_nkIdx, &config);
const NkVertex *vertices = (const NkVertex *)nk_buffer_memory_const(&g_nkVerts);
const nk_draw_index *indices = (const nk_draw_index *)nk_buffer_memory_const(&g_nkIdx);
RenderManager.MatrixMode(GL_PROJECTION);
RenderManager.MatrixPush();
RenderManager.MatrixSetIdentity();
RenderManager.MatrixOrthogonal(0, (float)winW, (float)winH, 0, -1, 1);
RenderManager.MatrixMode(GL_MODELVIEW);
RenderManager.MatrixPush();
RenderManager.MatrixSetIdentity();
RenderManager.StateSetDepthTestEnable(false);
RenderManager.StateSetDepthMask(false);
RenderManager.StateSetBlendEnable(true);
RenderManager.StateSetBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
struct GameVert { float x,y,z; float u,v; uint8_t r,g,b,a; int8_t nx,ny,nz,nw; uint32_t pad; };
const nk_draw_command *cmd;
unsigned int offset = 0;
nk_draw_foreach(cmd, &g_nkCtx, &g_nkCmds)
{
if (!cmd->elem_count) continue;
int texId = cmd->texture.id;
RenderManager.TextureBind(texId);
RenderManager.StateSetColour(1, 1, 1, 1);
unsigned int triCount = cmd->elem_count / 3;
std::vector<GameVert> tris(cmd->elem_count);
for (unsigned int i = 0; i < cmd->elem_count; i++)
{
const NkVertex &nv = vertices[indices[offset + i]];
tris[i].x = nv.pos[0];
tris[i].y = nv.pos[1];
tris[i].z = 0;
tris[i].u = nv.uv[0];
tris[i].v = nv.uv[1];
tris[i].r = nv.col[0];
tris[i].g = nv.col[1];
tris[i].b = nv.col[2];
tris[i].a = nv.col[3];
tris[i].nx = 0;
tris[i].ny = 0;
tris[i].nz = 127;
tris[i].nw = 0;
tris[i].pad = 0;
}
RenderManager.DrawVertices(
C4JRender::PRIMITIVE_TYPE_TRIANGLE_LIST,
(int)cmd->elem_count,
tris.data(),
C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1,
C4JRender::PIXEL_SHADER_TYPE_STANDARD);
offset += cmd->elem_count;
}
RenderManager.StateSetBlendEnable(false);
RenderManager.StateSetDepthTestEnable(true);
RenderManager.StateSetDepthMask(true);
RenderManager.MatrixMode(GL_MODELVIEW);
RenderManager.MatrixPop();
RenderManager.MatrixMode(GL_PROJECTION);
RenderManager.MatrixPop();
nk_clear(&g_nkCtx);
}
nk_context *getNkContext()
{
return &g_nkCtx;
}
nk_font *getDefaultFont()
{
return g_nkFont;
}
nk_font *getSmallFont()
{
return g_nkFontSmall;
}
nk_font *getTitleFont()
{
return g_nkFontTitle;
}
static bool loadFileIntoBuffer(const char *path, std::vector<unsigned char> &out)
{
FILE *f = fopen(path, "rb");
if (f == nullptr)
{
fprintf(stderr, "[NuklearBridge] Failed to open file: %s\n", path);
return false;
}
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
if (size <= 0)
{
fclose(f);
return false;
}
out.resize((size_t)size);
size_t read = fread(out.data(), 1, (size_t)size, f);
fclose(f);
return read == (size_t)size;
}
struct nk_image loadTexture(const char *path)
{
int w, h;
return loadTextureWithSize(path, &w, &h);
}
struct nk_image loadTextureWithSize(const char *path, int *outW, int *outH)
{
*outW = 0;
*outH = 0;
std::vector<unsigned char> fileData;
if (!loadFileIntoBuffer(path, fileData))
return nk_image_id(0);
int w = 0, h = 0, channels = 0;
unsigned char *pixels = stbi_load_from_memory(
fileData.data(), (int)fileData.size(),
&w, &h, &channels, 4);
if (pixels == nullptr)
{
fprintf(stderr, "[NuklearBridge] stbi failed to decode: %s\n", path);
return nk_image_id(0);
}
int texId = RenderManager.TextureCreate();
RenderManager.TextureBind(texId);
RenderManager.TextureData(w, h, pixels, 0);
RenderManager.TextureSetParam(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
RenderManager.TextureSetParam(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(pixels);
*outW = w;
*outH = h;
fprintf(stderr, "[NuklearBridge] Loaded texture: %s (%dx%d) -> slot %d\n", path, w, h, texId);
return nk_image_id(texId);
}
void nuklearHandleResize(int, int) {}
}}