mirror of
https://github.com/coah80/LegacyVulkEdition.git
synced 2026-07-19 06:58:07 +00:00
363 lines
11 KiB
C++
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) {}
|
|
|
|
}}
|