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

Merge branch '26-asset-loading' into 'develop'

Resolve "Asset Loading"

Closes #26

See merge request !19
parents c753b153 b97ce5e9
No related branches found
No related tags found
1 merge request!19Resolve "Asset Loading"
Pipeline #25191 passed
Showing
with 464 additions and 0 deletions
*.blend filter=lfs diff=lfs merge=lfs -text
*.blend1 filter=lfs diff=lfs merge=lfs -text
*.bin filter=lfs diff=lfs merge=lfs -text
*.glb filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.tif filter=lfs diff=lfs merge=lfs -text
*.psd filter=lfs diff=lfs merge=lfs -text
*.gltf filter=lfs diff=lfs merge=lfs
<<<<<<< HEAD
=======
>>>>>>> develop
# IDE specific files
.project
.cproject
......
[submodule "lib/glfw"]
path = lib/glfw
url = https://github.com/glfw/glfw.git
[submodule "modules/asset_loader/lib/fx-gltf"]
path = modules/asset_loader/lib/fx-gltf
url = https://github.com/jessey-git/fx-gltf.git
[submodule "modules/asset_loader/lib/json"]
path = modules/asset_loader/lib/json
url = https://github.com/nlohmann/json.git
[submodule "modules/asset_loader/lib/stb"]
path = modules/asset_loader/lib/stb
url = https://github.com/nothings/stb.git
[submodule "modules/camera/lib/glm"]
path = modules/camera/lib/glm
url = https://github.com/g-truc/glm.git
......@@ -3,6 +3,12 @@
![Vulkan-Chan](https://gitlab.uni-koblenz.de/uploads/-/system/project/avatar/3712/VulkanChan.jpg)
## Repository
Git LFS is used for bigger resource files like meshes and textures. So you need to install Git LFS and use `git lfs install` after cloning.
More information about Git LFS [here](https://git-lfs.github.com/).
## Build
Git submodules are used for libraries.
......
# Add new modules here:
add_subdirectory(asset_loader)
add_subdirectory(camera)
add_subdirectory(testing)
cmake_minimum_required(VERSION 3.16)
project(vkcv_asset_loader)
# setting c++ standard for the module
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(vkcv_asset_loader_source ${PROJECT_SOURCE_DIR}/src)
set(vkcv_asset_loader_include ${PROJECT_SOURCE_DIR}/include)
# Add source and header files to the module
set(vkcv_asset_loader_sources
${vkcv_asset_loader_include}/vkcv/asset/asset_loader.hpp
${vkcv_asset_loader_source}/vkcv/asset/asset_loader.cpp
)
# adding source files to the module
add_library(vkcv_asset_loader STATIC ${vkcv_asset_loader_sources})
# Setup some path variables to load libraries
set(vkcv_asset_loader_lib lib)
set(vkcv_asset_loader_lib_path ${PROJECT_SOURCE_DIR}/${vkcv_asset_loader_lib})
# Check and load NLOHMANN_JSON
include(config/NLOHMANN_JSON.cmake)
# Check and load FX-GLTF
include(config/FX_GLTF.cmake)
# Check and load STB
include(config/STB.cmake)
# link the required libraries to the module
target_link_libraries(vkcv_asset_loader ${vkcv_asset_loader_libraries})
# including headers of dependencies and the VkCV framework
target_include_directories(vkcv_asset_loader SYSTEM BEFORE PRIVATE ${vkcv_asset_loader_includes})
# add the own include directory for public headers
target_include_directories(vkcv_asset_loader BEFORE PUBLIC ${vkcv_asset_loader_include})
if (EXISTS "${vkcv_asset_loader_lib_path}/fx-gltf")
set(FX_GLTF_INSTALL OFF CACHE INTERNAL "")
set(FX_GLTF_USE_INSTALLED_DEPS OFF CACHE INTERNAL "")
set(BUILD_TESTING OFF CACHE INTERNAL "")
add_subdirectory(${vkcv_asset_loader_lib}/fx-gltf)
list(APPEND vkcv_asset_loader_libraries fx-gltf)
else()
message(WARNING "FX-GLTF is required..! Update the submodules!")
endif ()
if (EXISTS "${vkcv_asset_loader_lib_path}/json")
set(JSON_BuildTests OFF CACHE INTERNAL "")
add_subdirectory(${vkcv_asset_loader_lib}/json)
list(APPEND vkcv_asset_loader_libraries nlohmann_json::nlohmann_json)
else()
message(WARNING "NLOHMANN_JSON is required..! Update the submodules!")
endif ()
if (EXISTS "${vkcv_asset_loader_lib_path}/stb")
list(APPEND vkcv_asset_loader_includes ${vkcv_asset_loader_lib}/stb)
else()
message(WARNING "STB is required..! Update the submodules!")
endif ()
#pragma once
/**
* @authors Trevor Hollmann
* @file include/vkcv/asset/asset_loader.h
* @brief Interface of the asset loader module for the vkcv framework.
*/
#include <string>
#include <vector>
#include <cstdint>
/* 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
* The description of meshes is a hierarchy of structures with the Mesh at the
* top.
*
* Each Mesh has an array of one or more vertex groups (called "primitives" in
* glTF parlance) and an array of zero or more Materials.
*
* Each vertex group describes a part of the meshes vertices by defining how
* they should be rendered (as points, lines, triangles), how many indices and
* vertices there are, how the content of the vertex buffer is to be
* interpreted and which material from the Meshes materials array should be
* used for the surface of the vertices.
* As a bonus there is also the axis aligned bounding box of the vertices.
*
* The vertex buffer is presented as a single block of binary data with a given
* length in bytes.
* The layout of the vertex buffer is described by an array of VertexAttribute
* structs that define the type of attribute, the offset, length and stride in
* bytes and number and type of components of the attribute.
* These values can directly be given to vulkan when describing the content of
* vertex buffers. */
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. */
enum PrimitiveMode {
POINTS=0, LINES, LINELOOP, LINESTRIP, TRIANGLES, TRIANGLESTRIP,
TRIANGLEFAN
};
/* With these enums, 0 is reserved to signal uninitialized or invalid data. */
enum PrimitiveType { POSITION=1, NORMAL, TEXCOORD_0 };
/* The indices in the index buffer can be of different bit width. */
enum IndexType { UINT32=0, UINT16=1, UINT8=2 };
typedef struct {
// TODO not yet needed for the first (unlit) triangle
} Material;
/* This struct describes one vertex attribute of a vertex buffer. */
typedef struct {
PrimitiveType type; // POSITION, NORMAL, ...
uint32_t offset; // offset in bytes
uint32_t length; // length of ... in bytes
uint32_t stride; // stride in bytes
uint16_t componentType; // eg. 5126 for float
uint8_t componentCount; // eg. 3 for vec3
} VertexAttribute;
/* 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 {
enum PrimitiveMode mode; // draw as points, lines or triangle?
size_t numIndices, 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;
} vertexBuffer;
struct { float x, y, z; } min; // bounding box lower left
struct { float x, y, z; } max; // bounding box upper right
uint8_t materialIndex; // index to one of the meshes materials
} VertexGroup;
/* This struct represents a single mesh loaded from a glTF file. It consists of
* at least one VertexVroup and any number of Materials. */
typedef struct {
std::string name;
std::vector<VertexGroup> vertexGroups;
std::vector<Material> materials;
// FIXME Dirty hack to get one(!) texture for our cube demo
// hardcoded to always have RGBA channel layout
struct {
int w, h, ch; // width, height and channels of image
uint8_t *img; // raw bytes, free after use (deal with it)
} texture_hack;
} Mesh;
/**
* In its first iteration the asset loader module will only allow loading
* single meshes, one per glTF file.
* It will later be extended to allow loading entire scenes from glTF files.
*
* @param path must be the path to a glTF file containing a single mesh.
* @param mesh is a reference to a Mesh struct that will be filled with the
* content of the glTF file being loaded.
* */
int loadMesh(const std::string &path, Mesh &mesh);
}
Subproject commit f4f18f2017a049a23748c9c9aad42ba2de20bfd5
Subproject commit 0972f7ff0e651f09a306dba791cc42024b8642c1
Subproject commit c9064e317699d2e495f36ba4f9ac037e88ee371a
#include "vkcv/asset/asset_loader.hpp"
#include <iostream>
#include <string.h> // memcpy(3)
#include <stdlib.h> // calloc(3)
#include <fx/gltf.h>
#define STB_IMAGE_IMPLEMENTATION
#define STBI_ONLY_JPEG
#include <stb_image.h>
namespace vkcv::asset {
/**
* convert the accessor type from the fx-gltf library to an unsigned int
* @param type
* @return unsigned integer representation
*/
// TODO Return proper error code (we need to define those as macros or enums,
// will discuss during the next core meeting if that should happen on the scope
// of the vkcv framework or just this module)
uint8_t convertTypeToInt(const fx::gltf::Accessor::Type type) {
switch (type) {
case fx::gltf::Accessor::Type::None :
return 0;
case fx::gltf::Accessor::Type::Scalar :
return 1;
case fx::gltf::Accessor::Type::Vec2 :
return 2;
case fx::gltf::Accessor::Type::Vec3 :
return 3;
case fx::gltf::Accessor::Type::Vec4 :
return 4;
default: return 10; // TODO add cases for matrices (or maybe change the type in the struct itself)
}
}
/**
* This function unrolls nested exceptions via recursion and prints them
* @param e error code
* @param path path to file that is responsible for error
*/
void print_what (const std::exception& e, const std::string &path) {
fprintf(stderr, "ERROR loading file %s: %s\n", path.c_str(), e.what());
try {
std::rethrow_if_nested(e);
} catch (const std::exception& nested) {
std::cerr << "nested: ";
print_what(nested, path);
}
}
int loadMesh(const std::string &path, Mesh &mesh) {
fx::gltf::Document object;
try {
if (path.rfind(".glb", (path.length()-4)) != std::string::npos) {
object = fx::gltf::LoadFromBinary(path);
} else {
object = fx::gltf::LoadFromText(path);
}
} catch (const std::system_error &err) {
print_what(err, path);
return 0;
} catch (const std::exception &e) {
print_what(e, path);
return 0;
}
// TODO Temporary restriction: Only one mesh per glTF file allowed
// currently. Later, we want to support whole scenes with more than
// just meshes.
if (object.meshes.size() != 1) return 0;
fx::gltf::Mesh const &objectMesh = object.meshes[0];
// TODO We want to support more than one vertex group per mesh
// eventually... right now this is hard-coded to use only the first one
// because we only care about the example triangle and cube
fx::gltf::Primitive const &objectPrimitive = objectMesh.primitives[0];
fx::gltf::Accessor posAccessor;
std::vector<VertexAttribute> vertexAttributes;
vertexAttributes.reserve(objectPrimitive.attributes.size());
for (auto const & attrib : objectPrimitive.attributes) {
fx::gltf::Accessor accessor = object.accessors[attrib.second];
VertexAttribute attribute;
if (attrib.first == "POSITION") {
attribute.type = POSITION;
posAccessor = accessor;
} else if (attrib.first == "NORMAL") {
attribute.type = NORMAL;
} else if (attrib.first == "TEXCOORD_0") {
attribute.type = TEXCOORD_0;
} else {
return 0;
}
attribute.offset = object.bufferViews[accessor.bufferView].byteOffset;
attribute.length = object.bufferViews[accessor.bufferView].byteLength;
attribute.stride = object.bufferViews[accessor.bufferView].byteStride;
attribute.componentType = static_cast<uint16_t>(accessor.componentType);
if (convertTypeToInt(accessor.type) != 10) {
attribute.componentCount = convertTypeToInt(accessor.type);
} else {
return 0;
}
vertexAttributes.push_back(attribute);
}
// TODO consider the case where there is no index buffer (not all
// meshes have to use indexed rendering)
const fx::gltf::Accessor &indexAccessor = object.accessors[objectPrimitive.indices];
const fx::gltf::BufferView &indexBufferView = object.bufferViews[indexAccessor.bufferView];
const fx::gltf::Buffer &indexBuffer = object.buffers[indexBufferView.buffer];
std::vector<uint8_t> indexBufferData;
indexBufferData.resize(indexBufferView.byteLength);
{
const size_t off = indexBufferView.byteOffset;
const void *const ptr = ((char*)indexBuffer.data.data()) + off;
if (!memcpy(indexBufferData.data(), ptr, indexBufferView.byteLength)) {
std::cerr << "ERROR copying index buffer data.\n";
return 0;
}
}
const fx::gltf::BufferView& vertexBufferView = object.bufferViews[posAccessor.bufferView];
const fx::gltf::Buffer& vertexBuffer = object.buffers[vertexBufferView.buffer];
std::vector<uint8_t> vertexBufferData;
vertexBufferData.resize(vertexBufferView.byteLength);
{
const size_t off = vertexBufferView.byteOffset;
const void *const ptr = ((char*)vertexBuffer.data.data()) + off;
if (!memcpy(vertexBufferData.data(), ptr, vertexBufferView.byteLength)) {
std::cerr << "ERROR copying vertex buffer data.\n";
return 0;
}
}
IndexType indexType;
switch(indexAccessor.componentType) {
case fx::gltf::Accessor::ComponentType::UnsignedByte:
indexType = UINT8; break;
case fx::gltf::Accessor::ComponentType::UnsignedShort:
indexType = UINT16; break;
case fx::gltf::Accessor::ComponentType::UnsignedInt:
indexType = UINT32; break;
default:
std::cerr << "ERROR: Index type not supported: " <<
static_cast<uint16_t>(indexAccessor.componentType) <<
std::endl;
return 0;
}
const size_t numVertexGroups = objectMesh.primitives.size();
std::vector<VertexGroup> vertexGroups;
vertexGroups.reserve(numVertexGroups);
vertexGroups.push_back({
static_cast<PrimitiveMode>(objectPrimitive.mode),
object.accessors[objectPrimitive.indices].count,
posAccessor.count,
{indexType, indexBufferData},
{vertexBufferData, vertexAttributes},
{posAccessor.min[0], posAccessor.min[1], posAccessor.min[2]},
{posAccessor.max[0], posAccessor.max[1], posAccessor.max[2]},
static_cast<uint8_t>(objectPrimitive.material)
});
std::vector<Material> materials;
mesh = {
object.meshes[0].name,
vertexGroups,
materials,
0, 0, 0, NULL
};
// FIXME HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK
// fail quietly if there is no texture
if (object.textures.size()) {
const std::string mime_type("image/jpeg");
const fx::gltf::Texture &tex = object.textures[0];
const fx::gltf::Image &img = object.images[tex.source];
#ifndef NDEBUG
printf("texture name=%s sampler=%u source=%u\n",
tex.name.c_str(), tex.sampler, tex.source);
printf("image name=%s uri=%s mime=%s\n", img.name.c_str(),
img.uri.c_str(), img.mimeType.c_str());
#endif
size_t pos = path.find_last_of("/");
auto dir = path.substr(0, pos);
mesh.texture_hack.img = stbi_load((dir + "/" + img.uri).c_str(),
&mesh.texture_hack.w, &mesh.texture_hack.h,
&mesh.texture_hack.ch, 4);
}
// FIXME HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK
return 1;
}
}
# Add new projects/examples here:
add_subdirectory(first_triangle)
add_subdirectory(first_mesh)
first_mesh
\ No newline at end of file
cmake_minimum_required(VERSION 3.16)
project(first_mesh)
# setting c++ standard for the project
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# this should fix the execution path to load local files from the project
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# adding source files to the project
add_executable(first_mesh src/main.cpp)
# this should fix the execution path to load local files from the project (for MSVC)
if(MSVC)
set_target_properties(first_mesh PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
set_target_properties(first_mesh PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
# in addition to setting the output directory, the working directory has to be set
# by default visual studio sets the working directory to the build directory, when using the debugger
set_target_properties(first_mesh PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
endif()
# including headers of dependencies and the VkCV framework
target_include_directories(first_mesh SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include})
# linking with libraries from all dependencies and the VkCV framework
target_link_libraries(first_mesh vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries})
projects/first_mesh/resources/cube/boards2_vcyc_jpg.jpg

132 B

File added
File added
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