diff --git a/.idea/.name b/.idea/.name index d8662b5..f081644 100644 --- a/.idea/.name +++ b/.idea/.name @@ -1 +1 @@ -OpenVulkano +openVulkanoCpp \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 5039b4b..9cf720b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,8 +48,15 @@ if(IOS) add_executable(openVulkanoCpp examples/main.m ${resources}) else() file(GLOB_RECURSE sources CONFIGURE_DEPENDS "openVulkanoCpp/*.h" "openVulkanoCpp/*.c" "openVulkanoCpp/*.hpp" "openVulkanoCpp/*.cpp" "examples/*.hpp" "examples/*.cpp") - add_executable(openVulkanoCpp examples/main.cpp) + file(GLOB SHADER_SRC_FILES ${ROOT_FOLDER}/openVulkanoCpp/Shader/*) + list(FILTER SHADER_SRC_FILES EXCLUDE REGEX ".*\\.(hpp|cpp)$") + add_executable(openVulkanoCpp examples/main.cpp ${SHADER_SRC_FILES}) + if (MSVC) + set_property(GLOBAL PROPERTY USE_FOLDERS ON) + source_group("Shaders" FILES ${SHADER_SRC_FILES}) + endif() endif() + FilterPlatformPaths(sources) SetWarningSettings(openVulkanoCpp) set_property(TARGET openVulkanoCpp PROPERTY CXX_STANDARD 20) diff --git a/cmake/SetupVulkan.cmake b/cmake/SetupVulkan.cmake index a880584..24ed595 100644 --- a/cmake/SetupVulkan.cmake +++ b/cmake/SetupVulkan.cmake @@ -5,6 +5,11 @@ function(SetupVulkan TARGET) else () find_package(Vulkan REQUIRED) target_link_libraries(${TARGET} PRIVATE Vulkan::Vulkan) + find_package(Vulkan OPTIONAL_COMPONENTS shaderc_combined) + if (Vulkan_shaderc_combined_FOUND) + target_link_libraries(${TARGET} PRIVATE Vulkan::shaderc_combined) + target_compile_definitions(${TARGET} PRIVATE HAS_SHADERC) + endif () endif () target_include_directories(${TARGET} PUBLIC ${Vulkan_INCLUDE_DIR}) @@ -17,4 +22,5 @@ function(SetupVulkan TARGET) find_package(XCB REQUIRED) target_link_libraries(${TARGET} PRIVATE ${XCB_LIBRARIES}) endif() -endfunction() \ No newline at end of file +endfunction() + diff --git a/examples/ExampleApps/BillboardExampleApp.cpp b/examples/ExampleApps/BillboardExampleApp.cpp new file mode 100644 index 0000000..b579a17 --- /dev/null +++ b/examples/ExampleApps/BillboardExampleApp.cpp @@ -0,0 +1,160 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "BillboardExampleApp.hpp" +#include "Scene/Scene.hpp" +#include "Scene/Shader/Shader.hpp" +#include "Scene/Geometry.hpp" +#include "Scene/GeometryFactory.hpp" +#include "Scene/Material.hpp" +#include "Scene/Vertex.hpp" +#include "Scene/SimpleDrawable.hpp" +#include "Scene/UI/PerformanceInfo.hpp" +#include "Scene/UniformBuffer.hpp" +#include "Input/InputManager.hpp" +#include "Host/GraphicsAppManager.hpp" +#include "Host/GLFW/WindowGLFW.hpp" +#include "Math/Math.hpp" +#include "Base/EngineConfiguration.hpp" +#include "Controller/FreeCamCameraController.hpp" + +namespace OpenVulkano +{ + using namespace Scene; + using namespace Input; + using namespace Math; + + class BillboardExampleAppImpl final : public BillboardExampleApp + { + public: + + struct BillboardControlBlock + { + Math::Vector2f quadSize; + bool isFixedSize; + }; + + void Init() override + { + auto engineConfig = OpenVulkano::EngineConfiguration::GetEngineConfiguration(); + engineConfig->SetNumThreads(4); + engineConfig->SetPreferFramebufferFormatSRGB(false); + + std::srand(1); // Fix seed for random numbers + m_scene.Init(); + m_cam.Init(70, 16, 9, 0.1f, 100); + m_scene.SetCamera(&m_cam); + + m_quadBillboardShader.AddShaderProgram(OpenVulkano::ShaderProgramType::VERTEX, "Shader/billboardFromSinglePoint"); + m_quadBillboardShader.AddShaderProgram(OpenVulkano::ShaderProgramType::GEOMETRY, "Shader/billboardFromSinglePoint"); + m_quadBillboardShader.AddShaderProgram(OpenVulkano::ShaderProgramType::FRAGMENT, "Shader/basicTexture"); + m_quadBillboardShader.AddVertexInputDescription(OpenVulkano::Vertex::GetVertexInputDescription()); + m_quadBillboardShader.AddDescriptorSetLayoutBinding(Texture::DESCRIPTOR_SET_LAYOUT_BINDING); + m_quadBillboardShader.AddDescriptorSetLayoutBinding(UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING); + m_quadBillboardShader.topology = Topology::POINT_LIST; + + m_shader.AddShaderProgram(OpenVulkano::ShaderProgramType::VERTEX, "Shader/billboard"); + m_shader.AddShaderProgram(OpenVulkano::ShaderProgramType::FRAGMENT, "Shader/basic"); + m_shader.AddVertexInputDescription(OpenVulkano::Vertex::GetVertexInputDescription()); + m_shader.AddDescriptorSetLayoutBinding(Texture::DESCRIPTOR_SET_LAYOUT_BINDING); + m_shader.AddDescriptorSetLayoutBinding(UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING); + m_shader.cullMode = CullMode::NONE; + + constexpr int quadsCnt = 7; + constexpr int otherCnt = 2; + constexpr int cntDrawables = quadsCnt + otherCnt; + + m_bbContolBlock.quadSize = { 100.f, 100.f }; + m_bbContolBlock.isFixedSize = false; + m_uniBuffer.Init(sizeof(BillboardControlBlock), &m_bbContolBlock); + m_uniBuffer.setId = 3; + m_drawablesPool.resize(cntDrawables); + m_nodesPool.resize(cntDrawables); + m_geo.reserve(cntDrawables); + m_texturedMat.texture = &Texture::PLACEHOLDER; + + for (uint32_t i = 0; i < cntDrawables; i++) + { + Geometry* geo = nullptr; + m_nodesPool[i].Init(); + if (i < quadsCnt) + { + geo = new Geometry(); + geo->Init(1, 0); + geo->vertices[0].position = glm::vec3(1 + i, i, 0); + if (i >= 1 && i <= 3) + geo->vertices[0].color = glm::vec4(1, 1, 1, 1); + else + geo->vertices[0].color = glm::vec4(1, 0, 0, 1); + m_nodesPool[i].SetMatrix(Math::Utils::translate(glm::mat4x4(1.f), Vector3f(-5 + std::rand() % 5, -5 + std::rand() % 5, std::rand() % 5))); + m_drawablesPool[i].Init(&m_quadBillboardShader, geo, &m_texturedMat, &m_uniBuffer); + } + else + { + geo = GeometryFactory::MakePyramid(1, 1, glm::vec4(0, 1, 0, 1)); + m_nodesPool[i].SetMatrix(Math::Utils::translate(glm::mat4x4(1.f), Vector3f(-5 + std::rand() % 5, -5 + std::rand() % 5, -std::rand() % 10))); + m_drawablesPool[i].Init(&m_shader, geo, &m_mat, &m_uniBuffer); + } + m_geo.push_back(geo); + m_scene.GetRoot()->AddChild(&m_nodesPool[i]); + m_nodesPool[i].AddDrawable(&m_drawablesPool[i]); + } + + GetGraphicsAppManager()->GetRenderer()->SetScene(&m_scene); + + m_camController.Init(&m_cam); + m_camController.SetDefaultKeybindings(); + m_camController.SetPosition({ 0, 0, 5 }); + m_camController.SetBoostFactor(5); + + std::shared_ptr m_perfInfo = + std::make_shared(); + m_ui.AddElement(m_perfInfo); + GetGraphicsAppManager()->GetRenderer()->SetActiveUi(&m_ui); + } + + void Tick() override + { + m_camController.Tick(); + } + + void Close() override + { + for (Geometry* g: m_geo) + { + delete g; + } + } + + private: + OpenVulkano::Scene::Scene m_scene; + BillboardControlBlock m_bbContolBlock; + PerspectiveCamera m_cam; + UniformBuffer m_uniBuffer; + OpenVulkano::FreeCamCameraController m_camController; + Material m_mat; + Material m_texturedMat; + Shader m_shader; + Shader m_quadBillboardShader; + std::vector m_drawablesPool; + std::vector m_nodesPool; + Vector3f_SIMD m_position = { 0, 0, -10 }; + OpenVulkano::Scene::UI::SimpleUi m_ui; + std::vector m_geo; + std::shared_ptr m_perfInfo; + }; + + IGraphicsApp* BillboardExampleApp::Create() { return new BillboardExampleAppImpl(); } + + std::unique_ptr BillboardExampleApp::CreateUnique() + { + return std::make_unique(); + } + +} + +#pragma clang diagnostic pop +#pragma clang diagnostic pop \ No newline at end of file diff --git a/examples/ExampleApps/BillboardExampleApp.hpp b/examples/ExampleApps/BillboardExampleApp.hpp new file mode 100644 index 0000000..fa1c215 --- /dev/null +++ b/examples/ExampleApps/BillboardExampleApp.hpp @@ -0,0 +1,27 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "Base/IGraphicsApp.hpp" +#include + +namespace OpenVulkano +{ + class BillboardExampleApp : public IGraphicsApp + { + public: + static IGraphicsApp* Create(); + + static std::unique_ptr CreateUnique(); + + [[nodiscard]] std::string GetAppName() const final + { return "Billboard ExampleApp"; } + + [[nodiscard]] OpenVulkano::Version GetAppVersion() const final + { return {"v1.0"}; } + }; +} \ No newline at end of file diff --git a/examples/main.cpp b/examples/main.cpp index 18a8431..388087e 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -8,6 +8,7 @@ #include "ExampleApps/CubesExampleApp.hpp" #include "ExampleApps/MovingCubeApp.hpp" #include "ExampleApps/TexturedCubeExampleApp.hpp" +#include "ExampleApps/BillboardExampleApp.hpp" #include #include @@ -25,6 +26,7 @@ int main(int argc, char** argv) "Cubes Example App", "Moving Cube Example App", "Textured Cube Example App", + "Billboard Example App" }; int selectedExample = 0; @@ -41,6 +43,7 @@ int main(int argc, char** argv) case 0: app = CubesExampleApp::CreateUnique(); break; case 1: app = MovingCubeApp::CreateUnique(); break; case 2: app = TexturedCubeExampleApp::CreateUnique(); break; + case 3: app = BillboardExampleApp::CreateUnique(); break; default: throw std::runtime_error("Invalid menu selection!"); break; } diff --git a/openVulkanoCpp/Base/IGraphicsAppManager.hpp b/openVulkanoCpp/Base/IGraphicsAppManager.hpp index 536c6b5..14cf156 100644 --- a/openVulkanoCpp/Base/IGraphicsAppManager.hpp +++ b/openVulkanoCpp/Base/IGraphicsAppManager.hpp @@ -16,6 +16,7 @@ namespace OpenVulkano virtual RenderAPI::RenderApi GetRenderApi() const = 0; virtual IGraphicsApp* GetGraphicsApp() const = 0; virtual IRenderer* GetRenderer() const = 0; + virtual IWindow* GetWindow() const = 0; virtual bool IsRunning() const = 0; virtual bool IsPaused() const = 0; virtual void Stop() = 0; diff --git a/openVulkanoCpp/Base/Utils.cpp b/openVulkanoCpp/Base/Utils.cpp index d3f8fd1..4987888 100644 --- a/openVulkanoCpp/Base/Utils.cpp +++ b/openVulkanoCpp/Base/Utils.cpp @@ -7,9 +7,9 @@ #include "Utils.hpp" #ifdef _MSC_VER -#include + #include #else -#include + #include #endif #include #include @@ -57,11 +57,11 @@ namespace OpenVulkano #ifdef _MSC_VER return (uint64_t)::GetThreadId(::GetCurrentThread()); #else - return (uint64_t)pthread_self(); + return (uint64_t) pthread_self(); #endif } - Array Utils::ReadFile(const std::string& filePath, bool emptyOnMissing) + Array Utils::ReadFile(const std::string& filePath, bool emptyOnMissing, bool nullTerminateString) { std::ifstream file(filePath, std::ios::ate | std::ios::binary); if (!file.is_open()) @@ -70,9 +70,10 @@ namespace OpenVulkano throw std::runtime_error("Failed to open file '" + filePath + "'!"); } const size_t fileSize = static_cast(file.tellg()); - Array data(fileSize); + Array data(fileSize + nullTerminateString); file.seekg(0); file.read(data.Data(), fileSize); + if (nullTerminateString) data[fileSize] = '\0'; file.close(); return data; } diff --git a/openVulkanoCpp/Base/Utils.hpp b/openVulkanoCpp/Base/Utils.hpp index a29ea03..391a322 100644 --- a/openVulkanoCpp/Base/Utils.hpp +++ b/openVulkanoCpp/Base/Utils.hpp @@ -159,7 +159,8 @@ namespace OpenVulkano return subs; } - static Array ReadFile(const std::string& filePath, bool emptyOnMissing = false); + static Array ReadFile(const std::string& filePath, bool emptyOnMissing = false, + bool nullTerminateString = false); template static int GetUniqueTypeId() diff --git a/openVulkanoCpp/Controller/FreeCamCameraController.cpp b/openVulkanoCpp/Controller/FreeCamCameraController.cpp index 89342a1..393514f 100644 --- a/openVulkanoCpp/Controller/FreeCamCameraController.cpp +++ b/openVulkanoCpp/Controller/FreeCamCameraController.cpp @@ -32,8 +32,7 @@ namespace OpenVulkano vec = Math::Utils::normalize(vec); } - float timeScale = CURRENT_FRAME.frameTime; //TODO - + float timeScale = 5 * CURRENT_FRAME.frameTime; //TODO vec = vec * timeScale * 3.0f; // scale vector if (input->GetButton(m_actionBoost)) { diff --git a/openVulkanoCpp/Controller/FreeCamCameraController.hpp b/openVulkanoCpp/Controller/FreeCamCameraController.hpp index ef163c1..2fc33ba 100644 --- a/openVulkanoCpp/Controller/FreeCamCameraController.hpp +++ b/openVulkanoCpp/Controller/FreeCamCameraController.hpp @@ -18,7 +18,7 @@ namespace OpenVulkano class FreeCamCameraController final : public CameraController { - float m_yaw = 0, m_pitch = 0, m_boostFactor = 2; + float m_yaw = 0, m_pitch = 0, m_boostFactor = 1.5; Math::Vector3f_SIMD m_position = { 0, 0, 0 }; Input::InputAction* m_actionForward; diff --git a/openVulkanoCpp/Host/GLFW/InputMappingGLFW.hpp b/openVulkanoCpp/Host/GLFW/InputMappingGLFW.hpp index eadc109..320fdfb 100644 --- a/openVulkanoCpp/Host/GLFW/InputMappingGLFW.hpp +++ b/openVulkanoCpp/Host/GLFW/InputMappingGLFW.hpp @@ -48,7 +48,7 @@ namespace OpenVulkano::GLFW { remappedKey = i - 320; } - else if (i >= GLFW_KEY_NUM_LOCK) + else if (i == GLFW_KEY_NUM_LOCK) { remappedKey = 17; } diff --git a/openVulkanoCpp/Host/GraphicsAppManager.hpp b/openVulkanoCpp/Host/GraphicsAppManager.hpp index 9e53818..5d4b627 100644 --- a/openVulkanoCpp/Host/GraphicsAppManager.hpp +++ b/openVulkanoCpp/Host/GraphicsAppManager.hpp @@ -50,6 +50,8 @@ namespace OpenVulkano [[nodiscard]] IRenderer* GetRenderer() const override { return renderer.get(); } + [[nodiscard]] IWindow* GetWindow() const override { return window; } + [[nodiscard]] bool IsRunning() const override { return running; } [[nodiscard]] bool IsPaused() const override { return paused; } diff --git a/openVulkanoCpp/Image/Image.hpp b/openVulkanoCpp/Image/Image.hpp new file mode 100644 index 0000000..b440de3 --- /dev/null +++ b/openVulkanoCpp/Image/Image.hpp @@ -0,0 +1,28 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "Data/Containers/Array.hpp" +#include "Math/Math.hpp" +#include "Scene/DataFormat.hpp" + +namespace OpenVulkano::Image +{ + enum class ImageFileType + { + JPEG, + PNG, + BMP, + }; + + struct Image + { + Math::Vector3ui resolution; + DataFormat dataFormat; + Array data; + }; +} \ No newline at end of file diff --git a/openVulkanoCpp/Image/ImageLoader.hpp b/openVulkanoCpp/Image/ImageLoader.hpp new file mode 100644 index 0000000..d3a288f --- /dev/null +++ b/openVulkanoCpp/Image/ImageLoader.hpp @@ -0,0 +1,24 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "Image.hpp" +#include +#include +#include + +namespace OpenVulkano::Image +{ + class IImageLoader + { + public: + virtual ~IImageLoader() = default; + + virtual std::unique_ptr loadFromFile(const std::string& filePath) = 0; + virtual std::unique_ptr loadFromMemory(const std::vector& buffer) = 0; + }; +} \ No newline at end of file diff --git a/openVulkanoCpp/Image/ImageLoaderJpeg.cpp b/openVulkanoCpp/Image/ImageLoaderJpeg.cpp new file mode 100644 index 0000000..f9daec4 --- /dev/null +++ b/openVulkanoCpp/Image/ImageLoaderJpeg.cpp @@ -0,0 +1,72 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "ImageLoaderJpeg.hpp" +#include +#include +#include + +#if __has_include("turbojpeg.h") + #include +#else + #define STB_IMAGE_IMPLEMENTATION + #include +#endif + +namespace OpenVulkano::Image +{ + std::unique_ptr ImageLoaderJpeg::loadFromFile(const std::string& filePath) + { + std::ifstream file(filePath, std::ios::binary); + if (!file) { throw std::runtime_error("Could not open file: " + filePath); } + std::vector buffer((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + return loadJpeg(buffer.data(), buffer.size()); + } + + std::unique_ptr ImageLoaderJpeg::loadFromMemory(const std::vector& buffer) + { + return loadJpeg(buffer.data(), buffer.size()); + } + + std::unique_ptr ImageLoaderJpeg::loadJpeg(const uint8_t* data, size_t size) + { + Image result; + + int rows, cols; +#if __has_include("turbojpeg.h") + { + unsigned char* compressedImage = const_cast(data); + + int jpegSubsamp; + tjhandle jpegDecompressor = tjInitDecompress(); + tjDecompressHeader2(jpegDecompressor, compressedImage, size, &cols, &rows, &jpegSubsamp); + + const int channels = 4; + result.data = OpenVulkano::Array(cols * rows * channels); + result.dataFormat = OpenVulkano::DataFormat::R8G8B8A8_UINT; + + tjDecompress2(jpegDecompressor, compressedImage, size, result.data.Data(), cols, 0 /*pitch*/, rows, + TJPF_RGBA, TJFLAG_FASTDCT); + tjDestroy(jpegDecompressor); + } +#else + { + int channels; + uint8_t* pixelData = stbi_load_from_memory(data, size, &cols, &rows, &channels, 3); + result.data = OpenVulkano::Array(cols * rows * channels); + result.dataFormat = channels == 3 ? OpenVulkano::DataFormat::R8G8B8_UINT : + OpenVulkano::DataFormat::R8G8B8A8_UINT; + std::memcpy(result.data.Data(), pixelData, result.data.Size()); + stbi_image_free(pixelData); + } +#endif + result.resolution.x = cols; + result.resolution.y = rows; + result.resolution.z = 1; + + return std::make_unique(std::move(result)); + } +} \ No newline at end of file diff --git a/openVulkanoCpp/Image/ImageLoaderJpeg.hpp b/openVulkanoCpp/Image/ImageLoaderJpeg.hpp new file mode 100644 index 0000000..5890ac9 --- /dev/null +++ b/openVulkanoCpp/Image/ImageLoaderJpeg.hpp @@ -0,0 +1,22 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "ImageLoader.hpp" + +namespace OpenVulkano::Image +{ + class ImageLoaderJpeg : public IImageLoader + { + public: + std::unique_ptr loadFromFile(const std::string& filePath) override; + std::unique_ptr loadFromMemory(const std::vector& buffer) override; + + private: + std::unique_ptr loadJpeg(const uint8_t* data, size_t size); + }; +} \ No newline at end of file diff --git a/openVulkanoCpp/Input/InputManager.cpp b/openVulkanoCpp/Input/InputManager.cpp index b17ca6a..5e36010 100644 --- a/openVulkanoCpp/Input/InputManager.cpp +++ b/openVulkanoCpp/Input/InputManager.cpp @@ -69,7 +69,7 @@ namespace OpenVulkano::Input for (const KeyBinding binding : action->GetKeys()) { if (binding.key.GetInputDeviceType() != device->GetType()) continue; - if (device->GetButton(binding.key)) return true; + return device->GetButton(binding.key); } } return false; @@ -80,7 +80,7 @@ namespace OpenVulkano::Input for(const InputDevice* device : devices) { if (key.GetInputDeviceType() != device->GetType()) continue; - if (device->GetButton(key)) return true; + return device->GetButton(key); } return false; } diff --git a/openVulkanoCpp/Scene/SimpleDrawable.cpp b/openVulkanoCpp/Scene/SimpleDrawable.cpp index 7bc3fac..0920971 100644 --- a/openVulkanoCpp/Scene/SimpleDrawable.cpp +++ b/openVulkanoCpp/Scene/SimpleDrawable.cpp @@ -9,19 +9,21 @@ namespace OpenVulkano::Scene { - void SimpleDrawable::Init(Shader* shader, Geometry* mesh, Material* material) + void SimpleDrawable::Init(Shader* shader, Geometry* mesh, Material* material, UniformBuffer* uniBuffer) { - if (m_mesh || m_material) throw std::runtime_error("Drawable is already initialized."); + if (m_mesh || m_material || m_uniBuffer) throw std::runtime_error("Drawable is already initialized."); m_mesh = mesh; m_material = material; + m_uniBuffer = uniBuffer; SetShader(shader); } void SimpleDrawable::Init(SimpleDrawable* drawable) { - if (m_mesh || m_material) throw std::runtime_error("Drawable is already initialized."); + if (m_mesh || m_material || m_uniBuffer) throw std::runtime_error("Drawable is already initialized."); m_mesh = drawable->m_mesh; m_material = drawable->m_material; + m_uniBuffer = drawable->m_uniBuffer; SetShader(drawable->GetShader()); } } \ No newline at end of file diff --git a/openVulkanoCpp/Scene/SimpleDrawable.hpp b/openVulkanoCpp/Scene/SimpleDrawable.hpp index 49908d2..0f5aa13 100644 --- a/openVulkanoCpp/Scene/SimpleDrawable.hpp +++ b/openVulkanoCpp/Scene/SimpleDrawable.hpp @@ -12,20 +12,22 @@ namespace OpenVulkano::Scene { class Geometry; class Material; + class UniformBuffer; class SimpleDrawable final : public Drawable { Geometry* m_mesh = nullptr; Material* m_material = nullptr; + UniformBuffer* m_uniBuffer = nullptr; public: - SimpleDrawable() : Drawable(DrawEncoder::GetDrawEncoder()) - {} + SimpleDrawable() : Drawable(DrawEncoder::GetDrawEncoder()) {} explicit SimpleDrawable(const SimpleDrawable* toCopy) - : Drawable(DrawEncoder::GetDrawEncoder()) - , m_mesh(toCopy->m_mesh) - , m_material(toCopy->m_material) + : Drawable(DrawEncoder::GetDrawEncoder()) + , m_mesh(toCopy->m_mesh) + , m_material(toCopy->m_material) + , m_uniBuffer(toCopy->m_uniBuffer) { SetShader(toCopy->GetShader()); } @@ -35,12 +37,14 @@ namespace OpenVulkano::Scene //if (m_mesh) SimpleDrawable::Close(); } - void Init(Shader* shader, Geometry* mesh, Material* material); + void Init(Shader* shader, Geometry* mesh, Material* material, UniformBuffer* uniBuffer = nullptr); void Init(SimpleDrawable* drawable); [[nodiscard]] Geometry* GetMesh() const { return m_mesh; } [[nodiscard]] Material* GetMaterial() const { return m_material; } + + [[nodiscard]] UniformBuffer* GetBuffer() const { return m_uniBuffer; } }; } diff --git a/openVulkanoCpp/Scene/UniformBuffer.hpp b/openVulkanoCpp/Scene/UniformBuffer.hpp index 06cf9ef..1551d3f 100644 --- a/openVulkanoCpp/Scene/UniformBuffer.hpp +++ b/openVulkanoCpp/Scene/UniformBuffer.hpp @@ -16,18 +16,19 @@ namespace OpenVulkano::Scene static constexpr inline DescriptorSetLayoutBinding DESCRIPTOR_SET_LAYOUT_BINDING = { 0, DescriptorSetLayoutBinding::Type::TYPE_UNIFORM_BUFFER, 1, ShaderProgramType::ALL_GRAPHICS }; DescriptorSetLayoutBinding binding; - uint32_t setId = 2; + uint32_t setId; ICloseable* renderBuffer = nullptr; size_t size = 0; const void* data = nullptr; UpdateFrequency updateFrequency = UpdateFrequency::Never; bool updated = true; - void Init(size_t size, const void* data, const DescriptorSetLayoutBinding& binding = DESCRIPTOR_SET_LAYOUT_BINDING) + void Init(size_t size, const void* data, uint32_t setId = 2, const DescriptorSetLayoutBinding& binding = DESCRIPTOR_SET_LAYOUT_BINDING) { this->size = size; this->data = data; this->binding = binding; + this->setId = setId; } UpdateFrequency GetUpdateFrequency() const { return updateFrequency; } diff --git a/openVulkanoCpp/Shader/ShaderCompiler.cpp b/openVulkanoCpp/Shader/ShaderCompiler.cpp new file mode 100644 index 0000000..e70a3f2 --- /dev/null +++ b/openVulkanoCpp/Shader/ShaderCompiler.cpp @@ -0,0 +1,131 @@ +#if defined(HAS_SHADERC) + +#include "ShaderCompiler.hpp" + +#include "Base/Logger.hpp" +#include + +#include +#include + +namespace OpenVulkano +{ + + class ShaderIncluder : public shaderc::CompileOptions::IncluderInterface + { + struct IncludeData + { + shaderc_include_result result = {}; + std::string m_fullPath; + Array m_content; + }; + + public: + ShaderIncluder(const std::string& path); + ~ShaderIncluder() override = default; + + shaderc_include_result* GetInclude(const char* requestedSource, shaderc_include_type type, + const char* requestingSource, uint64_t includeDepth) override; + void ReleaseInclude(shaderc_include_result* data) override; + + private: + std::string ResolveInclude(const std::string& requestedSource) const; + + private: + std::string m_includePath; + }; + + shaderc_shader_kind CheckStage(const std::string& extensionName) + { + static std::map stageMap = { + { "vert", shaderc_glsl_vertex_shader }, { "frag", shaderc_glsl_fragment_shader }, + { "comp", shaderc_glsl_compute_shader }, { "geom", shaderc_glsl_geometry_shader }, + { "tesc", shaderc_glsl_tess_control_shader }, { "tese", shaderc_glsl_tess_evaluation_shader }, + { "rgen", shaderc_glsl_raygen_shader }, { "rint", shaderc_glsl_intersection_shader }, + { "rahit", shaderc_glsl_anyhit_shader }, { "rchit", shaderc_glsl_closesthit_shader }, + { "rmiss", shaderc_glsl_miss_shader }, { "rcall", shaderc_glsl_callable_shader }, + { "task", shaderc_glsl_task_shader }, { "mesh", shaderc_glsl_mesh_shader } + }; + + auto it = stageMap.find(extensionName); + if (it != stageMap.end()) return it->second; + else throw std::runtime_error("Failed to find shader stage for extension '" + extensionName + "'!"); + } + + Array ShaderCompiler::CompileGLSLToSpirv(const std::string& absPath, bool hasIncludes, + const std::string& incPath, const std::string& entryPoint) + { + Array file = Utils::ReadFile(absPath, false, true); + + shaderc::Compiler shaderCompiler; + shaderc::CompileOptions options; + + if (hasIncludes && incPath.size() > 0) { options.SetIncluder(std::make_unique(incPath)); } + options.SetSourceLanguage(shaderc_source_language_glsl); + options.SetSuppressWarnings(); + + auto extensionSplit = Utils::SplitAtLastOccurrence(absPath, '.'); + + shaderc::PreprocessedSourceCompilationResult preResult = + shaderCompiler.PreprocessGlsl(file.Data(), CheckStage(extensionSplit.second), entryPoint.c_str(), options); + + #if defined(_DEBUG) + std::string test = static_cast(preResult.begin(), preResult.end()); + // printf("Preprocessed shader: %s\n\n\n", test.c_str()); // Works fine + // Logger::APP->info("Preprocessed shader: {0}", test); gives error + #endif + + if (preResult.GetCompilationStatus() != shaderc_compilation_status_success) + { + throw std::runtime_error("Failed preprocessing shader. Reason: " + preResult.GetErrorMessage()); + } + + shaderc::CompilationResult result = shaderCompiler.CompileGlslToSpv( + static_cast(preResult.begin()), CheckStage(extensionSplit.second), "", options); + + if (result.GetCompilationStatus() != shaderc_compilation_status_success) + { + throw std::runtime_error("Failed compiling shader. Reason: " + result.GetErrorMessage()); + } + + Array returnResult(result.cend() - result.cbegin()); + // Copy the data to the return array + std::copy(result.begin(), result.end(), returnResult.Data()); + return returnResult; + } + + ShaderIncluder::ShaderIncluder(const std::string& path) : m_includePath(path) {} + + shaderc_include_result* ShaderIncluder::GetInclude(const char* requestedSource, shaderc_include_type type, + const char* requestingSource, uint64_t includeDepth) + { + IncludeData* includeData = new IncludeData(); + includeData->m_fullPath = ResolveInclude(requestedSource); + + // Do not null terminate the string + includeData->m_content = Utils::ReadFile(includeData->m_fullPath, false, false); + + shaderc_include_result* result = &includeData->result; + result->content = includeData->m_content.Data(); + result->content_length = includeData->m_content.Size(); + result->source_name = includeData->m_fullPath.data(); + result->source_name_length = includeData->m_fullPath.size(); + result->user_data = includeData; + + return result; + } + + void ShaderIncluder::ReleaseInclude(shaderc_include_result* data) + { + delete static_cast(data->user_data); + } + + std::string ShaderIncluder::ResolveInclude(const std::string& requestedSource) const + { + // Check if the file exists in the include path + std::string path = m_includePath + requestedSource; + if (std::filesystem::exists(path)) return path; + else throw std::runtime_error("Failed to resolve include '" + requestedSource + "'!"); + } +} +#endif diff --git a/openVulkanoCpp/Shader/ShaderCompiler.hpp b/openVulkanoCpp/Shader/ShaderCompiler.hpp new file mode 100644 index 0000000..4f42997 --- /dev/null +++ b/openVulkanoCpp/Shader/ShaderCompiler.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "Base/Utils.hpp" +#include "Base/Wrapper.hpp" + +namespace OpenVulkano +{ + class ShaderCompiler + { + public: + /** + * @param absPath - absolute path of the shader that needs to be compiled + * @param incPath - include path that will be used to resolve includes + * @param entryPoint - the name of the void function in the shader + * @param shaderStage - type of the shader that needs to be compiled + */ + static Array CompileGLSLToSpirv(const std::string& absPath, bool hasIncludes, + const std::string& incPath = std::string(), const std::string& entryPoint = "main") +#if defined(HAS_SHADERC) + ; +#else + { throw std::runtime_error("Shader compiler is not available on this platform"); } +#endif + }; +} \ No newline at end of file diff --git a/openVulkanoCpp/Shader/basicTexture.frag b/openVulkanoCpp/Shader/basicTexture.frag index acf99a1..d0095cc 100644 --- a/openVulkanoCpp/Shader/basicTexture.frag +++ b/openVulkanoCpp/Shader/basicTexture.frag @@ -3,6 +3,7 @@ layout(location = 0) in vec4 color; layout(location = 1) in vec2 texCoord; layout(location = 0) out vec4 outColor; + layout(set = 2, binding = 0) uniform sampler2D texSampler; void main() diff --git a/openVulkanoCpp/Shader/billboard.vert b/openVulkanoCpp/Shader/billboard.vert new file mode 100644 index 0000000..261d543 --- /dev/null +++ b/openVulkanoCpp/Shader/billboard.vert @@ -0,0 +1,58 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 pos; +layout(location = 4) in vec3 textureCoordinates; +layout(location = 5) in vec4 color; +layout(location = 0) out vec4 outColor; +layout(location = 1) out vec2 outTexture; + +layout(set = 0, binding = 0) uniform NodeData +{ + mat4 world; +} node; + +layout(set = 1, binding = 0) uniform CameraData +{ + mat4 viewProjection; + mat4 view; + mat4 projection; + vec4 camPos; +} cam; + +layout(set = 3, binding = 0) uniform BillboardData +{ + vec2 size; + bool isFixedSize; +} billboardInfo; + +void main() { + if (!billboardInfo.isFixedSize) + { + mat4 mv = cam.view * node.world; + + mv[0][0] = 1; + mv[0][1] = 0; + mv[0][2] = 0; + + mv[1][0] = 0; + mv[1][1] = 1; + mv[1][2] = 0; + + mv[2][0] = 0; + mv[2][1] = 0; + mv[2][2] = 1; + + gl_Position = cam.projection * mv * vec4(pos, 1); + } + else + { + vec4 billboardPos = vec4(0.5, 0.5, 0.5, 1); + vec4 viewPos = cam.view * node.world * billboardPos; + float dist = -viewPos.z; + gl_Position = cam.projection * (viewPos + vec4(pos.xy*dist*0.2,0,0)); + } + outColor = color; + outTexture = textureCoordinates.xy; +} diff --git a/openVulkanoCpp/Shader/billboardFromSinglePoint.geom b/openVulkanoCpp/Shader/billboardFromSinglePoint.geom new file mode 100644 index 0000000..13e60f0 --- /dev/null +++ b/openVulkanoCpp/Shader/billboardFromSinglePoint.geom @@ -0,0 +1,96 @@ +#version 450 + +layout(points) in; +layout(triangle_strip, max_vertices = 4) out; + +layout(set = 0, binding = 0) uniform NodeData +{ + mat4 world; +} node; + +layout(set = 1, binding = 0) uniform CameraData +{ + mat4 viewProjection; + mat4 view; + mat4 projection; + vec4 camPos; + float nearPlane; + float farPlane; + float width; + float height; + float fov; + float aspect; + float scaleFactor; + float pixelScaleFactor; +} cam; + +layout(set = 3, binding = 0) uniform BillboardData +{ + vec2 size; + bool isFixedSize; +} billboardInfo; + +layout(location = 0) out vec4 color; +layout(location = 1) out vec2 tex; + +layout(location = 0) in VS_OUT { + vec4 color; +} gs_in[]; + +void main() { + // The desired point for the billboard + vec3 pos = gl_in[0].gl_Position.xyz; + if(!billboardInfo.isFixedSize) + { + vec3 cameraRight = normalize(vec3(cam.view[0][0], cam.view[1][0], cam.view[2][0])); + vec3 cameraUp = normalize(vec3(cam.view[0][1], cam.view[1][1], cam.view[2][1])); + const vec2 offsets[4] = { + vec2(0.5, -0.5), + vec2(0.5, 0.5), + vec2(-0.5, -0.5), + vec2(-0.5, 0.5) + }; + const vec2 texCoords[4] = { + vec2(1, 0), + vec2(1, 1), + vec2(0, 0), + vec2(0, 1) + }; + + for (int i = 0; i < 4; i++) + { + vec2 scaledSize = billboardInfo.size / length(billboardInfo.size); + gl_Position = cam.viewProjection * vec4(pos + cameraRight * offsets[i].x * scaledSize.x + cameraUp * offsets[i].y * scaledSize.y, 1.0); + color = gs_in[0].color; + tex = texCoords[i].xy; + EmitVertex(); + } + EndPrimitive(); + } + else + { + const vec2 offsets[4] = { + vec2(-0.5, -0.5), + vec2(-0.5, 0.5), + vec2(0.5, -0.5), + vec2(0.5, 0.5) + }; + const vec2 texCoords[4] = { + vec2(0, 0), + vec2(0, 1), + vec2(1, 0), + vec2(1, 1) + }; + + for (int i = 0; i < 4; i++) + { + gl_Position = cam.viewProjection * vec4(pos, 1); + gl_Position /= gl_Position.w; + gl_Position.xy += offsets[i] * vec2(billboardInfo.size.x/cam.width, billboardInfo.size.x/cam.height); + color = gs_in[0].color; + tex = texCoords[i].xy; + EmitVertex(); + } + EndPrimitive(); + } +} diff --git a/openVulkanoCpp/Shader/billboardFromSinglePoint.vert b/openVulkanoCpp/Shader/billboardFromSinglePoint.vert new file mode 100644 index 0000000..ee8e711 --- /dev/null +++ b/openVulkanoCpp/Shader/billboardFromSinglePoint.vert @@ -0,0 +1,21 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 pos; +layout(location = 5) in vec4 color; + +layout(location = 0) out VS_OUT { + vec4 color; +} vs_out; + +layout(set = 0, binding = 0) uniform NodeData +{ + mat4 world; +} node; + +void main() { + // single point + gl_Position = node.world * vec4(pos, 1); + vs_out.color = color; +} diff --git a/openVulkanoCpp/Vulkan/Scene/SimpleDrawableVulkanEncoder.cpp b/openVulkanoCpp/Vulkan/Scene/SimpleDrawableVulkanEncoder.cpp index b38ea11..54ba990 100644 --- a/openVulkanoCpp/Vulkan/Scene/SimpleDrawableVulkanEncoder.cpp +++ b/openVulkanoCpp/Vulkan/Scene/SimpleDrawableVulkanEncoder.cpp @@ -6,9 +6,11 @@ #include "Scene/SimpleDrawable.hpp" #include "Scene/Material.hpp" +#include "Scene/UniformBuffer.hpp" #include "VulkanGeometry.hpp" #include "VulkanNode.hpp" #include "Vulkan/VulkanDrawContext.hpp" +#include "Vulkan/Scene/VulkanUniformBuffer.hpp" #include "VulkanTexture.hpp" using namespace OpenVulkano::Scene; @@ -22,6 +24,18 @@ namespace OpenVulkano::Vulkan VulkanGeometry* renderGeo = static_cast(mesh->renderGeo); if (!renderGeo) renderGeo = drawContext->renderer->GetResourceManager().PrepareGeometry(mesh); renderGeo->RecordBind(drawContext->commandBuffer); + + if (drawable->GetBuffer()) + { + + VulkanUniformBuffer* vkBuffer = static_cast(drawable->GetBuffer()->renderBuffer); + if (!vkBuffer) + { + vkBuffer = drawContext->renderer->GetResourceManager().PrepareUniformBuffer(drawable->GetBuffer()); + } + vkBuffer->Record(drawContext); + } + if (Material* material = drawable->GetMaterial()) { if (Texture* texture = material->texture) diff --git a/openVulkanoCpp/Vulkan/Scene/VulkanGeometry.hpp b/openVulkanoCpp/Vulkan/Scene/VulkanGeometry.hpp index c96932b..1392fea 100644 --- a/openVulkanoCpp/Vulkan/Scene/VulkanGeometry.hpp +++ b/openVulkanoCpp/Vulkan/Scene/VulkanGeometry.hpp @@ -52,7 +52,8 @@ namespace OpenVulkano::Vulkan void RecordDraw(vk::CommandBuffer& cmdBuffer) { - cmdBuffer.drawIndexed(m_geometry->GetIndexCount(), 1, 0, 0, 0); + if (m_geometry->GetIndexCount()) { cmdBuffer.drawIndexed(m_geometry->GetIndexCount(), 1, 0, 0, 0); } + else { cmdBuffer.draw(m_geometry->GetVertexCount(), 1, 0, 0); } } void Close() override