diff --git a/.gitignore b/.gitignore index 8d7f31d..e946c61 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,6 @@ CMakeCache.txt build.ninja cmake_install.cmake CMakeCache.txt -.ninja* \ No newline at end of file +CMakeUserPresets.json +.ninja* +.vs/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index eec78fb..3aca493 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,19 +1,37 @@ -cmake_minimum_required(VERSION 3.29) +cmake_minimum_required(VERSION 3.26) project(ConjureEngineProject) +set(CMAKE_CXX_STANDARD 20) + # Set the default build type to Debug if not specified by the user if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type (default Debug)" FORCE) endif() -set(CONAN_DEPENDENCIES_DIR ${CMAKE_BINARY_DIR}/Conan) +message(${CMAKE_BUILD_TYPE}) -execute_process(COMMAND conan install ${CMAKE_SOURCE_DIR}/ConjureEngine --output-folder=${CONAN_DEPENDENCIES_DIR} --build=missing) -execute_process(COMMAND conan install ${CMAKE_SOURCE_DIR}/Demo1 --output-folder=${CONAN_DEPENDENCIES_DIR} --build=missing) +# Set architecture +if(NOT DEFINED ARCH) + set(ARCH x64) # Default to x64 architecture +endif() -set(CMAKE_TOOLCHAIN_FILE "${CONAN_DEPENDENCIES_DIR}/build/Release/generators/conan_toolchain.cmake" CACHE FILEPATH "Conan toolchain file") -set(CMAKE_PREFIX_PATH "${CONAN_DEPENDENCIES_DIR}/build/Release/generators" ${CMAKE_PREFIX_PATH}) -set(CMAKE_MODULE_PATH "${CONAN_DEPENDENCIES_DIR}/build/Release/generators" ${CMAKE_MODULE_PATH}) +set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/build) +# Include the Conan-generated files + +IF (WIN32) + list(APPEND CMAKE_PREFIX_PATH "${CMAKE_BINARY_DIR}/generators") + include("${CMAKE_BINARY_DIR}/generators/conan_toolchain.cmake") +ELSE() + list(APPEND CMAKE_PREFIX_PATH "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/generators") + include("${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/generators/conan_toolchain.cmake") +ENDIF() + +# Set common output directories +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/intermediates/${CMAKE_BUILD_TYPE}/${ARCH}/${PROJECT_NAME}) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin/${CMAKE_BUILD_TYPE}/${ARCH}/${PROJECT_NAME}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin/${CMAKE_BUILD_TYPE}/${ARCH}/${PROJECT_NAME}) + +# Add subdirectories for engine and demo add_subdirectory(./ConjureEngine) -add_subdirectory(./Demo1) \ No newline at end of file +add_subdirectory(./Demo1) diff --git a/Configure.bat b/Configure.bat new file mode 100644 index 0000000..e4a13c0 --- /dev/null +++ b/Configure.bat @@ -0,0 +1,8 @@ +@REM Run Conan to install dependencies +rmdir build /s /q + +@REM DEBUG +conan install . -s build_type=Debug --build=missing + +@REM RELEASE +conan install . -s build_type=Release --build=missing diff --git a/Configure.sh b/Configure.sh new file mode 100755 index 0000000..180db67 --- /dev/null +++ b/Configure.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Run Conan to install dependencies +rm -Rf build + +# DEBUG +conan install . -s build_type=Debug --build=missing + +# RELEASE +conan install . -s build_type=Release --build=missing diff --git a/ConjureEngine/CMakeLists.txt b/ConjureEngine/CMakeLists.txt index 909c6f8..729604b 100644 --- a/ConjureEngine/CMakeLists.txt +++ b/ConjureEngine/CMakeLists.txt @@ -1,27 +1,36 @@ -cmake_minimum_required(VERSION 3.29) +cmake_minimum_required(VERSION 3.26) + +set(CMAKE_CXX_STANDARD 20) + project(ConjureEngine) -set(CMAKE_CXX_STANDARD 17) +set(HEADER_FILES + src/ConjureEngine/Application.h + src/ConjureEngine/ConjureEngine.h + src/ConjureEngine/VulkanContext.h + src/ConjureEngine/Window.h +) -# Set the architecture (assuming you're passing the architecture as a CMake variable) -# Replace 'x64' with your system's architecture, or set this dynamically based on the system -if(NOT DEFINED ARCH) - set(ARCH x64) # You can change this to x86 or any other architecture you are targeting -endif() - - - -# Set common output directories -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/intermediates/${ARCH}/${PROJECT_NAME}) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin/${ARCH}/${PROJECT_NAME}) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin/${ARCH}/${PROJECT_NAME}) +set(SOURCES_FILES + src/ConjureEngine/Application.cpp + src/ConjureEngine/VulkanContext.cpp + src/ConjureEngine/Window.cpp +) find_package(glm REQUIRED) +find_package(SDL2 REQUIRED) find_package(Vulkan REQUIRED) -add_library(${PROJECT_NAME} STATIC src/ConjureEngine/ConjureEngine.h src/ConjureEngine/ConjureEngine.cpp) - -target_link_libraries(${PROJECT_NAME} ${CONAN_LIBS}) +add_library(${PROJECT_NAME} STATIC ${HEADER_FILES} ${SOURCES_FILES}) # Specify include directories -target_include_directories(ConjureEngine PUBLIC include) \ No newline at end of file +target_include_directories(${PROJECT_NAME} PUBLIC include) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_BINARY_DIR}/include) + +target_link_libraries(${PROJECT_NAME} SDL2::SDL2 glm::glm Vulkan::Vulkan) + +target_precompile_headers( + ${PROJECT_NAME} + PRIVATE + src/ConjureEngine/PCH.h +) \ No newline at end of file diff --git a/ConjureEngine/docs/.gitkeep b/ConjureEngine/docs/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/ConjureEngine/docs/RenderingPipelineSteps.mmd b/ConjureEngine/docs/RenderingPipelineSteps.mmd new file mode 100644 index 0000000..b5a2ff5 --- /dev/null +++ b/ConjureEngine/docs/RenderingPipelineSteps.mmd @@ -0,0 +1,41 @@ +--- +title: Rendering Pipeline Steps +--- +stateDiagram-v2 + direction LR + state "Data" as Data1 + state "Vertex Shader" as VertexShader1 + state "Tesselation Shader" as TesselationShader1 + state "Rasterization" as Rasterization1 + state "Fragment Shader" as FragmentShader1 + state "Blending" as Blending1 + state "Texture" as Texture1 + + state "Texture" as Texture2 + state "Vertex Shader" as VertexShader2 + state "Tesselation Shader" as TesselationShader2 + state "Rasterization" as Rasterization2 + state "Fragment Shader" as FragmentShader2 + state "Blending" as Blending2 + state "Frame Buffer" as FrameBuffer2 + + Data1 --> VertexShader1 + state "Pass 1" as Pass1{ + direction LR + VertexShader1 --> TesselationShader1 + TesselationShader1 --> Rasterization1 + Rasterization1 --> FragmentShader1 + FragmentShader1 --> Blending1 + } + Blending1 --> Texture1 + + + Texture2 --> VertexShader2 + state "Pass 2" as Pass2{ + direction LR + VertexShader2 --> TesselationShader2 + TesselationShader2 --> Rasterization2 + Rasterization2 --> FragmentShader2 + FragmentShader2 --> Blending2 + } + Blending2 --> FrameBuffer2 \ No newline at end of file diff --git a/ConjureEngine/docs/class.mmd b/ConjureEngine/docs/class.mmd new file mode 100644 index 0000000..2b7b43a --- /dev/null +++ b/ConjureEngine/docs/class.mmd @@ -0,0 +1,24 @@ +classDiagram + namespace ConjureEngine { + class Application { + + Run() int + + Tick(double deltaTime) void + } + + class Window { + + std::shared_ptr~SDL_Window~ m_window; + + std::shared_ptr~VulkanContext~ m_vulkanContext; + } + + class VulkanContext { + - uint32_t m_extensionCount + - char** m_extensionNames + - VkInstance m_vkInst + - uint32_t m_physicalDeviceCount + - std::vector~VkPhysicalDevice~ m_physicalDevices + - VkPhysicalDevice m_selectedDevice + - uint32_t m_queueFamilyCount + } + } + + Window "1" *-- "1" VulkanContext \ No newline at end of file diff --git a/ConjureEngine/docs/renderingPipeline.mmd b/ConjureEngine/docs/renderingPipeline.mmd new file mode 100644 index 0000000..6a9a952 --- /dev/null +++ b/ConjureEngine/docs/renderingPipeline.mmd @@ -0,0 +1,28 @@ +--- +title: Rendering Pipeline +--- +classDiagram + class VAO + class VBO + class Shader + class Pipeline0 + class Pipeline1 + class Pipeline2 + + class Entity0 + class Entity1 + class Entity2 + + Entity0 -- Pipeline0 + Entity0 -- Pipeline1 + Entity0 -- Pipeline2 + + Entity1 -- Pipeline0 + Entity1 -- Pipeline1 + + Entity2 -- Pipeline1 + Entity2 -- Pipeline2 + + Pipeline0 -- Shader + Pipeline1 -- Shader + Pipeline2 -- Shader \ No newline at end of file diff --git a/ConjureEngine/src/ConjureEngine/Application.cpp b/ConjureEngine/src/ConjureEngine/Application.cpp new file mode 100644 index 0000000..a56d7df --- /dev/null +++ b/ConjureEngine/src/ConjureEngine/Application.cpp @@ -0,0 +1,62 @@ +// +// Created by calap on 11/21/2024. +// + +#include "Application.h" +#include "VulkanContext.h" + +namespace ConjureEngine { + Application::Application(const ApplicationInfo &applicationInfo): m_applicationInfo(applicationInfo) { + // Fill the application information + m_vkAppInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + m_vkAppInfo.pApplicationName = applicationInfo.title.c_str(); + m_vkAppInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + m_vkAppInfo.pEngineName = "Conjure Engine"; + m_vkAppInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); + m_vkAppInfo.apiVersion = VK_API_VERSION_1_0; + } + + int Application::Run() + { + // CREATE SDL WINDOW + Window window(this->m_applicationInfo.title.c_str(), m_applicationInfo.window); + + // INIT VULKAN + VulkanContext vulkanContext; + vulkanContext.AttachTo(window.GetWindow(), this->m_vkAppInfo); + + // INITIALIZATION OF THE WORLD + this->Awake(); + this->Start(); + + // MAIN LOOP + bool running = true; + + static uint64_t lastTick = SDL_GetTicks64(); // TIME IS IN MS + while (running) { + + + // HANDLE INPUTS HERE (CODE BELOW IS FROM A TUTORIAL) + SDL_Event windowEvent; + while (SDL_PollEvent(&windowEvent)) { + if (windowEvent.type == SDL_QUIT) { + running = false; + break; + } + } + + // TICK SCENE HERE + const uint64_t currentTick = SDL_GetTicks64(); // TIME IS IN MS + const uint64_t deltaTime = currentTick - lastTick; // TIME IS IN MS + this->Tick(static_cast(deltaTime)/1000.f); + + // HANDLE PHYSICS HERE + lastTick = currentTick; + } + + // CLEANUP HERE + this->Destroy(); + + return 0; + } +} // ConjureEngine diff --git a/ConjureEngine/src/ConjureEngine/Application.h b/ConjureEngine/src/ConjureEngine/Application.h new file mode 100644 index 0000000..67a5d87 --- /dev/null +++ b/ConjureEngine/src/ConjureEngine/Application.h @@ -0,0 +1,56 @@ +// +// Created by calap on 11/21/2024. +// + +#pragma once + +#include "PCH.h" +#include "Window.h" + +namespace ConjureEngine { + struct ApplicationInfo { + std::string title; + WindowInfo window{ + 0, + 0, + 0, + 0 + }; + }; + + class Application { + public: + explicit Application(const ApplicationInfo &applicationInfo); + virtual ~Application() = default; + + int Run(); + + /** + * First function call right after SDL and VULKAN are init + */ + virtual inline void Awake() {}; + + /** + * Called after awake is done + */ + virtual inline void Start() {}; + + /** + * Called every frame after Start + * @param deltaTime Time in seconds since last call to Tick + */ + virtual inline void Tick(double deltaTime) {} + + /** + * Call when the application is being closed + */ + virtual inline void Destroy() {}; + + + protected: + public: + protected: + ApplicationInfo m_applicationInfo; + VkApplicationInfo m_vkAppInfo{}; + }; +} // ConjureEngine diff --git a/ConjureEngine/src/ConjureEngine/ConjureEngine.cpp b/ConjureEngine/src/ConjureEngine/ConjureEngine.cpp deleted file mode 100644 index 107dd0c..0000000 --- a/ConjureEngine/src/ConjureEngine/ConjureEngine.cpp +++ /dev/null @@ -1,10 +0,0 @@ -// -// Created by Jimmy Tremblay-bernier on 2024-11-14. -// -#include "ConjureEngine.h" -#include - -void ConjureEngine::SayHello() -{ - printf("Hello World\n"); -} \ No newline at end of file diff --git a/ConjureEngine/src/ConjureEngine/ConjureEngine.h b/ConjureEngine/src/ConjureEngine/ConjureEngine.h index b2a96c2..4bc960e 100644 --- a/ConjureEngine/src/ConjureEngine/ConjureEngine.h +++ b/ConjureEngine/src/ConjureEngine/ConjureEngine.h @@ -1,4 +1,5 @@ #pragma once -namespace ConjureEngine { - void SayHello(); -} + +#include "Application.h" +#include "VulkanContext.h" +#include "Window.h" diff --git a/ConjureEngine/src/ConjureEngine/PCH.h b/ConjureEngine/src/ConjureEngine/PCH.h new file mode 100644 index 0000000..7748be7 --- /dev/null +++ b/ConjureEngine/src/ConjureEngine/PCH.h @@ -0,0 +1,22 @@ +// +// Created by calap on 11/21/2024. +// + +#pragma once + +// STANDARD LIBS +#include +#include +#include + +// GLM +#include "glm/glm.hpp" + +// SDL2 +#include "SDL2/SDL.h" +#include "SDL2/SDL_vulkan.h" +#include "SDL2/SDL_video.h" + +// VULKAN +#include "vulkan/vulkan.h" +#include "vulkan/vulkan_core.h" \ No newline at end of file diff --git a/ConjureEngine/src/ConjureEngine/VulkanContext.cpp b/ConjureEngine/src/ConjureEngine/VulkanContext.cpp new file mode 100644 index 0000000..46c8a94 --- /dev/null +++ b/ConjureEngine/src/ConjureEngine/VulkanContext.cpp @@ -0,0 +1,201 @@ +// +// Created by Jimmy Tremblay-bernier on 2024-11-22. +// + +#include "VulkanContext.h" + +#include "Window.h" + +#ifdef __APPLE__ + #include "vulkan/vulkan_metal.h" +#endif + +namespace ConjureEngine { + VulkanContext::VulkanContext()= default; + + void VulkanContext::AttachTo(SDL_Window* window, const VkApplicationInfo& appInfo) + { + // LOAD THE EXTENSIONS + SDL_bool sdlSuccess; + VkResult vk_result; + + // FETCH NUMBER OF EXTENSIONS TO RESERVE SPACE IN VECTOR + sdlSuccess = SDL_Vulkan_GetInstanceExtensions(window, &m_extensionCount, nullptr); + if(sdlSuccess != SDL_TRUE) + { + SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error Getting Extensions: %s", SDL_GetError()); + exit(1); + } + m_extensionNames.reserve(m_extensionCount); + + // FILLING UP THE VECTOR + sdlSuccess = SDL_Vulkan_GetInstanceExtensions(window, &m_extensionCount, m_extensionNames.data()); + if(sdlSuccess != SDL_TRUE) + { + SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error Getting Extensions: %s", SDL_GetError()); + exit(1); + } + else + { + SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM,"FETCHING OF EXTENSION FINISHED SUCCESSFULLY"); + } + + // Fill the instance create info struct using appInfo + VkInstanceCreateInfo vulkanInfos{}; + vulkanInfos.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + vulkanInfos.pApplicationInfo = &appInfo; + + // ENABLE THE REQUIRED EXTENSIONS + this->EnableGlobalExtentions(window, vulkanInfos); + + // CREATE THE VULKAN INSTANCE + vk_result = vkCreateInstance(&vulkanInfos, nullptr, &m_vkInst); + if(vk_result != VkResult::VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error while creating the device: CODE = %d", static_cast(vk_result)); + exit(1); + } + else { + SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM,"VULKAN INSTANCE CREATED SUCCESSFULLY"); + } + + // FETCH THE NUMBER OF PHYSICAL DEVICES (GPUs) TO RESERVE SPACE INTHE VECTOR + vk_result = vkEnumeratePhysicalDevices(m_vkInst, &m_physicalDeviceCount, nullptr); + if(vk_result != VkResult::VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error while enumerating physical devices: CODE = %d", static_cast(vk_result)); + exit(1); + } + m_physicalDevices = std::vector(m_physicalDeviceCount); + + // FILL THE VECTOR WITH ACTUAL DEVICES DATA + vk_result = vkEnumeratePhysicalDevices(m_vkInst, &m_physicalDeviceCount, m_physicalDevices.data()); + if(vk_result != VkResult::VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error while enumerating physical devices: CODE = %d", static_cast(vk_result)); + exit(1); + } + else { + SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "VULKAN ENUMERATION OF PHYSICAL DEVICES FINISHED SUCCESSFULLY"); + } + + // SELECT THE MAIN GPU (WE COULD HAVE A SMARTER SELECTION LATER. FOR NOW, WE SELECT THE FIRST ONE WE SEE) + m_selectedPhysicalDevice = m_physicalDevices[0]; + + // FETCH THE QUANTITY OF FAMILY QUEUE TO RESERVE SPACE IN THE VECTOR + vkGetPhysicalDeviceQueueFamilyProperties(m_selectedPhysicalDevice, &m_queueFamilyCount, nullptr); + std::vector queueFamilies(m_queueFamilyCount); + + // FILL THE VECTOR WITH ACTUAL QUEUE DATA + vkGetPhysicalDeviceQueueFamilyProperties(m_selectedPhysicalDevice, &m_queueFamilyCount, queueFamilies.data()); + + // CREATE THE SURFACE FOR RENDERING + sdlSuccess = SDL_Vulkan_CreateSurface(window, m_vkInst, &m_surface); + if(sdlSuccess != SDL_TRUE) + { + SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error Getting Extensions: %s", SDL_GetError()); + exit(1); + } + else + { + SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "VULKAN SURFACE CREATED SUCCESSFULLY"); + } + + // NOT SURE WHAT THIS DOES YET, WILL CHECK IN THE TUTORIAL + uint32_t graphicsQueueIndex = UINT32_MAX; + uint32_t presentQueueIndex = UINT32_MAX; + VkBool32 support; + uint32_t i = 0; + for (VkQueueFamilyProperties queueFamily: queueFamilies) { + if (graphicsQueueIndex == UINT32_MAX && queueFamily.queueCount > 0 && queueFamily.queueFlags & + VK_QUEUE_GRAPHICS_BIT) + graphicsQueueIndex = i; + if (presentQueueIndex == UINT32_MAX) { + vk_result = vkGetPhysicalDeviceSurfaceSupportKHR(m_selectedPhysicalDevice, i, m_surface, &support); + if(vk_result != VkResult::VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error while checking the surface capability: CODE = %d", static_cast(vk_result)); + } + + if (support) + presentQueueIndex = i; + } + ++i; + } + + // + float queuePriority = 1.0f; + m_deviceQueueCreateInfo = { + VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType + nullptr, // pNext + 0, // flags + graphicsQueueIndex, // graphicsQueueIndex + 1, // queueCount + &queuePriority, // pQueuePriorities + }; + + // FETCH THE PHYSICAL DEVICES FEATURES + VkPhysicalDeviceFeatures deviceFeatures = {}; + const char *deviceExtensionNames[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; + m_deviceCreateInfo = { + VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // sType + nullptr, // pNext + 0, // flags + 1, // queueCreateInfoCount + &m_deviceQueueCreateInfo, // pQueueCreateInfos + 0, // enabledLayerCount + nullptr, // ppEnabledLayerNames + 1, // enabledExtensionCount + deviceExtensionNames, // ppEnabledExtensionNames + &deviceFeatures, // pEnabledFeatures + }; + + + // CREATE VIRTUAL DEVICE FOR RENDERING + vk_result = vkCreateDevice(m_selectedPhysicalDevice, &m_deviceCreateInfo, nullptr, &m_device); + if(vk_result != VkResult::VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Error while creating the device: %d", static_cast(vk_result)); + exit(1); + } + else { + SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "DEVICE CREATED SUCCESSFULLY"); + } + + vkGetDeviceQueue(m_device, graphicsQueueIndex, 0, &m_graphicQueue); + + // WHEN POSSIBLE, WE WANT TO USE THE PRESENTATION QUEUE, BUT MAC DOESN'T SUPPORT IT + if(presentQueueIndex != UINT32_MAX) { + m_supportsPresentationQueue = true; + vkGetDeviceQueue(m_device, presentQueueIndex, 0, &m_presentQueue); + } + + + const std::string error = SDL_GetError(); + if(!error.empty()) { + SDL_Log("Initialized with errors: %s", error.c_str()); + } + else { + SDL_Log("Initialized without errors"); + } + } + + void VulkanContext::EnableGlobalExtentions(SDL_Window* window, VkInstanceCreateInfo &vulkanInfos) { + SDL_Vulkan_GetInstanceExtensions(window, &m_extensionCount, nullptr); + this->m_extensionNames.reserve(m_extensionCount); + SDL_Vulkan_GetInstanceExtensions(window, &m_extensionCount, m_extensionNames.data()); + + #ifdef __APPLE__ + this->m_extensionNames.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); + this->m_extensionNames.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME); + vulkanInfos.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; + #endif + + #ifdef __WIN32__ + this->m_extensionNames.push_back("VK_KHR_win32_surface"); + #endif + + vulkanInfos.enabledExtensionCount = static_cast(this->m_extensionNames.size()); + vulkanInfos.ppEnabledExtensionNames = this->m_extensionNames.data(); + } + + VulkanContext::~VulkanContext() { + vkDestroyDevice(m_device, nullptr); + vkDestroyInstance(m_vkInst, nullptr); + } +} // ConjureEngine diff --git a/ConjureEngine/src/ConjureEngine/VulkanContext.h b/ConjureEngine/src/ConjureEngine/VulkanContext.h new file mode 100644 index 0000000..a0e7aa8 --- /dev/null +++ b/ConjureEngine/src/ConjureEngine/VulkanContext.h @@ -0,0 +1,34 @@ +// +// Created by Jimmy Tremblay-bernier on 2024-11-22. +// + +#pragma once + +namespace ConjureEngine { +#include "PCH.h" + + class VulkanContext { + public: + explicit VulkanContext(); + ~VulkanContext(); + void AttachTo(SDL_Window* window, const VkApplicationInfo& appInfo); + private: + void EnableGlobalExtentions(SDL_Window* window, VkInstanceCreateInfo &vulkanInfos); + + private: + uint32_t m_extensionCount{0}; + std::vector m_extensionNames; + VkInstance m_vkInst{nullptr}; + uint32_t m_physicalDeviceCount{0}; + std::vector m_physicalDevices; + VkPhysicalDevice m_selectedPhysicalDevice{nullptr}; + uint32_t m_queueFamilyCount{0}; + VkSurfaceKHR m_surface{nullptr}; + VkDeviceQueueCreateInfo m_deviceQueueCreateInfo{}; + VkDeviceCreateInfo m_deviceCreateInfo{}; + VkDevice m_device{nullptr}; + VkQueue m_graphicQueue{nullptr}; + VkQueue m_presentQueue{nullptr}; + bool m_supportsPresentationQueue = false; + }; +} // ConjureEngine diff --git a/ConjureEngine/src/ConjureEngine/Window.cpp b/ConjureEngine/src/ConjureEngine/Window.cpp new file mode 100644 index 0000000..a927927 --- /dev/null +++ b/ConjureEngine/src/ConjureEngine/Window.cpp @@ -0,0 +1,33 @@ +// +// Created by calap on 11/21/2024. +// +#include "Window.h" + +namespace ConjureEngine { + + Window::Window(const char* title, const WindowInfo& windowInfo) + { + // INIT WINDOW + SDL_Init(SDL_INIT_VIDEO); + SDL_Vulkan_LoadLibrary(nullptr); + + m_window = SDL_CreateWindow(title, 0, 32, windowInfo.width, windowInfo.height, SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN); + } + + Window::~Window() { + SDL_DestroyWindow(m_window); + SDL_Vulkan_UnloadLibrary(); + SDL_Quit(); + + std::string error = SDL_GetError(); + if(!error.empty()) + { + SDL_Log("Cleaned up with errors: %s", error.c_str()); + } + } + + SDL_Window* Window::GetWindow() const + { + return m_window; + } +} // ConjureEngine \ No newline at end of file diff --git a/ConjureEngine/src/ConjureEngine/Window.h b/ConjureEngine/src/ConjureEngine/Window.h new file mode 100644 index 0000000..3856666 --- /dev/null +++ b/ConjureEngine/src/ConjureEngine/Window.h @@ -0,0 +1,30 @@ +// +// Created by calap on 11/21/2024. +// + +#pragma once + +#include "PCH.h" + +namespace ConjureEngine +{ + struct WindowInfo { + int x; + int y; + int width; + int height; + }; + + class Window { + public: + explicit Window(const char* title, const WindowInfo &windowInfo); + + ~Window(); + + SDL_Window* GetWindow() const; + private: + public: + private: + SDL_Window* m_window; + }; +} // ConjureEngine diff --git a/Demo1/.gitignore b/Demo1/.gitignore index e210f52..bc5d3a8 100644 --- a/Demo1/.gitignore +++ b/Demo1/.gitignore @@ -1,3 +1,5 @@ CMakeFiles/ cmake_install.cmake -CMakeUserPresets.json \ No newline at end of file +CMakeUserPresets.json +CompileShaders.log +**/*.spv \ No newline at end of file diff --git a/Demo1/CMakeLists.txt b/Demo1/CMakeLists.txt index 0eec459..b4c0794 100644 --- a/Demo1/CMakeLists.txt +++ b/Demo1/CMakeLists.txt @@ -1,23 +1,30 @@ -cmake_minimum_required(VERSION 3.29) +cmake_minimum_required(VERSION 3.26) project(Demo1) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) -# Set the architecture (assuming you're passing the architecture as a CMake variable) -# Replace 'x64' with your system's architecture, or set this dynamically based on the system -if(NOT DEFINED ARCH) - set(ARCH x64) # You can change this to x86 or any other architecture you are targeting -endif() +find_package(Python3 REQUIRED COMPONENTS Interpreter) -# Set common output directories -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/intermediates/${ARCH}/${PROJECT_NAME}) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin/${ARCH}/${PROJECT_NAME}) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin/${ARCH}/${PROJECT_NAME}) +add_executable(${PROJECT_NAME} + src/main.cpp + src/Demo1.cpp + src/Demo1.h +) -add_executable(${PROJECT_NAME} src/main.cpp) +# CREATE CUSTOM TARGET THAT COMPILE SHADERS EVERY BUILD +add_custom_target(shader_compilation ALL + COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/CompileShaders.py ${CMAKE_CURRENT_SOURCE_DIR}/src/Shaders ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Compiling shaders" +) + +# ADD THE CUSTOM TARGET AS A DEPENDENCY OF DEMO1 +add_dependencies(${PROJECT_NAME} shader_compilation) target_link_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/ConjureEngine) -target_link_libraries(${PROJECT_NAME} ${CONAN_LIBS} ConjureEngine) +find_package(SDL2 REQUIRED) + target_include_directories(${PROJECT_NAME} PRIVATE include) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/ConjureEngine/src) + +target_link_libraries(${PROJECT_NAME} SDL2::SDL2 ConjureEngine) \ No newline at end of file diff --git a/Demo1/CompileShaders.py b/Demo1/CompileShaders.py new file mode 100644 index 0000000..0cf7f44 --- /dev/null +++ b/Demo1/CompileShaders.py @@ -0,0 +1,46 @@ +import os +import sys +import subprocess +import logging + +os.chdir(sys.argv[1]) + +# Configure logging +logging.basicConfig( + filename=os.path.join(sys.argv[2], "CompileShaders.log"), # Log file name + filemode="w", # Append mode (use "w" for overwrite) + format="%(asctime)s - %(levelname)s - %(message)s", # Log format + level=logging.INFO # Logging level +) + +def filterFunc(fileName): + params = fileName.split(".") + return params[len(params) - 1] != "spv" + +def __main__(): + + SHADERS_PATH = os.path.join(os.getcwd()) + if os.name == "nt": + GLSLC_PATH = os.path.join(os.environ.get("VULKAN_SDK"), "bin", "glslc") + else: + GLSLC_PATH = "/usr/local/bin/glslc" + + logging.info(str.join(" ", ["SHADER PATH:", SHADERS_PATH])) + logging.info(str.join(" ", ["GLSLC PATH:", GLSLC_PATH])) + + shaders = filter(filterFunc, os.listdir(SHADERS_PATH)) + for shader in shaders: + name, ext = shader.split(".") + + command = GLSLC_PATH + args = [ + os.path.join(SHADERS_PATH, shader), + "-o", + os.path.join(SHADERS_PATH, name + "." + ext + ".spv") + ] + + logging.info(str.join(" ", ["Compiling:", shader])) + logging.info(str.join(" ", [command] + args)) + subprocess.Popen([command] + args) +if __name__ == "__main__": + __main__() \ No newline at end of file diff --git a/Demo1/conanfile.txt b/Demo1/conanfile.txt deleted file mode 100644 index bc6f23d..0000000 --- a/Demo1/conanfile.txt +++ /dev/null @@ -1,8 +0,0 @@ -[requires] - -[generators] -CMakeDeps -CMakeToolchain - -[layout] -cmake_layout \ No newline at end of file diff --git a/Demo1/src/Demo1.cpp b/Demo1/src/Demo1.cpp new file mode 100644 index 0000000..5d7705f --- /dev/null +++ b/Demo1/src/Demo1.cpp @@ -0,0 +1,42 @@ +// +// Created by Jimmy Tremblay-bernier on 2024-11-22. +// + +#include "Demo1.h" +#include "ConjureEngine/ConjureEngine.h" + +static ConjureEngine::ApplicationInfo appInfo = ConjureEngine::ApplicationInfo +{ + "Demo1", + ConjureEngine::WindowInfo { + 0, + 0, + 1920, + 1080 + } +}; + +namespace Demo1 { + Demo1::Demo1(): ConjureEngine::Application(appInfo) {} + + void Demo1::Awake() + { + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,"AWAKING"); + } + + void Demo1::Start() + { + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,"STARTING"); + } + + void Demo1::Tick(double deltaTime) + { + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "TICKING %f", deltaTime); + } + + void Demo1::Destroy() + { + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,"DESTROYING"); + } + +} // Demo1 diff --git a/Demo1/src/Demo1.h b/Demo1/src/Demo1.h new file mode 100644 index 0000000..73a5e7b --- /dev/null +++ b/Demo1/src/Demo1.h @@ -0,0 +1,19 @@ +/// +// Created by Jimmy Tremblay-bernier on 2024-11-22. +// + +#pragma once +#include "ConjureEngine/ConjureEngine.h" + +namespace Demo1 { + +class Demo1: public ConjureEngine::Application { + public: + Demo1(); + void Awake() override; + void Start() override; + void Tick(double deltaTime) override; + void Destroy() override; +}; + +} // Demo1 \ No newline at end of file diff --git a/Demo1/src/Shaders/Test.frag b/Demo1/src/Shaders/Test.frag new file mode 100644 index 0000000..2801bac --- /dev/null +++ b/Demo1/src/Shaders/Test.frag @@ -0,0 +1,7 @@ +#version 460 core + +layout (location = 0) out vec4 oColor; + +void main() { + oColor = vec4(1); +} \ No newline at end of file diff --git a/Demo1/src/Shaders/Test.vert b/Demo1/src/Shaders/Test.vert new file mode 100644 index 0000000..4a76a79 --- /dev/null +++ b/Demo1/src/Shaders/Test.vert @@ -0,0 +1,5 @@ +#version 460 core + +void main() { + gl_Position = vec4(0); +} \ No newline at end of file diff --git a/Demo1/src/main.cpp b/Demo1/src/main.cpp index 5668f0f..106453e 100644 --- a/Demo1/src/main.cpp +++ b/Demo1/src/main.cpp @@ -1,6 +1,92 @@ -#include "ConjureEngine/ConjureEngine.h" +#define SDL_MAIN_HANDLED -int main(int argc, char** argv) { - ConjureEngine::SayHello(); - return 0; +#include +#include + +#include "Demo1.h" + +static std::ofstream logFile; + +void OutputLog(void *userdata, int category, SDL_LogPriority priority, const char *message) +{ + (void)userdata; + + std::string categoryString; + switch (category) + { + case SDL_LOG_CATEGORY_APPLICATION: + categoryString = "Application"; + break; + case SDL_LOG_CATEGORY_ERROR: + categoryString = "Error"; + break; + case SDL_LOG_CATEGORY_ASSERT: + categoryString = "Assert"; + break; + case SDL_LOG_CATEGORY_SYSTEM: + categoryString = "System"; + break; + case SDL_LOG_CATEGORY_AUDIO: + categoryString = "Audio"; + break; + case SDL_LOG_CATEGORY_VIDEO: + categoryString = "Video"; + break; + case SDL_LOG_CATEGORY_RENDER: + categoryString = "Render"; + break; + case SDL_LOG_CATEGORY_INPUT: + categoryString = "Input"; + break; + case SDL_LOG_CATEGORY_TEST: + categoryString = "Test"; + break; + default: + categoryString = ""; + break; + } + + std::string priorityString; + switch (priority) + { + case SDL_LOG_PRIORITY_VERBOSE: + priorityString = "VERBOSE"; + break; + case SDL_LOG_PRIORITY_DEBUG: + priorityString = "DEBUG"; + break; + case SDL_LOG_PRIORITY_INFO: + priorityString = "INFO"; + break; + case SDL_LOG_PRIORITY_WARN: + priorityString = "WARN"; + break; + case SDL_LOG_PRIORITY_ERROR: + priorityString = "ERROR"; + break; + case SDL_LOG_PRIORITY_CRITICAL: + priorityString = "CRITICAL"; + break; + default: + std::cout << message << "\n"; + break; + } + + logFile << "[" << categoryString << " - " << priorityString << "] " << message << "\n"; + std::cout << "[" << categoryString << " - " << priorityString << "] " << message << "\n"; +} + +int main ( int argc, char* argv[] ) +{ + logFile = std::ofstream{"log.txt"}; + SDL_SetMainReady(); + + SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE); + SDL_LogSetOutputFunction(&OutputLog, nullptr); + + // I WANT TO CREATE AN APP + Demo1::Demo1 app; + + // AND THEN START IT + return app.Run(); } \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..6fd018e --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# Conjure Engine +## Installation +### Dependancies +To install this project, it is required that you install the folowind dependancies +on you computer: +- Vulkan SDK: https://vulkan.lunarg.com/sdk/home +- Conan 2: https://docs.conan.io/2/installation.html + +### Platforms supported +- Mac 14+ +- Windows 10+ +- Linux 6+ + +Those are wide guideline and any version recent enough of your OS should work +as long it supports vulkan and conan. + +### Installation step + +#### Configure your IDE +1. Execute `./Configure.sh` (LINUX/MAC) or `.\Configure.bat` (WINDOWS). This step installs all the dependencies for both release and debug. +2. Load the generated CMakeUserPresets.json that was generated at step 1. +3. Select an executable project (like Demo1) +4. Build \ No newline at end of file diff --git a/ConjureEngine/conanfile.txt b/conanfile.txt similarity index 100% rename from ConjureEngine/conanfile.txt rename to conanfile.txt