From 4545b59325a39faa28e2f9fe675bd66db96be544 Mon Sep 17 00:00:00 2001 From: Mateusz Antkiewicz Date: Sat, 17 Jun 2023 19:28:12 +0200 Subject: [PATCH 1/3] SSAO, Dynamic rendering, fixes --- EruptionEngine.ini | 3 +- EruptionEngine.vcxproj | 23 +- EruptionEngine.vcxproj.filters | 33 +- External/ImGui/imgui_impl_vulkan.cpp | 20 +- External/ImGui/imgui_impl_vulkan.h | 2 +- External/{TinyGLTF => JSON}/json.hpp | 0 External/TinyGLTF/tiny_gltf.cpp | 2 - External/TinyGLTF/tiny_gltf.h | 8251 ----------------- External/VMA/VMA.cpp | 1 + Shaders/ForwardFrag.frag | 40 +- Shaders/ForwardFrag.spv | Bin 37920 -> 39248 bytes Shaders/SSAO.frag | 93 + Shaders/SSAO.spv | Bin 0 -> 12036 bytes Shaders/compile.bat | 1 + Source/Assets/AssetManager.cpp | 9 +- Source/Assets/MeshImporter/GLTFImporter.cpp | 8 +- Source/Common/Helpers.cpp | 6 +- Source/Editor/EditorLayer.cpp | 3 +- Source/Editor/UIPanels/SettingsPanel.cpp | 26 +- Source/Input/InputManager.cpp | 2 +- Source/Renderer/Context.cpp | 58 +- Source/Renderer/Context.hpp | 1 + Source/Renderer/DescriptorSet.cpp | 4 +- Source/Renderer/Framebuffer.cpp | 34 - Source/Renderer/Framebuffer.hpp | 32 - Source/Renderer/ImGuiContext.cpp | 147 + Source/Renderer/ImGuiContext.hpp | 33 + Source/Renderer/Image.cpp | 14 +- Source/Renderer/Image.hpp | 3 + .../ComputePass.cpp} | 12 +- .../ComputePass.hpp} | 6 +- .../GraphicsPass.cpp} | 87 +- .../GraphicsPass.hpp} | 37 +- .../Pipeline.cpp => Passes/Pass.cpp} | 22 +- .../Pipeline.hpp => Passes/Pass.hpp} | 4 +- Source/Renderer/RenderPass.cpp | 114 - Source/Renderer/RenderPass.hpp | 51 - Source/Renderer/Renderer.cpp | 1023 +- Source/Renderer/Renderer.hpp | 98 +- Source/Renderer/Swapchain.cpp | 2 +- Source/Renderer/Swapchain.hpp | 5 +- Source/Renderer/Window.hpp | 4 - Source/Scene/Scene.cpp | 37 +- Source/Scene/Scene.hpp | 14 +- imgui.ini | 8 +- 45 files changed, 1034 insertions(+), 9339 deletions(-) rename External/{TinyGLTF => JSON}/json.hpp (100%) delete mode 100644 External/TinyGLTF/tiny_gltf.cpp delete mode 100644 External/TinyGLTF/tiny_gltf.h create mode 100644 Shaders/SSAO.frag create mode 100644 Shaders/SSAO.spv delete mode 100644 Source/Renderer/Framebuffer.cpp delete mode 100644 Source/Renderer/Framebuffer.hpp create mode 100644 Source/Renderer/ImGuiContext.cpp create mode 100644 Source/Renderer/ImGuiContext.hpp rename Source/Renderer/{Pipelines/ComputePipeline.cpp => Passes/ComputePass.cpp} (75%) rename Source/Renderer/{Pipelines/ComputePipeline.hpp => Passes/ComputePass.hpp} (76%) rename Source/Renderer/{Pipelines/GraphicsPipeline.cpp => Passes/GraphicsPass.cpp} (68%) rename Source/Renderer/{Pipelines/GraphicsPipeline.hpp => Passes/GraphicsPass.hpp} (59%) rename Source/Renderer/{Pipelines/Pipeline.cpp => Passes/Pass.cpp} (67%) rename Source/Renderer/{Pipelines/Pipeline.hpp => Passes/Pass.hpp} (97%) delete mode 100644 Source/Renderer/RenderPass.cpp delete mode 100644 Source/Renderer/RenderPass.hpp diff --git a/EruptionEngine.ini b/EruptionEngine.ini index c50fd43..b1d59cf 100644 --- a/EruptionEngine.ini +++ b/EruptionEngine.ini @@ -15,7 +15,8 @@ #define SHADOW_CASCADES 3 #define SOFT_SHADOWS 1 -#define BLUR_SSAO 0 +#define SOFT_SSAO 1 +#define SSAO_SAMPLES 64 #define MAX_POINT_LIGHT_SHADOWS 4 #define MAX_SPOT_LIGHT_SHADOWS 4 diff --git a/EruptionEngine.vcxproj b/EruptionEngine.vcxproj index 90e0bbc..5d776e8 100644 --- a/EruptionEngine.vcxproj +++ b/EruptionEngine.vcxproj @@ -137,7 +137,7 @@ true stdcpp20 stdc17 - $(VULKAN_SDK)\Include;$(SolutionDir)External\GLFW\include;$(SolutionDir)External\GLM\include;$(SolutionDir)External\STB;$(SolutionDir)Source;$(SolutionDir)External\ImGui;$(SolutionDir)External\CL;$(SolutionDir)External\PFD;$(SolutionDir)External\VMA;$(SolutionDir);$(SolutionDir)External\TinyGLTF;%(AdditionalIncludeDirectories) + $(VULKAN_SDK)\Include;$(SolutionDir)External\GLFW\include;$(SolutionDir)External\GLM\include;$(SolutionDir)External\STB;$(SolutionDir)Source;$(SolutionDir)External\ImGui;$(SolutionDir)External\CL;$(SolutionDir)External\PFD;$(SolutionDir)External\VMA;$(SolutionDir);$(SolutionDir)External\JSON;%(AdditionalIncludeDirectories) true Speed NotUsing @@ -169,7 +169,7 @@ true stdcpp20 stdc17 - $(VULKAN_SDK)\Include;$(SolutionDir)External\GLFW\include;$(SolutionDir)External\GLM\include;$(SolutionDir)External\STB;$(SolutionDir)Source;$(SolutionDir)External\ImGui;$(SolutionDir)External\CL;$(SolutionDir)External\PFD;$(SolutionDir)External\VMA;$(SolutionDir);$(SolutionDir)External\TinyGLTF;%(AdditionalIncludeDirectories) + $(VULKAN_SDK)\Include;$(SolutionDir)External\GLFW\include;$(SolutionDir)External\GLM\include;$(SolutionDir)External\STB;$(SolutionDir)Source;$(SolutionDir)External\ImGui;$(SolutionDir)External\CL;$(SolutionDir)External\PFD;$(SolutionDir)External\VMA;$(SolutionDir);$(SolutionDir)External\JSON;%(AdditionalIncludeDirectories) true Speed NotUsing @@ -194,11 +194,10 @@ - + - @@ -244,7 +243,6 @@ - @@ -254,14 +252,14 @@ - + - + - + @@ -269,10 +267,10 @@ + - @@ -293,7 +291,6 @@ - @@ -305,18 +302,18 @@ - + - + - + diff --git a/EruptionEngine.vcxproj.filters b/EruptionEngine.vcxproj.filters index 8d11395..60b6252 100644 --- a/EruptionEngine.vcxproj.filters +++ b/EruptionEngine.vcxproj.filters @@ -66,13 +66,13 @@ Source Files - + Source Files - + Source Files - + Source Files @@ -111,9 +111,6 @@ Source Files - - Source Files - Source Files @@ -138,21 +135,18 @@ Source Files - - Source Files - Source Files - - Source Files - Source Files Source Files + + Source Files + @@ -227,13 +221,13 @@ Header Files - + Header Files - + Header Files - + Header Files @@ -275,9 +269,6 @@ Header Files - - Header Files - Header Files @@ -305,9 +296,6 @@ Header Files - - Header Files - Header Files @@ -317,6 +305,9 @@ Header Files + + Header Files + diff --git a/External/ImGui/imgui_impl_vulkan.cpp b/External/ImGui/imgui_impl_vulkan.cpp index cd56c15..2c43305 100644 --- a/External/ImGui/imgui_impl_vulkan.cpp +++ b/External/ImGui/imgui_impl_vulkan.cpp @@ -113,7 +113,7 @@ struct ImGui_ImplVulkan_Data VkPipelineCreateFlags PipelineCreateFlags; VkDescriptorSetLayout DescriptorSetLayout; VkPipelineLayout PipelineLayout; - VkPipeline GraphicsPipeline; + VkPipeline GraphicsPass; uint32_t Subpass; VkShaderModule ShaderModuleVert; VkShaderModule ShaderModuleFrag; @@ -460,7 +460,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; if (pipeline == VK_NULL_HANDLE) - pipeline = bd->GraphicsPipeline; + pipeline = bd->GraphicsPass; // Allocate array to store enough vertex/index buffers. Each unique viewport gets its own storage. ImGui_ImplVulkan_ViewportData* viewport_renderer_data = (ImGui_ImplVulkan_ViewportData*)draw_data->OwnerViewport->RendererUserData; @@ -986,7 +986,7 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() check_vk_result(err); } - ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, bd->RenderPass, v->MSAASamples, &bd->GraphicsPipeline, bd->Subpass); + ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, bd->RenderPass, v->MSAASamples, &bd->GraphicsPass, bd->Subpass); return true; } @@ -1022,7 +1022,7 @@ void ImGui_ImplVulkan_DestroyDeviceObjects() if (bd->FontSampler) { vkDestroySampler(v->Device, bd->FontSampler, v->Allocator); bd->FontSampler = VK_NULL_HANDLE; } if (bd->DescriptorSetLayout) { vkDestroyDescriptorSetLayout(v->Device, bd->DescriptorSetLayout, v->Allocator); bd->DescriptorSetLayout = VK_NULL_HANDLE; } if (bd->PipelineLayout) { vkDestroyPipelineLayout(v->Device, bd->PipelineLayout, v->Allocator); bd->PipelineLayout = VK_NULL_HANDLE; } - if (bd->GraphicsPipeline) { vkDestroyPipeline(v->Device, bd->GraphicsPipeline, v->Allocator); bd->GraphicsPipeline = VK_NULL_HANDLE; } + if (bd->GraphicsPass) { vkDestroyPipeline(v->Device, bd->GraphicsPass, v->Allocator); bd->GraphicsPass = VK_NULL_HANDLE; } } bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data) @@ -1333,8 +1333,8 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V wd->ImageCount = 0; if (wd->RenderPass) vkDestroyRenderPass(device, wd->RenderPass, allocator); - if (wd->GraphicsPipeline) - vkDestroyPipeline(device, wd->GraphicsPipeline, allocator); + if (wd->GraphicsPass) + vkDestroyPipeline(device, wd->GraphicsPass, allocator); // If min image count was not specified, request different count of images dependent on selected present mode if (min_image_count == 0) @@ -1433,7 +1433,7 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V // We do not create a pipeline by default as this is also used by examples' main.cpp, // but secondary viewport in multi-viewport mode may want to create one with: - //ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->GraphicsPipeline, bd->Subpass); + //ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->GraphicsPass, bd->Subpass); } // Create The Image Views @@ -1484,7 +1484,7 @@ void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevic IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); (void)instance; ImGui_ImplVulkanH_CreateWindowSwapChain(physical_device, device, wd, allocator, width, height, min_image_count); - //ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->GraphicsPipeline, g_VulkanInitInfo.Subpass); + //ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->GraphicsPass, g_VulkanInitInfo.Subpass); ImGui_ImplVulkanH_CreateWindowCommandBuffers(physical_device, device, wd, queue_family, allocator); } @@ -1502,7 +1502,7 @@ void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui IM_FREE(wd->FrameSemaphores); wd->Frames = NULL; wd->FrameSemaphores = NULL; - vkDestroyPipeline(device, wd->GraphicsPipeline, allocator); + vkDestroyPipeline(device, wd->GraphicsPass, allocator); vkDestroyRenderPass(device, wd->RenderPass, allocator); vkDestroySwapchainKHR(device, wd->Swapchain, allocator); vkDestroySurfaceKHR(instance, wd->Surface, allocator); @@ -1677,7 +1677,7 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) } } - ImGui_ImplVulkan_RenderDrawData(viewport->DrawData, fd->CommandBuffer, wd->GraphicsPipeline); + ImGui_ImplVulkan_RenderDrawData(viewport->DrawData, fd->CommandBuffer, wd->GraphicsPass); { vkCmdEndRenderPass(fd->CommandBuffer); diff --git a/External/ImGui/imgui_impl_vulkan.h b/External/ImGui/imgui_impl_vulkan.h index d330e60..14e2be0 100644 --- a/External/ImGui/imgui_impl_vulkan.h +++ b/External/ImGui/imgui_impl_vulkan.h @@ -137,7 +137,7 @@ struct ImGui_ImplVulkanH_Window VkSurfaceFormatKHR SurfaceFormat; VkPresentModeKHR PresentMode; VkRenderPass RenderPass; - VkPipeline GraphicsPipeline; // The window pipeline may uses a different VkRenderPass than the one passed in ImGui_ImplVulkan_InitInfo + VkPipeline GraphicsPass; // The window pipeline may uses a different VkRenderPass than the one passed in ImGui_ImplVulkan_InitInfo bool ClearEnable; VkClearValue ClearValue; uint32_t FrameIndex; // Current frame being rendered to (0 <= FrameIndex < FrameInFlightCount) diff --git a/External/TinyGLTF/json.hpp b/External/JSON/json.hpp similarity index 100% rename from External/TinyGLTF/json.hpp rename to External/JSON/json.hpp diff --git a/External/TinyGLTF/tiny_gltf.cpp b/External/TinyGLTF/tiny_gltf.cpp deleted file mode 100644 index 59710bf..0000000 --- a/External/TinyGLTF/tiny_gltf.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#define TINYGLTF_IMPLEMENTATION -#include "tiny_gltf.h" \ No newline at end of file diff --git a/External/TinyGLTF/tiny_gltf.h b/External/TinyGLTF/tiny_gltf.h deleted file mode 100644 index 61d0001..0000000 --- a/External/TinyGLTF/tiny_gltf.h +++ /dev/null @@ -1,8251 +0,0 @@ -// -// Header-only tiny glTF 2.0 loader and serializer. -// -// -// The MIT License (MIT) -// -// Copyright (c) 2015 - Present Syoyo Fujita, AurĂ©lien Chatelain and many -// contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -// Version: -// - v2.8.1 Missed serialization texture sampler name fixed. PR#399. -// - v2.8.0 Add URICallbacks for custom URI handling in Buffer and Image. PR#397. -// - v2.7.0 Change WriteImageDataFunction user callback function signature. PR#393. -// - v2.6.3 Fix GLB file with empty BIN chunk was not handled. PR#382 and PR#383. -// - v2.6.2 Fix out-of-bounds access of accessors. PR#379. -// - v2.6.1 Better GLB validation check when loading. -// - v2.6.0 Support serializing sparse accessor(Thanks to @fynv). -// Disable expanding file path for security(no use of awkward `wordexp` anymore). -// - v2.5.0 Add SetPreserveImageChannels() option to load image data as is. -// - v2.4.3 Fix null object output when material has all default -// parameters. -// - v2.4.2 Decode percent-encoded URI. -// - v2.4.1 Fix some glTF object class does not have `extensions` and/or -// `extras` property. -// - v2.4.0 Experimental RapidJSON and C++14 support(Thanks to @jrkoone). -// - v2.3.1 Set default value of minFilter and magFilter in Sampler to -1. -// - v2.3.0 Modified Material representation according to glTF 2.0 schema -// (and introduced TextureInfo class) -// Change the behavior of `Value::IsNumber`. It return true either the -// value is int or real. -// - v2.2.0 Add loading 16bit PNG support. Add Sparse accessor support(Thanks -// to @Ybalrid) -// - v2.1.0 Add draco compression. -// - v2.0.1 Add comparison feature(Thanks to @Selmar). -// - v2.0.0 glTF 2.0!. -// -// Tiny glTF loader is using following third party libraries: -// -// - jsonhpp: C++ JSON library. -// - base64: base64 decode/encode library. -// - stb_image: Image loading library. -// -#ifndef TINY_GLTF_H_ -#define TINY_GLTF_H_ - -#define TINYGLTF_NO_STB_IMAGE_WRITE - -#include -#include -#include // std::fabs -#include -#include -#include -#include -#include -#include -#include - -//Auto-detect C++14 standard version -#if !defined(TINYGLTF_USE_CPP14) && defined(__cplusplus) && (__cplusplus >= 201402L) -#define TINYGLTF_USE_CPP14 -#endif - -#ifndef TINYGLTF_USE_CPP14 -#include -#endif - -#ifdef __ANDROID__ -#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS -#include -#endif -#endif - -#ifdef __GNUC__ -#if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 8)) -#define TINYGLTF_NOEXCEPT -#else -#define TINYGLTF_NOEXCEPT noexcept -#endif -#else -#define TINYGLTF_NOEXCEPT noexcept -#endif - -#define DEFAULT_METHODS(x) \ - ~x() = default; \ - x(const x &) = default; \ - x(x &&) TINYGLTF_NOEXCEPT = default; \ - x &operator=(const x &) = default; \ - x &operator=(x &&) TINYGLTF_NOEXCEPT = default; - -namespace tinygltf { - -#define TINYGLTF_MODE_POINTS (0) -#define TINYGLTF_MODE_LINE (1) -#define TINYGLTF_MODE_LINE_LOOP (2) -#define TINYGLTF_MODE_LINE_STRIP (3) -#define TINYGLTF_MODE_TRIANGLES (4) -#define TINYGLTF_MODE_TRIANGLE_STRIP (5) -#define TINYGLTF_MODE_TRIANGLE_FAN (6) - -#define TINYGLTF_COMPONENT_TYPE_BYTE (5120) -#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121) -#define TINYGLTF_COMPONENT_TYPE_SHORT (5122) -#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123) -#define TINYGLTF_COMPONENT_TYPE_INT (5124) -#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125) -#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126) -#define TINYGLTF_COMPONENT_TYPE_DOUBLE \ - (5130) // OpenGL double type. Note that some of glTF 2.0 validator does not - // support double type even the schema seems allow any value of - // integer: - // https://github.com/KhronosGroup/glTF/blob/b9884a2fd45130b4d673dd6c8a706ee21ee5c5f7/specification/2.0/schema/accessor.schema.json#L22 - -#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728) -#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729) -#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984) -#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985) -#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986) -#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987) - -#define TINYGLTF_TEXTURE_WRAP_REPEAT (10497) -#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071) -#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648) - -// Redeclarations of the above for technique.parameters. -#define TINYGLTF_PARAMETER_TYPE_BYTE (5120) -#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121) -#define TINYGLTF_PARAMETER_TYPE_SHORT (5122) -#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123) -#define TINYGLTF_PARAMETER_TYPE_INT (5124) -#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125) -#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126) - -#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664) -#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665) -#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666) - -#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667) -#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668) -#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669) - -#define TINYGLTF_PARAMETER_TYPE_BOOL (35670) -#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671) -#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672) -#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673) - -#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674) -#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675) -#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676) - -#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678) - -// End parameter types - -#define TINYGLTF_TYPE_VEC2 (2) -#define TINYGLTF_TYPE_VEC3 (3) -#define TINYGLTF_TYPE_VEC4 (4) -#define TINYGLTF_TYPE_MAT2 (32 + 2) -#define TINYGLTF_TYPE_MAT3 (32 + 3) -#define TINYGLTF_TYPE_MAT4 (32 + 4) -#define TINYGLTF_TYPE_SCALAR (64 + 1) -#define TINYGLTF_TYPE_VECTOR (64 + 4) -#define TINYGLTF_TYPE_MATRIX (64 + 16) - -#define TINYGLTF_IMAGE_FORMAT_JPEG (0) -#define TINYGLTF_IMAGE_FORMAT_PNG (1) -#define TINYGLTF_IMAGE_FORMAT_BMP (2) -#define TINYGLTF_IMAGE_FORMAT_GIF (3) - -#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406) -#define TINYGLTF_TEXTURE_FORMAT_RGB (6407) -#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408) -#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409) -#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410) - -#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553) -#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121) - -#define TINYGLTF_TARGET_ARRAY_BUFFER (34962) -#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963) - -#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633) -#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632) - -#define TINYGLTF_DOUBLE_EPS (1.e-12) -#define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS) - -#ifdef __ANDROID__ -#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS -#ifdef TINYGLTF_IMPLEMENTATION -AAssetManager *asset_manager = nullptr; -#else -extern AAssetManager *asset_manager; -#endif -#endif -#endif - -typedef enum { - NULL_TYPE, - REAL_TYPE, - INT_TYPE, - BOOL_TYPE, - STRING_TYPE, - ARRAY_TYPE, - BINARY_TYPE, - OBJECT_TYPE -} Type; - -static inline int32_t GetComponentSizeInBytes(uint32_t componentType) { - if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) { - return 1; - } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) { - return 1; - } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) { - return 2; - } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) { - return 2; - } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) { - return 4; - } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) { - return 4; - } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) { - return 4; - } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) { - return 8; - } else { - // Unknown component type - return -1; - } -} - -static inline int32_t GetNumComponentsInType(uint32_t ty) { - if (ty == TINYGLTF_TYPE_SCALAR) { - return 1; - } else if (ty == TINYGLTF_TYPE_VEC2) { - return 2; - } else if (ty == TINYGLTF_TYPE_VEC3) { - return 3; - } else if (ty == TINYGLTF_TYPE_VEC4) { - return 4; - } else if (ty == TINYGLTF_TYPE_MAT2) { - return 4; - } else if (ty == TINYGLTF_TYPE_MAT3) { - return 9; - } else if (ty == TINYGLTF_TYPE_MAT4) { - return 16; - } else { - // Unknown component type - return -1; - } -} - -// TODO(syoyo): Move these functions to TinyGLTF class -bool IsDataURI(const std::string &in); -bool DecodeDataURI(std::vector *out, std::string &mime_type, - const std::string &in, size_t reqBytes, bool checkSize); - -#ifdef __clang__ -#pragma clang diagnostic push -// Suppress warning for : static Value null_value -#pragma clang diagnostic ignored "-Wexit-time-destructors" -#pragma clang diagnostic ignored "-Wpadded" -#endif - -// Simple class to represent JSON object -class Value { - public: - typedef std::vector Array; - typedef std::map Object; - - Value() - : type_(NULL_TYPE), - int_value_(0), - real_value_(0.0), - boolean_value_(false) {} - - explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; } - explicit Value(int i) : type_(INT_TYPE) { - int_value_ = i; - real_value_ = i; - } - explicit Value(double n) : type_(REAL_TYPE) { real_value_ = n; } - explicit Value(const std::string &s) : type_(STRING_TYPE) { - string_value_ = s; - } - explicit Value(std::string &&s) - : type_(STRING_TYPE), string_value_(std::move(s)) {} - explicit Value(const char *s) : type_(STRING_TYPE) { - string_value_ = s; - } - explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) { - binary_value_.resize(n); - memcpy(binary_value_.data(), p, n); - } - explicit Value(std::vector &&v) noexcept - : type_(BINARY_TYPE), - binary_value_(std::move(v)) {} - explicit Value(const Array &a) : type_(ARRAY_TYPE) { array_value_ = a; } - explicit Value(Array &&a) noexcept : type_(ARRAY_TYPE), - array_value_(std::move(a)) {} - - explicit Value(const Object &o) : type_(OBJECT_TYPE) { object_value_ = o; } - explicit Value(Object &&o) noexcept : type_(OBJECT_TYPE), - object_value_(std::move(o)) {} - - DEFAULT_METHODS(Value) - - char Type() const { return static_cast(type_); } - - bool IsBool() const { return (type_ == BOOL_TYPE); } - - bool IsInt() const { return (type_ == INT_TYPE); } - - bool IsNumber() const { return (type_ == REAL_TYPE) || (type_ == INT_TYPE); } - - bool IsReal() const { return (type_ == REAL_TYPE); } - - bool IsString() const { return (type_ == STRING_TYPE); } - - bool IsBinary() const { return (type_ == BINARY_TYPE); } - - bool IsArray() const { return (type_ == ARRAY_TYPE); } - - bool IsObject() const { return (type_ == OBJECT_TYPE); } - - // Use this function if you want to have number value as double. - double GetNumberAsDouble() const { - if (type_ == INT_TYPE) { - return double(int_value_); - } else { - return real_value_; - } - } - - // Use this function if you want to have number value as int. - // TODO(syoyo): Support int value larger than 32 bits - int GetNumberAsInt() const { - if (type_ == REAL_TYPE) { - return int(real_value_); - } else { - return int_value_; - } - } - - // Accessor - template - const T &Get() const; - template - T &Get(); - - // Lookup value from an array - const Value &Get(int idx) const { - static Value null_value; - assert(IsArray()); - assert(idx >= 0); - return (static_cast(idx) < array_value_.size()) - ? array_value_[static_cast(idx)] - : null_value; - } - - // Lookup value from a key-value pair - const Value &Get(const std::string &key) const { - static Value null_value; - assert(IsObject()); - Object::const_iterator it = object_value_.find(key); - return (it != object_value_.end()) ? it->second : null_value; - } - - size_t ArrayLen() const { - if (!IsArray()) return 0; - return array_value_.size(); - } - - // Valid only for object type. - bool Has(const std::string &key) const { - if (!IsObject()) return false; - Object::const_iterator it = object_value_.find(key); - return (it != object_value_.end()) ? true : false; - } - - // List keys - std::vector Keys() const { - std::vector keys; - if (!IsObject()) return keys; // empty - - for (Object::const_iterator it = object_value_.begin(); - it != object_value_.end(); ++it) { - keys.push_back(it->first); - } - - return keys; - } - - size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); } - - bool operator==(const tinygltf::Value &other) const; - - protected: - int type_ = NULL_TYPE; - - int int_value_ = 0; - double real_value_ = 0.0; - std::string string_value_; - std::vector binary_value_; - Array array_value_; - Object object_value_; - bool boolean_value_ = false; -}; - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#define TINYGLTF_VALUE_GET(ctype, var) \ - template <> \ - inline const ctype &Value::Get() const { \ - return var; \ - } \ - template <> \ - inline ctype &Value::Get() { \ - return var; \ - } -TINYGLTF_VALUE_GET(bool, boolean_value_) -TINYGLTF_VALUE_GET(double, real_value_) -TINYGLTF_VALUE_GET(int, int_value_) -TINYGLTF_VALUE_GET(std::string, string_value_) -TINYGLTF_VALUE_GET(std::vector, binary_value_) -TINYGLTF_VALUE_GET(Value::Array, array_value_) -TINYGLTF_VALUE_GET(Value::Object, object_value_) -#undef TINYGLTF_VALUE_GET - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wc++98-compat" -#pragma clang diagnostic ignored "-Wpadded" -#endif - -/// Aggregate object for representing a color -using ColorValue = std::array; - -// === legacy interface ==== -// TODO(syoyo): Deprecate `Parameter` class. -struct Parameter { - bool bool_value = false; - bool has_number_value = false; - std::string string_value; - std::vector number_array; - std::map json_double_value; - double number_value = 0.0; - - // context sensitive methods. depending the type of the Parameter you are - // accessing, these are either valid or not - // If this parameter represent a texture map in a material, will return the - // texture index - - /// Return the index of a texture if this Parameter is a texture map. - /// Returned value is only valid if the parameter represent a texture from a - /// material - int TextureIndex() const { - const auto it = json_double_value.find("index"); - if (it != std::end(json_double_value)) { - return int(it->second); - } - return -1; - } - - /// Return the index of a texture coordinate set if this Parameter is a - /// texture map. Returned value is only valid if the parameter represent a - /// texture from a material - int TextureTexCoord() const { - const auto it = json_double_value.find("texCoord"); - if (it != std::end(json_double_value)) { - return int(it->second); - } - // As per the spec, if texCoord is omitted, this parameter is 0 - return 0; - } - - /// Return the scale of a texture if this Parameter is a normal texture map. - /// Returned value is only valid if the parameter represent a normal texture - /// from a material - double TextureScale() const { - const auto it = json_double_value.find("scale"); - if (it != std::end(json_double_value)) { - return it->second; - } - // As per the spec, if scale is omitted, this parameter is 1 - return 1; - } - - /// Return the strength of a texture if this Parameter is a an occlusion map. - /// Returned value is only valid if the parameter represent an occlusion map - /// from a material - double TextureStrength() const { - const auto it = json_double_value.find("strength"); - if (it != std::end(json_double_value)) { - return it->second; - } - // As per the spec, if strength is omitted, this parameter is 1 - return 1; - } - - /// Material factor, like the roughness or metalness of a material - /// Returned value is only valid if the parameter represent a texture from a - /// material - double Factor() const { return number_value; } - - /// Return the color of a material - /// Returned value is only valid if the parameter represent a texture from a - /// material - ColorValue ColorFactor() const { - return { - {// this aggregate initialize the std::array object, and uses C++11 RVO. - number_array[0], number_array[1], number_array[2], - (number_array.size() > 3 ? number_array[3] : 1.0)}}; - } - - Parameter() = default; - DEFAULT_METHODS(Parameter) - bool operator==(const Parameter &) const; -}; - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif - -typedef std::map ParameterMap; -typedef std::map ExtensionMap; - -struct AnimationChannel { - int sampler; // required - int target_node; // optional index of the node to target (alternative - // target should be provided by extension) - std::string target_path; // required with standard values of ["translation", - // "rotation", "scale", "weights"] - Value extras; - ExtensionMap extensions; - ExtensionMap target_extensions; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; - std::string target_extensions_json_string; - - AnimationChannel() : sampler(-1), target_node(-1) {} - DEFAULT_METHODS(AnimationChannel) - bool operator==(const AnimationChannel &) const; -}; - -struct AnimationSampler { - int input; // required - int output; // required - std::string interpolation; // "LINEAR", "STEP","CUBICSPLINE" or user defined - // string. default "LINEAR" - Value extras; - ExtensionMap extensions; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; - - AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {} - DEFAULT_METHODS(AnimationSampler) - bool operator==(const AnimationSampler &) const; -}; - -struct Animation { - std::string name; - std::vector channels; - std::vector samplers; - Value extras; - ExtensionMap extensions; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; - - Animation() = default; - DEFAULT_METHODS(Animation) - bool operator==(const Animation &) const; -}; - -struct Skin { - std::string name; - int inverseBindMatrices; // required here but not in the spec - int skeleton; // The index of the node used as a skeleton root - std::vector joints; // Indices of skeleton nodes - - Value extras; - ExtensionMap extensions; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; - - Skin() { - inverseBindMatrices = -1; - skeleton = -1; - } - DEFAULT_METHODS(Skin) - bool operator==(const Skin &) const; -}; - -struct Sampler { - std::string name; - // glTF 2.0 spec does not define default value for `minFilter` and - // `magFilter`. Set -1 in TinyGLTF(issue #186) - int minFilter = - -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR", - // "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST", - // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"] - int magFilter = - -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR"] - int wrapS = - TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", - // "REPEAT"], default "REPEAT" - int wrapT = - TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", - // "REPEAT"], default "REPEAT" - // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently - // not used. - - Value extras; - ExtensionMap extensions; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; - - Sampler() - : minFilter(-1), - magFilter(-1), - wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT), - wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) {} - DEFAULT_METHODS(Sampler) - bool operator==(const Sampler &) const; -}; - -struct Image { - std::string name; - int width; - int height; - int component; - int bits; // bit depth per channel. 8(byte), 16 or 32. - int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually - // UBYTE(bits = 8) or USHORT(bits = 16) - std::vector image; - int bufferView; // (required if no uri) - std::string mimeType; // (required if no uri) ["image/jpeg", "image/png", - // "image/bmp", "image/gif"] - std::string uri; // (required if no mimeType) uri is not decoded(e.g. - // whitespace may be represented as %20) - Value extras; - ExtensionMap extensions; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; - - // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg - // compressed for "image/jpeg" mime) This feature is good if you use custom - // image loader function. (e.g. delayed decoding of images for faster glTF - // parsing) Default parser for Image does not provide as-is loading feature at - // the moment. (You can manipulate this by providing your own LoadImageData - // function) - bool as_is; - - Image() : as_is(false) { - bufferView = -1; - width = -1; - height = -1; - component = -1; - bits = -1; - pixel_type = -1; - } - DEFAULT_METHODS(Image) - - bool operator==(const Image &) const; -}; - -struct Texture { - std::string name; - - int sampler; - int source; - Value extras; - ExtensionMap extensions; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; - - Texture() : sampler(-1), source(-1) {} - DEFAULT_METHODS(Texture) - - bool operator==(const Texture &) const; -}; - -struct TextureInfo { - int index = -1; // required. - int texCoord; // The set index of texture's TEXCOORD attribute used for - // texture coordinate mapping. - - Value extras; - ExtensionMap extensions; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; - - TextureInfo() : index(-1), texCoord(0) {} - DEFAULT_METHODS(TextureInfo) - bool operator==(const TextureInfo &) const; -}; - -struct NormalTextureInfo { - int index = -1; // required - int texCoord; // The set index of texture's TEXCOORD attribute used for - // texture coordinate mapping. - double scale; // scaledNormal = normalize(( - // * 2.0 - 1.0) * vec3(, , 1.0)) - - Value extras; - ExtensionMap extensions; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; - - NormalTextureInfo() : index(-1), texCoord(0), scale(1.0) {} - DEFAULT_METHODS(NormalTextureInfo) - bool operator==(const NormalTextureInfo &) const; -}; - -struct OcclusionTextureInfo { - int index = -1; // required - int texCoord; // The set index of texture's TEXCOORD attribute used for - // texture coordinate mapping. - double strength; // occludedColor = lerp(color, color * , ) - - Value extras; - ExtensionMap extensions; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; - - OcclusionTextureInfo() : index(-1), texCoord(0), strength(1.0) {} - DEFAULT_METHODS(OcclusionTextureInfo) - bool operator==(const OcclusionTextureInfo &) const; -}; - -// pbrMetallicRoughness class defined in glTF 2.0 spec. -struct PbrMetallicRoughness { - std::vector baseColorFactor; // len = 4. default [1,1,1,1] - TextureInfo baseColorTexture; - double metallicFactor; // default 1 - double roughnessFactor; // default 1 - TextureInfo metallicRoughnessTexture; - - Value extras; - ExtensionMap extensions; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; - - PbrMetallicRoughness() - : baseColorFactor(std::vector{1.0, 1.0, 1.0, 1.0}), - metallicFactor(1.0), - roughnessFactor(1.0) {} - DEFAULT_METHODS(PbrMetallicRoughness) - bool operator==(const PbrMetallicRoughness &) const; -}; - -// Each extension should be stored in a ParameterMap. -// members not in the values could be included in the ParameterMap -// to keep a single material model -struct Material { - std::string name; - - std::vector emissiveFactor; // length 3. default [0, 0, 0] - std::string alphaMode; // default "OPAQUE" - double alphaCutoff; // default 0.5 - bool doubleSided; // default false; - - PbrMetallicRoughness pbrMetallicRoughness; - - NormalTextureInfo normalTexture; - OcclusionTextureInfo occlusionTexture; - TextureInfo emissiveTexture; - - // For backward compatibility - // TODO(syoyo): Remove `values` and `additionalValues` in the next release. - ParameterMap values; - ParameterMap additionalValues; - - ExtensionMap extensions; - Value extras; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; - - Material() : alphaMode("OPAQUE"), alphaCutoff(0.5), doubleSided(false) {} - DEFAULT_METHODS(Material) - - bool operator==(const Material &) const; -}; - -struct BufferView { - std::string name; - int buffer{-1}; // Required - size_t byteOffset{0}; // minimum 0, default 0 - size_t byteLength{0}; // required, minimum 1. 0 = invalid - size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 = - // understood to be tightly packed - int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices - // or attribs. Could be 0 for other data - Value extras; - ExtensionMap extensions; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; - - bool dracoDecoded{false}; // Flag indicating this has been draco decoded - - BufferView() - : buffer(-1), - byteOffset(0), - byteLength(0), - byteStride(0), - target(0), - dracoDecoded(false) {} - DEFAULT_METHODS(BufferView) - bool operator==(const BufferView &) const; -}; - -struct Accessor { - int bufferView; // optional in spec but required here since sparse accessor - // are not supported - std::string name; - size_t byteOffset; - bool normalized; // optional. - int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_*** - size_t count; // required - int type; // (required) One of TINYGLTF_TYPE_*** .. - Value extras; - ExtensionMap extensions; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; - - std::vector - minValues; // optional. integer value is promoted to double - std::vector - maxValues; // optional. integer value is promoted to double - - struct { - int count; - bool isSparse; - struct { - int byteOffset; - int bufferView; - int componentType; // a TINYGLTF_COMPONENT_TYPE_ value - } indices; - struct { - int bufferView; - int byteOffset; - } values; - } sparse; - - /// - /// Utility function to compute byteStride for a given bufferView object. - /// Returns -1 upon invalid glTF value or parameter configuration. - /// - int ByteStride(const BufferView &bufferViewObject) const { - if (bufferViewObject.byteStride == 0) { - // Assume data is tightly packed. - int componentSizeInBytes = - GetComponentSizeInBytes(static_cast(componentType)); - if (componentSizeInBytes <= 0) { - return -1; - } - - int numComponents = GetNumComponentsInType(static_cast(type)); - if (numComponents <= 0) { - return -1; - } - - return componentSizeInBytes * numComponents; - } else { - // Check if byteStride is a multiple of the size of the accessor's component - // type. - int componentSizeInBytes = - GetComponentSizeInBytes(static_cast(componentType)); - if (componentSizeInBytes <= 0) { - return -1; - } - - if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) { - return -1; - } - return static_cast(bufferViewObject.byteStride); - } - - // unreachable return 0; - } - - Accessor() - : bufferView(-1), - byteOffset(0), - normalized(false), - componentType(-1), - count(0), - type(-1) { - sparse.isSparse = false; - } - DEFAULT_METHODS(Accessor) - bool operator==(const tinygltf::Accessor &) const; -}; - -struct PerspectiveCamera { - double aspectRatio; // min > 0 - double yfov; // required. min > 0 - double zfar; // min > 0 - double znear; // required. min > 0 - - PerspectiveCamera() - : aspectRatio(0.0), - yfov(0.0), - zfar(0.0) // 0 = use infinite projection matrix - , - znear(0.0) {} - DEFAULT_METHODS(PerspectiveCamera) - bool operator==(const PerspectiveCamera &) const; - - ExtensionMap extensions; - Value extras; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; -}; - -struct OrthographicCamera { - double xmag; // required. must not be zero. - double ymag; // required. must not be zero. - double zfar; // required. `zfar` must be greater than `znear`. - double znear; // required - - OrthographicCamera() : xmag(0.0), ymag(0.0), zfar(0.0), znear(0.0) {} - DEFAULT_METHODS(OrthographicCamera) - bool operator==(const OrthographicCamera &) const; - - ExtensionMap extensions; - Value extras; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; -}; - -struct Camera { - std::string type; // required. "perspective" or "orthographic" - std::string name; - - PerspectiveCamera perspective; - OrthographicCamera orthographic; - - Camera() {} - DEFAULT_METHODS(Camera) - bool operator==(const Camera &) const; - - ExtensionMap extensions; - Value extras; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; -}; - -struct Primitive { - std::map attributes; // (required) A dictionary object of - // integer, where each integer - // is the index of the accessor - // containing an attribute. - int material; // The index of the material to apply to this primitive - // when rendering. - int indices; // The index of the accessor that contains the indices. - int mode; // one of TINYGLTF_MODE_*** - std::vector > targets; // array of morph targets, - // where each target is a dict with attributes in ["POSITION, "NORMAL", - // "TANGENT"] pointing - // to their corresponding accessors - ExtensionMap extensions; - Value extras; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; - - Primitive() { - material = -1; - indices = -1; - mode = -1; - } - DEFAULT_METHODS(Primitive) - bool operator==(const Primitive &) const; -}; - -struct Mesh { - std::string name; - std::vector primitives; - std::vector weights; // weights to be applied to the Morph Targets - ExtensionMap extensions; - Value extras; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; - - Mesh() = default; - DEFAULT_METHODS(Mesh) - bool operator==(const Mesh &) const; -}; - -class Node { - public: - Node() : camera(-1), skin(-1), mesh(-1) {} - - DEFAULT_METHODS(Node) - - bool operator==(const Node &) const; - - int camera; // the index of the camera referenced by this node - - std::string name; - int skin; - int mesh; - std::vector children; - std::vector rotation; // length must be 0 or 4 - std::vector scale; // length must be 0 or 3 - std::vector translation; // length must be 0 or 3 - std::vector matrix; // length must be 0 or 16 - std::vector weights; // The weights of the instantiated Morph Target - - ExtensionMap extensions; - Value extras; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; -}; - -struct Buffer { - std::string name; - std::vector data; - std::string - uri; // considered as required here but not in the spec (need to clarify) - // uri is not decoded(e.g. whitespace may be represented as %20) - Value extras; - ExtensionMap extensions; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; - - Buffer() = default; - DEFAULT_METHODS(Buffer) - bool operator==(const Buffer &) const; -}; - -struct Asset { - std::string version = "2.0"; // required - std::string generator; - std::string minVersion; - std::string copyright; - ExtensionMap extensions; - Value extras; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; - - Asset() = default; - DEFAULT_METHODS(Asset) - bool operator==(const Asset &) const; -}; - -struct Scene { - std::string name; - std::vector nodes; - - ExtensionMap extensions; - Value extras; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; - - Scene() = default; - DEFAULT_METHODS(Scene) - bool operator==(const Scene &) const; -}; - -struct SpotLight { - double innerConeAngle; - double outerConeAngle; - - SpotLight() : innerConeAngle(0.0), outerConeAngle(0.7853981634) {} - DEFAULT_METHODS(SpotLight) - bool operator==(const SpotLight &) const; - - ExtensionMap extensions; - Value extras; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; -}; - -struct Light { - std::string name; - std::vector color; - double intensity{1.0}; - std::string type; - double range{0.0}; // 0.0 = infinite - SpotLight spot; - - Light() : intensity(1.0), range(0.0) {} - DEFAULT_METHODS(Light) - - bool operator==(const Light &) const; - - ExtensionMap extensions; - Value extras; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; -}; - -class Model { - public: - Model() = default; - DEFAULT_METHODS(Model) - - bool operator==(const Model &) const; - - std::vector accessors; - std::vector animations; - std::vector buffers; - std::vector bufferViews; - std::vector materials; - std::vector meshes; - std::vector nodes; - std::vector textures; - std::vector images; - std::vector skins; - std::vector samplers; - std::vector cameras; - std::vector scenes; - std::vector lights; - - int defaultScene = -1; - std::vector extensionsUsed; - std::vector extensionsRequired; - - Asset asset; - - Value extras; - ExtensionMap extensions; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; -}; - -enum SectionCheck { - NO_REQUIRE = 0x00, - REQUIRE_VERSION = 0x01, - REQUIRE_SCENE = 0x02, - REQUIRE_SCENES = 0x04, - REQUIRE_NODES = 0x08, - REQUIRE_ACCESSORS = 0x10, - REQUIRE_BUFFERS = 0x20, - REQUIRE_BUFFER_VIEWS = 0x40, - REQUIRE_ALL = 0x7f -}; - -/// -/// URIEncodeFunction type. Signature for custom URI encoding of external -/// resources such as .bin and image files. Used by tinygltf to re-encode the -/// final location of saved files. object_type may be used to encode buffer and -/// image URIs differently, for example. See -/// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#uris -/// -typedef bool (*URIEncodeFunction)(const std::string &in_uri, - const std::string &object_type, - std::string *out_uri, void *user_data); - -/// -/// URIDecodeFunction type. Signature for custom URI decoding of external -/// resources such as .bin and image files. Used by tinygltf when computing -/// filenames to write resources. -/// -typedef bool (*URIDecodeFunction)(const std::string &in_uri, - std::string *out_uri, void *user_data); - -// Declaration of default uri decode function -bool URIDecode(const std::string &in_uri, std::string *out_uri, - void *user_data); - -/// -/// A structure containing URI callbacks and a pointer to their user data. -/// -struct URICallbacks { - URIEncodeFunction encode; // Optional encode method - URIDecodeFunction decode; // Required decode method - - void *user_data; // An argument that is passed to all uri callbacks -}; - -/// -/// LoadImageDataFunction type. Signature for custom image loading callbacks. -/// -typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *, - std::string *, int, int, - const unsigned char *, int, - void *user_pointer); - -/// -/// WriteImageDataFunction type. Signature for custom image writing callbacks. -/// The out_uri parameter becomes the URI written to the gltf and may reference -/// a file or contain a data URI. -/// -typedef bool (*WriteImageDataFunction)(const std::string *basepath, - const std::string *filename, - const Image *image, bool embedImages, - const URICallbacks *uri_cb, - std::string *out_uri, - void *user_pointer); - -#ifndef TINYGLTF_NO_STB_IMAGE -// Declaration of default image loader callback -bool LoadImageData(Image *image, const int image_idx, std::string *err, - std::string *warn, int req_width, int req_height, - const unsigned char *bytes, int size, void *); -#endif - -#ifndef TINYGLTF_NO_STB_IMAGE_WRITE -// Declaration of default image writer callback -bool WriteImageData(const std::string *basepath, const std::string *filename, - const Image *image, bool embedImages, - const URICallbacks *uri_cb, std::string *out_uri, void *); -#endif - -/// -/// FilExistsFunction type. Signature for custom filesystem callbacks. -/// -typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *); - -/// -/// ExpandFilePathFunction type. Signature for custom filesystem callbacks. -/// -typedef std::string (*ExpandFilePathFunction)(const std::string &, void *); - -/// -/// ReadWholeFileFunction type. Signature for custom filesystem callbacks. -/// -typedef bool (*ReadWholeFileFunction)(std::vector *, - std::string *, const std::string &, - void *); - -/// -/// WriteWholeFileFunction type. Signature for custom filesystem callbacks. -/// -typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &, - const std::vector &, - void *); - -/// -/// GetFileSizeFunction type. Signature for custom filesystem callbacks. -/// -typedef bool (*GetFileSizeFunction)(size_t *filesize_out, std::string *err, const std::string &abs_filename, - void *userdata); - -/// -/// A structure containing all required filesystem callbacks and a pointer to -/// their user data. -/// -struct FsCallbacks { - FileExistsFunction FileExists; - ExpandFilePathFunction ExpandFilePath; - ReadWholeFileFunction ReadWholeFile; - WriteWholeFileFunction WriteWholeFile; - GetFileSizeFunction GetFileSizeInBytes; // To avoid GetFileSize Win32 API, add `InBytes` suffix. - - void *user_data; // An argument that is passed to all fs callbacks -}; - -#ifndef TINYGLTF_NO_FS -// Declaration of default filesystem callbacks - -bool FileExists(const std::string &abs_filename, void *); - -/// -/// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to -/// `C:\\Users\\tinygltf\\AppData`) -/// -/// @param[in] filepath File path string. Assume UTF-8 -/// @param[in] userdata User data. Set to `nullptr` if you don't need it. -/// -std::string ExpandFilePath(const std::string &filepath, void *userdata); - -bool ReadWholeFile(std::vector *out, std::string *err, - const std::string &filepath, void *); - -bool WriteWholeFile(std::string *err, const std::string &filepath, - const std::vector &contents, void *); - -bool GetFileSizeInBytes(size_t *filesize_out, std::string *err, const std::string &filepath, - void *); -#endif - -/// -/// glTF Parser/Serializer context. -/// -class TinyGLTF { - public: -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wc++98-compat" -#endif - - TinyGLTF() : bin_data_(nullptr), bin_size_(0), is_binary_(false) {} - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - - ~TinyGLTF() {} - - /// - /// Loads glTF ASCII asset from a file. - /// Set warning message to `warn` for example it fails to load asserts. - /// Returns false and set error string to `err` if there's an error. - /// - bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn, - const std::string &filename, - unsigned int check_sections = REQUIRE_VERSION); - - /// - /// Loads glTF ASCII asset from string(memory). - /// `length` = strlen(str); - /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an - /// expanded path (e.g. no tilde(`~`), no environment variables). Set warning - /// message to `warn` for example it fails to load asserts. Returns false and - /// set error string to `err` if there's an error. - /// - bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn, - const char *str, const unsigned int length, - const std::string &base_dir, - unsigned int check_sections = REQUIRE_VERSION); - - /// - /// Loads glTF binary asset from a file. - /// Set warning message to `warn` for example it fails to load asserts. - /// Returns false and set error string to `err` if there's an error. - /// - bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn, - const std::string &filename, - unsigned int check_sections = REQUIRE_VERSION); - - /// - /// Loads glTF binary asset from memory. - /// `length` = strlen(str); - /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an - /// expanded path (e.g. no tilde(`~`), no environment variables). - /// Set warning message to `warn` for example it fails to load asserts. - /// Returns false and set error string to `err` if there's an error. - /// - bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn, - const unsigned char *bytes, - const unsigned int length, - const std::string &base_dir = "", - unsigned int check_sections = REQUIRE_VERSION); - - /// - /// Write glTF to stream, buffers and images will be embedded - /// - bool WriteGltfSceneToStream(const Model *model, std::ostream &stream, - bool prettyPrint, bool writeBinary); - - /// - /// Write glTF to file. - /// - bool WriteGltfSceneToFile(const Model *model, const std::string &filename, - bool embedImages, bool embedBuffers, - bool prettyPrint, bool writeBinary); - - /// - /// Set callback to use for loading image data - /// - void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data); - - /// - /// Unset(remove) callback of loading image data - /// - void RemoveImageLoader(); - - /// - /// Set callback to use for writing image data - /// - void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data); - - /// - /// Set callbacks to use for URI encoding and decoding and their user data - /// - void SetURICallbacks(URICallbacks callbacks); - - /// - /// Set callbacks to use for filesystem (fs) access and their user data - /// - void SetFsCallbacks(FsCallbacks callbacks); - - /// - /// Set serializing default values(default = false). - /// When true, default values are force serialized to .glTF. - /// This may be helpful if you want to serialize a full description of glTF - /// data. - /// - /// TODO(LTE): Supply parsing option as function arguments to - /// `LoadASCIIFromFile()` and others, not by a class method - /// - void SetSerializeDefaultValues(const bool enabled) { - serialize_default_values_ = enabled; - } - - bool GetSerializeDefaultValues() const { return serialize_default_values_; } - - /// - /// Store original JSON string for `extras` and `extensions`. - /// This feature will be useful when the user want to reconstruct custom data - /// structure from JSON string. - /// - void SetStoreOriginalJSONForExtrasAndExtensions(const bool enabled) { - store_original_json_for_extras_and_extensions_ = enabled; - } - - bool GetStoreOriginalJSONForExtrasAndExtensions() const { - return store_original_json_for_extras_and_extensions_; - } - - /// - /// Specify whether preserve image channels when loading images or not. - /// (Not effective when the user supplies their own LoadImageData callbacks) - /// - void SetPreserveImageChannels(bool onoff) { - preserve_image_channels_ = onoff; - } - - /// - /// Set maximum allowed external file size in bytes. - /// Default: 2GB - /// Only effective for built-in ReadWholeFileFunction FS function. - /// - void SetMaxExternalFileSize(size_t max_bytes) { - max_external_file_size_ = max_bytes; - } - - size_t GetMaxExternalFileSize() const { - return max_external_file_size_; - } - - bool GetPreserveImageChannels() const { return preserve_image_channels_; } - - private: - /// - /// Loads glTF asset from string(memory). - /// `length` = strlen(str); - /// Set warning message to `warn` for example it fails to load asserts - /// Returns false and set error string to `err` if there's an error. - /// - bool LoadFromString(Model *model, std::string *err, std::string *warn, - const char *str, const unsigned int length, - const std::string &base_dir, unsigned int check_sections); - - const unsigned char *bin_data_ = nullptr; - size_t bin_size_ = 0; - bool is_binary_ = false; - - bool serialize_default_values_ = false; ///< Serialize default values? - - bool store_original_json_for_extras_and_extensions_ = false; - - bool preserve_image_channels_ = false; /// Default false(expand channels to - /// RGBA) for backward compatibility. - - size_t max_external_file_size_{size_t((std::numeric_limits::max)())}; // Default 2GB - - // Warning & error messages - std::string warn_; - std::string err_; - - FsCallbacks fs = { -#ifndef TINYGLTF_NO_FS - &tinygltf::FileExists, &tinygltf::ExpandFilePath, - &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile, &tinygltf::GetFileSizeInBytes, - - nullptr // Fs callback user data -#else - nullptr, nullptr, nullptr, nullptr, nullptr, - - nullptr // Fs callback user data -#endif - }; - - URICallbacks uri_cb = { - // Use paths as-is by default. This will use JSON string escaping. - nullptr, - // Decode all URIs before using them as paths as the application may have - // percent encoded them. - &tinygltf::URIDecode, - // URI callback user data - nullptr}; - - LoadImageDataFunction LoadImageData = -#ifndef TINYGLTF_NO_STB_IMAGE - &tinygltf::LoadImageData; -#else - nullptr; -#endif - void *load_image_user_data_{nullptr}; - bool user_image_loader_{false}; - - WriteImageDataFunction WriteImageData = -#ifndef TINYGLTF_NO_STB_IMAGE_WRITE - &tinygltf::WriteImageData; -#else - nullptr; -#endif - void *write_image_user_data_{nullptr}; -}; - -#ifdef __clang__ -#pragma clang diagnostic pop // -Wpadded -#endif - -} // namespace tinygltf - -#endif // TINY_GLTF_H_ - -#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__) -#include -//#include -#ifndef TINYGLTF_NO_FS -#include -#include -#include // for is_directory check -#endif -#include - -#ifdef __clang__ -// Disable some warnings for external files. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wfloat-equal" -#pragma clang diagnostic ignored "-Wexit-time-destructors" -#pragma clang diagnostic ignored "-Wconversion" -#pragma clang diagnostic ignored "-Wold-style-cast" -#pragma clang diagnostic ignored "-Wglobal-constructors" -#if __has_warning("-Wreserved-id-macro") -#pragma clang diagnostic ignored "-Wreserved-id-macro" -#endif -#pragma clang diagnostic ignored "-Wdisabled-macro-expansion" -#pragma clang diagnostic ignored "-Wpadded" -#pragma clang diagnostic ignored "-Wc++98-compat" -#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" -#pragma clang diagnostic ignored "-Wdocumentation-unknown-command" -#pragma clang diagnostic ignored "-Wswitch-enum" -#pragma clang diagnostic ignored "-Wimplicit-fallthrough" -#pragma clang diagnostic ignored "-Wweak-vtables" -#pragma clang diagnostic ignored "-Wcovered-switch-default" -#if __has_warning("-Wdouble-promotion") -#pragma clang diagnostic ignored "-Wdouble-promotion" -#endif -#if __has_warning("-Wcomma") -#pragma clang diagnostic ignored "-Wcomma" -#endif -#if __has_warning("-Wzero-as-null-pointer-constant") -#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" -#endif -#if __has_warning("-Wcast-qual") -#pragma clang diagnostic ignored "-Wcast-qual" -#endif -#if __has_warning("-Wmissing-variable-declarations") -#pragma clang diagnostic ignored "-Wmissing-variable-declarations" -#endif -#if __has_warning("-Wmissing-prototypes") -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#endif -#if __has_warning("-Wcast-align") -#pragma clang diagnostic ignored "-Wcast-align" -#endif -#if __has_warning("-Wnewline-eof") -#pragma clang diagnostic ignored "-Wnewline-eof" -#endif -#if __has_warning("-Wunused-parameter") -#pragma clang diagnostic ignored "-Wunused-parameter" -#endif -#if __has_warning("-Wmismatched-tags") -#pragma clang diagnostic ignored "-Wmismatched-tags" -#endif -#if __has_warning("-Wextra-semi-stmt") -#pragma clang diagnostic ignored "-Wextra-semi-stmt" -#endif -#endif - -// Disable GCC warnings -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wtype-limits" -#endif // __GNUC__ - -#ifndef TINYGLTF_NO_INCLUDE_JSON -#ifndef TINYGLTF_USE_RAPIDJSON -#include "json.hpp" -#else -#ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON -#include "document.h" -#include "prettywriter.h" -#include "rapidjson.h" -#include "stringbuffer.h" -#include "writer.h" -#endif -#endif -#endif - -#ifdef TINYGLTF_ENABLE_DRACO -#include "draco/compression/decode.h" -#include "draco/core/decoder_buffer.h" -#endif - -#ifndef TINYGLTF_NO_STB_IMAGE -#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE -#include "stb_image.h" -#endif -#endif - -#ifndef TINYGLTF_NO_STB_IMAGE_WRITE -#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE -#include "stb_image_write.h" -#endif -#endif - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - -#ifdef _WIN32 - -// issue 143. -// Define NOMINMAX to avoid min/max defines, -// but undef it after included Windows.h -#ifndef NOMINMAX -#define TINYGLTF_INTERNAL_NOMINMAX -#define NOMINMAX -#endif - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN -#endif -#ifndef __MINGW32__ -#include // include API for expanding a file path -#else -#include -#endif - -#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN -#undef WIN32_LEAN_AND_MEAN -#endif - -#if defined(TINYGLTF_INTERNAL_NOMINMAX) -#undef NOMINMAX -#endif - -#if defined(__GLIBCXX__) // mingw - -#include // _O_RDONLY - -#include // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf) - -#endif - -#elif !defined(__ANDROID__) && !defined(__OpenBSD__) -//#include -#endif - -#if defined(__sparcv9) || defined(__powerpc__) -// Big endian -#else -#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU -#define TINYGLTF_LITTLE_ENDIAN 1 -#endif -#endif - -namespace tinygltf { -namespace detail { -#ifdef TINYGLTF_USE_RAPIDJSON - -#ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR -// This uses the RapidJSON CRTAllocator. It is thread safe and multiple -// documents may be active at once. -using json = - rapidjson::GenericValue, rapidjson::CrtAllocator>; -using json_const_iterator = json::ConstMemberIterator; -using json_const_array_iterator = json const *; -using JsonDocument = - rapidjson::GenericDocument, rapidjson::CrtAllocator>; -rapidjson::CrtAllocator s_CrtAllocator; // stateless and thread safe -rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; } -#else -// This uses the default RapidJSON MemoryPoolAllocator. It is very fast, but -// not thread safe. Only a single JsonDocument may be active at any one time, -// meaning only a single gltf load/save can be active any one time. -using json = rapidjson::Value; -using json_const_iterator = json::ConstMemberIterator; -using json_const_array_iterator = json const *; -rapidjson::Document *s_pActiveDocument = nullptr; -rapidjson::Document::AllocatorType &GetAllocator() { - assert(s_pActiveDocument); // Root json node must be JsonDocument type - return s_pActiveDocument->GetAllocator(); -} - -#ifdef __clang__ -#pragma clang diagnostic push -// Suppress JsonDocument(JsonDocument &&rhs) noexcept -#pragma clang diagnostic ignored "-Wunused-member-function" -#endif - -struct JsonDocument : public rapidjson::Document { - JsonDocument() { - assert(s_pActiveDocument == - nullptr); // When using default allocator, only one document can be - // active at a time, if you need multiple active at once, - // define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR - s_pActiveDocument = this; - } - JsonDocument(const JsonDocument &) = delete; - JsonDocument(JsonDocument &&rhs) noexcept - : rapidjson::Document(std::move(rhs)) { - s_pActiveDocument = this; - rhs.isNil = true; - } - ~JsonDocument() { - if (!isNil) { - s_pActiveDocument = nullptr; - } - } - - private: - bool isNil = false; -}; - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR - -#else -using nlohmann::json; -using json_const_iterator = json::const_iterator; -using json_const_array_iterator = json_const_iterator; -using JsonDocument = json; -#endif - -void JsonParse(JsonDocument &doc, const char *str, size_t length, - bool throwExc = false) { -#ifdef TINYGLTF_USE_RAPIDJSON - (void)throwExc; - doc.Parse(str, length); -#else - doc = detail::json::parse(str, str + length, nullptr, throwExc); -#endif -} -} // namespace -} - -#ifdef __APPLE__ -#include "TargetConditionals.h" -#endif - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wc++98-compat" -#endif - -namespace tinygltf { - -/// -/// Internal LoadImageDataOption struct. -/// This struct is passed through `user_pointer` in LoadImageData. -/// The struct is not passed when the user supply their own LoadImageData -/// callbacks. -/// -struct LoadImageDataOption { - // true: preserve image channels(e.g. load as RGB image if the image has RGB - // channels) default `false`(channels are expanded to RGBA for backward - // compatibility). - bool preserve_channels{false}; -}; - -// Equals function for Value, for recursivity -static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) { - if (one.Type() != other.Type()) return false; - - switch (one.Type()) { - case NULL_TYPE: - return true; - case BOOL_TYPE: - return one.Get() == other.Get(); - case REAL_TYPE: - return TINYGLTF_DOUBLE_EQUAL(one.Get(), other.Get()); - case INT_TYPE: - return one.Get() == other.Get(); - case OBJECT_TYPE: { - auto oneObj = one.Get(); - auto otherObj = other.Get(); - if (oneObj.size() != otherObj.size()) return false; - for (auto &it : oneObj) { - auto otherIt = otherObj.find(it.first); - if (otherIt == otherObj.end()) return false; - - if (!Equals(it.second, otherIt->second)) return false; - } - return true; - } - case ARRAY_TYPE: { - if (one.Size() != other.Size()) return false; - for (int i = 0; i < int(one.Size()); ++i) - if (!Equals(one.Get(i), other.Get(i))) return false; - return true; - } - case STRING_TYPE: - return one.Get() == other.Get(); - case BINARY_TYPE: - return one.Get >() == - other.Get >(); - default: { - // unhandled type - return false; - } - } -} - -// Equals function for std::vector using TINYGLTF_DOUBLE_EPSILON -static bool Equals(const std::vector &one, - const std::vector &other) { - if (one.size() != other.size()) return false; - for (int i = 0; i < int(one.size()); ++i) { - if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false; - } - return true; -} - -bool Accessor::operator==(const Accessor &other) const { - return this->bufferView == other.bufferView && - this->byteOffset == other.byteOffset && - this->componentType == other.componentType && - this->count == other.count && this->extensions == other.extensions && - this->extras == other.extras && - Equals(this->maxValues, other.maxValues) && - Equals(this->minValues, other.minValues) && this->name == other.name && - this->normalized == other.normalized && this->type == other.type; -} -bool Animation::operator==(const Animation &other) const { - return this->channels == other.channels && - this->extensions == other.extensions && this->extras == other.extras && - this->name == other.name && this->samplers == other.samplers; -} -bool AnimationChannel::operator==(const AnimationChannel &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - this->target_node == other.target_node && - this->target_path == other.target_path && - this->sampler == other.sampler; -} -bool AnimationSampler::operator==(const AnimationSampler &other) const { - return this->extras == other.extras && this->extensions == other.extensions && - this->input == other.input && - this->interpolation == other.interpolation && - this->output == other.output; -} -bool Asset::operator==(const Asset &other) const { - return this->copyright == other.copyright && - this->extensions == other.extensions && this->extras == other.extras && - this->generator == other.generator && - this->minVersion == other.minVersion && this->version == other.version; -} -bool Buffer::operator==(const Buffer &other) const { - return this->data == other.data && this->extensions == other.extensions && - this->extras == other.extras && this->name == other.name && - this->uri == other.uri; -} -bool BufferView::operator==(const BufferView &other) const { - return this->buffer == other.buffer && this->byteLength == other.byteLength && - this->byteOffset == other.byteOffset && - this->byteStride == other.byteStride && this->name == other.name && - this->target == other.target && this->extensions == other.extensions && - this->extras == other.extras && - this->dracoDecoded == other.dracoDecoded; -} -bool Camera::operator==(const Camera &other) const { - return this->name == other.name && this->extensions == other.extensions && - this->extras == other.extras && - this->orthographic == other.orthographic && - this->perspective == other.perspective && this->type == other.type; -} -bool Image::operator==(const Image &other) const { - return this->bufferView == other.bufferView && - this->component == other.component && - this->extensions == other.extensions && this->extras == other.extras && - this->height == other.height && this->image == other.image && - this->mimeType == other.mimeType && this->name == other.name && - this->uri == other.uri && this->width == other.width; -} -bool Light::operator==(const Light &other) const { - return Equals(this->color, other.color) && this->name == other.name && - this->type == other.type; -} -bool Material::operator==(const Material &other) const { - return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) && - (this->normalTexture == other.normalTexture) && - (this->occlusionTexture == other.occlusionTexture) && - (this->emissiveTexture == other.emissiveTexture) && - Equals(this->emissiveFactor, other.emissiveFactor) && - (this->alphaMode == other.alphaMode) && - TINYGLTF_DOUBLE_EQUAL(this->alphaCutoff, other.alphaCutoff) && - (this->doubleSided == other.doubleSided) && - (this->extensions == other.extensions) && - (this->extras == other.extras) && (this->values == other.values) && - (this->additionalValues == other.additionalValues) && - (this->name == other.name); -} -bool Mesh::operator==(const Mesh &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - this->name == other.name && Equals(this->weights, other.weights) && - this->primitives == other.primitives; -} -bool Model::operator==(const Model &other) const { - return this->accessors == other.accessors && - this->animations == other.animations && this->asset == other.asset && - this->buffers == other.buffers && - this->bufferViews == other.bufferViews && - this->cameras == other.cameras && - this->defaultScene == other.defaultScene && - this->extensions == other.extensions && - this->extensionsRequired == other.extensionsRequired && - this->extensionsUsed == other.extensionsUsed && - this->extras == other.extras && this->images == other.images && - this->lights == other.lights && this->materials == other.materials && - this->meshes == other.meshes && this->nodes == other.nodes && - this->samplers == other.samplers && this->scenes == other.scenes && - this->skins == other.skins && this->textures == other.textures; -} -bool Node::operator==(const Node &other) const { - return this->camera == other.camera && this->children == other.children && - this->extensions == other.extensions && this->extras == other.extras && - Equals(this->matrix, other.matrix) && this->mesh == other.mesh && - this->name == other.name && Equals(this->rotation, other.rotation) && - Equals(this->scale, other.scale) && this->skin == other.skin && - Equals(this->translation, other.translation) && - Equals(this->weights, other.weights); -} -bool SpotLight::operator==(const SpotLight &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) && - TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle); -} -bool OrthographicCamera::operator==(const OrthographicCamera &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) && - TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) && - TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) && - TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear); -} -bool Parameter::operator==(const Parameter &other) const { - if (this->bool_value != other.bool_value || - this->has_number_value != other.has_number_value) - return false; - - if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value)) - return false; - - if (this->json_double_value.size() != other.json_double_value.size()) - return false; - for (auto &it : this->json_double_value) { - auto otherIt = other.json_double_value.find(it.first); - if (otherIt == other.json_double_value.end()) return false; - - if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false; - } - - if (!Equals(this->number_array, other.number_array)) return false; - - if (this->string_value != other.string_value) return false; - - return true; -} -bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const { - return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) && - this->extensions == other.extensions && this->extras == other.extras && - TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) && - TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) && - TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear); -} -bool Primitive::operator==(const Primitive &other) const { - return this->attributes == other.attributes && this->extras == other.extras && - this->indices == other.indices && this->material == other.material && - this->mode == other.mode && this->targets == other.targets; -} -bool Sampler::operator==(const Sampler &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - this->magFilter == other.magFilter && - this->minFilter == other.minFilter && this->name == other.name && - this->wrapS == other.wrapS && this->wrapT == other.wrapT; - - // this->wrapR == other.wrapR -} -bool Scene::operator==(const Scene &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - this->name == other.name && this->nodes == other.nodes; -} -bool Skin::operator==(const Skin &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - this->inverseBindMatrices == other.inverseBindMatrices && - this->joints == other.joints && this->name == other.name && - this->skeleton == other.skeleton; -} -bool Texture::operator==(const Texture &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - this->name == other.name && this->sampler == other.sampler && - this->source == other.source; -} -bool TextureInfo::operator==(const TextureInfo &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - this->index == other.index && this->texCoord == other.texCoord; -} -bool NormalTextureInfo::operator==(const NormalTextureInfo &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - this->index == other.index && this->texCoord == other.texCoord && - TINYGLTF_DOUBLE_EQUAL(this->scale, other.scale); -} -bool OcclusionTextureInfo::operator==(const OcclusionTextureInfo &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - this->index == other.index && this->texCoord == other.texCoord && - TINYGLTF_DOUBLE_EQUAL(this->strength, other.strength); -} -bool PbrMetallicRoughness::operator==(const PbrMetallicRoughness &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - (this->baseColorTexture == other.baseColorTexture) && - (this->metallicRoughnessTexture == other.metallicRoughnessTexture) && - Equals(this->baseColorFactor, other.baseColorFactor) && - TINYGLTF_DOUBLE_EQUAL(this->metallicFactor, other.metallicFactor) && - TINYGLTF_DOUBLE_EQUAL(this->roughnessFactor, other.roughnessFactor); -} -bool Value::operator==(const Value &other) const { - return Equals(*this, other); -} - -static void swap4(unsigned int *val) { -#ifdef TINYGLTF_LITTLE_ENDIAN - (void)val; -#else - unsigned int tmp = *val; - unsigned char *dst = reinterpret_cast(val); - unsigned char *src = reinterpret_cast(&tmp); - - dst[0] = src[3]; - dst[1] = src[2]; - dst[2] = src[1]; - dst[3] = src[0]; -#endif -} - -static std::string JoinPath(const std::string &path0, - const std::string &path1) { - if (path0.empty()) { - return path1; - } else { - // check '/' - char lastChar = *path0.rbegin(); - if (lastChar != '/') { - return path0 + std::string("/") + path1; - } else { - return path0 + path1; - } - } -} - -static std::string FindFile(const std::vector &paths, - const std::string &filepath, FsCallbacks *fs) { - if (fs == nullptr || fs->ExpandFilePath == nullptr || - fs->FileExists == nullptr) { - // Error, fs callback[s] missing - return std::string(); - } - - // https://github.com/syoyo/tinygltf/issues/416 - // Use strlen() since std::string's size/length reports the number of elements in the buffer, not the length of string(null-terminated) - // strip null-character in the middle of string. - size_t slength = strlen(filepath.c_str()); - if (slength == 0) { - return std::string(); - } - - std::string cleaned_filepath = std::string(filepath.c_str()); - - for (size_t i = 0; i < paths.size(); i++) { - std::string absPath = - fs->ExpandFilePath(JoinPath(paths[i], cleaned_filepath), fs->user_data); - if (fs->FileExists(absPath, fs->user_data)) { - return absPath; - } - } - - return std::string(); -} - -static std::string GetFilePathExtension(const std::string &FileName) { - if (FileName.find_last_of(".") != std::string::npos) - return FileName.substr(FileName.find_last_of(".") + 1); - return ""; -} - -static std::string GetBaseDir(const std::string &filepath) { - if (filepath.find_last_of("/\\") != std::string::npos) - return filepath.substr(0, filepath.find_last_of("/\\")); - return ""; -} - -static std::string GetBaseFilename(const std::string &filepath) { - auto idx = filepath.find_last_of("/\\"); - if (idx != std::string::npos) return filepath.substr(idx + 1); - return filepath; -} - -std::string base64_encode(unsigned char const *, unsigned int len); -std::string base64_decode(std::string const &s); - -/* - base64.cpp and base64.h - - Copyright (C) 2004-2008 RenĂ© Nyffenegger - - This source code is provided 'as-is', without any express or implied - warranty. In no event will the author be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this source code must not be misrepresented; you must not - claim that you wrote the original source code. If you use this source code - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original source code. - - 3. This notice may not be removed or altered from any source distribution. - - RenĂ© Nyffenegger rene.nyffenegger@adp-gmbh.ch - -*/ - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wsign-conversion" -#pragma clang diagnostic ignored "-Wconversion" -#endif - -static inline bool is_base64(unsigned char c) { - return (isalnum(c) || (c == '+') || (c == '/')); -} - -std::string base64_encode(unsigned char const *bytes_to_encode, - unsigned int in_len) { - std::string ret; - int i = 0; - int j = 0; - unsigned char char_array_3[3]; - unsigned char char_array_4[4]; - - const char *base64_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - - while (in_len--) { - char_array_3[i++] = *(bytes_to_encode++); - if (i == 3) { - char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; - char_array_4[1] = - ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); - char_array_4[2] = - ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); - char_array_4[3] = char_array_3[2] & 0x3f; - - for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]]; - i = 0; - } - } - - if (i) { - for (j = i; j < 3; j++) char_array_3[j] = '\0'; - - char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; - char_array_4[1] = - ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); - char_array_4[2] = - ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); - - for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]]; - - while ((i++ < 3)) ret += '='; - } - - return ret; -} - -std::string base64_decode(std::string const &encoded_string) { - int in_len = static_cast(encoded_string.size()); - int i = 0; - int j = 0; - int in_ = 0; - unsigned char char_array_4[4], char_array_3[3]; - std::string ret; - - const std::string base64_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - - while (in_len-- && (encoded_string[in_] != '=') && - is_base64(encoded_string[in_])) { - char_array_4[i++] = encoded_string[in_]; - in_++; - if (i == 4) { - for (i = 0; i < 4; i++) - char_array_4[i] = - static_cast(base64_chars.find(char_array_4[i])); - - char_array_3[0] = - (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = - ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - - for (i = 0; (i < 3); i++) ret += char_array_3[i]; - i = 0; - } - } - - if (i) { - for (j = i; j < 4; j++) char_array_4[j] = 0; - - for (j = 0; j < 4; j++) - char_array_4[j] = - static_cast(base64_chars.find(char_array_4[j])); - - char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = - ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - - for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; - } - - return ret; -} -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -// https://github.com/syoyo/tinygltf/issues/228 -// TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri -// decoding? -// -// Uri Decoding from DLIB -// http://dlib.net/dlib/server/server_http.cpp.html -// --- dlib begin ------------------------------------------------------------ -// Copyright (C) 2003 Davis E. King (davis@dlib.net) -// License: Boost Software License -// Boost Software License - Version 1.0 - August 17th, 2003 - -// Permission is hereby granted, free of charge, to any person or organization -// obtaining a copy of the software and accompanying documentation covered by -// this license (the "Software") to use, reproduce, display, distribute, -// execute, and transmit the Software, and to prepare derivative works of the -// Software, and to permit third-parties to whom the Software is furnished to -// do so, all subject to the following: -// The copyright notices in the Software and this entire statement, including -// the above license grant, this restriction and the following disclaimer, -// must be included in all copies of the Software, in whole or in part, and -// all derivative works of the Software, unless such copies or derivative -// works are solely in the form of machine-executable object code generated by -// a source language processor. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. -// -namespace dlib { - -inline unsigned char from_hex(unsigned char ch) { - if (ch <= '9' && ch >= '0') - ch -= '0'; - else if (ch <= 'f' && ch >= 'a') - ch -= 'a' - 10; - else if (ch <= 'F' && ch >= 'A') - ch -= 'A' - 10; - else - ch = 0; - return ch; -} - -static const std::string urldecode(const std::string &str) { - using namespace std; - string result; - string::size_type i; - for (i = 0; i < str.size(); ++i) { - if (str[i] == '+') { - result += ' '; - } else if (str[i] == '%' && str.size() > i + 2) { - const unsigned char ch1 = - from_hex(static_cast(str[i + 1])); - const unsigned char ch2 = - from_hex(static_cast(str[i + 2])); - const unsigned char ch = static_cast((ch1 << 4) | ch2); - result += static_cast(ch); - i += 2; - } else { - result += str[i]; - } - } - return result; -} - -} // namespace dlib -// --- dlib end -------------------------------------------------------------- - -bool URIDecode(const std::string &in_uri, std::string *out_uri, - void *user_data) { - (void)user_data; - *out_uri = dlib::urldecode(in_uri); - return true; -} - -static bool LoadExternalFile(std::vector *out, std::string *err, - std::string *warn, const std::string &filename, - const std::string &basedir, bool required, - size_t reqBytes, bool checkSize, size_t maxFileSize, FsCallbacks *fs) { - if (fs == nullptr || fs->FileExists == nullptr || - fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) { - // This is a developer error, assert() ? - if (err) { - (*err) += "FS callback[s] not set\n"; - } - return false; - } - - std::string *failMsgOut = required ? err : warn; - - out->clear(); - - std::vector paths; - paths.push_back(basedir); - paths.push_back("."); - - std::string filepath = FindFile(paths, filename, fs); - if (filepath.empty() || filename.empty()) { - if (failMsgOut) { - (*failMsgOut) += "File not found : " + filename + "\n"; - } - return false; - } - - // Check file size - if (fs->GetFileSizeInBytes) { - - size_t file_size{0}; - std::string _err; - bool ok = fs->GetFileSizeInBytes(&file_size, &_err, filepath, fs->user_data); - if (!ok) { - if (_err.size()) { - if (failMsgOut) { - (*failMsgOut) += "Getting file size failed : " + filename + ", err = " + _err + "\n"; - } - } - return false; - } - - if (file_size > maxFileSize) { - if (failMsgOut) { - (*failMsgOut) += "File size " + std::to_string(file_size) + " exceeds maximum allowed file size " + std::to_string(maxFileSize) + " : " + filepath + "\n"; - } - return false; - } - } - - std::vector buf; - std::string fileReadErr; - bool fileRead = - fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data); - if (!fileRead) { - if (failMsgOut) { - (*failMsgOut) += - "File read error : " + filepath + " : " + fileReadErr + "\n"; - } - return false; - } - - size_t sz = buf.size(); - if (sz == 0) { - if (failMsgOut) { - (*failMsgOut) += "File is empty : " + filepath + "\n"; - } - return false; - } - - if (checkSize) { - if (reqBytes == sz) { - out->swap(buf); - return true; - } else { - std::stringstream ss; - ss << "File size mismatch : " << filepath << ", requestedBytes " - << reqBytes << ", but got " << sz << std::endl; - if (failMsgOut) { - (*failMsgOut) += ss.str(); - } - return false; - } - } - - out->swap(buf); - return true; -} - -void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) { - LoadImageData = func; - load_image_user_data_ = user_data; - user_image_loader_ = true; -} - -void TinyGLTF::RemoveImageLoader() { - LoadImageData = -#ifndef TINYGLTF_NO_STB_IMAGE - &tinygltf::LoadImageData; -#else - nullptr; -#endif - - load_image_user_data_ = nullptr; - user_image_loader_ = false; -} - -#ifndef TINYGLTF_NO_STB_IMAGE -bool LoadImageData(Image *image, const int image_idx, std::string *err, - std::string *warn, int req_width, int req_height, - const unsigned char *bytes, int size, void *user_data) { - (void)warn; - - LoadImageDataOption option; - if (user_data) { - option = *reinterpret_cast(user_data); - } - - int w = 0, h = 0, comp = 0, req_comp = 0; - - unsigned char *data = nullptr; - - // preserve_channels true: Use channels stored in the image file. - // false: force 32-bit textures for common Vulkan compatibility. It appears - // that some GPU drivers do not support 24-bit images for Vulkan - req_comp = option.preserve_channels ? 0 : 4; - int bits = 8; - int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; - - // It is possible that the image we want to load is a 16bit per channel image - // We are going to attempt to load it as 16bit per channel, and if it worked, - // set the image data accordingly. We are casting the returned pointer into - // unsigned char, because we are representing "bytes". But we are updating - // the Image metadata to signal that this image uses 2 bytes (16bits) per - // channel: - if (stbi_is_16_bit_from_memory(bytes, size)) { - data = reinterpret_cast( - stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp)); - if (data) { - bits = 16; - pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT; - } - } - - // at this point, if data is still NULL, it means that the image wasn't - // 16bit per channel, we are going to load it as a normal 8bit per channel - // image as we used to do: - // if image cannot be decoded, ignore parsing and keep it by its path - // don't break in this case - // FIXME we should only enter this function if the image is embedded. If - // image->uri references - // an image file, it should be left as it is. Image loading should not be - // mandatory (to support other formats) - if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp); - if (!data) { - // NOTE: you can use `warn` instead of `err` - if (err) { - (*err) += - "Unknown image format. STB cannot decode image data for image[" + - std::to_string(image_idx) + "] name = \"" + image->name + "\".\n"; - } - return false; - } - - if ((w < 1) || (h < 1)) { - stbi_image_free(data); - if (err) { - (*err) += "Invalid image data for image[" + std::to_string(image_idx) + - "] name = \"" + image->name + "\"\n"; - } - return false; - } - - if (req_width > 0) { - if (req_width != w) { - stbi_image_free(data); - if (err) { - (*err) += "Image width mismatch for image[" + - std::to_string(image_idx) + "] name = \"" + image->name + - "\"\n"; - } - return false; - } - } - - if (req_height > 0) { - if (req_height != h) { - stbi_image_free(data); - if (err) { - (*err) += "Image height mismatch. for image[" + - std::to_string(image_idx) + "] name = \"" + image->name + - "\"\n"; - } - return false; - } - } - - if (req_comp != 0) { - // loaded data has `req_comp` channels(components) - comp = req_comp; - } - - image->width = w; - image->height = h; - image->component = comp; - image->bits = bits; - image->pixel_type = pixel_type; - image->image.resize(static_cast(w * h * comp) * size_t(bits / 8)); - std::copy(data, data + w * h * comp * (bits / 8), image->image.begin()); - stbi_image_free(data); - - return true; -} -#endif - -void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) { - WriteImageData = func; - write_image_user_data_ = user_data; -} - -#ifndef TINYGLTF_NO_STB_IMAGE_WRITE -static void WriteToMemory_stbi(void *context, void *data, int size) { - std::vector *buffer = - reinterpret_cast *>(context); - - unsigned char *pData = reinterpret_cast(data); - - buffer->insert(buffer->end(), pData, pData + size); -} - -bool WriteImageData(const std::string *basepath, const std::string *filename, - const Image *image, bool embedImages, - const URICallbacks *uri_cb, std::string *out_uri, - void *fsPtr) { - const std::string ext = GetFilePathExtension(*filename); - - // Write image to temporary buffer - std::string header; - std::vector data; - - if (ext == "png") { - if ((image->bits != 8) || - (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) { - // Unsupported pixel format - return false; - } - - if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width, - image->height, image->component, - &image->image[0], 0)) { - return false; - } - header = "data:image/png;base64,"; - } else if (ext == "jpg") { - if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width, - image->height, image->component, - &image->image[0], 100)) { - return false; - } - header = "data:image/jpeg;base64,"; - } else if (ext == "bmp") { - if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width, - image->height, image->component, - &image->image[0])) { - return false; - } - header = "data:image/bmp;base64,"; - } else if (!embedImages) { - // Error: can't output requested format to file - return false; - } - - if (embedImages) { - // Embed base64-encoded image into URI - if (data.size()) { - *out_uri = header + - base64_encode(&data[0], static_cast(data.size())); - } else { - // Throw error? - } - } else { - // Write image to disc - FsCallbacks *fs = reinterpret_cast(fsPtr); - if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) { - const std::string imagefilepath = JoinPath(*basepath, *filename); - std::string writeError; - if (!fs->WriteWholeFile(&writeError, imagefilepath, data, - fs->user_data)) { - // Could not write image file to disc; Throw error ? - return false; - } - } else { - // Throw error? - } - if (uri_cb->encode) { - if (!uri_cb->encode(*filename, "image", out_uri, uri_cb->user_data)) { - return false; - } - } else { - *out_uri = *filename; - } - } - - return true; -} -#endif - -void TinyGLTF::SetURICallbacks(URICallbacks callbacks) { - assert(callbacks.decode); - if (callbacks.decode) { - uri_cb = callbacks; - } -} - -void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; } - -#ifdef _WIN32 -static inline std::wstring UTF8ToWchar(const std::string &str) { - int wstr_size = - MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0); - std::wstring wstr((size_t)wstr_size, 0); - MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0], - (int)wstr.size()); - return wstr; -} - -static inline std::string WcharToUTF8(const std::wstring &wstr) { - int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), - nullptr, 0, nullptr, nullptr); - std::string str((size_t)str_size, 0); - WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0], - (int)str.size(), nullptr, nullptr); - return str; -} -#endif - -#ifndef TINYGLTF_NO_FS -// Default implementations of filesystem functions - -bool FileExists(const std::string &abs_filename, void *) { - bool ret; -#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS - if (asset_manager) { - AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(), - AASSET_MODE_STREAMING); - if (!asset) { - return false; - } - AAsset_close(asset); - ret = true; - } else { - return false; - } -#else -#ifdef _WIN32 -#if defined(_MSC_VER) || defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) - - // First check if a file is a directory. - DWORD result = GetFileAttributesW(UTF8ToWchar(abs_filename).c_str()); - if (result == INVALID_FILE_ATTRIBUTES) { - return false; - } - if (result & FILE_ATTRIBUTE_DIRECTORY) { - return false; - } - - FILE *fp = nullptr; - errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb"); - if (err != 0) { - return false; - } -#else - // TODO: is_directory check - FILE *fp = nullptr; - errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb"); - if (err != 0) { - return false; - } -#endif - -#else - struct stat sb; - if (stat(abs_filename.c_str(), &sb)) { - return false; - } - if (S_ISDIR(sb.st_mode)) { - return false; - } - - FILE *fp = fopen(abs_filename.c_str(), "rb"); -#endif - if (fp) { - ret = true; - fclose(fp); - } else { - ret = false; - } -#endif - - return ret; -} - -std::string ExpandFilePath(const std::string &filepath, void *) { - // https://github.com/syoyo/tinygltf/issues/368 - // - // No file path expansion in built-in FS function anymore, since glTF URI - // should not contain tilde('~') and environment variables, and for security - // reason(`wordexp`). - // - // Users need to supply `base_dir`(in `LoadASCIIFromString`, - // `LoadBinaryFromMemory`) in expanded absolute path. - - return filepath; - -#if 0 -#ifdef _WIN32 - // Assume input `filepath` is encoded in UTF-8 - std::wstring wfilepath = UTF8ToWchar(filepath); - DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0); - wchar_t *wstr = new wchar_t[wlen]; - ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen); - - std::wstring ws(wstr); - delete[] wstr; - return WcharToUTF8(ws); - -#else - -#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \ - defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__) - // no expansion - std::string s = filepath; -#else - std::string s; - wordexp_t p; - - if (filepath.empty()) { - return ""; - } - - // Quote the string to keep any spaces in filepath intact. - std::string quoted_path = "\"" + filepath + "\""; - // char** w; - int ret = wordexp(quoted_path.c_str(), &p, 0); - if (ret) { - // err - s = filepath; - return s; - } - - // Use first element only. - if (p.we_wordv) { - s = std::string(p.we_wordv[0]); - wordfree(&p); - } else { - s = filepath; - } - -#endif - - return s; -#endif -#endif -} - -bool GetFileSizeInBytes(size_t *filesize_out, std::string *err, - const std::string &filepath, void *userdata) { - (void)userdata; - -#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS - if (asset_manager) { - AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(), - AASSET_MODE_STREAMING); - if (!asset) { - if (err) { - (*err) += "File open error : " + filepath + "\n"; - } - return false; - } - size_t size = AAsset_getLength(asset); - - if (size == 0) { - if (err) { - (*err) += "Invalid file size : " + filepath + - " (does the path point to a directory?)"; - } - return false; - } - - return true; - } else { - if (err) { - (*err) += "No asset manager specified : " + filepath + "\n"; - } - return false; - } -#else -#ifdef _WIN32 -#if defined(__GLIBCXX__) // mingw - int file_descriptor = - _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY); - __gnu_cxx::stdio_filebuf wfile_buf(file_descriptor, std::ios_base::in); - std::istream f(&wfile_buf); -#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION) - // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept - // `wchar_t *` - std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary); -#else - // Unknown compiler/runtime - std::ifstream f(filepath.c_str(), std::ifstream::binary); -#endif -#else - std::ifstream f(filepath.c_str(), std::ifstream::binary); -#endif - if (!f) { - if (err) { - (*err) += "File open error : " + filepath + "\n"; - } - return false; - } - - // For directory(and pipe?), peek() will fail(Posix gnustl/libc++ only) - int buf = f.peek(); - if (!f) { - if (err) { - (*err) += "File read error. Maybe empty file or invalid file : " + filepath + "\n"; - } - return false; - } - - f.seekg(0, f.end); - size_t sz = static_cast(f.tellg()); - - //std::cout << "sz = " << sz << "\n"; - f.seekg(0, f.beg); - - if (int64_t(sz) < 0) { - if (err) { - (*err) += "Invalid file size : " + filepath + - " (does the path point to a directory?)"; - } - return false; - } else if (sz == 0) { - if (err) { - (*err) += "File is empty : " + filepath + "\n"; - } - return false; - } else if (sz >= (std::numeric_limits::max)()) { - if (err) { - (*err) += "Invalid file size : " + filepath + "\n"; - } - return false; - } - - (*filesize_out) = sz; - return true; -#endif -} - -bool ReadWholeFile(std::vector *out, std::string *err, - const std::string &filepath, void *) { -#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS - if (asset_manager) { - AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(), - AASSET_MODE_STREAMING); - if (!asset) { - if (err) { - (*err) += "File open error : " + filepath + "\n"; - } - return false; - } - size_t size = AAsset_getLength(asset); - if (size == 0) { - if (err) { - (*err) += "Invalid file size : " + filepath + - " (does the path point to a directory?)"; - } - return false; - } - out->resize(size); - AAsset_read(asset, reinterpret_cast(&out->at(0)), size); - AAsset_close(asset); - return true; - } else { - if (err) { - (*err) += "No asset manager specified : " + filepath + "\n"; - } - return false; - } -#else -#ifdef _WIN32 -#if defined(__GLIBCXX__) // mingw - int file_descriptor = - _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY); - __gnu_cxx::stdio_filebuf wfile_buf(file_descriptor, std::ios_base::in); - std::istream f(&wfile_buf); -#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION) - // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept - // `wchar_t *` - std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary); -#else - // Unknown compiler/runtime - std::ifstream f(filepath.c_str(), std::ifstream::binary); -#endif -#else - std::ifstream f(filepath.c_str(), std::ifstream::binary); -#endif - if (!f) { - if (err) { - (*err) += "File open error : " + filepath + "\n"; - } - return false; - } - - // For directory(and pipe?), peek() will fail(Posix gnustl/libc++ only) - int buf = f.peek(); - if (!f) { - if (err) { - (*err) += "File read error. Maybe empty file or invalid file : " + filepath + "\n"; - } - return false; - } - - f.seekg(0, f.end); - size_t sz = static_cast(f.tellg()); - - //std::cout << "sz = " << sz << "\n"; - f.seekg(0, f.beg); - - if (int64_t(sz) < 0) { - if (err) { - (*err) += "Invalid file size : " + filepath + - " (does the path point to a directory?)"; - } - return false; - } else if (sz == 0) { - if (err) { - (*err) += "File is empty : " + filepath + "\n"; - } - return false; - } else if (sz >= (std::numeric_limits::max)()) { - if (err) { - (*err) += "Invalid file size : " + filepath + "\n"; - } - return false; - } - - out->resize(sz); - f.read(reinterpret_cast(&out->at(0)), - static_cast(sz)); - - return true; -#endif -} - -bool WriteWholeFile(std::string *err, const std::string &filepath, - const std::vector &contents, void *) { -#ifdef _WIN32 -#if defined(__GLIBCXX__) // mingw - int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(), - _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); - __gnu_cxx::stdio_filebuf wfile_buf( - file_descriptor, std::ios_base::out | std::ios_base::binary); - std::ostream f(&wfile_buf); -#elif defined(_MSC_VER) - std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary); -#else // clang? - std::ofstream f(filepath.c_str(), std::ofstream::binary); -#endif -#else - std::ofstream f(filepath.c_str(), std::ofstream::binary); -#endif - if (!f) { - if (err) { - (*err) += "File open error for writing : " + filepath + "\n"; - } - return false; - } - - f.write(reinterpret_cast(&contents.at(0)), - static_cast(contents.size())); - if (!f) { - if (err) { - (*err) += "File write error: " + filepath + "\n"; - } - return false; - } - - return true; -} - -#endif // TINYGLTF_NO_FS - -static std::string MimeToExt(const std::string &mimeType) { - if (mimeType == "image/jpeg") { - return "jpg"; - } else if (mimeType == "image/png") { - return "png"; - } else if (mimeType == "image/bmp") { - return "bmp"; - } else if (mimeType == "image/gif") { - return "gif"; - } - - return ""; -} - -static bool UpdateImageObject(const Image &image, std::string &baseDir, - int index, bool embedImages, - const URICallbacks *uri_cb, - WriteImageDataFunction *WriteImageData, - void *user_data, std::string *out_uri) { - std::string filename; - std::string ext; - // If image has uri, use it as a filename - if (image.uri.size()) { - std::string decoded_uri; - if (!uri_cb->decode(image.uri, &decoded_uri, uri_cb->user_data)) { - // A decode failure results in a failure to write the gltf. - return false; - } - filename = GetBaseFilename(decoded_uri); - ext = GetFilePathExtension(filename); - } else if (image.bufferView != -1) { - // If there's no URI and the data exists in a buffer, - // don't change properties or write images - } else if (image.name.size()) { - ext = MimeToExt(image.mimeType); - // Otherwise use name as filename - filename = image.name + "." + ext; - } else { - ext = MimeToExt(image.mimeType); - // Fallback to index of image as filename - filename = std::to_string(index) + "." + ext; - } - - // If callback is set and image data exists, modify image data object. If - // image data does not exist, this is not considered a failure and the - // original uri should be maintained. - bool imageWritten = false; - if (*WriteImageData != nullptr && !filename.empty() && !image.image.empty()) { - imageWritten = (*WriteImageData)(&baseDir, &filename, &image, embedImages, - uri_cb, out_uri, user_data); - if (!imageWritten) { - return false; - } - } - - // Use the original uri if the image was not written. - if (!imageWritten) { - *out_uri = image.uri; - } - - return true; -} - -bool IsDataURI(const std::string &in) { - std::string header = "data:application/octet-stream;base64,"; - if (in.find(header) == 0) { - return true; - } - - header = "data:image/jpeg;base64,"; - if (in.find(header) == 0) { - return true; - } - - header = "data:image/png;base64,"; - if (in.find(header) == 0) { - return true; - } - - header = "data:image/bmp;base64,"; - if (in.find(header) == 0) { - return true; - } - - header = "data:image/gif;base64,"; - if (in.find(header) == 0) { - return true; - } - - header = "data:text/plain;base64,"; - if (in.find(header) == 0) { - return true; - } - - header = "data:application/gltf-buffer;base64,"; - if (in.find(header) == 0) { - return true; - } - - return false; -} - -bool DecodeDataURI(std::vector *out, std::string &mime_type, - const std::string &in, size_t reqBytes, bool checkSize) { - std::string header = "data:application/octet-stream;base64,"; - std::string data; - if (in.find(header) == 0) { - data = base64_decode(in.substr(header.size())); // cut mime string. - } - - if (data.empty()) { - header = "data:image/jpeg;base64,"; - if (in.find(header) == 0) { - mime_type = "image/jpeg"; - data = base64_decode(in.substr(header.size())); // cut mime string. - } - } - - if (data.empty()) { - header = "data:image/png;base64,"; - if (in.find(header) == 0) { - mime_type = "image/png"; - data = base64_decode(in.substr(header.size())); // cut mime string. - } - } - - if (data.empty()) { - header = "data:image/bmp;base64,"; - if (in.find(header) == 0) { - mime_type = "image/bmp"; - data = base64_decode(in.substr(header.size())); // cut mime string. - } - } - - if (data.empty()) { - header = "data:image/gif;base64,"; - if (in.find(header) == 0) { - mime_type = "image/gif"; - data = base64_decode(in.substr(header.size())); // cut mime string. - } - } - - if (data.empty()) { - header = "data:text/plain;base64,"; - if (in.find(header) == 0) { - mime_type = "text/plain"; - data = base64_decode(in.substr(header.size())); - } - } - - if (data.empty()) { - header = "data:application/gltf-buffer;base64,"; - if (in.find(header) == 0) { - data = base64_decode(in.substr(header.size())); - } - } - - // TODO(syoyo): Allow empty buffer? #229 - if (data.empty()) { - return false; - } - - if (checkSize) { - if (data.size() != reqBytes) { - return false; - } - out->resize(reqBytes); - } else { - out->resize(data.size()); - } - std::copy(data.begin(), data.end(), out->begin()); - return true; -} - -namespace detail { -bool GetInt(const detail::json &o, int &val) { -#ifdef TINYGLTF_USE_RAPIDJSON - if (!o.IsDouble()) { - if (o.IsInt()) { - val = o.GetInt(); - return true; - } else if (o.IsUint()) { - val = static_cast(o.GetUint()); - return true; - } else if (o.IsInt64()) { - val = static_cast(o.GetInt64()); - return true; - } else if (o.IsUint64()) { - val = static_cast(o.GetUint64()); - return true; - } - } - - return false; -#else - auto type = o.type(); - - if ((type == detail::json::value_t::number_integer) || - (type == detail::json::value_t::number_unsigned)) { - val = static_cast(o.get()); - return true; - } - - return false; -#endif -} - -#ifdef TINYGLTF_USE_RAPIDJSON -bool GetDouble(const detail::json &o, double &val) { - if (o.IsDouble()) { - val = o.GetDouble(); - return true; - } - - return false; -} -#endif - -bool GetNumber(const detail::json &o, double &val) { -#ifdef TINYGLTF_USE_RAPIDJSON - if (o.IsNumber()) { - val = o.GetDouble(); - return true; - } - - return false; -#else - if (o.is_number()) { - val = o.get(); - return true; - } - - return false; -#endif -} - -bool GetString(const detail::json &o, std::string &val) { -#ifdef TINYGLTF_USE_RAPIDJSON - if (o.IsString()) { - val = o.GetString(); - return true; - } - - return false; -#else - if (o.type() == detail::json::value_t::string) { - val = o.get(); - return true; - } - - return false; -#endif -} - -bool IsArray(const detail::json &o) { -#ifdef TINYGLTF_USE_RAPIDJSON - return o.IsArray(); -#else - return o.is_array(); -#endif -} - -detail::json_const_array_iterator ArrayBegin(const detail::json &o) { -#ifdef TINYGLTF_USE_RAPIDJSON - return o.Begin(); -#else - return o.begin(); -#endif -} - -detail::json_const_array_iterator ArrayEnd(const detail::json &o) { -#ifdef TINYGLTF_USE_RAPIDJSON - return o.End(); -#else - return o.end(); -#endif -} - -bool IsObject(const detail::json &o) { -#ifdef TINYGLTF_USE_RAPIDJSON - return o.IsObject(); -#else - return o.is_object(); -#endif -} - -detail::json_const_iterator ObjectBegin(const detail::json &o) { -#ifdef TINYGLTF_USE_RAPIDJSON - return o.MemberBegin(); -#else - return o.begin(); -#endif -} - -detail::json_const_iterator ObjectEnd(const detail::json &o) { -#ifdef TINYGLTF_USE_RAPIDJSON - return o.MemberEnd(); -#else - return o.end(); -#endif -} - -// Making this a const char* results in a pointer to a temporary when -// TINYGLTF_USE_RAPIDJSON is off. -std::string GetKey(detail::json_const_iterator &it) { -#ifdef TINYGLTF_USE_RAPIDJSON - return it->name.GetString(); -#else - return it.key().c_str(); -#endif -} - -bool FindMember(const detail::json &o, const char *member, detail::json_const_iterator &it) { -#ifdef TINYGLTF_USE_RAPIDJSON - if (!o.IsObject()) { - return false; - } - it = o.FindMember(member); - return it != o.MemberEnd(); -#else - it = o.find(member); - return it != o.end(); -#endif -} - -const detail::json &GetValue(detail::json_const_iterator &it) { -#ifdef TINYGLTF_USE_RAPIDJSON - return it->value; -#else - return it.value(); -#endif -} - -std::string JsonToString(const detail::json &o, int spacing = -1) { -#ifdef TINYGLTF_USE_RAPIDJSON - using namespace rapidjson; - StringBuffer buffer; - if (spacing == -1) { - Writer writer(buffer); - // TODO: Better error handling. - // https://github.com/syoyo/tinygltf/issues/332 - if (!o.Accept(writer)) { - return "tiny_gltf::JsonToString() failed rapidjson conversion"; - } - } else { - PrettyWriter writer(buffer); - writer.SetIndent(' ', uint32_t(spacing)); - if (!o.Accept(writer)) { - return "tiny_gltf::JsonToString() failed rapidjson conversion"; - } - } - return buffer.GetString(); -#else - return o.dump(spacing); -#endif -} - -} // namespace - -static bool ParseJsonAsValue(Value *ret, const detail::json &o) { - Value val{}; -#ifdef TINYGLTF_USE_RAPIDJSON - using rapidjson::Type; - switch (o.GetType()) { - case Type::kObjectType: { - Value::Object value_object; - for (auto it = o.MemberBegin(); it != o.MemberEnd(); ++it) { - Value entry; - ParseJsonAsValue(&entry, it->value); - if (entry.Type() != NULL_TYPE) - value_object.emplace(detail::GetKey(it), std::move(entry)); - } - if (value_object.size() > 0) val = Value(std::move(value_object)); - } break; - case Type::kArrayType: { - Value::Array value_array; - value_array.reserve(o.Size()); - for (auto it = o.Begin(); it != o.End(); ++it) { - Value entry; - ParseJsonAsValue(&entry, *it); - if (entry.Type() != NULL_TYPE) - value_array.emplace_back(std::move(entry)); - } - if (value_array.size() > 0) val = Value(std::move(value_array)); - } break; - case Type::kStringType: - val = Value(std::string(o.GetString())); - break; - case Type::kFalseType: - case Type::kTrueType: - val = Value(o.GetBool()); - break; - case Type::kNumberType: - if (!o.IsDouble()) { - int i = 0; - detail::GetInt(o, i); - val = Value(i); - } else { - double d = 0.0; - detail::GetDouble(o, d); - val = Value(d); - } - break; - case Type::kNullType: - break; - // all types are covered, so no `case default` - } -#else - switch (o.type()) { - case detail::json::value_t::object: { - Value::Object value_object; - for (auto it = o.begin(); it != o.end(); it++) { - Value entry; - ParseJsonAsValue(&entry, it.value()); - if (entry.Type() != NULL_TYPE) - value_object.emplace(it.key(), std::move(entry)); - } - if (value_object.size() > 0) val = Value(std::move(value_object)); - } break; - case detail::json::value_t::array: { - Value::Array value_array; - value_array.reserve(o.size()); - for (auto it = o.begin(); it != o.end(); it++) { - Value entry; - ParseJsonAsValue(&entry, it.value()); - if (entry.Type() != NULL_TYPE) - value_array.emplace_back(std::move(entry)); - } - if (value_array.size() > 0) val = Value(std::move(value_array)); - } break; - case detail::json::value_t::string: - val = Value(o.get()); - break; - case detail::json::value_t::boolean: - val = Value(o.get()); - break; - case detail::json::value_t::number_integer: - case detail::json::value_t::number_unsigned: - val = Value(static_cast(o.get())); - break; - case detail::json::value_t::number_float: - val = Value(o.get()); - break; - case detail::json::value_t::null: - case detail::json::value_t::discarded: - case detail::json::value_t::binary: - // default: - break; - } -#endif - const bool isNotNull = val.Type() != NULL_TYPE; - - if (ret) *ret = std::move(val); - - return isNotNull; -} - -static bool ParseExtrasProperty(Value *ret, const detail::json &o) { - detail::json_const_iterator it; - if (!detail::FindMember(o, "extras", it)) { - return false; - } - - return ParseJsonAsValue(ret, detail::GetValue(it)); -} - -static bool ParseBooleanProperty(bool *ret, std::string *err, const detail::json &o, - const std::string &property, - const bool required, - const std::string &parent_node = "") { - detail::json_const_iterator it; - if (!detail::FindMember(o, property.c_str(), it)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is missing"; - if (!parent_node.empty()) { - (*err) += " in " + parent_node; - } - (*err) += ".\n"; - } - } - return false; - } - - auto &value = detail::GetValue(it); - - bool isBoolean; - bool boolValue = false; -#ifdef TINYGLTF_USE_RAPIDJSON - isBoolean = value.IsBool(); - if (isBoolean) { - boolValue = value.GetBool(); - } -#else - isBoolean = value.is_boolean(); - if (isBoolean) { - boolValue = value.get(); - } -#endif - if (!isBoolean) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is not a bool type.\n"; - } - } - return false; - } - - if (ret) { - (*ret) = boolValue; - } - - return true; -} - -static bool ParseIntegerProperty(int *ret, std::string *err, const detail::json &o, - const std::string &property, - const bool required, - const std::string &parent_node = "") { - detail::json_const_iterator it; - if (!detail::FindMember(o, property.c_str(), it)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is missing"; - if (!parent_node.empty()) { - (*err) += " in " + parent_node; - } - (*err) += ".\n"; - } - } - return false; - } - - int intValue; - bool isInt = detail::GetInt(detail::GetValue(it), intValue); - if (!isInt) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is not an integer type.\n"; - } - } - return false; - } - - if (ret) { - (*ret) = intValue; - } - - return true; -} - -static bool ParseUnsignedProperty(size_t *ret, std::string *err, const detail::json &o, - const std::string &property, - const bool required, - const std::string &parent_node = "") { - detail::json_const_iterator it; - if (!detail::FindMember(o, property.c_str(), it)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is missing"; - if (!parent_node.empty()) { - (*err) += " in " + parent_node; - } - (*err) += ".\n"; - } - } - return false; - } - - auto &value = detail::GetValue(it); - - size_t uValue = 0; - bool isUValue; -#ifdef TINYGLTF_USE_RAPIDJSON - isUValue = false; - if (value.IsUint()) { - uValue = value.GetUint(); - isUValue = true; - } else if (value.IsUint64()) { - uValue = value.GetUint64(); - isUValue = true; - } -#else - isUValue = value.is_number_unsigned(); - if (isUValue) { - uValue = value.get(); - } -#endif - if (!isUValue) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is not a positive integer.\n"; - } - } - return false; - } - - if (ret) { - (*ret) = uValue; - } - - return true; -} - -static bool ParseNumberProperty(double *ret, std::string *err, const detail::json &o, - const std::string &property, - const bool required, - const std::string &parent_node = "") { - detail::json_const_iterator it; - - if (!detail::FindMember(o, property.c_str(), it)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is missing"; - if (!parent_node.empty()) { - (*err) += " in " + parent_node; - } - (*err) += ".\n"; - } - } - return false; - } - - double numberValue; - bool isNumber = detail::GetNumber(detail::GetValue(it), numberValue); - - if (!isNumber) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is not a number type.\n"; - } - } - return false; - } - - if (ret) { - (*ret) = numberValue; - } - - return true; -} - -static bool ParseNumberArrayProperty(std::vector *ret, std::string *err, - const detail::json &o, const std::string &property, - bool required, - const std::string &parent_node = "") { - detail::json_const_iterator it; - if (!detail::FindMember(o, property.c_str(), it)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is missing"; - if (!parent_node.empty()) { - (*err) += " in " + parent_node; - } - (*err) += ".\n"; - } - } - return false; - } - - if (!detail::IsArray(detail::GetValue(it))) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is not an array"; - if (!parent_node.empty()) { - (*err) += " in " + parent_node; - } - (*err) += ".\n"; - } - } - return false; - } - - ret->clear(); - auto end = detail::ArrayEnd(detail::GetValue(it)); - for (auto i = detail::ArrayBegin(detail::GetValue(it)); i != end; ++i) { - double numberValue; - const bool isNumber = detail::GetNumber(*i, numberValue); - if (!isNumber) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is not a number.\n"; - if (!parent_node.empty()) { - (*err) += " in " + parent_node; - } - (*err) += ".\n"; - } - } - return false; - } - ret->push_back(numberValue); - } - - return true; -} - -static bool ParseIntegerArrayProperty(std::vector *ret, std::string *err, - const detail::json &o, - const std::string &property, - bool required, - const std::string &parent_node = "") { - detail::json_const_iterator it; - if (!detail::FindMember(o, property.c_str(), it)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is missing"; - if (!parent_node.empty()) { - (*err) += " in " + parent_node; - } - (*err) += ".\n"; - } - } - return false; - } - - if (!detail::IsArray(detail::GetValue(it))) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is not an array"; - if (!parent_node.empty()) { - (*err) += " in " + parent_node; - } - (*err) += ".\n"; - } - } - return false; - } - - ret->clear(); - auto end = detail::ArrayEnd(detail::GetValue(it)); - for (auto i = detail::ArrayBegin(detail::GetValue(it)); i != end; ++i) { - int numberValue; - bool isNumber = detail::GetInt(*i, numberValue); - if (!isNumber) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is not an integer type.\n"; - if (!parent_node.empty()) { - (*err) += " in " + parent_node; - } - (*err) += ".\n"; - } - } - return false; - } - ret->push_back(numberValue); - } - - return true; -} - -static bool ParseStringProperty( - std::string *ret, std::string *err, const detail::json &o, - const std::string &property, bool required, - const std::string &parent_node = std::string()) { - detail::json_const_iterator it; - if (!detail::FindMember(o, property.c_str(), it)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is missing"; - if (parent_node.empty()) { - (*err) += ".\n"; - } else { - (*err) += " in `" + parent_node + "'.\n"; - } - } - } - return false; - } - - std::string strValue; - if (!detail::GetString(detail::GetValue(it), strValue)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is not a string type.\n"; - } - } - return false; - } - - if (ret) { - (*ret) = std::move(strValue); - } - - return true; -} - -static bool ParseStringIntegerProperty(std::map *ret, - std::string *err, const detail::json &o, - const std::string &property, - bool required, - const std::string &parent = "") { - detail::json_const_iterator it; - if (!detail::FindMember(o, property.c_str(), it)) { - if (required) { - if (err) { - if (!parent.empty()) { - (*err) += - "'" + property + "' property is missing in " + parent + ".\n"; - } else { - (*err) += "'" + property + "' property is missing.\n"; - } - } - } - return false; - } - - const detail::json &dict = detail::GetValue(it); - - // Make sure we are dealing with an object / dictionary. - if (!detail::IsObject(dict)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is not an object.\n"; - } - } - return false; - } - - ret->clear(); - - detail::json_const_iterator dictIt(detail::ObjectBegin(dict)); - detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict)); - - for (; dictIt != dictItEnd; ++dictIt) { - int intVal; - if (!detail::GetInt(detail::GetValue(dictIt), intVal)) { - if (required) { - if (err) { - (*err) += "'" + property + "' value is not an integer type.\n"; - } - } - return false; - } - - // Insert into the list. - (*ret)[detail::GetKey(dictIt)] = intVal; - } - return true; -} - -static bool ParseJSONProperty(std::map *ret, - std::string *err, const detail::json &o, - const std::string &property, bool required) { - detail::json_const_iterator it; - if (!detail::FindMember(o, property.c_str(), it)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is missing. \n'"; - } - } - return false; - } - - const detail::json &obj = detail::GetValue(it); - - if (!detail::IsObject(obj)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is not a JSON object.\n"; - } - } - return false; - } - - ret->clear(); - - detail::json_const_iterator it2(detail::ObjectBegin(obj)); - detail::json_const_iterator itEnd(detail::ObjectEnd(obj)); - for (; it2 != itEnd; ++it2) { - double numVal; - if (detail::GetNumber(detail::GetValue(it2), numVal)) - ret->emplace(std::string(detail::GetKey(it2)), numVal); - } - - return true; -} - -static bool ParseParameterProperty(Parameter *param, std::string *err, - const detail::json &o, const std::string &prop, - bool required) { - // A parameter value can either be a string or an array of either a boolean or - // a number. Booleans of any kind aren't supported here. Granted, it - // complicates the Parameter structure and breaks it semantically in the sense - // that the client probably works off the assumption that if the string is - // empty the vector is used, etc. Would a tagged union work? - if (ParseStringProperty(¶m->string_value, err, o, prop, false)) { - // Found string property. - return true; - } else if (ParseNumberArrayProperty(¶m->number_array, err, o, prop, - false)) { - // Found a number array. - return true; - } else if (ParseNumberProperty(¶m->number_value, err, o, prop, false)) { - param->has_number_value = true; - return true; - } else if (ParseJSONProperty(¶m->json_double_value, err, o, prop, - false)) { - return true; - } else if (ParseBooleanProperty(¶m->bool_value, err, o, prop, false)) { - return true; - } else { - if (required) { - if (err) { - (*err) += "parameter must be a string or number / number array.\n"; - } - } - return false; - } -} - -static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err, - const detail::json &o) { - (void)err; - - detail::json_const_iterator it; - if (!detail::FindMember(o, "extensions", it)) { - return false; - } - - auto &obj = detail::GetValue(it); - if (!detail::IsObject(obj)) { - return false; - } - ExtensionMap extensions; - detail::json_const_iterator extIt = detail::ObjectBegin(obj); // it.value().begin(); - detail::json_const_iterator extEnd = detail::ObjectEnd(obj); - for (; extIt != extEnd; ++extIt) { - auto &itObj = detail::GetValue(extIt); - if (!detail::IsObject(itObj)) continue; - std::string key(detail::GetKey(extIt)); - if (!ParseJsonAsValue(&extensions[key], itObj)) { - if (!key.empty()) { - // create empty object so that an extension object is still of type - // object - extensions[key] = Value{Value::Object{}}; - } - } - } - if (ret) { - (*ret) = std::move(extensions); - } - return true; -} - -static bool ParseAsset(Asset *asset, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - ParseStringProperty(&asset->version, err, o, "version", true, "Asset"); - ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset"); - ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset"); - ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset"); - - ParseExtensionsProperty(&asset->extensions, err, o); - - // Unity exporter version is added as extra here - ParseExtrasProperty(&(asset->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - asset->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - asset->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - return true; -} - -static bool ParseImage(Image *image, const int image_idx, std::string *err, - std::string *warn, const detail::json &o, - bool store_original_json_for_extras_and_extensions, - const std::string &basedir, const size_t max_file_size, FsCallbacks *fs, - const URICallbacks *uri_cb, - LoadImageDataFunction *LoadImageData = nullptr, - void *load_image_user_data = nullptr) { - // A glTF image must either reference a bufferView or an image uri - - // schema says oneOf [`bufferView`, `uri`] - // TODO(syoyo): Check the type of each parameters. - detail::json_const_iterator it; - bool hasBufferView = detail::FindMember(o, "bufferView", it); - bool hasURI = detail::FindMember(o, "uri", it); - - ParseStringProperty(&image->name, err, o, "name", false); - - if (hasBufferView && hasURI) { - // Should not both defined. - if (err) { - (*err) += - "Only one of `bufferView` or `uri` should be defined, but both are " - "defined for image[" + - std::to_string(image_idx) + "] name = \"" + image->name + "\"\n"; - } - return false; - } - - if (!hasBufferView && !hasURI) { - if (err) { - (*err) += "Neither required `bufferView` nor `uri` defined for image[" + - std::to_string(image_idx) + "] name = \"" + image->name + - "\"\n"; - } - return false; - } - - ParseExtensionsProperty(&image->extensions, err, o); - ParseExtrasProperty(&image->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator eit; - if (detail::FindMember(o, "extensions", eit)) { - image->extensions_json_string = detail::JsonToString(detail::GetValue(eit)); - } - } - { - detail::json_const_iterator eit; - if (detail::FindMember(o, "extras", eit)) { - image->extras_json_string = detail::JsonToString(detail::GetValue(eit)); - } - } - } - - if (hasBufferView) { - int bufferView = -1; - if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) { - if (err) { - (*err) += "Failed to parse `bufferView` for image[" + - std::to_string(image_idx) + "] name = \"" + image->name + - "\"\n"; - } - return false; - } - - std::string mime_type; - ParseStringProperty(&mime_type, err, o, "mimeType", false); - - int width = 0; - ParseIntegerProperty(&width, err, o, "width", false); - - int height = 0; - ParseIntegerProperty(&height, err, o, "height", false); - - // Just only save some information here. Loading actual image data from - // bufferView is done after this `ParseImage` function. - image->bufferView = bufferView; - image->mimeType = mime_type; - image->width = width; - image->height = height; - - return true; - } - - // Parse URI & Load image data. - - std::string uri; - std::string tmp_err; - if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) { - if (err) { - (*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) + - "] name = \"" + image->name + "\".\n"; - } - return false; - } - - std::vector img; - - if (IsDataURI(uri)) { - if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) { - if (err) { - (*err) += "Failed to decode 'uri' for image[" + - std::to_string(image_idx) + "] name = \"" + image->name + - "\"\n"; - } - return false; - } - } else { - // Assume external file - // Keep texture path (for textures that cannot be decoded) - image->uri = uri; -#ifdef TINYGLTF_NO_EXTERNAL_IMAGE - return true; -#else - std::string decoded_uri; - if (!uri_cb->decode(uri, &decoded_uri, uri_cb->user_data)) { - if (warn) { - (*warn) += "Failed to decode 'uri' for image[" + - std::to_string(image_idx) + "] name = \"" + image->name + - "\"\n"; - } - - // Image loading failure is not critical to overall gltf loading. - return true; - } - - if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir, - /* required */ false, /* required bytes */ 0, - /* checksize */ false, /* max file size */ max_file_size, fs)) { - if (warn) { - (*warn) += "Failed to load external 'uri' for image[" + - std::to_string(image_idx) + "] name = \"" + decoded_uri + - "\"\n"; - } - // If the image cannot be loaded, keep uri as image->uri. - return true; - } - - if (img.empty()) { - if (warn) { - (*warn) += "Image data is empty for image[" + - std::to_string(image_idx) + "] name = \"" + image->name + - "\" \n"; - } - return false; - } -#endif - } - - if (*LoadImageData == nullptr) { - if (err) { - (*err) += "No LoadImageData callback specified.\n"; - } - return false; - } - return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0), - static_cast(img.size()), load_image_user_data); -} - -static bool ParseTexture(Texture *texture, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions, - const std::string &basedir) { - (void)basedir; - int sampler = -1; - int source = -1; - ParseIntegerProperty(&sampler, err, o, "sampler", false); - - ParseIntegerProperty(&source, err, o, "source", false); - - texture->sampler = sampler; - texture->source = source; - - ParseExtensionsProperty(&texture->extensions, err, o); - ParseExtrasProperty(&texture->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - texture->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - texture->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - ParseStringProperty(&texture->name, err, o, "name", false); - - return true; -} - -static bool ParseTextureInfo( - TextureInfo *texinfo, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - if (texinfo == nullptr) { - return false; - } - - if (!ParseIntegerProperty(&texinfo->index, err, o, "index", - /* required */ true, "TextureInfo")) { - return false; - } - - ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false); - - ParseExtensionsProperty(&texinfo->extensions, err, o); - ParseExtrasProperty(&texinfo->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - texinfo->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - texinfo->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - return true; -} - -static bool ParseNormalTextureInfo( - NormalTextureInfo *texinfo, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - if (texinfo == nullptr) { - return false; - } - - if (!ParseIntegerProperty(&texinfo->index, err, o, "index", - /* required */ true, "NormalTextureInfo")) { - return false; - } - - ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false); - ParseNumberProperty(&texinfo->scale, err, o, "scale", false); - - ParseExtensionsProperty(&texinfo->extensions, err, o); - ParseExtrasProperty(&texinfo->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - texinfo->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - texinfo->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - return true; -} - -static bool ParseOcclusionTextureInfo( - OcclusionTextureInfo *texinfo, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - if (texinfo == nullptr) { - return false; - } - - if (!ParseIntegerProperty(&texinfo->index, err, o, "index", - /* required */ true, "NormalTextureInfo")) { - return false; - } - - ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false); - ParseNumberProperty(&texinfo->strength, err, o, "strength", false); - - ParseExtensionsProperty(&texinfo->extensions, err, o); - ParseExtrasProperty(&texinfo->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - texinfo->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - texinfo->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - return true; -} - -static bool ParseBuffer(Buffer *buffer, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions, - FsCallbacks *fs, const URICallbacks *uri_cb, - const std::string &basedir, const size_t max_buffer_size, bool is_binary = false, - const unsigned char *bin_data = nullptr, - size_t bin_size = 0) { - size_t byteLength; - if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true, - "Buffer")) { - return false; - } - - // In glTF 2.0, uri is not mandatory anymore - buffer->uri.clear(); - ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer"); - - // having an empty uri for a non embedded image should not be valid - if (!is_binary && buffer->uri.empty()) { - if (err) { - (*err) += "'uri' is missing from non binary glTF file buffer.\n"; - } - } - - detail::json_const_iterator type; - if (detail::FindMember(o, "type", type)) { - std::string typeStr; - if (detail::GetString(detail::GetValue(type), typeStr)) { - if (typeStr.compare("arraybuffer") == 0) { - // buffer.type = "arraybuffer"; - } - } - } - - if (is_binary) { - // Still binary glTF accepts external dataURI. - if (!buffer->uri.empty()) { - // First try embedded data URI. - if (IsDataURI(buffer->uri)) { - std::string mime_type; - if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength, - true)) { - if (err) { - (*err) += - "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n"; - } - return false; - } - } else { - // External .bin file. - std::string decoded_uri; - if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) { - return false; - } - if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, - decoded_uri, basedir, /* required */ true, - byteLength, /* checkSize */ true, /* max_file_size */max_buffer_size, fs)) { - return false; - } - } - } else { - // load data from (embedded) binary data - - if ((bin_size == 0) || (bin_data == nullptr)) { - if (err) { - (*err) += "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n"; - } - return false; - } - - if (byteLength > bin_size) { - if (err) { - std::stringstream ss; - ss << "Invalid `byteLength'. Must be equal or less than binary size: " - "`byteLength' = " - << byteLength << ", binary size = " << bin_size << std::endl; - (*err) += ss.str(); - } - return false; - } - - // Read buffer data - buffer->data.resize(static_cast(byteLength)); - memcpy(&(buffer->data.at(0)), bin_data, static_cast(byteLength)); - } - - } else { - if (IsDataURI(buffer->uri)) { - std::string mime_type; - if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength, - true)) { - if (err) { - (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n"; - } - return false; - } - } else { - // Assume external .bin file. - std::string decoded_uri; - if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) { - return false; - } - if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri, - basedir, /* required */ true, byteLength, - /* checkSize */ true, /* max file size */max_buffer_size, fs)) { - return false; - } - } - } - - ParseStringProperty(&buffer->name, err, o, "name", false); - - ParseExtensionsProperty(&buffer->extensions, err, o); - ParseExtrasProperty(&buffer->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - buffer->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - buffer->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - return true; -} - -static bool ParseBufferView( - BufferView *bufferView, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - int buffer = -1; - if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) { - return false; - } - - size_t byteOffset = 0; - ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false); - - size_t byteLength = 1; - if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true, - "BufferView")) { - return false; - } - - size_t byteStride = 0; - if (!ParseUnsignedProperty(&byteStride, err, o, "byteStride", false)) { - // Spec says: When byteStride of referenced bufferView is not defined, it - // means that accessor elements are tightly packed, i.e., effective stride - // equals the size of the element. - // We cannot determine the actual byteStride until Accessor are parsed, thus - // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner) - byteStride = 0; - } - - if ((byteStride > 252) || ((byteStride % 4) != 0)) { - if (err) { - std::stringstream ss; - ss << "Invalid `byteStride' value. `byteStride' must be the multiple of " - "4 : " - << byteStride << std::endl; - - (*err) += ss.str(); - } - return false; - } - - int target = 0; - ParseIntegerProperty(&target, err, o, "target", false); - if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) || - (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) { - // OK - } else { - target = 0; - } - bufferView->target = target; - - ParseStringProperty(&bufferView->name, err, o, "name", false); - - ParseExtensionsProperty(&bufferView->extensions, err, o); - ParseExtrasProperty(&bufferView->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - bufferView->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - bufferView->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - bufferView->buffer = buffer; - bufferView->byteOffset = byteOffset; - bufferView->byteLength = byteLength; - bufferView->byteStride = byteStride; - return true; -} - -static bool ParseSparseAccessor(Accessor *accessor, std::string *err, - const detail::json &o) { - accessor->sparse.isSparse = true; - - int count = 0; - if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) { - return false; - } - - detail::json_const_iterator indices_iterator; - detail::json_const_iterator values_iterator; - if (!detail::FindMember(o, "indices", indices_iterator)) { - (*err) = "the sparse object of this accessor doesn't have indices"; - return false; - } - - if (!detail::FindMember(o, "values", values_iterator)) { - (*err) = "the sparse object of this accessor doesn't have values"; - return false; - } - - const detail::json &indices_obj = detail::GetValue(indices_iterator); - const detail::json &values_obj = detail::GetValue(values_iterator); - - int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0; - if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj, - "bufferView", true, "SparseAccessor")) { - return false; - } - ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset", - false); - if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType", - true, "SparseAccessor")) { - return false; - } - - int values_buffer_view = 0, values_byte_offset = 0; - if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView", - true, "SparseAccessor")) { - return false; - } - ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset", - false); - - accessor->sparse.count = count; - accessor->sparse.indices.bufferView = indices_buffer_view; - accessor->sparse.indices.byteOffset = indices_byte_offset; - accessor->sparse.indices.componentType = component_type; - accessor->sparse.values.bufferView = values_buffer_view; - accessor->sparse.values.byteOffset = values_byte_offset; - - return true; -} - -static bool ParseAccessor(Accessor *accessor, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - int bufferView = -1; - ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor"); - - size_t byteOffset = 0; - ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor"); - - bool normalized = false; - ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor"); - - size_t componentType = 0; - if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true, - "Accessor")) { - return false; - } - - size_t count = 0; - if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) { - return false; - } - - std::string type; - if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) { - return false; - } - - if (type.compare("SCALAR") == 0) { - accessor->type = TINYGLTF_TYPE_SCALAR; - } else if (type.compare("VEC2") == 0) { - accessor->type = TINYGLTF_TYPE_VEC2; - } else if (type.compare("VEC3") == 0) { - accessor->type = TINYGLTF_TYPE_VEC3; - } else if (type.compare("VEC4") == 0) { - accessor->type = TINYGLTF_TYPE_VEC4; - } else if (type.compare("MAT2") == 0) { - accessor->type = TINYGLTF_TYPE_MAT2; - } else if (type.compare("MAT3") == 0) { - accessor->type = TINYGLTF_TYPE_MAT3; - } else if (type.compare("MAT4") == 0) { - accessor->type = TINYGLTF_TYPE_MAT4; - } else { - std::stringstream ss; - ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n"; - if (err) { - (*err) += ss.str(); - } - return false; - } - - ParseStringProperty(&accessor->name, err, o, "name", false); - - accessor->minValues.clear(); - accessor->maxValues.clear(); - ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false, - "Accessor"); - - ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false, - "Accessor"); - - accessor->count = count; - accessor->bufferView = bufferView; - accessor->byteOffset = byteOffset; - accessor->normalized = normalized; - { - if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE && - componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) { - // OK - accessor->componentType = int(componentType); - } else { - std::stringstream ss; - ss << "Invalid `componentType` in accessor. Got " << componentType - << "\n"; - if (err) { - (*err) += ss.str(); - } - return false; - } - } - - ParseExtensionsProperty(&(accessor->extensions), err, o); - ParseExtrasProperty(&(accessor->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - accessor->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - accessor->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - // check if accessor has a "sparse" object: - detail::json_const_iterator iterator; - if (detail::FindMember(o, "sparse", iterator)) { - // here this accessor has a "sparse" subobject - return ParseSparseAccessor(accessor, err, detail::GetValue(iterator)); - } - - return true; -} - -#ifdef TINYGLTF_ENABLE_DRACO - -static void DecodeIndexBuffer(draco::Mesh *mesh, size_t componentSize, - std::vector &outBuffer) { - if (componentSize == 4) { - assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize); - memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0], - outBuffer.size()); - } else { - size_t faceStride = componentSize * 3; - for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) { - const draco::Mesh::Face &face = mesh->face(f); - if (componentSize == 2) { - uint16_t indices[3] = {(uint16_t)face[0].value(), - (uint16_t)face[1].value(), - (uint16_t)face[2].value()}; - memcpy(outBuffer.data() + f.value() * faceStride, &indices[0], - faceStride); - } else { - uint8_t indices[3] = {(uint8_t)face[0].value(), - (uint8_t)face[1].value(), - (uint8_t)face[2].value()}; - memcpy(outBuffer.data() + f.value() * faceStride, &indices[0], - faceStride); - } - } - } -} - -template -static bool GetAttributeForAllPoints(draco::Mesh *mesh, - const draco::PointAttribute *pAttribute, - std::vector &outBuffer) { - size_t byteOffset = 0; - T values[4] = {0, 0, 0, 0}; - for (draco::PointIndex i(0); i < mesh->num_points(); ++i) { - const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i); - if (!pAttribute->ConvertValue(val_index, pAttribute->num_components(), - values)) - return false; - - memcpy(outBuffer.data() + byteOffset, &values[0], - sizeof(T) * pAttribute->num_components()); - byteOffset += sizeof(T) * pAttribute->num_components(); - } - - return true; -} - -static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh, - const draco::PointAttribute *pAttribute, - std::vector &outBuffer) { - bool decodeResult = false; - switch (componentType) { - case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: - decodeResult = - GetAttributeForAllPoints(mesh, pAttribute, outBuffer); - break; - case TINYGLTF_COMPONENT_TYPE_BYTE: - decodeResult = - GetAttributeForAllPoints(mesh, pAttribute, outBuffer); - break; - case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: - decodeResult = - GetAttributeForAllPoints(mesh, pAttribute, outBuffer); - break; - case TINYGLTF_COMPONENT_TYPE_SHORT: - decodeResult = - GetAttributeForAllPoints(mesh, pAttribute, outBuffer); - break; - case TINYGLTF_COMPONENT_TYPE_INT: - decodeResult = - GetAttributeForAllPoints(mesh, pAttribute, outBuffer); - break; - case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: - decodeResult = - GetAttributeForAllPoints(mesh, pAttribute, outBuffer); - break; - case TINYGLTF_COMPONENT_TYPE_FLOAT: - decodeResult = - GetAttributeForAllPoints(mesh, pAttribute, outBuffer); - break; - case TINYGLTF_COMPONENT_TYPE_DOUBLE: - decodeResult = - GetAttributeForAllPoints(mesh, pAttribute, outBuffer); - break; - default: - return false; - } - - return decodeResult; -} - -static bool ParseDracoExtension(Primitive *primitive, Model *model, - std::string *err, - const Value &dracoExtensionValue) { - (void)err; - auto bufferViewValue = dracoExtensionValue.Get("bufferView"); - if (!bufferViewValue.IsInt()) return false; - auto attributesValue = dracoExtensionValue.Get("attributes"); - if (!attributesValue.IsObject()) return false; - - auto attributesObject = attributesValue.Get(); - int bufferView = bufferViewValue.Get(); - - BufferView &view = model->bufferViews[bufferView]; - Buffer &buffer = model->buffers[view.buffer]; - // BufferView has already been decoded - if (view.dracoDecoded) return true; - view.dracoDecoded = true; - - const char *bufferViewData = - reinterpret_cast(buffer.data.data() + view.byteOffset); - size_t bufferViewSize = view.byteLength; - - // decode draco - draco::DecoderBuffer decoderBuffer; - decoderBuffer.Init(bufferViewData, bufferViewSize); - draco::Decoder decoder; - auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer); - if (!decodeResult.ok()) { - return false; - } - const std::unique_ptr &mesh = decodeResult.value(); - - // create new bufferView for indices - if (primitive->indices >= 0) { - int32_t componentSize = GetComponentSizeInBytes( - model->accessors[primitive->indices].componentType); - Buffer decodedIndexBuffer; - decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize); - - DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data); - - model->buffers.emplace_back(std::move(decodedIndexBuffer)); - - BufferView decodedIndexBufferView; - decodedIndexBufferView.buffer = int(model->buffers.size() - 1); - decodedIndexBufferView.byteLength = - int(mesh->num_faces() * 3 * componentSize); - decodedIndexBufferView.byteOffset = 0; - decodedIndexBufferView.byteStride = 0; - decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER; - model->bufferViews.emplace_back(std::move(decodedIndexBufferView)); - - model->accessors[primitive->indices].bufferView = - int(model->bufferViews.size() - 1); - model->accessors[primitive->indices].count = int(mesh->num_faces() * 3); - } - - for (const auto &attribute : attributesObject) { - if (!attribute.second.IsInt()) return false; - auto primitiveAttribute = primitive->attributes.find(attribute.first); - if (primitiveAttribute == primitive->attributes.end()) return false; - - int dracoAttributeIndex = attribute.second.Get(); - const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex); - const auto componentType = - model->accessors[primitiveAttribute->second].componentType; - - // Create a new buffer for this decoded buffer - Buffer decodedBuffer; - size_t bufferSize = mesh->num_points() * pAttribute->num_components() * - GetComponentSizeInBytes(componentType); - decodedBuffer.data.resize(bufferSize); - - if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute, - decodedBuffer.data)) - return false; - - model->buffers.emplace_back(std::move(decodedBuffer)); - - BufferView decodedBufferView; - decodedBufferView.buffer = int(model->buffers.size() - 1); - decodedBufferView.byteLength = bufferSize; - decodedBufferView.byteOffset = pAttribute->byte_offset(); - decodedBufferView.byteStride = pAttribute->byte_stride(); - decodedBufferView.target = primitive->indices >= 0 - ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER - : TINYGLTF_TARGET_ARRAY_BUFFER; - model->bufferViews.emplace_back(std::move(decodedBufferView)); - - model->accessors[primitiveAttribute->second].bufferView = - int(model->bufferViews.size() - 1); - model->accessors[primitiveAttribute->second].count = - int(mesh->num_points()); - } - - return true; -} -#endif - -static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err, - const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - int material = -1; - ParseIntegerProperty(&material, err, o, "material", false); - primitive->material = material; - - int mode = TINYGLTF_MODE_TRIANGLES; - ParseIntegerProperty(&mode, err, o, "mode", false); - primitive->mode = mode; // Why only triangles were supported ? - - int indices = -1; - ParseIntegerProperty(&indices, err, o, "indices", false); - primitive->indices = indices; - if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes", - true, "Primitive")) { - return false; - } - - // Look for morph targets - detail::json_const_iterator targetsObject; - if (detail::FindMember(o, "targets", targetsObject) && - detail::IsArray(detail::GetValue(targetsObject))) { - auto targetsObjectEnd = detail::ArrayEnd(detail::GetValue(targetsObject)); - for (detail::json_const_array_iterator i = detail::ArrayBegin(detail::GetValue(targetsObject)); - i != targetsObjectEnd; ++i) { - std::map targetAttribues; - - const detail::json &dict = *i; - if (detail::IsObject(dict)) { - detail::json_const_iterator dictIt(detail::ObjectBegin(dict)); - detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict)); - - for (; dictIt != dictItEnd; ++dictIt) { - int iVal; - if (detail::GetInt(detail::GetValue(dictIt), iVal)) - targetAttribues[detail::GetKey(dictIt)] = iVal; - } - primitive->targets.emplace_back(std::move(targetAttribues)); - } - } - } - - ParseExtrasProperty(&(primitive->extras), o); - ParseExtensionsProperty(&primitive->extensions, err, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - primitive->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - primitive->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - -#ifdef TINYGLTF_ENABLE_DRACO - auto dracoExtension = - primitive->extensions.find("KHR_draco_mesh_compression"); - if (dracoExtension != primitive->extensions.end()) { - ParseDracoExtension(primitive, model, err, dracoExtension->second); - } -#else - (void)model; -#endif - - return true; -} - -static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - ParseStringProperty(&mesh->name, err, o, "name", false); - - mesh->primitives.clear(); - detail::json_const_iterator primObject; - if (detail::FindMember(o, "primitives", primObject) && - detail::IsArray(detail::GetValue(primObject))) { - detail::json_const_array_iterator primEnd = detail::ArrayEnd(detail::GetValue(primObject)); - for (detail::json_const_array_iterator i = detail::ArrayBegin(detail::GetValue(primObject)); - i != primEnd; ++i) { - Primitive primitive; - if (ParsePrimitive(&primitive, model, err, *i, - store_original_json_for_extras_and_extensions)) { - // Only add the primitive if the parsing succeeds. - mesh->primitives.emplace_back(std::move(primitive)); - } - } - } - - // Should probably check if has targets and if dimensions fit - ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false); - - ParseExtensionsProperty(&mesh->extensions, err, o); - ParseExtrasProperty(&(mesh->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - mesh->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - mesh->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - return true; -} - -static bool ParseNode(Node *node, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - ParseStringProperty(&node->name, err, o, "name", false); - - int skin = -1; - ParseIntegerProperty(&skin, err, o, "skin", false); - node->skin = skin; - - // Matrix and T/R/S are exclusive - if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) { - ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false); - ParseNumberArrayProperty(&node->scale, err, o, "scale", false); - ParseNumberArrayProperty(&node->translation, err, o, "translation", false); - } - - int camera = -1; - ParseIntegerProperty(&camera, err, o, "camera", false); - node->camera = camera; - - int mesh = -1; - ParseIntegerProperty(&mesh, err, o, "mesh", false); - node->mesh = mesh; - - node->children.clear(); - ParseIntegerArrayProperty(&node->children, err, o, "children", false); - - ParseNumberArrayProperty(&node->weights, err, o, "weights", false); - - ParseExtensionsProperty(&node->extensions, err, o); - ParseExtrasProperty(&(node->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - node->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - node->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - return true; -} - -static bool ParsePbrMetallicRoughness( - PbrMetallicRoughness *pbr, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - if (pbr == nullptr) { - return false; - } - - std::vector baseColorFactor; - if (ParseNumberArrayProperty(&baseColorFactor, err, o, "baseColorFactor", - /* required */ false)) { - if (baseColorFactor.size() != 4) { - if (err) { - (*err) += - "Array length of `baseColorFactor` parameter in " - "pbrMetallicRoughness must be 4, but got " + - std::to_string(baseColorFactor.size()) + "\n"; - } - return false; - } - pbr->baseColorFactor = baseColorFactor; - } - - { - detail::json_const_iterator it; - if (detail::FindMember(o, "baseColorTexture", it)) { - ParseTextureInfo(&pbr->baseColorTexture, err, detail::GetValue(it), - store_original_json_for_extras_and_extensions); - } - } - - { - detail::json_const_iterator it; - if (detail::FindMember(o, "metallicRoughnessTexture", it)) { - ParseTextureInfo(&pbr->metallicRoughnessTexture, err, detail::GetValue(it), - store_original_json_for_extras_and_extensions); - } - } - - ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false); - ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false); - - ParseExtensionsProperty(&pbr->extensions, err, o); - ParseExtrasProperty(&pbr->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - pbr->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - pbr->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - return true; -} - -static bool ParseMaterial(Material *material, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - ParseStringProperty(&material->name, err, o, "name", /* required */ false); - - if (ParseNumberArrayProperty(&material->emissiveFactor, err, o, - "emissiveFactor", - /* required */ false)) { - if (material->emissiveFactor.size() != 3) { - if (err) { - (*err) += - "Array length of `emissiveFactor` parameter in " - "material must be 3, but got " + - std::to_string(material->emissiveFactor.size()) + "\n"; - } - return false; - } - } else { - // fill with default values - material->emissiveFactor = {0.0, 0.0, 0.0}; - } - - ParseStringProperty(&material->alphaMode, err, o, "alphaMode", - /* required */ false); - ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff", - /* required */ false); - ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided", - /* required */ false); - - { - detail::json_const_iterator it; - if (detail::FindMember(o, "pbrMetallicRoughness", it)) { - ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err, - detail::GetValue(it), - store_original_json_for_extras_and_extensions); - } - } - - { - detail::json_const_iterator it; - if (detail::FindMember(o, "normalTexture", it)) { - ParseNormalTextureInfo(&material->normalTexture, err, detail::GetValue(it), - store_original_json_for_extras_and_extensions); - } - } - - { - detail::json_const_iterator it; - if (detail::FindMember(o, "occlusionTexture", it)) { - ParseOcclusionTextureInfo(&material->occlusionTexture, err, detail::GetValue(it), - store_original_json_for_extras_and_extensions); - } - } - - { - detail::json_const_iterator it; - if (detail::FindMember(o, "emissiveTexture", it)) { - ParseTextureInfo(&material->emissiveTexture, err, detail::GetValue(it), - store_original_json_for_extras_and_extensions); - } - } - - // Old code path. For backward compatibility, we still store material values - // as Parameter. This will create duplicated information for - // example(pbrMetallicRoughness), but should be negligible in terms of memory - // consumption. - // TODO(syoyo): Remove in the next major release. - material->values.clear(); - material->additionalValues.clear(); - - detail::json_const_iterator it(detail::ObjectBegin(o)); - detail::json_const_iterator itEnd(detail::ObjectEnd(o)); - - for (; it != itEnd; ++it) { - std::string key(detail::GetKey(it)); - if (key == "pbrMetallicRoughness") { - if (detail::IsObject(detail::GetValue(it))) { - const detail::json &values_object = detail::GetValue(it); - - detail::json_const_iterator itVal(detail::ObjectBegin(values_object)); - detail::json_const_iterator itValEnd(detail::ObjectEnd(values_object)); - - for (; itVal != itValEnd; ++itVal) { - Parameter param; - if (ParseParameterProperty(¶m, err, values_object, detail::GetKey(itVal), - false)) { - material->values.emplace(detail::GetKey(itVal), std::move(param)); - } - } - } - } else if (key == "extensions" || key == "extras") { - // done later, skip, otherwise poorly parsed contents will be saved in the - // parametermap and serialized again later - } else { - Parameter param; - if (ParseParameterProperty(¶m, err, o, key, false)) { - // names of materials have already been parsed. Putting it in this map - // doesn't correctly reflect the glTF specification - if (key != "name") - material->additionalValues.emplace(std::move(key), std::move(param)); - } - } - } - - material->extensions.clear(); - ParseExtensionsProperty(&material->extensions, err, o); - ParseExtrasProperty(&(material->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator eit; - if (detail::FindMember(o, "extensions", eit)) { - material->extensions_json_string = detail::JsonToString(detail::GetValue(eit)); - } - } - { - detail::json_const_iterator eit; - if (detail::FindMember(o, "extras", eit)) { - material->extras_json_string = detail::JsonToString(detail::GetValue(eit)); - } - } - } - - return true; -} - -static bool ParseAnimationChannel( - AnimationChannel *channel, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - int samplerIndex = -1; - int targetIndex = -1; - if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true, - "AnimationChannel")) { - if (err) { - (*err) += "`sampler` field is missing in animation channels\n"; - } - return false; - } - - detail::json_const_iterator targetIt; - if (detail::FindMember(o, "target", targetIt) && detail::IsObject(detail::GetValue(targetIt))) { - const detail::json &target_object = detail::GetValue(targetIt); - - ParseIntegerProperty(&targetIndex, err, target_object, "node", false); - - if (!ParseStringProperty(&channel->target_path, err, target_object, "path", - true)) { - if (err) { - (*err) += "`path` field is missing in animation.channels.target\n"; - } - return false; - } - ParseExtensionsProperty(&channel->target_extensions, err, target_object); - if (store_original_json_for_extras_and_extensions) { - detail::json_const_iterator it; - if (detail::FindMember(target_object, "extensions", it)) { - channel->target_extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - channel->sampler = samplerIndex; - channel->target_node = targetIndex; - - ParseExtensionsProperty(&channel->extensions, err, o); - ParseExtrasProperty(&(channel->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - channel->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - channel->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - return true; -} - -static bool ParseAnimation(Animation *animation, std::string *err, - const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator channelsIt; - if (detail::FindMember(o, "channels", channelsIt) && - detail::IsArray(detail::GetValue(channelsIt))) { - detail::json_const_array_iterator channelEnd = detail::ArrayEnd(detail::GetValue(channelsIt)); - for (detail::json_const_array_iterator i = detail::ArrayBegin(detail::GetValue(channelsIt)); - i != channelEnd; ++i) { - AnimationChannel channel; - if (ParseAnimationChannel( - &channel, err, *i, - store_original_json_for_extras_and_extensions)) { - // Only add the channel if the parsing succeeds. - animation->channels.emplace_back(std::move(channel)); - } - } - } - } - - { - detail::json_const_iterator samplerIt; - if (detail::FindMember(o, "samplers", samplerIt) && detail::IsArray(detail::GetValue(samplerIt))) { - const detail::json &sampler_array = detail::GetValue(samplerIt); - - detail::json_const_array_iterator it = detail::ArrayBegin(sampler_array); - detail::json_const_array_iterator itEnd = detail::ArrayEnd(sampler_array); - - for (; it != itEnd; ++it) { - const detail::json &s = *it; - - AnimationSampler sampler; - int inputIndex = -1; - int outputIndex = -1; - if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) { - if (err) { - (*err) += "`input` field is missing in animation.sampler\n"; - } - return false; - } - ParseStringProperty(&sampler.interpolation, err, s, "interpolation", - false); - if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) { - if (err) { - (*err) += "`output` field is missing in animation.sampler\n"; - } - return false; - } - sampler.input = inputIndex; - sampler.output = outputIndex; - ParseExtensionsProperty(&(sampler.extensions), err, o); - ParseExtrasProperty(&(sampler.extras), s); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator eit; - if (detail::FindMember(o, "extensions", eit)) { - sampler.extensions_json_string = detail::JsonToString(detail::GetValue(eit)); - } - } - { - detail::json_const_iterator eit; - if (detail::FindMember(o, "extras", eit)) { - sampler.extras_json_string = detail::JsonToString(detail::GetValue(eit)); - } - } - } - - animation->samplers.emplace_back(std::move(sampler)); - } - } - } - - ParseStringProperty(&animation->name, err, o, "name", false); - - ParseExtensionsProperty(&animation->extensions, err, o); - ParseExtrasProperty(&(animation->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - animation->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - animation->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - return true; -} - -static bool ParseSampler(Sampler *sampler, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - ParseStringProperty(&sampler->name, err, o, "name", false); - - int minFilter = -1; - int magFilter = -1; - int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT; - int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT; - // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; - ParseIntegerProperty(&minFilter, err, o, "minFilter", false); - ParseIntegerProperty(&magFilter, err, o, "magFilter", false); - ParseIntegerProperty(&wrapS, err, o, "wrapS", false); - ParseIntegerProperty(&wrapT, err, o, "wrapT", false); - // ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf - // extension - - // TODO(syoyo): Check the value is allowed one. - // (e.g. we allow 9728(NEAREST), but don't allow 9727) - - sampler->minFilter = minFilter; - sampler->magFilter = magFilter; - sampler->wrapS = wrapS; - sampler->wrapT = wrapT; - // sampler->wrapR = wrapR; - - ParseExtensionsProperty(&(sampler->extensions), err, o); - ParseExtrasProperty(&(sampler->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - sampler->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - sampler->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - return true; -} - -static bool ParseSkin(Skin *skin, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - ParseStringProperty(&skin->name, err, o, "name", false, "Skin"); - - std::vector joints; - if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) { - return false; - } - skin->joints = std::move(joints); - - int skeleton = -1; - ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin"); - skin->skeleton = skeleton; - - int invBind = -1; - ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin"); - skin->inverseBindMatrices = invBind; - - ParseExtensionsProperty(&(skin->extensions), err, o); - ParseExtrasProperty(&(skin->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - skin->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - skin->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - return true; -} - -static bool ParsePerspectiveCamera( - PerspectiveCamera *camera, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - double yfov = 0.0; - if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) { - return false; - } - - double znear = 0.0; - if (!ParseNumberProperty(&znear, err, o, "znear", true, - "PerspectiveCamera")) { - return false; - } - - double aspectRatio = 0.0; // = invalid - ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false, - "PerspectiveCamera"); - - double zfar = 0.0; // = invalid - ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera"); - - camera->aspectRatio = aspectRatio; - camera->zfar = zfar; - camera->yfov = yfov; - camera->znear = znear; - - ParseExtensionsProperty(&camera->extensions, err, o); - ParseExtrasProperty(&(camera->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - camera->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - camera->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - // TODO(syoyo): Validate parameter values. - - return true; -} - -static bool ParseSpotLight(SpotLight *light, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false); - ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false); - - ParseExtensionsProperty(&light->extensions, err, o); - ParseExtrasProperty(&light->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - light->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - light->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - // TODO(syoyo): Validate parameter values. - - return true; -} - -static bool ParseOrthographicCamera( - OrthographicCamera *camera, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - double xmag = 0.0; - if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) { - return false; - } - - double ymag = 0.0; - if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) { - return false; - } - - double zfar = 0.0; - if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) { - return false; - } - - double znear = 0.0; - if (!ParseNumberProperty(&znear, err, o, "znear", true, - "OrthographicCamera")) { - return false; - } - - ParseExtensionsProperty(&camera->extensions, err, o); - ParseExtrasProperty(&(camera->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - camera->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - camera->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - camera->xmag = xmag; - camera->ymag = ymag; - camera->zfar = zfar; - camera->znear = znear; - - // TODO(syoyo): Validate parameter values. - - return true; -} - -static bool ParseCamera(Camera *camera, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) { - return false; - } - - if (camera->type.compare("orthographic") == 0) { - detail::json_const_iterator orthoIt; - if (!detail::FindMember(o, "orthographic", orthoIt)) { - if (err) { - std::stringstream ss; - ss << "Orthographic camera description not found." << std::endl; - (*err) += ss.str(); - } - return false; - } - - const detail::json &v = detail::GetValue(orthoIt); - if (!detail::IsObject(v)) { - if (err) { - std::stringstream ss; - ss << "\"orthographic\" is not a JSON object." << std::endl; - (*err) += ss.str(); - } - return false; - } - - if (!ParseOrthographicCamera( - &camera->orthographic, err, v, - store_original_json_for_extras_and_extensions)) { - return false; - } - } else if (camera->type.compare("perspective") == 0) { - detail::json_const_iterator perspIt; - if (!detail::FindMember(o, "perspective", perspIt)) { - if (err) { - std::stringstream ss; - ss << "Perspective camera description not found." << std::endl; - (*err) += ss.str(); - } - return false; - } - - const detail::json &v = detail::GetValue(perspIt); - if (!detail::IsObject(v)) { - if (err) { - std::stringstream ss; - ss << "\"perspective\" is not a JSON object." << std::endl; - (*err) += ss.str(); - } - return false; - } - - if (!ParsePerspectiveCamera( - &camera->perspective, err, v, - store_original_json_for_extras_and_extensions)) { - return false; - } - } else { - if (err) { - std::stringstream ss; - ss << "Invalid camera type: \"" << camera->type - << "\". Must be \"perspective\" or \"orthographic\"" << std::endl; - (*err) += ss.str(); - } - return false; - } - - ParseStringProperty(&camera->name, err, o, "name", false); - - ParseExtensionsProperty(&camera->extensions, err, o); - ParseExtrasProperty(&(camera->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - camera->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - camera->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - return true; -} - -static bool ParseLight(Light *light, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - if (!ParseStringProperty(&light->type, err, o, "type", true)) { - return false; - } - - if (light->type == "spot") { - detail::json_const_iterator spotIt; - if (!detail::FindMember(o, "spot", spotIt)) { - if (err) { - std::stringstream ss; - ss << "Spot light description not found." << std::endl; - (*err) += ss.str(); - } - return false; - } - - const detail::json &v = detail::GetValue(spotIt); - if (!detail::IsObject(v)) { - if (err) { - std::stringstream ss; - ss << "\"spot\" is not a JSON object." << std::endl; - (*err) += ss.str(); - } - return false; - } - - if (!ParseSpotLight(&light->spot, err, v, - store_original_json_for_extras_and_extensions)) { - return false; - } - } - - ParseStringProperty(&light->name, err, o, "name", false); - ParseNumberArrayProperty(&light->color, err, o, "color", false); - ParseNumberProperty(&light->range, err, o, "range", false); - ParseNumberProperty(&light->intensity, err, o, "intensity", false); - ParseExtensionsProperty(&light->extensions, err, o); - ParseExtrasProperty(&(light->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - light->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - light->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - return true; -} - -bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, - const char *json_str, - unsigned int json_str_length, - const std::string &base_dir, - unsigned int check_sections) { - if (json_str_length < 4) { - if (err) { - (*err) = "JSON string too short.\n"; - } - return false; - } - - detail::JsonDocument v; - -#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \ - defined(_CPPUNWIND)) && \ - !defined(TINYGLTF_NOEXCEPTION) - try { - detail::JsonParse(v, json_str, json_str_length, true); - - } catch (const std::exception &e) { - if (err) { - (*err) = e.what(); - } - return false; - } -#else - { - detail::JsonParse(v, json_str, json_str_length); - - if (!detail::IsObject(v)) { - // Assume parsing was failed. - if (err) { - (*err) = "Failed to parse JSON object\n"; - } - return false; - } - } -#endif - - if (!detail::IsObject(v)) { - // root is not an object. - if (err) { - (*err) = "Root element is not a JSON object\n"; - } - return false; - } - - { - bool version_found = false; - detail::json_const_iterator it; - if (detail::FindMember(v, "asset", it) && detail::IsObject(detail::GetValue(it))) { - auto &itObj = detail::GetValue(it); - detail::json_const_iterator version_it; - std::string versionStr; - if (detail::FindMember(itObj, "version", version_it) && - detail::GetString(detail::GetValue(version_it), versionStr)) { - version_found = true; - } - } - if (version_found) { - // OK - } else if (check_sections & REQUIRE_VERSION) { - if (err) { - (*err) += "\"asset\" object not found in .gltf or not an object type\n"; - } - return false; - } - } - - // scene is not mandatory. - // FIXME Maybe a better way to handle it than removing the code - - auto IsArrayMemberPresent = [](const detail::json &_v, const char *name) -> bool { - detail::json_const_iterator it; - return detail::FindMember(_v, name, it) && detail::IsArray(detail::GetValue(it)); - }; - - { - if ((check_sections & REQUIRE_SCENES) && - !IsArrayMemberPresent(v, "scenes")) { - if (err) { - (*err) += "\"scenes\" object not found in .gltf or not an array type\n"; - } - return false; - } - } - - { - if ((check_sections & REQUIRE_NODES) && !IsArrayMemberPresent(v, "nodes")) { - if (err) { - (*err) += "\"nodes\" object not found in .gltf\n"; - } - return false; - } - } - - { - if ((check_sections & REQUIRE_ACCESSORS) && - !IsArrayMemberPresent(v, "accessors")) { - if (err) { - (*err) += "\"accessors\" object not found in .gltf\n"; - } - return false; - } - } - - { - if ((check_sections & REQUIRE_BUFFERS) && - !IsArrayMemberPresent(v, "buffers")) { - if (err) { - (*err) += "\"buffers\" object not found in .gltf\n"; - } - return false; - } - } - - { - if ((check_sections & REQUIRE_BUFFER_VIEWS) && - !IsArrayMemberPresent(v, "bufferViews")) { - if (err) { - (*err) += "\"bufferViews\" object not found in .gltf\n"; - } - return false; - } - } - - model->buffers.clear(); - model->bufferViews.clear(); - model->accessors.clear(); - model->meshes.clear(); - model->cameras.clear(); - model->nodes.clear(); - model->extensionsUsed.clear(); - model->extensionsRequired.clear(); - model->extensions.clear(); - model->defaultScene = -1; - - // 1. Parse Asset - { - detail::json_const_iterator it; - if (detail::FindMember(v, "asset", it) && detail::IsObject(detail::GetValue(it))) { - const detail::json &root = detail::GetValue(it); - - ParseAsset(&model->asset, err, root, - store_original_json_for_extras_and_extensions_); - } - } - -#ifdef TINYGLTF_USE_CPP14 - auto ForEachInArray = [](const detail::json &_v, const char *member, - const auto &cb) -> bool -#else - // The std::function<> implementation can be less efficient because it will - // allocate heap when the size of the captured lambda is above 16 bytes with - // clang and gcc, but it does not require C++14. - auto ForEachInArray = [](const detail::json &_v, const char *member, - const std::function &cb) -> bool -#endif - { - detail::json_const_iterator itm; - if (detail::FindMember(_v, member, itm) && detail::IsArray(detail::GetValue(itm))) { - const detail::json &root = detail::GetValue(itm); - auto it = detail::ArrayBegin(root); - auto end = detail::ArrayEnd(root); - for (; it != end; ++it) { - if (!cb(*it)) return false; - } - } - return true; - }; - - // 2. Parse extensionUsed - { - ForEachInArray(v, "extensionsUsed", [&](const detail::json &o) { - std::string str; - detail::GetString(o, str); - model->extensionsUsed.emplace_back(std::move(str)); - return true; - }); - } - - { - ForEachInArray(v, "extensionsRequired", [&](const detail::json &o) { - std::string str; - detail::GetString(o, str); - model->extensionsRequired.emplace_back(std::move(str)); - return true; - }); - } - - // 3. Parse Buffer - { - bool success = ForEachInArray(v, "buffers", [&](const detail::json &o) { - if (!detail::IsObject(o)) { - if (err) { - (*err) += "`buffers' does not contain an JSON object."; - } - return false; - } - Buffer buffer; - if (!ParseBuffer(&buffer, err, o, - store_original_json_for_extras_and_extensions_, &fs, - &uri_cb, base_dir, max_external_file_size_, is_binary_, bin_data_, bin_size_)) { - return false; - } - - model->buffers.emplace_back(std::move(buffer)); - return true; - }); - - if (!success) { - return false; - } - } - // 4. Parse BufferView - { - bool success = ForEachInArray(v, "bufferViews", [&](const detail::json &o) { - if (!detail::IsObject(o)) { - if (err) { - (*err) += "`bufferViews' does not contain an JSON object."; - } - return false; - } - BufferView bufferView; - if (!ParseBufferView(&bufferView, err, o, - store_original_json_for_extras_and_extensions_)) { - return false; - } - - model->bufferViews.emplace_back(std::move(bufferView)); - return true; - }); - - if (!success) { - return false; - } - } - - // 5. Parse Accessor - { - bool success = ForEachInArray(v, "accessors", [&](const detail::json &o) { - if (!detail::IsObject(o)) { - if (err) { - (*err) += "`accessors' does not contain an JSON object."; - } - return false; - } - Accessor accessor; - if (!ParseAccessor(&accessor, err, o, - store_original_json_for_extras_and_extensions_)) { - return false; - } - - model->accessors.emplace_back(std::move(accessor)); - return true; - }); - - if (!success) { - return false; - } - } - - // 6. Parse Mesh - { - bool success = ForEachInArray(v, "meshes", [&](const detail::json &o) { - if (!detail::IsObject(o)) { - if (err) { - (*err) += "`meshes' does not contain an JSON object."; - } - return false; - } - Mesh mesh; - if (!ParseMesh(&mesh, model, err, o, - store_original_json_for_extras_and_extensions_)) { - return false; - } - - model->meshes.emplace_back(std::move(mesh)); - return true; - }); - - if (!success) { - return false; - } - } - - // Assign missing bufferView target types - // - Look for missing Mesh indices - // - Look for missing Mesh attributes - for (auto &mesh : model->meshes) { - for (auto &primitive : mesh.primitives) { - if (primitive.indices > - -1) // has indices from parsing step, must be Element Array Buffer - { - if (size_t(primitive.indices) >= model->accessors.size()) { - if (err) { - (*err) += "primitive indices accessor out of bounds"; - } - return false; - } - - auto bufferView = - model->accessors[size_t(primitive.indices)].bufferView; - if (bufferView < 0 || size_t(bufferView) >= model->bufferViews.size()) { - if (err) { - (*err) += "accessor[" + std::to_string(primitive.indices) + - "] invalid bufferView"; - } - return false; - } - - model->bufferViews[size_t(bufferView)].target = - TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER; - // we could optionally check if accessors' bufferView type is Scalar, as - // it should be - } - - for (auto &attribute : primitive.attributes) { - const auto accessorsIndex = size_t(attribute.second); - if (accessorsIndex < model->accessors.size()) { - const auto bufferView = model->accessors[accessorsIndex].bufferView; - // bufferView could be null(-1) for sparse morph target - if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) { - model->bufferViews[size_t(bufferView)].target = - TINYGLTF_TARGET_ARRAY_BUFFER; - } - } - } - - for (auto &target : primitive.targets) { - for (auto &attribute : target) { - const auto accessorsIndex = size_t(attribute.second); - if (accessorsIndex < model->accessors.size()) { - const auto bufferView = model->accessors[accessorsIndex].bufferView; - // bufferView could be null(-1) for sparse morph target - if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) { - model->bufferViews[size_t(bufferView)].target = - TINYGLTF_TARGET_ARRAY_BUFFER; - } - } - } - } - } - } - - // 7. Parse Node - { - bool success = ForEachInArray(v, "nodes", [&](const detail::json &o) { - if (!detail::IsObject(o)) { - if (err) { - (*err) += "`nodes' does not contain an JSON object."; - } - return false; - } - Node node; - if (!ParseNode(&node, err, o, - store_original_json_for_extras_and_extensions_)) { - return false; - } - - model->nodes.emplace_back(std::move(node)); - return true; - }); - - if (!success) { - return false; - } - } - - // 8. Parse scenes. - { - bool success = ForEachInArray(v, "scenes", [&](const detail::json &o) { - if (!detail::IsObject(o)) { - if (err) { - (*err) += "`scenes' does not contain an JSON object."; - } - return false; - } - std::vector nodes; - ParseIntegerArrayProperty(&nodes, err, o, "nodes", false); - - Scene scene; - scene.nodes = std::move(nodes); - - ParseStringProperty(&scene.name, err, o, "name", false); - - ParseExtensionsProperty(&scene.extensions, err, o); - ParseExtrasProperty(&scene.extras, o); - - if (store_original_json_for_extras_and_extensions_) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - scene.extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - scene.extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } - - model->scenes.emplace_back(std::move(scene)); - return true; - }); - - if (!success) { - return false; - } - } - - // 9. Parse default scenes. - { - detail::json_const_iterator rootIt; - int iVal; - if (detail::FindMember(v, "scene", rootIt) && detail::GetInt(detail::GetValue(rootIt), iVal)) { - model->defaultScene = iVal; - } - } - - // 10. Parse Material - { - bool success = ForEachInArray(v, "materials", [&](const detail::json &o) { - if (!detail::IsObject(o)) { - if (err) { - (*err) += "`materials' does not contain an JSON object."; - } - return false; - } - Material material; - ParseStringProperty(&material.name, err, o, "name", false); - - if (!ParseMaterial(&material, err, o, - store_original_json_for_extras_and_extensions_)) { - return false; - } - - model->materials.emplace_back(std::move(material)); - return true; - }); - - if (!success) { - return false; - } - } - - // 11. Parse Image - void *load_image_user_data{nullptr}; - - LoadImageDataOption load_image_option; - - if (user_image_loader_) { - // Use user supplied pointer - load_image_user_data = load_image_user_data_; - } else { - load_image_option.preserve_channels = preserve_image_channels_; - load_image_user_data = reinterpret_cast(&load_image_option); - } - - { - int idx = 0; - bool success = ForEachInArray(v, "images", [&](const detail::json &o) { - if (!detail::IsObject(o)) { - if (err) { - (*err) += "image[" + std::to_string(idx) + "] is not a JSON object."; - } - return false; - } - Image image; - if (!ParseImage(&image, idx, err, warn, o, - store_original_json_for_extras_and_extensions_, base_dir, - max_external_file_size_, &fs, &uri_cb, &this->LoadImageData, - load_image_user_data)) { - return false; - } - - if (image.bufferView != -1) { - // Load image from the buffer view. - if (size_t(image.bufferView) >= model->bufferViews.size()) { - if (err) { - std::stringstream ss; - ss << "image[" << idx << "] bufferView \"" << image.bufferView - << "\" not found in the scene." << std::endl; - (*err) += ss.str(); - } - return false; - } - - const BufferView &bufferView = - model->bufferViews[size_t(image.bufferView)]; - if (size_t(bufferView.buffer) >= model->buffers.size()) { - if (err) { - std::stringstream ss; - ss << "image[" << idx << "] buffer \"" << bufferView.buffer - << "\" not found in the scene." << std::endl; - (*err) += ss.str(); - } - return false; - } - const Buffer &buffer = model->buffers[size_t(bufferView.buffer)]; - - if (*LoadImageData == nullptr) { - if (err) { - (*err) += "No LoadImageData callback specified.\n"; - } - return false; - } - bool ret = LoadImageData( - &image, idx, err, warn, image.width, image.height, - &buffer.data[bufferView.byteOffset], - static_cast(bufferView.byteLength), load_image_user_data); - if (!ret) { - return false; - } - } - - model->images.emplace_back(std::move(image)); - ++idx; - return true; - }); - - if (!success) { - return false; - } - } - - // 12. Parse Texture - { - bool success = ForEachInArray(v, "textures", [&](const detail::json &o) { - if (!detail::IsObject(o)) { - if (err) { - (*err) += "`textures' does not contain an JSON object."; - } - return false; - } - Texture texture; - if (!ParseTexture(&texture, err, o, - store_original_json_for_extras_and_extensions_, - base_dir)) { - return false; - } - - model->textures.emplace_back(std::move(texture)); - return true; - }); - - if (!success) { - return false; - } - } - - // 13. Parse Animation - { - bool success = ForEachInArray(v, "animations", [&](const detail::json &o) { - if (!detail::IsObject(o)) { - if (err) { - (*err) += "`animations' does not contain an JSON object."; - } - return false; - } - Animation animation; - if (!ParseAnimation(&animation, err, o, - store_original_json_for_extras_and_extensions_)) { - return false; - } - - model->animations.emplace_back(std::move(animation)); - return true; - }); - - if (!success) { - return false; - } - } - - // 14. Parse Skin - { - bool success = ForEachInArray(v, "skins", [&](const detail::json &o) { - if (!detail::IsObject(o)) { - if (err) { - (*err) += "`skins' does not contain an JSON object."; - } - return false; - } - Skin skin; - if (!ParseSkin(&skin, err, o, - store_original_json_for_extras_and_extensions_)) { - return false; - } - - model->skins.emplace_back(std::move(skin)); - return true; - }); - - if (!success) { - return false; - } - } - - // 15. Parse Sampler - { - bool success = ForEachInArray(v, "samplers", [&](const detail::json &o) { - if (!detail::IsObject(o)) { - if (err) { - (*err) += "`samplers' does not contain an JSON object."; - } - return false; - } - Sampler sampler; - if (!ParseSampler(&sampler, err, o, - store_original_json_for_extras_and_extensions_)) { - return false; - } - - model->samplers.emplace_back(std::move(sampler)); - return true; - }); - - if (!success) { - return false; - } - } - - // 16. Parse Camera - { - bool success = ForEachInArray(v, "cameras", [&](const detail::json &o) { - if (!detail::IsObject(o)) { - if (err) { - (*err) += "`cameras' does not contain an JSON object."; - } - return false; - } - Camera camera; - if (!ParseCamera(&camera, err, o, - store_original_json_for_extras_and_extensions_)) { - return false; - } - - model->cameras.emplace_back(std::move(camera)); - return true; - }); - - if (!success) { - return false; - } - } - - // 17. Parse Extensions - ParseExtensionsProperty(&model->extensions, err, v); - - // 18. Specific extension implementations - { - detail::json_const_iterator rootIt; - if (detail::FindMember(v, "extensions", rootIt) && detail::IsObject(detail::GetValue(rootIt))) { - const detail::json &root = detail::GetValue(rootIt); - - detail::json_const_iterator it(detail::ObjectBegin(root)); - detail::json_const_iterator itEnd(detail::ObjectEnd(root)); - for (; it != itEnd; ++it) { - // parse KHR_lights_punctual extension - std::string key(detail::GetKey(it)); - if ((key == "KHR_lights_punctual") && detail::IsObject(detail::GetValue(it))) { - const detail::json &object = detail::GetValue(it); - detail::json_const_iterator itLight; - if (detail::FindMember(object, "lights", itLight)) { - const detail::json &lights = detail::GetValue(itLight); - if (!detail::IsArray(lights)) { - continue; - } - - auto arrayIt(detail::ArrayBegin(lights)); - auto arrayItEnd(detail::ArrayEnd(lights)); - for (; arrayIt != arrayItEnd; ++arrayIt) { - Light light; - if (!ParseLight(&light, err, *arrayIt, - store_original_json_for_extras_and_extensions_)) { - return false; - } - model->lights.emplace_back(std::move(light)); - } - } - } - } - } - } - - // 19. Parse Extras - ParseExtrasProperty(&model->extras, v); - - if (store_original_json_for_extras_and_extensions_) { - model->extras_json_string = detail::JsonToString(v["extras"]); - model->extensions_json_string = detail::JsonToString(v["extensions"]); - } - - return true; -} - -bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err, - std::string *warn, const char *str, - unsigned int length, - const std::string &base_dir, - unsigned int check_sections) { - is_binary_ = false; - bin_data_ = nullptr; - bin_size_ = 0; - - return LoadFromString(model, err, warn, str, length, base_dir, - check_sections); -} - -bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err, - std::string *warn, const std::string &filename, - unsigned int check_sections) { - std::stringstream ss; - - if (fs.ReadWholeFile == nullptr) { - // Programmer error, assert() ? - ss << "Failed to read file: " << filename - << ": one or more FS callback not set" << std::endl; - if (err) { - (*err) = ss.str(); - } - return false; - } - - std::vector data; - std::string fileerr; - bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data); - if (!fileread) { - ss << "Failed to read file: " << filename << ": " << fileerr << std::endl; - if (err) { - (*err) = ss.str(); - } - return false; - } - - size_t sz = data.size(); - if (sz == 0) { - if (err) { - (*err) = "Empty file."; - } - return false; - } - - std::string basedir = GetBaseDir(filename); - - bool ret = LoadASCIIFromString( - model, err, warn, reinterpret_cast(&data.at(0)), - static_cast(data.size()), basedir, check_sections); - - return ret; -} - -bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err, - std::string *warn, - const unsigned char *bytes, - unsigned int size, - const std::string &base_dir, - unsigned int check_sections) { - if (size < 20) { - if (err) { - (*err) = "Too short data size for glTF Binary."; - } - return false; - } - - if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' && - bytes[3] == 'F') { - // ok - } else { - if (err) { - (*err) = "Invalid magic."; - } - return false; - } - - unsigned int version; // 4 bytes - unsigned int length; // 4 bytes - unsigned int chunk0_length; // 4 bytes - unsigned int chunk0_format; // 4 bytes; - - memcpy(&version, bytes + 4, 4); - swap4(&version); - memcpy(&length, bytes + 8, 4); - swap4(&length); - memcpy(&chunk0_length, bytes + 12, 4); // JSON data length - swap4(&chunk0_length); - memcpy(&chunk0_format, bytes + 16, 4); - swap4(&chunk0_format); - - // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#binary-gltf-layout - // - // In case the Bin buffer is not present, the size is exactly 20 + size of - // JSON contents, - // so use "greater than" operator. - // - // https://github.com/syoyo/tinygltf/issues/372 - // Use 64bit uint to avoid integer overflow. - uint64_t header_and_json_size = 20ull + uint64_t(chunk0_length); - - if (header_and_json_size > std::numeric_limits::max()) { - // Do not allow 4GB or more GLB data. - (*err) = "Invalid glTF binary. GLB data exceeds 4GB."; - } - - if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) || (length > size) || - (header_and_json_size > uint64_t(length)) || - (chunk0_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format. - if (err) { - (*err) = "Invalid glTF binary."; - } - return false; - } - - // Padding check - // The start and the end of each chunk must be aligned to a 4-byte boundary. - // No padding check for chunk0 start since its 4byte-boundary is ensured. - if ((header_and_json_size % 4) != 0) { - if (err) { - (*err) = "JSON Chunk end does not aligned to a 4-byte boundary."; - } - } - - //std::cout << "header_and_json_size = " << header_and_json_size << "\n"; - //std::cout << "length = " << length << "\n"; - - // Chunk1(BIN) data - // The spec says: When the binary buffer is empty or when it is stored by other means, this chunk SHOULD be omitted. - // So when header + JSON data == binary size, Chunk1 is omitted. - if (header_and_json_size == uint64_t(length)) { - - bin_data_ = nullptr; - bin_size_ = 0; - } else { - // Read Chunk1 info(BIN data) - // At least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin payload could be 1~3 bytes, but need to be aligned to 4 bytes) - if ((header_and_json_size + 12ull) > uint64_t(length)) { - if (err) { - (*err) = "Insufficient storage space for Chunk1(BIN data). At least Chunk1 Must have 4 bytes or more bytes, but got " + std::to_string((header_and_json_size + 12ull) - uint64_t(length)) + ".\n"; - } - return false; - } - - unsigned int chunk1_length; // 4 bytes - unsigned int chunk1_format; // 4 bytes; - memcpy(&chunk1_length, bytes + header_and_json_size, 4); // JSON data length - swap4(&chunk1_length); - memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4); - swap4(&chunk1_format); - - //std::cout << "chunk1_length = " << chunk1_length << "\n"; - - if (chunk1_length < 4) { - if (err) { - (*err) = "Insufficient Chunk1(BIN) data size."; - } - return false; - } - - if ((chunk1_length % 4) != 0) { - if (err) { - (*err) = "BIN Chunk end does not aligned to a 4-byte boundary."; - } - return false; - } - - if (uint64_t(chunk1_length) + header_and_json_size > uint64_t(length)) { - if (err) { - (*err) = "BIN Chunk data length exceeds the GLB size."; - } - return false; - } - - if (chunk1_format != 0x004e4942) { - if (err) { - (*err) = "Invalid type for chunk1 data."; - } - return false; - } - - //std::cout << "chunk1_length = " << chunk1_length << "\n"; - - bin_data_ = bytes + header_and_json_size + - 8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format) - - bin_size_ = size_t(chunk1_length); - } - - // Extract JSON string. - std::string jsonString(reinterpret_cast(&bytes[20]), - chunk0_length); - - is_binary_ = true; - - bool ret = LoadFromString(model, err, warn, - reinterpret_cast(&bytes[20]), - chunk0_length, base_dir, check_sections); - if (!ret) { - return ret; - } - - return true; -} - -bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err, - std::string *warn, - const std::string &filename, - unsigned int check_sections) { - std::stringstream ss; - - if (fs.ReadWholeFile == nullptr) { - // Programmer error, assert() ? - ss << "Failed to read file: " << filename - << ": one or more FS callback not set" << std::endl; - if (err) { - (*err) = ss.str(); - } - return false; - } - - std::vector data; - std::string fileerr; - bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data); - if (!fileread) { - ss << "Failed to read file: " << filename << ": " << fileerr << std::endl; - if (err) { - (*err) = ss.str(); - } - return false; - } - - std::string basedir = GetBaseDir(filename); - - bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0), - static_cast(data.size()), - basedir, check_sections); - - return ret; -} - -/////////////////////// -// GLTF Serialization -/////////////////////// -namespace detail { -detail::json JsonFromString(const char *s) { -#ifdef TINYGLTF_USE_RAPIDJSON - return detail::json(s, detail::GetAllocator()); -#else - return detail::json(s); -#endif -} - -void JsonAssign(detail::json &dest, const detail::json &src) { -#ifdef TINYGLTF_USE_RAPIDJSON - dest.CopyFrom(src, detail::GetAllocator()); -#else - dest = src; -#endif -} - -void JsonAddMember(detail::json &o, const char *key, detail::json &&value) { -#ifdef TINYGLTF_USE_RAPIDJSON - if (!o.IsObject()) { - o.SetObject(); - } - o.AddMember(detail::json(key, detail::GetAllocator()), std::move(value), detail::GetAllocator()); -#else - o[key] = std::move(value); -#endif -} - -void JsonPushBack(detail::json &o, detail::json &&value) { -#ifdef TINYGLTF_USE_RAPIDJSON - o.PushBack(std::move(value), detail::GetAllocator()); -#else - o.push_back(std::move(value)); -#endif -} - -bool JsonIsNull(const detail::json &o) { -#ifdef TINYGLTF_USE_RAPIDJSON - return o.IsNull(); -#else - return o.is_null(); -#endif -} - -void JsonSetObject(detail::json &o) { -#ifdef TINYGLTF_USE_RAPIDJSON - o.SetObject(); -#else - o = o.object({}); -#endif -} - -void JsonReserveArray(detail::json &o, size_t s) { -#ifdef TINYGLTF_USE_RAPIDJSON - o.SetArray(); - o.Reserve(static_cast(s), detail::GetAllocator()); -#endif - (void)(o); - (void)(s); -} -} // namespace - -// typedef std::pair json_object_pair; - -template -static void SerializeNumberProperty(const std::string &key, T number, - detail::json &obj) { - // obj.insert( - // json_object_pair(key, detail::json(static_cast(number)))); - // obj[key] = static_cast(number); - detail::JsonAddMember(obj, key.c_str(), detail::json(number)); -} - -#ifdef TINYGLTF_USE_RAPIDJSON -template <> -void SerializeNumberProperty(const std::string &key, size_t number, detail::json &obj) { - detail::JsonAddMember(obj, key.c_str(), detail::json(static_cast(number))); -} -#endif - -template -static void SerializeNumberArrayProperty(const std::string &key, - const std::vector &value, - detail::json &obj) { - if (value.empty()) return; - - detail::json ary; - detail::JsonReserveArray(ary, value.size()); - for (const auto &s : value) { - detail::JsonPushBack(ary, detail::json(s)); - } - detail::JsonAddMember(obj, key.c_str(), std::move(ary)); -} - -static void SerializeStringProperty(const std::string &key, - const std::string &value, detail::json &obj) { - detail::JsonAddMember(obj, key.c_str(), detail::JsonFromString(value.c_str())); -} - -static void SerializeStringArrayProperty(const std::string &key, - const std::vector &value, - detail::json &obj) { - detail::json ary; - detail::JsonReserveArray(ary, value.size()); - for (auto &s : value) { - detail::JsonPushBack(ary, detail::JsonFromString(s.c_str())); - } - detail::JsonAddMember(obj, key.c_str(), std::move(ary)); -} - -static bool ValueToJson(const Value &value, detail::json *ret) { - detail::json obj; -#ifdef TINYGLTF_USE_RAPIDJSON - switch (value.Type()) { - case REAL_TYPE: - obj.SetDouble(value.Get()); - break; - case INT_TYPE: - obj.SetInt(value.Get()); - break; - case BOOL_TYPE: - obj.SetBool(value.Get()); - break; - case STRING_TYPE: - obj.SetString(value.Get().c_str(), detail::GetAllocator()); - break; - case ARRAY_TYPE: { - obj.SetArray(); - obj.Reserve(static_cast(value.ArrayLen()), - detail::GetAllocator()); - for (unsigned int i = 0; i < value.ArrayLen(); ++i) { - Value elementValue = value.Get(int(i)); - detail::json elementJson; - if (ValueToJson(value.Get(int(i)), &elementJson)) - obj.PushBack(std::move(elementJson), detail::GetAllocator()); - } - break; - } - case BINARY_TYPE: - // TODO - // obj = detail::json(value.Get>()); - return false; - break; - case OBJECT_TYPE: { - obj.SetObject(); - Value::Object objMap = value.Get(); - for (auto &it : objMap) { - detail::json elementJson; - if (ValueToJson(it.second, &elementJson)) { - obj.AddMember(detail::json(it.first.c_str(), detail::GetAllocator()), - std::move(elementJson), detail::GetAllocator()); - } - } - break; - } - case NULL_TYPE: - default: - return false; - } -#else - switch (value.Type()) { - case REAL_TYPE: - obj = detail::json(value.Get()); - break; - case INT_TYPE: - obj = detail::json(value.Get()); - break; - case BOOL_TYPE: - obj = detail::json(value.Get()); - break; - case STRING_TYPE: - obj = detail::json(value.Get()); - break; - case ARRAY_TYPE: { - for (unsigned int i = 0; i < value.ArrayLen(); ++i) { - Value elementValue = value.Get(int(i)); - detail::json elementJson; - if (ValueToJson(value.Get(int(i)), &elementJson)) - obj.push_back(elementJson); - } - break; - } - case BINARY_TYPE: - // TODO - // obj = json(value.Get>()); - return false; - break; - case OBJECT_TYPE: { - Value::Object objMap = value.Get(); - for (auto &it : objMap) { - detail::json elementJson; - if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson; - } - break; - } - case NULL_TYPE: - default: - return false; - } -#endif - if (ret) *ret = std::move(obj); - return true; -} - -static void SerializeValue(const std::string &key, const Value &value, - detail::json &obj) { - detail::json ret; - if (ValueToJson(value, &ret)) { - detail::JsonAddMember(obj, key.c_str(), std::move(ret)); - } -} - -static void SerializeGltfBufferData(const std::vector &data, - detail::json &o) { - std::string header = "data:application/octet-stream;base64,"; - if (data.size() > 0) { - std::string encodedData = - base64_encode(&data[0], static_cast(data.size())); - SerializeStringProperty("uri", header + encodedData, o); - } else { - // Issue #229 - // size 0 is allowed. Just emit mime header. - SerializeStringProperty("uri", header, o); - } -} - -static bool SerializeGltfBufferData(const std::vector &data, - const std::string &binFilename) { -#ifdef _WIN32 -#if defined(__GLIBCXX__) // mingw - int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(), - _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); - __gnu_cxx::stdio_filebuf wfile_buf( - file_descriptor, std::ios_base::out | std::ios_base::binary); - std::ostream output(&wfile_buf); - if (!wfile_buf.is_open()) return false; -#elif defined(_MSC_VER) - std::ofstream output(UTF8ToWchar(binFilename).c_str(), std::ofstream::binary); - if (!output.is_open()) return false; -#else - std::ofstream output(binFilename.c_str(), std::ofstream::binary); - if (!output.is_open()) return false; -#endif -#else - std::ofstream output(binFilename.c_str(), std::ofstream::binary); - if (!output.is_open()) return false; -#endif - if (data.size() > 0) { - output.write(reinterpret_cast(&data[0]), - std::streamsize(data.size())); - } else { - // Issue #229 - // size 0 will be still valid buffer data. - // write empty file. - } - return true; -} - -#if 0 // FIXME(syoyo): not used. will be removed in the future release. -static void SerializeParameterMap(ParameterMap ¶m, detail::json &o) { - for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end(); - ++paramIt) { - if (paramIt->second.number_array.size()) { - SerializeNumberArrayProperty(paramIt->first, - paramIt->second.number_array, o); - } else if (paramIt->second.json_double_value.size()) { - detail::json json_double_value; - for (std::map::iterator it = - paramIt->second.json_double_value.begin(); - it != paramIt->second.json_double_value.end(); ++it) { - if (it->first == "index") { - json_double_value[it->first] = paramIt->second.TextureIndex(); - } else { - json_double_value[it->first] = it->second; - } - } - - o[paramIt->first] = json_double_value; - } else if (!paramIt->second.string_value.empty()) { - SerializeStringProperty(paramIt->first, paramIt->second.string_value, o); - } else if (paramIt->second.has_number_value) { - o[paramIt->first] = paramIt->second.number_value; - } else { - o[paramIt->first] = paramIt->second.bool_value; - } - } -} -#endif - -static void SerializeExtensionMap(const ExtensionMap &extensions, detail::json &o) { - if (!extensions.size()) return; - - detail::json extMap; - for (ExtensionMap::const_iterator extIt = extensions.begin(); - extIt != extensions.end(); ++extIt) { - // Allow an empty object for extension(#97) - detail::json ret; - bool isNull = true; - if (ValueToJson(extIt->second, &ret)) { - isNull = detail::JsonIsNull(ret); - detail::JsonAddMember(extMap, extIt->first.c_str(), std::move(ret)); - } - if (isNull) { - if (!(extIt->first.empty())) { // name should not be empty, but for sure - // create empty object so that an extension name is still included in - // json. - detail::json empty; - detail::JsonSetObject(empty); - detail::JsonAddMember(extMap, extIt->first.c_str(), std::move(empty)); - } - } - } - detail::JsonAddMember(o, "extensions", std::move(extMap)); -} - -static void SerializeGltfAccessor(const Accessor &accessor, detail::json &o) { - if (accessor.bufferView >= 0) - SerializeNumberProperty("bufferView", accessor.bufferView, o); - - if (accessor.byteOffset != 0) - SerializeNumberProperty("byteOffset", int(accessor.byteOffset), o); - - SerializeNumberProperty("componentType", accessor.componentType, o); - SerializeNumberProperty("count", accessor.count, o); - - if ((accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) || - (accessor.componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)) { - SerializeNumberArrayProperty("min", accessor.minValues, o); - SerializeNumberArrayProperty("max", accessor.maxValues, o); - } else { - // Issue #301. Serialize as integer. - // Assume int value is within [-2**31-1, 2**31-1] - { - std::vector values; - std::transform(accessor.minValues.begin(), accessor.minValues.end(), - std::back_inserter(values), - [](double v) { return static_cast(v); }); - - SerializeNumberArrayProperty("min", values, o); - } - - { - std::vector values; - std::transform(accessor.maxValues.begin(), accessor.maxValues.end(), - std::back_inserter(values), - [](double v) { return static_cast(v); }); - - SerializeNumberArrayProperty("max", values, o); - } - } - - if (accessor.normalized) - SerializeValue("normalized", Value(accessor.normalized), o); - std::string type; - switch (accessor.type) { - case TINYGLTF_TYPE_SCALAR: - type = "SCALAR"; - break; - case TINYGLTF_TYPE_VEC2: - type = "VEC2"; - break; - case TINYGLTF_TYPE_VEC3: - type = "VEC3"; - break; - case TINYGLTF_TYPE_VEC4: - type = "VEC4"; - break; - case TINYGLTF_TYPE_MAT2: - type = "MAT2"; - break; - case TINYGLTF_TYPE_MAT3: - type = "MAT3"; - break; - case TINYGLTF_TYPE_MAT4: - type = "MAT4"; - break; - } - - SerializeStringProperty("type", type, o); - if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o); - - if (accessor.extras.Type() != NULL_TYPE) { - SerializeValue("extras", accessor.extras, o); - } - - // sparse - if (accessor.sparse.isSparse) - { - detail::json sparse; - SerializeNumberProperty("count", accessor.sparse.count, sparse); - { - detail::json indices; - SerializeNumberProperty("bufferView", accessor.sparse.indices.bufferView, indices); - SerializeNumberProperty("byteOffset", accessor.sparse.indices.byteOffset, indices); - SerializeNumberProperty("componentType", accessor.sparse.indices.componentType, indices); - detail::JsonAddMember(sparse, "indices", std::move(indices)); - } - { - detail::json values; - SerializeNumberProperty("bufferView", accessor.sparse.values.bufferView, values); - SerializeNumberProperty("byteOffset", accessor.sparse.values.byteOffset, values); - detail::JsonAddMember(sparse, "values", std::move(values)); - } - detail::JsonAddMember(o, "sparse", std::move(sparse)); - } -} - -static void SerializeGltfAnimationChannel(const AnimationChannel &channel, - detail::json &o) { - SerializeNumberProperty("sampler", channel.sampler, o); - { - detail::json target; - - if (channel.target_node >= 0) { - SerializeNumberProperty("node", channel.target_node, target); - } - - SerializeStringProperty("path", channel.target_path, target); - - SerializeExtensionMap(channel.target_extensions, target); - - detail::JsonAddMember(o, "target", std::move(target)); - } - - if (channel.extras.Type() != NULL_TYPE) { - SerializeValue("extras", channel.extras, o); - } - - SerializeExtensionMap(channel.extensions, o); -} - -static void SerializeGltfAnimationSampler(const AnimationSampler &sampler, - detail::json &o) { - SerializeNumberProperty("input", sampler.input, o); - SerializeNumberProperty("output", sampler.output, o); - SerializeStringProperty("interpolation", sampler.interpolation, o); - - if (sampler.extras.Type() != NULL_TYPE) { - SerializeValue("extras", sampler.extras, o); - } -} - -static void SerializeGltfAnimation(const Animation &animation, detail::json &o) { - if (!animation.name.empty()) - SerializeStringProperty("name", animation.name, o); - - { - detail::json channels; - detail::JsonReserveArray(channels, animation.channels.size()); - for (unsigned int i = 0; i < animation.channels.size(); ++i) { - detail::json channel; - AnimationChannel gltfChannel = animation.channels[i]; - SerializeGltfAnimationChannel(gltfChannel, channel); - detail::JsonPushBack(channels, std::move(channel)); - } - - detail::JsonAddMember(o, "channels", std::move(channels)); - } - - { - detail::json samplers; - detail::JsonReserveArray(samplers, animation.samplers.size()); - for (unsigned int i = 0; i < animation.samplers.size(); ++i) { - detail::json sampler; - AnimationSampler gltfSampler = animation.samplers[i]; - SerializeGltfAnimationSampler(gltfSampler, sampler); - detail::JsonPushBack(samplers, std::move(sampler)); - } - detail::JsonAddMember(o, "samplers", std::move(samplers)); - } - - if (animation.extras.Type() != NULL_TYPE) { - SerializeValue("extras", animation.extras, o); - } - - SerializeExtensionMap(animation.extensions, o); -} - -static void SerializeGltfAsset(const Asset &asset, detail::json &o) { - if (!asset.generator.empty()) { - SerializeStringProperty("generator", asset.generator, o); - } - - if (!asset.copyright.empty()) { - SerializeStringProperty("copyright", asset.copyright, o); - } - - auto version = asset.version; - if (version.empty()) { - // Just in case - // `version` must be defined - version = "2.0"; - } - - // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0? - SerializeStringProperty("version", version, o); - - if (asset.extras.Keys().size()) { - SerializeValue("extras", asset.extras, o); - } - - SerializeExtensionMap(asset.extensions, o); -} - -static void SerializeGltfBufferBin(const Buffer &buffer, detail::json &o, - std::vector &binBuffer) { - SerializeNumberProperty("byteLength", buffer.data.size(), o); - binBuffer = buffer.data; - - if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o); - - if (buffer.extras.Type() != NULL_TYPE) { - SerializeValue("extras", buffer.extras, o); - } -} - -static void SerializeGltfBuffer(const Buffer &buffer, detail::json &o) { - SerializeNumberProperty("byteLength", buffer.data.size(), o); - SerializeGltfBufferData(buffer.data, o); - - if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o); - - if (buffer.extras.Type() != NULL_TYPE) { - SerializeValue("extras", buffer.extras, o); - } -} - -static bool SerializeGltfBuffer(const Buffer &buffer, detail::json &o, - const std::string &binFilename, - const std::string &binUri) { - if (!SerializeGltfBufferData(buffer.data, binFilename)) return false; - SerializeNumberProperty("byteLength", buffer.data.size(), o); - SerializeStringProperty("uri", binUri, o); - - if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o); - - if (buffer.extras.Type() != NULL_TYPE) { - SerializeValue("extras", buffer.extras, o); - } - return true; -} - -static void SerializeGltfBufferView(const BufferView &bufferView, detail::json &o) { - SerializeNumberProperty("buffer", bufferView.buffer, o); - SerializeNumberProperty("byteLength", bufferView.byteLength, o); - - // byteStride is optional, minimum allowed is 4 - if (bufferView.byteStride >= 4) { - SerializeNumberProperty("byteStride", bufferView.byteStride, o); - } - // byteOffset is optional, default is 0 - if (bufferView.byteOffset > 0) { - SerializeNumberProperty("byteOffset", bufferView.byteOffset, o); - } - // Target is optional, check if it contains a valid value - if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER || - bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) { - SerializeNumberProperty("target", bufferView.target, o); - } - if (bufferView.name.size()) { - SerializeStringProperty("name", bufferView.name, o); - } - - if (bufferView.extras.Type() != NULL_TYPE) { - SerializeValue("extras", bufferView.extras, o); - } -} - -static void SerializeGltfImage(const Image &image, const std::string &uri, - detail::json &o) { - // From 2.7.0, we look for `uri` parameter, not `Image.uri` - // if uri is empty, the mimeType and bufferview should be set - if (uri.empty()) { - SerializeStringProperty("mimeType", image.mimeType, o); - SerializeNumberProperty("bufferView", image.bufferView, o); - } else { - SerializeStringProperty("uri", uri, o); - } - - if (image.name.size()) { - SerializeStringProperty("name", image.name, o); - } - - if (image.extras.Type() != NULL_TYPE) { - SerializeValue("extras", image.extras, o); - } - - SerializeExtensionMap(image.extensions, o); -} - -static void SerializeGltfTextureInfo(const TextureInfo &texinfo, detail::json &o) { - SerializeNumberProperty("index", texinfo.index, o); - - if (texinfo.texCoord != 0) { - SerializeNumberProperty("texCoord", texinfo.texCoord, o); - } - - if (texinfo.extras.Type() != NULL_TYPE) { - SerializeValue("extras", texinfo.extras, o); - } - - SerializeExtensionMap(texinfo.extensions, o); -} - -static void SerializeGltfNormalTextureInfo(const NormalTextureInfo &texinfo, - detail::json &o) { - SerializeNumberProperty("index", texinfo.index, o); - - if (texinfo.texCoord != 0) { - SerializeNumberProperty("texCoord", texinfo.texCoord, o); - } - - if (!TINYGLTF_DOUBLE_EQUAL(texinfo.scale, 1.0)) { - SerializeNumberProperty("scale", texinfo.scale, o); - } - - if (texinfo.extras.Type() != NULL_TYPE) { - SerializeValue("extras", texinfo.extras, o); - } - - SerializeExtensionMap(texinfo.extensions, o); -} - -static void SerializeGltfOcclusionTextureInfo( - const OcclusionTextureInfo &texinfo, detail::json &o) { - SerializeNumberProperty("index", texinfo.index, o); - - if (texinfo.texCoord != 0) { - SerializeNumberProperty("texCoord", texinfo.texCoord, o); - } - - if (!TINYGLTF_DOUBLE_EQUAL(texinfo.strength, 1.0)) { - SerializeNumberProperty("strength", texinfo.strength, o); - } - - if (texinfo.extras.Type() != NULL_TYPE) { - SerializeValue("extras", texinfo.extras, o); - } - - SerializeExtensionMap(texinfo.extensions, o); -} - -static void SerializeGltfPbrMetallicRoughness(const PbrMetallicRoughness &pbr, - detail::json &o) { - std::vector default_baseColorFactor = {1.0, 1.0, 1.0, 1.0}; - if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) { - SerializeNumberArrayProperty("baseColorFactor", pbr.baseColorFactor, - o); - } - - if (!TINYGLTF_DOUBLE_EQUAL(pbr.metallicFactor, 1.0)) { - SerializeNumberProperty("metallicFactor", pbr.metallicFactor, o); - } - - if (!TINYGLTF_DOUBLE_EQUAL(pbr.roughnessFactor, 1.0)) { - SerializeNumberProperty("roughnessFactor", pbr.roughnessFactor, o); - } - - if (pbr.baseColorTexture.index > -1) { - detail::json texinfo; - SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo); - detail::JsonAddMember(o, "baseColorTexture", std::move(texinfo)); - } - - if (pbr.metallicRoughnessTexture.index > -1) { - detail::json texinfo; - SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo); - detail::JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo)); - } - - SerializeExtensionMap(pbr.extensions, o); - - if (pbr.extras.Type() != NULL_TYPE) { - SerializeValue("extras", pbr.extras, o); - } -} - -static void SerializeGltfMaterial(const Material &material, detail::json &o) { - if (material.name.size()) { - SerializeStringProperty("name", material.name, o); - } - - // QUESTION(syoyo): Write material parameters regardless of its default value? - - if (!TINYGLTF_DOUBLE_EQUAL(material.alphaCutoff, 0.5)) { - SerializeNumberProperty("alphaCutoff", material.alphaCutoff, o); - } - - if (material.alphaMode.compare("OPAQUE") != 0) { - SerializeStringProperty("alphaMode", material.alphaMode, o); - } - - if (material.doubleSided != false) - detail::JsonAddMember(o, "doubleSided", detail::json(material.doubleSided)); - - if (material.normalTexture.index > -1) { - detail::json texinfo; - SerializeGltfNormalTextureInfo(material.normalTexture, texinfo); - detail::JsonAddMember(o, "normalTexture", std::move(texinfo)); - } - - if (material.occlusionTexture.index > -1) { - detail::json texinfo; - SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo); - detail::JsonAddMember(o, "occlusionTexture", std::move(texinfo)); - } - - if (material.emissiveTexture.index > -1) { - detail::json texinfo; - SerializeGltfTextureInfo(material.emissiveTexture, texinfo); - detail::JsonAddMember(o, "emissiveTexture", std::move(texinfo)); - } - - std::vector default_emissiveFactor = {0.0, 0.0, 0.0}; - if (!Equals(material.emissiveFactor, default_emissiveFactor)) { - SerializeNumberArrayProperty("emissiveFactor", - material.emissiveFactor, o); - } - - { - detail::json pbrMetallicRoughness; - SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness, - pbrMetallicRoughness); - // Issue 204 - // Do not serialize `pbrMetallicRoughness` if pbrMetallicRoughness has all - // default values(json is null). Otherwise it will serialize to - // `pbrMetallicRoughness : null`, which cannot be read by other glTF - // importers (and validators). - // - if (!detail::JsonIsNull(pbrMetallicRoughness)) { - detail::JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness)); - } - } - -#if 0 // legacy way. just for the record. - if (material.values.size()) { - detail::json pbrMetallicRoughness; - SerializeParameterMap(material.values, pbrMetallicRoughness); - detail::JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness)); - } - - SerializeParameterMap(material.additionalValues, o); -#else - -#endif - - SerializeExtensionMap(material.extensions, o); - - if (material.extras.Type() != NULL_TYPE) { - SerializeValue("extras", material.extras, o); - } -} - -static void SerializeGltfMesh(const Mesh &mesh, detail::json &o) { - detail::json primitives; - detail::JsonReserveArray(primitives, mesh.primitives.size()); - for (unsigned int i = 0; i < mesh.primitives.size(); ++i) { - detail::json primitive; - const Primitive &gltfPrimitive = mesh.primitives[i]; // don't make a copy - { - detail::json attributes; - for (auto attrIt = gltfPrimitive.attributes.begin(); - attrIt != gltfPrimitive.attributes.end(); ++attrIt) { - SerializeNumberProperty(attrIt->first, attrIt->second, attributes); - } - - detail::JsonAddMember(primitive, "attributes", std::move(attributes)); - } - - // Indices is optional - if (gltfPrimitive.indices > -1) { - SerializeNumberProperty("indices", gltfPrimitive.indices, primitive); - } - // Material is optional - if (gltfPrimitive.material > -1) { - SerializeNumberProperty("material", gltfPrimitive.material, - primitive); - } - SerializeNumberProperty("mode", gltfPrimitive.mode, primitive); - - // Morph targets - if (gltfPrimitive.targets.size()) { - detail::json targets; - detail::JsonReserveArray(targets, gltfPrimitive.targets.size()); - for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) { - detail::json targetAttributes; - std::map targetData = gltfPrimitive.targets[k]; - for (std::map::iterator attrIt = targetData.begin(); - attrIt != targetData.end(); ++attrIt) { - SerializeNumberProperty(attrIt->first, attrIt->second, - targetAttributes); - } - detail::JsonPushBack(targets, std::move(targetAttributes)); - } - detail::JsonAddMember(primitive, "targets", std::move(targets)); - } - - SerializeExtensionMap(gltfPrimitive.extensions, primitive); - - if (gltfPrimitive.extras.Type() != NULL_TYPE) { - SerializeValue("extras", gltfPrimitive.extras, primitive); - } - - detail::JsonPushBack(primitives, std::move(primitive)); - } - - detail::JsonAddMember(o, "primitives", std::move(primitives)); - - if (mesh.weights.size()) { - SerializeNumberArrayProperty("weights", mesh.weights, o); - } - - if (mesh.name.size()) { - SerializeStringProperty("name", mesh.name, o); - } - - SerializeExtensionMap(mesh.extensions, o); - if (mesh.extras.Type() != NULL_TYPE) { - SerializeValue("extras", mesh.extras, o); - } -} - -static void SerializeSpotLight(const SpotLight &spot, detail::json &o) { - SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o); - SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o); - SerializeExtensionMap(spot.extensions, o); - if (spot.extras.Type() != NULL_TYPE) { - SerializeValue("extras", spot.extras, o); - } -} - -static void SerializeGltfLight(const Light &light, detail::json &o) { - if (!light.name.empty()) SerializeStringProperty("name", light.name, o); - SerializeNumberProperty("intensity", light.intensity, o); - if (light.range > 0.0) { - SerializeNumberProperty("range", light.range, o); - } - SerializeNumberArrayProperty("color", light.color, o); - SerializeStringProperty("type", light.type, o); - if (light.type == "spot") { - detail::json spot; - SerializeSpotLight(light.spot, spot); - detail::JsonAddMember(o, "spot", std::move(spot)); - } - SerializeExtensionMap(light.extensions, o); - if (light.extras.Type() != NULL_TYPE) { - SerializeValue("extras", light.extras, o); - } -} - -static void SerializeGltfNode(const Node &node, detail::json &o) { - if (node.translation.size() > 0) { - SerializeNumberArrayProperty("translation", node.translation, o); - } - if (node.rotation.size() > 0) { - SerializeNumberArrayProperty("rotation", node.rotation, o); - } - if (node.scale.size() > 0) { - SerializeNumberArrayProperty("scale", node.scale, o); - } - if (node.matrix.size() > 0) { - SerializeNumberArrayProperty("matrix", node.matrix, o); - } - if (node.mesh != -1) { - SerializeNumberProperty("mesh", node.mesh, o); - } - - if (node.skin != -1) { - SerializeNumberProperty("skin", node.skin, o); - } - - if (node.camera != -1) { - SerializeNumberProperty("camera", node.camera, o); - } - - if (node.weights.size() > 0) { - SerializeNumberArrayProperty("weights", node.weights, o); - } - - if (node.extras.Type() != NULL_TYPE) { - SerializeValue("extras", node.extras, o); - } - - SerializeExtensionMap(node.extensions, o); - if (!node.name.empty()) SerializeStringProperty("name", node.name, o); - SerializeNumberArrayProperty("children", node.children, o); -} - -static void SerializeGltfSampler(const Sampler &sampler, detail::json &o) { - if (!sampler.name.empty()) { - SerializeStringProperty("name", sampler.name, o); - } - if (sampler.magFilter != -1) { - SerializeNumberProperty("magFilter", sampler.magFilter, o); - } - if (sampler.minFilter != -1) { - SerializeNumberProperty("minFilter", sampler.minFilter, o); - } - // SerializeNumberProperty("wrapR", sampler.wrapR, o); - SerializeNumberProperty("wrapS", sampler.wrapS, o); - SerializeNumberProperty("wrapT", sampler.wrapT, o); - - if (sampler.extras.Type() != NULL_TYPE) { - SerializeValue("extras", sampler.extras, o); - } -} - -static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera, - detail::json &o) { - SerializeNumberProperty("zfar", camera.zfar, o); - SerializeNumberProperty("znear", camera.znear, o); - SerializeNumberProperty("xmag", camera.xmag, o); - SerializeNumberProperty("ymag", camera.ymag, o); - - if (camera.extras.Type() != NULL_TYPE) { - SerializeValue("extras", camera.extras, o); - } -} - -static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera, - detail::json &o) { - SerializeNumberProperty("zfar", camera.zfar, o); - SerializeNumberProperty("znear", camera.znear, o); - if (camera.aspectRatio > 0) { - SerializeNumberProperty("aspectRatio", camera.aspectRatio, o); - } - - if (camera.yfov > 0) { - SerializeNumberProperty("yfov", camera.yfov, o); - } - - if (camera.extras.Type() != NULL_TYPE) { - SerializeValue("extras", camera.extras, o); - } -} - -static void SerializeGltfCamera(const Camera &camera, detail::json &o) { - SerializeStringProperty("type", camera.type, o); - if (!camera.name.empty()) { - SerializeStringProperty("name", camera.name, o); - } - - if (camera.type.compare("orthographic") == 0) { - detail::json orthographic; - SerializeGltfOrthographicCamera(camera.orthographic, orthographic); - detail::JsonAddMember(o, "orthographic", std::move(orthographic)); - } else if (camera.type.compare("perspective") == 0) { - detail::json perspective; - SerializeGltfPerspectiveCamera(camera.perspective, perspective); - detail::JsonAddMember(o, "perspective", std::move(perspective)); - } else { - // ??? - } - - if (camera.extras.Type() != NULL_TYPE) { - SerializeValue("extras", camera.extras, o); - } - SerializeExtensionMap(camera.extensions, o); -} - -static void SerializeGltfScene(const Scene &scene, detail::json &o) { - SerializeNumberArrayProperty("nodes", scene.nodes, o); - - if (scene.name.size()) { - SerializeStringProperty("name", scene.name, o); - } - if (scene.extras.Type() != NULL_TYPE) { - SerializeValue("extras", scene.extras, o); - } - SerializeExtensionMap(scene.extensions, o); -} - -static void SerializeGltfSkin(const Skin &skin, detail::json &o) { - // required - SerializeNumberArrayProperty("joints", skin.joints, o); - - if (skin.inverseBindMatrices >= 0) { - SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o); - } - - if (skin.skeleton >= 0) { - SerializeNumberProperty("skeleton", skin.skeleton, o); - } - - if (skin.name.size()) { - SerializeStringProperty("name", skin.name, o); - } -} - -static void SerializeGltfTexture(const Texture &texture, detail::json &o) { - if (texture.sampler > -1) { - SerializeNumberProperty("sampler", texture.sampler, o); - } - if (texture.source > -1) { - SerializeNumberProperty("source", texture.source, o); - } - if (texture.name.size()) { - SerializeStringProperty("name", texture.name, o); - } - if (texture.extras.Type() != NULL_TYPE) { - SerializeValue("extras", texture.extras, o); - } - SerializeExtensionMap(texture.extensions, o); -} - -/// -/// Serialize all properties except buffers and images. -/// -static void SerializeGltfModel(const Model *model, detail::json &o) { - // ACCESSORS - if (model->accessors.size()) { - detail::json accessors; - detail::JsonReserveArray(accessors, model->accessors.size()); - for (unsigned int i = 0; i < model->accessors.size(); ++i) { - detail::json accessor; - SerializeGltfAccessor(model->accessors[i], accessor); - detail::JsonPushBack(accessors, std::move(accessor)); - } - detail::JsonAddMember(o, "accessors", std::move(accessors)); - } - - // ANIMATIONS - if (model->animations.size()) { - detail::json animations; - detail::JsonReserveArray(animations, model->animations.size()); - for (unsigned int i = 0; i < model->animations.size(); ++i) { - if (model->animations[i].channels.size()) { - detail::json animation; - SerializeGltfAnimation(model->animations[i], animation); - detail::JsonPushBack(animations, std::move(animation)); - } - } - - detail::JsonAddMember(o, "animations", std::move(animations)); - } - - // ASSET - detail::json asset; - SerializeGltfAsset(model->asset, asset); - detail::JsonAddMember(o, "asset", std::move(asset)); - - // BUFFERVIEWS - if (model->bufferViews.size()) { - detail::json bufferViews; - detail::JsonReserveArray(bufferViews, model->bufferViews.size()); - for (unsigned int i = 0; i < model->bufferViews.size(); ++i) { - detail::json bufferView; - SerializeGltfBufferView(model->bufferViews[i], bufferView); - detail::JsonPushBack(bufferViews, std::move(bufferView)); - } - detail::JsonAddMember(o, "bufferViews", std::move(bufferViews)); - } - - // Extensions required - if (model->extensionsRequired.size()) { - SerializeStringArrayProperty("extensionsRequired", - model->extensionsRequired, o); - } - - // MATERIALS - if (model->materials.size()) { - detail::json materials; - detail::JsonReserveArray(materials, model->materials.size()); - for (unsigned int i = 0; i < model->materials.size(); ++i) { - detail::json material; - SerializeGltfMaterial(model->materials[i], material); - - if (detail::JsonIsNull(material)) { - // Issue 294. - // `material` does not have any required parameters - // so the result may be null(unmodified) when all material parameters - // have default value. - // - // null is not allowed thus we create an empty JSON object. - detail::JsonSetObject(material); - } - detail::JsonPushBack(materials, std::move(material)); - } - detail::JsonAddMember(o, "materials", std::move(materials)); - } - - // MESHES - if (model->meshes.size()) { - detail::json meshes; - detail::JsonReserveArray(meshes, model->meshes.size()); - for (unsigned int i = 0; i < model->meshes.size(); ++i) { - detail::json mesh; - SerializeGltfMesh(model->meshes[i], mesh); - detail::JsonPushBack(meshes, std::move(mesh)); - } - detail::JsonAddMember(o, "meshes", std::move(meshes)); - } - - // NODES - if (model->nodes.size()) { - detail::json nodes; - detail::JsonReserveArray(nodes, model->nodes.size()); - for (unsigned int i = 0; i < model->nodes.size(); ++i) { - detail::json node; - SerializeGltfNode(model->nodes[i], node); - detail::JsonPushBack(nodes, std::move(node)); - } - detail::JsonAddMember(o, "nodes", std::move(nodes)); - } - - // SCENE - if (model->defaultScene > -1) { - SerializeNumberProperty("scene", model->defaultScene, o); - } - - // SCENES - if (model->scenes.size()) { - detail::json scenes; - detail::JsonReserveArray(scenes, model->scenes.size()); - for (unsigned int i = 0; i < model->scenes.size(); ++i) { - detail::json currentScene; - SerializeGltfScene(model->scenes[i], currentScene); - detail::JsonPushBack(scenes, std::move(currentScene)); - } - detail::JsonAddMember(o, "scenes", std::move(scenes)); - } - - // SKINS - if (model->skins.size()) { - detail::json skins; - detail::JsonReserveArray(skins, model->skins.size()); - for (unsigned int i = 0; i < model->skins.size(); ++i) { - detail::json skin; - SerializeGltfSkin(model->skins[i], skin); - detail::JsonPushBack(skins, std::move(skin)); - } - detail::JsonAddMember(o, "skins", std::move(skins)); - } - - // TEXTURES - if (model->textures.size()) { - detail::json textures; - detail::JsonReserveArray(textures, model->textures.size()); - for (unsigned int i = 0; i < model->textures.size(); ++i) { - detail::json texture; - SerializeGltfTexture(model->textures[i], texture); - detail::JsonPushBack(textures, std::move(texture)); - } - detail::JsonAddMember(o, "textures", std::move(textures)); - } - - // SAMPLERS - if (model->samplers.size()) { - detail::json samplers; - detail::JsonReserveArray(samplers, model->samplers.size()); - for (unsigned int i = 0; i < model->samplers.size(); ++i) { - detail::json sampler; - SerializeGltfSampler(model->samplers[i], sampler); - detail::JsonPushBack(samplers, std::move(sampler)); - } - detail::JsonAddMember(o, "samplers", std::move(samplers)); - } - - // CAMERAS - if (model->cameras.size()) { - detail::json cameras; - detail::JsonReserveArray(cameras, model->cameras.size()); - for (unsigned int i = 0; i < model->cameras.size(); ++i) { - detail::json camera; - SerializeGltfCamera(model->cameras[i], camera); - detail::JsonPushBack(cameras, std::move(camera)); - } - detail::JsonAddMember(o, "cameras", std::move(cameras)); - } - - // EXTENSIONS - SerializeExtensionMap(model->extensions, o); - - auto extensionsUsed = model->extensionsUsed; - - // LIGHTS as KHR_lights_punctual - if (model->lights.size()) { - detail::json lights; - detail::JsonReserveArray(lights, model->lights.size()); - for (unsigned int i = 0; i < model->lights.size(); ++i) { - detail::json light; - SerializeGltfLight(model->lights[i], light); - detail::JsonPushBack(lights, std::move(light)); - } - detail::json khr_lights_cmn; - detail::JsonAddMember(khr_lights_cmn, "lights", std::move(lights)); - detail::json ext_j; - - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - detail::JsonAssign(ext_j, detail::GetValue(it)); - } - } - - detail::JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn)); - - detail::JsonAddMember(o, "extensions", std::move(ext_j)); - - // Also add "KHR_lights_punctual" to `extensionsUsed` - { - auto has_khr_lights_punctual = - std::find_if(extensionsUsed.begin(), extensionsUsed.end(), - [](const std::string &s) { - return (s.compare("KHR_lights_punctual") == 0); - }); - - if (has_khr_lights_punctual == extensionsUsed.end()) { - extensionsUsed.push_back("KHR_lights_punctual"); - } - } - } - - // Extensions used - if (extensionsUsed.size()) { - SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o); - } - - // EXTRAS - if (model->extras.Type() != NULL_TYPE) { - SerializeValue("extras", model->extras, o); - } -} - -static bool WriteGltfStream(std::ostream &stream, const std::string &content) { - stream << content << std::endl; - return stream.good(); -} - -static bool WriteGltfFile(const std::string &output, - const std::string &content) { -#ifdef _WIN32 -#if defined(_MSC_VER) - std::ofstream gltfFile(UTF8ToWchar(output).c_str()); -#elif defined(__GLIBCXX__) - int file_descriptor = _wopen(UTF8ToWchar(output).c_str(), - _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); - __gnu_cxx::stdio_filebuf wfile_buf( - file_descriptor, std::ios_base::out | std::ios_base::binary); - std::ostream gltfFile(&wfile_buf); - if (!wfile_buf.is_open()) return false; -#else - std::ofstream gltfFile(output.c_str()); - if (!gltfFile.is_open()) return false; -#endif -#else - std::ofstream gltfFile(output.c_str()); - if (!gltfFile.is_open()) return false; -#endif - return WriteGltfStream(gltfFile, content); -} - -static bool WriteBinaryGltfStream(std::ostream &stream, - const std::string &content, - const std::vector &binBuffer) { - const std::string header = "glTF"; - const int version = 2; - - const uint32_t content_size = uint32_t(content.size()); - const uint32_t binBuffer_size = uint32_t(binBuffer.size()); - // determine number of padding bytes required to ensure 4 byte alignment - const uint32_t content_padding_size = - content_size % 4 == 0 ? 0 : 4 - content_size % 4; - const uint32_t bin_padding_size = - binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4; - - // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info. - // Chunk data must be located at 4-byte boundary, which may require padding - const uint32_t length = - 12 + 8 + content_size + content_padding_size + - (binBuffer_size ? (8 + binBuffer_size + bin_padding_size) : 0); - - stream.write(header.c_str(), std::streamsize(header.size())); - stream.write(reinterpret_cast(&version), sizeof(version)); - stream.write(reinterpret_cast(&length), sizeof(length)); - - // JSON chunk info, then JSON data - const uint32_t model_length = uint32_t(content.size()) + content_padding_size; - const uint32_t model_format = 0x4E4F534A; - stream.write(reinterpret_cast(&model_length), - sizeof(model_length)); - stream.write(reinterpret_cast(&model_format), - sizeof(model_format)); - stream.write(content.c_str(), std::streamsize(content.size())); - - // Chunk must be multiplies of 4, so pad with spaces - if (content_padding_size > 0) { - const std::string padding = std::string(size_t(content_padding_size), ' '); - stream.write(padding.c_str(), std::streamsize(padding.size())); - } - if (binBuffer.size() > 0) { - // BIN chunk info, then BIN data - const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size; - const uint32_t bin_format = 0x004e4942; - stream.write(reinterpret_cast(&bin_length), - sizeof(bin_length)); - stream.write(reinterpret_cast(&bin_format), - sizeof(bin_format)); - stream.write(reinterpret_cast(binBuffer.data()), - std::streamsize(binBuffer.size())); - // Chunksize must be multiplies of 4, so pad with zeroes - if (bin_padding_size > 0) { - const std::vector padding = - std::vector(size_t(bin_padding_size), 0); - stream.write(reinterpret_cast(padding.data()), - std::streamsize(padding.size())); - } - } - - stream.flush(); - return stream.good(); -} - -static bool WriteBinaryGltfFile(const std::string &output, - const std::string &content, - const std::vector &binBuffer) { -#ifdef _WIN32 -#if defined(_MSC_VER) - std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary); -#elif defined(__GLIBCXX__) - int file_descriptor = _wopen(UTF8ToWchar(output).c_str(), - _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); - __gnu_cxx::stdio_filebuf wfile_buf( - file_descriptor, std::ios_base::out | std::ios_base::binary); - std::ostream gltfFile(&wfile_buf); -#else - std::ofstream gltfFile(output.c_str(), std::ios::binary); -#endif -#else - std::ofstream gltfFile(output.c_str(), std::ios::binary); -#endif - return WriteBinaryGltfStream(gltfFile, content, binBuffer); -} - -bool TinyGLTF::WriteGltfSceneToStream(const Model *model, std::ostream &stream, - bool prettyPrint = true, - bool writeBinary = false) { - detail::JsonDocument output; - - /// Serialize all properties except buffers and images. - SerializeGltfModel(model, output); - - // BUFFERS - std::vector binBuffer; - if (model->buffers.size()) { - detail::json buffers; - detail::JsonReserveArray(buffers, model->buffers.size()); - for (unsigned int i = 0; i < model->buffers.size(); ++i) { - detail::json buffer; - if (writeBinary && i == 0 && model->buffers[i].uri.empty()) { - SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer); - } else { - SerializeGltfBuffer(model->buffers[i], buffer); - } - detail::JsonPushBack(buffers, std::move(buffer)); - } - detail::JsonAddMember(output, "buffers", std::move(buffers)); - } - - // IMAGES - if (model->images.size()) { - detail::json images; - detail::JsonReserveArray(images, model->images.size()); - for (unsigned int i = 0; i < model->images.size(); ++i) { - detail::json image; - - std::string dummystring = ""; - // UpdateImageObject need baseDir but only uses it if embeddedImages is - // enabled, since we won't write separate images when writing to a stream - // we - std::string uri; - if (!UpdateImageObject(model->images[i], dummystring, int(i), true, - &uri_cb, &this->WriteImageData, - this->write_image_user_data_, &uri)) { - return false; - } - SerializeGltfImage(model->images[i], uri, image); - detail::JsonPushBack(images, std::move(image)); - } - detail::JsonAddMember(output, "images", std::move(images)); - } - - if (writeBinary) { - return WriteBinaryGltfStream(stream, detail::JsonToString(output), binBuffer); - } else { - return WriteGltfStream(stream, detail::JsonToString(output, prettyPrint ? 2 : -1)); - } -} - -bool TinyGLTF::WriteGltfSceneToFile(const Model *model, - const std::string &filename, - bool embedImages = false, - bool embedBuffers = false, - bool prettyPrint = true, - bool writeBinary = false) { - detail::JsonDocument output; - std::string defaultBinFilename = GetBaseFilename(filename); - std::string defaultBinFileExt = ".bin"; - std::string::size_type pos = - defaultBinFilename.rfind('.', defaultBinFilename.length()); - - if (pos != std::string::npos) { - defaultBinFilename = defaultBinFilename.substr(0, pos); - } - std::string baseDir = GetBaseDir(filename); - if (baseDir.empty()) { - baseDir = "./"; - } - /// Serialize all properties except buffers and images. - SerializeGltfModel(model, output); - - // BUFFERS - std::vector usedFilenames; - std::vector binBuffer; - if (model->buffers.size()) { - detail::json buffers; - detail::JsonReserveArray(buffers, model->buffers.size()); - for (unsigned int i = 0; i < model->buffers.size(); ++i) { - detail::json buffer; - if (writeBinary && i == 0 && model->buffers[i].uri.empty()) { - SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer); - } else if (embedBuffers) { - SerializeGltfBuffer(model->buffers[i], buffer); - } else { - std::string binSavePath; - std::string binFilename; - std::string binUri; - if (!model->buffers[i].uri.empty() && - !IsDataURI(model->buffers[i].uri)) { - binUri = model->buffers[i].uri; - if (!uri_cb.decode(binUri, &binFilename, uri_cb.user_data)) { - return false; - } - } else { - binFilename = defaultBinFilename + defaultBinFileExt; - bool inUse = true; - int numUsed = 0; - while (inUse) { - inUse = false; - for (const std::string &usedName : usedFilenames) { - if (binFilename.compare(usedName) != 0) continue; - inUse = true; - binFilename = defaultBinFilename + std::to_string(numUsed++) + - defaultBinFileExt; - break; - } - } - - if (uri_cb.encode) { - if (!uri_cb.encode(binFilename, "buffer", &binUri, - uri_cb.user_data)) { - return false; - } - } else { - binUri = binFilename; - } - } - usedFilenames.push_back(binFilename); - binSavePath = JoinPath(baseDir, binFilename); - if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath, - binUri)) { - return false; - } - } - detail::JsonPushBack(buffers, std::move(buffer)); - } - detail::JsonAddMember(output, "buffers", std::move(buffers)); - } - - // IMAGES - if (model->images.size()) { - detail::json images; - detail::JsonReserveArray(images, model->images.size()); - for (unsigned int i = 0; i < model->images.size(); ++i) { - detail::json image; - - std::string uri; - if (!UpdateImageObject(model->images[i], baseDir, int(i), embedImages, - &uri_cb, &this->WriteImageData, - this->write_image_user_data_, &uri)) { - return false; - } - SerializeGltfImage(model->images[i], uri, image); - detail::JsonPushBack(images, std::move(image)); - } - detail::JsonAddMember(output, "images", std::move(images)); - } - - if (writeBinary) { - return WriteBinaryGltfFile(filename, detail::JsonToString(output), binBuffer); - } else { - return WriteGltfFile(filename, detail::JsonToString(output, (prettyPrint ? 2 : -1))); - } -} - -} // namespace tinygltf - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#endif // TINYGLTF_IMPLEMENTATION diff --git a/External/VMA/VMA.cpp b/External/VMA/VMA.cpp index b6d337c..e2de74f 100644 --- a/External/VMA/VMA.cpp +++ b/External/VMA/VMA.cpp @@ -1,2 +1,3 @@ #define VMA_IMPLEMENTATION +//#define VMA_VULKAN_VERSION 1002000 #include "VMA.h" \ No newline at end of file diff --git a/Shaders/ForwardFrag.frag b/Shaders/ForwardFrag.frag index be954fe..d1da88f 100644 --- a/Shaders/ForwardFrag.frag +++ b/Shaders/ForwardFrag.frag @@ -4,6 +4,8 @@ #include "camera.glsl" #include "lights.glsl" +#define PI 3.14159265359 + layout(location = 0) in vec4 fPosition; layout(location = 1) in vec3 fNormal; layout(location = 2) in vec2 fTexcoord; @@ -12,14 +14,13 @@ layout(location = 3) noperspective in vec2 fUV; layout(location = 0) out vec4 FragColor; layout(set = 1, binding = 1) uniform sampler2D textures[MAX_TEXTURES]; -//layout(set = 1, binding = 2) uniform sampler2D roughnessTextures[MAX_TEXTURES]; -//layout(set = 1, binding = 3) uniform sampler2D metalnessTextures[MAX_TEXTURES]; -//layout(set = 1, binding = 4) uniform sampler2D normalTextures[MAX_TEXTURES]; layout(set = 2, binding = 0) uniform samplerCube pointShadowMaps[MAX_POINT_LIGHT_SHADOWS]; layout(set = 2, binding = 1) uniform sampler2D spotShadowMaps[MAX_SPOT_LIGHT_SHADOWS]; layout(set = 2, binding = 2) uniform sampler2D dirShadowMaps[MAX_DIR_LIGHT_SHADOWS*SHADOW_CASCADES]; +layout(set = 4, binding = 0) uniform sampler2D SSAO; + struct LightGrid { uint offset; @@ -47,7 +48,7 @@ const mat4 biasMat = mat4( 0.5, 0.5, 0.0, 1.0 ); -vec3 pcfSampleOffsets[20] = vec3[]( +const vec3 pcfSampleOffsets[20] = vec3[]( vec3( 1, 1, 1), vec3( 1, -1, 1), vec3(-1, -1, 1), vec3(-1, 1, 1), vec3( 1, 1, -1), vec3( 1, -1, -1), vec3(-1, -1, -1), vec3(-1, 1, -1), vec3( 1, 1, 0), vec3( 1, -1, 0), vec3(-1, -1, 0), vec3(-1, 1, 0), @@ -102,8 +103,6 @@ layout(std430, set = 1, binding = 3) buffer Lights vec4 cascadeFrustumSizeRatios[SHADOW_CASCADES]; }; -#define PI 3.14159265359 - vec3 FresnelSchlick(float cosTheta, vec3 F0) { return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0); @@ -247,6 +246,27 @@ vec3 NormalMapping(uint textureId, float multiplier) return result; } +float GetSSAO() +{ +#if SOFT_SSAO + vec2 texelSize = 1.0 / vec2(textureSize(SSAO, 0)); + + float ssao = 0.0; + for (int x = -2; x < 2; ++x) + { + for (int y = -2; y < 2; ++y) + { + vec2 offset = vec2(float(x), float(y)) * texelSize; + ssao += texture(SSAO, fUV + offset).r; + } + } + + return ssao / 16.0; +#else + return texture(SSAO, fUV).r; +#endif +} + void main() { Material material = materials[materialId]; @@ -257,7 +277,9 @@ void main() float roughness = texture(textures[material.roughnessId], fTexcoord).g * material.roughnessVal; float metalness = texture(textures[material.metalnessId], fTexcoord).b * material.metalnessVal; - vec3 ambient = albedo * ambientLight; + float ssao = GetSSAO(); + + vec3 ambient = albedo * ambientLight * ssao; vec3 lighting = vec3(0.0); float linearDepth = fPosition.w; @@ -397,6 +419,10 @@ void main() ); result = lighting + ambient + cascadeColors[uint(cascade - (4.0 * floor(cascade/4.0)))] * 0.18; break; + + case 9: + result = vec3(ssao); + break; } result = vec3(1.0) - exp(-result * exposure); diff --git a/Shaders/ForwardFrag.spv b/Shaders/ForwardFrag.spv index fa1e910467a6790c2c1a621db1471fe28ed6076e..c7d9ecbf89731b392b40150242934ebbefa62e61 100644 GIT binary patch literal 39248 zcmb82cVJ#+-Tn_GY0KVAS#23*ZvLj1 z8*qZ6AR;0l;=+ZBi>1Qv^EuDCk~@#}kKglhzxQ=r-*L|~bHI*er%tS@WvUgbm8z?{ zs`|HDwJb_iS?@@_LN%f0vkske=(fWnJ=^ZQ%l101Ty)J{ zZ=m<&p1z^pc_V#i^y)XM@ar3x*T1-@bm+pP>_->=E78UQL%qWTz5R3Mo!Z|w@APT& zci7{M`8(}FANju(u+JMDKKj(&k!~PLN426cjI^s-u37`UU*GV^P~Y6eBYlGdvt}JX z&1egWYX4R7IlPHojrzbEcU7xX4-GC}aOyzs@NlgySFKAut9Ni=@5s>EO@~ZLDzn*U zXSFu<;XQ*R$JV^7T8Dakp5?2J&_?qt>>D|Cv{&g_Yb>ueSDmM$TA#X=v!mJod}ytA zRU1-|&%biD71|Lqk2oAK!?#!-Q%VeCHoA zI9yM{oJHO9*n^I7SM=dJbYb@*u-a?Xx)(0$@7;HB@xTa5XSEyJ+`ev?zV_K&pTYSf z&2VaMI@%F~eFGzPBNa#7gi6g{Ij#wKPe=3k9O~}rTTIqy9!K+9Q;RmS_VWlG?c+fM zJ-uh?-?Dj(v}|)mdlMUnX|-tH{5kbdj$)4BX=egB<4Hd~mQP;P-P6M+@#GMF64*63 zVrcL*AiZCwI)Hj~?q@F?=xfdY@H7su#q&nC8JmY~*ihTd-=VJhb`Z7xKVaefop(NI z*Vng>;jC3>wJ*8reOI?Nq1umn{!sUVqX+AW-LG%{eEKk<+8^D19X)seR*!7^HmN=q z4zKssVcm-sx#Jh-ki_0q9ZcLv?^z>@hk6g{soUtP4xwJSxPPQ?QGZ`=y|25f*=VzR zN9N3#G25+4WJh%hwI>&`vE4h0JF8Q{9_I(vw$5r{cx~^f-UuI9ys-9l6ZP~C4C-T^ z3#e(b#urnc-r&or6LST4ET*%1FMRC0(pi0=;jQyPXLU1t!Gg1Bz16>4s26n)b+_8= ztZr-LABE$;16AF}kG0u9F^+dspM|%^+EslCJpR1fRo#W=-tHcl=eDcI_6_R8_dB4@ z|4ne&pKrJEd&lvP>U;266zBT>akkFt2W|YpHvZ5!-ckJ+?*28`W8-X{)#KxMNA)E9 z^!@bhs-6b39=$`|BcA$d?HTHNUHS%URlk?eh8OjEUeXPvvwDTv^=hq8SG5`k|00g3 zeFq1Jde{*9Oh#)R%dOm-z}crg=v__g4(Yur`s59V0kuXoXiTd~e} zDq2t98GSu$XpfgO;PVy_4fPI;jPqN9KGtqWbw0eWY2)o+4|?OTRUa7X9a=QV8MmH- zTE8BBUjN{5?{JxSLUj}RBF~4da{~wCF#cWDH|ji`#=7VBx5m{~J%iS|7T{L5*Ht}> z)*Pp!=Uwkd<{#aA);#B}>0<{c&7LEUuXB5^;(0z75$9~V+I|A|&T1v`F~=THXw6q` z@e{(=fRA01I_vwM`K^;l?Y}Pek?w&7%wgT$RQTM!D6Mtb3hn5bhu8jlg1Kek%%ZKU zI>`8OYuZ^IjOIxueK-<6JlxIIw3Y8Dc-H)N;MSUVRz2Xs`SXW+>)ajn{n`9uj;-4{ z7u#W+@`w7oy0*^yYW~KqFB2*?rnK$6Ke5(zAH8NDn>;OB2e#2`e4DKkTWb#IjIh}j zxFfxm*j883mM5F&6Tq!|i29T~s(VcuZ3k2QC!vq_tg#aAD5UOJky zv$_>Lb}!jk-3c#`$4|k_>&9ot@viEN^0C98{9V;o&_?%A-DX$y9kkK4tF_MRA+)jM zuCsa=-o0>cUyk*kz{l=uT4Pf4SGMUrmbzmt@9)%fAKPCOYd`l&>-@_-XmNk1zk8)+ z8-IVN&Aqa;`#Wv!m-7Bj&Arlof2ZzV8GnD*RXv72XA##c@Bgv8ero>4kF}*c*1my( z-l2ULk8n;J^*5w>?5l6;lgBX+F6R6+>fdV9@s7<;OLLqWgVjN?0{Ek~I$K_tg zytJ-E%hm0;59W0b&+G2#ox`I5kG`JT=JmGvx_s^9erS9S7+O3$vUs64Xgpx`4c2}3 zoc7CR?%XfG2lqD*--M!cRe!9tf!^++)~%TH{};3-FCHD$v+%y!e`57Ac;9aCJ!dYS zKfiZq_T1BWmE!z#)i(2bJXl9-9g}Aqs~n9q^?hgQ=i2lQoKZjCxxTjLdNf)S-_l(N z?^|mT9DmoK{k&iv*Py3&?&1Z94feE--$`|T=bv{z!<+$*?#qLAov&rD*I~@=1ULI( zxjMdBgQc#MZLeS(t+ib0I=Q#>TXD>f%ivI-s~y`)WBr^nue-io9Ub$^W44*T8ps^4 zf_~27EG>1kRn-p2^T34a71}M=6LamW?r&bb*6x*#Y8e-ZpL=9w@Hx&{!#9ABK%E?~ z$-bXk(0{TI#Py>^E3T{BrtxboFSYMf4w8R+!^Gi#`?hXjs6&0p>}qq)cC z)o)2`e`2-oxSS@KGiN4uw$7(Ee`9U78gPvJH#VQ&4;q)F?(yvAHn;BgVc3Ur?rC+q zZhLmAk2PJltJ+fY9A_kKUDfwc>XT$rS$XM?LN-?J&#rO|8*~=?SI`%Y4hB@w0jh7R@vXEa>kYZCAakIxKqq7P6%siROEe0p2>ap3NszC!_uM`{vGS z5Pj@8noyl7=Ug_Yeg|E@BB7Pe>Kyd)_m>jtU*6Bh54JL`9q zQ&@lZQ}|@CKe_KBwYwL?wTJs&(!aeW-uIAlfBO6PQ+fP-=Qjn;6xVjS`TWh|`r+SN zz4tV>{<|yKeSIJ+>3bZ1Q(M)jW^Og_XXB@9I z&kJtfpMv`<$D@|%^Wt`adrWbK)V{xsy%KlhV_~~Qp<%O30ab=-pf81DT*&jK#xgNgz zG2c_QUu!*9gqQu5>*L=5GfuVk{(2qO@5+ubKi~bRT~lbj_ffm9(A;-w*Ed?;@2L56 zKdZT(?%!qbn^IfG_~e57e#f!dmVKT9_m_Op7ZswhdpY!gV>XUadiL)PS{8Rh; zKE{5O+UX8s^NQs&9Odw zA8KRNHst7=PM!6zwq1Qwi;b~wGpKEMI>j}aNv&NybFANiaQ*CyYjhB`xud&R4u>0S zAFYp~RMnDw!t0M3P{L2F?Ke&j_ua(fs~j}S-(K6R8C#Rhwth}AXG-G!RQoL%je7yU z&gXx)n|AxV<}$5)8NMFeJ-sIXe|@lfVhXi=R(HHp;f_~*YsPE6DWz!U`7_5B zU^R23t>YWJTw4!ZNp5?mx9oM_wO$UXl5BMoSq#bRZzx9{7znho|i(JuGhi+0cbTY$CYnA;NWe5mX1 z^}=^C{xbeK@RwhC<(2Rg;ku;1zMnC_cIW9Fjn$GF8}cURT(Iq_w-@KGEcX9A{Oyl% z#-IQH5x)VW@|}t?8-jgzA~)}a)cjLx_FL}z6SWQDz7w%8_EG;!;9j59^_Tmdj+*wj zHP{&W<#7I~+s-`QfZ$ldKL~d&!f%F~&wb;2nDQE%cNq4^m>ntN)M8`IWgi{|1M6JA z>o8tzL*AnKJ|Sz+UfX7JrnW!6A26Cp!@wYD)<9tr{`Q2-P{7JBTb@$-2?6(Imu+|*E+~+2>?5$(L_VXL;F(|(l?)>GR*ym%pdm`&`4;rV~S8Ug3x3udsn%q3kQQL-^?aF;lQ}bBL z-z6Pij+@lBmE$$FIbSB;z8EWChV5iNkNwQmDIO4Xe``G5|J`u4981=*&29A39^1Sk z<|tyyxSl3w;?VNaC%X&SC=DQN@KWgmddOn5bdkXvcXR!VB`APr3!2PM)Pr1*_ z{vH`{YB9EE0|%{QmPYT3U>gR>s3xobVs9MkA(t@&>0$D&7T=_}NEk3;ud zX+C4Uhtk)3tItrpC$o>;XwH#+$v)`?KXc@6d)vo;u>EyyTu1prin+{desi1aOt881 zkzYvdbusJlb~xL!-h0lM-1iA;yYlqnTs6~IYcHT+uIJYKi6s>C8*AQM+qmD@#(p4n zzp)Ls_8Z%9E zJK@%Tw-c`2Z+611Z*SvYE4canRww?(`>js6wcqN5Yxi57l0VSK{Z=P->xbL;BLz3# zqix)8brP@LZ*@xkWWil8ztxFd?)N(3p1=KuB;0&{LsD|TAqltkyOEOn-AKv(W~AhP zFH&;96)Cyjij>@ML`v@WA>qE0^4pMb>%oF+_uG%y-T!|35pKPtjr;9K?2g}WKT7Vm zA0_wOk8tZN+W3_PH=o~r#9!{WA0_wOkCOZCN4V?dw;$nNTka^h`TR~IcDdh3ggYM( z6kOeJB4W4pdx(8+_fT@b zdk8n5-#wJv?;c9-cMm1^yN8nd-9xz7A-{VF_gdmN5AqpY1$<6>JH=-=-=*!vHNflX zWsN?AtBlWX@1XcRsE;=Ne2%<=qU}g>O(O1{jRvgqJDw}iyv7*2H&-CJe#UqWy9#U` z%yo=?yBcnc@9T__>z9~!fsNsx(U@x>#+*noMy_9Ct_2&z>0mVG-Ed=k&t{BVzr?%; zYz#|28gm`o7~hK+BiAo6{|7dPX&#MvFWea4CmJKy&lul%iY0L-U#@s+LMy_9CZUh_STBV(v;Kum;ZH!#M#C!;BjBA;gTj0j{jBbov zzu0dBYq#&&Qy+$_>*ITrkAQvGqppwlfFA{`%co%b7})nc#}VVb^~d4*sLy5;j^h(x zb&EgTE1~V!=TAcHOZK-hx5L#h!bg4w*gWcv$9o{PjOWwfj5p)?44S(B-V@37cRfA_ z*55s%zw7#WxVrw{Gs$9ufz3GcfUB!Z-CY9Pv+yBaNGAd zGnanff~&g*@^6EURd)>DH>za}_kzvgKFt{J1FL5Y-vv7c^^D)mP*UskR>2{!DA^K4&jvj=dhf3Y&KKna^)>b9)}# z2)rR>HhHFyU}LbF#h*FtpYPeGQqn)KRpxk+dXvUxj>+InDQXsf=2(QU`8R9qmogUl z=5X6ox4*_uqo|wT{%--c-?@fv3D(D7w5=M=wvM2`zCYZWqOUe{IDVg3)jdWJ#CKb; zTK2H*Z3kDM!FaZ(-hq;Fd%i4vcWms*w-ela{v9R#-x;ocA#Iv}7qD^a?r*hS!M3X{ z?Y;*5zuMgmoA&tb-sW4j|5|L?)BYY{U$?aicU zx4rq)>f(NCk4Mkj3&EcEt-V$bQZJ(T)BeWBZfzet?uNkT2p!a>9 zOFlp`w{3|Ndk)zB7XBu%xpS<%8LW@GbLF|^EtIn<&XxAWp4-?Q<6^Kpwk2RSbDU58 zR*LsF=Fq0!Ph7{w<~h=}yAZDC&*R?v`HSG^QH-@MxqimDA1?+QgSn3Je*6-+F}Zfi z^-DXKf{mdIqwTy6ZjARJwjD%RCV>+7{`}PjF zF?pVp>z8(}02`C%N!xiR+?YI1%JoY-SAvakKAbn(xe9KK``huz^-IjvU}N-6%)8*m zcn@lfTt8zTLAwTQjB91gwcvMCjPbgopYQ$O1J~D|e%FEjkD_nZQLbNN-U~J+>uAjT zD8^(R<@zP&dayAxJ=)Iu;l^Yg<@zP&2Cy;wGaB;&h%s45xqgYc5o}D>(Y}3U{p6?M`lx5W zeH!d|b4@ePXW;7b`Rr1BK8L2B+@A*JH;)V>Jz?>yRaU-~7mW1H64 zy)Siqx1(#zc)ko)3;#;P(}%CZ)$~ien)qwf=5-|th)v-cka+pj!tJC+~9)%AaX zTAo}#0()Pg?IEx{eg84o_X*k_2Fqjn3E2A=ZI6KE$@x?8osI2LuspWM!5(+UJqDJ? z_A~G)6m3sX%VYaF_=LvxB(;2Z{yy#ao&s-0?OZrM$6sl`!LV@ zzXY2{JwCqzyXUg+e+_out0(R^U}LqVuTO)0M{quS(Ej}vuBNYJ)28P7E~0ktSsU-Y z%J0Dal-y7J9;}x0w>Gu%UT6?*?mtlHKIxBOb6b0#^e5^+Q~VkGm&R_Lwx0!?BmA%6 z!zj5=`Wsjub&nzWGZb^%mN>C5fR8Eoi(qqSTrYw3QO|wSzrfE^Ja=nPu788IImW+J z%VVP(wPud`TLZZFNzRux{T_pNfS09sZE;;X!D{|;{B*%zA$P8?a{Y{P-%J1-V`^i( z?oWgpll777mv$zB>$Zz_mTO{ieU$TvdCzg*f{mvV{8kyO>H^XZU^>Ut1b5!+k+k3G>Z1zV{8i6mVWI3Rtw*; z;pxLpa5ep$AAQv_j$Ocx)#Euic12T9pI-x3^IQ1j-3_kpzY}0y|K_chysrhDH}@;9 z#U5zt$-5_5t<1X@Ts?XH+qs%~-HZEx&0Ajcr=zLc|N8g=yLYZ3#&&0d^_gL5^6U$4 z&$AzzdhUDo2fGIDFKy}f0bp}#Pn)yAwwc%i!HLzDc{>Pfymsf!ZvoUY_lKxclIKvc zc~+y;HewD&Q%}s{V72hsVCOA+=?JhnHl^sR-L?;-)|R-Vz-r-hz^-fd>Cs?))Z=pu zxL%{?csmwe#vBLNM?Ld*JlI^?lKTX($AGr<`$Ta19eZpiH8%gfBmLY%j_F9Sw)F3H z;PUu*JzSrx#~Z+E`Xye?ed4)(KG=PdcRpSd7NDtLjgS0Pa4$vOcHM)0V9$p+@16!% z^B3*(MstkO`WuZ<-j}t$7hH&Dy#Csqr>kh&cijVU+tVL9;7axAr`?MOa5Nu zwd##%>UoA30;}a2Vg&5Z`yXw?6gBUE#Lkn~owLC0xz9#ZcTViiRFCmdF1>u>Sdbq34Q= z(A4$6fLgvgXC7@AgI`N+-|esMn^Qmioo{`--^?6YXHQ%T-iV^_CDd~J;(6`uVEdB4 z2fK$ZLsQrPZPfDQdIvbqGM9tp8S^{Ad6u~XERXFfaGqtZ1k022UEn;+Tn(1Tb}cy1 zGS`6RvAqX;3VpYq?*_|b`#*4=Wv&Cu^DOgTu+K8ih2wJ^_Sb&dzU`RLIPsG z%su>Bu$sT@_0PfYpyatjuAea;z8)E2sVZ;jJES7xG{O| zkn3lRd+EzyW0zB3oc4IH^?Z1Pjo_q6q z!LEh(=Gxq^-=bDep6`Nfr_A#`xVn48y>LIcd`9>_Tp#tc^#gF)(r#P#QLAS!{t)b5 zl)GOZ0{gdf|DOLN@WYg4SXBG+AXtBW^!IxGW3WECem?^4rKqQ`KLMB5??>Tk{xaV` zZ8ZBE?XgC4ALjb~IGXYLYxi7q6>I7>@d>!?>*H7-pjOM8KM8hT%lqh`!__nXUw||I zPR9L9c*g$}SbP4y>wfwbT%X*J{Tggeb^Gu$YBlk1sB;hDe)=uk{^Y!3zTd&s^?#aL zZvQ=Qe-G9_&lj%OAK>b_=lCO7E%zLM0{iovr0p4sn&%{Oa{m?Fp8IcT>i&J#w*C%Q z%lxVR1MYnJZbtr3xPI!+sq6AQ*tKv>&r!>5*L#&0z|T^QOJ80B+m~Fw&HFF7x;b8? zmYd%>`w!T;()Mp^d2FwMT?1_|Q_FJ?LKo_L5c_05%xk=U`nz8Gc%8_cS)1Eqa9L~~ zyZZWFs@ySn57G&C4CS*`7n-{Mezz)5u8H8>gVf)!Ha5q%960wNe$Oh;_*MYt9>i~3 z<;l4cIQJla=PHkF6>#oB{N`02+iKub*!%X=?_cGytpUzGh~L7>a}Tm6*n1G?!tpr{ z`)j{!-*(JrocSEvT43in_ZMr!k81SXU#x>>Zf)tuy5REuVm-K;zV=(2nrruA^1I)y zjraQfDr}zj+_&;q!_{(4)TUxzeymB$_6X0sGpV($U39gp$ zybi49f1e}okzNn?nDrRaKKlNn@zb93*~wtrd;=xl+noYdcg%CCkE6H_9>?>*YWif{ zJz({m59fo`dMTO91z^XoEpw?>&ZXyy~Nb9oxPoXgYU`luV@HFyB*wSKVKzk_hK z*cY|g-w0RBTn>TN{zR_KCfe0 z`;&X|cYxLWMZ2QW%pdKYjYd~T@7b?JGhTn~Uf*r=GO)J)(8Jv*{`-^mZ6%67|E}^H z{Jh@z@2`qIM*MeOy*8~(o&Fx#=;3o3o;5idZo4V&Dcd@RqD}wItN&YmwwHZ(9N0K@ zzpr!6PoOw%?{~C&Uejmvohn$n?^N~mzyFhUdL7tyPom`beLdJQXnUEl$ulRmw+6+Z z{~f8!!>R>dt-;RK>J;b4|5lCvo~ha?6zlMwHhyXwU)aXq*v5w&o;mA=+iv*WhUa*m z2iHg4wLgVgE&jb=wea~3Py7P7KI+EXe>MGG$3BYl5q=ujImzEGP6z9wo;B(RyGGL} z+Fe6)I~Us0mjSR^_+Z1+w?%M$)QxxU)Z#w`w!hk(M|o@`VEw}vgZ0hcI|HnC6eaEX zKP+LKHusPF<1C80ezxQKs3q??V726U6WG5CsmJHd;L_(UaQ)PiQ!W1If*nuzd0^vn zzFz{?N8LRwFZcAiaDQu2a-6SS;B~<6ds`-Yx>G zW!}uM7XM4YYT=hQJoETAxIXF`_hn#p?-`so`>2*2?*KbaZT43l+dCUu_?2+uvmdSk z+gJ7U;cBpP+7kCJuyGmZHDLYJZP$5Gi~qG?`x5?cus#{nd%*gryO#2DEjNPu^L&@{ zlh>T!^$WZ~gI!1e{Rr21W9qE&d%@)zf1r)u+{SNd~0agpY6Kwn0-=6~Oqwe05XYaXQTT%Q?rewcODe%++Z_;4**QN#D ztnk^qz|#u6MS-^jyKlFqcHb^X{aK26vyZ=2aP4;$T)%G=T>CfM__qtL|GjPedj&WC z{x<%DHvVA2&G%5jjsJ1MwLeyH?T@$dC)@bb1vmbgf*b!*!L`5AaM#^+{2aw~3;#TL z3rg;hz5v!oJ@>a?1bb}kPtorFavju@=gZ*D3;q?b?c|vIDp()&9CKd-=a|!eJjL9u zskY?48|?m)do0~U(I@wMUkBe!Q8(T_pqBV=f$f_%*IypncfgKCn|nc?cJ2eaf3>+U zztV)YHZv!1gqz2<%@NSJR-ksvLc@OGbn^&V9ZMim2F8EaNCIvV5c5U4MedjWM z|2FRbzO#16y$$v1aQmJAe!v=VHH$y1WhpaQFXPri(7*h5!PiDp&-$$cR!hnHt&3)U z?e0Uzs+QcZ0;}cU-E^K`4Oh>yoy~r_oiIkcAZl- z=U{v4&ET$A4}3f7X>hd#jM@Kw@Rk%c|F-Nq#VOQVQPdrqd~3?46!q+>ZNRRvdrF^e zDf+3~57$yn{~ht$9{d{Wa~r+`TuoosS?+te9jP6Uwcm>DOuY-mpY~lFyLEEE25gS- z-N3f>Y5L~BN3y$|qV8PEccPfvw#13u16=lVPq>=>bpOfi$6nONTHDWksHao>X`j*9 zt&@8u*c{>eg3EsH2iHg4e#-ZznA^6*#=5QtfL&L)=bBk?_gDTM@dLqX7JpW0_aKP# zm3PPogVka`1Z=-zKNPO!esJF&1~x`LF^7X4TViH|)nY#aT;@Cyu9loffsLv4W>3w5 z+m<%-xX+HJxVJpt$&aDvr|x-D?O2NA*Ovc|%5h-lOk3WQo}iuL&-L|uqLyR!M6h$Z z9>w+@`$=H+@YjJo@8!PY^>BUEdGUkO~b?3%Dp9)sX{0@NC!Ur3koQvT4sK@7x zVCOFTZwRcWZ{}4k{=;DXJ?Dzc>*WFT%im0j*UK5y`!;wv>irt*HD-T`b2^JU*TFN$ zr!9O*!MzTi-^SlwaO19R)>xR+_8ABI1_H4^7o&!;QUkfUz_8%PwE-}*qw5}@H-Xi&&)*C-PTlL-xzzqVw`qF|Ma}sTm)A3O`{7)8Jcyl#%_;7!w^AEt z?fLgY>We7;v|rrVt?i$C{}Qk{?&MflpZaYSwM!}XNqzyvT;>+ruJi3U&3jp6v$oxL zP+vjur~RFc-8$`F2{wmw=XzcZcJ8h!eBK3C&o%2BuyN}4O@2AW_UxND`QHsTf6l${ z0js6G>%i^p{U4foV%`f@%kl6&uzA#-1NWF(a^4I!=ZC1%k6Xa%`rJx=8|5a7x;{5h zf0&{!cQ1Yf>|RWcd*H5-ZMtvorkKy;=$l|QeG>mIu)01T3*Q0TuWvU#o-^+StLOgr zKCp4>=F#W7V8qlT?)twWMZMFD63@(q$N8slCapCh5uzIdhkAjU;PoAHG&7EWSF|dB> zxkfz>R`)zyUZb8s^Eh=*oXekq)%3T|+SEKgJXYMl*0$|&`6Sr=n`_F?!D`Nn@!HhN zR2UC|+X@q&~R8?)yV1j^i-u zJa0Wij$BtfemZ#ADfw~*pNj8FZG6>&8^1;yU$>10i zUjnP0PRTRhzrgPKl)Q8LH@g1X^Dgv1VE(DE^V(f!^Q-5$e+8`eGR1y)%-BJ7zdevI zV}~h;Sr(iabISEG-e=tjVE;R2nS+U7HH$w|*K_bdGww;~`k2doAy2O5!DX%$;A$3s z$@O59YejT@%;lbvo6GfD8SHx5u6z}^n!fHgd3;v`>uX+pSBIt2-W>$ zUk6M`J@49#alfi3W<9WD&78gpY~FGmUJX~zepnxDoO<$X0Ct?ovmw~AlzBFSt0&LK zVB^%YeSI*65U~_m~)mMA+Z{FCv57jT< zx=aIWvptVvdGc=!Hh=gwV12^31>2uo>$U?M<1gCwjb^{1?a*ktM%xi$et*$+YP2$T z=SI^te!D>Ii@#{QHd-0`nnu$#$M|j#`)xn%SM=S%_Q^ijX7tyBZO{DXQ{RGm4~n^r z)5rPrJgDw`>9ZF_E%UcG*kk*u=AL4o#z#HtHXZC3TsQ6J^?a(HJTt%^6X7$#`p7+} z>!|If!kG7mQhQR9n%576% zM^Tg_3i41v5mYQl5es%jMU?k_=bkmQbB*_9yVqKO+4a<%@H&>fcJ$k|aeP9E8x`$w#@JD?Nk2?NjjFS zs#Pd2Q4S~nG|Gy^=|{t$4qBMf3Q6F66u4-lKf&PVa=Jxdr4%XUI)tc0kdiv-03=N#objXyXGMjC7 zR;yDVF{^**n3{K0YfvwmXX$DKwBbDSdxz!@_bNSWjb$Bk)p{B>5FqxJO<%)C&_CGg+5qd^TTq|c z+jE*-a6Pw#x7Kr+Y7g+l?s+p8&g&lPnKrk3R{v>?ZRb4)cidsm-g*(T4x_4F@SQ!i zf3Ti}X$!h%vIiaGuIPhx==|;lV71-Uy5}#L*E6wyVc!r+XSF-pjNWdSzV;cfPyg(p zW;nGr0c~o3Z{JYeNW~F1s#5b;j%yU&6VN<92fAnVE+lI-kE5BbsYM%I`+0;8_wkUv zSv{xg-?Dj(v~1IcdlMUnX|-VH>}mB-j$)4BX=fBT<4Hd~mQPyHJ!=-5#FInxF<{qV z>OlX=KzhGMbrALN+)tU`*V~%^!Eqd33ug{(Gcpg`u%Wh@y+d8~?jUOYFJS)cop(NN z*ArUDaMr4`nn>o6st%x@J;82cp}r>HP;`^~knwW9nn! zh%&Kv4oFt4|#-q)k6BhiNE zZ_4c1gFQpd`gc^PVe=&8o;*vuqqws=2ki0QI&XAV7lhaLj_M+K-@^H|@B6{Cdiwh3 zQ=8{1YMQR`C#g?q@Qu`o`4o60rn9;OK5~BPtnO`i>wM5zeFZ*e&grz?>fim;3%Uoo zTWxk$Uu)wJ!SUaLs_x^%ZT3eN;T_cz@YYzns;9txVs^5xeg9~~*Kk0_jS*?cVdbQT4tJ)mC zfa7XnfB(QNc9%Z88CRc^TDkXtvtPOD^`7Y@x>pWF<1|p?so*ne+*KV1X3rkbvtVd0 zQl0N?v{}8U_ReBsd)!zW5x*U~2!0}J{& z^VU;P>vy8hoYz0tGg#&wRecG4f#=26If8?75dW^~H|VYNKT@5$tNJ}!b8HTuYrO}V zJ-z4jna)wupCvd^&YF5`oyYqV&+WNzI6tFn`&+ShR?CQwKIT|LYrcGozcqX%_{epr zv%c4v-8zBP{;Ojj>h7DveAVrZh0o}X(prNp(5CNyMD4!^nA;T2Cfd5HN$|rt{SNec zrE8so)%=Z|>qpgo47KeXJi69(AHIGblRPb(*N5TjZJVtVTWf67hFI`9?h4QAw%=8> zt+rcvoQp$fcYZM!YKNk=&ySkFa(>hn zH$UpS5BH&%18x6nezg6s`O&tx`B7io{HQN(e$*E?KkAE{pULQ%3l9M2Sj}JNP2D|y zM5B95P61C{I5>A=f8QWGlbzR!Rri-zSHhg?&SlT(+?jA8)W@7h4(IHw7Jx_Y>pH8m z;N@}hPI!6lK4%f$Rh=&%IV{QFRlOH&cn{TWc2$?54X<6TbyioSb^ z>Kp3lj=6s!=c8f&R-2A@WPVzj;~enZN1b@bSN7TaP5b8<7dMyM9OvTZQk%!Y;^tD@ z(%3SW?w^~`coxXi)O}L(x9C_$*S^lpfA^`C$FPy{w){u-!=h_$8_v(-`l-!%N#I7axY|FTGySW>UP`*GrI?8 zcF*dW#%(x{ou0~O_O$xCbnWAQXnYPDSU5PeaK1N19Du$3b)P+l-O|jR`{g$9yyn4| zP?WCfj#}&M=^kj^{y6{lpf!2%=&0_6_tyTSt4F{SyS?Arf8p%eJp)r_oXl$s=clW- znb+gNI$G-vmchK^j+Y{Vt}p zmWy2{_m+Ok+Lx9em%V{rS35S}_cZ;SHnV%4Ll_?O@*}qWeJsx$uYi8$5iD)(+gcyr z>702`?!iY@-=N)cJu%nu>i%ZdYwcd?s2*)J_sG-WGo3Mf{LfEr5OPsCGna z9mAc~p5XdCzzvXdwKsfZF6zeW$r{BKN1MMn^ZI9W&l@>s)?SnA*9>+3eH%ab!hy+8 zocv_?EMHXAysPSuz4e*`pRQ_PL~EW8yQ+m~-9tk?eG5IM_0_p9KxW>c%O_c3;Ss4 zN}Ipdx(r{d%4K1`I;yWWe$MgN!NZ=!dd^hy*M81aZ=JUny_Ztw5N^A_Xyq|g`@NL@ zp2J7(En5BX{9WEl>F2rIeVlh|9;@pA>t0IR|GJmb=DB-u_bA#Hy_f2&zJXTHJu&Xt z=iwuJc69Y}?9=-yCaQ)o!c|F7Js&`eZM6cffwzM_Sd>7Hj(|_w7$f#;lwEuqB+ga^`K5`t5 zs`i$1E}K@rYpGvL&`M`D0lhrFCbsc|+V~-D{O~qDrHvoe#;3ROW7_z!ZTxL*{P;G0 z;v&4GIvL(N-nyzGaPHz*%KCUv^Ea#Y*E7wt!syz!zSqqA@%l(Jrj^HIVq}lE{8`?` z-sjtf%`9$jYV+7A@1NEF``&U?-KWLfKc9;K$X@QMJ`mp8vg99CT@tO>&FWX6|M!0B zs%}JI^c{a^b*tL&ySdKlbMW-rT=&A6$8t;6d+L7la!)6WueK`=hRE@5N~T{bB0{ss8ID=BA_m z`^4EtAH#dy&iY;9M(hRmR`^)3Ke_J*wYx9FwTJtz(7yvqyzd6({`B|nG3ELnOzXaf z@waiS8rHO{d4C!|UDdpwjOKnUYGiUl`ZZl_U znOo=VU^1yW4&#$cZa*@Q@iErEInL{w{I@d@<9tuyuNA#xeO#j2n{zpsy)8EX+0^*}(?A|~}%Rck(8lrh0VH@wHI3G7Q{Wuq{&rNWD`uL01 zJ|Ft&s$bS!t)tPh?rQDxp_ci`x~sLXyIRJYW93tgpKEOYKi%-G@683b?|Bzfjz=xy zsU93vk5P?3t#Lm%W?0L39xSws=aE9ocpfXXjOU3$%XpqFw5;7v3N35*e4%CSR(y!> zFPLfjZ2z1G-B_@_;^_Wt@#M?arYF`Ex(3xt{J{-`#9f zTSoZUg1>~_vDudS@}4F6qA~2cUDwULV`$%bcTV;3y^FuZ*$*{k{e8b;KT2*t%&UD> z3}wE&FWDHoKl7<2-ghQtKDqf~-wR+q`)mL5K4W9JKl7=@-}e}0KDqh)*}pHt%Y1nb z-?-&c=TqZP?eFtX~Ex4ufC*|hM9jFMJ*+M)6wvIAbv+TKI-~L_gg=; zcKt1dR{Hy~L)tPXdHm=>J-MR4tG$#I{t|xzIPGbReqy7Wv(!(5rw^t6 z_C_~XsdvMpSttLD#$Vr3pIP{q`AdK9gWI0|_cu0k$RDD% zKb{Zb{|K7x$+bNWU;5dN_lo`dO-$_1!&&QkEnTbM%9%j>`zqLTx0>z!P8&aU`z!bT zi9hXs221O;YPj!D)NIfEZ!}#0C22>S=XCSQeZQinedPu_KJ9Yfw`lizusK*;=4Tw- zJ)^!pZ(1fbcITnJ*q5@{-oZ`0amM>D$NcTF@-kMu9=-6Li81SgeK#UE@8Q(^sWrzT z_x*_4`f%TQn8UpKPlbDpQP*GYcQk6+k7}?n@}uGWsoTyvpU`OGbK%ZS_$lym-}J%F z@4EZmWiN29n|Wtp|BUgt5T_O!lQq5$Oqx2^^lm-skH7cw;kP z?9ZYtSMcZH%fp?AUx1y59OEy*-N)*-wFTP_E}8Lar(?9H*RTa^QhUb+~+Vg<9y~yoX=Kr<5q8c)Qpq+d}W;DcOEu`bB^9S z`(rFzEyeskgSGnHde)Mge;;ad`TS*mxzAf_+9x&mS_JuVaOW@gxhFQ1mjb7Sg+oFg$s)>zl%O+>yv}>#92%Bx1+Z_6OF4>dXYh|wO zd%^vwJ3n%tQ~j;a(cs@l<+#hdWG-^7IR^{zb58V^pHA%@dtS?WU5Vzq5$zvu>~&w^ z=KM68? zA9n!TU)RQUldY?RPif+WqDx+N6G!hqvU?y5$<~?zwHRO z-lgE$CluWM@3$MJ|3PisZ#QCh{C>Mpa=+aux!-PtTTgG}#}wRre!CHWx!-P-+;2BZ z?zbD^uAkp-gnMl{wczISJBQfie&Z1Cd|Xj*b-!td-P-RNO78az;l}&jLb&tkcMIXx zez#C^zgq~mKYq7Ra=%+Bx!)~>Tl?KY$^C92+Xr4zQfp?^S{^D>5aZ0R~(<~j-mLhsE;=Ne8zkW zMca|&8bjQ%jRvgqJGQr?d3`a~_T~B+{9a^bW_&fE(jG4rAo{C1xhr7^ZwU zW)|ES-{Tk~*UuQ|y9aEH^WnO9jLe1`1!rW9Cwfk?WV3lflNg zR*9JhH^ygiW90fJWjzc|hhg4OL$=Ho+f+xIv#mwuPP)m;PmrC?*#9fS9bY8k_a!RBzEW(*$z zt7ihL3^O`7=C*k3($V^Gn8X6<9rE_ypKkb;ocS zwVM8}^C!XXE&F>b_2($JP_i!ie7@1MH@^TkPTibWQ>%$@qrRPDzB{S!qPRzV|B^o5 z-RSz?0hWvJq0Tw^-Uk1Z5q}Zv^MUni^!q;QFH`(!|4L)Gwhf=j?gyLW4*KD{^sj=| zoU7OIk$;I|E^~|1zpsJqS8{y|Y_9#7vB#C-~Y`prioZ-}d4z@qz8oSTT^O<9>XU}8P?ml~(T5fL7V=seWqD&!= z=agT9)hzzZY5#r&|1~B3>jRtP0qWm0HgmiJ{%xbF`!mM^e9ixAW50;8$bSd7O?CTg z{O>91=C}WU0Nd|e1OEut$6vHRHJWWrrN6&`|Cyq%Hgh=s7pc`fMi0jKuVA(8VcYu~ zTzx;r^BVQvDH-=E;L`UWjXnAP2{)gAr;+}@4p+Z`HqHMpuyN|{Z?!kTwyQ1e{u}(i z+WikU?eTrH&9`iyVOQGI{t|HG)zj}K!S>sC!|8VintI|o!N#hm-(6t)uFd)V3s@~V zmIbS&pUc60N9y@Qo5x>#mxpUNkM98Fv8@O$^Q;6<9&Ndwa4uJdYd4SY4&=$RCb-PA z7TkBQ#%nA4vo>72d3@&}Po8zbWuEom$)m08&-!rf=J8#H+&o@iHUckTe&im<8^bSy zyN9)J0#{2}7FB&yG`d`W7vZ~M^XO|EzW>nfaeFJdHV0=P#Z4cK+J?GgSz_zzPMZ4|! z{zhHwyBm*3&)YkJJ?~q4t=xrrSBgLFyES%e``}*L9c+&9@nHAf?aYDK&ON~TsC&(l z?@TebZHW^*0qlMY-w$l=94q^S^-*`OJhw~)`)=O-qCK$(fVDYB-$%(~I|!_1j!D!9 zQ*I%bIkf3_E&Pzi<~h=}I~1l5@JlA2j%*uZ%2WR>1<-`+cdZ_ zc^;JOmv*Lujmh(%?Hmm^CeMR%{nE}cU}KyQ=goHB0yoCv$??ebOU$ugWAshTTj9od ze`$+4TH|GwrVioRJ#xqgXxJJ^`4qcPnS zW3rBN{Sq?+Yz$2gw=)xNOx97ZUt(r~jp5I5Ob^7EtfO4N#LNa8lXbLjb124S9p(BZ zW-i#6tfMi#6l1cEa{Zhm_xdSd=h8Xy7;&EG!PUd(gUfSLA6y@G*UWdnYVltHR?GS7 z9bo5pe~LC^`l;0uGYC$MeHelp<2hBEF$2`<#<=fJ1)C@Qw1($?@^rX9>e+8+fE{nH zY34Z-t{$JW7UT0yH1*_u7uYy;@7v6CHrO_`dA(3O2khV3Xv=-+xnRdOuCaSx>i8C- zYs+}f1FMC`EWJ;60at{fZDu{$>aGwaL1kJThA}=g{$9+kLUG^z-kv#d@gxE zwLj1C+TKS|^Bga>U#`^$z^>gn4Zj$yrhNkT4}opletnQyF200X-;D1v@Yap(Qfm2l z&KAz&hrtI?8*iNUj8mJ}vh>Y5eY+g&zR~w1)bi~8E5Y{5<2L>ug{$j-1+_f3kAuB0 z(e^R0JbnKJ*!Kb2t^&(r`y|-=7j0LA<;i(1_^ifu4OkxA^T^s-9#-P&)+q>R-XoMMeSTTKF48y?U(J_j`@r;pL2gR*nRjR_K7(@12&I( zd_D_y&t>1=0(Rf4C+=3TvD(tt&w+1ajOR7EKMz;a*Rg3+bA5NEcJEmm@4dboiajJ>C^Tc_=N!R846B6t5ryeG{x^ zjz_5d}-6~2Kcuco7WcC{BgLNzZ^gR2mb~o*H^iI#<*{u02|}E&KR%z--a8L z^^xnBcD@5PhAs@Z^If0yf1kMY>e~a9GnP#mSRlqljQmt<9YdqU}JKh7#wV72hyH9Xhg-^2A$&wTs=?0mQ<68A^A zdVK!07@t3*sVDbez{aWP9^J7Mh`uuOOn%@v7?|@Z2kynu;Fs~aoEY031J_4Ad6ojV z=UEy}J@>uKfV01}rQgef&8ap&!+DTyjy^dx54@Ohh-)~st@jn!-zt`9J zABLu`zu&mX$8+ZKJTe))2eo~-zqW5q{q%Rf_3?f)b7XC9_rww4muXMm!@+X<;(2W< z*uHr068|I7)b*c2Est#)IL|Uif#n&)(cnDGOb5$jdkZ+vGRJ`B$@x}ro@I^&%VRqZ zoM)N0f#tEC0N$(_m*3^cV>=1FL1Q}+EYGvd+rd7|I2Vr3aoAt`W&5^cKI6>i8gzr# zpyZik2G~66@tFyBEpl#|1@@RxPh1b!SZ(R+Y;gH3GY77wuWO)9&Go&TF?kGF8}GBs zTx_?)bI;!kSIfP!HZ|k#XyWgpHuuTsd6qc^Y;J3xW#((6_%pV@v0JC@1z>ZypL_@V z4zSM!xrecT18C}bz8M4?r|$7BpGUDh+ZQMQY2a4=QT4aEXTZ(xby%BzH-XOtYx8{O zo<9q$<}Z8wo$ymBdG3(wXN<@AyTHbHUNpvY*V%AmGM94w(#|>BD0E@Copa&Fz9}d!N%B6=fJ*Q1UDwn9di8=^FFXKdG4@&Kirr+cgXe2T3p=N%X|9|qN(TJ{6k>Z z!h3UV?$-}ct0&K;VB0D4Tn1NnZ@3pe3@)D$J_6TAJ#AeMPFvb->k?}9?8T3Q-HUSf z%g4a?u<-tU{>Q;rQSx^+SAzA|M}M!^p8)HV>-W{*ttjg0>nFkG_4^vQn!n8VwT)(f zqg~f%?!#QauSYXpf9;-&E@Mr-Cf)$IeSIA371U~3^P9lVYk432DY$yZe=|7af1HLt z1JC$B4c4B&-*rEI7Oqe3$8G_eQ{6t?NUbKml{)tj?x)Yg?N81t=KBI%UH{Kf%k96% z?QLNF^L*iY-40jJJ;xnjwcK;u1@`ATN!y(iHP1=nbiUt>{>Xc`>Ex&>%Gd?!1q#&OJ5!Y+m~Fw&HE5s-5d{4%gyhc zeI4vvX?vJj9@{s-u7S2ksO7l_`6k$V5c_05%xk=U`nz8Gc%8_cS)1Eq@G-E*uD*{_ z%N>LFAdiC`L-}m=e{gmEzeO!iu5W{L5Ap<9o-uqEoO_V(faS401`&tV3eMhi?DEprKF9YpaQ2+O^3vCF#rGfJ>_L6yrLSYx*KxlN z_87=_5C4LzrDPoHWe(>pIsOeUzn%CGTrDLz)XN;sdHU>PlykcTyqw!5T`Y>aG48bv z@F*75Z=4)UCtS^Qh4+_TVDJBOUNvSEn)=c{5X=X^DIIp?dx^-<5b*8}@aSmtVdxLWKRwAnX=t7SZ6!D`pj@4P?S2=1}! zF{C~3JvMIqwC6mw3D`EZGFT zw+1_YZJA58axOhbBf@XV$5%;osTPkZKa53p@&%UtdWR(H()dkOz-UgmNiu$n#@_r75D%;f~I zS~-{d!5zQ0%%xg6m!2P!^8jp~xAJ$r2g23thyMm}66HwdE%$34`v*6A`f&){IQ87O z9}0GE)}d&39^7+>fu~VxPyf`?pCiEbC-=`&;A;M&O>H#uM?137=<4u&_EBiY>#yBw zvTgeB7PbAG9_~)@-z(a;Y!Ne;AuQb7Fg|Q2ebx$vmuBVBbS$u2u#+KmK<_{CAzspZ_s`>+rMM_&II- zf;N6p8-IVpGiUzy6m2)$|8}uF$Ksh_ebilh`=b{BcY@Wz-_`KMpAFYX-FW-2roVgU zT#EA%ejeC4$=@}+8+iXG^>!X&umx0xi!a?TmX~{aO}M|+DA~KK7kCY@`*<0s(e&AeR=R?EDZUoHOEfYrjUZFuJKI=DXS8TSp~>nPV# zoHzTZmK-;M9j7+?E068djV=6UxbfK!p8?xf_4MJhVB@qU?iR3d8RxBF{nTyOc~Oi1 z=fL(Q{PSRaGNvzp^-*^%<>gv#0Qa{xCFiGg3cPNC*K4rrxIV=--jF(LdUUi*}TBx3cN*uw*Z0#|78^TpRys!S(-n8-JnT#=qFcUvA^C6x@8jF1Ycp7F_$E3a88<&0aFR+@v$*Go{{|39B;r{_UhdF1y z3D!qFZ8$mhJ8dlCWKs0BjW?w5_;-TcAK_i_at%ho^-<3nj0W?kK5n$T2IfB2fh?rO2)B1y8haeZv(L7$+>q!uzu?C84LD&mNqwn z>!+@dbGb3tzGu&E0@hF6Yq>nva{c$9_}iJ{wR~IZT^hUt^{x%xsjf$!8+LE>(bVH9 zUYqx%&b4QA+R>J4^KJzn58k8T=032EPc684{QpZ_=JWq2vD|TQLp=^|zw>YQw}7iz z{8=qY*^l)yZYu=+%YVPUHJWF?y&1?(jO~Ma_8?yLRqPxw`E-r)ti@_SAd9U9Va2?Wp&GtIc7|{Z*=#gs5>_K{uKYatLoWP6Tz;rdrF@JDEg_}57$yn{~hr=2)rBh*$tlr zSJT&Zmiu1rU~0!>?e`srQXfX~r+so`w@&WE!R81*0&H7%(Kr9?#}tY_>dvM75Q@2N zOPtsv!DT;>f~(n2_n+K;Orti|+I}8QeGJ8)_O~>4>*PKbY>x1^g3Ers4X%&6{gh9q znA^6*#=5S@gI!m-=b97X?yvm2?i0ak7JpW0_aun(mG{4I2dl;24Ypsg&w#7BAKbSy z!N#a3W)|47C8h_g7W-^)nR5YgXn z=20BKw*0qC=7XIxZFx`H4_5c*`g%T5%Q3qE?3`{#v34zPOo0NC?h?kfi2`l!ce z2yE^g=L^C5sehKN<~syKOL;5zkSxG=ALu^xlgQZ+vDyGunXOwEIo&ac4g3!Ti@3&GA^_TNQdHGMO$YVm&`SbxvC z;_`ZV5dHGEKgH|ie$*2iycG2T4fYyyAjLVIM4jv4#pKf#er3VE4t}(aUsrJBZf)at zHr&2@ZulVFKIHFeJ_P4a-Cu2v*FLCcyqAF0ax7d5c1*5wd@cj4XJ3C9Y@E8+ugj_Z zc`noT5sI4Q7nj#Bb^GDibHC-d_old)K1OYvwddQbs6RpRr~T^2Zf*bE>z@Rh;|}*e z^|chWYbf?f{&9-A%q_ML&adM%@AZw%+IDZGzKP;b`==Vab=v(j*c{HC>-QP3b9Zy$ z^I5QZu2HvujZ?R8@*60&XWzuh|2eSvbFTe7SS{^+0o>l+ZD{I=xgD&Q{eK78JnGJY zdrB=izYI3#ebnj4SHSA}+)w>g%9kkW`rJ+ZHHy02z4!pwy_g&i!d)YCJVgC4*Id^2Hd_p()f5@{3cjE_qdOOjZ-&|K97MNSA4z&)=xd!|AEyXr<_N7_W230 z{nnQ2$+y9dO(GzD>Yi)M>(GzUJT{#Z=khtQn*KR%)jTFV zKHRg`w(YU_6R>+W*NvZo)tr|cb86)==iZlpg4%r6$@ep``zHB*4pvJ(ZED7Q&M^Ke zYUj{n@)uy|uskMTKvT~#@*-F*>+}-%d5U`G^kuN~lH=%?U^RU+FKY3B1+2g4K5?!i z?z71he}_=KjvP#VXoKDNhfy5I;naDi`VBd9?eJQ&1P?SNA6@YA_%74NmoK>SE4A^} z+xXamj{|Q}aO3xAxZ~X${s-E049>gHj(>#zo}#Xg_p*NitIKz%{ww7#lpJe+1M8!n zXSvtF>K1=iG+ocRbE4gR&gy$<5_@Eem$NY*)S@ZN zfb})6zAM7j^mQ-F^>y7=1+PNMysri(q@H(e#<*YA6SIasl+5XxVDpyiuohfB`(bUc zaq7vl4%l%f&$?j8Qs!9?uAV&WgN;+q{@DO*er*}YhTx1tyW{XUQ&0OFfgM};#$bJX zN9EjX0yc-&Qhl{2|E7)2drkfFJdCV&*kdAm0$3lp=al`xUMDh!{o!gU`R_?hM6)g5?U~a&`le0oY2yH} LZFtQRd(HZP;(f*j diff --git a/Shaders/SSAO.frag b/Shaders/SSAO.frag new file mode 100644 index 0000000..9b8e78d --- /dev/null +++ b/Shaders/SSAO.frag @@ -0,0 +1,93 @@ +#version 450 + +#include "camera.glsl" + +layout(location = 0) out float Occlusion; +layout(location = 0) in vec2 fTexcoord; + +layout(set = 1, binding = 0) uniform sampler2D fDepth; + +const vec3 kernels[64] = vec3[64]( + vec3(-0.068561 , 0.0591945, 0.0), vec3(-0.0722413, 0.064881 , 0.0), vec3(-0.0356842, 0.0528774, 0.0), vec3(-0.0503744,-0.024011 , 0.0), + vec3(-0.0461418,-0.0327981, 0.0), vec3( 0.0715148, 0.0770443, 0.0), vec3( 0.0120626, 0.0119903, 0.0), vec3( 0.097976 , 0.047019 , 0.0), + vec3(-0.0359385, 0.042113 , 0.0), vec3( 0.024831 , 0.0246512, 0.0), vec3(-0.0416858,-0.0301449, 0.0), vec3( 0.0592394,-0.0552211, 0.0), + vec3( 0.0999734, 0.077195 , 0.0), vec3( 0.109393 , 0.0025727, 0.0), vec3(-0.034774 ,-0.116399 , 0.0), vec3( 0.0848333,-0.0563106, 0.0), + vec3(-0.0583617, 0.103013 , 0.0), vec3( 0.0551693, 0.0546054, 0.0), vec3(-0.0657415,-0.0909375, 0.0), vec3(-0.0384386,-0.0381255, 0.0), + vec3( 0.0049157, 0.0034071, 0.0), vec3(-0.132679 ,-0.10911 , 0.0), vec3(-0.0122596,-0.0158563, 0.0), vec3( 0.0973467, 0.148693 , 0.0), + vec3(-0.0637455, 0.0331347, 0.0), vec3( 0.100462 , 0.0588527, 0.0), vec3( 0.0361528,-0.102876 , 0.0), vec3(-0.0165081,-0.0521415, 0.0), + vec3(-0.186336 , 0.110243 , 0.0), vec3(-0.0651563,-0.0964584, 0.0), vec3(-0.128904 ,-0.0314065, 0.0), vec3( 0.246164 ,-0.0209147, 0.0), + vec3( 0.219602 , 0.109242 , 0.0), vec3(-0.140972 , 0.193563 , 0.0), vec3(-0.218098 , 0.07883 , 0.0), vec3(-0.201637 , 0.125925 , 0.0), + vec3(-0.0219822,-0.190489 , 0.0), vec3( 0.197557 , 0.117705 , 0.0), vec3( 0.224875 ,-0.095269 , 0.0), vec3(-0.0051506, 0.0057472, 0.0), + vec3( 0.0907334, 0.0709684, 0.0), vec3( 0.0081909, 0.441157 , 0.0), vec3(-0.174515 , 0.397806 , 0.0), vec3( 0.290092 ,-0.0485296, 0.0), + vec3(-0.0721474, 0.0099621, 0.0), vec3(-0.100516 , 0.0750153, 0.0), vec3( 0.373977 ,-0.292787 , 0.0), vec3(-0.0874456, 0.173854 , 0.0), + vec3(-0.0793103, 0.124525 , 0.0), vec3( 0.0902032,-0.102222 , 0.0), vec3( 0.119788 ,-0.0441132, 0.0), vec3(-0.37276 , 0.521842 , 0.0), + vec3( 0.30332 , 0.126207 , 0.0), vec3(-0.0141141, 0.0463802, 0.0), vec3( 0.287755 , 0.324245 , 0.0), vec3( 0.26092 , 0.495898 , 0.0), + vec3( 0.0955977, 0.204283 , 0.0), vec3( 0.261691 , 0.18296 , 0.0), vec3( 0.0752712, 0.310255 , 0.0), vec3( 0.103214 ,-0.444275 , 0.0), + vec3(-0.0105898,-0.0468894, 0.0), vec3( 0.111373 ,-0.532453 , 0.0), vec3(-0.4037 , 0.784518 , 0.0), vec3(-0.0934949, 0.117462 , 0.0) +); + +const vec3 rotations[16] = vec3[16]( + vec3(-0.210184, 0.137647, 0.0), vec3(-0.225408,-0.061218, 0.0), vec3( 0.453909, -0.976196, 0.0), vec3(-0.22286 ,-0.325755, 0.0), + vec3( 0.854986,-0.675635, 0.0), vec3(-0.127765, 0.588569, 0.0), vec3( 0.725356, -0.37757 , 0.0), vec3( 0.24072 , 0.057066, 0.0), + vec3(-0.760906,-0.668703, 0.0), vec3(-0.056086, 0.203964, 0.0), vec3(-0.319561, -0.474057, 0.0), vec3( 0.059689, 0.308158, 0.0), + vec3( 0.432201, 0.378429, 0.0), vec3( 0.976759, 0.496303, 0.0), vec3( 0.440987, -0.098916, 0.0), vec3( 0.825155,-0.832357, 0.0) +); + +layout(push_constant) uniform SSAOParameters +{ + float screenWidth; + float screenHeight; + + float radius; + float bias; + float multiplier; +} params; + +layout(set = 0, binding = 0) uniform CameraBuffer { + CameraBufferObject camera; +}; + +vec3 GetViewPosition(vec2 ndcUV) +{ + float depth = texture(fDepth, ndcUV * 0.5 + 0.5).r; + + vec4 ndc = vec4(ndcUV, depth, 1.0); + vec4 view = camera.invProj * ndc; + + return view.xyz / view.w; +} + +void main() +{ + vec2 noiseScale = vec2(params.screenWidth/4.0, params.screenHeight/4.0); + + vec3 viewPos = GetViewPosition(fTexcoord * 2.0 - 1.0); + vec3 viewNormal = normalize(cross(dFdx(viewPos.xyz), dFdy(viewPos.xyz))); + + int x = int(floor(fTexcoord.x * noiseScale.x)) % 4; + int y = int(floor(fTexcoord.y * noiseScale.y)) % 4; + + vec3 randomVec = normalize(rotations[y * 4 + x]); + + vec3 tangent = normalize(randomVec - viewNormal * dot(randomVec, viewNormal)); + vec3 bitangent = cross(viewNormal, tangent); + mat3 TBN = mat3(tangent, bitangent, viewNormal); + + float occlusion = 0.0; + for(uint i = 0; i < SSAO_SAMPLES; ++i) + { + vec3 samplePos = TBN * kernels[i]; + samplePos = viewPos + samplePos * params.radius; + + vec4 offset = vec4(samplePos, 1.0); + offset = camera.proj * offset; + offset.xy /= offset.w; + + float sampleDepth = GetViewPosition(offset.xy).z; + + float rangeCheck = smoothstep(0.0, 1.0, params.radius / abs(viewPos.z - sampleDepth)); + occlusion += (sampleDepth >= samplePos.z + params.bias ? 1.0 : 0.0) * rangeCheck * params.multiplier; + } + + Occlusion = 1.0 - (occlusion / SSAO_SAMPLES); +} \ No newline at end of file diff --git a/Shaders/SSAO.spv b/Shaders/SSAO.spv new file mode 100644 index 0000000000000000000000000000000000000000..29e4ca80d8610b964f91ebfd44011ca204513582 GIT binary patch literal 12036 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 { EN_WARN("AssetManager::LoadMesh() - Failed to load a texture with name \"" + texture->GetName() + "\" from \"" + texture->GetFilePath() + "\" because a texture with that name already exists!"); - return false; + continue; } m_Textures[texture->GetName()] = texture; } for (const auto& material : data.materials) { + if (m_Materials.contains(material->GetName())) { EN_WARN("AssetManager::LoadMesh() - Failed to create a material with name \"" + material->GetName() + "\" because a material with that name already exists!"); - return false; + continue; } - + m_Materials[material->GetName()] = material; } diff --git a/Source/Assets/MeshImporter/GLTFImporter.cpp b/Source/Assets/MeshImporter/GLTFImporter.cpp index c2b9163..43c3ba6 100644 --- a/Source/Assets/MeshImporter/GLTFImporter.cpp +++ b/Source/Assets/MeshImporter/GLTFImporter.cpp @@ -167,16 +167,16 @@ namespace en finalOffset /= sizeof(unsigned short); const unsigned short* binaryDataShort = (const unsigned short*)m_BinaryData.data(); - for (size_t i = finalOffset; i < finalOffset + count; i++) - indices.push_back((uint32_t)binaryDataShort[i]); + for (size_t i = finalOffset, j = 0; i < finalOffset + count; ++i, ++j) + indices[j] = (uint32_t)binaryDataShort[i]; } else if (componentType == 5122) { finalOffset /= sizeof(short); const short* binaryDataShort = (const short*)m_BinaryData.data(); - for (size_t i = finalOffset; i < finalOffset + count; i++) - indices.push_back((uint32_t)binaryDataShort[i]); + for (size_t i = finalOffset, j = 0; i < finalOffset + count; i++, ++j) + indices[j] = (uint32_t)binaryDataShort[i]; } return indices; diff --git a/Source/Common/Helpers.cpp b/Source/Common/Helpers.cpp index bb7e579..8b005eb 100644 --- a/Source/Common/Helpers.cpp +++ b/Source/Common/Helpers.cpp @@ -206,10 +206,10 @@ namespace en switch (newLayout) { case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - sourceStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; break; @@ -280,7 +280,7 @@ namespace en } else commandBuffer = cmdBuffer; - + vkCmdPipelineBarrier( commandBuffer, srcStage, dstStage, diff --git a/Source/Editor/EditorLayer.cpp b/Source/Editor/EditorLayer.cpp index 056cc3f..154127c 100644 --- a/Source/Editor/EditorLayer.cpp +++ b/Source/Editor/EditorLayer.cpp @@ -278,7 +278,7 @@ namespace en { static int mode = 0; - if (ImGui::SliderInt("Debug View", &mode, 0, 8)) + if (ImGui::SliderInt("Debug View", &mode, 0, 9)) m_Renderer->m_DebugMode = mode; std::string modeName = ""; @@ -294,6 +294,7 @@ namespace en case 6: modeName = "Active Cluster Density";break; case 7: modeName = "Depth Split Distances"; break; case 8: modeName = "CSM Cascades"; break; + case 9: modeName = "SSAO"; break; default: modeName = "Unknown Mode"; break; } diff --git a/Source/Editor/UIPanels/SettingsPanel.cpp b/Source/Editor/UIPanels/SettingsPanel.cpp index 46cba5c..a809a73 100644 --- a/Source/Editor/UIPanels/SettingsPanel.cpp +++ b/Source/Editor/UIPanels/SettingsPanel.cpp @@ -5,7 +5,7 @@ namespace en { constexpr std::array g_AntialiasingModeNames = { "None", "FXAA"}; - constexpr std::array g_AmbientOcclusionModeNames = { "SSAO", "None" }; + constexpr std::array g_AmbientOcclusionModeNames = { "None", "SSAO" }; void SettingsPanel::Render() { @@ -16,6 +16,7 @@ namespace en static bool vSync = m_Renderer->GetVSyncEnabled(); static bool depthPrepass = m_Renderer->GetDepthPrepassEnabled(); static Renderer::AntialiasingMode antialiasingMode = m_Renderer->GetAntialiasingMode(); + static Renderer::SSAOMode ssaoMode = m_Renderer->GetSSAOMode(); if (ImGui::Checkbox("VSync", &vSync)) m_Renderer->SetVSyncEnabled(vSync); @@ -56,28 +57,30 @@ namespace en } } - /* + if (ImGui::CollapsingHeader("Ambient Occlusion")) { - if (ImGui::Combo("Ambient Occlusion Mode", (int*)&m_Renderer->m_PostProcessParams.ambientOcclusionMode, g_AmbientOcclusionModeNames.data(), g_AmbientOcclusionModeNames.size())) - m_Renderer->ReloadBackend(); + if (ImGui::Combo("Ambient Occlusion Mode", (int*)&ssaoMode, g_AmbientOcclusionModeNames.data(), g_AmbientOcclusionModeNames.size())) + m_Renderer->SetSSAOMode(ssaoMode); + + auto& ssao = m_Renderer->GetSSAOProperties(); - switch (m_Renderer->m_PostProcessParams.ambientOcclusionMode) + switch (ssaoMode) { - case Renderer::AmbientOcclusionMode::SSAO: + case Renderer::SSAOMode::SSAO: ImGui::Spacing(); if (ImGui::Button("Restore Defaults")) - m_Renderer->m_PostProcessParams.ambientOcclusion = Renderer::PostProcessingParams::AmbientOcclusion{}; + ssao = Renderer::SSAOProperties{}; ImGui::Spacing(); - ImGui::DragFloat("SSAO Bias", &m_Renderer->m_PostProcessParams.ambientOcclusion.bias, 0.05f, 0.0f, 1.0f), "%.2f", ImGuiSliderFlags_AlwaysClamp; - ImGui::DragFloat("SSAO Radius", &m_Renderer->m_PostProcessParams.ambientOcclusion.radius, 0.1f, 0.0f, 5.0f, "%.1f", ImGuiSliderFlags_AlwaysClamp); - ImGui::DragFloat("SSAO Multiplier", &m_Renderer->m_PostProcessParams.ambientOcclusion.multiplier, 0.1f, 0.0f, 5.0f, "%.1f", ImGuiSliderFlags_AlwaysClamp); + ImGui::DragFloat("SSAO Bias", &ssao.bias, 0.05f, 0.0f, 1.0f), "%.2f", ImGuiSliderFlags_AlwaysClamp; + ImGui::DragFloat("SSAO Radius", &ssao.radius, 0.1f, 0.0f, 5.0f, "%.1f", ImGuiSliderFlags_AlwaysClamp); + ImGui::DragFloat("SSAO Multiplier", &ssao.multiplier, 0.1f, 0.0f, 5.0f, "%.1f", ImGuiSliderFlags_AlwaysClamp); break; - case Renderer::AmbientOcclusionMode::None: + case Renderer::SSAOMode::None: ImGui::Text("Ambient occlusion is disabled."); break; @@ -87,7 +90,6 @@ namespace en } } - */ if (ImGui::CollapsingHeader("Lights and Shadows")) { diff --git a/Source/Input/InputManager.cpp b/Source/Input/InputManager.cpp index ac3aebc..47aea73 100644 --- a/Source/Input/InputManager.cpp +++ b/Source/Input/InputManager.cpp @@ -4,7 +4,7 @@ namespace en { InputManager::InputManager() { - m_Window = Window::Get().m_NativeHandle; + m_Window = Window::Get().GetNativeHandle(); m_LastMousePos = GetMousePosition(); diff --git a/Source/Renderer/Context.cpp b/Source/Renderer/Context.cpp index a36c728..347cacc 100644 --- a/Source/Renderer/Context.cpp +++ b/Source/Renderer/Context.cpp @@ -5,8 +5,23 @@ en::Context* g_CurrentContext = nullptr; constexpr std::array validationLayers { "VK_LAYER_KHRONOS_validation" }; -constexpr std::array deviceExtensions { - VK_KHR_SWAPCHAIN_EXTENSION_NAME +constexpr std::array deviceExtensions { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME +}; +constexpr VkPhysicalDeviceVulkan13Features deviceFeaturesVK1_3{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, + .pNext = (void*)nullptr, + .dynamicRendering = VK_TRUE, +}; +constexpr VkPhysicalDeviceVulkan12Features deviceFeaturesVK1_2{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, + .pNext = (void*)&deviceFeaturesVK1_3, + .descriptorBindingUpdateUnusedWhilePending = VK_TRUE, +}; +constexpr VkPhysicalDeviceVulkan11Features deviceFeaturesVK1_1{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES, + .pNext = (void*)&deviceFeaturesVK1_2, }; constexpr VkPhysicalDeviceFeatures deviceFeatures { .samplerAnisotropy = VK_TRUE, @@ -115,7 +130,7 @@ namespace en } void Context::CreateWindowSurface() { - if (glfwCreateWindowSurface(m_Instance, Window::Get().m_NativeHandle, nullptr, &m_WindowSurface) != VK_SUCCESS) + if (glfwCreateWindowSurface(m_Instance, Window::Get().GetNativeHandle(), nullptr, &m_WindowSurface) != VK_SUCCESS) EN_ERROR("Context::VKCreateWindowSurface() - Failed to create window surface!"); } void Context::PickPhysicalDevice() @@ -200,14 +215,9 @@ namespace en queueCreateInfos.emplace_back(queueCreateInfo); } - VkPhysicalDeviceDescriptorIndexingFeaturesEXT descriptorFeatures{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES, - .descriptorBindingUpdateUnusedWhilePending = VK_TRUE - }; - VkDeviceCreateInfo createInfo{ .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, - .pNext = &descriptorFeatures, + .pNext = &deviceFeaturesVK1_1, .queueCreateInfoCount = static_cast(queueCreateInfos.size()), .pQueueCreateInfos = queueCreateInfos.data(), .enabledLayerCount = 0U, @@ -261,7 +271,6 @@ namespace en if (vkCreateCommandPool(m_LogicalDevice, &transferCommandPoolCreateInfo, nullptr, &m_TransferCommandPool) != VK_SUCCESS) EN_ERROR("Context::VKCreateCommandPool() - Failed to create a transfer command pool!"); } - void Context::CreateDescriptorAllocator() { m_DescriptorAllocator = MakeScope(m_LogicalDevice); @@ -390,10 +399,9 @@ namespace en swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); } - VkPhysicalDeviceFeatures supportedFeatures; - vkGetPhysicalDeviceFeatures(device, &supportedFeatures); + bool featuresSupported = CheckDeviceFeaturesSupport(device); - return m_QueueFamilies.IsComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy; + return m_QueueFamilies.IsComplete() && extensionsSupported && swapChainAdequate && featuresSupported; } bool Context::CheckDeviceExtensionSupport(VkPhysicalDevice& device) { @@ -410,4 +418,28 @@ namespace en return requiredExtensions.empty(); } + bool Context::CheckDeviceFeaturesSupport(VkPhysicalDevice& device) + { + VkPhysicalDeviceVulkan13Features supportedFeaturesVK1_3{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, + }; + VkPhysicalDeviceVulkan12Features supportedFeaturesVK1_2{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, + }; + VkPhysicalDeviceVulkan11Features supportedFeaturesVK1_1{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES, + }; + VkPhysicalDeviceFeatures2 supportedFeatures{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, + }; + + supportedFeatures.pNext = (void*)&supportedFeaturesVK1_1; + vkGetPhysicalDeviceFeatures2(device, &supportedFeatures); + supportedFeatures.pNext = (void*)&supportedFeaturesVK1_2; + vkGetPhysicalDeviceFeatures2(device, &supportedFeatures); + supportedFeatures.pNext = (void*)&supportedFeaturesVK1_3; + vkGetPhysicalDeviceFeatures2(device, &supportedFeatures); + + return supportedFeatures.features.samplerAnisotropy && supportedFeaturesVK1_3.dynamicRendering && supportedFeaturesVK1_2.descriptorBindingUpdateUnusedWhilePending; + } } \ No newline at end of file diff --git a/Source/Renderer/Context.hpp b/Source/Renderer/Context.hpp index 73fea8d..7ddf6f2 100644 --- a/Source/Renderer/Context.hpp +++ b/Source/Renderer/Context.hpp @@ -87,6 +87,7 @@ namespace en bool IsDeviceSuitable(VkPhysicalDevice& device); bool CheckDeviceExtensionSupport(VkPhysicalDevice& device); + bool CheckDeviceFeaturesSupport(VkPhysicalDevice& device); }; } diff --git a/Source/Renderer/DescriptorSet.cpp b/Source/Renderer/DescriptorSet.cpp index bcc901d..889af06 100644 --- a/Source/Renderer/DescriptorSet.cpp +++ b/Source/Renderer/DescriptorSet.cpp @@ -52,7 +52,7 @@ namespace en } std::vector descriptorWrites(info.imageInfos.size() + info.bufferInfos.size()); - for (uint32_t i = 0; const auto & image : info.imageInfos) + for (uint32_t i = 0; const auto& image : info.imageInfos) { descriptorWrites[image.index] = VkWriteDescriptorSet { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, @@ -64,7 +64,7 @@ namespace en .pImageInfo = descriptorImageInfos[i++].data() }; } - for (uint32_t i = 0; const auto & buffer : info.bufferInfos) + for (uint32_t i = 0; const auto& buffer : info.bufferInfos) { descriptorWrites[buffer.index] = VkWriteDescriptorSet { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, diff --git a/Source/Renderer/Framebuffer.cpp b/Source/Renderer/Framebuffer.cpp deleted file mode 100644 index 76e2524..0000000 --- a/Source/Renderer/Framebuffer.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "Framebuffer.hpp" - -namespace en -{ - Framebuffer::Framebuffer(Handle renderPass, const VkExtent2D extent, const RenderPassAttachment& colorAttachment, const RenderPassAttachment& depthAttachment) - : m_Extent(extent), m_ColorAttachment(colorAttachment), m_DepthAttachment(depthAttachment) - { - std::vector imageViews{ }; - if (colorAttachment.imageView != VK_NULL_HANDLE) - imageViews.push_back(colorAttachment.imageView); - if (depthAttachment.imageView != VK_NULL_HANDLE) - imageViews.push_back(depthAttachment.imageView); - - - VkFramebufferCreateInfo framebufferInfo { - .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - .renderPass = renderPass->GetHandle(), - .attachmentCount = static_cast(imageViews.size()), - .pAttachments = imageViews.data(), - - .width = extent.width, - .height = extent.height, - .layers = 1U, - }; - - if (vkCreateFramebuffer(Context::Get().m_LogicalDevice, &framebufferInfo, nullptr, &m_Handle) != VK_SUCCESS) - EN_ERROR("Framebuffer::Framebuffer() - Failed to create a Framebuffer!"); - } - - Framebuffer::~Framebuffer() - { - vkDestroyFramebuffer(Context::Get().m_LogicalDevice, m_Handle, nullptr); - } -} \ No newline at end of file diff --git a/Source/Renderer/Framebuffer.hpp b/Source/Renderer/Framebuffer.hpp deleted file mode 100644 index 8e3c2c9..0000000 --- a/Source/Renderer/Framebuffer.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -#ifndef EN_FRAMEBUFFER_HPP -#define EN_FRAMEBUFFER_HPP - -#include -#include - -namespace en -{ - class Framebuffer - { - public: - Framebuffer( - Handle renderPass, - const VkExtent2D extent, - const RenderPassAttachment& colorAttachment = {}, - const RenderPassAttachment& depthAttachment = {} - ); - ~Framebuffer(); - - const VkFramebuffer GetHandle() const { return m_Handle; } - - const VkExtent2D m_Extent{}; - const RenderPassAttachment m_ColorAttachment; - const RenderPassAttachment m_DepthAttachment; - - private: - VkFramebuffer m_Handle{}; - }; -} - -#endif \ No newline at end of file diff --git a/Source/Renderer/ImGuiContext.cpp b/Source/Renderer/ImGuiContext.cpp new file mode 100644 index 0000000..b0eea6d --- /dev/null +++ b/Source/Renderer/ImGuiContext.cpp @@ -0,0 +1,147 @@ +#include "ImGuiContext.hpp" + +#include + +namespace en +{ + static void ImGuiCheckResult(VkResult err) + { + if (err == 0) return; + + EN_ERROR("[ImGui Error]:" + err); + } + + ImGuiContext::ImGuiContext(VkFormat imageFormat, VkExtent2D imageExtent, const std::vector& imageViews) + { + UseContext(); + + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGui::StyleColorsDark(); + + ImGui_ImplGlfw_InitForVulkan(Window::Get().GetNativeHandle(), true); + + ImGui_ImplVulkan_InitInfo initInfo{ + .Instance = ctx.m_Instance, + .PhysicalDevice = ctx.m_PhysicalDevice, + .Device = ctx.m_LogicalDevice, + .QueueFamily = ctx.m_QueueFamilies.graphics.value(), + .Queue = ctx.m_GraphicsQueue, + .DescriptorPool = ctx.m_DescriptorAllocator->GetPool(), + .MinImageCount = static_cast(imageViews.size()), + .ImageCount = static_cast(imageViews.size()), + .CheckVkResultFn = ImGuiCheckResult, + }; + + VkAttachmentDescription colorDescription{ + .format = imageFormat, + .samples = VK_SAMPLE_COUNT_1_BIT, + .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .initialLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + }; + + VkAttachmentReference colorReference{ + .attachment = 0U, + .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + }; + + VkSubpassDescription subpass{ + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .colorAttachmentCount = 1U, + .pColorAttachments = &colorReference + }; + + VkSubpassDependency dependency{ + .srcSubpass = VK_SUBPASS_EXTERNAL, + .dstSubpass = 0U, + .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT + }; + + VkRenderPassCreateInfo renderPassInfo{ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + .attachmentCount = 1U, + .pAttachments = &colorDescription, + .subpassCount = 1U, + .pSubpasses = &subpass, + .dependencyCount = 1U, + .pDependencies = &dependency + }; + + if (vkCreateRenderPass(Context::Get().m_LogicalDevice, &renderPassInfo, nullptr, &m_RenderPass) != VK_SUCCESS) + EN_ERROR("ImGuiContext::ImGuiContext() - Failed to create a RenderPass!"); + + ImGui_ImplVulkan_Init(&initInfo, m_RenderPass); + + VkCommandBuffer cmd = Helpers::BeginSingleTimeGraphicsCommands(); + ImGui_ImplVulkan_CreateFontsTexture(cmd); + Helpers::EndSingleTimeGraphicsCommands(cmd); + + ImGui_ImplVulkanH_SelectSurfaceFormat(ctx.m_PhysicalDevice, ctx.m_WindowSurface, &imageFormat, 1U, VK_COLORSPACE_SRGB_NONLINEAR_KHR); + + UpdateFramebuffers(imageExtent, imageViews); + } + ImGuiContext::~ImGuiContext() + { + ImGui_ImplVulkan_DestroyFontUploadObjects(); + ImGui_ImplVulkan_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + + vkDestroyRenderPass(Context::Get().m_LogicalDevice, m_RenderPass, nullptr); + + for (const auto& framebuffer : m_Framebuffers) + vkDestroyFramebuffer(Context::Get().m_LogicalDevice, framebuffer, nullptr); + } + void ImGuiContext::UpdateFramebuffers(VkExtent2D imageExtent, const std::vector& imageViews) + { + m_Extent = imageExtent; + + for (const auto& framebuffer : m_Framebuffers) + vkDestroyFramebuffer(Context::Get().m_LogicalDevice, framebuffer, nullptr); + + m_Framebuffers.resize(imageViews.size()); + + for (int i{}; i < imageViews.size(); ++i) + { + VkFramebufferCreateInfo framebufferInfo{ + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .renderPass = m_RenderPass, + .attachmentCount = 1U, + .pAttachments = &imageViews[i], + + .width = imageExtent.width, + .height = imageExtent.height, + .layers = 1U, + }; + + if (vkCreateFramebuffer(Context::Get().m_LogicalDevice, &framebufferInfo, nullptr, &m_Framebuffers[i]) != VK_SUCCESS) + EN_ERROR("ImGuiContext::UpdateFramebuffers() - Failed to create a Framebuffer!"); + } + + ImGui_ImplVulkan_SetMinImageCount(imageViews.size()); + } + void ImGuiContext::Render(VkCommandBuffer cmd, uint32_t imageIndex) + { + VkRenderPassBeginInfo renderPassInfo { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .renderPass = m_RenderPass, + .framebuffer = m_Framebuffers[imageIndex], + .renderArea = { + .offset = { 0U, 0U }, + .extent = m_Extent + }, + }; + + vkCmdBeginRenderPass(cmd, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + + ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd); + + vkCmdEndRenderPass(cmd); + } +} \ No newline at end of file diff --git a/Source/Renderer/ImGuiContext.hpp b/Source/Renderer/ImGuiContext.hpp new file mode 100644 index 0000000..e2e3f3d --- /dev/null +++ b/Source/Renderer/ImGuiContext.hpp @@ -0,0 +1,33 @@ +#pragma once + +#ifndef EN_IMGUICONTEXT_HPP +#define EN_IMGUICONTEXT_HPP + +#include +#include +#include + +#include +#include + +namespace en +{ + class ImGuiContext + { + public: + ImGuiContext(VkFormat imageFormat, VkExtent2D imageExtent, const std::vector& imageViews); + ~ImGuiContext(); + + void UpdateFramebuffers(VkExtent2D imageExtent, const std::vector& imageViews); + + void Render(VkCommandBuffer cmd, uint32_t imageIndex); + + private: + VkRenderPass m_RenderPass{}; + VkExtent2D m_Extent{}; + + std::vector m_Framebuffers{}; + }; +} + +#endif \ No newline at end of file diff --git a/Source/Renderer/Image.cpp b/Source/Renderer/Image.cpp index 353d865..f625ea2 100644 --- a/Source/Renderer/Image.cpp +++ b/Source/Renderer/Image.cpp @@ -7,7 +7,7 @@ namespace en { Image::Image(VkExtent2D size, VkFormat format, VkImageUsageFlags usageFlags, VkImageAspectFlags aspectFlags, VkImageCreateFlags createFlags, VkImageLayout initialLayout, uint32_t layerCount, bool genMipMaps) - : m_Size(size), m_Format(format), m_UsageFlags(usageFlags), m_AspectFlags(aspectFlags), m_InitialLayout(initialLayout), m_LayerCount(layerCount) + : m_IsBorrowed(false), m_Size(size), m_Format(format), m_UsageFlags(usageFlags), m_AspectFlags(aspectFlags), m_InitialLayout(initialLayout), m_LayerCount(layerCount) { if (genMipMaps) m_MipLevelCount = static_cast(std::floor(std::log2(std::max(m_Size.width, m_Size.height)))) + 1U; @@ -59,12 +59,20 @@ namespace en m_LayerImageViews.push_back(view); } } - + + Helpers::SimpleTransitionImageLayout(m_Image, m_Format, m_AspectFlags, m_CurrentLayout, m_InitialLayout, m_LayerCount, m_MipLevelCount); m_CurrentLayout = m_InitialLayout; } + Image::Image(VkImage image, VkImageView view, VkExtent2D size, VkFormat format, VkImageUsageFlags usageFlags, VkImageAspectFlags aspectFlags,VkImageLayout layout, uint32_t layerCount) + : m_IsBorrowed(true), m_Image(image), m_ImageView(view), m_Size(size), m_Format(format), m_UsageFlags(usageFlags), m_AspectFlags(aspectFlags), m_InitialLayout(layout), m_CurrentLayout(layout), m_LayerCount(layerCount) + { + } Image::~Image() { + if (m_IsBorrowed) + return; + UseContext(); for(auto& layer : m_LayerImageViews) @@ -81,7 +89,7 @@ namespace en { VkDeviceSize imageByteSize = m_Size.width * m_Size.height * 4U; - ChangeLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + ChangeLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); MemoryBuffer stagingBuffer( imageByteSize, diff --git a/Source/Renderer/Image.hpp b/Source/Renderer/Image.hpp index 377a359..a1a2bfa 100644 --- a/Source/Renderer/Image.hpp +++ b/Source/Renderer/Image.hpp @@ -14,6 +14,7 @@ namespace en public: Image(VkExtent2D size, VkFormat format, VkImageUsageFlags usageFlags, VkImageAspectFlags aspectFlags, VkImageCreateFlags createFlags, VkImageLayout initialLayout, uint32_t layerCount = 1U, bool genMipMaps = false); + Image(VkImage image, VkImageView view, VkExtent2D size, VkFormat format, VkImageUsageFlags usageFlags, VkImageAspectFlags aspectFlags, VkImageLayout layout, uint32_t layerCount = 1U); ~Image(); void SetData(void* data); @@ -29,6 +30,8 @@ namespace en const uint32_t m_LayerCount{}; + const bool m_IsBorrowed; + const VkImage GetHandle() const { return m_Image; }; const VkImageView GetViewHandle() const { return m_ImageView; }; diff --git a/Source/Renderer/Pipelines/ComputePipeline.cpp b/Source/Renderer/Passes/ComputePass.cpp similarity index 75% rename from Source/Renderer/Pipelines/ComputePipeline.cpp rename to Source/Renderer/Passes/ComputePass.cpp index fb4668f..603e859 100644 --- a/Source/Renderer/Pipelines/ComputePipeline.cpp +++ b/Source/Renderer/Passes/ComputePass.cpp @@ -1,8 +1,8 @@ -#include "ComputePipeline.hpp" +#include "ComputePass.hpp" namespace en { - ComputePipeline::ComputePipeline(const CreateInfo& createInfo) + ComputePass::ComputePass(const CreateInfo& createInfo) { UseContext(); @@ -18,7 +18,7 @@ namespace en }; if (vkCreatePipelineLayout(ctx.m_LogicalDevice, &pipelineLayoutCreateInfo, nullptr, &m_Layout)) - EN_ERROR("ComputePipeline::ComputePipeline() - Failed to create the compute pipeline layout!"); + EN_ERROR("ComputePass::ComputePass() - Failed to create the compute pipeline layout!"); const VkComputePipelineCreateInfo pipelineCreateInfo{ .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, @@ -35,19 +35,19 @@ namespace en }; if (vkCreateComputePipelines(ctx.m_LogicalDevice, VK_NULL_HANDLE, 1U, &pipelineCreateInfo, nullptr, &m_Pipeline) != VK_SUCCESS) - EN_ERROR("ComputePipeline::ComputePipeline() - Failed to create compute pipeline!"); + EN_ERROR("ComputePass::ComputePass() - Failed to create compute pipeline!"); DestroyShaderModule(shaderModule); } - void ComputePipeline::Bind(const VkCommandBuffer cmd) + void ComputePass::Bind(const VkCommandBuffer cmd) { m_BoundCommandBuffer = cmd; vkCmdBindPipeline(m_BoundCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, m_Pipeline); } - void ComputePipeline::Dispatch(const uint32_t x, const uint32_t y, const uint32_t z) + void ComputePass::Dispatch(const uint32_t x, const uint32_t y, const uint32_t z) { vkCmdDispatch(m_BoundCommandBuffer, x, y, z); } diff --git a/Source/Renderer/Pipelines/ComputePipeline.hpp b/Source/Renderer/Passes/ComputePass.hpp similarity index 76% rename from Source/Renderer/Pipelines/ComputePipeline.hpp rename to Source/Renderer/Passes/ComputePass.hpp index 7be9d2b..c356883 100644 --- a/Source/Renderer/Pipelines/ComputePipeline.hpp +++ b/Source/Renderer/Passes/ComputePass.hpp @@ -3,11 +3,11 @@ #ifndef EN_COMPUTE_PIPELINE_HPP #define EN_COMPUTE_PIPELINE_HPP -#include +#include namespace en { - class ComputePipeline : public Pipeline + class ComputePass : public Pass { public: struct CreateInfo @@ -17,7 +17,7 @@ namespace en std::vector pushConstantRanges{}; }; - ComputePipeline(const CreateInfo& createInfo); + ComputePass(const CreateInfo& createInfo); void Bind(const VkCommandBuffer cmd); diff --git a/Source/Renderer/Pipelines/GraphicsPipeline.cpp b/Source/Renderer/Passes/GraphicsPass.cpp similarity index 68% rename from Source/Renderer/Pipelines/GraphicsPipeline.cpp rename to Source/Renderer/Passes/GraphicsPass.cpp index df0dc5b..1ec1b7d 100644 --- a/Source/Renderer/Pipelines/GraphicsPipeline.cpp +++ b/Source/Renderer/Passes/GraphicsPass.cpp @@ -1,10 +1,10 @@ -#include "GraphicsPipeline.hpp" +#include "GraphicsPass.hpp" #include namespace en { - GraphicsPipeline::GraphicsPipeline(const CreateInfo& pipeline) + GraphicsPass::GraphicsPass(const CreateInfo& pipeline) { UseContext(); @@ -121,7 +121,7 @@ namespace en }; if (vkCreatePipelineLayout(ctx.m_LogicalDevice, &pipelineLayoutInfo, nullptr, &m_Layout) != VK_SUCCESS) - EN_ERROR("GraphicsPipeline::CreatePipeline() - Failed to create pipeline layout!"); + EN_ERROR("GraphicsPass::CreatePipeline() - Failed to create pipeline layout!"); std::vector shaderStages; @@ -141,10 +141,19 @@ namespace en } if (shaderStages.empty()) - EN_ERROR("GraphicsPipeline::CreatePipeline() - No shaders stages specified!"); + EN_ERROR("GraphicsPass::CreatePipeline() - No shaders stages specified!"); + + VkPipelineRenderingCreateInfo pipelineRenderingInfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, + .colorAttachmentCount = static_cast(pipeline.colorFormat != VK_FORMAT_UNDEFINED), + .pColorAttachmentFormats = &pipeline.colorFormat, + .depthAttachmentFormat = pipeline.depthFormat, + }; VkGraphicsPipelineCreateInfo pipelineInfo{ - .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = (void*)&pipelineRenderingInfo, + .stageCount = static_cast(shaderStages.size()), .pStages = shaderStages.data(), @@ -157,57 +166,95 @@ namespace en .pColorBlendState = &colorBlending, .pDynamicState = &dynamicState, - .layout = m_Layout, - .renderPass = pipeline.renderPass->GetHandle(), - .subpass = 0U, + .layout = m_Layout, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = -1 }; if (vkCreateGraphicsPipelines(ctx.m_LogicalDevice, VK_NULL_HANDLE, 1U, &pipelineInfo, nullptr, &m_Pipeline) != VK_SUCCESS) - EN_ERROR("GraphicsPipeline::CreatePipeline() - Failed to create pipeline!"); + EN_ERROR("GraphicsPass::CreatePipeline() - Failed to create pipeline!"); DestroyShaderModule(vShaderModule); DestroyShaderModule(fShaderModule); } - void GraphicsPipeline::Bind(VkCommandBuffer commandBuffer, VkExtent2D extent, VkCullModeFlags cullMode) + void GraphicsPass::Begin(VkCommandBuffer commandBuffer, const RenderInfo& info) { m_BoundCommandBuffer = commandBuffer; - vkCmdBindPipeline(m_BoundCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_Pipeline); + VkRenderingAttachmentInfo colorAttachmentInfo { + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .imageView = info.colorAttachmentView ? info.colorAttachmentView : VK_NULL_HANDLE, + .imageLayout = info.colorAttachmentView ? info.colorAttachmentLayout : VK_IMAGE_LAYOUT_UNDEFINED, + .loadOp = info.colorLoadOp, + .storeOp = info.colorStoreOp, + .clearValue = VkClearValue { + .color = info.clearColor + } + }; + + VkRenderingAttachmentInfo depthAttachmentInfo{ + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .imageView = info.depthAttachmentView ? info.depthAttachmentView : VK_NULL_HANDLE, + .imageLayout = info.depthAttachmentView ? info.depthAttachmentLayout : VK_IMAGE_LAYOUT_UNDEFINED, + .loadOp = info.depthLoadOp, + .storeOp = info.depthStoreOp, + .clearValue = VkClearValue { + .depthStencil = info.clearDepth + }, + }; + VkRect2D renderArea { + .extent = info.extent + }; + + VkRenderingInfo renderingInfo{ + .sType = VK_STRUCTURE_TYPE_RENDERING_INFO_KHR, + .renderArea = renderArea, + .layerCount = 1U, + .colorAttachmentCount = 1U, + .pColorAttachments = &colorAttachmentInfo, + .pDepthAttachment = &depthAttachmentInfo, + }; VkViewport viewport { - .width = static_cast(extent.width), - .height = static_cast(extent.height), + .width = static_cast(info.extent.width), + .height = static_cast(info.extent.height), .maxDepth = 1.0f }; VkRect2D scissor { - .extent = extent + .extent = info.extent }; - vkCmdSetCullMode(m_BoundCommandBuffer, cullMode); + vkCmdBeginRendering(m_BoundCommandBuffer, &renderingInfo); + + vkCmdSetCullMode(m_BoundCommandBuffer, info.cullMode); vkCmdSetViewport(m_BoundCommandBuffer, 0U, 1U, &viewport); vkCmdSetScissor(m_BoundCommandBuffer, 0U, 1U, &scissor); + + vkCmdBindPipeline(m_BoundCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_Pipeline); + } + void GraphicsPass::End() + { + vkCmdEndRendering(m_BoundCommandBuffer); } - void GraphicsPipeline::BindVertexBuffer(Handle buffer, VkDeviceSize offset) + void GraphicsPass::BindVertexBuffer(Handle buffer, VkDeviceSize offset) { VkBuffer vBuffer = buffer->GetHandle(); vkCmdBindVertexBuffers(m_BoundCommandBuffer, 0U, 1U, &vBuffer, &offset); } - void GraphicsPipeline::BindIndexBuffer(Handle buffer) + void GraphicsPass::BindIndexBuffer(Handle buffer) { vkCmdBindIndexBuffer(m_BoundCommandBuffer, buffer->GetHandle(), 0U, VK_INDEX_TYPE_UINT32); } - void GraphicsPipeline::DrawIndexedIndirect(Handle buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride) + void GraphicsPass::DrawIndexedIndirect(Handle buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride) { vkCmdDrawIndexedIndirect(m_BoundCommandBuffer, buffer->GetHandle(), offset, drawCount, stride); } - void GraphicsPipeline::DrawIndexed(uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, uint32_t firstInstance) + void GraphicsPass::DrawIndexed(uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, uint32_t firstInstance) { vkCmdDrawIndexed(m_BoundCommandBuffer, indexCount, instanceCount, firstIndex, 0U, firstInstance); } - void GraphicsPipeline::Draw(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) + void GraphicsPass::Draw(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) { vkCmdDraw(m_BoundCommandBuffer, vertexCount, instanceCount, firstVertex, firstInstance); } diff --git a/Source/Renderer/Pipelines/GraphicsPipeline.hpp b/Source/Renderer/Passes/GraphicsPass.hpp similarity index 59% rename from Source/Renderer/Pipelines/GraphicsPipeline.hpp rename to Source/Renderer/Passes/GraphicsPass.hpp index b098960..a823fda 100644 --- a/Source/Renderer/Pipelines/GraphicsPipeline.hpp +++ b/Source/Renderer/Passes/GraphicsPass.hpp @@ -3,15 +3,13 @@ #ifndef EN_GRAPHICS_PIPELINE_HPP #define EN_GRAPHICS_PIPELINE_HPP -#include - +#include #include -#include #include namespace en { - class GraphicsPipeline : public Pipeline + class GraphicsPass : public Pass { public: struct Attachment @@ -26,14 +24,15 @@ namespace en }; struct CreateInfo { - Handle renderPass{}; - std::string vShader = ""; std::string fShader = ""; std::vector descriptorLayouts{}; std::vector pushConstantRanges{}; + VkFormat colorFormat = VK_FORMAT_UNDEFINED; + VkFormat depthFormat = VK_FORMAT_UNDEFINED; + bool useVertexBindings = false; bool enableDepthTest = false; bool enableDepthWrite = true; @@ -42,10 +41,32 @@ namespace en VkCompareOp compareOp = VK_COMPARE_OP_LESS; VkPolygonMode polygonMode = VK_POLYGON_MODE_FILL; }; + struct RenderInfo + { + VkImageView colorAttachmentView = VK_NULL_HANDLE; + VkImageView depthAttachmentView = VK_NULL_HANDLE; + + VkImageLayout colorAttachmentLayout = VK_IMAGE_LAYOUT_UNDEFINED; + VkImageLayout depthAttachmentLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + VkExtent2D extent{}; + + VkClearColorValue clearColor{ {0.0f, 0.0f, 0.0f, 1.0f} }; + VkClearDepthStencilValue clearDepth{ 1.0f }; + + VkAttachmentLoadOp colorLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + VkAttachmentStoreOp colorStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + + VkAttachmentLoadOp depthLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + VkAttachmentStoreOp depthStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + + VkCullModeFlags cullMode = VK_CULL_MODE_BACK_BIT; + }; - GraphicsPipeline(const CreateInfo& pipeline); + GraphicsPass(const CreateInfo& pipeline); - void Bind(VkCommandBuffer commandBuffer, VkExtent2D extent, VkCullModeFlags cullMode = VK_CULL_MODE_BACK_BIT); + void Begin(VkCommandBuffer commandBuffer, const RenderInfo& info); + void End(); void BindVertexBuffer(Handle buffer, VkDeviceSize offset = 0U); void BindIndexBuffer(Handle buffer); diff --git a/Source/Renderer/Pipelines/Pipeline.cpp b/Source/Renderer/Passes/Pass.cpp similarity index 67% rename from Source/Renderer/Pipelines/Pipeline.cpp rename to Source/Renderer/Passes/Pass.cpp index 5c7760c..69b7112 100644 --- a/Source/Renderer/Pipelines/Pipeline.cpp +++ b/Source/Renderer/Passes/Pass.cpp @@ -1,10 +1,10 @@ -#include "Pipeline.hpp" +#include "Pass.hpp" #include namespace en { - Pipeline::~Pipeline() + Pass::~Pass() { UseContext(); @@ -15,21 +15,21 @@ namespace en vkDestroyPipeline(ctx.m_LogicalDevice, m_Pipeline, nullptr); } - void Pipeline::PushConstants(const void* data, uint32_t size, uint32_t offset, VkShaderStageFlags shaderStage) + void Pass::PushConstants(const void* data, uint32_t size, uint32_t offset, VkShaderStageFlags shaderStage) { vkCmdPushConstants(m_BoundCommandBuffer, m_Layout, shaderStage, offset, size, data); } - void Pipeline::BindDescriptorSet(Handle descriptor, uint32_t index, VkPipelineBindPoint bindPoint) + void Pass::BindDescriptorSet(Handle descriptor, uint32_t index, VkPipelineBindPoint bindPoint) { const VkDescriptorSet descriptorSet = descriptor->GetHandle(); vkCmdBindDescriptorSets(m_BoundCommandBuffer, bindPoint, m_Layout, index, 1U, &descriptorSet, 0U, nullptr); } - void Pipeline::BindDescriptorSet(VkDescriptorSet descriptor, uint32_t index, VkPipelineBindPoint bindPoint) + void Pass::BindDescriptorSet(VkDescriptorSet descriptor, uint32_t index, VkPipelineBindPoint bindPoint) { vkCmdBindDescriptorSets(m_BoundCommandBuffer, bindPoint, m_Layout, index, 1U, &descriptor, 0U, nullptr); } - VkPipelineShaderStageCreateInfo Pipeline::CreateShaderInfo(VkShaderModule shaderModule, VkShaderStageFlagBits stage) + VkPipelineShaderStageCreateInfo Pass::CreateShaderInfo(VkShaderModule shaderModule, VkShaderStageFlagBits stage) { return VkPipelineShaderStageCreateInfo { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, @@ -39,7 +39,7 @@ namespace en }; } - VkShaderModule Pipeline::CreateShaderModule(const std::string& path) + VkShaderModule Pass::CreateShaderModule(const std::string& path) { VkShaderModule shaderModule{}; @@ -57,18 +57,18 @@ namespace en return shaderModule; } - void Pipeline::DestroyShaderModule(VkShaderModule shaderModule) + void Pass::DestroyShaderModule(VkShaderModule shaderModule) { if (shaderModule != VK_NULL_HANDLE) vkDestroyShaderModule(Context::Get().m_LogicalDevice, shaderModule, nullptr); } - std::vector Pipeline::ReadShaderFile(const std::string& path) + std::vector Pass::ReadShaderFile(const std::string& path) { std::ifstream file(path, std::ios::ate | std::ios::binary); if (!file.is_open()) - EN_ERROR("Pipeline::ReadShaderFile() - Failed to open shader source file!"); + EN_ERROR("Pass::ReadShaderFile() - Failed to open shader source file!"); size_t fileSize = (size_t)file.tellg(); std::vector buffer(fileSize); @@ -78,7 +78,7 @@ namespace en file.close(); if (buffer.empty()) - EN_WARN("Pipeline::ReadShaderFile() - The read buffer is empty!"); + EN_WARN("Pass::ReadShaderFile() - The read buffer is empty!"); return buffer; } diff --git a/Source/Renderer/Pipelines/Pipeline.hpp b/Source/Renderer/Passes/Pass.hpp similarity index 97% rename from Source/Renderer/Pipelines/Pipeline.hpp rename to Source/Renderer/Passes/Pass.hpp index 1ac1a07..4785a1e 100644 --- a/Source/Renderer/Pipelines/Pipeline.hpp +++ b/Source/Renderer/Passes/Pass.hpp @@ -13,10 +13,10 @@ namespace en { - class Pipeline + class Pass { public: - ~Pipeline(); + ~Pass(); void PushConstants(const void* data, uint32_t size, uint32_t offset, VkShaderStageFlags shaderStage); diff --git a/Source/Renderer/RenderPass.cpp b/Source/Renderer/RenderPass.cpp deleted file mode 100644 index aeab330..0000000 --- a/Source/Renderer/RenderPass.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "RenderPass.hpp" - -namespace en -{ - RenderPass::RenderPass(const RenderPassAttachment& colorAttachment, const RenderPassAttachment& depthStencilAttachment, const std::vector& dependencies) - : m_UsesDepthAttachment(depthStencilAttachment.format != VK_FORMAT_UNDEFINED), - m_UsesColorAttachment(colorAttachment.format != VK_FORMAT_UNDEFINED) - { - UseContext(); - - VkAttachmentDescription colorDescription{ - .format = colorAttachment.format, - .samples = VK_SAMPLE_COUNT_1_BIT, - .loadOp = colorAttachment.loadOp, - .storeOp = colorAttachment.storeOp, - .stencilLoadOp = colorAttachment.stencilLoadOp, - .stencilStoreOp = colorAttachment.stencilStoreOp, - .initialLayout = colorAttachment.initialLayout, - .finalLayout = colorAttachment.finalLayout, - }; - - VkAttachmentReference colorReference{ - .attachment = 0U, - .layout = colorAttachment.refLayout, - }; - - VkAttachmentDescription depthDescription { - .format = depthStencilAttachment.format, - .samples = VK_SAMPLE_COUNT_1_BIT, - .loadOp = depthStencilAttachment.loadOp, - .storeOp = depthStencilAttachment.storeOp, - .stencilLoadOp = depthStencilAttachment.stencilLoadOp, - .stencilStoreOp = depthStencilAttachment.stencilStoreOp, - .initialLayout = depthStencilAttachment.initialLayout, - .finalLayout = depthStencilAttachment.finalLayout, - }; - - VkAttachmentReference depthReference { - .attachment = static_cast(m_UsesColorAttachment), - .layout = depthStencilAttachment.refLayout - }; - - std::vector descriptions{}; - - if (m_UsesColorAttachment) - descriptions.emplace_back(colorDescription); - if (m_UsesDepthAttachment) - descriptions.emplace_back(depthDescription); - - VkSubpassDescription subpass { - .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, - .colorAttachmentCount = static_cast(m_UsesColorAttachment), - .pColorAttachments = m_UsesColorAttachment ? &colorReference : nullptr, - .pDepthStencilAttachment = m_UsesDepthAttachment ? &depthReference : nullptr, - }; - - VkRenderPassCreateInfo renderPassInfo{ - .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, - .attachmentCount = static_cast(descriptions.size()), - .pAttachments = descriptions.data(), - .subpassCount = 1U, - .pSubpasses = &subpass, - .dependencyCount = static_cast(dependencies.size()), - .pDependencies = dependencies.data() - }; - - if (vkCreateRenderPass(ctx.m_LogicalDevice, &renderPassInfo, nullptr, &m_RenderPass) != VK_SUCCESS) - EN_ERROR("RenderPass::RenderPass() - Failed to create a RenderPass!"); - } - - RenderPass::~RenderPass() - { - vkDestroyRenderPass(Context::Get().m_LogicalDevice, m_RenderPass, nullptr); - } - void RenderPass::Begin(VkCommandBuffer cmd, VkFramebuffer framebuffer, VkExtent2D extent, glm::vec4 clearColor, float depthClearValue, uint32_t stencilClearValue) - { - m_BoundCMD = cmd; - - VkClearValue colorClearValue{}; - colorClearValue.color = VkClearColorValue { - clearColor.r, clearColor.g, clearColor.b, clearColor.a - }; - - VkClearValue depthStencilClearValue{}; - depthStencilClearValue.depthStencil = VkClearDepthStencilValue{ - depthClearValue, stencilClearValue - }; - - std::vector clearValues{}; - if (m_UsesColorAttachment) - clearValues.emplace_back(colorClearValue); - if (m_UsesDepthAttachment) - clearValues.emplace_back(depthStencilClearValue); - - VkRenderPassBeginInfo renderPassInfo{ - .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - .renderPass = m_RenderPass, - .framebuffer = framebuffer, - .renderArea = { - .offset = { 0U, 0U }, - .extent = extent - }, - - .clearValueCount = static_cast(clearValues.size()), - .pClearValues = clearValues.data(), - }; - - vkCmdBeginRenderPass(m_BoundCMD, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); - } - void RenderPass::End() - { - vkCmdEndRenderPass(m_BoundCMD); - } -} \ No newline at end of file diff --git a/Source/Renderer/RenderPass.hpp b/Source/Renderer/RenderPass.hpp deleted file mode 100644 index 1965087..0000000 --- a/Source/Renderer/RenderPass.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once -#ifndef EN_RENDERPASS_HPP -#define EN_RENDERPASS_HPP - -#include -//#include - -namespace en -{ - struct RenderPassAttachment - { - VkImageView imageView = VK_NULL_HANDLE; - - VkFormat format = VK_FORMAT_UNDEFINED; - - VkImageLayout initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - VkImageLayout refLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkImageLayout finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkAttachmentLoadOp loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - VkAttachmentStoreOp storeOp = VK_ATTACHMENT_STORE_OP_STORE; - - VkAttachmentLoadOp stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - VkAttachmentStoreOp stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - }; - class RenderPass - { - public: - RenderPass( - const RenderPassAttachment& colorAttachment = {}, - const RenderPassAttachment& depthStencilAttachment = {}, - const std::vector& dependencies = {} - ); - - ~RenderPass(); - - void Begin(VkCommandBuffer cmd, VkFramebuffer framebuffer, VkExtent2D extent, glm::vec4 clearColor = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f), float depthClearValue = 1.0f, uint32_t stencilClearValue = 0U); - void End(); - - const VkRenderPass GetHandle() const { return m_RenderPass; } - - const bool m_UsesDepthAttachment; - const bool m_UsesColorAttachment; - - private: - VkRenderPass m_RenderPass{}; - VkCommandBuffer m_BoundCMD{}; - }; -} - -#endif \ No newline at end of file diff --git a/Source/Renderer/Renderer.cpp b/Source/Renderer/Renderer.cpp index 0f13529..5f67c1b 100644 --- a/Source/Renderer/Renderer.cpp +++ b/Source/Renderer/Renderer.cpp @@ -1,7 +1,5 @@ #include "Renderer.hpp" -#include - namespace en { Renderer* g_CurrentBackend{}; @@ -21,7 +19,6 @@ namespace en { vkDeviceWaitIdle(g_Ctx->m_LogicalDevice); - DestroyImGuiContext(); DestroyPerFrameData(); } @@ -75,14 +72,10 @@ namespace en if (m_Scene) { ShadowPass(); - ClusterComputePass(); - - if(m_Settings.depthPrePass) - DepthPass(); - + DepthPass(); + SSAOPass(); ForwardPass(); - AntialiasingPass(); } @@ -138,13 +131,7 @@ namespace en m_SkipFrame = false; - if (m_ReloadQueued) - { - ReloadBackendImpl(); - m_SkipFrame = true; - return; - } - else if (result == VK_ERROR_OUT_OF_DATE_KHR) + if (result == VK_ERROR_OUT_OF_DATE_KHR) { RecreateFramebuffer(); m_SkipFrame = true; @@ -167,33 +154,35 @@ namespace en void Renderer::ShadowPass() { if (m_SkipFrame) return; - + for (const auto& i : m_Scene->m_ActivePointLightsShadowIDs) { const auto& light = m_Scene->m_PointLights[i]; - m_PointShadowMaps[light.m_ShadowmapIndex].image->ChangeLayout( + m_PointShadowMaps[light.m_ShadowmapIndex]->ChangeLayout( VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_SHADER_WRITE_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, m_Frames[m_FrameIndex].commandBuffer ); for (uint32_t cubeSide = 0U; cubeSide < 6U; cubeSide++) { - m_PerspectiveShadowsRenderPass->Begin( - m_Frames[m_FrameIndex].commandBuffer, - m_PointShadowMaps[light.m_ShadowmapIndex].framebuffers[cubeSide]->GetHandle(), - m_PointShadowMaps[light.m_ShadowmapIndex].framebuffers[cubeSide]->m_Extent - ); + GraphicsPass::RenderInfo renderInfo{ + .colorAttachmentView = m_PointShadowMaps[light.m_ShadowmapIndex]->GetLayerViewHandle(cubeSide), + .depthAttachmentView = m_PointShadowDepthBuffer->GetViewHandle(), - m_PointShadowPipeline->Bind( - m_Frames[m_FrameIndex].commandBuffer, - m_PointShadowMaps[light.m_ShadowmapIndex].framebuffers[cubeSide]->m_Extent, - VK_CULL_MODE_FRONT_BIT - ); + .colorAttachmentLayout = m_PointShadowMaps[light.m_ShadowmapIndex]->GetLayout(), + .depthAttachmentLayout = m_PointShadowDepthBuffer->GetLayout(), + + .extent = m_PointShadowMaps[light.m_ShadowmapIndex]->m_Size, + + .cullMode = VK_CULL_MODE_FRONT_BIT + }; - m_PointShadowPipeline->BindDescriptorSet(m_Scene->m_LightingDescriptorSet->GetHandle()); + m_PointShadowPass->Begin(m_Frames[m_FrameIndex].commandBuffer, renderInfo); + + m_PointShadowPass->BindDescriptorSet(m_Scene->m_LightingDescriptorSet->GetHandle()); for (const auto& [name, sceneObject] : m_Scene->m_SceneObjects) { @@ -201,7 +190,7 @@ namespace en uint32_t pushConstant[3]{ sceneObject->GetMatrixIndex(), light.m_ShadowmapIndex, cubeSide }; - m_PointShadowPipeline->PushConstants( + m_PointShadowPass->PushConstants( pushConstant, sizeof(uint32_t) * 3, 0U, @@ -212,41 +201,47 @@ namespace en { if (!subMesh.m_Active) continue; - m_PointShadowPipeline->BindVertexBuffer(subMesh.m_VertexBuffer); - m_PointShadowPipeline->BindIndexBuffer(subMesh.m_IndexBuffer); + m_PointShadowPass->BindVertexBuffer(subMesh.m_VertexBuffer); + m_PointShadowPass->BindIndexBuffer(subMesh.m_IndexBuffer); - m_PointShadowPipeline->DrawIndexed(subMesh.m_IndexCount); + m_PointShadowPass->DrawIndexed(subMesh.m_IndexCount); } } - m_PerspectiveShadowsRenderPass->End(); + m_PointShadowPass->End(); } - m_PointShadowMaps[light.m_ShadowmapIndex].image->m_CurrentLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + m_PointShadowMaps[light.m_ShadowmapIndex]->ChangeLayout( + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + m_Frames[m_FrameIndex].commandBuffer + ); } + for (const auto& i : m_Scene->m_ActiveSpotLightsShadowIDs) { const auto& light = m_Scene->m_SpotLights[i]; - m_SpotShadowMaps[light.m_ShadowmapIndex].image->ChangeLayout( + m_SpotShadowMaps[light.m_ShadowmapIndex]->ChangeLayout( VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, m_Frames[m_FrameIndex].commandBuffer ); - m_DirShadowsRenderPass->Begin( - m_Frames[m_FrameIndex].commandBuffer, - m_SpotShadowMaps[light.m_ShadowmapIndex].framebuffer->GetHandle(), - m_SpotShadowMaps[light.m_ShadowmapIndex].framebuffer->m_Extent - ); + GraphicsPass::RenderInfo renderInfo{ + .depthAttachmentView = m_SpotShadowMaps[light.m_ShadowmapIndex]->GetViewHandle(), + .depthAttachmentLayout = m_SpotShadowMaps[light.m_ShadowmapIndex]->GetLayout(), - m_SpotShadowPipeline->Bind( - m_Frames[m_FrameIndex].commandBuffer, - m_SpotShadowMaps[light.m_ShadowmapIndex].framebuffer->m_Extent, - VK_CULL_MODE_FRONT_BIT - ); + .extent = m_SpotShadowMaps[light.m_ShadowmapIndex]->m_Size, + + .cullMode = VK_CULL_MODE_FRONT_BIT + }; - m_SpotShadowPipeline->BindDescriptorSet(m_Scene->m_LightingDescriptorSet->GetHandle()); + m_SpotShadowPass->Begin(m_Frames[m_FrameIndex].commandBuffer, renderInfo); + + m_SpotShadowPass->BindDescriptorSet(m_Scene->m_LightingDescriptorSet->GetHandle()); for (const auto& [name, sceneObject] : m_Scene->m_SceneObjects) { @@ -254,7 +249,7 @@ namespace en uint32_t pushConstant[2]{ sceneObject->GetMatrixIndex(), light.m_ShadowmapIndex }; - m_SpotShadowPipeline->PushConstants( + m_SpotShadowPass->PushConstants( pushConstant, sizeof(uint32_t) * 2, 0U, @@ -265,43 +260,49 @@ namespace en { if (!subMesh.m_Active) continue; - m_SpotShadowPipeline->BindVertexBuffer(subMesh.m_VertexBuffer); - m_SpotShadowPipeline->BindIndexBuffer(subMesh.m_IndexBuffer); + m_SpotShadowPass->BindVertexBuffer(subMesh.m_VertexBuffer); + m_SpotShadowPass->BindIndexBuffer(subMesh.m_IndexBuffer); - m_SpotShadowPipeline->DrawIndexed(subMesh.m_IndexCount); + m_SpotShadowPass->DrawIndexed(subMesh.m_IndexCount); } } - m_SpotShadowMaps[light.m_ShadowmapIndex].image->m_CurrentLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - m_DirShadowsRenderPass->End(); + m_SpotShadowPass->End(); + + m_SpotShadowMaps[light.m_ShadowmapIndex]->ChangeLayout( + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + m_Frames[m_FrameIndex].commandBuffer + ); } + for (const auto& i : m_Scene->m_ActiveDirLightsShadowIDs) { const auto& light = m_Scene->m_DirectionalLights[i]; for (uint32_t cascadeIndex = 0U; cascadeIndex < SHADOW_CASCADES; cascadeIndex++) { - m_DirShadowMaps[light.m_ShadowmapIndex * SHADOW_CASCADES + cascadeIndex].image->ChangeLayout( + m_DirShadowMaps[light.m_ShadowmapIndex * SHADOW_CASCADES + cascadeIndex]->ChangeLayout( VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, m_Frames[m_FrameIndex].commandBuffer ); - m_DirShadowsRenderPass->Begin( - m_Frames[m_FrameIndex].commandBuffer, - m_DirShadowMaps[light.m_ShadowmapIndex * SHADOW_CASCADES + cascadeIndex].framebuffer->GetHandle(), - m_DirShadowMaps[light.m_ShadowmapIndex * SHADOW_CASCADES + cascadeIndex].framebuffer->m_Extent - ); + GraphicsPass::RenderInfo renderInfo{ + .depthAttachmentView = m_DirShadowMaps[light.m_ShadowmapIndex * SHADOW_CASCADES + cascadeIndex]->GetViewHandle(), + .depthAttachmentLayout = m_DirShadowMaps[light.m_ShadowmapIndex * SHADOW_CASCADES + cascadeIndex]->GetLayout(), - m_DirShadowPipeline->Bind( - m_Frames[m_FrameIndex].commandBuffer, - m_DirShadowMaps[light.m_ShadowmapIndex * SHADOW_CASCADES + cascadeIndex].framebuffer->m_Extent, - VK_CULL_MODE_FRONT_BIT - ); + .extent = m_DirShadowMaps[light.m_ShadowmapIndex * SHADOW_CASCADES + cascadeIndex]->m_Size, + + .cullMode = VK_CULL_MODE_FRONT_BIT + }; + + m_DirShadowPass->Begin(m_Frames[m_FrameIndex].commandBuffer, renderInfo); - m_DirShadowPipeline->BindDescriptorSet(m_Scene->m_LightingDescriptorSet->GetHandle(), 0U); - m_DirShadowPipeline->BindDescriptorSet(m_CameraBuffer->GetDescriptorHandle(m_FrameIndex), 1U); + m_DirShadowPass->BindDescriptorSet(m_Scene->m_LightingDescriptorSet->GetHandle(), 0U); + m_DirShadowPass->BindDescriptorSet(m_CameraBuffer->GetDescriptorHandle(m_FrameIndex), 1U); for (const auto& [name, sceneObject] : m_Scene->m_SceneObjects) { @@ -309,7 +310,7 @@ namespace en uint32_t pushConstant[3] { sceneObject->GetMatrixIndex(), light.m_ShadowmapIndex, cascadeIndex }; - m_DirShadowPipeline->PushConstants( + m_DirShadowPass->PushConstants( pushConstant, sizeof(uint32_t)*3, 0U, @@ -320,15 +321,21 @@ namespace en { if (!subMesh.m_Active) continue; - m_DirShadowPipeline->BindVertexBuffer(subMesh.m_VertexBuffer); - m_DirShadowPipeline->BindIndexBuffer(subMesh.m_IndexBuffer); + m_DirShadowPass->BindVertexBuffer(subMesh.m_VertexBuffer); + m_DirShadowPass->BindIndexBuffer(subMesh.m_IndexBuffer); - m_DirShadowPipeline->DrawIndexed(subMesh.m_IndexCount); + m_DirShadowPass->DrawIndexed(subMesh.m_IndexCount); } } - m_DirShadowMaps[light.m_ShadowmapIndex * SHADOW_CASCADES + cascadeIndex].image->m_CurrentLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - m_DirShadowsRenderPass->End(); + m_DirShadowPass->End(); + + m_DirShadowMaps[light.m_ShadowmapIndex * SHADOW_CASCADES + cascadeIndex]->ChangeLayout( + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + m_Frames[m_FrameIndex].commandBuffer + ); } } } @@ -344,134 +351,182 @@ namespace en glm::uvec4 screenSize(m_Swapchain->GetExtent().width, m_Swapchain->GetExtent().height, sizeX, sizeY); - m_ClusterAABBCreation->Bind(m_Frames[m_FrameIndex].commandBuffer); - - m_ClusterAABBCreation->PushConstants(&screenSize, sizeof(glm::uvec4), 0U, VK_SHADER_STAGE_COMPUTE_BIT); - - m_ClusterAABBCreation->BindDescriptorSet(m_ClusterSSBOs.aabbClustersDescriptor, 0U, VK_PIPELINE_BIND_POINT_COMPUTE); - - m_ClusterAABBCreation->BindDescriptorSet(m_CameraBuffer->GetDescriptorHandle(m_FrameIndex), 1U, VK_PIPELINE_BIND_POINT_COMPUTE); - - m_ClusterAABBCreation->Dispatch(CLUSTERED_TILES_X, CLUSTERED_TILES_Y, CLUSTERED_TILES_Z); + m_ClusterAABBCreationPass->Bind(m_Frames[m_FrameIndex].commandBuffer); + m_ClusterAABBCreationPass->PushConstants(&screenSize, sizeof(glm::uvec4), 0U, VK_SHADER_STAGE_COMPUTE_BIT); + m_ClusterAABBCreationPass->BindDescriptorSet(m_ClusterSSBOs.aabbClustersDescriptor, 0U, VK_PIPELINE_BIND_POINT_COMPUTE); + m_ClusterAABBCreationPass->BindDescriptorSet(m_CameraBuffer->GetDescriptorHandle(m_FrameIndex), 1U, VK_PIPELINE_BIND_POINT_COMPUTE); + m_ClusterAABBCreationPass->Dispatch(CLUSTERED_TILES_X, CLUSTERED_TILES_Y, CLUSTERED_TILES_Z); m_ClusterFrustumChanged = false; } - m_ClusterLightCulling->Bind(m_Frames[m_FrameIndex].commandBuffer); + m_ClusterLightCullingPass->Bind(m_Frames[m_FrameIndex].commandBuffer); - m_ClusterLightCulling->BindDescriptorSet(m_ClusterSSBOs.clusterLightCullingDescriptor, 0U, VK_PIPELINE_BIND_POINT_COMPUTE); - m_ClusterLightCulling->BindDescriptorSet(m_CameraBuffer->GetDescriptorHandle(m_FrameIndex), 1U, VK_PIPELINE_BIND_POINT_COMPUTE); - m_ClusterLightCulling->BindDescriptorSet(m_Scene->m_LightsBufferDescriptorSet, 2U, VK_PIPELINE_BIND_POINT_COMPUTE); + m_ClusterLightCullingPass->BindDescriptorSet(m_ClusterSSBOs.clusterLightCullingDescriptor, 0U, VK_PIPELINE_BIND_POINT_COMPUTE); + m_ClusterLightCullingPass->BindDescriptorSet(m_CameraBuffer->GetDescriptorHandle(m_FrameIndex), 1U, VK_PIPELINE_BIND_POINT_COMPUTE); + m_ClusterLightCullingPass->BindDescriptorSet(m_Scene->m_LightsBufferDescriptorSet, 2U, VK_PIPELINE_BIND_POINT_COMPUTE); - m_ClusterLightCulling->Dispatch(1U, 1U, CLUSTERED_BATCHES); + m_ClusterLightCullingPass->Dispatch(1U, 1U, CLUSTERED_BATCHES); } void Renderer::DepthPass() { - if (m_SkipFrame) return; + if (m_SkipFrame || !m_Settings.depthPrePass) return; - m_DepthRenderPass->Begin( - m_Frames[m_FrameIndex].commandBuffer, - m_DepthFramebuffer->GetHandle(), - m_DepthFramebuffer->m_Extent - ); + GraphicsPass::RenderInfo renderInfo { + .depthAttachmentView = m_DepthBuffer->GetViewHandle(), + .depthAttachmentLayout = m_DepthBuffer->GetLayout(), + .extent = m_DepthBuffer->m_Size + }; - m_DepthPipeline->Bind(m_Frames[m_FrameIndex].commandBuffer, m_Swapchain->GetExtent()); + m_DepthPass->Begin(m_Frames[m_FrameIndex].commandBuffer, renderInfo); - m_DepthPipeline->BindDescriptorSet(m_CameraBuffer->GetDescriptorHandle(m_FrameIndex), 0U); - m_DepthPipeline->BindDescriptorSet(m_Scene->m_GlobalDescriptorSet, 1U); + m_DepthPass->BindDescriptorSet(m_CameraBuffer->GetDescriptorHandle(m_FrameIndex), 0U); + m_DepthPass->BindDescriptorSet(m_Scene->m_GlobalDescriptorSet, 1U); for (const auto& [name, sceneObject] : m_Scene->m_SceneObjects) { if (!sceneObject->m_Active || !sceneObject->m_Mesh->m_Active) continue; - m_DepthPipeline->PushConstants(&sceneObject->GetMatrixIndex(), sizeof(uint32_t), 0U, VK_SHADER_STAGE_VERTEX_BIT); + m_DepthPass->PushConstants(&sceneObject->GetMatrixIndex(), sizeof(uint32_t), 0U, VK_SHADER_STAGE_VERTEX_BIT); for (const auto& subMesh : sceneObject->m_Mesh->m_SubMeshes) { if (!subMesh.m_Active) continue; - m_DepthPipeline->BindVertexBuffer(subMesh.m_VertexBuffer); - m_DepthPipeline->BindIndexBuffer(subMesh.m_IndexBuffer); + m_DepthPass->BindVertexBuffer(subMesh.m_VertexBuffer); + m_DepthPass->BindIndexBuffer(subMesh.m_IndexBuffer); - m_DepthPipeline->DrawIndexed(subMesh.m_IndexCount); + m_DepthPass->DrawIndexed(subMesh.m_IndexCount); } } - m_DepthRenderPass->End(); + m_DepthPass->End(); + + if (m_Settings.ssaoMode == SSAOMode::None) + m_DepthBuffer->ChangeLayout(m_DepthBuffer->GetLayout(), + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + m_Frames[m_FrameIndex].commandBuffer + ); + else + m_DepthBuffer->ChangeLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + m_Frames[m_FrameIndex].commandBuffer + ); } - void Renderer::ForwardPass() + void Renderer::SSAOPass() { if (m_SkipFrame) return; + m_SSAOTarget->ChangeLayout( + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + m_Frames[m_FrameIndex].commandBuffer + ); + + m_Settings.ssao.screenWidth = m_SSAOTarget->m_Size.width; + m_Settings.ssao.screenHeight = m_SSAOTarget->m_Size.height; + + GraphicsPass::RenderInfo renderInfo { + .colorAttachmentView = m_SSAOTarget->GetViewHandle(), + .colorAttachmentLayout = m_SSAOTarget->GetLayout(), + .extent = m_SSAOTarget->m_Size, + .clearColor = { 1.0f, 1.0f, 1.0f, 1.0f }, + .cullMode = VK_CULL_MODE_FRONT_BIT, + }; + + m_SSAOPass->Begin(m_Frames[m_FrameIndex].commandBuffer, renderInfo); + if (m_Settings.ssaoMode != SSAOMode::None && m_Settings.depthPrePass) + { + m_SSAOPass->PushConstants(&m_Settings.ssao, sizeof(m_Settings.ssao), 0U, VK_SHADER_STAGE_FRAGMENT_BIT); + m_SSAOPass->BindDescriptorSet(m_CameraBuffer->GetDescriptorHandle(m_FrameIndex), 0U); + m_SSAOPass->BindDescriptorSet(m_DepthBufferDescriptor, 1U); + m_SSAOPass->Draw(3U); + } + m_SSAOPass->End(); + + m_DepthBuffer->ChangeLayout( + VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, + VK_ACCESS_SHADER_READ_BIT, + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + m_Frames[m_FrameIndex].commandBuffer + ); + + m_SSAOTarget->ChangeLayout( + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + m_Frames[m_FrameIndex].commandBuffer + ); + } + void Renderer::ForwardPass() + { + if (m_SkipFrame) return; + if(m_Settings.antialiasingMode != AntialiasingMode::None) m_AliasedImage->ChangeLayout( VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, m_Frames[m_FrameIndex].commandBuffer - ); - - //m_ClusterSSBOs.pointLightGrid->PipelineBarrier( - // VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, - // VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - // m_Frames[m_FrameIndex].commandBuffer - //); - //m_ClusterSSBOs.pointLightIndices->PipelineBarrier( - // VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, - // VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - // m_Frames[m_FrameIndex].commandBuffer - //); - //m_ClusterSSBOs.spotLightGrid->PipelineBarrier( - // VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, - // VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - // m_Frames[m_FrameIndex].commandBuffer - //); - //m_ClusterSSBOs.spotLightIndices->PipelineBarrier( - // VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, - // VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - // m_Frames[m_FrameIndex].commandBuffer - //); - - m_ForwardPass->Begin( - m_Frames[m_FrameIndex].commandBuffer, - m_ForwardFramebuffers[m_Swapchain->m_ImageIndex]->GetHandle(), - m_ForwardFramebuffers[m_Swapchain->m_ImageIndex]->m_Extent, - glm::vec4(m_Scene->m_AmbientColor, 1.0) ); - m_Pipeline->Bind(m_Frames[m_FrameIndex].commandBuffer, m_Swapchain->GetExtent()); + GraphicsPass::RenderInfo renderInfo{ + .colorAttachmentView = m_Settings.antialiasingMode != AntialiasingMode::None ? m_AliasedImage->GetViewHandle() : m_Swapchain->m_ImageViews[m_Swapchain->m_ImageIndex], + .depthAttachmentView = m_DepthBuffer->GetViewHandle(), + + .colorAttachmentLayout = m_Settings.antialiasingMode != AntialiasingMode::None ? m_AliasedImage->GetLayout() : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + .depthAttachmentLayout = m_DepthBuffer->GetLayout(), + + .extent = m_Settings.antialiasingMode != AntialiasingMode::None ? m_AliasedImage->m_Size : m_Swapchain->GetExtent(), + + .clearColor{ + m_Scene->m_AmbientColor.r, m_Scene->m_AmbientColor.g, m_Scene->m_AmbientColor.b, 1.0f + }, + + .depthLoadOp = m_Settings.depthPrePass ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR, + .depthStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + }; - m_Pipeline->BindDescriptorSet(m_CameraBuffer->GetDescriptorHandle(m_FrameIndex), 0U); - m_Pipeline->BindDescriptorSet(m_Scene->m_GlobalDescriptorSet, 1U); - m_Pipeline->BindDescriptorSet(m_ShadowMapsDescriptor, 2U); - m_Pipeline->BindDescriptorSet(m_ClusterDescriptor, 3U); + m_ForwardPass->Begin(m_Frames[m_FrameIndex].commandBuffer, renderInfo); - m_Pipeline->PushConstants(&m_Scene->m_MainCamera->m_Exposure, sizeof(float), sizeof(uint32_t)*2, VK_SHADER_STAGE_FRAGMENT_BIT); + m_ForwardPass->BindDescriptorSet(m_CameraBuffer->GetDescriptorHandle(m_FrameIndex), 0U); + m_ForwardPass->BindDescriptorSet(m_Scene->m_GlobalDescriptorSet, 1U); + m_ForwardPass->BindDescriptorSet(m_ShadowMapsDescriptor, 2U); + m_ForwardPass->BindDescriptorSet(m_ClusterDescriptor, 3U); + m_ForwardPass->BindDescriptorSet(m_SSAODescriptor, 4U); + + m_ForwardPass->PushConstants(&m_Scene->m_MainCamera->m_Exposure, sizeof(float), sizeof(uint32_t)*2, VK_SHADER_STAGE_FRAGMENT_BIT); for (const auto& [name, sceneObject] : m_Scene->m_SceneObjects) { if(!sceneObject->m_Active || !sceneObject->m_Mesh->m_Active) continue; - m_Pipeline->PushConstants(&sceneObject->GetMatrixIndex(), sizeof(uint32_t), 0U, VK_SHADER_STAGE_VERTEX_BIT); + m_ForwardPass->PushConstants(&sceneObject->GetMatrixIndex(), sizeof(uint32_t), 0U, VK_SHADER_STAGE_VERTEX_BIT); for (const auto& subMesh : sceneObject->m_Mesh->m_SubMeshes) { if (!subMesh.m_Active) continue; - m_Pipeline->PushConstants(&subMesh.GetMaterialIndex(), sizeof(uint32_t), sizeof(uint32_t), VK_SHADER_STAGE_FRAGMENT_BIT); + m_ForwardPass->PushConstants(&subMesh.GetMaterialIndex(), sizeof(uint32_t), sizeof(uint32_t), VK_SHADER_STAGE_FRAGMENT_BIT); - m_Pipeline->BindVertexBuffer(subMesh.m_VertexBuffer); - m_Pipeline->BindIndexBuffer(subMesh.m_IndexBuffer); + m_ForwardPass->BindVertexBuffer(subMesh.m_VertexBuffer); + m_ForwardPass->BindIndexBuffer(subMesh.m_IndexBuffer); - m_Pipeline->DrawIndexed(subMesh.m_IndexCount); + m_ForwardPass->DrawIndexed(subMesh.m_IndexCount); } } m_ForwardPass->End(); - // Vulkan changes the layout after RenderPass - if (m_Settings.antialiasingMode != AntialiasingMode::None) - m_AliasedImage->m_CurrentLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } void Renderer::AntialiasingPass() { @@ -487,36 +542,28 @@ namespace en m_Settings.antialiasing.texelSizeX = 1.0f / m_Swapchain->GetExtent().width; m_Settings.antialiasing.texelSizeY = 1.0f / m_Swapchain->GetExtent().height; - m_AntialiasingPass->Begin( - m_Frames[m_FrameIndex].commandBuffer, - m_AntialiasingFramebuffers[m_Swapchain->m_ImageIndex]->GetHandle(), - m_AntialiasingFramebuffers[m_Swapchain->m_ImageIndex]->m_Extent - ); - - m_AntialiasingPipeline->Bind(m_Frames[m_FrameIndex].commandBuffer, m_Swapchain->GetExtent(), VK_CULL_MODE_FRONT_BIT); + GraphicsPass::RenderInfo renderInfo{ + .colorAttachmentView = m_Swapchain->m_ImageViews[m_Swapchain->m_ImageIndex], + .colorAttachmentLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - m_AntialiasingPipeline->BindDescriptorSet(m_AntialiasingDescriptor); - m_AntialiasingPipeline->PushConstants(&m_Settings.antialiasing, sizeof(m_Settings.antialiasing), 0U, VK_SHADER_STAGE_FRAGMENT_BIT); + .extent = m_Swapchain->GetExtent(), - m_AntialiasingPipeline->Draw(3U); + .cullMode = VK_CULL_MODE_FRONT_BIT, + }; + m_AntialiasingPass->Begin(m_Frames[m_FrameIndex].commandBuffer, renderInfo); + m_AntialiasingPass->BindDescriptorSet(m_AntialiasingDescriptor); + m_AntialiasingPass->PushConstants(&m_Settings.antialiasing, sizeof(m_Settings.antialiasing), 0U, VK_SHADER_STAGE_FRAGMENT_BIT); + m_AntialiasingPass->Draw(3U); m_AntialiasingPass->End(); } void Renderer::ImGuiPass() { if (m_SkipFrame || m_ImGuiRenderCallback == nullptr) return; - + m_ImGuiRenderCallback(); - m_ImGuiRenderPass->Begin( - m_Frames[m_FrameIndex].commandBuffer, - m_ImGuiFramebuffers[m_Swapchain->m_ImageIndex]->GetHandle(), - m_ImGuiFramebuffers[m_Swapchain->m_ImageIndex]->m_Extent - ); - - ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), m_Frames[m_FrameIndex].commandBuffer); - - m_ImGuiRenderPass->End(); + m_ImGuiContext->Render(m_Frames[m_FrameIndex].commandBuffer, m_Swapchain->m_ImageIndex); } void Renderer::EndRender() { @@ -564,6 +611,8 @@ namespace en if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || m_FramebufferResized) RecreateFramebuffer(); + else if (m_ReloadQueued) + ReloadBackendImpl(); else if (result != VK_SUCCESS) EN_ERROR("Renderer::EndRender() - Failed to present swap chain image!"); @@ -618,6 +667,11 @@ namespace en m_Settings.antialiasingMode = antialiasingMode; ReloadBackend(); } + void Renderer::SetSSAOMode(const SSAOMode ssaoMode) + { + m_Settings.ssaoMode = ssaoMode; + ReloadBackend(); + } void Renderer::UpdateCSM() { @@ -722,25 +776,23 @@ namespace en glm::ivec2 size{}; while (size.x == 0 || size.y == 0) size = Window::Get().GetFramebufferSize(); - + vkDeviceWaitIdle(g_Ctx->m_LogicalDevice); m_Swapchain.reset(); // It is critical to reset before creating a new one m_Swapchain = MakeHandle(m_Settings.vSync); CreateDepthBuffer(); - CreateDepthPass(); - CreateForwardPass(); - CreateAntialiasingPass(); - CreateImGuiRenderPass(); + CreateAATarget(); + CreateSSAOTarget(); - ImGui_ImplVulkan_SetMinImageCount(m_Swapchain->m_ImageViews.size()); + m_ImGuiContext->UpdateFramebuffers(m_Swapchain->GetExtent(), m_Swapchain->m_ImageViews); m_FramebufferResized = false; vkDeviceWaitIdle(g_Ctx->m_LogicalDevice); - EN_LOG("Resized"); + EN_LOG("Resized to (" + std::to_string(m_Swapchain->GetExtent().width) + ", " + std::to_string(m_Swapchain->GetExtent().height) + ")"); } void Renderer::ReloadBackend() { @@ -765,7 +817,7 @@ namespace en EN_SUCCESS("Init began!") m_Swapchain.reset(); - m_Swapchain = MakeHandle(m_Settings.vSync); + m_Swapchain = MakeHandle(m_Settings.vSync); EN_SUCCESS("Created swapchain!") @@ -777,37 +829,45 @@ namespace en EN_SUCCESS("Created per frame data!") - CreateShadowPasses(); + m_FullscreenSampler = MakeHandle(VK_FILTER_LINEAR); - EN_SUCCESS("Created shadow passes!") + EN_SUCCESS("Created fullscreen sampler!") CreateShadowResources(); EN_SUCCESS("Created shadow resources!") - CreateShadowPipelines(); + CreateShadowPasses(); - EN_SUCCESS("Created shadow pipelines!") + EN_SUCCESS("Created shadow passes!") CreateDepthBuffer(); EN_SUCCESS("Created depth buffer!") - CreateDepthPass(); + CreateAATarget(); - EN_SUCCESS("Created the depth pass!") + EN_SUCCESS("Created the antialiasing target!") + + CreateSSAOTarget(); - CreateDepthPipeline(); + EN_SUCCESS("Created the SSAO target!") - EN_SUCCESS("Created the depth pipeline!") + CreateDepthPass(); + + EN_SUCCESS("Created the depth pass!") CreateClusterBuffers(); EN_SUCCESS("Created the cluster buffers!") - CreateClusterPipelines(); + CreateClusterPasses(); - EN_SUCCESS("Created the cluster pipelines!") + EN_SUCCESS("Created the cluster pass!") + + CreateSSAOPass(); + + EN_SUCCESS("Created the SSAO pass!") CreateForwardPass(); @@ -817,21 +877,13 @@ namespace en EN_SUCCESS("Created the antialiasing pass!") - CreateForwardPipeline(); - - EN_SUCCESS("Created the forward pipeline!") - - CreateAntialiasingPipeline(); - - EN_SUCCESS("Created the antialiasing pipeline!") + if (newImGui) + m_ImGuiContext = MakeHandle(m_Swapchain->GetFormat(), m_Swapchain->GetExtent(), m_Swapchain->m_ImageViews); + else + m_ImGuiContext->UpdateFramebuffers(m_Swapchain->GetExtent(), m_Swapchain->m_ImageViews); - if (newImGui) - CreateImGuiContext(); - else - CreateImGuiRenderPass(); - EN_SUCCESS("Created the ImGui context!") - + EN_SUCCESS("Created the renderer Vulkan backend"); vkDeviceWaitIdle(g_Ctx->m_LogicalDevice); @@ -851,79 +903,26 @@ namespace en VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ); - m_SpotShadowDepthBuffer = MakeHandle( - VkExtent2D{ - m_Settings.spotLightShadowResolution, - m_Settings.spotLightShadowResolution - }, - m_Settings.shadowsFormat, - VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, - VK_IMAGE_ASPECT_DEPTH_BIT, - 0U, - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL - ); - m_ShadowSampler = MakeHandle(VK_FILTER_LINEAR); for (auto& shadowMap : m_PointShadowMaps) { - Handle shadowMapImage = MakeHandle( + shadowMap = MakeHandle( VkExtent2D{ m_Settings.pointLightShadowResolution, m_Settings.pointLightShadowResolution }, - VK_FORMAT_R32_SFLOAT, + m_Settings.shadowsFormat == VK_FORMAT_D32_SFLOAT ? VK_FORMAT_R32_SFLOAT : VK_FORMAT_R16_SFLOAT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 6U - ); - - std::array, 6> framebuffers{}; - - for (uint32_t i = 0U; i < 6; i++) - { - framebuffers[i] = MakeHandle( - m_PerspectiveShadowsRenderPass, - VkExtent2D{ - m_Settings.pointLightShadowResolution, - m_Settings.pointLightShadowResolution - }, - RenderPassAttachment{ - .imageView = shadowMapImage->GetLayerViewHandle(i), - - .format = VK_FORMAT_R32_SFLOAT, - - .initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - .refLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - .finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - }, - RenderPassAttachment{ - .imageView = m_PointShadowDepthBuffer->GetViewHandle(), - - .format = m_Settings.shadowsFormat, - - .initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, - .refLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, - .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, - - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - }); - } - - shadowMap = OmniShadowMap{ - shadowMapImage, - framebuffers - }; + ); } for (auto& shadowMap : m_SpotShadowMaps) { - Handle shadowMapImage = MakeHandle( + shadowMap = MakeHandle( VkExtent2D{ m_Settings.spotLightShadowResolution, m_Settings.spotLightShadowResolution @@ -933,35 +932,11 @@ namespace en VK_IMAGE_ASPECT_DEPTH_BIT, 0U, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL - ); - - shadowMap = ShadowMap{ - .image = shadowMapImage, - .framebuffer = MakeHandle( - m_DirShadowsRenderPass, - VkExtent2D { - m_Settings.spotLightShadowResolution, - m_Settings.spotLightShadowResolution - }, - RenderPassAttachment{}, - RenderPassAttachment{ - .imageView = shadowMapImage->GetViewHandle(), - - .format = m_Settings.shadowsFormat, - - .initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, - .refLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, - .finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - } - ) - }; + ); } for (auto& shadowMap : m_DirShadowMaps) { - Handle shadowMapImage = MakeHandle( + shadowMap = MakeHandle( VkExtent2D{ m_Settings.dirLightShadowResolution, m_Settings.dirLightShadowResolution @@ -972,30 +947,6 @@ namespace en 0U, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ); - - shadowMap = ShadowMap{ - .image = shadowMapImage, - .framebuffer = MakeHandle( - m_DirShadowsRenderPass, - VkExtent2D { - m_Settings.dirLightShadowResolution, - m_Settings.dirLightShadowResolution - }, - RenderPassAttachment{}, - RenderPassAttachment{ - .imageView = shadowMapImage->GetViewHandle(), - - .format = m_Settings.shadowsFormat, - - .initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, - .refLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, - .finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - } - ) - }; } std::vector pointShadowMaps{}; @@ -1005,23 +956,23 @@ namespace en for (const auto& shadowMap : m_PointShadowMaps) { pointShadowMaps.emplace_back(DescriptorInfo::ImageInfoContent{ - .imageView = shadowMap.image->GetViewHandle(), + .imageView = shadowMap->GetViewHandle(), .imageSampler = m_ShadowSampler->GetHandle() - }); + }); } for (const auto& shadowMap : m_SpotShadowMaps) { spotShadowMaps.emplace_back(DescriptorInfo::ImageInfoContent{ - .imageView = shadowMap.image->GetViewHandle(), + .imageView = shadowMap->GetViewHandle(), .imageSampler = m_ShadowSampler->GetHandle() - }); + }); } for (const auto& shadowMap : m_DirShadowMaps) { dirShadowMaps.emplace_back(DescriptorInfo::ImageInfoContent{ - .imageView = shadowMap.image->GetViewHandle(), + .imageView = shadowMap->GetViewHandle(), .imageSampler = m_ShadowSampler->GetHandle() - }); + }); } m_ShadowMapsDescriptor = MakeHandle(DescriptorInfo{ @@ -1062,34 +1013,6 @@ namespace en } void Renderer::CreateShadowPasses() - { - RenderPassAttachment colorAttachment{ - .format = VK_FORMAT_R32_SFLOAT, - - .refLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - .finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - }; - - RenderPassAttachment depthAttachment{ - .format = m_Settings.shadowsFormat, - - .refLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, - .finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - }; - - m_DirShadowsRenderPass = MakeHandle(RenderPassAttachment{}, depthAttachment); - - depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - m_PerspectiveShadowsRenderPass = MakeHandle(colorAttachment, depthAttachment); - } - void Renderer::CreateShadowPipelines() { constexpr VkPushConstantRange model_lightIndex_cascadeIndex{ .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, @@ -1097,15 +1020,15 @@ namespace en .size = sizeof(uint32_t) * 3, }; - GraphicsPipeline::CreateInfo dirInfo{ - .renderPass = m_DirShadowsRenderPass, - + GraphicsPass::CreateInfo dirInfo{ .vShader = "Shaders/DirShadowVert.spv", //.fShader = "Shaders/ShadowFrag.spv", .descriptorLayouts {Scene::GetLightingDescriptorLayout(), CameraBuffer::GetLayout()}, .pushConstantRanges {model_lightIndex_cascadeIndex}, + .depthFormat = m_Settings.shadowsFormat, + .useVertexBindings = true, .enableDepthTest = true, .enableDepthWrite = true, @@ -1115,7 +1038,7 @@ namespace en .polygonMode = VK_POLYGON_MODE_FILL, }; - m_DirShadowPipeline = MakeHandle(dirInfo); + m_DirShadowPass = MakeHandle(dirInfo); constexpr VkPushConstantRange model_lightIndex{ .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, @@ -1123,15 +1046,15 @@ namespace en .size = sizeof(uint32_t) * 2, }; - GraphicsPipeline::CreateInfo spotInfo{ - .renderPass = m_DirShadowsRenderPass, - + GraphicsPass::CreateInfo spotInfo{ .vShader = "Shaders/SpotShadowVert.spv", //.fShader = "Shaders/ShadowFrag.spv", .descriptorLayouts {Scene::GetLightingDescriptorLayout()}, .pushConstantRanges {model_lightIndex}, + .depthFormat = m_Settings.shadowsFormat, + .useVertexBindings = true, .enableDepthTest = true, .enableDepthWrite = true, @@ -1141,7 +1064,7 @@ namespace en .polygonMode = VK_POLYGON_MODE_FILL, }; - m_SpotShadowPipeline = MakeHandle(spotInfo); + m_SpotShadowPass = MakeHandle(spotInfo); constexpr VkPushConstantRange model_lightIndex_shadowmapIndex{ .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, @@ -1149,15 +1072,16 @@ namespace en .size = sizeof(uint32_t) * 3, }; - GraphicsPipeline::CreateInfo pointInfo{ - .renderPass = m_PerspectiveShadowsRenderPass, - + GraphicsPass::CreateInfo pointInfo{ .vShader = "Shaders/PointShadowVert.spv", .fShader = "Shaders/ShadowFrag.spv", .descriptorLayouts {Scene::GetLightingDescriptorLayout()}, .pushConstantRanges {model_lightIndex_shadowmapIndex}, + .colorFormat = m_Settings.shadowsFormat == VK_FORMAT_D32_SFLOAT ? VK_FORMAT_R32_SFLOAT : VK_FORMAT_R16_SFLOAT, + .depthFormat = m_Settings.shadowsFormat, + .useVertexBindings = true, .enableDepthTest = true, .enableDepthWrite = true, @@ -1167,40 +1091,29 @@ namespace en .polygonMode = VK_POLYGON_MODE_FILL, }; - m_PointShadowPipeline = MakeHandle(pointInfo); + m_PointShadowPass = MakeHandle(pointInfo); } - - void Renderer::CreateDepthPass() + void Renderer::CreateSSAOPass() { - std::vector dependencies{ - VkSubpassDependency{ - .srcSubpass = VK_SUBPASS_EXTERNAL, - .dstSubpass = 0U, - .srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, - .dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, - .srcAccessMask = 0U, - .dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - } + constexpr VkPushConstantRange ssao{ + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + .offset = 0U, + .size = sizeof(m_Settings.ssao), }; - RenderPassAttachment depthAttachment{ - .imageView = m_DepthBuffer->GetViewHandle(), - - .format = m_DepthBuffer->m_Format, + GraphicsPass::CreateInfo info{ + .vShader = "Shaders/FullscreenTri.spv", + .fShader = "Shaders/SSAO.spv", - .initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, - .refLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, - .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + .descriptorLayouts {CameraBuffer::GetLayout(), m_DepthBufferDescriptor->GetLayout()}, + .pushConstantRanges {ssao}, - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .colorFormat = m_SSAOTarget->m_Format, }; - m_DepthRenderPass = MakeHandle(RenderPassAttachment{}, depthAttachment, dependencies); - - m_DepthFramebuffer = MakeHandle(m_DepthRenderPass, m_Swapchain->GetExtent(), RenderPassAttachment{}, depthAttachment); + m_SSAOPass = MakeHandle(info); } - void Renderer::CreateDepthPipeline() + void Renderer::CreateDepthPass() { constexpr VkPushConstantRange model{ .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, @@ -1208,14 +1121,14 @@ namespace en .size = sizeof(uint32_t), }; - GraphicsPipeline::CreateInfo info{ - .renderPass = m_DepthRenderPass, - + GraphicsPass::CreateInfo info{ .vShader = "Shaders/Depth.spv", .descriptorLayouts {CameraBuffer::GetLayout(), Scene::GetGlobalDescriptorLayout()}, .pushConstantRanges {model}, + .depthFormat = m_DepthBuffer->m_Format, + .useVertexBindings = true, .enableDepthTest = true, .enableDepthWrite = true, @@ -1225,118 +1138,9 @@ namespace en .polygonMode = VK_POLYGON_MODE_FILL, }; - m_DepthPipeline = MakeHandle(info); + m_DepthPass = MakeHandle(info); } - void Renderer::CreateForwardPass() - { - std::vector dependencies{}; - - if (m_Settings.depthPrePass) - { - dependencies.emplace_back(VkSubpassDependency{ - .srcSubpass = VK_SUBPASS_EXTERNAL, - .dstSubpass = 0U, - .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, - .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - .srcAccessMask = 0U, - .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - }); - } - else - { - dependencies.emplace_back(VkSubpassDependency{ - .srcSubpass = VK_SUBPASS_EXTERNAL, - .dstSubpass = 0U, - .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, - .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, - .srcAccessMask = 0U, - .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - }); - } - - m_AliasedImage = MakeHandle( - m_Swapchain->GetExtent(), - m_Swapchain->GetFormat(), - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL - ); - - m_FullscreenSampler = MakeHandle(VK_FILTER_NEAREST); - - m_AntialiasingDescriptor = MakeHandle(DescriptorInfo{ - std::vector{ - DescriptorInfo::ImageInfo { - .contents {{ - .imageView = m_AliasedImage->GetViewHandle(), - .imageSampler = m_FullscreenSampler->GetHandle() - }} - } - }, - std::vector{}, - }); - - std::vector attachments{}; - if (m_Settings.antialiasingMode == AntialiasingMode::None) - { - for (int i = 0; i < m_Swapchain->m_ImageViews.size(); i++) - { - attachments.emplace_back(RenderPassAttachment{ - .imageView = m_Swapchain->m_ImageViews[i], - - .format = m_Swapchain->GetFormat(), - - .initialLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - .refLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - }); - } - } - else - { - attachments.emplace_back(RenderPassAttachment{ - .imageView = m_AliasedImage->GetViewHandle(), - - .format = m_AliasedImage->m_Format, - - .initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - .refLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - .finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - }); - } - - RenderPassAttachment depthAttachment{ - .imageView = m_DepthBuffer->GetViewHandle(), - - .format = m_DepthBuffer->m_Format, - - .initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, - .refLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, - .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, - - .loadOp = m_Settings.depthPrePass ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - }; - - m_ForwardPass = MakeHandle(attachments[0], depthAttachment, dependencies); - - m_ForwardFramebuffers.resize(m_Swapchain->m_ImageViews.size()); - if (m_Settings.antialiasingMode == AntialiasingMode::None) - for (uint32_t i = 0U; i < m_Swapchain->m_ImageViews.size(); i++) - m_ForwardFramebuffers[i] = MakeHandle(m_ForwardPass, m_Swapchain->GetExtent(), attachments[i], depthAttachment); - else - for (uint32_t i = 0U; i < m_Swapchain->m_ImageViews.size(); i++) - m_ForwardFramebuffers[i] = MakeHandle(m_ForwardPass, m_Swapchain->GetExtent(), attachments[0], depthAttachment); - } - void Renderer::CreateForwardPipeline() { m_ClusterDescriptor = MakeHandle(DescriptorInfo{ std::vector{}, @@ -1383,9 +1187,7 @@ namespace en .size = sizeof(uint32_t)+sizeof(float), }; - GraphicsPipeline::CreateInfo info{ - .renderPass = m_ForwardPass, - + GraphicsPass::CreateInfo info{ .vShader = "Shaders/ForwardVert.spv", .fShader = "Shaders/ForwardFrag.spv", @@ -1393,10 +1195,14 @@ namespace en CameraBuffer::GetLayout(), Scene::GetGlobalDescriptorLayout(), m_ShadowMapsDescriptor->GetLayout(), - m_ClusterDescriptor->GetLayout() + m_ClusterDescriptor->GetLayout(), + m_SSAODescriptor->GetLayout(), }, .pushConstantRanges {model, material_postprocessing}, + .colorFormat = m_Settings.antialiasingMode == AntialiasingMode::None ? m_Swapchain->GetFormat() : m_AliasedImage->m_Format, + .depthFormat = m_DepthBuffer->m_Format, + .useVertexBindings = true, .enableDepthTest = true, .enableDepthWrite = !m_Settings.depthPrePass, @@ -1406,44 +1212,9 @@ namespace en .polygonMode = VK_POLYGON_MODE_FILL, }; - m_Pipeline = MakeHandle(info); + m_ForwardPass = MakeHandle(info); } - void Renderer::CreateAntialiasingPass() - { - std::vector dependencies{ VkSubpassDependency{ - .srcSubpass = VK_SUBPASS_EXTERNAL, - .dstSubpass = 0U, - .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - .srcAccessMask = 0U, - .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - }}; - - std::vector attachments{}; - for (int i = 0; i < m_Swapchain->m_ImageViews.size(); i++) - { - attachments.emplace_back(RenderPassAttachment{ - .imageView = m_Swapchain->m_ImageViews[i], - - .format = m_Swapchain->GetFormat(), - - .initialLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - .refLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - }); - } - - m_AntialiasingPass = MakeHandle(attachments[0], RenderPassAttachment{}, dependencies); - - m_AntialiasingFramebuffers.resize(m_Swapchain->m_ImageViews.size()); - for (uint32_t i = 0U; i < m_Swapchain->m_ImageViews.size(); i++) - m_AntialiasingFramebuffers[i] = MakeHandle(m_AntialiasingPass, m_Swapchain->GetExtent(), attachments[i]); - } - void Renderer::CreateAntialiasingPipeline() { constexpr VkPushConstantRange antialiasing{ .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, @@ -1451,17 +1222,17 @@ namespace en .size = sizeof(AntialiasingProperties), }; - GraphicsPipeline::CreateInfo info{ - .renderPass = m_AntialiasingPass, - + GraphicsPass::CreateInfo info{ .vShader = "Shaders/FullscreenTri.spv", .fShader = "Shaders/FXAA.spv", .descriptorLayouts {m_AntialiasingDescriptor->GetLayout()}, .pushConstantRanges {antialiasing}, + + .colorFormat = m_Swapchain->GetFormat(), }; - m_AntialiasingPipeline = MakeHandle(info); + m_AntialiasingPass = MakeHandle(info); } void Renderer::CreateClusterBuffers() @@ -1510,7 +1281,7 @@ namespace en VMA_MEMORY_USAGE_GPU_ONLY ); } - void Renderer::CreateClusterPipelines() + void Renderer::CreateClusterPasses() { DescriptorInfo::BufferInfo aabbBufferInfo{ .index = 0U, @@ -1594,15 +1365,15 @@ namespace en .size = sizeof(glm::uvec4) }; - ComputePipeline::CreateInfo aabbInfo{ + ComputePass::CreateInfo aabbInfo{ .sourcePath = "Shaders/ClusterAABB.spv", .descriptorLayouts = { m_ClusterSSBOs.aabbClustersDescriptor->GetLayout(), CameraBuffer::GetLayout() }, .pushConstantRanges = { stvPushConstant }, }; - m_ClusterAABBCreation = MakeHandle(aabbInfo); + m_ClusterAABBCreationPass = MakeHandle(aabbInfo); - ComputePipeline::CreateInfo lightCullInfo{ + ComputePass::CreateInfo lightCullInfo{ .sourcePath = "Shaders/ClusterLightCulling.spv", .descriptorLayouts = { m_ClusterSSBOs.clusterLightCullingDescriptor->GetLayout(), @@ -1611,19 +1382,95 @@ namespace en }, }; - m_ClusterLightCulling = MakeHandle(lightCullInfo); + m_ClusterLightCullingPass = MakeHandle(lightCullInfo); } + void Renderer::CreateAATarget() + { + m_AliasedImage = MakeHandle( + m_Swapchain->GetExtent(), + m_Swapchain->GetFormat(), + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + VK_IMAGE_ASPECT_COLOR_BIT, + 0U, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL + ); + + DescriptorInfo info { + std::vector{ + DescriptorInfo::ImageInfo { + .contents {{ + .imageView = m_AliasedImage->GetViewHandle(), + .imageSampler = m_FullscreenSampler->GetHandle() + }} + } + }, + std::vector{}, + }; + + if (m_AntialiasingDescriptor) + m_AntialiasingDescriptor->Update(info); + else + m_AntialiasingDescriptor = MakeHandle(info); + } + void Renderer::CreateSSAOTarget() + { + m_SSAOTarget = MakeHandle( + VkExtent2D{ + m_Swapchain->GetExtent().width / 2U, + m_Swapchain->GetExtent().height / 2U + }, + VK_FORMAT_R8_UNORM, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + VK_IMAGE_ASPECT_COLOR_BIT, + 0U, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL + ); + + DescriptorInfo info{ + std::vector{ + DescriptorInfo::ImageInfo { + .contents {{ + .imageView = m_SSAOTarget->GetViewHandle(), + .imageSampler = m_FullscreenSampler->GetHandle() + }}, + } + }, + std::vector{}, + }; + + if (m_SSAODescriptor) + m_SSAODescriptor->Update(info); + else + m_SSAODescriptor = MakeHandle(info); + } void Renderer::CreateDepthBuffer() { m_DepthBuffer = MakeHandle( m_Swapchain->GetExtent(), VK_FORMAT_D32_SFLOAT, - VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_IMAGE_ASPECT_DEPTH_BIT, 0U, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL ); + + DescriptorInfo info{ + std::vector{ + DescriptorInfo::ImageInfo { + .contents {{ + .imageView = m_DepthBuffer->GetViewHandle(), + .imageSampler = m_FullscreenSampler->GetHandle() + }} + } + }, + std::vector{}, + }; + + if (m_DepthBufferDescriptor) + m_DepthBufferDescriptor->Update(info); + else + m_DepthBufferDescriptor = MakeHandle(info); } void Renderer::CreatePerFrameData() @@ -1643,10 +1490,10 @@ namespace en EN_ERROR("Renderer::CreatePerFrameData() - Failed to create a submit fence!"); if (vkCreateSemaphore(g_Ctx->m_LogicalDevice, &semaphoreInfo, nullptr, &frame.mainSemaphore) != VK_SUCCESS) - EN_ERROR("GraphicsPipeline::CreatePerFrameData - Failed to create a main semaphore!"); + EN_ERROR("GraphicsPass::CreatePerFrameData - Failed to create a main semaphore!"); if (vkCreateSemaphore(g_Ctx->m_LogicalDevice, &semaphoreInfo, nullptr, &frame.presentSemaphore) != VK_SUCCESS) - EN_ERROR("GraphicsPipeline::CreatePerFrameData - Failed to create a present semaphore!"); + EN_ERROR("GraphicsPass::CreatePerFrameData - Failed to create a present semaphore!"); VkCommandBufferAllocateInfo allocInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, @@ -1670,92 +1517,4 @@ namespace en vkFreeCommandBuffers(g_Ctx->m_LogicalDevice, g_Ctx->m_GraphicsCommandPool, 1U, &frame.commandBuffer); } } - - void ImGuiCheckResult(VkResult err) - { - if (err == 0) return; - - EN_ERROR("ImGui Error:" + err); - } - - void Renderer::CreateImGuiRenderPass() - { - std::vector dependencies{ - VkSubpassDependency { - .srcSubpass = VK_SUBPASS_EXTERNAL, - .dstSubpass = 0U, - .srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - .srcAccessMask = VK_ACCESS_SHADER_READ_BIT, - .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT - } - }; - - std::vector attachments{}; - for (int i = 0; i < m_Swapchain->m_ImageViews.size(); i++) - { - attachments.emplace_back(RenderPassAttachment{ - .imageView = m_Swapchain->m_ImageViews[i], - - .format = m_Swapchain->GetFormat(), - - .initialLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - .refLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - - .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - - .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, - .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, - }); - } - - m_ImGuiRenderPass = MakeHandle(attachments[0], RenderPassAttachment{}, dependencies); - - m_ImGuiFramebuffers.resize(m_Swapchain->m_ImageViews.size()); - for (uint32_t i = 0U; i < m_Swapchain->m_ImageViews.size(); i++) - m_ImGuiFramebuffers[i] = MakeHandle(m_ImGuiRenderPass, m_Swapchain->GetExtent(), attachments[i]); - } - void Renderer::CreateImGuiContext() - { - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - ImGui::StyleColorsDark(); - - CreateImGuiRenderPass(); - - ImGui_ImplGlfw_InitForVulkan(Window::Get().m_NativeHandle, true); - - ImGui_ImplVulkan_InitInfo initInfo{ - .Instance = g_Ctx->m_Instance, - .PhysicalDevice = g_Ctx->m_PhysicalDevice, - .Device = g_Ctx->m_LogicalDevice, - .QueueFamily = g_Ctx->m_QueueFamilies.graphics.value(), - .Queue = g_Ctx->m_GraphicsQueue, - .DescriptorPool = g_Ctx->m_DescriptorAllocator->GetPool(), - .MinImageCount = static_cast(m_Swapchain->m_ImageViews.size()), - .ImageCount = static_cast(m_Swapchain->m_ImageViews.size()), - .CheckVkResultFn = ImGuiCheckResult - }; - - ImGui_ImplVulkan_Init(&initInfo, m_ImGuiRenderPass->GetHandle()); - - VkCommandBuffer cmd = Helpers::BeginSingleTimeGraphicsCommands(); - ImGui_ImplVulkan_CreateFontsTexture(cmd); - Helpers::EndSingleTimeGraphicsCommands(cmd); - - VkFormat format = m_Swapchain->GetFormat(); - - ImGui_ImplVulkanH_SelectSurfaceFormat(g_Ctx->m_PhysicalDevice, g_Ctx->m_WindowSurface, &format, 1U, VK_COLORSPACE_SRGB_NONLINEAR_KHR); - ImGui_ImplVulkan_SetMinImageCount(m_Swapchain->m_ImageViews.size()); - } - void Renderer::DestroyImGuiContext() - { - ImGui_ImplVulkan_DestroyFontUploadObjects(); - ImGui_ImplVulkan_Shutdown(); - ImGui_ImplGlfw_Shutdown(); - - m_ImGuiRenderPass.reset(); - } } \ No newline at end of file diff --git a/Source/Renderer/Renderer.hpp b/Source/Renderer/Renderer.hpp index f1a0daf..6f0ee20 100644 --- a/Source/Renderer/Renderer.hpp +++ b/Source/Renderer/Renderer.hpp @@ -5,22 +5,16 @@ #include "../../EruptionEngine.ini" -#include -#include -#include - #include #include #include #include -#include -#include -#include +#include +#include +#include -#include -#include #include #include @@ -32,6 +26,8 @@ #include +#include + #include #include @@ -65,6 +61,22 @@ namespace en float texelSizeY = 1.0f / 1080.0f; }; + enum struct SSAOMode + { + None = 0, + SSAO = 1 + }; + + struct SSAOProperties + { + float screenWidth = 1920.0f; + float screenHeight = 1080.0f; + + float radius = 0.5f; + float bias = 0.025f; + float multiplier = 1.0f; + }; + void SetVSyncEnabled(const bool enabled); const bool GetVSyncEnabled() const { return m_Settings.vSync; }; @@ -92,7 +104,11 @@ namespace en void SetAntialiasingMode(const AntialiasingMode antialiasingMode); const AntialiasingMode GetAntialiasingMode() const { return m_Settings.antialiasingMode; }; + void SetSSAOMode(const SSAOMode ssaoMode); + const SSAOMode GetSSAOMode() const { return m_Settings.ssaoMode; }; + AntialiasingProperties& GetAntialiasingProperties() { return m_Settings.antialiasing; }; + SSAOProperties& GetSSAOProperties() { return m_Settings.ssao; }; void ReloadBackend(); @@ -114,15 +130,17 @@ namespace en Handle m_Swapchain; Handle m_CameraBuffer; - Handle m_ImGuiRenderPass; - Handle m_ForwardPass; - Handle m_AntialiasingPass; - Handle m_DepthRenderPass; + Handle m_SSAOPass; + Handle m_ForwardPass; + Handle m_AntialiasingPass; + Handle m_DepthPass; - Handle m_DirShadowsRenderPass; - Handle m_PerspectiveShadowsRenderPass; + Handle m_PointShadowPass; + Handle m_SpotShadowPass; + Handle m_DirShadowPass; - Handle m_DepthFramebuffer; + Handle m_ClusterAABBCreationPass; + Handle m_ClusterLightCullingPass; Handle m_ShadowSampler; Handle m_FullscreenSampler; @@ -130,31 +148,21 @@ namespace en Handle m_ShadowMapsDescriptor; Handle m_ClusterDescriptor; Handle m_AntialiasingDescriptor; + Handle m_SSAODescriptor; + Handle m_DepthBufferDescriptor; - std::vector> m_ImGuiFramebuffers; - std::vector> m_ForwardFramebuffers; - std::vector> m_AntialiasingFramebuffers; - - std::array m_PointShadowMaps; - std::array m_SpotShadowMaps; - std::array m_DirShadowMaps; - - Handle m_Pipeline; - Handle m_AntialiasingPipeline; - Handle m_DepthPipeline; - - Handle m_DirShadowPipeline; - Handle m_SpotShadowPipeline; - Handle m_PointShadowPipeline; - - Handle m_ClusterAABBCreation; - Handle m_ClusterLightCulling; + std::array, MAX_POINT_LIGHT_SHADOWS> m_PointShadowMaps; + std::array, MAX_SPOT_LIGHT_SHADOWS> m_SpotShadowMaps; + std::array, MAX_DIR_LIGHT_SHADOWS* SHADOW_CASCADES> m_DirShadowMaps; Handle m_DepthBuffer; + Handle m_SSAOTarget; Handle m_PointShadowDepthBuffer; Handle m_SpotShadowDepthBuffer; Handle m_AliasedImage; + Handle m_ImGuiContext; + struct Settings { bool vSync = true; @@ -170,7 +178,10 @@ namespace en VkFormat shadowsFormat = VK_FORMAT_D32_SFLOAT; AntialiasingMode antialiasingMode = AntialiasingMode::FXAA; - AntialiasingProperties antialiasing; + AntialiasingProperties antialiasing{}; + + SSAOMode ssaoMode = SSAOMode::SSAO; + SSAOProperties ssao{}; } m_Settings; struct CSM { @@ -225,6 +236,7 @@ namespace en void ShadowPass(); void ClusterComputePass(); void DepthPass(); + void SSAOPass(); void ForwardPass(); void AntialiasingPass(); void ImGuiPass(); @@ -235,19 +247,13 @@ namespace en void CreateShadowResources(); void CreateShadowPasses(); - void CreateShadowPipelines(); - + void CreateSSAOPass(); void CreateDepthPass(); - void CreateDepthPipeline(); - void CreateForwardPass(); - void CreateForwardPipeline(); - void CreateAntialiasingPass(); - void CreateAntialiasingPipeline(); void CreateClusterBuffers(); - void CreateClusterPipelines(); + void CreateClusterPasses(); static void FramebufferResizeCallback(GLFWwindow* window, int width, int height); void RecreateFramebuffer(); @@ -255,14 +261,12 @@ namespace en void CreateBackend(bool newImGui = true); + void CreateAATarget(); + void CreateSSAOTarget(); void CreateDepthBuffer(); void CreatePerFrameData(); void DestroyPerFrameData(); - - void CreateImGuiRenderPass(); - void CreateImGuiContext(); - void DestroyImGuiContext(); }; } diff --git a/Source/Renderer/Swapchain.cpp b/Source/Renderer/Swapchain.cpp index 5b878cb..1bb5131 100644 --- a/Source/Renderer/Swapchain.cpp +++ b/Source/Renderer/Swapchain.cpp @@ -69,8 +69,8 @@ namespace en ChangeLayout( i, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - 0U, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + 0U, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT ); diff --git a/Source/Renderer/Swapchain.hpp b/Source/Renderer/Swapchain.hpp index 3e0581d..c9be404 100644 --- a/Source/Renderer/Swapchain.hpp +++ b/Source/Renderer/Swapchain.hpp @@ -22,8 +22,9 @@ namespace en void ChangeLayout(uint32_t index, VkImageLayout newLayout, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage, VkCommandBuffer cmd = VK_NULL_HANDLE); - const VkFormat const GetFormat() { return m_ImageFormat; }; - const VkExtent2D const GetExtent() { return m_Extent; }; + const VkImageLayout const GetLayout(uint32_t i) { return m_CurrentLayouts[i]; }; + const VkFormat const GetFormat() { return m_ImageFormat; }; + const VkExtent2D const GetExtent() { return m_Extent; }; private: std::vector m_CurrentLayouts; diff --git a/Source/Renderer/Window.hpp b/Source/Renderer/Window.hpp index 4220b92..071eef6 100644 --- a/Source/Renderer/Window.hpp +++ b/Source/Renderer/Window.hpp @@ -16,10 +16,6 @@ namespace en { class Window { - friend class Context; - friend class Renderer; - friend class InputManager; - public: Window( const std::string& title = "Unnamed Default Window", diff --git a/Source/Scene/Scene.cpp b/Source/Scene/Scene.cpp index 25e5329..5d1c628 100644 --- a/Source/Scene/Scene.cpp +++ b/Source/Scene/Scene.cpp @@ -80,10 +80,10 @@ namespace en std::vector imageViews(MAX_TEXTURES); for (auto& view : imageViews) { - view.imageView = AssetManager::Get().GetTexture("SkullAlbedo")->m_Image->GetViewHandle();//AssetManager::Get().GetWhiteSRGBTexture()->m_Image->GetViewHandle(); - view.imageSampler = AssetManager::Get().GetTexture("SkullAlbedo")->m_Sampler->GetHandle();//AssetManager::Get().GetWhiteSRGBTexture()->m_ImageSampler; + view.imageView = AssetManager::Get().GetWhiteSRGBTexture()->m_Image->GetViewHandle(); + view.imageSampler = AssetManager::Get().GetWhiteSRGBTexture()->m_Sampler->GetHandle(); } - + m_LightsBufferDescriptorSet = MakeHandle(DescriptorInfo{ std::vector{}, std::vector{ @@ -777,11 +777,15 @@ namespace en uint32_t totalPointLights = MAX_POINT_LIGHTS; uint32_t changedPointLights = changedPointLightsIDs.size(); + bool updated = false; + if ((float)changedPointLights / totalPointLights > POINT_LIGHTS_UPDATE_THRESHOLD) { EN_LOG("TOTAL POINT LIGHTS UPDATE"); m_LightsStagingBuffer->MapMemory(m_GPULights.pointLights.data(), sizeof(m_GPULights.pointLights)); m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sizeof(m_GPULights.pointLights), 0U, (size_t)&m_GPULights.pointLights - (size_t)&m_GPULights); + + updated = true; } else { @@ -790,6 +794,8 @@ namespace en { m_LightsStagingBuffer->MapMemory(&m_GPULights.pointLights[changedId], sizeof(PointLight::Buffer)); m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sizeof(PointLight::Buffer), 0U, (size_t)&m_GPULights.pointLights[changedId] - (size_t)&m_GPULights); + + updated = true; } } @@ -801,6 +807,8 @@ namespace en EN_LOG("TOTAL SPOT LIGHTS UPDATE"); m_LightsStagingBuffer->MapMemory(m_GPULights.spotLights.data(), sizeof(m_GPULights.spotLights)); m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sizeof(m_GPULights.spotLights), 0U, (size_t)&m_GPULights.spotLights - (size_t)&m_GPULights); + + updated = true; } else { @@ -808,6 +816,8 @@ namespace en { m_LightsStagingBuffer->MapMemory(&m_GPULights.spotLights[changedId], sizeof(SpotLight::Buffer)); m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sizeof(SpotLight::Buffer), 0U, (size_t)&m_GPULights.spotLights[changedId] - (size_t)&m_GPULights); + + updated = true; } } @@ -819,6 +829,8 @@ namespace en EN_LOG("TOTAL DIR LIGHTS UPDATE"); m_LightsStagingBuffer->MapMemory(m_GPULights.dirLights.data(), sizeof(m_GPULights.dirLights)); m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sizeof(m_GPULights.dirLights), 0U, (size_t)&m_GPULights.dirLights - (size_t)&m_GPULights); + + updated = true; } else { @@ -826,6 +838,8 @@ namespace en { m_LightsStagingBuffer->MapMemory(&m_GPULights.dirLights[changedId], sizeof(DirectionalLight::Buffer)); m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sizeof(DirectionalLight::Buffer), 0U, (size_t)&m_GPULights.dirLights[changedId] - (size_t)&m_GPULights); + + updated = true; } } @@ -842,11 +856,22 @@ namespace en m_LightsStagingBuffer->MapMemory(&m_GPULights.activePointLights, sceneLightingSize); m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sceneLightingSize, 0U, allLightsSize); + + updated = true; } - //EN_LOG("TOTAL LIGHTS UPDATE"); - //m_LightsStagingBuffer->MapMemory(&m_GPULights, sizeof(GPULights)); - //m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sizeof(GPULights)); + if (updated) + { + //VkCommandBuffer cmd = Helpers::BeginSingleTimeGraphicsCommands() + // // FIX + //m_LightsBuffer->PipelineBarrier( + // VK_ACCESS_MEMORY_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, + // VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + // cmd + //) + // + //Helpers::BeginSingleTimeGraphicsCommands(cmd) + } } void Scene::UpdateGlobalDescriptor() { diff --git a/Source/Scene/Scene.hpp b/Source/Scene/Scene.hpp index 627f4dd..3762a49 100644 --- a/Source/Scene/Scene.hpp +++ b/Source/Scene/Scene.hpp @@ -6,8 +6,7 @@ #include #include -#include -#include +#include #include #include @@ -19,17 +18,6 @@ namespace en { - struct OmniShadowMap - { - Handle image; - std::array, 6> framebuffers; - }; - struct ShadowMap - { - Handle image; - Handle framebuffer; - }; - class Scene { friend class Renderer; diff --git a/imgui.ini b/imgui.ini index 9ecd605..6981ba7 100644 --- a/imgui.ini +++ b/imgui.ini @@ -26,8 +26,8 @@ Size=405,428 Collapsed=0 [Window][Debug Menu] -Pos=1317,193 -Size=486,357 +Pos=1378,273 +Size=301,150 Collapsed=0 [Window][Scene Hierarchy] @@ -58,13 +58,13 @@ Size=409,172 Collapsed=0 [Window][Settings] -Pos=1339,574 +Pos=1271,523 Size=500,407 Collapsed=0 [Docking][Data] DockSpace ID=0x1315676B Window=0x4647B76E Pos=0,33 Size=1920,1047 Split=X - DockNode ID=0x00000005 Parent=0x1315676B SizeRef=833,1047 Split=X + DockNode ID=0x00000005 Parent=0x1315676B SizeRef=1673,1047 Split=X DockNode ID=0x00000001 Parent=0x00000005 SizeRef=272,1047 Split=Y Selected=0x2E9237F7 DockNode ID=0x00000003 Parent=0x00000001 SizeRef=499,496 Selected=0x2E9237F7 DockNode ID=0x00000004 Parent=0x00000001 SizeRef=499,261 Selected=0xBC4BD31B From 7cbae5130db8901fb147f08063e81cce794f12f0 Mon Sep 17 00:00:00 2001 From: Mateusz Antkiewicz Date: Sat, 24 Jun 2023 20:14:18 +0200 Subject: [PATCH 2/3] Improved SSAO, Fixed sync errors --- EruptionEngine.ini | 1 - Shaders/ClusterAABB.spv | Bin 7560 -> 7648 bytes Shaders/ClusterLightCulling.spv | Bin 21764 -> 21852 bytes Shaders/Depth.spv | Bin 3104 -> 3192 bytes Shaders/DirShadowVert.spv | Bin 5816 -> 5904 bytes Shaders/ForwardFrag.frag | 1 - Shaders/ForwardFrag.spv | Bin 39248 -> 39336 bytes Shaders/ForwardVert.spv | Bin 4892 -> 4980 bytes Shaders/SSAO.frag | 41 +++-- Shaders/SSAO.spv | Bin 12036 -> 12776 bytes Shaders/camera.glsl | 3 + Source/Core/Eruption.cpp | 14 +- Source/Editor/EditorLayer.cpp | 2 +- Source/Editor/UIPanels/SettingsPanel.cpp | 30 +++- Source/Renderer/Buffers/MemoryBuffer.cpp | 4 +- Source/Renderer/Buffers/MemoryBuffer.hpp | 2 +- Source/Renderer/Camera/CameraBuffer.cpp | 3 + Source/Renderer/Camera/CameraBuffer.hpp | 3 + Source/Renderer/Renderer.cpp | 60 +++++-- Source/Renderer/Renderer.hpp | 41 +++-- Source/Scene/Scene.cpp | 220 ++++++++++++++++++----- Source/Scene/Scene.hpp | 15 +- imgui.ini | 30 ++-- 23 files changed, 340 insertions(+), 130 deletions(-) diff --git a/EruptionEngine.ini b/EruptionEngine.ini index b1d59cf..ecdfafc 100644 --- a/EruptionEngine.ini +++ b/EruptionEngine.ini @@ -16,7 +16,6 @@ #define SOFT_SHADOWS 1 #define SOFT_SSAO 1 -#define SSAO_SAMPLES 64 #define MAX_POINT_LIGHT_SHADOWS 4 #define MAX_SPOT_LIGHT_SHADOWS 4 diff --git a/Shaders/ClusterAABB.spv b/Shaders/ClusterAABB.spv index d7d5500398b7bb8d701a85d0b6d3219db8ee3334..3c6ecdc781884d8eb0871a3d177e24c38d70d905 100644 GIT binary patch delta 109 zcmeCMeqg;}0<#Jm11p0a0|SE~5LdY6mjNjTn3xcdT?rK1+{0YLst8l13>00!%)sCQ l)d^As5?iwQC&ykUW>E&a$p^WGCtu)Z0b+yAKe#6d0s!;e6*&L^ delta 38 scmaE0-C@090`uk_%mu8Q130%aF$*);O%~)4o~*#b0>lQJBX~Ll0sS)zLI3~& diff --git a/Shaders/ClusterLightCulling.spv b/Shaders/ClusterLightCulling.spv index 94f35584cf871c8c4ff35f4efe327890c3ed6b8f..febace42f1d056fbbe69ee8744d6d48120fb9be7 100644 GIT binary patch delta 101 zcmZo!#dv2G00! f%)sCQ)d^As5?iu4SMnb%7 diff --git a/Shaders/DirShadowVert.spv b/Shaders/DirShadowVert.spv index 8c2b05dfc206647e8badb8d1d37eb58ce4fcf17b..54be4990025e5111f327af76ca23d2aa0f81a41e 100644 GIT binary patch delta 99 zcmdm?J3()Q4zmgy11p0h0|SE~5LdY6mjNjTn3xcdT?rK1tic??p$Jo@3>00!%)sCQ b)d^As5?ivlk~@=)QFQWSLC?)9LQ_}(wD1n} delta 28 kcmbQBw?lV>4)f*^<`9m}7r2wy7=0Iq@x>;M1& diff --git a/Shaders/SSAO.frag b/Shaders/SSAO.frag index 9b8e78d..9695e31 100644 --- a/Shaders/SSAO.frag +++ b/Shaders/SSAO.frag @@ -41,40 +41,53 @@ layout(push_constant) uniform SSAOParameters float radius; float bias; float multiplier; + + uint samples; + + float noiseScale; } params; layout(set = 0, binding = 0) uniform CameraBuffer { CameraBufferObject camera; }; -vec3 GetViewPosition(vec2 ndcUV) +float GetViewZ(vec2 uv) +{ + float depth = texture(fDepth, uv).r; + + return camera.invProj[3][2] / (camera.invProj[2][3] * depth + camera.invProj[3][3]); +} +vec3 GetViewPosition(vec2 uv) { - float depth = texture(fDepth, ndcUV * 0.5 + 0.5).r; + float depth = texture(fDepth, uv).r; - vec4 ndc = vec4(ndcUV, depth, 1.0); + vec4 ndc = vec4(uv * 2.0 - 1.0, depth, 1.0); vec4 view = camera.invProj * ndc; return view.xyz / view.w; } - +float LinearDepth(float d) +{ + return camera.zNear * camera.zFar / (camera.zFar + d * (camera.zNear - camera.zFar)); +} void main() { - vec2 noiseScale = vec2(params.screenWidth/4.0, params.screenHeight/4.0); + vec2 noiseScale = vec2(params.screenWidth/params.noiseScale, params.screenHeight/params.noiseScale); - vec3 viewPos = GetViewPosition(fTexcoord * 2.0 - 1.0); + vec3 viewPos = GetViewPosition(fTexcoord); vec3 viewNormal = normalize(cross(dFdx(viewPos.xyz), dFdy(viewPos.xyz))); - int x = int(floor(fTexcoord.x * noiseScale.x)) % 4; - int y = int(floor(fTexcoord.y * noiseScale.y)) % 4; - - vec3 randomVec = normalize(rotations[y * 4 + x]); + int x = int(fTexcoord.x * noiseScale.x) % 4; + int y = int(fTexcoord.y * noiseScale.y) % 4; + vec3 randomVec = normalize(rotations[y * 4 + x]); + vec3 tangent = normalize(randomVec - viewNormal * dot(randomVec, viewNormal)); vec3 bitangent = cross(viewNormal, tangent); mat3 TBN = mat3(tangent, bitangent, viewNormal); float occlusion = 0.0; - for(uint i = 0; i < SSAO_SAMPLES; ++i) + for(uint i = 0; i < params.samples; ++i) { vec3 samplePos = TBN * kernels[i]; samplePos = viewPos + samplePos * params.radius; @@ -82,12 +95,12 @@ void main() vec4 offset = vec4(samplePos, 1.0); offset = camera.proj * offset; offset.xy /= offset.w; - - float sampleDepth = GetViewPosition(offset.xy).z; + + float sampleDepth = GetViewZ(offset.xy * 0.5 + 0.5); float rangeCheck = smoothstep(0.0, 1.0, params.radius / abs(viewPos.z - sampleDepth)); occlusion += (sampleDepth >= samplePos.z + params.bias ? 1.0 : 0.0) * rangeCheck * params.multiplier; } - Occlusion = 1.0 - (occlusion / SSAO_SAMPLES); + Occlusion = 1.0 - (occlusion / params.samples); } \ No newline at end of file diff --git a/Shaders/SSAO.spv b/Shaders/SSAO.spv index 29e4ca80d8610b964f91ebfd44011ca204513582..c837bad40942e9e74a8741439fdaa1b9ae682d23 100644 GIT binary patch literal 12776 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 literal 12036 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 - auto m_Skull = m_ExampleScene->CreateSceneObject("SkullModel", m_AssetManager->GetMesh("SkullModel")); - m_Skull->SetPosition(glm::vec3(0, 0.5f, -0.3)); - m_Skull->SetRotation(glm::vec3(45.0f, 90.0f, 0.0f)); - - auto m_Sponza = m_ExampleScene->CreateSceneObject("Sponza", m_AssetManager->GetMesh("Sponza")); m_Sponza->SetScale(glm::vec3(1.2f)); - + m_AssetManager->GetMaterial("BlueCurtains")->SetNormalStrength(0.6f); m_AssetManager->GetMaterial("RedCurtains")->SetNormalStrength(0.6f); m_AssetManager->GetMaterial("GreenCurtains")->SetNormalStrength(0.6f); + auto m_Skull = m_ExampleScene->CreateSceneObject("SkullModel", m_AssetManager->GetMesh("SkullModel")); + m_Skull->SetPosition(glm::vec3(0, 0.5f, -0.3)); + m_Skull->SetRotation(glm::vec3(45.0f, 90.0f, 0.0f)); + + + + //for (uint32_t i = 0; i < MAX_POINT_LIGHTS; i++) //{ // glm::vec3 spawnPoint( diff --git a/Source/Editor/EditorLayer.cpp b/Source/Editor/EditorLayer.cpp index 154127c..e709a6d 100644 --- a/Source/Editor/EditorLayer.cpp +++ b/Source/Editor/EditorLayer.cpp @@ -269,7 +269,7 @@ namespace en if (ImGui::CollapsingHeader("Stats")) { ImGui::Text(("FPS: " + std::to_string(1.0 / m_Renderer->GetFrameTime())).c_str()); - ImGui::Text((std::to_string(m_Renderer->GetFrameTime()) + "ms/frame").c_str()); + ImGui::Text((std::to_string(m_Renderer->GetFrameTime()*1000.0f) + "ms/frame").c_str()); } SPACE(); diff --git a/Source/Editor/UIPanels/SettingsPanel.cpp b/Source/Editor/UIPanels/SettingsPanel.cpp index a809a73..227aedd 100644 --- a/Source/Editor/UIPanels/SettingsPanel.cpp +++ b/Source/Editor/UIPanels/SettingsPanel.cpp @@ -6,6 +6,7 @@ namespace en { constexpr std::array g_AntialiasingModeNames = { "None", "FXAA"}; constexpr std::array g_AmbientOcclusionModeNames = { "None", "SSAO" }; + constexpr std::array g_QualityLevelNames = { "Low", "Medium", "High", "Ultra"}; void SettingsPanel::Render() { @@ -15,8 +16,12 @@ namespace en static bool vSync = m_Renderer->GetVSyncEnabled(); static bool depthPrepass = m_Renderer->GetDepthPrepassEnabled(); + static Renderer::AntialiasingMode antialiasingMode = m_Renderer->GetAntialiasingMode(); - static Renderer::SSAOMode ssaoMode = m_Renderer->GetSSAOMode(); + static Renderer::AmbientOcclusionMode ssaoMode = m_Renderer->GetAmbientOcclusionMode(); + + //static Renderer::QualityLevel antialiasingQuality = m_Renderer->GetAntialaliasingQuality(); + static Renderer::QualityLevel aoModeQuality = m_Renderer->GetAmbientOcclusionQuality(); if (ImGui::Checkbox("VSync", &vSync)) m_Renderer->SetVSyncEnabled(vSync); @@ -28,6 +33,9 @@ namespace en { if (ImGui::Combo("Antialiasing Mode", (int*)&antialiasingMode, g_AntialiasingModeNames.data(), g_AntialiasingModeNames.size())) m_Renderer->SetAntialiasingMode(antialiasingMode); + + //if (ImGui::Combo("Antialiasing Quality", (int*)&antialiasingQuality, g_QualityLevelNames.data(), g_QualityLevelNames.size())) + // m_Renderer->SetAntialaliasingQuality(antialiasingQuality); auto& aa = m_Renderer->GetAntialiasingProperties(); @@ -61,26 +69,32 @@ namespace en if (ImGui::CollapsingHeader("Ambient Occlusion")) { if (ImGui::Combo("Ambient Occlusion Mode", (int*)&ssaoMode, g_AmbientOcclusionModeNames.data(), g_AmbientOcclusionModeNames.size())) - m_Renderer->SetSSAOMode(ssaoMode); + m_Renderer->SetAmbientOcclusionMode(ssaoMode); - auto& ssao = m_Renderer->GetSSAOProperties(); + if (ImGui::Combo("Ambient Occlusion Quality", (int*)&aoModeQuality, g_QualityLevelNames.data(), g_QualityLevelNames.size())) + m_Renderer->SetAmbientOcclusionQuality(aoModeQuality); + auto& ssao = m_Renderer->GetAmbientOcclusionProperties(); + int s = ssao._samples; switch (ssaoMode) { - case Renderer::SSAOMode::SSAO: + case Renderer::AmbientOcclusionMode::SSAO: ImGui::Spacing(); if (ImGui::Button("Restore Defaults")) - ssao = Renderer::SSAOProperties{}; + ssao = Renderer::AmbientOcclusionProperties{}; ImGui::Spacing(); - + ImGui::DragFloat("SSAO Bias", &ssao.bias, 0.05f, 0.0f, 1.0f), "%.2f", ImGuiSliderFlags_AlwaysClamp; ImGui::DragFloat("SSAO Radius", &ssao.radius, 0.1f, 0.0f, 5.0f, "%.1f", ImGuiSliderFlags_AlwaysClamp); ImGui::DragFloat("SSAO Multiplier", &ssao.multiplier, 0.1f, 0.0f, 5.0f, "%.1f", ImGuiSliderFlags_AlwaysClamp); + ImGui::DragFloat("ssao_noise_scale", &ssao._noiseScale, 0.1f, 1.0f, 8.0f, "%.1f", ImGuiSliderFlags_AlwaysClamp); + ImGui::DragInt("ssao_samples", &s, 1.0f, 1.0f, 64.0f, "%.1f", ImGuiSliderFlags_AlwaysClamp); + break; - case Renderer::SSAOMode::None: + case Renderer::AmbientOcclusionMode::None: ImGui::Text("Ambient occlusion is disabled."); break; @@ -88,7 +102,7 @@ namespace en EN_ERROR("void SettingsPanel::Render() - postProcessing.ambientOcclusionMode was an unknown value!"); break; } - + ssao._samples = s; } if (ImGui::CollapsingHeader("Lights and Shadows")) diff --git a/Source/Renderer/Buffers/MemoryBuffer.cpp b/Source/Renderer/Buffers/MemoryBuffer.cpp index 6aff489..35ae8bb 100644 --- a/Source/Renderer/Buffers/MemoryBuffer.cpp +++ b/Source/Renderer/Buffers/MemoryBuffer.cpp @@ -28,14 +28,14 @@ namespace en vmaDestroyBuffer(Context::Get().m_Allocator, m_Buffer, m_Allocation); } - void MemoryBuffer::MapMemory(const void* memory, VkDeviceSize memorySize) + void MemoryBuffer::MapMemory(const void* memory, VkDeviceSize memorySize, VkDeviceSize srcOffset, VkDeviceSize dstOffset) { UseContext(); void* data; vmaMapMemory(ctx.m_Allocator, m_Allocation, &data); - memcpy(data, memory, static_cast(memorySize)); + memcpy((void*)((VkDeviceSize)data+dstOffset), (void*)((VkDeviceSize)memory + srcOffset), static_cast(memorySize)); vmaUnmapMemory(ctx.m_Allocator, m_Allocation); } void MemoryBuffer::CopyInto(const void* memory, VkDeviceSize memorySize, uint32_t dstOffset, VkCommandBuffer cmd) diff --git a/Source/Renderer/Buffers/MemoryBuffer.hpp b/Source/Renderer/Buffers/MemoryBuffer.hpp index 3187b39..3f01aac 100644 --- a/Source/Renderer/Buffers/MemoryBuffer.hpp +++ b/Source/Renderer/Buffers/MemoryBuffer.hpp @@ -13,7 +13,7 @@ namespace en MemoryBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VmaMemoryUsage vmaMemoryUsage); ~MemoryBuffer(); - void MapMemory(const void* memory, VkDeviceSize memorySize); + void MapMemory(const void* memory, VkDeviceSize memorySize, VkDeviceSize srcOffset = 0U, VkDeviceSize dstOffset = 0U); void CopyInto(const void* memory, VkDeviceSize memorySize, uint32_t dstOffset = 0U, VkCommandBuffer cmd = VK_NULL_HANDLE); diff --git a/Source/Renderer/Camera/CameraBuffer.cpp b/Source/Renderer/Camera/CameraBuffer.cpp index 64cc82d..65129dc 100644 --- a/Source/Renderer/Camera/CameraBuffer.cpp +++ b/Source/Renderer/Camera/CameraBuffer.cpp @@ -76,6 +76,9 @@ namespace en m_CBOs[frameIndex].clusterScale = (float)CLUSTERED_TILES_Z / std::log2f(camera->m_FarPlane / camera->m_NearPlane); m_CBOs[frameIndex].clusterBias = -((float)CLUSTERED_TILES_Z * std::log2f(camera->m_NearPlane) / std::log2f(camera->m_FarPlane / camera->m_NearPlane)); + + m_CBOs[frameIndex].xFov = camera->m_Fov; + m_CBOs[frameIndex].yFov = camera->m_Fov * ((float)extent.height / (float)extent.width); } VkDescriptorSetLayout CameraBuffer::GetLayout() diff --git a/Source/Renderer/Camera/CameraBuffer.hpp b/Source/Renderer/Camera/CameraBuffer.hpp index 04d5522..83a4b2d 100644 --- a/Source/Renderer/Camera/CameraBuffer.hpp +++ b/Source/Renderer/Camera/CameraBuffer.hpp @@ -57,6 +57,9 @@ namespace en float zNear = 0.0f; float zFar = 1.0f; + + float xFov = 1.0f; + float yFov = 1.0f; }; std::array m_CBOs; diff --git a/Source/Renderer/Renderer.cpp b/Source/Renderer/Renderer.cpp index 5f67c1b..c2a4226 100644 --- a/Source/Renderer/Renderer.cpp +++ b/Source/Renderer/Renderer.cpp @@ -56,8 +56,6 @@ namespace en else WaitForActiveFrame(); - m_Scene->UpdateSceneGPU(); - m_CameraBuffer->MapBuffer(m_FrameIndex); } else @@ -69,6 +67,8 @@ namespace en BeginRender(); + m_Scene->UpdateSceneGPU(m_Frames[m_FrameIndex].commandBuffer); + if (m_Scene) { ShadowPass(); @@ -402,7 +402,7 @@ namespace en m_DepthPass->End(); - if (m_Settings.ssaoMode == SSAOMode::None) + if (m_Settings.ambientOcclusionMode == AmbientOcclusionMode::None) m_DepthBuffer->ChangeLayout(m_DepthBuffer->GetLayout(), VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, @@ -430,8 +430,28 @@ namespace en m_Frames[m_FrameIndex].commandBuffer ); - m_Settings.ssao.screenWidth = m_SSAOTarget->m_Size.width; - m_Settings.ssao.screenHeight = m_SSAOTarget->m_Size.height; + m_Settings.ambientOcclusion.screenWidth = m_SSAOTarget->m_Size.width; + m_Settings.ambientOcclusion.screenHeight = m_SSAOTarget->m_Size.height; + + switch (m_Settings.ambientOcclusionQuality) + { + case QualityLevel::Low: + m_Settings.ambientOcclusion._samples = 16U; + m_Settings.ambientOcclusion._noiseScale = 4.0f; + break; + case QualityLevel::Medium: + m_Settings.ambientOcclusion._samples = 32U; + m_Settings.ambientOcclusion._noiseScale = 4.0f; + break; + case QualityLevel::High: + m_Settings.ambientOcclusion._samples = 32U; + m_Settings.ambientOcclusion._noiseScale = 2.0f; + break; + case QualityLevel::Ultra: + m_Settings.ambientOcclusion._samples = 32U; + m_Settings.ambientOcclusion._noiseScale = 1.0f; + break; + } GraphicsPass::RenderInfo renderInfo { .colorAttachmentView = m_SSAOTarget->GetViewHandle(), @@ -442,9 +462,9 @@ namespace en }; m_SSAOPass->Begin(m_Frames[m_FrameIndex].commandBuffer, renderInfo); - if (m_Settings.ssaoMode != SSAOMode::None && m_Settings.depthPrePass) + if (m_Settings.ambientOcclusionMode != AmbientOcclusionMode::None && m_Settings.depthPrePass) { - m_SSAOPass->PushConstants(&m_Settings.ssao, sizeof(m_Settings.ssao), 0U, VK_SHADER_STAGE_FRAGMENT_BIT); + m_SSAOPass->PushConstants(&m_Settings.ambientOcclusion, sizeof(m_Settings.ambientOcclusion), 0U, VK_SHADER_STAGE_FRAGMENT_BIT); m_SSAOPass->BindDescriptorSet(m_CameraBuffer->GetDescriptorHandle(m_FrameIndex), 0U); m_SSAOPass->BindDescriptorSet(m_DepthBufferDescriptor, 1U); m_SSAOPass->Draw(3U); @@ -667,9 +687,20 @@ namespace en m_Settings.antialiasingMode = antialiasingMode; ReloadBackend(); } - void Renderer::SetSSAOMode(const SSAOMode ssaoMode) + void Renderer::SetAmbientOcclusionMode(const AmbientOcclusionMode aoMode) { - m_Settings.ssaoMode = ssaoMode; + m_Settings.ambientOcclusionMode = aoMode; + ReloadBackend(); + } + + //void Renderer::SetAntialaliasingQuality(const QualityLevel quality) + //{ + // m_Settings.antialiasingQuality = quality; + // ReloadBackend(); + //} + void Renderer::SetAmbientOcclusionQuality(const QualityLevel quality) + { + m_Settings.ambientOcclusionQuality = quality; ReloadBackend(); } @@ -1095,10 +1126,10 @@ namespace en } void Renderer::CreateSSAOPass() { - constexpr VkPushConstantRange ssao{ + constexpr VkPushConstantRange ao { .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, .offset = 0U, - .size = sizeof(m_Settings.ssao), + .size = sizeof(m_Settings.ambientOcclusion), }; GraphicsPass::CreateInfo info{ @@ -1106,7 +1137,7 @@ namespace en .fShader = "Shaders/SSAO.spv", .descriptorLayouts {CameraBuffer::GetLayout(), m_DepthBufferDescriptor->GetLayout()}, - .pushConstantRanges {ssao}, + .pushConstantRanges {ao}, .colorFormat = m_SSAOTarget->m_Format, }; @@ -1416,10 +1447,7 @@ namespace en void Renderer::CreateSSAOTarget() { m_SSAOTarget = MakeHandle( - VkExtent2D{ - m_Swapchain->GetExtent().width / 2U, - m_Swapchain->GetExtent().height / 2U - }, + m_Swapchain->GetExtent(), VK_FORMAT_R8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_IMAGE_ASPECT_COLOR_BIT, diff --git a/Source/Renderer/Renderer.hpp b/Source/Renderer/Renderer.hpp index 6f0ee20..4555743 100644 --- a/Source/Renderer/Renderer.hpp +++ b/Source/Renderer/Renderer.hpp @@ -44,6 +44,14 @@ namespace en void BindScene(en::Handle scene); void UnbindScene(); + enum struct QualityLevel + { + Low = 0, + Medium = 1, + High = 2, + Ultra = 3 + }; + enum struct AntialiasingMode { None = 0, @@ -61,20 +69,23 @@ namespace en float texelSizeY = 1.0f / 1080.0f; }; - enum struct SSAOMode + enum struct AmbientOcclusionMode { None = 0, SSAO = 1 }; - struct SSAOProperties + struct AmbientOcclusionProperties { float screenWidth = 1920.0f; float screenHeight = 1080.0f; - float radius = 0.5f; + float radius = 0.85f; float bias = 0.025f; - float multiplier = 1.0f; + float multiplier = 1.2f; + + uint32_t _samples = 64U; + float _noiseScale = 4.0f; }; void SetVSyncEnabled(const bool enabled); @@ -104,11 +115,17 @@ namespace en void SetAntialiasingMode(const AntialiasingMode antialiasingMode); const AntialiasingMode GetAntialiasingMode() const { return m_Settings.antialiasingMode; }; - void SetSSAOMode(const SSAOMode ssaoMode); - const SSAOMode GetSSAOMode() const { return m_Settings.ssaoMode; }; + void SetAmbientOcclusionMode(const AmbientOcclusionMode aoMode); + const AmbientOcclusionMode GetAmbientOcclusionMode() const { return m_Settings.ambientOcclusionMode; }; AntialiasingProperties& GetAntialiasingProperties() { return m_Settings.antialiasing; }; - SSAOProperties& GetSSAOProperties() { return m_Settings.ssao; }; + AmbientOcclusionProperties& GetAmbientOcclusionProperties() { return m_Settings.ambientOcclusion; }; + + //void SetAntialaliasingQuality(const QualityLevel quality); + //const QualityLevel GetAntialaliasingQuality() const { return m_Settings.antialiasingQuality; }; + + void SetAmbientOcclusionQuality(const QualityLevel quality); + const QualityLevel GetAmbientOcclusionQuality() const { return m_Settings.ambientOcclusionQuality; }; void ReloadBackend(); @@ -153,7 +170,7 @@ namespace en std::array, MAX_POINT_LIGHT_SHADOWS> m_PointShadowMaps; std::array, MAX_SPOT_LIGHT_SHADOWS> m_SpotShadowMaps; - std::array, MAX_DIR_LIGHT_SHADOWS* SHADOW_CASCADES> m_DirShadowMaps; + std::array, MAX_DIR_LIGHT_SHADOWS * SHADOW_CASCADES> m_DirShadowMaps; Handle m_DepthBuffer; Handle m_SSAOTarget; @@ -180,8 +197,9 @@ namespace en AntialiasingMode antialiasingMode = AntialiasingMode::FXAA; AntialiasingProperties antialiasing{}; - SSAOMode ssaoMode = SSAOMode::SSAO; - SSAOProperties ssao{}; + AmbientOcclusionMode ambientOcclusionMode = AmbientOcclusionMode::SSAO; + AmbientOcclusionProperties ambientOcclusion{}; + QualityLevel ambientOcclusionQuality = QualityLevel::High; } m_Settings; struct CSM { @@ -194,8 +212,7 @@ namespace en std::array cascadeRadiuses{}; } m_CSM; - struct ClusterSSBOs - { + struct ClusterSSBOs { Handle aabbClusters; Handle aabbClustersDescriptor; diff --git a/Source/Scene/Scene.cpp b/Source/Scene/Scene.cpp index 5d1c628..1236795 100644 --- a/Source/Scene/Scene.cpp +++ b/Source/Scene/Scene.cpp @@ -535,14 +535,92 @@ namespace en m_GPULights.spotLights[m_GPULights.activeSpotLights].range = 0.0f; m_GPULights.spotLights[m_GPULights.activeSpotLights].direction = glm::vec3(0.0f); m_GPULights.spotLights[m_GPULights.activeSpotLights].outerCutoff = 0.0f; + + + if ((float)m_ChangedPointLightsIDs.size() / MAX_POINT_LIGHTS > POINT_LIGHTS_UPDATE_THRESHOLD) + { + VkDeviceSize offset = (size_t)&m_GPULights.pointLights - (size_t)&m_GPULights; + m_LightsStagingBuffer->MapMemory(m_GPULights.pointLights.data(), sizeof(m_GPULights.pointLights), 0U, offset); + } + else + { + for (const auto& changedId : m_ChangedPointLightsIDs) + { + VkDeviceSize offset = (size_t)&m_GPULights.pointLights[changedId] - (size_t)&m_GPULights; + m_LightsStagingBuffer->MapMemory(&m_GPULights.pointLights[changedId], sizeof(PointLight::Buffer), 0U, offset); + } + } + + if ((float)m_ChangedSpotLightsIDs.size() / MAX_SPOT_LIGHTS > SPOT_LIGHTS_UPDATE_THRESHOLD) + { + VkDeviceSize offset = (size_t)&m_GPULights.spotLights - (size_t)&m_GPULights; + m_LightsStagingBuffer->MapMemory(m_GPULights.spotLights.data(), sizeof(m_GPULights.spotLights), 0U, offset); + } + else + { + for (const auto& changedId : m_ChangedSpotLightsIDs) + { + VkDeviceSize offset = (size_t)&m_GPULights.spotLights[changedId] - (size_t)&m_GPULights; + m_LightsStagingBuffer->MapMemory(&m_GPULights.spotLights[changedId], sizeof(SpotLight::Buffer), 0U, offset); + } + } + + if ((float)m_ChangedDirLightsIDs.size() / MAX_DIR_LIGHTS > DIR_LIGHTS_UPDATE_THRESHOLD) + { + VkDeviceSize offset = (size_t)&m_GPULights.dirLights - (size_t)&m_GPULights; + m_LightsStagingBuffer->MapMemory(m_GPULights.dirLights.data(), sizeof(m_GPULights.dirLights), 0U, offset); + } + else + { + for (const auto& changedId : m_ChangedDirLightsIDs) + { + VkDeviceSize offset = (size_t)&m_GPULights.dirLights[changedId] - (size_t)&m_GPULights; + m_LightsStagingBuffer->MapMemory(&m_GPULights.dirLights[changedId], sizeof(DirectionalLight::Buffer), 0U, offset); + } + } + + constexpr VkDeviceSize pointLightsSize = sizeof(m_GPULights.pointLights); + constexpr VkDeviceSize spotLightsSize = sizeof(m_GPULights.spotLights); + constexpr VkDeviceSize dirLightsSize = sizeof(m_GPULights.dirLights); + constexpr VkDeviceSize allLightsSize = pointLightsSize + spotLightsSize + dirLightsSize; + + if (m_SceneLightingChanged) + m_LightsStagingBuffer->MapMemory(&m_GPULights.activePointLights, sizeof(GPULights) - allLightsSize, 0U, allLightsSize); + + // If more than MATRICES_UPDATE_THRESHOLD percent of matrices have changed, do a whole copy + if ((float)(m_ChangedMaterialIDs.size() / m_Materials.size()) > MATERIALS_UPDATE_THRESHOLD) + { + m_GlobalMaterialsStagingBuffer->MapMemory(m_GPUMaterials.data(), sizeof(GPUMaterial) * m_Materials.size()); + } + else + { + for (const auto& changedMaterialId : m_ChangedMaterialIDs) + { + VkDeviceSize offset = changedMaterialId * sizeof(GPUMaterial); + m_GlobalMaterialsStagingBuffer->MapMemory(&m_GPUMaterials[changedMaterialId], sizeof(GPUMaterial), 0U, offset); + } + } + + if ((float)m_ChangedMatrixIDs.size() / m_Matrices.size() > MATRICES_UPDATE_THRESHOLD) + { + m_GlobalMatricesStagingBuffer->MapMemory(m_Matrices.data(), sizeof(glm::mat4) * m_Matrices.size()); + } + else + { + for (const auto& changedMatrixId : m_ChangedMatrixIDs) + { + VkDeviceSize offset = changedMatrixId * sizeof(glm::mat4); + m_GlobalMatricesStagingBuffer->MapMemory(&m_Matrices[changedMatrixId], sizeof(glm::mat4), 0U, offset); + } + } } - void Scene::UpdateSceneGPU() + void Scene::UpdateSceneGPU(const VkCommandBuffer cmd) { - UpdateMatrixBuffer(m_ChangedMatrixIDs); + UpdateMatrixBuffer(cmd, m_ChangedMatrixIDs); - UpdateLightsBuffer(m_ChangedPointLightsIDs, m_ChangedSpotLightsIDs, m_ChangedDirLightsIDs); + UpdateLightsBuffer(cmd, m_ChangedPointLightsIDs, m_ChangedSpotLightsIDs, m_ChangedDirLightsIDs); - UpdateMaterialBuffer(m_ChangedMaterialIDs); + UpdateMaterialBuffer(cmd, m_ChangedMaterialIDs); if (m_GlobalDescriptorChanged) { @@ -632,7 +710,7 @@ namespace en m_OccupiedMatrices.insert(index); m_Matrices[index] = matrix; - + return index; } uint32_t Scene::RegisterMaterial(Handle material) @@ -702,55 +780,87 @@ namespace en m_Textures[index] = nullptr; } - void Scene::UpdateMatrixBuffer(const std::vector& changedMatrixIds) + void Scene::UpdateMatrixBuffer(const VkCommandBuffer cmd, const std::vector& changedMatrixIds) { if (m_Matrices.size() == 0) return; + bool updated = false; + + m_GlobalMatricesBuffer->PipelineBarrier( + VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_MEMORY_WRITE_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + cmd + ); + if (sizeof(glm::mat4) * m_Matrices.size() > m_GlobalMatricesBuffer->GetSize()) { EN_LOG("MATRIX RESIZE"); uint32_t overflow = sizeof(glm::mat4) * m_Matrices.size() - m_GlobalMatricesBuffer->GetSize(); - m_GlobalMatricesBuffer->Resize((m_GlobalMatricesBuffer->GetSize() + overflow)*MATRICES_OVERFLOW_MULTIPLIER); - m_GlobalMatricesStagingBuffer->Resize(m_GlobalMatricesBuffer->GetSize()); + m_GlobalMatricesBuffer->Resize((m_GlobalMatricesBuffer->GetSize() + overflow)*MATRICES_OVERFLOW_MULTIPLIER, cmd); + m_GlobalMatricesStagingBuffer->Resize(m_GlobalMatricesBuffer->GetSize(), cmd); m_GlobalDescriptorChanged = true; + + updated = true; } uint32_t totalMatrices = m_Matrices.size(); uint32_t changedMatrices = changedMatrixIds.size(); - + // If more than MATRICES_UPDATE_THRESHOLD percent of matrices have changed, do a whole copy if ((float)changedMatrices / totalMatrices > MATRICES_UPDATE_THRESHOLD) { EN_LOG("TOTAL MATRIX UPDATE"); - m_GlobalMatricesStagingBuffer->MapMemory(m_Matrices.data(), sizeof(glm::mat4) * m_Matrices.size()); - m_GlobalMatricesStagingBuffer->CopyTo(m_GlobalMatricesBuffer, sizeof(glm::mat4) * m_Matrices.size()); + m_GlobalMatricesStagingBuffer->CopyTo(m_GlobalMatricesBuffer, sizeof(glm::mat4) * m_Matrices.size(), 0U, 0U, cmd); + + updated = true; } else { for (const auto& changedMatrixId : changedMatrixIds) { - m_GlobalMatricesStagingBuffer->MapMemory(&m_Matrices[changedMatrixId], sizeof(glm::mat4)); - m_GlobalMatricesStagingBuffer->CopyTo(m_GlobalMatricesBuffer, sizeof(glm::mat4), 0U, changedMatrixId * sizeof(glm::mat4)); + VkDeviceSize offset = changedMatrixId * sizeof(glm::mat4); + m_GlobalMatricesStagingBuffer->CopyTo(m_GlobalMatricesBuffer, sizeof(glm::mat4), offset, offset, cmd); + + updated = true; } } + + if (updated) + { + m_GlobalMatricesBuffer->PipelineBarrier( + VK_ACCESS_MEMORY_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, + cmd + ); + } } - void Scene::UpdateMaterialBuffer(const std::vector& changedMaterialIds) + void Scene::UpdateMaterialBuffer(const VkCommandBuffer cmd, const std::vector& changedMaterialIds) { if (m_Materials.size() == 0) return; + bool updated = false; + + m_GlobalMaterialsBuffer->PipelineBarrier( + VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_MEMORY_WRITE_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + cmd + ); + if (sizeof(GPUMaterial) * m_Materials.size() > m_GlobalMaterialsBuffer->GetSize()) { EN_LOG("MATERIAL RESIZE"); uint32_t overflow = sizeof(GPUMaterial) * m_Materials.size() - m_GlobalMaterialsBuffer->GetSize(); - m_GlobalMaterialsBuffer->Resize((m_GlobalMaterialsBuffer->GetSize() + overflow) * MATERIALS_OVERFLOW_MULTIPLIER); - m_GlobalMaterialsStagingBuffer->Resize(m_GlobalMaterialsBuffer->GetSize()); + m_GlobalMaterialsBuffer->Resize((m_GlobalMaterialsBuffer->GetSize() + overflow) * MATERIALS_OVERFLOW_MULTIPLIER, cmd); + m_GlobalMaterialsStagingBuffer->Resize(m_GlobalMaterialsBuffer->GetSize(), cmd); m_GlobalDescriptorChanged = true; + + updated = true; } uint32_t totalMaterials = m_Materials.size(); @@ -760,40 +870,57 @@ namespace en if ((float)changedMaterials / totalMaterials > MATERIALS_UPDATE_THRESHOLD) { EN_LOG("TOTAL MATERIAL UPDATE"); - m_GlobalMaterialsStagingBuffer->MapMemory(m_GPUMaterials.data(), sizeof(GPUMaterial) * m_Materials.size()); - m_GlobalMaterialsStagingBuffer->CopyTo(m_GlobalMaterialsBuffer, sizeof(GPUMaterial) * m_Materials.size()); + m_GlobalMaterialsStagingBuffer->CopyTo(m_GlobalMaterialsBuffer, sizeof(GPUMaterial) * m_Materials.size(), 0U, 0U, cmd); + + updated = true; } else { for (const auto& changedMaterialId : changedMaterialIds) { - m_GlobalMaterialsStagingBuffer->MapMemory(&m_GPUMaterials[changedMaterialId], sizeof(GPUMaterial)); - m_GlobalMaterialsStagingBuffer->CopyTo(m_GlobalMaterialsBuffer, sizeof(GPUMaterial), 0U, changedMaterialId * sizeof(GPUMaterial)); + VkDeviceSize offset = changedMaterialId * sizeof(GPUMaterial); + m_GlobalMaterialsStagingBuffer->CopyTo(m_GlobalMaterialsBuffer, sizeof(GPUMaterial), offset, offset, cmd); + + updated = true; } } + + if (updated) + { + m_GlobalMaterialsBuffer->PipelineBarrier( + VK_ACCESS_MEMORY_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + cmd + ); + } } - void Scene::UpdateLightsBuffer(const std::vector& changedPointLightsIDs, const std::vector& changedSpotLightsIDs, const std::vector& changedDirLightsIDs) + void Scene::UpdateLightsBuffer(const VkCommandBuffer cmd, const std::vector& changedPointLightsIDs, const std::vector& changedSpotLightsIDs, const std::vector& changedDirLightsIDs) { uint32_t totalPointLights = MAX_POINT_LIGHTS; uint32_t changedPointLights = changedPointLightsIDs.size(); bool updated = false; + m_LightsBuffer->PipelineBarrier( + VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_MEMORY_WRITE_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + cmd + ); + if ((float)changedPointLights / totalPointLights > POINT_LIGHTS_UPDATE_THRESHOLD) { EN_LOG("TOTAL POINT LIGHTS UPDATE"); - m_LightsStagingBuffer->MapMemory(m_GPULights.pointLights.data(), sizeof(m_GPULights.pointLights)); - m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sizeof(m_GPULights.pointLights), 0U, (size_t)&m_GPULights.pointLights - (size_t)&m_GPULights); + VkDeviceSize offset = (size_t)&m_GPULights.pointLights - (size_t)&m_GPULights; + m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sizeof(m_GPULights.pointLights), offset, offset, cmd); updated = true; } else { - int u = 0; for (const auto& changedId : changedPointLightsIDs) { - m_LightsStagingBuffer->MapMemory(&m_GPULights.pointLights[changedId], sizeof(PointLight::Buffer)); - m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sizeof(PointLight::Buffer), 0U, (size_t)&m_GPULights.pointLights[changedId] - (size_t)&m_GPULights); + VkDeviceSize offset = (size_t)&m_GPULights.pointLights[changedId] - (size_t)&m_GPULights; + m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sizeof(PointLight::Buffer), offset, offset, cmd); updated = true; } @@ -805,8 +932,8 @@ namespace en if ((float)changedSpotLights / totalSpotLights > SPOT_LIGHTS_UPDATE_THRESHOLD) { EN_LOG("TOTAL SPOT LIGHTS UPDATE"); - m_LightsStagingBuffer->MapMemory(m_GPULights.spotLights.data(), sizeof(m_GPULights.spotLights)); - m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sizeof(m_GPULights.spotLights), 0U, (size_t)&m_GPULights.spotLights - (size_t)&m_GPULights); + VkDeviceSize offset = (size_t)&m_GPULights.spotLights - (size_t)&m_GPULights; + m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sizeof(m_GPULights.spotLights), offset, offset, cmd); updated = true; } @@ -814,8 +941,8 @@ namespace en { for (const auto& changedId : changedSpotLightsIDs) { - m_LightsStagingBuffer->MapMemory(&m_GPULights.spotLights[changedId], sizeof(SpotLight::Buffer)); - m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sizeof(SpotLight::Buffer), 0U, (size_t)&m_GPULights.spotLights[changedId] - (size_t)&m_GPULights); + VkDeviceSize offset = (size_t)&m_GPULights.spotLights[changedId] - (size_t)&m_GPULights; + m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sizeof(SpotLight::Buffer), offset, offset, cmd); updated = true; } @@ -827,8 +954,8 @@ namespace en if ((float)changedDirLights / totalDirLights > DIR_LIGHTS_UPDATE_THRESHOLD) { EN_LOG("TOTAL DIR LIGHTS UPDATE"); - m_LightsStagingBuffer->MapMemory(m_GPULights.dirLights.data(), sizeof(m_GPULights.dirLights)); - m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sizeof(m_GPULights.dirLights), 0U, (size_t)&m_GPULights.dirLights - (size_t)&m_GPULights); + VkDeviceSize offset = (size_t)&m_GPULights.dirLights - (size_t)&m_GPULights; + m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sizeof(m_GPULights.dirLights), offset, offset, cmd); updated = true; } @@ -836,17 +963,17 @@ namespace en { for (const auto& changedId : changedDirLightsIDs) { - m_LightsStagingBuffer->MapMemory(&m_GPULights.dirLights[changedId], sizeof(DirectionalLight::Buffer)); - m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sizeof(DirectionalLight::Buffer), 0U, (size_t)&m_GPULights.dirLights[changedId] - (size_t)&m_GPULights); + VkDeviceSize offset = (size_t)&m_GPULights.dirLights[changedId] - (size_t)&m_GPULights; + m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sizeof(DirectionalLight::Buffer), offset, offset, cmd); updated = true; } } - auto pointLightsSize = sizeof(m_GPULights.pointLights); - auto spotLightsSize = sizeof(m_GPULights.spotLights); - auto dirLightsSize = sizeof(m_GPULights.dirLights); - auto allLightsSize = pointLightsSize + spotLightsSize + dirLightsSize; + constexpr VkDeviceSize pointLightsSize = sizeof(m_GPULights.pointLights); + constexpr VkDeviceSize spotLightsSize = sizeof(m_GPULights.spotLights); + constexpr VkDeviceSize dirLightsSize = sizeof(m_GPULights.dirLights); + constexpr VkDeviceSize allLightsSize = pointLightsSize + spotLightsSize + dirLightsSize; if (m_SceneLightingChanged) { @@ -854,23 +981,18 @@ namespace en auto sceneLightingSize = sizeof(GPULights) - allLightsSize; - m_LightsStagingBuffer->MapMemory(&m_GPULights.activePointLights, sceneLightingSize); - m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sceneLightingSize, 0U, allLightsSize); + m_LightsStagingBuffer->CopyTo(m_LightsBuffer, sceneLightingSize, allLightsSize, allLightsSize, cmd); updated = true; } if (updated) { - //VkCommandBuffer cmd = Helpers::BeginSingleTimeGraphicsCommands() - // // FIX - //m_LightsBuffer->PipelineBarrier( - // VK_ACCESS_MEMORY_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, - // VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - // cmd - //) - // - //Helpers::BeginSingleTimeGraphicsCommands(cmd) + m_LightsBuffer->PipelineBarrier( + VK_ACCESS_MEMORY_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, + cmd + ); } } void Scene::UpdateGlobalDescriptor() diff --git a/Source/Scene/Scene.hpp b/Source/Scene/Scene.hpp index 3762a49..eb3d7e1 100644 --- a/Source/Scene/Scene.hpp +++ b/Source/Scene/Scene.hpp @@ -57,7 +57,7 @@ namespace en private: void UpdateSceneCPU(); - void UpdateSceneGPU(); + void UpdateSceneGPU(const VkCommandBuffer cmd); uint32_t RegisterMatrix(const glm::mat4& matrix = glm::mat4(1.0f)); uint32_t RegisterMaterial(Handle material); @@ -67,10 +67,10 @@ namespace en void DeregisterMaterial(uint32_t index); void DeregisterTexture(uint32_t index); - void UpdateMatrixBuffer (const std::vector& changedMatrixIds); - void UpdateMaterialBuffer (const std::vector& changedMaterialIds); + void UpdateMatrixBuffer (const VkCommandBuffer cmd, const std::vector& changedMatrixIds); + void UpdateMaterialBuffer (const VkCommandBuffer cmd, const std::vector& changedMaterialIds); void UpdateGlobalDescriptor(); - void UpdateLightsBuffer (const std::vector& changedPointLightsIDs, const std::vector& changedSpotLightsIDs, const std::vector& changedDirLightsIDs); + void UpdateLightsBuffer (const VkCommandBuffer cmd, const std::vector& changedPointLightsIDs, const std::vector& changedSpotLightsIDs, const std::vector& changedDirLightsIDs); struct GPUMaterial { glm::vec3 color = glm::vec3(1.0f); @@ -114,15 +114,22 @@ namespace en std::unordered_map m_RegisteredTextures; std::unordered_map m_RegisteredMaterials; + //std::array, FRAMES_IN_FLIGHT> m_LightsBuffer; Handle m_LightsBuffer; Handle m_LightsStagingBuffer; + //std::array, FRAMES_IN_FLIGHT> m_GlobalMaterialsBuffer; Handle m_GlobalMaterialsBuffer; Handle m_GlobalMaterialsStagingBuffer; + //std::array, FRAMES_IN_FLIGHT> m_GlobalMatricesBuffer; Handle m_GlobalMatricesBuffer; Handle m_GlobalMatricesStagingBuffer; + //std::array, FRAMES_IN_FLIGHT> m_GlobalDescriptorSet; + //std::array, FRAMES_IN_FLIGHT> m_LightingDescriptorSet; + //std::array, FRAMES_IN_FLIGHT> m_LightsBufferDescriptorSet; + Handle m_GlobalDescriptorSet; Handle m_LightingDescriptorSet; Handle m_LightsBufferDescriptorSet; diff --git a/imgui.ini b/imgui.ini index 6981ba7..4bdc090 100644 --- a/imgui.ini +++ b/imgui.ini @@ -1,6 +1,6 @@ [Window][DockSpace Demo] Pos=0,33 -Size=1920,1047 +Size=2560,1336 Collapsed=0 [Window][Debug##Default] @@ -9,14 +9,14 @@ Size=400,400 Collapsed=0 [Window][Asset Manager] -Pos=274,732 -Size=1399,348 +Pos=273,732 +Size=1400,348 Collapsed=0 DockId=0x00000008,0 [Window][Camera Properties] -Pos=0,720 -Size=272,360 +Pos=0,909 +Size=271,460 Collapsed=0 DockId=0x00000004,0 @@ -26,19 +26,19 @@ Size=405,428 Collapsed=0 [Window][Debug Menu] -Pos=1378,273 -Size=301,150 +Pos=1683,398 +Size=322,194 Collapsed=0 [Window][Scene Hierarchy] Pos=0,33 -Size=272,685 +Size=271,874 Collapsed=0 DockId=0x00000003,0 [Window][Inspector] -Pos=1675,33 -Size=245,1047 +Pos=2315,33 +Size=245,1336 Collapsed=0 DockId=0x00000006,0 @@ -58,17 +58,17 @@ Size=409,172 Collapsed=0 [Window][Settings] -Pos=1271,523 -Size=500,407 +Pos=1621,648 +Size=500,449 Collapsed=0 [Docking][Data] -DockSpace ID=0x1315676B Window=0x4647B76E Pos=0,33 Size=1920,1047 Split=X +DockSpace ID=0x1315676B Window=0x4647B76E Pos=0,33 Size=2560,1336 Split=X DockNode ID=0x00000005 Parent=0x1315676B SizeRef=1673,1047 Split=X - DockNode ID=0x00000001 Parent=0x00000005 SizeRef=272,1047 Split=Y Selected=0x2E9237F7 + DockNode ID=0x00000001 Parent=0x00000005 SizeRef=271,1047 Split=Y Selected=0x2E9237F7 DockNode ID=0x00000003 Parent=0x00000001 SizeRef=499,496 Selected=0x2E9237F7 DockNode ID=0x00000004 Parent=0x00000001 SizeRef=499,261 Selected=0xBC4BD31B - DockNode ID=0x00000002 Parent=0x00000005 SizeRef=1399,1047 Split=Y + DockNode ID=0x00000002 Parent=0x00000005 SizeRef=873,1047 Split=Y DockNode ID=0x00000007 Parent=0x00000002 SizeRef=1167,697 CentralNode=1 Selected=0xA0FC9443 DockNode ID=0x00000008 Parent=0x00000002 SizeRef=1167,348 Selected=0x0E238246 DockNode ID=0x00000006 Parent=0x1315676B SizeRef=245,1047 Selected=0xE7039252 From eb1c93ed5ac03d9e378de96b8efd1e94ee8fa54d Mon Sep 17 00:00:00 2001 From: Mateusz Antkiewicz Date: Thu, 20 Jul 2023 22:01:51 +0200 Subject: [PATCH 3/3] SSAO optimisations and depth prepass fix. --- Shaders/Depth.spv | Bin 3192 -> 3272 bytes Shaders/Depth.vert | 3 ++- Shaders/SSAO.frag | 13 +++++++----- Shaders/SSAO.spv | Bin 12776 -> 12700 bytes Source/Core/Eruption.cpp | 15 ++++---------- Source/Editor/UIPanels/SettingsPanel.cpp | 2 -- Source/Renderer/Renderer.cpp | 14 ++++++------- Source/Renderer/Renderer.hpp | 8 ++++---- Source/Renderer/Swapchain.cpp | 4 ++-- imgui.ini | 24 +++++++++++------------ 10 files changed, 39 insertions(+), 44 deletions(-) diff --git a/Shaders/Depth.spv b/Shaders/Depth.spv index 630e3abf9fd98325a22680269b610a07249aa7d8..c6aff9361a8eb6a0581d05f9c2501d06c25bbae9 100644 GIT binary patch literal 3272 zcmZve-EtH~6vvxvb`ydKC|@dsO#-5VQ4|nGU?G^rsz9Pk;pNsa+fYNz?rhC|xNwyR z@B*v6@WKl&d;lMT@=1IQZ>;kF>zUrPTQ+sJ&-tIvK7FP`Wo~W3n0@AeIcOTDSVzp9 z5F@PS_U2Y=Yke^6tY5zJDHd;zj7m@3#lT=c#R{Nzb-BNp9m|GPc+)26jm=PDh>4pelw;ro`@J>SWJ>`NNzUbmFir1_RhBkkgq;?}4^>iBI6?&#M(9=#vLi+vAe+hjwh@*~e_0weN z8&}NDlCwFmXS10r)u>CD?;~Av8LQ|kvscENP9Jwl9}c1`>TJbPTHa~V3uds?-AST> zYAf#Iknk4|Y?N#F*kQ0u2Tidf!X}&9Slbk1mY#RS~o2Lh5T3H?=J{Y^lhyg||9)kyCp2*uAXm z+3^j4{Ssij31Ghl7;^{qTY&Kt{JwTUN^7(g?v&|Cs#HOrPn`4ejK_AN!ly(Sc)+%)EnLRaETgC5yO!3n3S_%r$Qcxo!((W{!)PBj)gi zY`BXPBJkVF%pAQVVveyvhaLTUSA-6k-to;bL*FSjHP=O8ERU^-2cuq(t(F+I@2EQJ zTNBkq*rR6_e^y*{XGP@3=B7@`nZSI#SH;tF>ZRAd-Y>+XLnh8O5xzd|m*TMjr$5}y SbrJJQEOH@Z_m56+P4q7yk`L1W literal 3192 zcmZvd%aRmT5QdBC>E7pw zf;l09u!dV(8`~Qz!@RR{`N}022ZNd;!Z{S|b^Ce!cEhBHazOH_iC4Z82*`c<+@*I zJWbnMarP+A@^~UuXo0iG)*mK$((eh~?fBfm;YlywPG0C_dM5-a@+ye=Gn2WoHp2DlZ(!!-~=Cw@G7dcWVHAPx0`_~I%?D< z%nzZOR>mso`qq?pKg_cv(oyt{81$gbV>=4dvZqBY4o+=%k`SMw7l(wuco2t~R>Ka1 ztvjf~jtHBq_s3c)?5Ot7ON=>uSGwPKgRIp6|Igd?(-Cl}i$;HgVE&-7)7zKJpOoMV3E?faqQw<@t%V6%SY?)#zRw<9q>uvtIq;QOKD_gZ3p zV6%SI&G$pckHVWD*sLEuz8`!1Ry{wksb5h)`T=jpS4V9V83}7^8og{hw=V{ zz49>Lf3QDF%-(Y_9_AX1zB9+X^GgyQ(-#ZX>FlZVs)TtouHD~si_iTHv-@)AOuwv)ioyBpPiV)Tnv#?9 zCr@-{^SFMQm!^d0VyQDP#6K;aIe`zhpdB+}F|Z*`@t6V5#D1gHnE^1)q4!M*aXAN_ z-t(N2PS1%;UgSx?ITwF)dQGm_Eze`xV<%6(S@?sY^Ud;gzFC}s-TY5#PmSR74TA%= zAaA}|@c&Q9jr_?8;cU*c?;v^4%7QXw7Kux}-qXt2VAKnp`2@52oY9`ygKs@1pEHWJ zsFgi&m$ah8Th>YpFyf*!gJ5u(t)l1i!FlcJD;Qk#3nfOZ56ZpqSETQfaDVvd%qJK* zbLY<_L9lZR|1Y(pKlH))%rAU;`ju9E$PNAtt=Pcfqch)NS5*mk^q+^g{|KQzw-tr@ zyf2}K%oVk}DK<4~O0eA)n>&3d;hyl|aKDcv;1=tm_S6jyHjDMK_Sk+=EMk8mX-dH1 zP@78<>hirf-0$-e(^6qylo)q2-sKYGo?EIKb-NK`?KO=yDH&)INXhTUz70O Rk@q*M5<0wpRmE$P{{X9^0=WPH diff --git a/Shaders/Depth.vert b/Shaders/Depth.vert index 534dc1b..3f1cd19 100644 --- a/Shaders/Depth.vert +++ b/Shaders/Depth.vert @@ -20,5 +20,6 @@ layout(push_constant) uniform ModelMatrixID { void main() { - gl_Position = camera.projView * modelMatrix[modelMatrixID] * vec4(vPosition, 1.0); + vec4 vWorldSpace = modelMatrix[modelMatrixID] * vec4(vPosition, 1.0); + gl_Position = camera.projView * vWorldSpace; } \ No newline at end of file diff --git a/Shaders/SSAO.frag b/Shaders/SSAO.frag index 9695e31..dea53b7 100644 --- a/Shaders/SSAO.frag +++ b/Shaders/SSAO.frag @@ -72,7 +72,7 @@ float LinearDepth(float d) } void main() { - vec2 noiseScale = vec2(params.screenWidth/params.noiseScale, params.screenHeight/params.noiseScale); + vec2 noiseScale = vec2(params.screenWidth, params.screenHeight) / params.noiseScale; vec3 viewPos = GetViewPosition(fTexcoord); vec3 viewNormal = normalize(cross(dFdx(viewPos.xyz), dFdy(viewPos.xyz))); @@ -92,12 +92,15 @@ void main() vec3 samplePos = TBN * kernels[i]; samplePos = viewPos + samplePos * params.radius; - vec4 offset = vec4(samplePos, 1.0); - offset = camera.proj * offset; - offset.xy /= offset.w; + // Faster and shorter form of: + //vec4 offset = camera.proj * vec4(samplePos, 1.0); + //offset.xyz /= offset.w; - float sampleDepth = GetViewZ(offset.xy * 0.5 + 0.5); + vec2 offsetXY = vec2(camera.proj[0][0] * samplePos.x, camera.proj[1][1] * samplePos.y); + offsetXY /= camera.proj[2][3] * samplePos.z; + float sampleDepth = GetViewZ(offsetXY * 0.5 + 0.5); + float rangeCheck = smoothstep(0.0, 1.0, params.radius / abs(viewPos.z - sampleDepth)); occlusion += (sampleDepth >= samplePos.z + params.bias ? 1.0 : 0.0) * rangeCheck * params.multiplier; } diff --git a/Shaders/SSAO.spv b/Shaders/SSAO.spv index c837bad40942e9e74a8741439fdaa1b9ae682d23..0ed76b3b3b8a574497b86474e4d16d4e4e31cf1c 100644 GIT binary patch literal 12700 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 F=f8RNks|;A literal 12776 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 diff --git a/Source/Core/Eruption.cpp b/Source/Core/Eruption.cpp index 1fea129..1a9bb8a 100644 --- a/Source/Core/Eruption.cpp +++ b/Source/Core/Eruption.cpp @@ -51,12 +51,6 @@ void Eruption::Update() if (m_Input->IsKey(en::Key::LShift) && m_Input->IsKey(en::Key::R, en::InputState::Pressed)) m_Renderer->ReloadBackend(); - else if (m_Input->IsKey(en::Key::Y, en::InputState::Pressed)) - { - m_Renderer->SetVSyncEnabled(false); - //m_Renderer->m_PostProcessParams.antialiasingMode = en::Renderer::AntialiasingMode::None; - } - double deltaTime = m_Renderer->GetFrameTime(); static float targetYaw = m_Camera->m_Yaw; @@ -103,9 +97,9 @@ void Eruption::Render() m_Renderer->Render(); } - void Eruption::CreateExampleScene() { + /* m_AssetManager->LoadMesh("SkullModel", "Models/Skull/Skull.gltf"); m_AssetManager->LoadTexture("SkullAlbedo", "Models/Skull/SkullAlbedo.jpg"); @@ -117,9 +111,9 @@ void Eruption::CreateExampleScene() m_AssetManager->GetMesh("SkullModel")->m_SubMeshes[0].SetMaterial(m_AssetManager->GetMaterial("SkullMaterial")); m_AssetManager->LoadMesh("Sponza", "Models/Sponza/Sponza.gltf"); - + */ m_ExampleScene = en::MakeHandle(); - + /* auto m_Sponza = m_ExampleScene->CreateSceneObject("Sponza", m_AssetManager->GetMesh("Sponza")); m_Sponza->SetScale(glm::vec3(1.2f)); @@ -130,7 +124,7 @@ void Eruption::CreateExampleScene() auto m_Skull = m_ExampleScene->CreateSceneObject("SkullModel", m_AssetManager->GetMesh("SkullModel")); m_Skull->SetPosition(glm::vec3(0, 0.5f, -0.3)); m_Skull->SetRotation(glm::vec3(45.0f, 90.0f, 0.0f)); - + */ @@ -160,7 +154,6 @@ void Eruption::CreateExampleScene() m_Renderer->BindScene(m_ExampleScene); } - void Eruption::Run() { Init(); diff --git a/Source/Editor/UIPanels/SettingsPanel.cpp b/Source/Editor/UIPanels/SettingsPanel.cpp index 227aedd..2046db1 100644 --- a/Source/Editor/UIPanels/SettingsPanel.cpp +++ b/Source/Editor/UIPanels/SettingsPanel.cpp @@ -89,8 +89,6 @@ namespace en ImGui::DragFloat("SSAO Bias", &ssao.bias, 0.05f, 0.0f, 1.0f), "%.2f", ImGuiSliderFlags_AlwaysClamp; ImGui::DragFloat("SSAO Radius", &ssao.radius, 0.1f, 0.0f, 5.0f, "%.1f", ImGuiSliderFlags_AlwaysClamp); ImGui::DragFloat("SSAO Multiplier", &ssao.multiplier, 0.1f, 0.0f, 5.0f, "%.1f", ImGuiSliderFlags_AlwaysClamp); - ImGui::DragFloat("ssao_noise_scale", &ssao._noiseScale, 0.1f, 1.0f, 8.0f, "%.1f", ImGuiSliderFlags_AlwaysClamp); - ImGui::DragInt("ssao_samples", &s, 1.0f, 1.0f, 64.0f, "%.1f", ImGuiSliderFlags_AlwaysClamp); break; diff --git a/Source/Renderer/Renderer.cpp b/Source/Renderer/Renderer.cpp index c2a4226..2022882 100644 --- a/Source/Renderer/Renderer.cpp +++ b/Source/Renderer/Renderer.cpp @@ -436,16 +436,16 @@ namespace en switch (m_Settings.ambientOcclusionQuality) { case QualityLevel::Low: - m_Settings.ambientOcclusion._samples = 16U; - m_Settings.ambientOcclusion._noiseScale = 4.0f; + m_Settings.ambientOcclusion._samples = 8U; + m_Settings.ambientOcclusion._noiseScale = 2.0f; break; case QualityLevel::Medium: - m_Settings.ambientOcclusion._samples = 32U; - m_Settings.ambientOcclusion._noiseScale = 4.0f; + m_Settings.ambientOcclusion._samples = 16U; + m_Settings.ambientOcclusion._noiseScale = 2.0f; break; case QualityLevel::High: - m_Settings.ambientOcclusion._samples = 32U; - m_Settings.ambientOcclusion._noiseScale = 2.0f; + m_Settings.ambientOcclusion._samples = 16U; + m_Settings.ambientOcclusion._noiseScale = 1.0f; break; case QualityLevel::Ultra: m_Settings.ambientOcclusion._samples = 32U; @@ -1239,7 +1239,7 @@ namespace en .enableDepthWrite = !m_Settings.depthPrePass, .blendEnable = false, - .compareOp = m_Settings.depthPrePass ? VK_COMPARE_OP_LESS_OR_EQUAL : VK_COMPARE_OP_LESS, + .compareOp = m_Settings.depthPrePass ? VK_COMPARE_OP_EQUAL : VK_COMPARE_OP_LESS, .polygonMode = VK_POLYGON_MODE_FILL, }; diff --git a/Source/Renderer/Renderer.hpp b/Source/Renderer/Renderer.hpp index 4555743..8cfa729 100644 --- a/Source/Renderer/Renderer.hpp +++ b/Source/Renderer/Renderer.hpp @@ -80,12 +80,12 @@ namespace en float screenWidth = 1920.0f; float screenHeight = 1080.0f; - float radius = 0.85f; + float radius = 0.75f; float bias = 0.025f; - float multiplier = 1.2f; + float multiplier = 1.0f; - uint32_t _samples = 64U; - float _noiseScale = 4.0f; + uint32_t _samples = 16U; + float _noiseScale = 2.0f; }; void SetVSyncEnabled(const bool enabled); diff --git a/Source/Renderer/Swapchain.cpp b/Source/Renderer/Swapchain.cpp index 1bb5131..68235c5 100644 --- a/Source/Renderer/Swapchain.cpp +++ b/Source/Renderer/Swapchain.cpp @@ -27,7 +27,7 @@ namespace en .imageColorSpace = surfaceFormat.colorSpace, .imageExtent = extent, .imageArrayLayers = 1U, - .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, .preTransform = swapChainSupport.capabilities.currentTransform, .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, .presentMode = presentMode, @@ -80,7 +80,7 @@ namespace en { UseContext(); - for (auto& imageView : m_ImageViews) + for (const auto& imageView : m_ImageViews) vkDestroyImageView(ctx.m_LogicalDevice, imageView, nullptr); vkDestroySwapchainKHR(ctx.m_LogicalDevice, m_Swapchain, nullptr); diff --git a/imgui.ini b/imgui.ini index 4bdc090..45d530c 100644 --- a/imgui.ini +++ b/imgui.ini @@ -1,6 +1,6 @@ [Window][DockSpace Demo] Pos=0,33 -Size=2560,1336 +Size=1920,1047 Collapsed=0 [Window][Debug##Default] @@ -9,14 +9,14 @@ Size=400,400 Collapsed=0 [Window][Asset Manager] -Pos=273,732 -Size=1400,348 +Pos=273,1021 +Size=2287,348 Collapsed=0 DockId=0x00000008,0 [Window][Camera Properties] -Pos=0,909 -Size=271,460 +Pos=0,720 +Size=271,360 Collapsed=0 DockId=0x00000004,0 @@ -26,19 +26,19 @@ Size=405,428 Collapsed=0 [Window][Debug Menu] -Pos=1683,398 +Pos=1387,230 Size=322,194 Collapsed=0 [Window][Scene Hierarchy] Pos=0,33 -Size=271,874 +Size=271,685 Collapsed=0 DockId=0x00000003,0 [Window][Inspector] -Pos=2315,33 -Size=245,1336 +Pos=1675,33 +Size=245,1047 Collapsed=0 DockId=0x00000006,0 @@ -58,12 +58,12 @@ Size=409,172 Collapsed=0 [Window][Settings] -Pos=1621,648 -Size=500,449 +Pos=1441,546 +Size=500,504 Collapsed=0 [Docking][Data] -DockSpace ID=0x1315676B Window=0x4647B76E Pos=0,33 Size=2560,1336 Split=X +DockSpace ID=0x1315676B Window=0x4647B76E Pos=0,33 Size=1920,1047 Split=X DockNode ID=0x00000005 Parent=0x1315676B SizeRef=1673,1047 Split=X DockNode ID=0x00000001 Parent=0x00000005 SizeRef=271,1047 Split=Y Selected=0x2E9237F7 DockNode ID=0x00000003 Parent=0x00000001 SizeRef=499,496 Selected=0x2E9237F7