Skip to content
Snippets Groups Projects
Commit 061f87fe authored by Alexander Gauggel's avatar Alexander Gauggel
Browse files

Merge branch '79-rework-asset-loader-api' into 'develop'

Resolve "Rework Asset Loader API"

Closes #79

See merge request !69
parents ab528a93 d20e4137
No related branches found
No related tags found
1 merge request!69Resolve "Rework Asset Loader API"
Pipeline #26864 failed
...@@ -22,9 +22,6 @@ ...@@ -22,9 +22,6 @@
[submodule "modules/gui/lib/imgui"] [submodule "modules/gui/lib/imgui"]
path = modules/gui/lib/imgui path = modules/gui/lib/imgui
url = https://github.com/ocornut/imgui.git url = https://github.com/ocornut/imgui.git
[submodule "lib/VulkanMemoryAllocator"]
path = lib/VulkanMemoryAllocator
url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git
[submodule "lib/VulkanMemoryAllocator-Hpp"] [submodule "lib/VulkanMemoryAllocator-Hpp"]
path = lib/VulkanMemoryAllocator-Hpp path = lib/VulkanMemoryAllocator-Hpp
url = https://github.com/malte-v/VulkanMemoryAllocator-Hpp.git url = https://github.com/malte-v/VulkanMemoryAllocator-Hpp.git
......
...@@ -53,9 +53,10 @@ namespace vkcv { ...@@ -53,9 +53,10 @@ namespace vkcv {
VKCV_DEBUG_MESSAGE_LEN, \ VKCV_DEBUG_MESSAGE_LEN, \
__VA_ARGS__ \ __VA_ARGS__ \
); \ ); \
auto output = getLogOutput(level); \
if (level != vkcv::LogLevel::RAW_INFO) { \ if (level != vkcv::LogLevel::RAW_INFO) { \
fprintf( \ fprintf( \
getLogOutput(level), \ output, \
"[%s]: %s [%s, line %d: %s]\n", \ "[%s]: %s [%s, line %d: %s]\n", \
vkcv::getLogName(level), \ vkcv::getLogName(level), \
output_message, \ output_message, \
...@@ -65,12 +66,13 @@ namespace vkcv { ...@@ -65,12 +66,13 @@ namespace vkcv {
); \ ); \
} else { \ } else { \
fprintf( \ fprintf( \
getLogOutput(level), \ output, \
"[%s]: %s\n", \ "[%s]: %s\n", \
vkcv::getLogName(level), \ vkcv::getLogName(level), \
output_message \ output_message \
); \ ); \
} \ } \
fflush(output); \
} }
#else #else
......
...@@ -31,10 +31,10 @@ include(config/FX_GLTF.cmake) ...@@ -31,10 +31,10 @@ include(config/FX_GLTF.cmake)
include(config/STB.cmake) include(config/STB.cmake)
# link the required libraries to the module # 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 # 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 # add the own include directory for public headers
target_include_directories(vkcv_asset_loader BEFORE PUBLIC ${vkcv_asset_loader_include}) target_include_directories(vkcv_asset_loader BEFORE PUBLIC ${vkcv_asset_loader_include})
......
...@@ -11,17 +11,7 @@ ...@@ -11,17 +11,7 @@
#include <cstdint> #include <cstdint>
#include <filesystem> #include <filesystem>
/** These macros define limits of the following structs. Implementations can /* LOADING MESHES
* 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
* The description of meshes is a hierarchy of structures with the Mesh at the * The description of meshes is a hierarchy of structures with the Mesh at the
* top. * top.
* *
...@@ -46,53 +36,89 @@ ...@@ -46,53 +36,89 @@
namespace vkcv::asset { 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 { enum class PrimitiveMode : uint8_t {
POINTS=0, LINES, LINELOOP, LINESTRIP, TRIANGLES, TRIANGLESTRIP, POINTS = 0,
TRIANGLEFAN 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 }; * The indices in the index buffer can be of different bit width.
*/
typedef struct { enum class IndexType : uint8_t {
// TODO define struct for samplers (low priority) UNDEFINED=0,
// NOTE: glTF defines samplers based on OpenGL, which can not be UINT8=1,
// directly translated to Vulkan. Specifically, OpenGL (and glTF) UINT16=2,
// define a different set of Min/Mag-filters than Vulkan. UINT32=3
} 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 asset loader module only supports the PBR-MetallicRoughness model for /**
* materials.*/ * This struct defines a sampler for a texture object. All values here can
typedef struct { * directly be passed to VkSamplerCreateInfo.
uint16_t textureMask; // bit mask with active texture targets * NOTE that glTF defines samplers based on OpenGL, which can not be directly
// Indices into the Array.textures array * translated to Vulkan. The vkcv::asset::Sampler struct defined here adheres
int baseColor, metalRough, normal, occlusion, emissive; * to the Vulkan spec, having alerady translated the flags from glTF to Vulkan.
// Scaling factors for each texture target * Since glTF does not specify border sampling for more than two dimensions,
struct { float r, g, b, a; } baseColorFactor; * the addressModeW is hardcoded to a default: VK_SAMPLER_ADDRESS_MODE_REPEAT.
float metallicFactor, roughnessFactor; */
float normalScale; struct Sampler {
float occlusionStrength; int minFilter, magFilter;
struct { float r, g, b; } emissiveFactor; int mipmapMode;
} Material; 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 * 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 { 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 * 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 * working with the bitmask of texture targets ("textureMask") in the Material
* struct: * struct:
...@@ -103,100 +129,196 @@ enum class PBRTextureTarget { ...@@ -103,100 +129,196 @@ enum class PBRTextureTarget {
* contact with bit-level operations. */ * contact with bit-level operations. */
#define bitflag(ENUM) (0x1u << ((unsigned)(ENUM))) #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: * The asset loader module only supports the PBR-MetallicRoughness model for
* Material mat = ...; * materials.
* if (materialHasTexture(&mat, baseColor)) {...} */ */
bool materialHasTexture(const Material *const m, const PBRTextureTarget t); 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 { enum class PrimitiveType : uint32_t {
UNDEFINED = 0, UNDEFINED = 0,
POSITION = 1, POSITION = 1,
NORMAL = 2, NORMAL = 2,
TEXCOORD_0 = 3, TEXCOORD_0 = 3,
TEXCOORD_1 = 4, 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 * enum is not needed for translation, it's only for the programmers
* convenience (easier to read in if/switch statements etc). While this enum * 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 * 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 { enum class ComponentType : uint16_t {
NONE = 0, INT8 = 5120, UINT8 = 5121, INT16 = 5122, UINT16 = 5123, NONE = 0,
UINT32 = 5125, FLOAT32 = 5126 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, ... PrimitiveType type; // POSITION, NORMAL, ...
uint32_t offset; // offset in bytes uint32_t offset; // offset in bytes
uint32_t length; // length of ... in bytes uint32_t length; // length of ... in bytes
uint32_t stride; // stride in bytes uint32_t stride; // stride in bytes
ComponentType componentType; // eg. 5126 for float
uint8_t componentCount; // eg. 3 for vec3 ComponentType componentType; // eg. 5126 for float
} VertexAttribute; 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 * 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 * 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 * by indexBuffer.data being empty. Each vertex buffer can have one or more
* vertex attributes. */ * vertex attributes.
typedef struct { */
struct VertexGroup {
enum PrimitiveMode mode; // draw as points, lines or triangle? enum PrimitiveMode mode; // draw as points, lines or triangle?
size_t numIndices, numVertices; size_t numIndices;
size_t numVertices;
struct { struct {
enum IndexType type; // data type of the indices enum IndexType type; // data type of the indices
std::vector<uint8_t> data; // binary data of the index buffer std::vector<uint8_t> data; // binary data of the index buffer
} indexBuffer; } indexBuffer;
struct { struct {
std::vector<uint8_t> data; // binary data of the vertex buffer std::vector<uint8_t> data; // binary data of the vertex buffer
std::vector<VertexAttribute> attributes; // description of one std::vector<VertexAttribute> attributes; // description of one
} vertexBuffer; } vertexBuffer;
struct { float x, y, z; } min; // bounding box lower left struct { float x, y, z; } min; // bounding box lower left
struct { float x, y, z; } max; // bounding box upper right struct { float x, y, z; } max; // bounding box upper right
int materialIndex; // index to one of the materials 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 * consists of at least one VertexGroup, which then references other resources
* such as Materials. */ * such as Materials.
typedef struct { */
struct Mesh {
std::string name; std::string name;
std::array<float, 16> modelMatrix; std::array<float, 16> modelMatrix;
std::vector<int> vertexGroups; 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. * the resources used by those objects.
* For now the only type of object are the meshes and they are represented in a * Note that parent-child relations are not yet possible.
* flat array. */
* Note that parent-child relations are not yet possible. */ struct Scene {
typedef struct {
std::vector<Mesh> meshes; std::vector<Mesh> meshes;
std::vector<VertexGroup> vertexGroups; std::vector<VertexGroup> vertexGroups;
std::vector<Material> materials; std::vector<Material> materials;
std::vector<Texture> textures; std::vector<Texture> textures;
std::vector<Sampler> samplers; 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 * @param scene is a reference to a Scene struct that will be filled with the
* content of the glTF file being loaded. * content of the glTF file being loaded.
* */ * @return ASSET_ERROR on failure, otherwise ASSET_SUCCESS
int loadScene(const std::string &path, Scene &scene); */
int loadScene(const std::filesystem::path &path, Scene &scene);
struct TextureData {
int width; /**
int height; * Simply loads a single image at the given path and returns a Texture
int componentCount; * struct describing it. This is for special use cases only (eg.
std::vector<char*> data; * 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
TextureData loadTexture(const std::filesystem::path& path); * 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
This diff is collapsed.
first_triangle mesh_shader
\ No newline at end of file
...@@ -49,7 +49,7 @@ namespace vkcv { ...@@ -49,7 +49,7 @@ namespace vkcv {
usageFlags = vk::BufferUsageFlagBits::eIndexBuffer; usageFlags = vk::BufferUsageFlagBits::eIndexBuffer;
break; break;
default: default:
// TODO: maybe an issue vkcv_log(LogLevel::WARNING, "Unknown buffer type");
break; break;
} }
...@@ -81,6 +81,10 @@ namespace vkcv { ...@@ -81,6 +81,10 @@ namespace vkcv {
break; break;
} }
if (type == BufferType::STAGING) {
memoryUsage = vma::MemoryUsage::eCpuToGpu;
}
auto bufferAllocation = allocator.createBuffer( auto bufferAllocation = allocator.createBuffer(
vk::BufferCreateInfo(createFlags, size, usageFlags), vk::BufferCreateInfo(createFlags, size, usageFlags),
vma::AllocationCreateInfo( vma::AllocationCreateInfo(
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment