diff --git a/.gitmodules b/.gitmodules index 375b9368a..fd0f6e50d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -62,3 +62,6 @@ [submodule "third_party/tracy"] path = third_party/tracy url = https://github.com/wolfpld/tracy.git +[submodule "samples/complex/render_octmap/octomap"] + path = samples/complex/render_octmap/octomap + url = https://github.com/OctoMap/octomap.git diff --git a/assets b/assets index 8db8ce9c5..3606a63cc 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 8db8ce9c528330f0b1261b07531b009732b08731 +Subproject commit 3606a63ccb56f6f48f6ce42849fe1de6007480b6 diff --git a/samples/README.adoc b/samples/README.adoc index 315c31262..18ac83cf2 100644 --- a/samples/README.adoc +++ b/samples/README.adoc @@ -44,3 +44,6 @@ include::./extensions/README.adoc[] [[tooling-samples]] include::./tooling/README.adoc[] + +[[complex-samples]] +include::../complex/README.adoc[] \ No newline at end of file diff --git a/samples/complex/README.adoc b/samples/complex/README.adoc new file mode 100644 index 000000000..6c6a5ce20 --- /dev/null +++ b/samples/complex/README.adoc @@ -0,0 +1,35 @@ +//// +- Copyright (c) 2021-2024, The Khronos Group +- +- SPDX-License-Identifier: Apache-2.0 +- +- Licensed under the Apache License, Version 2.0 the "License"; +- you may not use this file except in compliance with the License. +- You may obtain a copy of the License at +- +- http://www.apache.org/licenses/LICENSE-2.0 +- +- Unless required by applicable law or agreed to in writing, software +- distributed under the License is distributed on an "AS IS" BASIS, +- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +- See the License for the specific language governing permissions and +- limitations under the License. +- +//// +ifndef::complex_samplespath[:complex_samplespath:] + +== Complex samples + +The goal of these samples is to demonstrate how to use Vulkan in a real world scenario. These are meant to be demonstrations of how to use Vulkan as a whole instead of a singular item within Vulkan. + +This format for samples is to demonstrate how to work with other libraries or setup projects. Some of the samples may or may not use the Framework as needed. + +While the Vulkan concepts found here aren't complex, the interaction and unique benefit with the real-world use cases they work with are meant to be. Examples of what might eventually be found here include AI, OpenXR, GLTF, etc. Topics which don't belong in the other sample categories by themselves yet are useful in many areas. + +=== xref:./{complex_samplespath}render_octmap/README.adoc[Render an Octo-Map] + +*Instancing*: https://docs.vulkan.org/spec/latest/chapters/drawing.html#vkCmdDrawIndexed[`vkCmdDrawIndexed`] + +Uses instancing to render octmaps generated in realtime by ARCore and ARKit then saved, and joined together using Point Cloud Registration into one shared map. + +These maps are commonly used by Robot Operating System (ROS) and this is a solution showing how to render the map created. This is a topic of interest to SLAM and AI with OpenXR for map understanding and joining. diff --git a/samples/complex/render_octomap/CMakeLists.txt b/samples/complex/render_octomap/CMakeLists.txt new file mode 100644 index 000000000..4dbd85f7c --- /dev/null +++ b/samples/complex/render_octomap/CMakeLists.txt @@ -0,0 +1,43 @@ +get_filename_component(FOLDER_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +get_filename_component(PARENT_DIR ${CMAKE_CURRENT_LIST_DIR} PATH) +get_filename_component(CATEGORY_NAME ${PARENT_DIR} NAME) + +add_library( octomath STATIC + octomap/octomap/src/math/Vector3.cpp + octomap/octomap/src/math/Quaternion.cpp + octomap/octomap/src/math/Pose6D.cpp +) +target_include_directories(octomath PUBLIC ${CMAKE_CURRENT_LIST_DIR}/octomap/octomap/include) + +add_library(octomap SHARED + octomap/octomap/src/AbstractOcTree.cpp + octomap/octomap/src/AbstractOccupancyOcTree.cpp + octomap/octomap/src/Pointcloud.cpp + octomap/octomap/src/ScanGraph.cpp + octomap/octomap/src/CountingOcTree.cpp + octomap/octomap/src/OcTree.cpp + octomap/octomap/src/OcTreeNode.cpp + octomap/octomap/src/OcTreeStamped.cpp + octomap/octomap/src/ColorOcTree.cpp +) +target_link_libraries(octomap PUBLIC octomath) + +add_library(dynamicEDT3d SHARED octomap/dynamicEDT3D/src/dynamicEDT3D.cpp) +target_include_directories(dynamicEDT3d PUBLIC octomap/dynamicEDT3D/include) +target_link_libraries(dynamicEDT3d PUBLIC octomap) + +add_sample_with_tags( + ID ${FOLDER_NAME} + CATEGORY ${CATEGORY_NAME} + AUTHOR "Holochip" + NAME "Octmap rendering" + DESCRIPTION "Demonstration of how to render an OctMap which was generated from ICP combining ARCore and ARKit SLAM maps" + FILES + ImGUIUtil.cpp + Screens/MapView.cpp + SHADER_FILES_GLSL + "render_octomap/glsl/render.vert" + "render_octomap/glsl/render.frag" + LIBS + dynamicEDT3d +) \ No newline at end of file diff --git a/samples/complex/render_octomap/ImGUIUtil.cpp b/samples/complex/render_octomap/ImGUIUtil.cpp new file mode 100644 index 000000000..e7f4779c2 --- /dev/null +++ b/samples/complex/render_octomap/ImGUIUtil.cpp @@ -0,0 +1,645 @@ +// +// Created by Steven Winston on 4/14/24. +// + +#include "ImGUIUtil.h" +#include "GLFW/glfw3.h" +#include "api_vulkan_sample.h" +#include +#include + +ImGuiKey Glfw_KeyToImGuiKey(int keycode) +{ + switch (keycode) + { + case GLFW_KEY_TAB: return ImGuiKey_Tab; + case GLFW_KEY_LEFT: return ImGuiKey_LeftArrow; + case GLFW_KEY_RIGHT: return ImGuiKey_RightArrow; + case GLFW_KEY_UP: return ImGuiKey_UpArrow; + case GLFW_KEY_DOWN: return ImGuiKey_DownArrow; + case GLFW_KEY_PAGE_UP: return ImGuiKey_PageUp; + case GLFW_KEY_PAGE_DOWN: return ImGuiKey_PageDown; + case GLFW_KEY_HOME: return ImGuiKey_Home; + case GLFW_KEY_END: return ImGuiKey_End; + case GLFW_KEY_INSERT: return ImGuiKey_Insert; + case GLFW_KEY_DELETE: return ImGuiKey_Delete; + case GLFW_KEY_BACKSPACE: return ImGuiKey_Backspace; + case GLFW_KEY_SPACE: return ImGuiKey_Space; + case GLFW_KEY_ENTER: return ImGuiKey_Enter; + case GLFW_KEY_ESCAPE: return ImGuiKey_Escape; + case GLFW_KEY_APOSTROPHE: return ImGuiKey_Apostrophe; + case GLFW_KEY_COMMA: return ImGuiKey_Comma; + case GLFW_KEY_MINUS: return ImGuiKey_Minus; + case GLFW_KEY_PERIOD: return ImGuiKey_Period; + case GLFW_KEY_SLASH: return ImGuiKey_Slash; + case GLFW_KEY_SEMICOLON: return ImGuiKey_Semicolon; + case GLFW_KEY_EQUAL: return ImGuiKey_Equal; + case GLFW_KEY_LEFT_BRACKET: return ImGuiKey_LeftBracket; + case GLFW_KEY_BACKSLASH: return ImGuiKey_Backslash; + case GLFW_KEY_RIGHT_BRACKET: return ImGuiKey_RightBracket; + case GLFW_KEY_GRAVE_ACCENT: return ImGuiKey_GraveAccent; + case GLFW_KEY_CAPS_LOCK: return ImGuiKey_CapsLock; + case GLFW_KEY_SCROLL_LOCK: return ImGuiKey_ScrollLock; + case GLFW_KEY_NUM_LOCK: return ImGuiKey_NumLock; + case GLFW_KEY_PRINT_SCREEN: return ImGuiKey_PrintScreen; + case GLFW_KEY_PAUSE: return ImGuiKey_Pause; + case GLFW_KEY_KP_0: return ImGuiKey_Keypad0; + case GLFW_KEY_KP_1: return ImGuiKey_Keypad1; + case GLFW_KEY_KP_2: return ImGuiKey_Keypad2; + case GLFW_KEY_KP_3: return ImGuiKey_Keypad3; + case GLFW_KEY_KP_4: return ImGuiKey_Keypad4; + case GLFW_KEY_KP_5: return ImGuiKey_Keypad5; + case GLFW_KEY_KP_6: return ImGuiKey_Keypad6; + case GLFW_KEY_KP_7: return ImGuiKey_Keypad7; + case GLFW_KEY_KP_8: return ImGuiKey_Keypad8; + case GLFW_KEY_KP_9: return ImGuiKey_Keypad9; + case GLFW_KEY_KP_DECIMAL: return ImGuiKey_KeypadDecimal; + case GLFW_KEY_KP_DIVIDE: return ImGuiKey_KeypadDivide; + case GLFW_KEY_KP_MULTIPLY: return ImGuiKey_KeypadMultiply; + case GLFW_KEY_KP_SUBTRACT: return ImGuiKey_KeypadSubtract; + case GLFW_KEY_KP_ADD: return ImGuiKey_KeypadAdd; + case GLFW_KEY_KP_ENTER: return ImGuiKey_KeypadEnter; + case GLFW_KEY_KP_EQUAL: return ImGuiKey_KeypadEqual; + case GLFW_KEY_LEFT_SHIFT: return ImGuiKey_LeftShift; + case GLFW_KEY_LEFT_CONTROL: return ImGuiKey_LeftCtrl; + case GLFW_KEY_LEFT_ALT: return ImGuiKey_LeftAlt; + case GLFW_KEY_LEFT_SUPER: return ImGuiKey_LeftSuper; + case GLFW_KEY_RIGHT_SHIFT: return ImGuiKey_RightShift; + case GLFW_KEY_RIGHT_CONTROL: return ImGuiKey_RightCtrl; + case GLFW_KEY_RIGHT_ALT: return ImGuiKey_RightAlt; + case GLFW_KEY_RIGHT_SUPER: return ImGuiKey_RightSuper; + case GLFW_KEY_MENU: return ImGuiKey_Menu; + case GLFW_KEY_0: return ImGuiKey_0; + case GLFW_KEY_1: return ImGuiKey_1; + case GLFW_KEY_2: return ImGuiKey_2; + case GLFW_KEY_3: return ImGuiKey_3; + case GLFW_KEY_4: return ImGuiKey_4; + case GLFW_KEY_5: return ImGuiKey_5; + case GLFW_KEY_6: return ImGuiKey_6; + case GLFW_KEY_7: return ImGuiKey_7; + case GLFW_KEY_8: return ImGuiKey_8; + case GLFW_KEY_9: return ImGuiKey_9; + case GLFW_KEY_A: return ImGuiKey_A; + case GLFW_KEY_B: return ImGuiKey_B; + case GLFW_KEY_C: return ImGuiKey_C; + case GLFW_KEY_D: return ImGuiKey_D; + case GLFW_KEY_E: return ImGuiKey_E; + case GLFW_KEY_F: return ImGuiKey_F; + case GLFW_KEY_G: return ImGuiKey_G; + case GLFW_KEY_H: return ImGuiKey_H; + case GLFW_KEY_I: return ImGuiKey_I; + case GLFW_KEY_J: return ImGuiKey_J; + case GLFW_KEY_K: return ImGuiKey_K; + case GLFW_KEY_L: return ImGuiKey_L; + case GLFW_KEY_M: return ImGuiKey_M; + case GLFW_KEY_N: return ImGuiKey_N; + case GLFW_KEY_O: return ImGuiKey_O; + case GLFW_KEY_P: return ImGuiKey_P; + case GLFW_KEY_Q: return ImGuiKey_Q; + case GLFW_KEY_R: return ImGuiKey_R; + case GLFW_KEY_S: return ImGuiKey_S; + case GLFW_KEY_T: return ImGuiKey_T; + case GLFW_KEY_U: return ImGuiKey_U; + case GLFW_KEY_V: return ImGuiKey_V; + case GLFW_KEY_W: return ImGuiKey_W; + case GLFW_KEY_X: return ImGuiKey_X; + case GLFW_KEY_Y: return ImGuiKey_Y; + case GLFW_KEY_Z: return ImGuiKey_Z; + case GLFW_KEY_F1: return ImGuiKey_F1; + case GLFW_KEY_F2: return ImGuiKey_F2; + case GLFW_KEY_F3: return ImGuiKey_F3; + case GLFW_KEY_F4: return ImGuiKey_F4; + case GLFW_KEY_F5: return ImGuiKey_F5; + case GLFW_KEY_F6: return ImGuiKey_F6; + case GLFW_KEY_F7: return ImGuiKey_F7; + case GLFW_KEY_F8: return ImGuiKey_F8; + case GLFW_KEY_F9: return ImGuiKey_F9; + case GLFW_KEY_F10: return ImGuiKey_F10; + case GLFW_KEY_F11: return ImGuiKey_F11; + case GLFW_KEY_F12: return ImGuiKey_F12; + case GLFW_KEY_F13: return ImGuiKey_F13; + case GLFW_KEY_F14: return ImGuiKey_F14; + case GLFW_KEY_F15: return ImGuiKey_F15; + case GLFW_KEY_F16: return ImGuiKey_F16; + case GLFW_KEY_F17: return ImGuiKey_F17; + case GLFW_KEY_F18: return ImGuiKey_F18; + case GLFW_KEY_F19: return ImGuiKey_F19; + case GLFW_KEY_F20: return ImGuiKey_F20; + case GLFW_KEY_F21: return ImGuiKey_F21; + case GLFW_KEY_F22: return ImGuiKey_F22; + case GLFW_KEY_F23: return ImGuiKey_F23; + case GLFW_KEY_F24: return ImGuiKey_F24; + default: return ImGuiKey_None; + } +} + + +ImGUIUtil::ImGUIUtil(ApiVulkanSample *_base) : base(_base) +{ + // This conflicts with the hpp_gui context. Disable for now. + // ImGuiContext* context = ImGui::CreateContext(); + // ImGui::SetCurrentContext(context); + auto &device = base->get_render_context().get_device(); + vertexBuffer = + vkb::core::BufferBuilderC(1) + .with_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) + .with_vma_usage(VMA_MEMORY_USAGE_GPU_TO_CPU) + .with_debug_name("GUI vertex buffer") + .build_unique(device); + + indexBuffer = + vkb::core::BufferBuilderC(1) + .with_usage(VK_BUFFER_USAGE_INDEX_BUFFER_BIT) + .with_vma_usage(VMA_MEMORY_USAGE_GPU_TO_CPU) + .with_debug_name("GUI index buffer") + .build_unique(device); +} + +ImGUIUtil::~ImGUIUtil() +{ + // ImGui::DestroyContext(); // this would double free due to the default one in hpp_gui... + // Release all Vulkan resources required for rendering imGui + vkFreeMemory(base->get_render_context().get_device().get_handle(), fontMemory, nullptr); + vkDestroySampler(base->get_render_context().get_device().get_handle(), sampler, nullptr); + vkDestroyPipelineCache(base->get_render_context().get_device().get_handle(), pipelineCache, nullptr); + vkDestroyPipeline(base->get_render_context().get_device().get_handle(), pipeline, nullptr); + vkDestroyPipelineLayout(base->get_render_context().get_device().get_handle(), pipelineLayout, nullptr); + vkDestroyDescriptorPool(base->get_render_context().get_device().get_handle(), descriptorPool, nullptr); + vkDestroyDescriptorSetLayout(base->get_render_context().get_device().get_handle(), descriptorSetLayout, nullptr); +} + +// Initialize styles, keys, etc. +void ImGUIUtil::init(float width, float height) +{ + // Color scheme + vulkanStyle = ImGui::GetStyle(); + vulkanStyle.Colors[ImGuiCol_TitleBg] = ImVec4(1.0f, 0.0f, 0.0f, 0.6f); + vulkanStyle.Colors[ImGuiCol_TitleBgActive] = ImVec4(1.0f, 0.0f, 0.0f, 0.8f); + vulkanStyle.Colors[ImGuiCol_MenuBarBg] = ImVec4(1.0f, 0.0f, 0.0f, 0.4f); + vulkanStyle.Colors[ImGuiCol_Header] = ImVec4(1.0f, 0.0f, 0.0f, 0.4f); + vulkanStyle.Colors[ImGuiCol_CheckMark] = ImVec4(0.0f, 1.0f, 0.0f, 1.0f); + + setStyle(0); + // Dimensions + ImGuiIO& io = ImGui::GetIO(); + io.DisplaySize = ImVec2(width, height); + io.DisplayFramebufferScale = ImVec2(1.0f, 1.0f); +} + +void ImGUIUtil::setStyle(uint32_t index) +{ + switch (index) + { + case 0: + { + ImGuiStyle& style = ImGui::GetStyle(); + style = vulkanStyle; + break; + } + case 1: + ImGui::StyleColorsClassic(); + break; + case 2: + ImGui::StyleColorsDark(); + break; + case 3: + ImGui::StyleColorsLight(); + break; + } +} + +// Initialize all Vulkan resources used by the ui +void ImGUIUtil::initResources(VkRenderPass renderPass, VkQueue copyQueue) +{ + ImGuiIO& io = ImGui::GetIO(); + + // Create font texture + unsigned char* fontData; + int texWidth, texHeight; + + io.Fonts->GetTexDataAsRGBA32(&fontData, &texWidth, &texHeight); + VkDeviceSize uploadSize = texWidth*texHeight * 4 * sizeof(char); + + //SRS - Get Vulkan device driver information if available, use later for display + if (base->get_render_context().get_device().get_gpu().is_extension_supported(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME)) + { + VkPhysicalDeviceProperties2 deviceProperties2 = {}; + deviceProperties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + deviceProperties2.pNext = &driverProperties; + driverProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES; + vkGetPhysicalDeviceProperties2(base->get_render_context().get_device().get_gpu().get_handle(), &deviceProperties2); + } + + // Create target image for copy + VkExtent3D font_extent{vkb::to_u32(texWidth), vkb::to_u32(texHeight), 1u}; + + font_image = std::make_unique(base->get_render_context().get_device(), font_extent, VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, + VMA_MEMORY_USAGE_GPU_ONLY); + font_image->set_debug_name("GUI font image"); + + font_image_view = std::make_unique(*font_image, VK_IMAGE_VIEW_TYPE_2D); + font_image_view->set_debug_name("View on GUI font image"); + + // Upload font data into the vulkan image memory + { + vkb::core::BufferC stage_buffer = vkb::core::BufferC::create_staging_buffer(base->get_render_context().get_device(), uploadSize, fontData); + + auto &command_buffer = base->get_render_context().get_device().request_command_buffer(); + + vkb::FencePool fence_pool{base->get_render_context().get_device()}; + + // Begin recording + command_buffer.begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, 0); + + { + // Prepare for transfer + vkb::ImageMemoryBarrier memory_barrier{}; + memory_barrier.old_layout = VK_IMAGE_LAYOUT_UNDEFINED; + memory_barrier.new_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + memory_barrier.src_access_mask = 0; + memory_barrier.dst_access_mask = VK_ACCESS_TRANSFER_WRITE_BIT; + memory_barrier.src_stage_mask = VK_PIPELINE_STAGE_HOST_BIT; + memory_barrier.dst_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT; + + command_buffer.image_memory_barrier(*font_image_view, memory_barrier); + } + + // Copy + VkBufferImageCopy buffer_copy_region{}; + buffer_copy_region.imageSubresource.layerCount = font_image_view->get_subresource_range().layerCount; + buffer_copy_region.imageSubresource.aspectMask = font_image_view->get_subresource_range().aspectMask; + buffer_copy_region.imageExtent = font_image->get_extent(); + + command_buffer.copy_buffer_to_image(stage_buffer, *font_image, {buffer_copy_region}); + + { + // Prepare for fragmen shader + vkb::ImageMemoryBarrier memory_barrier{}; + memory_barrier.old_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + memory_barrier.new_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + memory_barrier.src_access_mask = VK_ACCESS_TRANSFER_WRITE_BIT; + memory_barrier.dst_access_mask = VK_ACCESS_SHADER_READ_BIT; + memory_barrier.src_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT; + memory_barrier.dst_stage_mask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + + command_buffer.image_memory_barrier(*font_image_view, memory_barrier); + } + // End recording + command_buffer.end(); + auto &queue = base->get_render_context().get_device().get_queue_by_flags(VK_QUEUE_GRAPHICS_BIT, 0); + + queue.submit(command_buffer, base->get_render_context().get_device().request_fence()); + + // Wait for the command buffer to finish its work before destroying the staging buffer + VK_CHECK(base->get_render_context().get_device().get_fence_pool().wait()); + base->get_render_context().get_device().get_fence_pool().reset(); + base->get_render_context().get_device().get_command_pool().reset_pool(); + } + // Font texture Sampler + VkSamplerCreateInfo samplerInfo = vkb::initializers::sampler_create_info(); + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + VK_CHECK(vkCreateSampler(base->get_render_context().get_device().get_handle(), &samplerInfo, nullptr, &sampler)); + + // Descriptor pool + std::vector poolSizes = { + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 6) + }; + VkDescriptorPoolCreateInfo descriptorPoolInfo = vkb::initializers::descriptor_pool_create_info(poolSizes, 7); + VK_CHECK(vkCreateDescriptorPool(base->get_render_context().get_device().get_handle(), &descriptorPoolInfo, nullptr, &descriptorPool)); + + // Descriptor set layout + std::vector setLayoutBindings = { + vkb::initializers::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + VK_SHADER_STAGE_FRAGMENT_BIT, 0), + }; + VkDescriptorSetLayoutCreateInfo descriptorLayout = vkb::initializers::descriptor_set_layout_create_info(setLayoutBindings); + VK_CHECK(vkCreateDescriptorSetLayout(base->get_render_context().get_device().get_handle(), &descriptorLayout, nullptr, &descriptorSetLayout)); + + // Descriptor set + VkDescriptorSetAllocateInfo allocInfo = vkb::initializers::descriptor_set_allocate_info(descriptorPool, + &descriptorSetLayout, 1); + VK_CHECK(vkAllocateDescriptorSets(base->get_render_context().get_device().get_handle(), &allocInfo, &descriptorSet)); + VkDescriptorImageInfo fontDescriptor = vkb::initializers::descriptor_image_info( + sampler, + font_image_view->get_handle(), + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL + ); + std::vector writeDescriptorSets = { + vkb::initializers::write_descriptor_set(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, + &fontDescriptor) + }; + + std::vector liveMapDescriptorSets = MapsView.LoadAssets(base, allocInfo, copyQueue); + if (liveMapDescriptorSets.size() > 0) + { + writeDescriptorSets.insert(liveMapDescriptorSets.end(), liveMapDescriptorSets.begin(), liveMapDescriptorSets.end()); + } + + vkUpdateDescriptorSets(base->get_render_context().get_device().get_handle(), static_cast(writeDescriptorSets.size()), + writeDescriptorSets.data(), + 0, nullptr); + + // Pipeline cache + VkPipelineCacheCreateInfo pipelineCacheCreateInfo = {}; + pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; + VK_CHECK(vkCreatePipelineCache(base->get_render_context().get_device().get_handle(), &pipelineCacheCreateInfo, nullptr, &pipelineCache)); + + // Pipeline layout + // Push constants for UI rendering parameters + VkPushConstantRange pushConstantRange = vkb::initializers::push_constant_range(VK_SHADER_STAGE_VERTEX_BIT, sizeof + (PushConstBlock), 0); + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vkb::initializers::pipeline_layout_create_info + (&descriptorSetLayout, 1); + pipelineLayoutCreateInfo.pushConstantRangeCount = 1; + pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantRange; + VK_CHECK(vkCreatePipelineLayout(base->get_render_context().get_device().get_handle(), &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); + + // Setup graphics pipeline for UI rendering + VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = + vkb::initializers::pipeline_input_assembly_state_create_info(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); + + VkPipelineRasterizationStateCreateInfo rasterizationState = + vkb::initializers::pipeline_rasterization_state_create_info(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, + VK_FRONT_FACE_COUNTER_CLOCKWISE); + + // Enable blending + VkPipelineColorBlendAttachmentState blendAttachmentState{}; + blendAttachmentState.blendEnable = VK_TRUE; + blendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + blendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + blendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD; + blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + blendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD; + blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + + VkPipelineColorBlendStateCreateInfo colorBlendState = + vkb::initializers::pipeline_color_blend_state_create_info(1, &blendAttachmentState); + + VkPipelineDepthStencilStateCreateInfo depthStencilState = + vkb::initializers::pipeline_depth_stencil_state_create_info(VK_FALSE, VK_FALSE, VK_COMPARE_OP_LESS_OR_EQUAL); + + VkPipelineViewportStateCreateInfo viewportState = + vkb::initializers::pipeline_viewport_state_create_info(1, 1, 0); + + VkPipelineMultisampleStateCreateInfo multisampleState = + vkb::initializers::pipeline_multisample_state_create_info(VK_SAMPLE_COUNT_1_BIT); + + std::vector dynamicStateEnables = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR + }; + VkPipelineDynamicStateCreateInfo dynamicState = + vkb::initializers::pipeline_dynamic_state_create_info(dynamicStateEnables); + + std::vector shaderStages{ + base->load_shader("render_octomap", "imgui.vert", VK_SHADER_STAGE_VERTEX_BIT), + base->load_shader("render_octomap", "imgui.frag", VK_SHADER_STAGE_FRAGMENT_BIT) + }; + + VkGraphicsPipelineCreateInfo pipelineCreateInfo = vkb::initializers::pipeline_create_info(pipelineLayout, renderPass); + + pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState; + pipelineCreateInfo.pRasterizationState = &rasterizationState; + pipelineCreateInfo.pColorBlendState = &colorBlendState; + pipelineCreateInfo.pMultisampleState = &multisampleState; + pipelineCreateInfo.pViewportState = &viewportState; + pipelineCreateInfo.pDepthStencilState = &depthStencilState; + pipelineCreateInfo.pDynamicState = &dynamicState; + pipelineCreateInfo.stageCount = static_cast(shaderStages.size()); + pipelineCreateInfo.pStages = shaderStages.data(); + + // Vertex bindings an attributes based on ImGui vertex definition + std::vector vertexInputBindings = { + vkb::initializers::vertex_input_binding_description(0, sizeof(ImDrawVert), VK_VERTEX_INPUT_RATE_VERTEX), + }; + std::vector vertexInputAttributes = { + vkb::initializers::vertex_input_attribute_description(0, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(ImDrawVert, pos)), + // Location 0: Position + vkb::initializers::vertex_input_attribute_description(0, 1, VK_FORMAT_R32G32_SFLOAT, offsetof(ImDrawVert, uv)), + // Location 1: UV + vkb::initializers::vertex_input_attribute_description(0, 2, VK_FORMAT_R8G8B8A8_UNORM, offsetof(ImDrawVert, col)), + // Location 0: Color + }; + VkPipelineVertexInputStateCreateInfo vertexInputState = vkb::initializers::pipeline_vertex_input_state_create_info(); + vertexInputState.vertexBindingDescriptionCount = static_cast(vertexInputBindings.size()); + vertexInputState.pVertexBindingDescriptions = vertexInputBindings.data(); + vertexInputState.vertexAttributeDescriptionCount = static_cast(vertexInputAttributes.size()); + vertexInputState.pVertexAttributeDescriptions = vertexInputAttributes.data(); + + pipelineCreateInfo.pVertexInputState = &vertexInputState; + + VK_CHECK(vkCreateGraphicsPipelines(base->get_render_context().get_device().get_handle(), pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline)); +} + +// Starts a new imGui frame and sets up windows and ui elements +bool ImGUIUtil::newFrame(bool updateFrameGraph) { + ImGui::NewFrame(); + + { + ImGuiStyle &style = ImGui::GetStyle(); + style.ChildRounding = 0.0f; + + + style.WindowPadding = ImVec2(15, 15); // Set window padding + style.FramePadding = ImVec2(5, 5); // Set padding within the widgets + style.ItemInnerSpacing = ImVec2(10, 10); // Set spacing between elements inside widgets + + ImGuiIO& io = ImGui::GetIO(); + ImGui::SetNextWindowSize(io.DisplaySize); + ImGui::SetNextWindowPos(ImVec2(0,0)); + + // NEW WINDOW // + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Remove padding + ImGui::PushStyleColor(ImGuiCol_WindowBg, { 0,0,0,0 }); + ImGui::Begin(" ", nullptr, + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | + ImGuiWindowFlags_NoScrollWithMouse); + ImGui::PopStyleVar(); + ImGui:: PopStyleColor(); + + + MapsView.DrawUI(); + // END WINDOW // + ImGui::End(); + } + ImGui::EndFrame(); + + // Render to generate draw buffers + ImGui::Render(); + if(needsUpdateBuffers) { + needsUpdateBuffers = false; + return true; + } + return false; + +} + +// Update vertex and index buffer containing the imGui elements when required +void ImGUIUtil::updateBuffers() +{ + ImDrawData* imDrawData = ImGui::GetDrawData(); + + if (!imDrawData) + return; + + // Note: Alignment is done inside buffer creation + VkDeviceSize vertexBufferSize = imDrawData->TotalVtxCount * sizeof(ImDrawVert); + VkDeviceSize indexBufferSize = imDrawData->TotalIdxCount * sizeof(ImDrawIdx); + + if ((vertexBufferSize == 0) || (indexBufferSize == 0)) { + return; + } + + // Update buffers only if vertex or index count has been changed compared to current buffer size + if ((vertexBuffer->get_handle() == VK_NULL_HANDLE) || (vertexBufferSize != imDrawData->TotalVtxCount)) + { + vertexBuffer.reset(); + vertexBuffer = std::make_unique(base->get_render_context().get_device(), vertexBufferSize, + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VMA_MEMORY_USAGE_GPU_TO_CPU); + vertexCount = imDrawData->TotalVtxCount; + vertexBuffer->set_debug_name("GUI Util vertex buffer"); + } + + if ((indexBuffer->get_handle() == VK_NULL_HANDLE) || (indexCount != imDrawData->TotalIdxCount)) + { + indexCount = imDrawData->TotalIdxCount; + + indexBuffer.reset(); + indexBuffer = std::make_unique(base->get_render_context().get_device(), indexBufferSize, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VMA_MEMORY_USAGE_GPU_TO_CPU); + indexBuffer->set_debug_name("GUI index buffer"); + } + + // Upload data + ImDrawVert* vtxDst = (ImDrawVert*)vertexBuffer->map(); + ImDrawIdx* idxDst = (ImDrawIdx*)indexBuffer->map(); + + for (int n = 0; n < imDrawData->CmdListsCount; n++) { + const ImDrawList* cmd_list = imDrawData->CmdLists[n]; + memcpy(vtxDst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert)); + memcpy(idxDst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); + vtxDst += cmd_list->VtxBuffer.Size; + idxDst += cmd_list->IdxBuffer.Size; + } + + // Flush to make writes visible to GPU + vertexBuffer->flush(); + indexBuffer->flush(); +} + +void ImGUIUtil::drawFrame(VkCommandBuffer commandBuffer) +{ + ImGuiIO& io = ImGui::GetIO(); + + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + + VkViewport viewport = vkb::initializers::viewport(ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y, 0.0f, 1.0f); + vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + + // UI scale and translate via push constants + pushConstBlock.scale = glm::vec2 (2.0f / io.DisplaySize.x, 2.0f / io.DisplaySize.y); + pushConstBlock.translate = glm::vec2 (-1.0f); + vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstBlock), &pushConstBlock); + + // Render commands + ImDrawData* imDrawData = ImGui::GetDrawData(); + uint32_t vertexOffset = 0; + uint32_t indexOffset = 0; + + if (imDrawData->CmdListsCount > 0) { + + VkDeviceSize offsets[1] = { 0 }; + vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexBuffer->get_handle(), offsets); + vkCmdBindIndexBuffer(commandBuffer, indexBuffer->get_handle(), 0, VK_INDEX_TYPE_UINT16); + + for (auto i = 0; i < imDrawData->CmdListsCount; i++) + { + const ImDrawList* cmd_list = imDrawData->CmdLists[i]; + for (auto j = 0; j < cmd_list->CmdBuffer.Size; j++) + { + const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[j]; + VkRect2D scissorRect; + scissorRect.offset.x = glm::max(pcmd->ClipRect.x, 0.0f); + scissorRect.offset.y = glm::max(pcmd->ClipRect.y, 0.0f); + scissorRect.extent.width = (uint32_t)(pcmd->ClipRect.z - pcmd->ClipRect.x); + scissorRect.extent.height = (uint32_t)(pcmd->ClipRect.w - pcmd->ClipRect.y); + vkCmdSetScissor(commandBuffer, 0, 1, &scissorRect); + + if(((void*)pcmd->TextureId) != nullptr) + { + VkDescriptorSet desc_set[1] = { (VkDescriptorSet)pcmd->TextureId }; + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, desc_set, 0, nullptr); + } else { + //bind the font + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); + } + vkCmdDrawIndexed(commandBuffer, pcmd->ElemCount, 1, indexOffset, vertexOffset, 0); + indexOffset += pcmd->ElemCount; + } + vertexOffset += cmd_list->VtxBuffer.Size; + } + } +} + +void ImGUIUtil::TextColorAlign(int align, const ImVec4& col, const char* text, ...) { + va_list vaList; + va_start(vaList, text); + + float font_width = ImGui::CalcTextSize(text).x; + + switch(align) { + case 1: + ImGui::SameLine( + ImGui::GetContentRegionAvail().x * 0.5f - font_width * 0.5f + ); + break; + case 2: + ImGui::SameLine( + ImGui::GetContentRegionAvail().x - font_width + ); + break; + case 0: + default: + break; + } + + ImGui::TextColoredV(col, text, vaList); + + va_end(vaList); +} + +void ImGUIUtil::handleKey(int key, int, int action, int) { +#if !(defined TARGET_OS_IPHONE) && !(defined __ANDROID__) + ImGuiIO &io = ImGui::GetIO(); + if (action == GLFW_PRESS) + io.AddKeyEvent(Glfw_KeyToImGuiKey(key), true); + if (action == GLFW_RELEASE) + io.AddKeyEvent(Glfw_KeyToImGuiKey(key), false); + + // Modifiers are not reliable across systems + io.KeyCtrl = ImGui::IsKeyPressed(Glfw_KeyToImGuiKey(GLFW_KEY_LEFT_CONTROL)) || ImGui::IsKeyPressed(Glfw_KeyToImGuiKey(GLFW_KEY_RIGHT_CONTROL)); + io.KeyShift = ImGui::IsKeyPressed(Glfw_KeyToImGuiKey(GLFW_KEY_LEFT_SHIFT)) || ImGui::IsKeyPressed(Glfw_KeyToImGuiKey(GLFW_KEY_RIGHT_SHIFT)); + io.KeyAlt = ImGui::IsKeyPressed(Glfw_KeyToImGuiKey(GLFW_KEY_LEFT_ALT)) || ImGui::IsKeyPressed(Glfw_KeyToImGuiKey(GLFW_KEY_RIGHT_ALT)); + io.KeySuper = ImGui::IsKeyPressed(Glfw_KeyToImGuiKey(GLFW_KEY_LEFT_SUPER)) || ImGui::IsKeyPressed(Glfw_KeyToImGuiKey(GLFW_KEY_RIGHT_SUPER)); +#endif +} + +bool ImGUIUtil::GetWantKeyCapture() { + ImGuiIO &io = ImGui::GetIO(); + return io.WantCaptureKeyboard; +} + +void ImGUIUtil::charPressed(uint32_t key) { + ImGuiIO& io = ImGui::GetIO(); + io.AddInputCharacter(key); +} diff --git a/samples/complex/render_octomap/ImGUIUtil.h b/samples/complex/render_octomap/ImGUIUtil.h new file mode 100644 index 000000000..ee1f9349b --- /dev/null +++ b/samples/complex/render_octomap/ImGUIUtil.h @@ -0,0 +1,85 @@ +// +// Created by Steven Winston on 4/14/24. +// + +#ifndef ORB_SLAM3_IMGUI_H +#define ORB_SLAM3_IMGUI_H + +#include "core/buffer.h" +#include "api_vulkan_sample.h" + +#include "Screens/MapView.h" +#include + +// ---------------------------------------------------------------------------- +// ImGUI class +// ---------------------------------------------------------------------------- + +class ImGUIUtil { +private: + // Vulkan resources for rendering the UI + VkSampler sampler; + std::unique_ptr vertexBuffer; + std::unique_ptr indexBuffer; + uint32_t vertexCount = 0; + uint32_t indexCount = 0; + VkDeviceMemory fontMemory = VK_NULL_HANDLE; + std::unique_ptr font_image; + std::unique_ptr font_image_view; + VkPipelineCache pipelineCache; + VkPipelineLayout pipelineLayout; + VkPipeline pipeline; + VkDescriptorPool descriptorPool; + VkDescriptorSetLayout descriptorSetLayout; + VkDescriptorSet descriptorSet; + VkPhysicalDeviceDriverProperties driverProperties = {}; + ApiVulkanSample *base; + ImGuiStyle vulkanStyle; + ImFont* montserratExtraBoldNormal; + ImFont* montserratExtraBoldSmall; + ImFont* montserratBoldNormal; + ImFont* montserratRegularNormal; + int selectedStyle = 0; + float windowWidth, windowHeight; + bool needsUpdateBuffers = false; +public: + enum ViewState { + LIVEMAPS_ACTIVE, + }state; + + MapView MapsView; + + // UI params are set via push constants + struct PushConstBlock { + glm::vec2 scale; + glm::vec2 translate; + } pushConstBlock; + + explicit ImGUIUtil(ApiVulkanSample *_base); + ~ImGUIUtil(); + + // Initialize styles, keys, etc. + void init(float width, float height); + void setStyle(uint32_t index); + static void TextColorAlign(int align, const ImVec4& col, const char* text, ...); + + // Initialize all Vulkan resources used by the ui + void initResources(VkRenderPass renderPass, VkQueue copyQueue); + + // Starts a new imGui frame and sets up windows and ui elements + bool newFrame(bool updateFrameGraph); + + // Update vertex and index buffer containing the imGui elements when required + void updateBuffers(); + + // Draw current imGui frame into a command buffer + void drawFrame(VkCommandBuffer commandBuffer); + + static void handleKey(int key, int scancode, int action, int mode); + + static bool GetWantKeyCapture(); + + static void charPressed(uint32_t key); +}; + +#endif //ORB_SLAM3_IMGUI_H diff --git a/samples/complex/render_octomap/README.adoc b/samples/complex/render_octomap/README.adoc new file mode 100644 index 000000000..f2c1bb00f --- /dev/null +++ b/samples/complex/render_octomap/README.adoc @@ -0,0 +1,26 @@ +== Rendering an OctMap +SLAM or Simultaneous Localization and Mapping is the main driving force behind modern robotics, AR. SLAM is a process which allows us to interact with the physical world. The main step in Localization involves determining where the camera is at the present moment in relation to where it has been in the past. The main idea is if you know where you were (a fix) and you know your momentum and direction, along with the time, you will be able to predict where you will be in the future. This process is called Dead Reckoning. + +When we want to do Dead Reckoning with a camera and an IMU, we're moving into https://virtualrealitypop.com/a-tectonic-shift-in-augmented-reality-c095d0c69df[Visual Inertial Odometry]. However, this only gives us knowledge of where we are in relation to where we were, or Localization. There is no map that is generated nor saved within that process. + +So, at one frame we have a point cloud from our VIO efforts, and the next frame will have another point cloud. If we can register one point cloud with another, then we can join them into one point cloud. This type of point cloud registration is commonly done with things like https://en.wikipedia.org/wiki/Iterative_closest_point[Iterative Closest Point]. + +Now, with our joined point cloud, we need to be able to recognize that sometimes, we've been places before, or we'd wind up with disjoint maps. That's where Loop Closure comes in. + +The preceding high-level background describes how we get to a situation where there's plenty of desire to be able to work with and render point clouds that are dynamically generated. ARCore, and ARKit are both able to create a point cloud map, and everything from Drones to robots use this same basic system to register and deal with the world around them. To navigate a room, a drone/robot might need to be able to determine if a voxel is occupied or not by using an occupancy grid. This gives rise to solutions which are optimized for storing such maps that can be dynamically updated in real time. + +The library https://octomap.github.io/[octomap] provides just such a library. + +In this sample, we demonstrate how using instancing in Vulkan, we can dynamically display and update an Octomap that was generated by ARKit. Using MoltenVK, we can display the map in real time, and communicate it rapidly over a network for desktops to render and combine in real time. +Here, we're simply showing how the render process works. Additionally, we're also providing a Vulkan IMGui wrapper and demonstrating how it is used. It's very similar if not nearly identical to the one found in the Framework, but it does have the ability to render images on the GUI and we use it to demonstrate how to place and size a viewport render context from a GUI to the rendering backend. This sample is meant to join several techniques in a way that mimics how a real world engine might work. + +== Real-world Project background +https://www.holochip.com[Holochip] is an awardee on the U.S. Environmental Protection Agency (US EPA) Phase II SBIR program, Localization and Mapping AI Application (LAMA). LAMA is an iOS and Android compatible SLAM solution for disaster response teams. It will allow responders to map, localize, place markers, notes, voice recording, and use AI object detection in large spaces, and communicate that information with on-site coordinators in GPS- and network-denied environments. + +[cols="a,a", frame=none, grid=none] +|=== +| image::./images/mapping.png[mapping] +| image::./images/markers.png[markers] +|=== + +For further details on this project contact mailto:info@holochip.com[] diff --git a/samples/complex/render_octomap/Screens/MapView.cpp b/samples/complex/render_octomap/Screens/MapView.cpp new file mode 100644 index 000000000..40c6491a7 --- /dev/null +++ b/samples/complex/render_octomap/Screens/MapView.cpp @@ -0,0 +1,66 @@ +// +// Created by swinston on 5/22/24. +// + +#include "MapView.h" + +MapView::MapView() : mapSize({153.0f, 221.0f}) +{ + +} + +MapView::~MapView() = default; + +std::vector MapView::LoadAssets(ApiVulkanSample *, const VkDescriptorSetAllocateInfo &, VkQueue) { + return {}; +} + +bool MapView::DrawUI() { + ImGuiStyle &style = ImGui::GetStyle(); + style.ChildRounding = 0.0f; + + const ImVec4 oscSidebarColor = ImVec4( + 0x41 / 255.0f, // Red component + 0x40 / 255.0f, // Green component + 0x42 / 255.0f, // Blue component + 1.0f // Alpha component, fully opaque + ); + + float oscWindowHeight = 1080; + float oscWindowWidth = 1920; + float oscWindowMainPadding = 20.0f; + + ImGui::SetNextWindowSize(ImVec2(oscWindowWidth, oscWindowHeight)); + + + style.WindowRounding = 12.0f; + style.ChildRounding = 12.0f; + style.FrameRounding = 12.0f; + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 12.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(100, 100)); + + float sidebarExpandedWidth = (240.0f); + float displaySpaceY = oscWindowMainPadding; + float displaySpaceX = ((oscWindowMainPadding * 2) + sidebarExpandedWidth); + float displaySpaceHeight = (ImGui::GetContentRegionAvail().y - (oscWindowMainPadding * 2)); + float displaySpaceWidth = (ImGui::GetContentRegionAvail().x - (oscWindowMainPadding * 3) - sidebarExpandedWidth); + + ImGui::SetCursorPosY(displaySpaceY); + ImGui::SetCursorPosX(displaySpaceX); + ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(oscSidebarColor)); + ImGui::BeginChild("liveMaps", ImVec2(displaySpaceWidth, displaySpaceHeight), false); + ImGui::PopStyleColor(); + ImGui::EndChild(); + ImGui::SetCursorPosY(0); + ImGui::SetCursorPosX(0); + + mapSize = {displaySpaceWidth, displaySpaceHeight}; + mapPos = {displaySpaceX, displaySpaceY}; + + ImGui::SetCursorPosY(0); + ImGui::SetCursorPosX(0); + ImGui::PopStyleVar(); + ImGui::PopStyleVar(); + + return false; +} diff --git a/samples/complex/render_octomap/Screens/MapView.h b/samples/complex/render_octomap/Screens/MapView.h new file mode 100644 index 000000000..4f5f9e299 --- /dev/null +++ b/samples/complex/render_octomap/Screens/MapView.h @@ -0,0 +1,24 @@ +// +// Created by swinston on 12/13/24. +// + +#ifndef MAPVIEW_H +#define MAPVIEW_H + +#include "api_vulkan_sample.h" + +class MapView { +public: + MapView(); + ~MapView(); + glm::vec2 mapPos; + glm::vec2 mapSize; + + std::vector LoadAssets(ApiVulkanSample *base, const VkDescriptorSetAllocateInfo &allocInfo, VkQueue copyQueue); + + bool DrawUI(); +}; + + + +#endif //MAPVIEW_H diff --git a/samples/complex/render_octomap/images/mapping.png b/samples/complex/render_octomap/images/mapping.png new file mode 100644 index 000000000..e3f518b9d Binary files /dev/null and b/samples/complex/render_octomap/images/mapping.png differ diff --git a/samples/complex/render_octomap/images/markers.png b/samples/complex/render_octomap/images/markers.png new file mode 100644 index 000000000..5b3da3275 Binary files /dev/null and b/samples/complex/render_octomap/images/markers.png differ diff --git a/samples/complex/render_octomap/render_octomap.cpp b/samples/complex/render_octomap/render_octomap.cpp new file mode 100644 index 000000000..3a70ffcc5 --- /dev/null +++ b/samples/complex/render_octomap/render_octomap.cpp @@ -0,0 +1,447 @@ +// +// Created by swinston on 12/12/24. +// + +#include "render_octomap.h" +#include "octomap/octomap.h" + +render_octomap::render_octomap() + : vertices() + , indexCount() + , pipelineCache(VK_NULL_HANDLE) + , pipelineLayout(VK_NULL_HANDLE) + , pipeline(VK_NULL_HANDLE) + , descriptorPool(VK_NULL_HANDLE) + , descriptorSetLayout(VK_NULL_HANDLE) + , descriptorSet(VK_NULL_HANDLE) + , gui(nullptr) + , mMaxTreeDepth() + , m_zMin() + , m_zMax() + , lastMapBuildSize() +{ + title = "Octomap Viewer"; + map = new octomap::OcTree(0.1f); +} +render_octomap::~render_octomap() +{ + if (has_device()) + { + vkDestroyPipeline(get_device().get_handle(), pipeline, nullptr); + vkDestroyPipelineLayout(get_device().get_handle(), pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(get_device().get_handle(), descriptorSetLayout, nullptr); + vkDestroyPipelineCache(get_device().get_handle(), pipelineCache, nullptr); + vkDestroyDescriptorPool(get_device().get_handle(), descriptorPool, nullptr); + delete gui; + gui = nullptr; + } + delete map; + map = nullptr; +} +void render_octomap::BuildCubes() { + const octomap::OcTree *tree = map; + if (tree->size() == 0) { + return; + } + float nextBuildSize = static_cast(lastMapBuildSize) + (static_cast(lastMapBuildSize) * 0.05f); + if (static_cast(tree->size()) < nextBuildSize) { + return; + } + + // maximum size to prevent crashes on large maps: (should be checked in a better way than a constant) +// bool showAll = ((double)tree->size() < 5 * 1e6); + double minX, minY, minZ, maxX, maxY, maxZ; + tree->getMetricMin(minX, minY, minZ); + tree->getMetricMax(maxX, maxY, maxZ); + + // set min/max Z for color height map + m_zMin = static_cast(minZ); + m_zMax = static_cast(maxZ); + + //this is to get just grey; doing full color heightmap for now. + //h = std::min(std::max((h-m_zMin)/ (m_zMax - m_zMin), 0.0f), 1.0f) * 0.4f + 0.3f; // h \in [0.3, 0.7] + + instances.clear(); + for (auto it = tree->begin_tree(mMaxTreeDepth), end = tree->end_tree(); it != end; ++it) { +// for(auto it = tree->begin_leafs_bbx(bb_min, bb_max, mMaxTreeDepth), end=tree->end_leafs_bbx(); it!= end; ++it) { + if (it.isLeaf() && tree->isNodeOccupied(*it)) { +// if (tree->isNodeOccupied(*it) ) { + glm::vec3 coords = {it.getCoordinate().x(), it.getCoordinate().y(), it.getCoordinate().z()}; + coords.y *= -1; + InstanceData instance; + instance.pos[0] = coords[0]; + instance.pos[1] = coords[1]; + instance.pos[2] = coords[2]; + float h = coords[2]; + if (m_zMin >= m_zMax) + h = 0.5f; + else { + h = (1.0f - std::min(std::max((h - m_zMin) / (m_zMax - m_zMin), 0.0f), 1.0f)) * 0.8f; + } + + // blend over HSV-values (more colors) + float r, g, b; + float s = 1.0f; + float v = 1.0f; + + h -= floor(h); + h *= 6; + int i; + float m, n, f; + + i = floor(h); + f = h - static_cast(i); + if (!(i & 1)) + f = 1 - f; // if "i" is even + m = v * (1 - s); + n = v * (1 - s * f); + + switch (i) { + case 6: + case 0:r = v; + g = n; + b = m; + break; + case 1:r = n; + g = v; + b = m; + break; + case 2:r = m; + g = v; + b = n; + break; + case 3:r = m; + g = n; + b = v; + break; + case 4:r = n; + g = m; + b = v; + break; + case 5:r = v; + g = m; + b = n; + break; + default:r = 1; + g = 0.5f; + b = 0.5f; + break; + } + + instance.col[0] = r; + instance.col[1] = g; + instance.col[2] = b; + instance.col[3] = 1.0f; + instance.scale = 1.0f; // static_cast(it.getSize()); + instances.push_back(instance); + } + } // end for all voxels + // Create buffers + if (!instances.empty()) { + instanceBuffer = std::make_unique(get_device(), + instances.size() * sizeof(InstanceData), + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VMA_MEMORY_USAGE_CPU_TO_GPU); + + auto buf = instanceBuffer->map(); + memcpy(buf, instances.data(), instances.size() * sizeof(InstanceData)); + instanceBuffer->flush(); + instanceBuffer->unmap(); + } + // instance buffer + + + lastBuildTime = std::chrono::system_clock::now(); + lastMapBuildSize = tree->size(); +} + +void render_octomap::build_command_buffers() +{ + VkCommandBufferBeginInfo command_buffer_begin_info = vkb::initializers::command_buffer_begin_info(); + + VkClearValue clear_values[2]; + clear_values[0].color = {{0.0f, 0.0f, 0.033f, 0.0f}}; + clear_values[1].depthStencil = {1.0f, 0}; + + VkRenderPassBeginInfo render_pass_begin_info = vkb::initializers::render_pass_begin_info(); + render_pass_begin_info.renderPass = render_pass; + render_pass_begin_info.renderArea.extent.width = width; + render_pass_begin_info.renderArea.extent.height = height; + render_pass_begin_info.clearValueCount = 2; + render_pass_begin_info.pClearValues = clear_values; + gui->newFrame(frame_count == 0); + gui->updateBuffers(); + + for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i) + { + // Set target frame buffer + render_pass_begin_info.framebuffer = framebuffers[i]; + VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffers[i], &command_buffer_begin_info)); + + vkCmdBeginRenderPass(draw_cmd_buffers[i], &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); + // Render imGui + gui->drawFrame(draw_cmd_buffers[i]); + VkViewport viewport = vkb::initializers::viewport(gui->MapsView.mapSize.x, gui->MapsView.mapSize.y, 0.0f, 1.0f); + viewport.x = gui->MapsView.mapPos.x; + viewport.y = gui->MapsView.mapPos.y; + vkCmdSetViewport(draw_cmd_buffers[i], 0, 1, &viewport); + VkRect2D scissorRect; + scissorRect.offset.x = static_cast(gui->MapsView.mapPos.x); + scissorRect.offset.y = static_cast(gui->MapsView.mapPos.y); + scissorRect.extent.width = static_cast(gui->MapsView.mapSize.x); + scissorRect.extent.height = static_cast(gui->MapsView.mapSize.y); + vkCmdSetScissor(draw_cmd_buffers[i], 0, 1, &scissorRect); + + VkDeviceSize offsets[1] = {0}; + vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, + &descriptorSet, 0, nullptr); + vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + vkCmdBindVertexBuffers(draw_cmd_buffers[i], 0, 1, &vertexBuffer->get_handle(), offsets); + vkCmdBindVertexBuffers(draw_cmd_buffers[i], 1, 1, &instanceBuffer->get_handle(), offsets); + vkCmdBindIndexBuffer(draw_cmd_buffers[i], indexBuffer->get_handle(), 0, VK_INDEX_TYPE_UINT32); + + + vkCmdDrawIndexed(draw_cmd_buffers[i], indexCount, instances.size(), 0, 0, 0); + // draw_ui(draw_cmd_buffers[i]); + vkCmdEndRenderPass(draw_cmd_buffers[i]); + + VK_CHECK(vkEndCommandBuffer(draw_cmd_buffers[i])); + } +} +bool render_octomap::prepare(const vkb::ApplicationOptions &options) +{ + if (!ApiVulkanSample::prepare(options)) + { + return false; + } + camera.type = vkb::CameraType::LookAt; + camera.set_perspective(60.0f, static_cast(width) / static_cast(height), 0.1f, 256.0f); + camera.set_rotation({0.0f, 0.0f, 0.0f}); + camera.set_translation({0.0f, 0.0f, -1.0f}); + map->readBinary("assets/scenes/octMap.bin"); + BuildCubes(); + gui = new ImGUIUtil(this); + gui->init(static_cast(width), static_cast(height)); + gui->initResources(render_pass, queue); + createPipelines(render_pass); + build_command_buffers(); + prepared = true; + return true; +} + +void render_octomap::createPipelines(VkRenderPass renderPass) { + SetupVertexDescriptions(); + prepareUBO(); + auto inputAssemblyState = vkb::initializers::pipeline_input_assembly_state_create_info(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); + auto raster_State = vkb::initializers::pipeline_rasterization_state_create_info(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0); + auto blendAttachmentState = vkb::initializers::pipeline_color_blend_attachment_state(0xf, VK_FALSE); + auto colorBlendState = vkb::initializers::pipeline_color_blend_state_create_info(1, &blendAttachmentState); + auto depthStencilState = vkb::initializers::pipeline_depth_stencil_state_create_info(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL); + auto viewportState = vkb::initializers::pipeline_viewport_state_create_info(1, 1, 0); + auto multisampleState = vkb::initializers::pipeline_multisample_state_create_info(VK_SAMPLE_COUNT_1_BIT, 0); + + std::vector dynamicStateEnables = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR, + VK_DYNAMIC_STATE_LINE_WIDTH + }; + + auto dynamicState = vkb::initializers::pipeline_dynamic_state_create_info(dynamicStateEnables.data(), dynamicStateEnables.size(), 0); + + VkPipelineCacheCreateInfo pipelineCacheCreateInfo = {}; + pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; + VK_CHECK(vkCreatePipelineCache(get_device().get_handle(), &pipelineCacheCreateInfo, nullptr, &pipelineCache)); + + // Rendering pipeline + std::vector poolSizes = { + // Graphics pipelines uniform buffers + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2) + }; + VkDescriptorPoolCreateInfo descriptorPoolInfo = vkb::initializers::descriptor_pool_create_info(poolSizes, 3); + VK_CHECK(vkCreateDescriptorPool(get_device().get_handle(), &descriptorPoolInfo, nullptr, &descriptorPool)); + + std::vector setLayoutBindings = { + // Binding 0: Vertex shader uniform buffer + vkb::initializers::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0) + }; + + auto descriptorLayout = vkb::initializers::descriptor_set_layout_create_info(setLayoutBindings); + VK_CHECK(vkCreateDescriptorSetLayout(get_device().get_handle(), &descriptorLayout, nullptr, &descriptorSetLayout)); + + auto pPipelineLayoutCreateInfo = vkb::initializers::pipeline_layout_create_info(&descriptorSetLayout, 1); + VK_CHECK(vkCreatePipelineLayout(get_device().get_handle(), &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout)); + + // Load shaders + std::vector shaderStages = { + load_shader("render_octomap", "render.vert", VK_SHADER_STAGE_VERTEX_BIT), + load_shader("render_octomap", "render.frag", VK_SHADER_STAGE_FRAGMENT_BIT) + }; + + auto pipelineCreateInfo = vkb::initializers::pipeline_create_info(pipelineLayout, renderPass, 0); + + pipelineCreateInfo.pVertexInputState = &vertices.inputState; + pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState; + pipelineCreateInfo.pRasterizationState = &raster_State; + pipelineCreateInfo.pColorBlendState = &colorBlendState; + pipelineCreateInfo.pMultisampleState = &multisampleState; + pipelineCreateInfo.pViewportState = &viewportState; + pipelineCreateInfo.pDepthStencilState = &depthStencilState; + pipelineCreateInfo.pDynamicState = &dynamicState; + pipelineCreateInfo.stageCount = shaderStages.size(); + pipelineCreateInfo.pStages = shaderStages.data(); + pipelineCreateInfo.renderPass = renderPass; + + VK_CHECK( + vkCreateGraphicsPipelines(get_device().get_handle(), pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline)); + + auto allocInfo = vkb::initializers::descriptor_set_allocate_info(descriptorPool, &descriptorSetLayout, 1); + VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &allocInfo, &descriptorSet)); + VkDescriptorBufferInfo buffer_descriptor = create_descriptor(*uniformBufferVS); + std::vector baseImageWriteDescriptorSets = { + vkb::initializers::write_descriptor_set(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, + &buffer_descriptor) + }; + + vkUpdateDescriptorSets(get_device().get_handle(), baseImageWriteDescriptorSets.size(), baseImageWriteDescriptorSets.data(), 0, nullptr); +} + +void render_octomap::prepareUBO() { + // Vertex shader uniform buffer block + uniformBufferVS = std::make_unique(get_device(), + sizeof(uboVS), + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VMA_MEMORY_USAGE_CPU_TO_GPU); + updateUBO(); +} + +void render_octomap::updateUBO() { + uboVS.projection = camera.matrices.perspective; + uboVS.camera = camera.matrices.view; + + uniformBufferVS->convert_and_update(uboVS); +} + +// This just gives the first vertexBuffer +void render_octomap::generateMasterCube() { + // Setup vertices for a single quad made from two triangles + std::vector verticesLoc = + { + {{0.5f, 0.5f, 0.5f}}, + {{0.5f, 0.5f, -0.5f}}, + {{0.5f, -0.5f, 0.5f}}, + {{0.5f, -0.5f, -0.5f}}, + {{-0.5f, 0.5f, 0.5f}}, + {{-0.5f, 0.5f, -0.5f}}, + {{-0.5f, -0.5f, 0.5f}}, + {{-0.5f, -0.5f, -0.5f}} + }; + + // Setup indices + std::vector indices = { + //right face + 0, 1, 3, 3, 2, 0, + //left face + 4, 5, 7, 7, 6, 4, + + //Top face + 0, 1, 5, 5, 4, 0, + //Bottom face + 2, 3, 7, 7, 6, 2, + + //Back face + 0, 2, 6, 6, 4, 0, + //Front face + 1, 3, 7, 7, 5, 1 + }; + indexCount = static_cast(indices.size()); + + // Create buffers + // For the sake of simplicity we won't stage the vertex data to the gpu memory + // Vertex buffer + vertexBuffer = std::make_unique(get_device(), + verticesLoc.size() * sizeof(Vertex), + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VMA_MEMORY_USAGE_CPU_TO_GPU); + + auto buf = vertexBuffer->map(); + memccpy(buf, verticesLoc.data(), 0, verticesLoc.size() * sizeof(Vertex)); + vertexBuffer->flush(); + vertexBuffer->unmap(); + // Index buffer + indexBuffer = std::make_unique(get_device(), + indices.size() * sizeof(uint32_t), + VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VMA_MEMORY_USAGE_CPU_TO_GPU); + buf = indexBuffer->map(); + memccpy(buf, indices.data(), 0, indices.size() * sizeof(uint32_t)); + indexBuffer->flush(); + indexBuffer->unmap(); +} + +void render_octomap::SetupVertexDescriptions() +{ + generateMasterCube(); + // Binding description + vertices.bindingDescriptions = { + vkb::initializers::vertex_input_binding_description(0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX), + vkb::initializers::vertex_input_binding_description(1, sizeof(InstanceData), VK_VERTEX_INPUT_RATE_INSTANCE)}; + + // Attribute descriptions + // Describes memory layout and shader positions + vertices.attributeDescriptions = { + // Location 0: Position + vkb::initializers::vertex_input_attribute_description(0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0), + + // Per-Instance attributes + vkb::initializers::vertex_input_attribute_description(1, 1, VK_FORMAT_R32G32B32_SFLOAT, 0), // Location 1: Position + vkb::initializers::vertex_input_attribute_description(1, 2, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float) * 3), // Location 2: Color + vkb::initializers::vertex_input_attribute_description(1, 3, VK_FORMAT_R32_SFLOAT, sizeof(float) * 7), // Location 3: Scale + }; + + // Assign to vertex buffer + vertices.inputState = vkb::initializers::pipeline_vertex_input_state_create_info(); + vertices.inputState.vertexBindingDescriptionCount = vertices.bindingDescriptions.size(); + vertices.inputState.pVertexBindingDescriptions = vertices.bindingDescriptions.data(); + vertices.inputState.vertexAttributeDescriptionCount = vertices.attributeDescriptions.size(); + vertices.inputState.pVertexAttributeDescriptions = vertices.attributeDescriptions.data(); +} +bool render_octomap::resize(const uint32_t width, const uint32_t height) +{ + ApiVulkanSample::resize(width, height); + rebuild_command_buffers(); + return true; +} + +void render_octomap::render(float delta_time) +{ + if (!prepared) + { + return; + } + prepare_frame(); + + // Update imGui + ImGuiIO& io = ImGui::GetIO(); + + io.DisplaySize = ImVec2(static_cast(width), static_cast(height)); + io.DeltaTime = delta_time; + + // Command buffer to be submitted to the queue + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &draw_cmd_buffers[current_buffer]; + + // Submit to queue + VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE)); + + submit_frame(); + if (!paused || camera.updated) + { + updateUBO(); + } + BuildCubes(); +} + +std::unique_ptr create_render_octomap() +{ + return std::make_unique(); +} \ No newline at end of file diff --git a/samples/complex/render_octomap/render_octomap.h b/samples/complex/render_octomap/render_octomap.h new file mode 100644 index 000000000..57be06e49 --- /dev/null +++ b/samples/complex/render_octomap/render_octomap.h @@ -0,0 +1,76 @@ +// +// Created by swinston on 12/12/24. +// + +#ifndef RENDER_OCTOMAP_H +#define RENDER_OCTOMAP_H + +#include "api_vulkan_sample.h" +#include +#include +#include +#include + +namespace octomap { + class OcTree; +} + +class render_octomap : public ApiVulkanSample +{ + public: + render_octomap(); + ~render_octomap() override; + void BuildCubes(); + void build_command_buffers() override; + bool prepare(const vkb::ApplicationOptions &options) override; + void render(float delta_time) override; + void createPipelines(VkRenderPass renderPass); + void prepareUBO(); + void updateUBO(); + void generateMasterCube(); + void SetupVertexDescriptions(); + bool resize(const uint32_t width, const uint32_t height) override; + + private: + struct + { + VkPipelineVertexInputStateCreateInfo inputState; + std::vector bindingDescriptions; + std::vector attributeDescriptions; + } vertices; + struct InstanceData + { + float pos[3]; + float col[4]; + float scale{0.0f}; + }; + struct + { + glm::mat4 projection; + glm::mat4 camera; + } uboVS; + std::unique_ptr vertexBuffer; + std::unique_ptr indexBuffer; + std::unique_ptr instanceBuffer; + std::unique_ptr uniformBufferVS; + uint32_t indexCount; + VkPipelineCache pipelineCache; + VkPipelineLayout pipelineLayout; + VkPipeline pipeline; + VkDescriptorPool descriptorPool; + VkDescriptorSetLayout descriptorSetLayout; + VkDescriptorSet descriptorSet; + octomap::OcTree *map; + ImGUIUtil *gui; + unsigned int mMaxTreeDepth; + + float m_zMin; + float m_zMax; + uint64_t lastMapBuildSize; + std::chrono::time_point lastBuildTime; + std::vector instances; +}; + +std::unique_ptr create_render_octomap(); + +#endif //RENDER_OCTOMAP_H diff --git a/shaders/render_octomap/glsl/imgui.frag b/shaders/render_octomap/glsl/imgui.frag new file mode 100644 index 000000000..e88e11b83 --- /dev/null +++ b/shaders/render_octomap/glsl/imgui.frag @@ -0,0 +1,13 @@ +#version 450 + +layout (binding = 0) uniform sampler2D fontSampler; + +layout (location = 0) in vec2 inUV; +layout (location = 1) in vec4 inColor; + +layout (location = 0) out vec4 outColor; + +void main() +{ + outColor = inColor * texture(fontSampler, inUV); +} diff --git a/shaders/render_octomap/glsl/imgui.vert b/shaders/render_octomap/glsl/imgui.vert new file mode 100644 index 000000000..b5bb7ae01 --- /dev/null +++ b/shaders/render_octomap/glsl/imgui.vert @@ -0,0 +1,25 @@ +#version 450 + +layout (location = 0) in vec2 inPos; +layout (location = 1) in vec2 inUV; +layout (location = 2) in vec4 inColor; + +layout (push_constant) uniform PushConstants { + vec2 scale; + vec2 translate; +} pushConstants; + +layout (location = 0) out vec2 outUV; +layout (location = 1) out vec4 outColor; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + outUV = inUV; + outColor = inColor; + gl_Position = vec4(inPos * pushConstants.scale + pushConstants.translate, 0.0, 1.0); +} diff --git a/shaders/render_octomap/glsl/render.frag b/shaders/render_octomap/glsl/render.frag new file mode 100644 index 000000000..cd3883b9f --- /dev/null +++ b/shaders/render_octomap/glsl/render.frag @@ -0,0 +1,10 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec4 inColor; +layout(location = 0) out vec4 rt0; + +void main() +{ + rt0 = inColor; +} \ No newline at end of file diff --git a/shaders/render_octomap/glsl/render.vert b/shaders/render_octomap/glsl/render.vert new file mode 100644 index 000000000..a727ab0cd --- /dev/null +++ b/shaders/render_octomap/glsl/render.vert @@ -0,0 +1,29 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 aPos; + +// Instanced attributes +layout (location = 1) in vec3 instancePos; +layout (location = 2) in vec4 inColor; +layout (location = 3) in float instanceScale; + +layout (binding = 0) uniform UBO +{ + mat4 projection; + mat4 camera; +} ubo; + +layout (location = 0) out vec4 outColor; + +void main() +{ + outColor = inColor; + vec4 locPos = vec4(aPos, 1.0); + float eps = 0.00001; + vec4 Pos = vec4((locPos.xyz * instanceScale) + instancePos, 1.0); + Pos.x -= eps; + Pos.y -= eps; + Pos.z -= eps; + gl_Position = ubo.projection * ubo.camera * Pos; +} \ No newline at end of file