diff --git a/README.md b/README.md new file mode 100644 index 0000000..7a3c954 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +This is a prototype for a game engine im doing, its very messy and imperfect, but its a working vulkan renderer (and an small minigame that doesnt work yet) made in c++23 that works in basically any system that supports vulkan (with some changes to the CMakeLists.txt file) + +I'm learning while writing this so expect some bad practices and weird stuff! \ No newline at end of file diff --git a/main.cpp b/main.cpp index 69562c7..f711a3b 100644 --- a/main.cpp +++ b/main.cpp @@ -7,8 +7,12 @@ #include #include #include +#include bool skip_rendering = false; +bool stop_physics = false; +std::atomic_bool pressed_space = false; +std::atomic_bool pressed_shift = false; vk::SurfaceFormatKHR format; vk::Extent2D framebuffer_extension; @@ -26,13 +30,31 @@ struct bounding_box float y; float width; float height; + float velocityX; + float velocityY; + float accX; + float accY; }; + + struct uniform +{ + glm::mat4 view; +}; + +struct push { glm::mat4 transform; }; +struct quad +{ + bounding_box box; + std::vector vertices; + push trans; +}; + bounding_box player; float velocityX = 1.0f; float velocityY = 0.50f; @@ -197,16 +219,53 @@ void simple_physics() } } -void simple_physics_step(float t) +bool simple_physics_step(float t, bounding_box &box, std::vector &boxes, bool &on_ground) { - bool collision_x = player.x + player.width / 2 >= 1.0f || player.x - player.width / 2 <= -1.0f; - bool collision_y = player.y + player.height / 2 >=1.0f || player.y - player.height / 2 <= -1.0f; + box.y += box.velocityY * t + 0.5 * box.accY * (t * t); + box.velocityY += box.accY * t; + + box.x += box.velocityX * t + 0.5 * box.accX * (t * t); + box.velocityX += box.accX * t; + bool end_game = false; + + for (auto &b: boxes) + { + b.box.y += b.box.velocityY * t + 0.5 * b.box.accY * (t * t); + b.box.velocityY += b.box.accY * t; + + b.box.x += b.box.velocityX * t + 0.5 * b.box.accX * (t * t); + b.box.velocityX += b.box.accX * t; + + bool collision_x = b.box.x + b.box.width / 2 >= box.x - box.width /2 && b.box.x - b.box.width / 2 <= box.x + box.width /2; + bool collision_y = b.box.y + b.box.height / 2 >= box.y - box.height /2 && b.box.y - b.box.height / 2 <= box.y + box.height /2; + end_game = collision_x && collision_y; + if (end_game) + { + #ifdef NDEBUG + std::println("Collision between pos x: {} y: {} and pos x: {} and pos y: {} ", box.x, box.y, b.box.x, b.box.y); + std::println("With width: {} and height: {} and width: {} and height: {}", box.width, box.height, b.box.width, b.box.height); + std::println("Rightmost vertex in position {} collided with leftmost vertex in position {}", box.x + box.width/2, b.box.x - b.box.width/2); + #endif + } + } + + bool collision_x = box.x + box.width / 2 >= 1.0f || box.x - box.width / 2 <= -1.0f; + bool collision_top_y = box.y - box.height / 2 <= -1.0f; + bool collision_bottom_y = box.y + box.height / 2 >=1.0f; if (collision_x) - velocityX = -velocityX; - else if (collision_y) - velocityY = -velocityY; - player.x = player.x + (velocityX * t); - player.y = player.y + (velocityY * t); + box.velocityX = 0; + else if (collision_top_y) + { + box.velocityY = 0; + box.y = -1.0f + box.height/2; + } + else if (collision_bottom_y) + { + box.velocityY = 0.0f; + box.y = 1.0f - box.height/2; + on_ground = true; + } + return end_game; } std::pair create_buffer(const vk::Device &device, vk::PhysicalDevice selected_physical_device, vk::BufferUsageFlagBits usage, size_t size) @@ -238,11 +297,66 @@ std::pair create_buffer(const vk::Device &device, return std::make_pair(vertex_buffer_memory, vertex_buffer); } +void keyboard_handle(GLFWwindow *window, int key, int scancode, int action, int mods) +{ + if (key == GLFW_KEY_SPACE && action == GLFW_PRESS) + { + //std::println("JUMP!"); + pressed_space = true; + } + else if (key == GLFW_KEY_LEFT_SHIFT && action == GLFW_PRESS) + { + pressed_shift = true; + } + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + exit(0); +} + +bounding_box spawn_enemy(std::mt19937 rng) +{ + std::uniform_real_distribution dist(0.05, 0.4); + std::uniform_real_distribution y_dist(0.6, 0.9); + std::uniform_real_distribution vel_dist(-1.5, -0.5); + bounding_box ret{0}; + ret.height = dist(rng); + ret.width = dist(rng); + ret.x = 0.8; + ret.y = y_dist(rng); + ret.velocityX = vel_dist(rng); + return ret; +} + +void add_quad_to_vertices(std::vector &vertices, std::vector new_quad) +{ + new_quad = convert_quad_to_triangles(new_quad); + + for (auto vert: new_quad) + { + //std::println("Vertex pos x: {}, y: {}, z: {}", vert.position.x, vert.position.y, vert.position.z); + vertices.push_back(vert); + } +} + +std::vector bounding_box_to_vertices(const bounding_box &box) +{ + float half_width = box.width/2; + float half_height = box.height/2; + std::vector vertices = { + {{-half_width, -half_height}, {1.0f, 0.0f, 0.0f}}, + {{half_width, -half_height}, {1.0f, 0.0f, 0.0f}}, + {{half_width, half_height}, {0.0f, 1.0f, 0.0f}}, + {{-half_width, half_height}, {0.0f, 0.0f, 1.0f}} + }; + return vertices; +} + int main() { using clock = std::chrono::system_clock; using ms = std::chrono::duration; GLFWwindow *window = create_window(1000, 800, "hello"); + std::random_device dev; + std::mt19937 rng(dev()); if (!window) return -1; @@ -268,7 +382,12 @@ int main() #endif vk::InstanceCreateInfo createinfo = vk::InstanceCreateInfo( - vk::InstanceCreateFlags(VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR), + #ifdef __APPLE__ + vk::InstanceCreateFlags(VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR), + #endif + #ifdef __linux__ + vk::InstanceCreateFlags(), + #endif &appinfo,layers.size(), layers.data(), extensions.size(), extensions.data()); vk::Instance instance = vk::createInstance(createinfo); @@ -436,13 +555,17 @@ int main() vk::DescriptorSetLayoutCreateInfo descriptor_layout_info(vk::DescriptorSetLayoutCreateFlags(), 1, &descriptor_binding); vk::DescriptorSetLayout descriptor_layout = device.createDescriptorSetLayout(descriptor_layout_info); + vk::PushConstantRange push_constant(vk::ShaderStageFlags(vk::ShaderStageFlagBits::eVertex), 0, sizeof(push)); + vk::PipelineLayoutCreateInfo layout_info = {}; layout_info.setLayoutCount = 1; layout_info.pSetLayouts = &descriptor_layout; + layout_info.pushConstantRangeCount = 1; + layout_info.pPushConstantRanges = &push_constant; vk::PipelineLayout pipeline_layout = device.createPipelineLayout(layout_info); uniform u{}; - u.transform = glm::mat4(1.0f); + u.view = glm::mat4(1.0f); auto rec = create_buffer(device, selected_physical_device, vk::BufferUsageFlagBits::eUniformBuffer, sizeof(uniform)); vk::DeviceMemory uniform_buffer_data = rec.first; vk::Buffer uniform_buffer = rec.second; @@ -512,19 +635,20 @@ int main() framebuffer_extension.height, 1); framebuffers.push_back(device.createFramebuffer(framebuffer_info)); } - - std::vector vertices = { - {{-0.1f, -0.1f}, {1.0f, 0.0f, 0.0f}}, - {{0.1f, -0.1f}, {1.0f, 0.0f, 0.0f}}, - {{0.1f, 0.1f}, {0.0f, 1.0f, 0.0f}}, - {{-0.1f, 0.1f}, {0.0f, 0.0f, 1.0f}} + quad play{}; + play.box = player; + play.vertices = { + {{-0.05f, -0.1f}, {1.0f, 0.0f, 0.0f}}, + {{0.05f, -0.1f}, {1.0f, 0.0f, 0.0f}}, + {{0.05f, 0.1f}, {0.0f, 1.0f, 0.0f}}, + {{-0.05f, 0.1f}, {0.0f, 0.0f, 1.0f}} }; - vertices = convert_quad_to_triangles(vertices); - auto ret = create_buffer(device, selected_physical_device, vk::BufferUsageFlagBits::eVertexBuffer, sizeof(vertices[0]) * vertices.size()); - vk::DeviceMemory vertex_buffer_memory = ret.first; + play.vertices = convert_quad_to_triangles(play.vertices); + auto ret = create_buffer(device, selected_physical_device, vk::BufferUsageFlagBits::eVertexBuffer, sizeof(vertex) * 6 * 100); + vk::DeviceMemory vertex_memory = ret.first; vk::Buffer vertex_buffer = ret.second; - char *data = (char *)device.mapMemory(vertex_buffer_memory, 0, sizeof(vertices[0]) * vertices.size()); - memcpy(data, vertices.data(), sizeof(vertices[0]) * vertices.size()); + char *vertex_data = (char *)device.mapMemory(vertex_memory, 0, sizeof(vertex) * 6 * 100); // 100 quads + memcpy(vertex_data, play.vertices.data(), sizeof(play.vertices[0]) * play.vertices.size()); vk::CommandPoolCreateInfo command_pool_info = {}; @@ -575,22 +699,34 @@ int main() player.x = 0.0f; player.y = 0.0f; player.height = 0.2f; - player.width = 0.2f; + player.width = 0.1f; //velocityX = 0.008f; - std::vector render_vertices = vertices; + float angle = 0.0f; float vel2 = 0.005f; //std::thread phy_thread(simple_physics); + glfwSetKeyCallback(window, keyboard_handle); auto before = clock::now(); + player.accY = 1.3f; + player.x = -0.8; + player.y = -0.5; + play.box = player; + std::vector enemies; + bool on_ground = true; + int jumps = 0; + int score = 0; while(!glfwWindowShouldClose(window)) { glfwPollEvents(); + std::vector render_vertices = play.vertices; auto res_wait = device.waitForFences(next_frame_fence, VK_TRUE, UINT64_MAX); if (res_wait != vk::Result::eSuccess) throw std::runtime_error("failed waiting!"); device.resetFences(next_frame_fence); if (skip_rendering) + { continue; + } auto image_result = device.acquireNextImageKHR(swapchain, UINT64_MAX, image_semaphore); if (image_result.result != vk::Result::eSuccess) { @@ -598,14 +734,70 @@ int main() } uint32_t image_index = image_result.value; vkResetCommandBuffer(command_buffers[0], 0); + if (enemies.empty()) + { + quad enemy{}; + std::mt19937 rng2(dev()); + enemy.box = spawn_enemy(rng2); + enemy.vertices = bounding_box_to_vertices(enemy.box); + enemy.trans.transform = glm::mat4{1.0f}; + enemies.push_back(enemy); + } auto time_elapsed = clock::now() - before; - simple_physics_step(std::chrono::duration_cast>(time_elapsed).count()); + if (pressed_space == true) + { + #ifndef NDEBUG + //std::println("JUMP!"); + #endif + if (on_ground || jumps <= 1) + { + if (jumps <= 1) + play.box.velocityY = -1.2f; + on_ground = false; + jumps++; + std::println("Jumps {}", jumps); + } + pressed_space = false; + } + if (pressed_shift == true) + { + play.box.velocityY = 3.0f; + pressed_shift = false; + } + bool end_game = false; + if (stop_physics == false) + end_game = simple_physics_step(std::chrono::duration_cast>(time_elapsed).count(), play.box, enemies, on_ground); + if (on_ground) + jumps = 0; + if (end_game == true) + { + std::println("You lost!"); + std::println("Your score was {}", score); + stop_physics = true; + #ifndef NDEBUG + std::println("Collision between pos x: {} y: {} and pos x: {} and pos y: {} ", play.box.x, play.box.y, enemies[0].box.x, enemies[0].box.y); + std::println("With width: {} and height: {} and width: {} and height: {}", play.box.width, play.box.height, enemies[0].box.width, enemies[0].box.height); + std::println("Rightmost vertex in position {} collided with leftmost vertex in position {}", play.box.x + play.box.width/2, enemies[0].box.x - enemies[0].box.width/2); + std::println("Jumps {}", jumps); + #endif + } before = clock::now(); - u.transform = move({player.x, player.y}) * rotate(angle); - memcpy(uniform_data, &u, sizeof(uniform)); + play.trans.transform = move({play.box.x, play.box.y}); + for (auto &e: enemies) + { + if (e.box.x + e.box.width/2 <= -1.0f) + { + score += (int)(abs((e.box.width * 10)) + abs((e.box.height * 10)) + abs((e.box.velocityX * 10))); + enemies.clear(); + break; + } + e.trans.transform = move({e.box.x, e.box.y}); + add_quad_to_vertices(render_vertices, e.vertices); + } + //memcpy(uniform_data, &u, sizeof(uniform)); - angle -= 1.0f; - memcpy(data, render_vertices.data(), sizeof(vertices[0]) * vertices.size()); + angle -= 0.01f; + memcpy(vertex_data, render_vertices.data(), sizeof(render_vertices[0]) * render_vertices.size()); vk::CommandBufferBeginInfo begin_info = {}; @@ -621,14 +813,23 @@ int main() //command_buffers[0].setScissor(0, scissor); command_buffers[0].bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 0, descriptor_sets, nullptr); command_buffers[0].bindVertexBuffers(0, 1, &vertex_buffer, &offset); - command_buffers[0].draw(vertices.size(), 1, 0, 0); + command_buffers[0].pushConstants(pipeline_layout, vk::ShaderStageFlags(vk::ShaderStageFlagBits::eVertex), 0, sizeof(push), &play.trans); + command_buffers[0].draw(6, 1, 0, 0); + uint32_t offset_vertex = 6; + for (auto &e: enemies) + { + command_buffers[0].pushConstants(pipeline_layout, vk::ShaderStageFlags(vk::ShaderStageFlagBits::eVertex), 0, sizeof(push), &e.trans); + command_buffers[0].draw(6, 1, offset_vertex, 0); + offset_vertex += 6; + } + command_buffers[0].endRenderPass(); if (vkEndCommandBuffer(command_buffers[0]) != VK_SUCCESS) { throw std::runtime_error("Command buffer creation failed!"); } - vk::PipelineStageFlags flags = vk::PipelineStageFlags(vk::PipelineStageFlagBits::eColorAttachmentOutput); + vk::PipelineStageFlags flags(vk::PipelineStageFlagBits::eColorAttachmentOutput); vk::SubmitInfo submit_info = vk::SubmitInfo(); submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = &image_semaphore; @@ -653,12 +854,15 @@ int main() } thread = false; //phy_thread.join(); - device.unmapMemory(vertex_buffer_memory); + device.unmapMemory(uniform_buffer_data); + device.unmapMemory(vertex_memory); device.waitIdle(); device.destroyDescriptorPool(descriptor_pool); device.destroyDescriptorSetLayout(descriptor_layout); device.destroyBuffer(vertex_buffer); - device.freeMemory(vertex_buffer_memory); + device.destroyBuffer(uniform_buffer); + device.freeMemory(vertex_memory); + device.freeMemory(uniform_buffer_data); for (auto &framebuffer: framebuffers) { device.destroyFramebuffer(framebuffer); diff --git a/shaders/fragment.spv b/shaders/fragment.spv deleted file mode 100644 index 692e6c1..0000000 Binary files a/shaders/fragment.spv and /dev/null differ diff --git a/shaders/vertex.spv b/shaders/vertex.spv deleted file mode 100644 index be23c46..0000000 Binary files a/shaders/vertex.spv and /dev/null differ diff --git a/shaders/vertex.vert b/shaders/vertex.vert index 0ff624b..4f04c13 100644 --- a/shaders/vertex.vert +++ b/shaders/vertex.vert @@ -3,9 +3,13 @@ // x -> -1 (left) 1(right) // y -> -1 (top) 1(bottom) -layout(binding = 0) uniform un{ +layout( push_constant ) uniform push{ mat4 transform; -} trans; +}trans; + +layout(binding = 0) uniform un{ + mat4 view; +} view; layout(location = 0) in vec2 in_position; layout(location = 1) in vec3 in_color;