Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 119-graphicspipeline-refactoring
  • 129-projekte-und-assets-auslagern
  • 132-denoising-module
  • 143-ar-vr-support-via-openxr
  • 43-multi-threading
  • 91-compute-first-network
  • 95-arm64-raspberry-pi-4-support
  • develop
  • master
  • optimizations
  • 0.1.0
  • 0.2.0
12 results

Target

Select target project
  • vulkan2021/vkcv-framework
1 result
Select Git revision
  • 119-graphicspipeline-refactoring
  • 129-projekte-und-assets-auslagern
  • 132-denoising-module
  • 143-ar-vr-support-via-openxr
  • 43-multi-threading
  • 91-compute-first-network
  • 95-arm64-raspberry-pi-4-support
  • develop
  • master
  • optimizations
  • 0.1.0
  • 0.2.0
12 results
Show changes
Showing
with 1298 additions and 725 deletions
......@@ -73,17 +73,29 @@ namespace vkcv
using Handle::Handle;
};
class PipelineHandle : public Handle {
friend class PipelineManager;
class GraphicsPipelineHandle : public Handle {
friend class GraphicsPipelineManager;
private:
using Handle::Handle;
};
class ComputePipelineHandle : public Handle {
friend class ComputePipelineManager;
private:
using Handle::Handle;
};
class DescriptorSetHandle : public Handle {
friend class DescriptorManager;
private:
using Handle::Handle;
};
class DescriptorSetLayoutHandle : public Handle {
friend class DescriptorManager;
private:
using Handle::Handle;
};
class SamplerHandle : public Handle {
friend class SamplerManager;
......@@ -93,6 +105,7 @@ namespace vkcv
class ImageHandle : public Handle {
friend class ImageManager;
private:
using Handle::Handle;
public:
[[nodiscard]]
......@@ -102,6 +115,18 @@ namespace vkcv
};
class WindowHandle : public Handle {
friend class WindowManager;
private:
using Handle::Handle;
};
class SwapchainHandle : public Handle {
friend class SwapchainManager;
private:
using Handle::Handle;
};
class CommandStreamHandle : public Handle {
friend class CommandStreamManager;
private:
......
......@@ -53,9 +53,10 @@ namespace vkcv {
VKCV_DEBUG_MESSAGE_LEN, \
__VA_ARGS__ \
); \
auto output = getLogOutput(level); \
if (level != vkcv::LogLevel::RAW_INFO) { \
fprintf( \
getLogOutput(level), \
output, \
"[%s]: %s [%s, line %d: %s]\n", \
vkcv::getLogName(level), \
output_message, \
......@@ -65,12 +66,13 @@ namespace vkcv {
); \
} else { \
fprintf( \
getLogOutput(level), \
output, \
"[%s]: %s\n", \
vkcv::getLogName(level), \
output_message \
); \
} \
fflush(output); \
}
#else
......
......@@ -32,13 +32,21 @@ namespace vkcv {
const std::vector<Queue> &getTransferQueues() const;
static void queueCreateInfosQueueHandles(vk::PhysicalDevice &physicalDevice,
std::vector<float> &queuePriorities,
std::vector<vk::QueueFlagBits> &queueFlags,
const std::vector<float> &queuePriorities,
const std::vector<vk::QueueFlagBits> &queueFlags,
std::vector<vk::DeviceQueueCreateInfo> &queueCreateInfos,
std::vector<std::pair<int, int>> &queuePairsGraphics,
std::vector<std::pair<int, int>> &queuePairsCompute,
std::vector<std::pair<int, int>> &queuePairsTransfer);
/**
* checks for surface support in the queues
* @param physicalDevice to get the Queues
* @param surface that needs to checked
* @return
*/
static uint32_t checkSurfaceSupport(const vk::PhysicalDevice &physicalDevice, vk::SurfaceKHR &surface);
private:
std::vector<Queue> m_graphicsQueues;
std::vector<Queue> m_computeQueues;
......
......@@ -13,8 +13,8 @@
#include <vulkan/vulkan.hpp>
#include <spirv_cross.hpp>
#include "VertexLayout.hpp"
#include "ShaderStage.hpp"
#include "DescriptorConfig.hpp"
#include "ShaderStage.hpp"
namespace vkcv {
......@@ -51,7 +51,13 @@ namespace vkcv {
const std::vector<VertexAttachment> &getVertexAttachments() const;
size_t getPushConstantSize() const;
const std::vector<std::vector<DescriptorBinding>>& getReflectedDescriptors() const;
/**
* Returns the reflected descriptor sets/layouts/bindings in a map of maps.
* First uint32_t serves as descriptor SET id.
* Second uint32_t serves as the descriptor set's BINDING id.
* @return
*/
const std::unordered_map<uint32_t, std::unordered_map<uint32_t, DescriptorBinding>>& getReflectedDescriptors() const;
private:
/**
......@@ -65,7 +71,7 @@ namespace vkcv {
// contains all vertex input attachments used in the vertex buffer
std::vector<VertexAttachment> m_VertexAttachments;
std::vector<std::vector<DescriptorBinding>> m_DescriptorSets;
std::unordered_map<uint32_t, std::unordered_map<uint32_t, DescriptorBinding>> m_DescriptorSets;
size_t m_pushConstantSize = 0;
};
}
#pragma once
#include <vulkan/vulkan.hpp>
namespace vkcv {
enum class ShaderStage
{
VERTEX,
TESS_CONTROL,
TESS_EVAL,
GEOMETRY,
FRAGMENT,
COMPUTE
};
enum class ShaderStage : VkShaderStageFlags {
VERTEX = static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eVertex),
TESS_CONTROL = static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eTessellationControl),
TESS_EVAL = static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eTessellationEvaluation),
GEOMETRY = static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eGeometry),
FRAGMENT = static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eFragment),
COMPUTE = static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eCompute),
TASK = static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eTaskNV),
MESH = static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eMeshNV)
};
using ShaderStages = vk::Flags<ShaderStage>;
constexpr vk::ShaderStageFlags getShaderStageFlags(ShaderStages shaderStages) noexcept {
return vk::ShaderStageFlags(static_cast<VkShaderStageFlags>(shaderStages));
}
constexpr ShaderStages operator|(ShaderStage stage0, ShaderStage stage1) noexcept {
return ShaderStages(stage0) | stage1;
}
constexpr ShaderStages operator&(ShaderStage stage0, ShaderStage stage1) noexcept {
return ShaderStages(stage0) & stage1;
}
constexpr ShaderStages operator^(ShaderStage stage0, ShaderStage stage1) noexcept {
return ShaderStages(stage0) ^ stage1;
}
constexpr ShaderStages operator~(ShaderStage stage) noexcept {
return ~(ShaderStages(stage));
}
}
......@@ -13,6 +13,8 @@ namespace vkcv
class Swapchain final {
private:
friend class Core;
friend class Window;
friend class SwapchainManager;
struct Surface
{
......@@ -20,6 +22,7 @@ namespace vkcv
std::vector<vk::SurfaceFormatKHR> formats;
vk::SurfaceCapabilitiesKHR capabilities;
std::vector<vk::PresentModeKHR> presentModes;
uint32_t presentQueueIndex;
};
Surface m_Surface;
......@@ -34,15 +37,18 @@ namespace vkcv
std::atomic<bool> m_RecreationRequired;
/**
* Constructor of a SwapChain object
* glfw is not initialized in this class because ist must be sure that there exists a context first
* glfw is already initialized by the window class
* @param surface used by the swapchain
* @param swapchain to show images in the window
* @param format
*/
// TODO:
/**
* Constructor of a SwapChain object
* glfw is not initialized in this class because ist must be sure that there exists a context first
* glfw is already initialized by the window class
* @param surface used by the swapchain
* @param swapchain to show images in the window
* @param format of the swapchain
* @param colorSpace of the swapchain
* @param presentMode of the swapchain
* @param imageCount of the swapchain
* @param extent of the swapchain
*/
Swapchain(const Surface &surface,
vk::SwapchainKHR swapchain,
vk::Format format,
......@@ -52,22 +58,20 @@ namespace vkcv
vk::Extent2D extent) noexcept;
/**
* TODO
*
* @return
* checks if the update flag is true
* @return if an update is needed
*/
bool shouldUpdateSwapchain() const;
/**
* TODO
*
* recreates the swapchain
* context
* window
*/
void updateSwapchain(const Context &context, const Window &window);
/**
*
* signal that the swapchain needs to be recreated
*/
void signalSwapchainRecreation();
......@@ -114,13 +118,17 @@ namespace vkcv
uint32_t getImageCount() const;
/**
* TODO
*
* @return
* @return the 2d extent of the swapchain
*/
[[nodiscard]]
const vk::Extent2D& getExtent() const;
};
/**
* @return the familyQueueIndex for the surface
*/
[[nodiscard]]
const uint32_t& getPresentQueueIndex() const;
};
}
#pragma once
/**
* @authors Sebastian Gaida
* @file src/vkcv/Window.hpp
* @brief Window class to handle a basic rendering surface and input
*/
#define NOMINMAX
#include <algorithm>
#include <string>
#include "Event.hpp"
#include "Handles.hpp"
struct GLFWwindow;
namespace vkcv {
class Window {
protected:
GLFWwindow *m_window;
/**
*
* @param GLFWwindow of the class
*/
explicit Window(GLFWwindow *window);
friend class WindowManager;
friend class SwapchainManager;
private:
/**
* mouse callback for moving the mouse on the screen
* @param[in] window The window that received the event.
* @param[in] xpos The new cursor x-coordinate, relative to the left edge of the content area.
* @param[in] ypos The new cursor y-coordinate, relative to the top edge of the content area.
*/
static void onMouseMoveEvent(GLFWwindow *window, double x, double y);
/**
* mouseButton callback for mouse buttons
* @param[in] button The [mouse button](@ref buttons) that was pressed or released.
* @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`. Future releases may add more actions.
* @param[in] mods Bit field describing which [modifier keys](@ref mods) were held down.
*/
static void onMouseButtonEvent(GLFWwindow *callbackWindow, int button, int action, int mods);
/**
* @brief A callback function for handling mouse scrolling events.
* @param[in] callbackWindow The window that received the event.
* @param[in] xoffset The extent of horizontal scrolling.
* @param[in] yoffset The extent of vertical scrolling.
*/
static void onMouseScrollEvent(GLFWwindow *callbackWindow, double xoffset, double yoffset);
/**
* resize callback for the resize option of the window
* @param[in] window The window that was resized.
* @param[in] width The new width, in screen coordinates, of the window.
* @param[in] height The new height, in screen coordinates, of the window.
*/
static void onResize(GLFWwindow *callbackWindow, int width, int height);
/**
* key callback for the pressed key
* @param[in] window The window that received the event.
* @param[in] key The [keyboard key](@ref keys) that was pressed or released.
* @param[in] scancode The system-specific scancode of the key.
* @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`.
* @param[in] mods Bit field describing which [modifier keys](@ref mods) were held down.
*/
static void onKeyEvent(GLFWwindow *callbackWindow, int key, int scancode, int action, int mods);
/**
* char callback for any typed character
* @param[in] window The window that received the event
* @param[in] c The character that got typed
*/
static void onCharEvent(GLFWwindow *callbackWindow, unsigned int c);
/**
* @brief A callback function for gamepad input events.
* @param gamepadIndex The gamepad index.
*/
static void onGamepadEvent(int gamepadIndex);
private:
std::string m_title;
bool m_resizable;
bool m_shouldClose;
GLFWwindow *m_window;
SwapchainHandle m_swapchainHandle;
event_handle<int, int> m_resizeHandle;
public:
/**
* creates a GLFWwindow with the parameters in the function
* @param[in] windowTitle of the window
* @param[in] width of the window (optional)
* @param[in] height of the window (optional)
/**
* creates an uninitialized #Window
*/
Window();
/**
* creates a #Window with the parameters
*
* @param[in] title title of the window
* @param[in] width width of the window (optional)
* @param[in] height height of the window (optional)
* @param[in] resizable resize ability of the window (optional)
* @return Window class
*/
static Window create( const char *windowTitle, int width = -1, int height = -1, bool resizable = false);
explicit Window(const char* title, int width = -1, int height = -1, bool resizable = false);
/**
* Copy-constructor of #Window
*
* @param other Other instance of #Window
*/
Window(const Window& other) = delete;
/**
* Copy-operator of #Window
*
* @param other Other instance of #Window
* @return Reference to itself
*/
Window &operator=(const Window &other) = delete;
/**
* checks if the window is still open, or the close event was called
* This function should be changed/removed later on
* @return bool if the window is still open
*/
[[nodiscard]]
bool isWindowOpen() const;
bool isOpen() const;
/**
* gets the currently focused window and returns it
* only accessible to WindowManager
* @return
*/
static Window& getFocusedWindow();
/**
* checks if any GLFWWindows are open
* @return bool if a window is open
*/
static bool hasOpenWindow();
/**
* polls all events on the GLFWwindow
*/
static void pollEvents();
/**
*
* @return
*/
static const std::vector<std::string>& getExtensions();
/**
* basic events of the window
......@@ -120,26 +103,16 @@ namespace vkcv {
*/
[[nodiscard]]
GLFWwindow *getWindow() const;
/**
* Copy-operator of #Window is deleted!
*
* @param other Other instance of #Window
* @return Reference to itself
*/
Window &operator=(const Window &other) = delete;
/**
* Move-operator of #Window uses default behavior!
*
* @param other Other instance of #Window
* @return Reference to itself
* gets the window title
* @return string with window title
*/
Window &operator=(Window &&other) = default;
[[nodiscard]]
const std::string& getTitle() const;
/**
* gets the window width
* @param window glfwWindow
* @return int with window width
*/
[[nodiscard]]
......@@ -147,23 +120,40 @@ namespace vkcv {
/**
* gets the window height
* @param window glfwWindow
* @return int with window height
*/
[[nodiscard]]
int getHeight() const;
/**
* is the window resizable
* @return bool with window resizable
*/
[[nodiscard]]
bool isResizable() const;
/**
* Destructor of #Window, terminates GLFW
*/
virtual ~Window();
/**
* destroys the window
*/
//void destroyWindow();
/**
* gets the windows framebuffer size
* @param width
* @param height
*/
void getFramebufferSize(int& width, int& height) const;
/**
* gets the SwapchainHandle corresponding to the swapchain of the window
* @return
*/
SwapchainHandle getSwapchainHandle() const;
};
}
......@@ -4,6 +4,8 @@ add_subdirectory(asset_loader)
add_subdirectory(camera)
add_subdirectory(gui)
add_subdirectory(material)
add_subdirectory(meshlet)
add_subdirectory(scene)
add_subdirectory(shader_compiler)
add_subdirectory(testing)
add_subdirectory(upscaling)
......@@ -31,10 +31,10 @@ include(config/FX_GLTF.cmake)
include(config/STB.cmake)
# link the required libraries to the module
target_link_libraries(vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv)
target_link_libraries(vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv ${vkcv_libraries})
# including headers of dependencies and the VkCV framework
target_include_directories(vkcv_asset_loader SYSTEM BEFORE PRIVATE ${vkcv_asset_loader_includes})
target_include_directories(vkcv_asset_loader SYSTEM BEFORE PRIVATE ${vkcv_asset_loader_includes} ${vkcv_includes})
# add the own include directory for public headers
target_include_directories(vkcv_asset_loader BEFORE PUBLIC ${vkcv_asset_loader_include})
......
......@@ -11,17 +11,7 @@
#include <cstdint>
#include <filesystem>
/** These macros define limits of the following structs. Implementations can
* test against these limits when performing sanity checks. The main constraint
* expressed is that of the data type: Material indices are identified by a
* uint8_t in the VertexGroup struct, so there can't be more than UINT8_MAX
* materials in the mesh. Should these limits be too narrow, the data type has
* to be changed, but the current ones should be generous enough for most use
* cases. */
#define MAX_MATERIALS_PER_MESH UINT8_MAX
#define MAX_VERTICES_PER_VERTEX_GROUP UINT32_MAX
/** LOADING MESHES
/* LOADING MESHES
* The description of meshes is a hierarchy of structures with the Mesh at the
* top.
*
......@@ -46,53 +36,89 @@
namespace vkcv::asset {
/** This enum matches modes in fx-gltf, the library returns a standard mode
* (TRIANGLES) if no mode is given in the file. */
/**
* These return codes are limited to the asset loader module. If unified return
* codes are defined for the vkcv framework, these will be used instead.
*/
#define ASSET_ERROR 0
#define ASSET_SUCCESS 1
/**
* This enum matches modes in fx-gltf, the library returns a standard mode
* (TRIANGLES) if no mode is given in the file.
*/
enum class PrimitiveMode : uint8_t {
POINTS=0, LINES, LINELOOP, LINESTRIP, TRIANGLES, TRIANGLESTRIP,
TRIANGLEFAN
POINTS = 0,
LINES = 1,
LINELOOP = 2,
LINESTRIP = 3,
TRIANGLES = 4,
TRIANGLESTRIP = 5,
TRIANGLEFAN = 6
};
/** The indices in the index buffer can be of different bit width. */
enum class IndexType : uint8_t { UNDEFINED=0, UINT8=1, UINT16=2, UINT32=3 };
typedef struct {
// TODO define struct for samplers (low priority)
// NOTE: glTF defines samplers based on OpenGL, which can not be
// directly translated to Vulkan. Specifically, OpenGL (and glTF)
// define a different set of Min/Mag-filters than Vulkan.
} Sampler;
/** struct for defining the loaded texture */
typedef struct {
int sampler; // index into the sampler array of the Scene
uint8_t channels; // number of channels
uint16_t w, h; // width and height of the texture
std::vector<uint8_t> data; // binary data of the decoded texture
} Texture;
/**
* The indices in the index buffer can be of different bit width.
*/
enum class IndexType : uint8_t {
UNDEFINED=0,
UINT8=1,
UINT16=2,
UINT32=3
};
/** The asset loader module only supports the PBR-MetallicRoughness model for
* materials.*/
typedef struct {
uint16_t textureMask; // bit mask with active texture targets
// Indices into the Array.textures array
int baseColor, metalRough, normal, occlusion, emissive;
// Scaling factors for each texture target
struct { float r, g, b, a; } baseColorFactor;
float metallicFactor, roughnessFactor;
float normalScale;
float occlusionStrength;
struct { float r, g, b; } emissiveFactor;
} Material;
/**
* This struct defines a sampler for a texture object. All values here can
* directly be passed to VkSamplerCreateInfo.
* NOTE that glTF defines samplers based on OpenGL, which can not be directly
* translated to Vulkan. The vkcv::asset::Sampler struct defined here adheres
* to the Vulkan spec, having alerady translated the flags from glTF to Vulkan.
* Since glTF does not specify border sampling for more than two dimensions,
* the addressModeW is hardcoded to a default: VK_SAMPLER_ADDRESS_MODE_REPEAT.
*/
struct Sampler {
int minFilter, magFilter;
int mipmapMode;
float minLOD, maxLOD;
int addressModeU, addressModeV, addressModeW;
};
/** Flags for the bit-mask in the Material struct. To check if a material has a
/**
* This struct describes a (partially) loaded texture.
* The data member is not populated after calling probeScene() but only when
* calling loadMesh(), loadScene() or loadTexture(). Note that textures are
* currently always loaded with 4 channels as RGBA, even if the image has just
* RGB or is grayscale. In the case where the glTF-file does not provide a URI
* but references a buffer view for the raw data, the path member will be empty
* even though the rest is initialized properly.
* NOTE: Loading textures without URI is untested.
*/
struct Texture {
std::filesystem::path path; // URI to the encoded texture data
int sampler; // index into the sampler array of the Scene
union { int width; int w; };
union { int height; int h; };
int channels;
std::vector<uint8_t> data; // binary data of the decoded texture
};
/**
* Flags for the bit-mask in the Material struct. To check if a material has a
* certain texture target, you can use the hasTexture() function below, passing
* the material struct and the enum. */
* the material struct and the enum.
*/
enum class PBRTextureTarget {
baseColor=1, metalRough=2, normal=4, occlusion=8, emissive=16
baseColor=1,
metalRough=2,
normal=4,
occlusion=8,
emissive=16
};
/** This macro translates the index of an enum in the defined order to an
/**
* This macro translates the index of an enum in the defined order to an
* integer with a single bit set in the corresponding place. It is used for
* working with the bitmask of texture targets ("textureMask") in the Material
* struct:
......@@ -103,100 +129,196 @@ enum class PBRTextureTarget {
* contact with bit-level operations. */
#define bitflag(ENUM) (0x1u << ((unsigned)(ENUM)))
/** To signal that a certain texture target is active in a Material struct, its
* bit is set in the textureMask. You can use this function to check that:
* Material mat = ...;
* if (materialHasTexture(&mat, baseColor)) {...} */
bool materialHasTexture(const Material *const m, const PBRTextureTarget t);
/**
* The asset loader module only supports the PBR-MetallicRoughness model for
* materials.
*/
struct Material {
uint16_t textureMask; // bit mask with active texture targets
// Indices into the Scene.textures vector
int baseColor, metalRough, normal, occlusion, emissive;
// Scaling factors for each texture target
struct { float r, g, b, a; } baseColorFactor;
float metallicFactor, roughnessFactor;
float normalScale;
float occlusionStrength;
struct { float r, g, b; } emissiveFactor;
/**
* To signal that a certain texture target is active in this Material
* struct, its bit is set in the textureMask. You can use this function
* to check that:
* if (myMaterial.hasTexture(baseColor)) {...}
*
* @param t The target to query for
* @return Boolean to signal whether the texture target is active in
* the material.
*/
bool hasTexture(PBRTextureTarget target) const;
};
/** With these enums, 0 is reserved to signal uninitialized or invalid data. */
/* With these enums, 0 is reserved to signal uninitialized or invalid data. */
enum class PrimitiveType : uint32_t {
UNDEFINED = 0,
POSITION = 1,
NORMAL = 2,
TEXCOORD_0 = 3,
TEXCOORD_1 = 4,
TANGENT = 5
TANGENT = 5,
COLOR_0 = 6,
COLOR_1 = 7,
JOINTS_0 = 8,
WEIGHTS_0 = 9
};
/** These integer values are used the same way in OpenGL, Vulkan and glTF. This
/**
* These integer values are used the same way in OpenGL, Vulkan and glTF. This
* enum is not needed for translation, it's only for the programmers
* convenience (easier to read in if/switch statements etc). While this enum
* exists in (almost) the same definition in the fx-gltf library, we want to
* avoid exposing that dependency, thus it is re-defined here. */
* avoid exposing that dependency, thus it is re-defined here.
*/
enum class ComponentType : uint16_t {
NONE = 0, INT8 = 5120, UINT8 = 5121, INT16 = 5122, UINT16 = 5123,
UINT32 = 5125, FLOAT32 = 5126
NONE = 0,
INT8 = 5120,
UINT8 = 5121,
INT16 = 5122,
UINT16 = 5123,
UINT32 = 5125,
FLOAT32 = 5126
};
/** This struct describes one vertex attribute of a vertex buffer. */
typedef struct {
/**
* This struct describes one vertex attribute of a vertex buffer.
*/
struct VertexAttribute {
PrimitiveType type; // POSITION, NORMAL, ...
uint32_t offset; // offset in bytes
uint32_t length; // length of ... in bytes
uint32_t stride; // stride in bytes
ComponentType componentType; // eg. 5126 for float
uint8_t componentCount; // eg. 3 for vec3
} VertexAttribute;
ComponentType componentType; // eg. 5126 for float
uint8_t componentCount; // eg. 3 for vec3
};
/** This struct represents one (possibly the only) part of a mesh. There is
/**
* This struct represents one (possibly the only) part of a mesh. There is
* always one vertexBuffer and zero or one indexBuffer (indexed rendering is
* common but not always used). If there is no index buffer, this is indicated
* by indexBuffer.data being empty. Each vertex buffer can have one or more
* vertex attributes. */
typedef struct {
* vertex attributes.
*/
struct VertexGroup {
enum PrimitiveMode mode; // draw as points, lines or triangle?
size_t numIndices, numVertices;
size_t numIndices;
size_t numVertices;
struct {
enum IndexType type; // data type of the indices
std::vector<uint8_t> data; // binary data of the index buffer
} indexBuffer;
struct {
std::vector<uint8_t> data; // binary data of the vertex buffer
std::vector<VertexAttribute> attributes; // description of one
} vertexBuffer;
struct { float x, y, z; } min; // bounding box lower left
struct { float x, y, z; } max; // bounding box upper right
int materialIndex; // index to one of the materials
} VertexGroup;
};
/** This struct represents a single mesh as it was loaded from a glTF file. It
/**
* This struct represents a single mesh as it was loaded from a glTF file. It
* consists of at least one VertexGroup, which then references other resources
* such as Materials. */
typedef struct {
* such as Materials.
*/
struct Mesh {
std::string name;
std::array<float, 16> modelMatrix;
std::vector<int> vertexGroups;
} Mesh;
};
/** The scene struct is simply a collection of objects in the scene as well as
/**
* The scene struct is simply a collection of objects in the scene as well as
* the resources used by those objects.
* For now the only type of object are the meshes and they are represented in a
* flat array.
* Note that parent-child relations are not yet possible. */
typedef struct {
* Note that parent-child relations are not yet possible.
*/
struct Scene {
std::vector<Mesh> meshes;
std::vector<VertexGroup> vertexGroups;
std::vector<Material> materials;
std::vector<Texture> textures;
std::vector<Sampler> samplers;
} Scene;
std::vector<std::string> uris;
};
/**
* Load every mesh from the glTF file, as well as materials and textures.
* Parse the given glTF file and create a shallow description of the content.
* Only the meta-data of the objects in the scene is loaded, not the binary
* content. The rationale is to provide a means of probing the content of a
* glTF file without the costly process of loading and decoding large amounts
* of data. The returned Scene struct can be used to search for specific meshes
* in the scene, that can then be loaded on their own using the loadMesh()
* function. Note that the Scene struct received as output argument will be
* overwritten by this function.
* After this function completes, the returned Scene struct is completely
* initialized and all information is final, except for the missing binary
* data. This means that indices to vectors will remain valid even when the
* shallow scene struct is filled with data by loadMesh().
* Note that for URIs only (local) filesystem paths are supported, no
* URLs using network protocols etc.
*
* @param path must be the path to a glTF or glb file.
* @param path must be the path to a glTF- or glb-file.
* @param scene is a reference to a Scene struct that will be filled with the
* meta-data of all objects described in the glTF file.
* @return ASSET_ERROR on failure, otherwise ASSET_SUCCESS
*/
int probeScene(const std::filesystem::path &path, Scene &scene);
/**
* This function loads a single mesh from the given file and adds it to the
* given scene. The scene must already be initialized (via probeScene()).
* The mesh_index refers to the Scenes meshes array and identifies the mesh to
* load. To find the mesh you want, iterate over the probed scene and check the
* meshes details (eg. name).
* Besides the mesh, this function will also add any associated data to the
* Scene struct such as Materials and Textures required by the Mesh.
*
* @param path must be the path to a glTF- or glb-file.
* @param scene is the scene struct to which the results will be written.
* @return ASSET_ERROR on failure, otherwise ASSET_SUCCESS
*/
int loadMesh(Scene &scene, int mesh_index);
/**
* Load every mesh from the glTF file, as well as materials, textures and other
* associated objects.
*
* @param path must be the path to a glTF- or glb-file.
* @param scene is a reference to a Scene struct that will be filled with the
* content of the glTF file being loaded.
* */
int loadScene(const std::string &path, Scene &scene);
struct TextureData {
int width;
int height;
int componentCount;
std::vector<char*> data;
};
TextureData loadTexture(const std::filesystem::path& path);
* @return ASSET_ERROR on failure, otherwise ASSET_SUCCESS
*/
int loadScene(const std::filesystem::path &path, Scene &scene);
/**
* Simply loads a single image at the given path and returns a Texture
* struct describing it. This is for special use cases only (eg.
* loading a font atlas) and not meant to be used for regular assets.
* The sampler is set to -1, signalling that this Texture was loaded
* outside the context of a glTF-file.
* If there was an error loading or decoding the image, the returned struct
* will be cleared to all 0 with path and data being empty; make sure to always
* check that !data.empty() before using the struct.
*
* @param path must be the path to an image file.
* @return Texture struct describing the loaded image.
*/
Texture loadTexture(const std::filesystem::path& path);
}
} // end namespace vkcv::asset
......@@ -29,42 +29,6 @@ namespace vkcv::camera {
float m_fov_min;
float m_fov_max;
/**
* @brief Indicates forward movement of the camera depending on the performed @p action.
* @param[in] action The performed action.
*/
void moveForward(int action);
/**
* @brief Indicates backward movement of the camera depending on the performed @p action.
* @param[in] action The performed action.
*/
void moveBackward(int action);
/**
* @brief Indicates left movement of the camera depending on the performed @p action.
* @param[in] action The performed action.
*/
void moveLeft(int action);
/**
* @brief Indicates right movement of the camera depending on the performed @p action.
* @param[in] action The performed action.
*/
void moveRight(int action);
/**
* @brief Indicates upward movement of the camera depending on the performed @p action.
* @param[in] action The performed action.
*/
void moveUpward(int action);
/**
* @brief Indicates downward movement of the camera depending on the performed @p action.
* @param[in] action The performed action.
*/
void moveDownward(int action);
public:
/**
......
......@@ -52,8 +52,8 @@ namespace vkcv::camera {
}
void CameraManager::mouseMoveCallback(double x, double y){
auto xoffset = static_cast<float>(x - m_lastX);
auto yoffset = static_cast<float>(y - m_lastY);
auto xoffset = static_cast<float>(x - m_lastX) / m_window.getWidth();
auto yoffset = static_cast<float>(y - m_lastY) / m_window.getHeight();
m_lastX = x;
m_lastY = y;
getActiveController().mouseMoveCallback(xoffset, yoffset, getActiveCamera());
......
......@@ -50,11 +50,11 @@ namespace vkcv::camera {
}
// handle yaw rotation
float yaw = camera.getYaw() + static_cast<float>(xOffset) * m_cameraSpeed;
float yaw = camera.getYaw() + static_cast<float>(xOffset) * 90.0f * m_cameraSpeed;
camera.setYaw(yaw);
// handle pitch rotation
float pitch = camera.getPitch() - static_cast<float>(yOffset) * m_cameraSpeed;
float pitch = camera.getPitch() - static_cast<float>(yOffset) * 90.0f * m_cameraSpeed;
pitch = glm::clamp(pitch, -89.0f, 89.0f);
camera.setPitch(pitch);
}
......@@ -82,22 +82,22 @@ namespace vkcv::camera {
void PilotCameraController::keyCallback(int key, int scancode, int action, int mods, Camera &camera) {
switch (key) {
case GLFW_KEY_W:
moveForward(action);
m_forward = static_cast<bool>(action);
break;
case GLFW_KEY_S:
moveBackward(action);
m_backward = static_cast<bool>(action);
break;
case GLFW_KEY_A:
moveLeft(action);
m_left = static_cast<bool>(action);
break;
case GLFW_KEY_D:
moveRight(action);
m_right = static_cast<bool>(action);
break;
case GLFW_KEY_E:
moveUpward(action);
m_upward = static_cast<bool>(action);
break;
case GLFW_KEY_Q:
moveDownward(action);
m_downward = static_cast<bool>(action);
break;
default:
break;
......@@ -109,31 +109,25 @@ namespace vkcv::camera {
}
void PilotCameraController::mouseMoveCallback(double xoffset, double yoffset, Camera &camera) {
if(!m_rotationActive){
return;
}
float sensitivity = 0.05f;
xoffset *= sensitivity;
yoffset *= sensitivity;
xoffset *= static_cast<float>(m_rotationActive);
yoffset *= static_cast<float>(m_rotationActive);
panView(xoffset , yoffset, camera);
}
void PilotCameraController::mouseButtonCallback(int button, int action, int mods, Camera &camera) {
if(button == GLFW_MOUSE_BUTTON_2 && m_rotationActive == false && action == GLFW_PRESS){
m_rotationActive = true;
}
else if(button == GLFW_MOUSE_BUTTON_2 && m_rotationActive == true && action == GLFW_RELEASE){
m_rotationActive = false;
}
if (button == GLFW_MOUSE_BUTTON_2) {
if (m_rotationActive != (action == GLFW_PRESS)) {
m_rotationActive = (action == GLFW_PRESS);
}
}
}
void PilotCameraController::gamepadCallback(int gamepadIndex, Camera &camera, double frametime) {
GLFWgamepadstate gamepadState;
glfwGetGamepadState(gamepadIndex, &gamepadState);
float sensitivity = 100.0f;
float sensitivity = 1.0f;
double threshold = 0.1;
// handle rotations
......@@ -162,29 +156,4 @@ namespace vkcv::camera {
* -copysign(1.0, stickLeftX);
}
void PilotCameraController::moveForward(int action){
m_forward = static_cast<bool>(action);
}
void PilotCameraController::moveBackward(int action){
m_backward = static_cast<bool>(action);
}
void PilotCameraController::moveLeft(int action){
m_left = static_cast<bool>(action);
}
void PilotCameraController::moveRight(int action){
m_right = static_cast<bool>(action);
}
void PilotCameraController::moveUpward(int action){
m_upward = static_cast<bool>(action);
}
void PilotCameraController::moveDownward(int action){
m_downward = static_cast<bool>(action);
}
}
\ No newline at end of file
......@@ -23,10 +23,10 @@ namespace vkcv::camera {
}
// handle yaw rotation
m_yaw = m_yaw + static_cast<float>(xOffset) * m_cameraSpeed;
m_yaw = m_yaw + static_cast<float>(xOffset) * 90.0f * m_cameraSpeed;
// handle pitch rotation
m_pitch = m_pitch + static_cast<float>(yOffset) * m_cameraSpeed;
m_pitch = m_pitch + static_cast<float>(yOffset) * 90.0f * m_cameraSpeed;
}
void TrackballCameraController::updateRadius(double offset, Camera &camera) {
......@@ -67,15 +67,10 @@ namespace vkcv::camera {
}
void TrackballCameraController::mouseMoveCallback(double xoffset, double yoffset, Camera &camera) {
if(!m_rotationActive){
return;
}
float sensitivity = 0.025f;
xoffset *= sensitivity;
yoffset *= sensitivity;
xoffset *= static_cast<float>(m_rotationActive);
yoffset *= static_cast<float>(m_rotationActive);
panView(xoffset , yoffset, camera);
panView(xoffset, yoffset, camera);
}
void TrackballCameraController::mouseButtonCallback(int button, int action, int mods, Camera &camera) {
......@@ -91,7 +86,7 @@ namespace vkcv::camera {
GLFWgamepadstate gamepadState;
glfwGetGamepadState(gamepadIndex, &gamepadState);
float sensitivity = 100.0f;
float sensitivity = 1.0f;
double threshold = 0.1;
// handle rotations
......
......@@ -11,7 +11,7 @@ namespace vkcv::gui {
class GUI final {
private:
Window& m_window;
WindowHandle m_windowHandle;
Core& m_core;
const Context& m_context;
......@@ -33,7 +33,7 @@ namespace vkcv::gui {
* @param core Valid #Core instance of the framework
* @param window Valid #Window instance of the framework
*/
GUI(Core& core, Window& window);
GUI(Core& core, WindowHandle& windowHandle);
GUI(const GUI& other) = delete;
GUI(GUI&& other) = delete;
......
......@@ -6,6 +6,9 @@
namespace vkcv::gui {
const static vk::ImageLayout initialImageLayout = vk::ImageLayout::eColorAttachmentOptimal;
const static vk::ImageLayout finalImageLayout = vk::ImageLayout::ePresentSrcKHR;
static void checkVulkanResult(VkResult resultCode) {
if (resultCode == 0)
return;
......@@ -15,31 +18,33 @@ namespace vkcv::gui {
vkcv_log(LogLevel::ERROR, "ImGui has a problem with Vulkan! (%s)", vk::to_string(result).c_str());
}
GUI::GUI(Core& core, Window& window) :
m_window(window),
GUI::GUI(Core& core, WindowHandle& windowHandle) :
m_windowHandle(windowHandle),
m_core(core),
m_context(m_core.getContext()),
m_gui_context(nullptr) {
IMGUI_CHECKVERSION();
m_gui_context = ImGui::CreateContext();
Window& window = m_core.getWindow(windowHandle);
ImGui_ImplGlfw_InitForVulkan(window.getWindow(), false);
ImGui_ImplGlfw_InitForVulkan(m_window.getWindow(), false);
f_mouseButton = m_window.e_mouseButton.add([&](int button, int action, int mods) {
ImGui_ImplGlfw_MouseButtonCallback(m_window.getWindow(), button, action, mods);
f_mouseButton = window.e_mouseButton.add([&](int button, int action, int mods) {
ImGui_ImplGlfw_MouseButtonCallback(window.getWindow(), button, action, mods);
});
f_mouseScroll = m_window.e_mouseScroll.add([&](double xoffset, double yoffset) {
ImGui_ImplGlfw_ScrollCallback(m_window.getWindow(), xoffset, yoffset);
f_mouseScroll = window.e_mouseScroll.add([&](double xoffset, double yoffset) {
ImGui_ImplGlfw_ScrollCallback(window.getWindow(), xoffset, yoffset);
});
f_key = m_window.e_key.add([&](int key, int scancode, int action, int mods) {
ImGui_ImplGlfw_KeyCallback(m_window.getWindow(), key, scancode, action, mods);
f_key = window.e_key.add([&](int key, int scancode, int action, int mods) {
ImGui_ImplGlfw_KeyCallback(window.getWindow(), key, scancode, action, mods);
});
f_char = m_window.e_char.add([&](unsigned int c) {
ImGui_ImplGlfw_CharCallback(m_window.getWindow(), c);
f_char = window.e_char.add([&](unsigned int c) {
ImGui_ImplGlfw_CharCallback(window.getWindow(), c);
});
vk::DescriptorPoolSize pool_sizes[] = {
......@@ -66,7 +71,7 @@ namespace vkcv::gui {
m_descriptor_pool = m_context.getDevice().createDescriptorPool(descriptorPoolCreateInfo);
const vk::PhysicalDevice& physicalDevice = m_context.getPhysicalDevice();
const Swapchain& swapchain = m_core.getSwapchain();
const Swapchain& swapchain = m_core.getSwapchain(m_windowHandle);
const uint32_t graphicsQueueFamilyIndex = (
m_context.getQueueManager().getGraphicsQueues()[0].familyIndex
......@@ -93,8 +98,8 @@ namespace vkcv::gui {
vk::AttachmentStoreOp::eStore,
vk::AttachmentLoadOp::eDontCare,
vk::AttachmentStoreOp::eDontCare,
vk::ImageLayout::eUndefined,
vk::ImageLayout::ePresentSrcKHR
initialImageLayout,
finalImageLayout
);
const vk::AttachmentReference attachmentReference (
......@@ -152,18 +157,19 @@ namespace vkcv::gui {
GUI::~GUI() {
m_context.getDevice().waitIdle();
Window& window = m_core.getWindow(m_windowHandle);
ImGui_ImplVulkan_Shutdown();
m_context.getDevice().destroyRenderPass(m_render_pass);
m_context.getDevice().destroyDescriptorPool(m_descriptor_pool);
ImGui_ImplGlfw_Shutdown();
m_window.e_mouseButton.remove(f_mouseButton);
m_window.e_mouseScroll.remove(f_mouseScroll);
m_window.e_key.remove(f_key);
m_window.e_char.remove(f_char);
window.e_mouseButton.remove(f_mouseButton);
window.e_mouseScroll.remove(f_mouseScroll);
window.e_key.remove(f_key);
window.e_char.remove(f_char);
if (m_gui_context) {
ImGui::DestroyContext(m_gui_context);
......@@ -171,7 +177,7 @@ namespace vkcv::gui {
}
void GUI::beginGUI() {
const Swapchain& swapchain = m_core.getSwapchain();
const Swapchain& swapchain = m_core.getSwapchain(m_windowHandle);
const auto extent = swapchain.getExtent();
if ((extent.width > 0) && (extent.height > 0)) {
......@@ -194,9 +200,9 @@ namespace vkcv::gui {
return;
}
const Swapchain& swapchain = m_core.getSwapchain();
const Swapchain& swapchain = m_core.getSwapchain(m_windowHandle);
const auto extent = swapchain.getExtent();
const vk::ImageView swapchainImageView = m_core.getSwapchainImageView();
const vk::FramebufferCreateInfo framebufferCreateInfo (
......@@ -215,6 +221,10 @@ namespace vkcv::gui {
submitInfo.queueType = QueueType::Graphics;
m_core.recordAndSubmitCommandsImmediate(submitInfo, [&](const vk::CommandBuffer& commandBuffer) {
assert(initialImageLayout == vk::ImageLayout::eColorAttachmentOptimal);
m_core.prepareImageForAttachmentManually(commandBuffer, vkcv::ImageHandle::createSwapchainImageHandle());
const vk::Rect2D renderArea (
vk::Offset2D(0, 0),
extent
......@@ -227,12 +237,17 @@ namespace vkcv::gui {
0,
nullptr
);
commandBuffer.beginRenderPass(beginInfo, vk::SubpassContents::eInline);
ImGui_ImplVulkan_RenderDrawData(drawData, static_cast<VkCommandBuffer>(commandBuffer));
commandBuffer.endRenderPass();
// executing the renderpass changed the image layout without going through the image manager
// therefore the layout must be updated manually
m_core.updateImageLayoutManual(vkcv::ImageHandle::createSwapchainImageHandle(), finalImageLayout);
}, [&]() {
m_context.getDevice().destroyFramebuffer(framebuffer);
});
......
......@@ -23,6 +23,7 @@ namespace vkcv::material {
MaterialType m_Type;
DescriptorSetHandle m_DescriptorSet;
DescriptorSetLayoutHandle m_DescriptorSetLayout;
std::vector<Texture> m_Textures;
public:
......@@ -40,12 +41,15 @@ namespace vkcv::material {
[[nodiscard]]
const DescriptorSetHandle& getDescriptorSet() const;
[[nodiscard]]
const DescriptorSetLayoutHandle& getDescriptorSetLayout() const;
explicit operator bool() const;
bool operator!() const;
static const std::vector<DescriptorBinding>& getDescriptorBindings(MaterialType type);
static const std::unordered_map<uint32_t ,DescriptorBinding>& getDescriptorBindings(MaterialType type);
static Material createPBR(Core &core,
const ImageHandle &colorImg,
......
......@@ -14,6 +14,10 @@ namespace vkcv::material {
const DescriptorSetHandle & Material::getDescriptorSet() const {
return m_DescriptorSet;
}
const DescriptorSetLayoutHandle & Material::getDescriptorSetLayout() const {
return m_DescriptorSetLayout;
}
Material::operator bool() const {
return (m_Type != MaterialType::UNKNOWN);
......@@ -23,24 +27,25 @@ namespace vkcv::material {
return (m_Type == MaterialType::UNKNOWN);
}
const std::vector<DescriptorBinding>& Material::getDescriptorBindings(MaterialType type)
const DescriptorBindings& Material::getDescriptorBindings(MaterialType type)
{
static std::vector<DescriptorBinding> pbr_bindings;
static std::vector<DescriptorBinding> default_bindings;
static DescriptorBindings pbr_bindings = {};
static DescriptorBindings default_bindings = {};
switch (type) {
case MaterialType::PBR_MATERIAL:
if (pbr_bindings.empty()) {
pbr_bindings.emplace_back(0, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT);
pbr_bindings.emplace_back(1, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT);
pbr_bindings.emplace_back(2, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT);
pbr_bindings.emplace_back(3, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT);
pbr_bindings.emplace_back(4, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT);
pbr_bindings.emplace_back(5, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT);
pbr_bindings.emplace_back(6, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT);
pbr_bindings.emplace_back(7, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT);
pbr_bindings.emplace_back(8, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT);
pbr_bindings.emplace_back(9, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT);
if (pbr_bindings.empty())
{
pbr_bindings.insert(std::make_pair(0, DescriptorBinding(0, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT)));
pbr_bindings.insert(std::make_pair(1, DescriptorBinding(1, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT)));
pbr_bindings.insert(std::make_pair(2, DescriptorBinding(2, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT)));
pbr_bindings.insert(std::make_pair(3, DescriptorBinding(3, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT)));
pbr_bindings.insert(std::make_pair(4, DescriptorBinding(4, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT)));
pbr_bindings.insert(std::make_pair(5, DescriptorBinding(5, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT)));
pbr_bindings.insert(std::make_pair(6, DescriptorBinding(6, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT)));
pbr_bindings.insert(std::make_pair(7, DescriptorBinding(7, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT)));
pbr_bindings.insert(std::make_pair(8, DescriptorBinding(8, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT)));
pbr_bindings.insert(std::make_pair(9, DescriptorBinding(9, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT)));
}
return pbr_bindings;
......@@ -163,7 +168,8 @@ namespace vkcv::material {
material.m_Type = MaterialType::PBR_MATERIAL;
const auto& bindings = getDescriptorBindings(material.m_Type);
material.m_DescriptorSet = core.createDescriptorSet(bindings);;
material.m_DescriptorSetLayout = core.createDescriptorSetLayout(bindings);
material.m_DescriptorSet = core.createDescriptorSet(material.m_DescriptorSetLayout);;
material.m_Textures.reserve(bindings.size());
material.m_Textures.push_back({ images[0], samplers[0], std::vector<float>(baseColorFactor, baseColorFactor+4) });
......
cmake_minimum_required(VERSION 3.16)
project(vkcv_meshlet)
# setting c++ standard for the module
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(vkcv_meshlet_source ${PROJECT_SOURCE_DIR}/src)
set(vkcv_meshlet_include ${PROJECT_SOURCE_DIR}/include)
# Add source and header files to the module
set(vkcv_meshlet_sources
${vkcv_meshlet_include}/vkcv/meshlet/Meshlet.hpp
${vkcv_meshlet_source}/vkcv/meshlet/Meshlet.cpp
${vkcv_meshlet_include}/vkcv/meshlet/Tipsify.hpp
${vkcv_meshlet_source}/vkcv/meshlet/Tipsify.cpp
${vkcv_meshlet_include}/vkcv/meshlet/Forsyth.hpp
${vkcv_meshlet_source}/vkcv/meshlet/Forsyth.cpp)
# adding source files to the module
add_library(vkcv_meshlet STATIC ${vkcv_meshlet_sources})
# link the required libraries to the module
target_link_libraries(vkcv_meshlet vkcv ${vkcv_libraries})
# including headers of dependencies and the VkCV framework
target_include_directories(vkcv_meshlet SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include})
# add the own include directory for public headers
target_include_directories(vkcv_meshlet BEFORE PUBLIC ${vkcv_meshlet_include})
# linking with libraries from all dependencies and the VkCV framework
target_link_libraries(vkcv_meshlet vkcv vkcv_asset_loader vkcv_camera)