#include #include #define GLFW_INCLUDE_NONE #define GLFW_INCLUDE_VULKAN #include #include "rusty.hpp" #include "vulkan.hpp" #include "tmath.hpp" #include "image.hpp" #include "model_object.hpp" const uint32_t object_matrix_count = 2048; struct GlobalVars { tmath::fmat4 proj_view; alignas(16) tmath::fvec3 light; alignas(16) tmath::fvec3 eye; }; class Application { public: ~Application() { if(device) device->wait_idle(); swapchain.reset(); } void init(GLFWwindow* window) { mdl_objects.push_back(std::make_shared(verts)); field = std::make_shared(verts); mdl_objects.push_back(field); auto u1 = std::make_shared(verts); u1->set_pos(0, 0); mdl_objects.push_back(u1); auto u4 = std::make_shared(verts); u4->set_pos(4, 0); u1->attack(*u4, 0.4f); mdl_objects.push_back(u4); this->window = window; uint32_t ext_count; const char** ext_names = glfwGetRequiredInstanceExtensions(&ext_count); instance = vk::Instance::begin_build().set_application_name("Tacraft").set_enging_name("tacraft") .set_layers({"VK_LAYER_LUNARG_standard_validation"}) .set_extensions(rs::vector(ext_names, ext_names + ext_count)) .end(); physical_device = instance->enumerate_physical_device().nth(0).value(); auto maxsample = std::min(physical_device->properties()->limits.framebufferColorSampleCounts, physical_device->properties()->limits.framebufferDepthSampleCounts); VkSurfaceKHR raw_surface; glfwCreateWindowSurface(instance->handle(), window, nullptr, &raw_surface); surface = vk::Surface::from_raw_surface(raw_surface, *instance, *physical_device); auto findex = physical_device->enumerate_queue_families().enumerate().filter([&](size_t idx, const VkQueueFamilyProperties* p) -> bool { VkBool32 support_present = false; vkGetPhysicalDeviceSurfaceSupportKHR(physical_device->handle(), idx, raw_surface, &support_present); return (p->queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0 && support_present && glfwGetPhysicalDevicePresentationSupport(instance->handle(), physical_device->handle(), idx); }).unzip().first.nth(0).value(); device = vk::Device::begin_build(*physical_device).set_extensions({"VK_KHR_swapchain"}) .add_queue_family(findex, 1.0f).add_queue_family(findex, 1.0f) .enable_features(*physical_device->supported_features()) .end(); auto gqueue = device->enumerate_queues().next().value(); auto pqueue = device->enumerate_queues().next().value_or(gqueue); graphic_queue = *gqueue; present_queue = *pqueue; vert_shader = vk::ShaderModule::load_from_src(*device, "./shader/shader.vert", vk::ShaderModule::shader_type_vertex); if(!vert_shader) { std::cout << "vertex shader null!" << std::endl; return; } frag_shader = vk::ShaderModule::load_from_src(*device, "./shader/shader.frag", vk::ShaderModule::shader_type_fragment); if(!frag_shader) { std::cout << "fragment shader null!" << std::endl; return; } render_pass = vk::RenderPass::begin_build(*device) .set_samples(sample_count) .end(); if(!render_pass) { std::cout << "renderpass null!" << std::endl; return; } auto& vertices = verts.all_vertices(); auto& vert_indices = verts.all_indices(); vertex_buffer = vk::Buffer::begin_build(*device, *physical_device).alloc_size(vertices.size() * sizeof(Vertex)) .set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) .set_sharing_mode(VK_SHARING_MODE_EXCLUSIVE) .set_cpu_accessible() .end(); vertex_buffer->update_buffer(vertices.data(), vertices.size() * sizeof(Vertex)); index_buffer = vk::Buffer::begin_build(*device, *physical_device).alloc_size(vert_indices.size() * sizeof(uint32_t)) .set_usage(VK_BUFFER_USAGE_INDEX_BUFFER_BIT) .set_sharing_mode(VK_SHARING_MODE_EXCLUSIVE) .set_cpu_accessible() .end(); index_buffer->update_buffer(vert_indices.data(), vert_indices.size() * sizeof(uint32_t)); auto image = Image::load_from_file("res/snow.tga"); auto [imgx, imgy] = image->img_size(); img_buffer = vk::Buffer::begin_build(*device, *physical_device).alloc_size(imgx * imgy * 4 + 64) .set_usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT) .set_sharing_mode(VK_SHARING_MODE_EXCLUSIVE) .set_cpu_accessible() .end(); img_buffer->update_buffer(image->img_data(), imgx * imgy * 4); memset(img_buffer->map(imgx * imgy * 4, 64), 0xff, 64); img_buffer->unmap(); teximage = vk::Image::begin_build(*device, *physical_device) .set_device_local() .set_format(VK_FORMAT_R8G8B8A8_UNORM) .set_extent({(uint32_t)imgx, (uint32_t)imgy, 1}) .end(); teximage_view = teximage->create_view(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT); whiteimage = vk::Image::begin_build(*device, *physical_device) .set_device_local() .set_format(VK_FORMAT_R8G8B8A8_UNORM) .set_extent({(uint32_t)4, (uint32_t)4, 1}) .end(); whiteimage_view = whiteimage->create_view(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT); sampler = vk::Sampler::begin_build(*device).set_anisotrophy(16.0f).end(); descriptor_set_layout = vk::DescriptorSetLayout::begin_build(*device) .add_binding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER).set_count(1).set_stage(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT) .add_binding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER).set_count(1).set_stage(VK_SHADER_STAGE_VERTEX_BIT) .add_binding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER).set_count(2).set_stage(VK_SHADER_STAGE_FRAGMENT_BIT) .end(); descriptor_pool = vk::DescriptorPool::begin_build(*device).set_max_sets(2) .add_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 16) .add_pool_size(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 16) .add_pool_size(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 16) .end(); descriptor_sets = descriptor_pool->alloc_descriptor_set(*descriptor_set_layout, 2); uint32_t buffer_size = 0x100 + sizeof(VertexAttribute) * object_matrix_count; for(uint32_t i = 0; i < 2; ++i) { uniform_buffers.emplace_back(vk::Buffer::begin_build(*device, *physical_device).alloc_size(buffer_size) .set_usage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) .set_sharing_mode(VK_SHARING_MODE_EXCLUSIVE) .set_cpu_accessible() .end()); for(auto& ds : descriptor_sets) { ds->update_write(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, 0) .update_write_buffer_info(*uniform_buffers.back(), 0, sizeof(GlobalVars), 1) .update_end(); ds->update_write(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, 0) .update_write_buffer_info(*uniform_buffers.back(), 0x100, sizeof(VertexAttribute) * object_matrix_count, 1) .update_write(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2, 0) .update_write_image_info(*whiteimage_view, *sampler) .update_write_image_info(*teximage_view, *sampler) .update_end(); } } draw_command_pool = vk::CommandPool::begin_build(*device) .set_queue_family_index(graphic_queue.queue_family_index) .set_pool_flag(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT) .end(); onetime_command_pool = vk::CommandPool::begin_build(*device) .set_queue_family_index(graphic_queue.queue_family_index) .set_pool_flag(VK_COMMAND_POOL_CREATE_TRANSIENT_BIT) .end(); draw_command_pool->alloc_command_buffer(command_buffers, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 2); renderer = vk::Renderer::create_renderer(*device, 2); onetime_command_pool->alloc_command_buffer(onetime_commands, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 2); onetime_commands[0]->begin_command(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT) .begin_pipeline_barrior(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT) .add_image_memory_barrior(*teximage) .image_set_layout(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) .image_set_aspect_mask(VK_IMAGE_ASPECT_COLOR_BIT) .image_set_access_mask(0, VK_ACCESS_TRANSFER_WRITE_BIT) .end_pipeline_barrior() .begin_copy_buffer_to_image(*img_buffer, *teximage) .add_region(0, {0, 0, 0}, {(uint32_t)imgx, (uint32_t)imgy, 1u}) .end_buffer_image_copy() .begin_pipeline_barrior(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT) .add_image_memory_barrior(*teximage) .image_set_layout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) .image_set_aspect_mask(VK_IMAGE_ASPECT_COLOR_BIT) .image_set_access_mask(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT) .end_pipeline_barrior() .end_command(); onetime_commands[1]->begin_command(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT) .begin_pipeline_barrior(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT) .add_image_memory_barrior(*whiteimage) .image_set_layout(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) .image_set_aspect_mask(VK_IMAGE_ASPECT_COLOR_BIT) .image_set_access_mask(0, VK_ACCESS_TRANSFER_WRITE_BIT) .end_pipeline_barrior() .begin_copy_buffer_to_image(*img_buffer, *whiteimage) .add_region(imgx * imgy * 4, {0, 0, 0}, {4u, 4u, 1u}) .end_buffer_image_copy() .begin_pipeline_barrior(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT) .add_image_memory_barrior(*whiteimage) .image_set_layout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) .image_set_aspect_mask(VK_IMAGE_ASPECT_COLOR_BIT) .image_set_access_mask(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT) .end_pipeline_barrior() .end_command(); recreate_sized_object(); } void draw_frame() { if(skipdraw) return; if(recreate || renderer->out_of_date()) { recreate_sized_object(); recreate = false; } uint32_t frame_index = renderer->prepare_for_next_frame(); uint32_t image_index = renderer->accquire_next_image(*swapchain); if(renderer->out_of_date()) return; // update attributes // global_vars.light = tmath::fmat4::identity().translate(5.0f, 0.0f, 8.0f).rotatex(glfwGetTime() * 0.5f).rotatez(pi * 0.25f) * tmath::fvec3{0.0f, 0.0f, 0.0f}; for(auto& mdo : mdl_objects) mdo->update(glfwGetTime()); auto ptr = uniform_buffers[frame_index]->map(0, 0x100 + sizeof(VertexAttribute) * object_matrix_count); memcpy(ptr, &global_vars, sizeof(GlobalVars)); auto att_ptr = reinterpret_cast((uintptr_t)ptr + 0x100); memcpy(att_ptr, verts.all_attributes().data(), verts.all_attributes().size() * sizeof(VertexAttribute)); uniform_buffers[frame_index]->unmap(); if(!onetime_commands.empty()) { renderer->submit_onetime_command(graphic_queue, onetime_commands).wait_idle(graphic_queue); onetime_commands.clear(); } command_buffers[frame_index]->begin_command(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT) .begin_render_pass(*render_pass, *framebuffers[image_index], VkClearValue{0.0, 0.0, 0.0, 0.8}) .bind_pipeline_and_descriptor_sets(*pipeline, *descriptor_sets[frame_index]) .draw_indexed(*vertex_buffer, *index_buffer, verts.all_indices().size()) .end_render_pass() .end_command(); renderer->submit_draw_command(graphic_queue, *command_buffers[frame_index]); renderer->present(present_queue, *swapchain, image_index); } void recreate_sized_object() { device->wait_idle(); swapchain = nullptr; framebuffers.clear(); glfwGetFramebufferSize(window, &width, &height); surface->refresh(); const VkSurfaceFormatKHR* format = surface->default_format(); auto caps = surface->capabilities(); swapchain = vk::Swapchain::begin_build(*device) .set_surface(*surface) .set_min_image_count(caps->maxImageCount) .set_format(format->format) .set_extent({(uint32_t)width, (uint32_t)height}) .set_image_usage(caps->supportedUsageFlags) .set_queue_family_indices(device->queue_family_in_use()) .set_transform(caps->currentTransform) .end(); auto swapchain_images = swapchain->get_swapchain_images(); depthimage = vk::Image::begin_build(*device, *physical_device) .set_device_local() .set_samples(sample_count) .set_usage(VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) .set_format(VK_FORMAT_D24_UNORM_S8_UINT) .set_tilling(VK_IMAGE_TILING_OPTIMAL) .set_extent({(uint32_t)width, (uint32_t)height, 1u}) .end(); depthimage_view = depthimage->create_view(VK_FORMAT_D24_UNORM_S8_UINT, VK_IMAGE_ASPECT_DEPTH_BIT); onetime_command_pool->alloc_command_buffer(onetime_commands, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1); onetime_commands.back()->begin_command(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT) .begin_pipeline_barrior(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT) .add_image_memory_barrior(*depthimage) .image_set_layout(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) .image_set_aspect_mask(VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) .image_set_access_mask(0, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT) .end_pipeline_barrior() .end_command(); if(sample_count != VK_SAMPLE_COUNT_1_BIT) { msaaimage = vk::Image::begin_build(*device, *physical_device) .set_device_local() .set_usage(VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) .set_samples(sample_count) .set_format(format->format) .set_tilling(VK_IMAGE_TILING_OPTIMAL) .set_extent({(uint32_t)width, (uint32_t)height, 1u}) .end(); msaaimage_view = msaaimage->create_view(format->format, VK_IMAGE_ASPECT_COLOR_BIT); onetime_command_pool->alloc_command_buffer(onetime_commands, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1); onetime_commands.back()->begin_command(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT) .begin_pipeline_barrior(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT) .add_image_memory_barrior(*msaaimage) .image_set_layout(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) .image_set_aspect_mask(VK_IMAGE_ASPECT_COLOR_BIT) .image_set_access_mask(0, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT) .end_pipeline_barrior() .end_command(); } framebuffers.clear(); for(auto view : swapchain_images) { if(sample_count == VK_SAMPLE_COUNT_1_BIT) { framebuffers.emplace_back(vk::Framebuffer::begin_build(*device) .set_render_pass(*render_pass) .set_framebuffer_size(width, height) .add_image_view(*view) .add_image_view(*depthimage_view) .end() ); } else { framebuffers.emplace_back(vk::Framebuffer::begin_build(*device) .set_render_pass(*render_pass) .set_framebuffer_size(width, height) .add_image_view(*msaaimage_view) .add_image_view(*depthimage_view) .add_image_view(*view) .end() ); } } pipeline = vk::Pipeline::begin_build(*device) .set_render_pass(*render_pass) .set_vertex_shader(*vert_shader) .set_fragment_shader(*frag_shader) .set_cull_mode(VK_CULL_MODE_BACK_BIT) .normal_viewport((float)width, (float)height) .normal_scissor((uint32_t)width, (uint32_t)height) .add_descriptor_set_layout(*descriptor_set_layout) .set_samples(sample_count) .bind_vertex_size(sizeof(Vertex)) .add_binding_attribute(VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, position)) .add_binding_attribute(VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal)) .add_binding_attribute(VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, texcoord)) .add_binding_attribute(VK_FORMAT_R32G32_SINT, offsetof(Vertex, index)) .end(); global_vars.proj_view = tmath::fmat4::perspective_fov(3.1415926f / 3.0f, (float)width / (float)height, 0.1f, 100.0f).scale(1.0f, -1.0f, 1.0f) * tmath::fmat4::look_at({8.0f, 20.0f, 10.0f}, {8.0f, 10.0f, 0.0f}, {0.0f, 0.0f, 1.0f}); global_vars.light = {8.0f, 24.0f, 20.0f}; global_vars.eye = {8.0f, 20.0f, 10.0f}; } void copy_buffer_to_image() { } void post_window_resized(bool skip = false) { skipdraw = skip; recreate = true; } void mouse_move(float x, float y) { auto& m = global_vars.proj_view; float w = x * 2.0f / width - 1.0f; float h = y * 2.0f / height - 1.0f; float a1 = m[0] - w * m[3]; float b1 = m[4] - w * m[7]; float c1 = m[12] - w * m[15]; float a2 = m[1] - h * m[3]; float b2 = m[5] - h * m[7]; float c2 = m[13] - h * m[15]; float d = a1 * b2 - a2 * b1; float zx = (b1 * c2 - b2 * c1) / d; float zy = (c1 * a2 - c2 * a1) / d; field->mouse_point(zx, zy); } private: bool recreate = false; bool skipdraw = false; GLFWwindow* window = nullptr; std::shared_ptr instance; const vk::PhysicalDevice* physical_device; std::shared_ptr device; std::shared_ptr swapchain; std::shared_ptr surface; rs::vector> framebuffers; std::shared_ptr vert_shader; std::shared_ptr frag_shader; std::shared_ptr render_pass; std::shared_ptr pipeline; std::shared_ptr draw_command_pool; std::shared_ptr onetime_command_pool; rs::vector> command_buffers; rs::vector> onetime_commands; std::shared_ptr renderer; vk::Queue graphic_queue; vk::Queue present_queue; std::shared_ptr vertex_buffer; std::shared_ptr index_buffer; rs::vector> uniform_buffers; std::shared_ptr descriptor_set_layout; std::shared_ptr descriptor_pool; rs::vector> descriptor_sets; std::shared_ptr img_buffer; std::shared_ptr teximage; std::shared_ptr whiteimage; std::shared_ptr depthimage; std::shared_ptr msaaimage; std::shared_ptr teximage_view; std::shared_ptr whiteimage_view; std::shared_ptr depthimage_view; std::shared_ptr msaaimage_view; std::shared_ptr sampler; GlobalVars global_vars; VkSampleCountFlagBits sample_count = VK_SAMPLE_COUNT_4_BIT; VertCollection verts; rs::vector> mdl_objects; std::shared_ptr field; int32_t width; int32_t height; }; Application app; void show_mat4(tmath::fmat4& m) { printf("| %f %f %f %f |\n", m[0], m[4], m[8], m[12]); printf("| %f %f %f %f |\n", m[1], m[5], m[9], m[13]); printf("| %f %f %f %f |\n", m[2], m[6], m[10], m[14]); printf("| %f %f %f %f |\n", m[3], m[7], m[11], m[15]); } GLFWwindow* window; int dx, dy, dwx, dwy; bool window_moving = false; int main(int argc, char *argv[]) { if (!glfwInit()) exit(EXIT_FAILURE); if (!glfwVulkanSupported()) { std::cout << "Vulkan not supported" << std::endl; exit(EXIT_FAILURE); } GLFWmonitor* monitor = glfwGetPrimaryMonitor(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); window = glfwCreateWindow(1280, 800, "Tacraft!", NULL, NULL); if (!window) { glfwTerminate(); exit(EXIT_FAILURE); } const GLFWvidmode *mode = glfwGetVideoMode(monitor); int monitorX, monitorY; glfwGetMonitorPos(monitor, &monitorX, &monitorY); int windowWidth, windowHeight; glfwGetWindowSize(window, &windowWidth, &windowHeight); glfwSetWindowPos(window, monitorX + (mode->width - windowWidth) / 2, monitorY + (mode->height - windowHeight) / 2); float xscale, yscale; glfwGetWindowContentScale(window, &xscale, &yscale); app.init(window); glfwSetKeyCallback(window, [](GLFWwindow* window, int key, int scancode, int action, int mods) { if(action == GLFW_PRESS) { if(key == GLFW_KEY_ESCAPE) glfwSetWindowShouldClose(window, GLFW_TRUE); } else { } }); glfwSetWindowSizeCallback(window, [](GLFWwindow* window, int x, int y) { app.post_window_resized(x == 0 && y == 0); }); glfwSetMouseButtonCallback(window, [](GLFWwindow*,int button,int action,int mods){ if(action == GLFW_PRESS) { if(button == GLFW_MOUSE_BUTTON_RIGHT) { double mx, my; glfwGetWindowPos(window, &dwx, &dwy); glfwGetCursorPos(window, &mx, &my); dx = dwx + (int)mx; dy = dwy + (int)my; window_moving = true; } } else { if(button == GLFW_MOUSE_BUTTON_RIGHT) { window_moving = false; } } }); glfwSetCursorPosCallback(window, [](GLFWwindow*,double x,double y) { if(window_moving) { int wx, wy, delx, dely; glfwGetWindowPos(window, &wx, &wy); delx = wx + (int)x - dx; dely = wy + (int)y - dy; glfwSetWindowPos(window, dwx + delx, dwy + dely); } app.mouse_move(x, y); }); glfwSetTime(0.0); double prev_time = 0.0; double time_check= 0.0; uint32_t frame = 0; while (!glfwWindowShouldClose(window)) { glfwPollEvents(); app.draw_frame(); frame++; double curtime = glfwGetTime(); time_check += (curtime - prev_time); prev_time = curtime; if(time_check > 1.0) { time_check -= 1.0; char buffer[50]; sprintf(buffer, "Tacraft! %d", frame); glfwSetWindowTitle(window, buffer); frame = 0; } } glfwDestroyWindow(window); glfwTerminate(); exit(EXIT_SUCCESS); return 0; };