diff --git a/client/include/gpu.h b/client/include/gpu.h index b974206..5d629df 100644 --- a/client/include/gpu.h +++ b/client/include/gpu.h @@ -47,8 +47,8 @@ VkResult command_transition_image_layout(VkDevice device, VkCommandPool transfer } extern const uint32_t MAX_FRAMES_IN_FLIGHT; -extern const uint32_t WINDOW_WIDTH; -extern const uint32_t WINDOW_HEIGHT; +extern const uint32_t WINDOW_MIN_WIDTH; +extern const uint32_t WINDOW_MIN_HEIGHT; typedef struct SwapchainDetailsStruct { VkSurfaceCapabilitiesKHR capabilities; diff --git a/client/include/ui.h b/client/include/ui.h index d98dc92..c859fa3 100644 --- a/client/include/ui.h +++ b/client/include/ui.h @@ -9,6 +9,12 @@ #include "ft2build.h" #include FT_FREETYPE_H +#define ANCHOR_TOP_LEFT 0 +#define ANCHOR_TOP_RIGHT 1 +#define ANCHOR_BOTTOM_LEFT 2 +#define ANCHOR_BOTTOM_RIGHT 3 +#define ANCHOR_CENTER 4 + typedef struct ComputePipelineStruct { VkPipelineLayout layout; VkPipeline pipeline; @@ -76,6 +82,7 @@ typedef struct UIStringStruct { float size; uint32_t offset; uint32_t length; + uint32_t font; uint32_t id; } UIString; @@ -85,6 +92,7 @@ typedef struct UIDrawableStruct { vec4 color; uint32_t type; uint32_t code; + uint32_t index; uint32_t id; } UIDrawable; @@ -97,7 +105,6 @@ typedef struct UILayerStruct { DrawCommand draw; DispatchCommand dispatch_strings; - uint32_t font_index; uint32_t max_drawables; uint32_t max_codes; uint32_t max_strings; @@ -129,6 +136,7 @@ typedef struct UIlayerStorageStruct { typedef struct UIContainerStruct { vec2 offset; vec2 size; + uint32_t anchor; } UIContainer; typedef struct UIContainerStorageStruct { @@ -148,6 +156,7 @@ typedef struct UIContainerStorageStruct { typedef struct UIContextStruct { VkDeviceAddress font_infos; vec2 screen; + vec2 extent; vec2 scale; } UIContext; @@ -211,7 +220,6 @@ typedef struct UILayerInputStruct { uint32_t num_strings; uint32_t num_codes; uint32_t num_drawables; - uint32_t font; UIString* strings; uint32_t* codes; diff --git a/client/shader_src/string.comp b/client/shader_src/string.comp index 6e6ae16..f4af704 100644 --- a/client/shader_src/string.comp +++ b/client/shader_src/string.comp @@ -13,7 +13,7 @@ layout(local_size_x = 1) in; void main() { uint gID = gl_GlobalInvocationID.x; String string = pc.layer.strings.s[gID]; - Font font = pc.context.fonts.f[pc.layer.font_index]; + Font font = pc.context.fonts.f[string.font]; uint buffer_pos = atomicAdd(pc.layer.draw.instance_count, string.len); float x = 0; @@ -25,6 +25,7 @@ void main() { pc.layer.drawables.d[buffer_pos + i].size = vec2(string.size, string.size); pc.layer.drawables.d[buffer_pos + i].color = string.color; pc.layer.drawables.d[buffer_pos + i].code = pc.layer.codes.c[string.offset + i]; + pc.layer.drawables.d[buffer_pos + i].index = string.font; pc.layer.drawables.d[buffer_pos + i].type = 1; } } diff --git a/client/shader_src/ui.frag b/client/shader_src/ui.frag index 1c401b1..9fd2d34 100644 --- a/client/shader_src/ui.frag +++ b/client/shader_src/ui.frag @@ -20,12 +20,13 @@ layout(location = 1) in vec2 fragUV; layout(location = 2) flat in uint code; layout(location = 3) flat in uint index; layout(location = 4) flat in uint type; +layout(location = 5) flat in vec2 container_offset; layout(location = 0) out vec4 outColor; void main() { vec2 pos = (gl_FragCoord.xy - vec2(0.5, 0.5))/pc.context.scale; - vec2 min = pc.layer.container.offset; + vec2 min = container_offset; vec2 max = min + pc.layer.container.size; if(pos.x < min.x || pos.y < min.y || pos.x > max.x || pos.y > max.y) { diff --git a/client/shader_src/ui.vert b/client/shader_src/ui.vert index 2a01c78..3324214 100644 --- a/client/shader_src/ui.vert +++ b/client/shader_src/ui.vert @@ -13,6 +13,7 @@ layout(location = 1) out vec2 fragUV; layout(location = 2) out flat uint code; layout(location = 3) out flat uint index; layout(location = 4) out flat uint type; +layout(location = 5) out flat vec2 container_offset; const vec2 square[6] = { vec2(0.0, 0.0), @@ -26,10 +27,31 @@ const vec2 square[6] = { void main() { Drawable drawable = pc.layer.drawables.d[gl_InstanceIndex]; vec2 pos = square[gl_VertexIndex]; + + // Top Left + if(pc.layer.container.anchor == 0) { + container_offset = pc.layer.container.offset; + } + + // Top Right + if(pc.layer.container.anchor == 1) { + container_offset = pc.layer.container.offset + vec2(pc.context.extent.x - pc.layer.container.size.x, 0); + } + + // Bottom Left + if(pc.layer.container.anchor == 2) { + container_offset = pc.layer.container.offset + vec2(0, pc.context.extent.y - pc.layer.container.size.y); + } + + // Bottom Right + if(pc.layer.container.anchor == 3) { + container_offset = pc.layer.container.offset + vec2(pc.context.extent.x - pc.layer.container.size.x, pc.context.extent.y - pc.layer.container.size.y); + } + if(drawable.type == 1){ // Glyph - Font font = pc.context.fonts.f[pc.layer.font_index]; + Font font = pc.context.fonts.f[drawable.index]; Symbol symbol = font.symbols.s[drawable.code]; fragUV = pos * vec2(symbol.width, symbol.height); @@ -38,11 +60,11 @@ void main() { fragUV = pos; } - gl_Position = vec4((pos * drawable.size + drawable.pos + pc.layer.container.offset) * pc.context.screen * 2, 0.0, 1.0) - vec4(1.0, 1.0, 0.0, 0.0); + gl_Position = vec4((pos * drawable.size + drawable.pos + container_offset) * pc.context.screen * 2, 0.0, 1.0) - vec4(1.0, 1.0, 0.0, 0.0); fragColor = drawable.color; code = drawable.code; type = drawable.type; - index = pc.layer.font_index; + index = drawable.index; } diff --git a/client/shader_src/ui_common.glsl b/client/shader_src/ui_common.glsl index e01d4a4..a1cd7d6 100644 --- a/client/shader_src/ui_common.glsl +++ b/client/shader_src/ui_common.glsl @@ -44,6 +44,7 @@ struct String { float size; uint offset; uint len; + uint font; uint id; }; @@ -61,6 +62,7 @@ struct Drawable { vec4 color; uint type; uint code; + uint index; uint id; }; @@ -71,6 +73,7 @@ layout(std430, buffer_reference) readonly buffer DrawableList { layout(std430, buffer_reference) readonly buffer Container { vec2 offset; vec2 size; + uint anchor; }; layout(std430, buffer_reference) readonly buffer Layer { @@ -81,7 +84,6 @@ layout(std430, buffer_reference) readonly buffer Layer { DrawCommand draw; DispatchCommand dispatch; - uint font_index; uint max_drawables; uint max_codes; uint max_strings; @@ -93,5 +95,6 @@ layout(std430, buffer_reference) readonly buffer Layer { layout(std430, buffer_reference) buffer Context { FontList fonts; vec2 screen; + vec2 extent; vec2 scale; }; diff --git a/client/src/gpu.c b/client/src/gpu.c index d2e0b5e..a7e3e4b 100644 --- a/client/src/gpu.c +++ b/client/src/gpu.c @@ -6,8 +6,8 @@ #include "vulkan/vulkan_core.h" const uint32_t MAX_FRAMES_IN_FLIGHT = 2; -const uint32_t WINDOW_WIDTH = 800; -const uint32_t WINDOW_HEIGHT = 600; +const uint32_t WINDOW_MIN_WIDTH = 800; +const uint32_t WINDOW_MIN_HEIGHT = 600; const char * validation_layers[] = { "VK_LAYER_KHRONOS_validation", @@ -56,9 +56,12 @@ GLFWwindow* init_window() { glfwSetErrorCallback(glfw_error); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + + // TODO: recreate the framebuffer on resize glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); - GLFWwindow* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "roleplay", 0, 0); + GLFWwindow* window = glfwCreateWindow(WINDOW_MIN_WIDTH, WINDOW_MIN_HEIGHT, "roleplay", 0, 0); + glfwSetWindowSizeLimits(window, WINDOW_MIN_WIDTH, WINDOW_MIN_HEIGHT, GLFW_DONT_CARE, GLFW_DONT_CARE); return window; } diff --git a/client/src/main.c b/client/src/main.c index 50491b6..2356a12 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -33,13 +33,44 @@ VkResult render_thread(GLFWwindow* window, RenderContext* render) { VK_RESULT(load_font("test.ttf", 16, VK_TRUE, library, render, &ui, &font_index)); + UIString context_strings[] = { + { + .pos = {0, 16}, + .size = 16, + .color = {1.0, 1.0, 1.0, 1.0}, + .offset = 0, + .length = strlen("Example"), + .font = font_index, + }, + }; + + uint32_t context_codes[100]; + map_string("Example Text", context_codes, 0, ui.fonts[0].charmap, ui.fonts[0].num_symbols); + + UILayerInput context_layers[] = { + { + .num_strings = 1, + .strings = context_strings, + .num_codes = 100, + .codes = context_codes, + }, + }; + + UIContainerInput context_info = { + .id = 0x01, + .anchor = ANCHOR_TOP_LEFT, + .size = {WINDOW_MIN_WIDTH, WINDOW_MIN_HEIGHT}, + .layer_count = sizeof(context_layers)/sizeof(UILayerInput), + .layers = context_layers, + }; + vec2 map_size = {250, 200}; UIDrawable map_rects[] = { { .pos = {0.0, 0.0}, .size = {map_size[0], map_size[1]}, - .color = {0.0, 0.5, 0.8, 1.0}, + .color = {0.0, 0.5,0.8, 1.0}, .type = 0, }, }; @@ -52,14 +83,14 @@ VkResult render_thread(GLFWwindow* window, RenderContext* render) { }; UIContainerInput map_info = { - .id = 0x01, - .offset = {WINDOW_WIDTH-map_size[0], 0.0}, + .id = 0x02, + .anchor = ANCHOR_TOP_RIGHT, .size = {map_size[0], map_size[1]}, .layer_count = sizeof(map_layers)/sizeof(UILayerInput), .layers = map_layers, }; - vec2 inventory_size = {map_size[0], WINDOW_HEIGHT-map_size[1]}; + vec2 inventory_size = {map_size[0], WINDOW_MIN_HEIGHT-map_size[1]}; UIDrawable inventory_rects[] = { { @@ -78,14 +109,14 @@ VkResult render_thread(GLFWwindow* window, RenderContext* render) { }; UIContainerInput inventory_info = { - .id = 0x02, - .offset = {WINDOW_WIDTH-inventory_size[0], WINDOW_HEIGHT-inventory_size[1]}, + .id = 0x03, + .anchor = ANCHOR_BOTTOM_RIGHT, .size = {inventory_size[0], inventory_size[1]}, .layer_count = sizeof(inventory_layers)/sizeof(UILayerInput), .layers = inventory_layers, }; - vec2 chat_size = {WINDOW_WIDTH-inventory_size[0], 200}; + vec2 chat_size = {WINDOW_MIN_WIDTH-inventory_size[0], 200}; UIDrawable chat_rects[] = { { @@ -104,13 +135,14 @@ VkResult render_thread(GLFWwindow* window, RenderContext* render) { }; UIContainerInput chat_info = { - .id = 0x02, - .offset = {0, WINDOW_HEIGHT-chat_size[1]}, + .id = 0x04, + .anchor = ANCHOR_BOTTOM_LEFT, .size = {chat_size[0], chat_size[1]}, .layer_count = sizeof(chat_layers)/sizeof(UILayerInput), .layers = chat_layers, }; + VK_RESULT(create_container(&context_info, render, &ui)); VK_RESULT(create_container(&map_info, render, &ui)); VK_RESULT(create_container(&inventory_info, render, &ui)); VK_RESULT(create_container(&chat_info, render, &ui)); @@ -142,9 +174,15 @@ int network_thread() { } void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { + (void)window; + (void)key; + (void)scancode; + (void)action; + (void)mods; } void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) { + (void)mods; switch(button) { // Handle camera hover case GLFW_MOUSE_BUTTON_MIDDLE: @@ -166,9 +204,15 @@ void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) } void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { + (void)window; + (void)xoffset; + (void)yoffset; } void cursor_pos_callback(GLFWwindow* window, double xpos, double ypos) { + (void)window; + (void)xpos; + (void)ypos; } int main() { diff --git a/client/src/ui.c b/client/src/ui.c index 5745965..9b68c68 100644 --- a/client/src/ui.c +++ b/client/src/ui.c @@ -215,14 +215,15 @@ VkResult create_ui_pipeline( .alphaToOneEnable = VK_FALSE, }; - // additive blending + // For blending, calculate the new color as new_color = (1-a)(old_color)+(a)(fragment_color) + // Don't store an alpha value in the color attachment, since it's not used VkPipelineColorBlendAttachmentState color_blend_attachment = { .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, .blendEnable = VK_TRUE, .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA, - .dstColorBlendFactor = VK_BLEND_FACTOR_ONE, + .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, .colorBlendOp = VK_BLEND_OP_ADD, - .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, + .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, .alphaBlendOp = VK_BLEND_OP_ADD, }; @@ -302,6 +303,7 @@ VkResult create_container( context->containers[index].data.offset[1] = container->offset[1]; context->containers[index].data.size[0] = container->size[0]; context->containers[index].data.size[1] = container->size[1]; + context->containers[index].data.anchor = container->anchor; memcpy(mapped, &context->containers[index].data, sizeof(UIContainer)); VkCommandBuffer command_buffer = command_begin_single(gpu->device, gpu->transfer_pool); @@ -374,7 +376,6 @@ VkResult create_layer( container->layers[index].data.dispatch_strings.y = 1; container->layers[index].data.dispatch_strings.z = 1; - container->layers[index].data.font_index = input->font; container->layers[index].data.max_drawables = input->num_drawables + input->num_codes; container->layers[index].data.max_strings = input->num_strings; container->layers[index].data.num_drawables = input->num_drawables; @@ -1078,6 +1079,8 @@ VkResult create_ui_context( memory->data.font_infos = buffer_address(gpu->device, memory->font_infos); memory->data.screen[0] = gpu->window_scale[0] / gpu->swapchain_extent.width; memory->data.screen[1] = gpu->window_scale[1] / gpu->swapchain_extent.height; + memory->data.extent[0] = gpu->swapchain_extent.width / gpu->window_scale[0]; + memory->data.extent[1] = gpu->swapchain_extent.height / gpu->window_scale[1]; memory->data.scale[0] = gpu->window_scale[0]; memory->data.scale[1] = gpu->window_scale[1]; memcpy(mapped, &memory->data, sizeof(UIContext));