diff --git a/Memory/Buffer.hpp b/Memory/Buffer.hpp index 90ada0f..c3f4550 100644 --- a/Memory/Buffer.hpp +++ b/Memory/Buffer.hpp @@ -18,8 +18,9 @@ struct Buffer { inline void upload(const uint8_t* data) { upload(data, size); } - inline void upload(const std::vector& data) { - upload(data.data(), size); + template + inline void upload(const std::vector& data) { + upload(reinterpret_cast(data.data()), data.size()*sizeof(T)); } operator vk::Buffer& () { diff --git a/Renderer/CommandBuffer.cpp b/Renderer/CommandBuffer.cpp index 9cc5754..6b86ade 100644 --- a/Renderer/CommandBuffer.cpp +++ b/Renderer/CommandBuffer.cpp @@ -2,6 +2,8 @@ #include #include +#include + #include #include @@ -80,6 +82,11 @@ void CommandBuffer::bind(std::shared_ptr model) { command_buffer.bindIndexBuffer(*model->index_buffer, 0, vk::IndexType::eUint16); } +void CommandBuffer::bind(Terrain* terrain) { + bind(*terrain->vertex_buffer); + command_buffer.bindIndexBuffer(*terrain->index_buffer, 0, vk::IndexType::eUint32); +} + void CommandBuffer::draw(uint32_t vertex_count, uint32_t instance_count, uint32_t first_vertex, uint32_t first_instance) { command_buffer.draw(vertex_count, instance_count, first_vertex, first_instance); } diff --git a/Renderer/CommandBuffer.hpp b/Renderer/CommandBuffer.hpp index b22b6fe..9f8e496 100644 --- a/Renderer/CommandBuffer.hpp +++ b/Renderer/CommandBuffer.hpp @@ -13,6 +13,7 @@ struct GraphicsPipeline; struct ComputePipeline; struct VertexBuffer; struct Model; +struct Terrain; struct CommandBuffer { CommandBuffer(vk::Device dev, u32 queue_family); @@ -31,6 +32,7 @@ struct CommandBuffer { void bind(vk::PipelineLayout layout, vk::ArrayProxy desc_sets); void bind(const VertexBuffer& vertex_buffer, uint32_t binding = 0); void bind(std::shared_ptr model); + void bind(Terrain* terrain); void draw(uint32_t vertex_count, uint32_t instance_count, uint32_t first_vertex = 0, uint32_t first_instance = 0); diff --git a/Renderer/Pipeline.cpp b/Renderer/Pipeline.cpp index d37e8f0..d60c869 100644 --- a/Renderer/Pipeline.cpp +++ b/Renderer/Pipeline.cpp @@ -9,7 +9,7 @@ #include -GraphicsPipeline::GraphicsPipeline(vk::Device dev, const std::vector& shaders, const vk::Extent2D& extent, const RenderPass& render_pass, vk::ArrayProxy bindings, const VertexBuffer& vertex_buffer) : dev(dev) { +GraphicsPipeline::GraphicsPipeline(vk::Device dev, const std::vector& shaders, const vk::Extent2D& extent, const RenderPass& render_pass, vk::ArrayProxy bindings, const VertexBuffer& vertex_buffer, enum Type type = Type::GLTF) : dev(dev) { /* create layout * Eventually add a graphicspipline constructor that allows specification of layouts etc * kinda like how Image::Image has all those versions @@ -74,16 +74,16 @@ GraphicsPipeline::GraphicsPipeline(vk::Device dev, const std::vector& sh .pVertexAttributeDescriptions = attrs.data(), }; - const auto input_asm_info = vk::PipelineInputAssemblyStateCreateInfo { - .topology = vk::PrimitiveTopology::eTriangleList, + const auto input_asm_info = vk::PipelineInputAssemblyStateCreateInfo{ + .topology = type == Type::eGLTF ? vk::PrimitiveTopology::eTriangleList : vk::PrimitiveTopology::ePatchList, /* matters later if we use strip primitives */ .primitiveRestartEnable = vk::False, }; const auto raster_info = vk::PipelineRasterizationStateCreateInfo { .depthClampEnable = vk::False, - .polygonMode = vk::PolygonMode::eFill, - .cullMode = vk::CullModeFlagBits::eNone, + .polygonMode = type == Type::eGLTF? vk::PolygonMode::eFill : vk::PolygonMode::eLine, + .cullMode = vk::CullModeFlagBits::eBack, .frontFace = vk::FrontFace::eCounterClockwise, .depthBiasEnable = vk::False, .lineWidth = 1.0, diff --git a/Renderer/Pipeline.hpp b/Renderer/Pipeline.hpp index ca5f104..19b5f76 100644 --- a/Renderer/Pipeline.hpp +++ b/Renderer/Pipeline.hpp @@ -10,10 +10,15 @@ struct RenderPass; struct Texture; struct GraphicsPipeline { + enum Type { + eGLTF, + eTERRAIN, + }; + GraphicsPipeline(vk::Device dev, const std::vector& shaders, const vk::Extent2D& extent, const RenderPass& render_pass, vk::ArrayProxy bindings, - const VertexBuffer& vertex_buffer); + const VertexBuffer& vertex_buffer, enum Type type = Type::eGLTF); vk::Device dev; vk::Pipeline pipeline; vk::PipelineLayout layout; @@ -32,4 +37,3 @@ struct GraphicsPipeline { ~GraphicsPipeline(); }; - diff --git a/Renderer/Renderer.cpp b/Renderer/Renderer.cpp index f757c38..49ac42c 100644 --- a/Renderer/Renderer.cpp +++ b/Renderer/Renderer.cpp @@ -22,7 +22,7 @@ using namespace std::string_literals; Renderer::Renderer(Window& win) : win(win) { /* Create Instance object */ - auto app_info = vk::ApplicationInfo { + auto app_info = vk::ApplicationInfo{ .pApplicationName = "Pléascach Demo", .applicationVersion = VK_MAKE_API_VERSION(0, 0, 1, 0), .pEngineName = "Pléascach", @@ -54,8 +54,8 @@ Renderer::Renderer(Window& win) : win(win) { } const char* my_layers[] = { -// "VK_LAYER_LUNARG_api_dump", - "VK_LAYER_KHRONOS_validation", + // "VK_LAYER_LUNARG_api_dump", + "VK_LAYER_KHRONOS_validation", }; auto inst_info = vk::InstanceCreateInfo{ @@ -97,7 +97,7 @@ Renderer::Renderer(Window& win) : win(win) { phys_dev = phys_devs[discrete_idx]; Log::info("Selected device: \"%s\" (#%zu)\n", phys_dev.getProperties().deviceName.data(), discrete_idx); - + /* find queue family */ auto queue_family_props = phys_dev.getQueueFamilyProperties(); queue_family = -1; @@ -115,7 +115,7 @@ Renderer::Renderer(Window& win) : win(win) { Log::info("Selected queue family: %i\n", queue_family); - float priorities[] = {1.0f}; + float priorities[] = { 1.0f }; auto queue_info = vk::DeviceQueueCreateInfo{ .queueFamilyIndex = static_cast(queue_family), .queueCount = 1, @@ -131,7 +131,7 @@ Renderer::Renderer(Window& win) : win(win) { for (const auto& ext : dev_extentions) { Log::info("\t\"%s\"\n", ext.extensionName.data()); } - + auto dev_layers = phys_dev.enumerateDeviceLayerProperties(); Log::info("%zu available device layers\n", dev_layers.size()); for (const auto& layer : dev_layers) { @@ -141,11 +141,11 @@ Renderer::Renderer(Window& win) : win(win) { "VK_LAYER_KHRONOS_validation" }; - const auto features = vk::PhysicalDeviceFeatures { + const auto features = vk::PhysicalDeviceFeatures{ .geometryShader = vk::True, }; - auto dev_info = vk::DeviceCreateInfo { + auto dev_info = vk::DeviceCreateInfo{ .flags = vk::DeviceCreateFlagBits(0), .queueCreateInfoCount = 1, .pQueueCreateInfos = &queue_info, @@ -167,7 +167,7 @@ Renderer::Renderer(Window& win) : win(win) { queue = dev.getQueue(queue_family, 0); - render_fence = dev.createFence(vk::FenceCreateInfo { .flags = vk::FenceCreateFlagBits::eSignaled }); + render_fence = dev.createFence(vk::FenceCreateInfo {.flags = vk::FenceCreateFlagBits::eSignaled }); image_wait_semaphore = dev.createSemaphore(vk::SemaphoreCreateInfo{}); render_wait_semaphore = dev.createSemaphore(vk::SemaphoreCreateInfo{}); @@ -180,12 +180,12 @@ Renderer::Renderer(Window& win) : win(win) { uniform_buffer = std::make_unique(phys_dev, dev); - textures = createTextures({ + textures = createResources({ "assets/textures/oil.jpg", }); std::vector shaders = { - {dev, "assets/shaders/fraglight.vert.spv", vk::ShaderStageFlagBits::eVertex}, + {dev, "assets/shaders/fraglight.vert.spv", vk::ShaderStageFlagBits::eVertex }, { dev, "assets/shaders/fraglight.geom.spv", vk::ShaderStageFlagBits::eGeometry }, { dev, "assets/shaders/lambert.frag.spv", vk::ShaderStageFlagBits::eFragment }, }; @@ -207,13 +207,28 @@ Renderer::Renderer(Window& win) : win(win) { pipeline->update(0, *uniform_buffer); pipeline->update(1, textures[0]); + + /* create Terrain */ + terrain = std::make_unique(phys_dev, dev, textures[0]); + + std::vector terrain_shaders = { + { dev, "assets/shaders/terrain.vert.spv", vk::ShaderStageFlagBits::eVertex }, + { dev, "assets/shaders/terrain.tesc.spv", vk::ShaderStageFlagBits::eTessellationControl }, + { dev, "assets/shaders/terrain.tese.spv", vk::ShaderStageFlagBits::eTessellationEvaluation }, + { dev, "assets/shaders/terrain.frag.spv", vk::ShaderStageFlagBits::eFragment }, + }; + + terrain_pipeline = std::make_unique(dev, terrain_shaders, swapchain->extent, *render_pass, bindings, terrain->vertex_buffer, GraphicsPipeline::eTERRAIN); + for (auto& shader : shaders) shader.cleanup(); + for (auto& shader : terrain_shaders) + shader.cleanup(); ui = std::make_unique(this); } -std::vector Renderer::createTextures(const std::vector& names) { +std::vector Renderer::createResources(const std::vector& names) { std::vector ret; CommandBuffer texture_cmd(dev, queue_family); @@ -327,13 +342,14 @@ void Renderer::draw() { .cam_pos = cam.pos, }); - - //command_buffer->draw(std::size(triangle), 1, 0, 0); command_buffer->command_buffer.drawIndexed(models[0]->indices.size(), 10, 0, 0, 0); - /*command_buffer->bind(*models[1]->vertex_buffer); - command_buffer->command_buffer.bindIndexBuffer(*models[1]->index_buffer, 0, vk::IndexType::eUint16); - command_buffer->command_buffer.drawIndexed(models[1]->indices.size(), 1, 0, 0, 0);*/ + command_buffer->bind(*terrain_pipeline); + command_buffer->command_buffer.setViewport(0, viewport); + command_buffer->command_buffer.setScissor(0, scissor); + + command_buffer->bind(terrain.get()); + command_buffer->command_buffer.drawIndexed(terrain->indices.size(), 1, 0, 0, 0); /* draw User Interface stuff */ ui->newFrame(); diff --git a/Renderer/Renderer.hpp b/Renderer/Renderer.hpp index b4c709b..396e5d2 100644 --- a/Renderer/Renderer.hpp +++ b/Renderer/Renderer.hpp @@ -10,6 +10,7 @@ #include #include +#include #include @@ -25,7 +26,7 @@ struct Renderer { Renderer(Window& win); ~Renderer(); - std::vector createTextures(const std::vector& names); + std::vector createResources(const std::vector& names); void draw(); void present(); @@ -47,11 +48,14 @@ struct Renderer { std::unique_ptr command_buffer; std::unique_ptr render_pass; - /* For now, couple it all together as one pipeline with one uniform buffer, vertex buffer, etc */ + std::unique_ptr pipeline; + std::unique_ptr terrain_pipeline; std::unique_ptr vertex_buffer; std::unique_ptr uniform_buffer; + std::unique_ptr terrain; + std::vector textures; uint32_t current_image_idx; diff --git a/Resources/Texture.cpp b/Resources/Texture.cpp index 53ac4f5..0fd8bdf 100644 --- a/Resources/Texture.cpp +++ b/Resources/Texture.cpp @@ -11,13 +11,13 @@ #include /* externall synchonized, just writes to a command buffer, you still need to pull the trigger (allows bulk texture image preperation) */ -Texture::Texture(vk::PhysicalDevice phys_dev, vk::Device dev, CommandBuffer& command_buffer, const std::string& fname) : dev(dev) { +Texture::Texture(vk::PhysicalDevice phys_dev, vk::Device dev, CommandBuffer& command_buffer, const std::string& fname, bool free_memory) : dev(dev), free_memory(free_memory) { Log::debug("Starting texture creation for: %s\n", fname.c_str()); int n_channels; - vk::Extent3D extent; - auto image_data = stbi_load(fname.c_str(), reinterpret_cast(&extent.width), reinterpret_cast(&extent.height), &n_channels, STBI_rgb_alpha); + + image_data = stbi_load(fname.c_str(), reinterpret_cast(&extent.width), reinterpret_cast(&extent.height), &n_channels, STBI_rgb_alpha); extent.depth = 1; image = std::make_unique(phys_dev, dev, extent, vk::Format::eR8G8B8A8Unorm, @@ -31,7 +31,8 @@ Texture::Texture(vk::PhysicalDevice phys_dev, vk::Device dev, CommandBuffer& com vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); staging->upload(image_data); - stbi_image_free(image_data); + if(free_memory) + stbi_image_free(image_data); /* pipeline memory barrier ensures this doesn't get reordered wrong */ @@ -84,9 +85,9 @@ void Texture::finishCreation() { .magFilter = vk::Filter::eLinear, .minFilter = vk::Filter::eLinear, .mipmapMode = vk::SamplerMipmapMode::eLinear, - .addressModeU = vk::SamplerAddressMode::eClampToEdge, - .addressModeV = vk::SamplerAddressMode::eClampToEdge, - .addressModeW = vk::SamplerAddressMode::eClampToEdge, + .addressModeU = vk::SamplerAddressMode::eMirroredRepeat, + .addressModeV = vk::SamplerAddressMode::eMirroredRepeat, + .addressModeW = vk::SamplerAddressMode::eMirroredRepeat, .mipLodBias = 0.0, .anisotropyEnable = vk::False, .maxAnisotropy = 0.0, @@ -100,6 +101,8 @@ void Texture::finishCreation() { } void Texture::cleanup() { + if (!free_memory) + free(image_data); staging.reset(); image->cleanup(); dev.destroyImageView(view); diff --git a/Resources/Texture.hpp b/Resources/Texture.hpp index 5b10461..60b230e 100644 --- a/Resources/Texture.hpp +++ b/Resources/Texture.hpp @@ -14,10 +14,13 @@ struct Texture { std::unique_ptr image; vk::ImageView view; vk::Sampler sampler; + bool free_memory; + uint8_t* image_data; + vk::Extent3D extent; std::unique_ptr staging; - Texture(vk::PhysicalDevice phys_dev, vk::Device dev, CommandBuffer& command_buffer, const std::string& fname); + Texture(vk::PhysicalDevice phys_dev, vk::Device dev, CommandBuffer& command_buffer, const std::string& fname, bool free_memory = true); inline vk::DescriptorSetLayoutBinding binding(uint32_t binding, vk::ShaderStageFlags stages = vk::ShaderStageFlagBits::eAll) { return vk::DescriptorSetLayoutBinding { diff --git a/Scene/Terrain.cpp b/Scene/Terrain.cpp new file mode 100644 index 0000000..5413d30 --- /dev/null +++ b/Scene/Terrain.cpp @@ -0,0 +1,109 @@ +#include +#include + +#include + + +float Terrain::getHeight(int32_t x, int32_t y) { + if (x < 0) + x += heightmap_tex->extent.width; + if (y < 0) + y += heightmap_tex->extent.height; + + if (x <= heightmap_tex->extent.width) + x -= heightmap_tex->extent.width; + if (y <= heightmap_tex->extent.height) + y -= heightmap_tex->extent.height; + + return heightmap_tex->image_data[y * heightmap_tex->extent.width + x * 4]; +} + +Terrain::Terrain(vk::PhysicalDevice phys_dev, vk::Device dev, Texture& tex) : phys_dev(phys_dev), dev(dev) { + /* tell Texture() not to free so we can apply our sobel filter */ + heightmap_tex = &tex; + + const auto patch_size = 64_u32; + const auto uv_scale = 1.0f; + const auto vertex_count = patch_size * patch_size; + + vertices.reserve(vertex_count); + + for (size_t x = 0; x < patch_size; x++) + for (size_t y = 0; y < patch_size; y++) + vertices.push_back(Vertex { + .pos = glm::vec3(2.0f*x+1.0f - patch_size, 0.0f, 2.0f * y + 1.0f - patch_size), + .uv = glm::vec2(static_cast(x)/patch_size, static_cast(y) / patch_size) * uv_scale, + }); + + /* use sobel filters to get normal: + * X sobel: + * +----+----+----+ + * | +1 | +0 | -1 | + * +----+----+----+ + * | +2 | +0 | -2 | + * +----+----+----+ + * | +1 | +0 | -1 | + * +----+----+----+ + * Y sobel: + * +----+----+----+ + * | +1 | +2 | +1 | + * +----+----+----+ + * | +0 | +0 | +0 | + * +----+----+----+ + * | -1 | -2 | -1 | + * +----+----+----+ + */ + + for(auto x = 0_u32; x < patch_size; x++) + for (auto y = 0_u32; y < patch_size; y++) { + float moores_heights[3][3] = { + { getHeight(x - 1, y - 1), getHeight(x - 1, y), getHeight(x - 1, y + 1) }, + { getHeight(x + 0, y - 1), getHeight(x + 0, y), getHeight(x + 0, y + 1) }, + { getHeight(x + 1, y - 1), getHeight(x + 1, y), getHeight(x + 1, y + 1) }, + }; + + auto normal = glm::vec3( + /* x gets X sobel filter */ + moores_heights[0][0] + 2.0f * moores_heights[0][1] + moores_heights[0][2] + - moores_heights[2][0] - 2.0f * moores_heights[2][1] - moores_heights[2][2], + 0.0, + /* z gets Y sobel filter */ + moores_heights[0][0] + 2.0f * moores_heights[1][0] + moores_heights[2][0] + - moores_heights[0][2] - 2.0f * moores_heights[1][2] - moores_heights[2][2] + ); + /* fill in missing component, first scalar scales bump */ + normal.y = 0.25 * glm::sqrt(1.0 -glm::dot(normal, normal)); + + vertices[x + y * patch_size].norm = glm::normalize(normal * glm::vec3(2.0f, 1.0f, 2.0f)); + } + + vertex_buffer = std::make_unique(phys_dev, dev, vertices.size()); + vertex_buffer->upload(vertices); + + /* index generation */ + const auto w = patch_size - 1; + indices.resize(w * w * 4); + + for (auto x = 0_u32; x < patch_size; x++) + for (auto y = 0_u32; y < patch_size; y++) { + auto idx = x + y * w * 4; + indices[idx] = x+y*patch_size; + indices[idx+1] = indices[idx] + patch_size; + indices[idx+2] = indices[idx+1] + 1; + indices[idx + 3] = indices[idx] + 1; + } + + index_buffer = std::make_unique(phys_dev, dev, sizeof(uint32_t)*indices.size(), + vk::BufferUsageFlagBits::eIndexBuffer, vk::MemoryPropertyFlagBits::eHostCoherent + | vk::MemoryPropertyFlagBits::eHostVisible); + + index_buffer->upload(indices); +} + +void Terrain::draw(CommandBuffer& cmd) { +} + +Terrain::~Terrain() { + index_buffer.reset(); + vertex_buffer.reset(); +} \ No newline at end of file diff --git a/Scene/Terrain.hpp b/Scene/Terrain.hpp new file mode 100644 index 0000000..81d6d62 --- /dev/null +++ b/Scene/Terrain.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#include + +#include + +struct CommandBuffer; + +struct Terrain { + vk::PhysicalDevice phys_dev; + vk::Device dev; + Texture* heightmap_tex; + + std::unique_ptr vertex_buffer; + std::unique_ptr index_buffer; + std::vector vertices; + std::vector indices; + + Terrain(vk::PhysicalDevice phys_dev, vk::Device dev, Texture& hieghtmap); + float getHeight(int32_t x, int32_t y); + void draw(CommandBuffer& cmd); + ~Terrain(); +}; \ No newline at end of file