diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..620ef7d8332699a31fb89fa5f93c99ad389e262e --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +*.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 diff --git a/.gitignore b/.gitignore index f64181f0e271d7eba3ba146d8dc9cbf229c5f285..d2bf98a016f588760241f9dc7f90f6197c458404 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ +<<<<<<< HEAD +======= +>>>>>>> develop # IDE specific files .project .cproject diff --git a/.gitmodules b/.gitmodules index 323286b592292b798a8b6ca03dde3651dd36239e..983b753744e8767da0ec3c959c32a3766ee346f6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,15 @@ [submodule "lib/SPIRV-Cross"] path = lib/SPIRV-Cross url = https://github.com/KhronosGroup/SPIRV-Cross.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 \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 569efcf20fe84a64205b7060bbb89587cbe579da..ddcb8274be02c5265924bc9b69c39f788ba50132 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,12 +72,15 @@ endif() # add include directories from dependencies as system includes target_include_directories(vkcv SYSTEM BEFORE PRIVATE ${vkcv_includes}) +message(STATUS ${vkcv_includes}) # add the own include directory for public headers target_include_directories(vkcv BEFORE PUBLIC ${vkcv_include}) +message(STATUS ${vkcv_include}) # link the framework using all required libraries target_link_libraries(vkcv ${vkcv_libraries}) +message(STATUS ${vkcv_libraries}) # add sub-projects/examples as targets add_subdirectory(projects) diff --git a/README.md b/README.md index 520b6242f86ccc3db6e1de78cf16ec26d550efb2..4289a74308776017f099d048cf749c9693e6609f 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,12 @@  +## 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. diff --git a/include/vkcv/BufferManager.hpp b/include/vkcv/BufferManager.hpp index eb8cb178d1b844fff57892c7312ed71d325900cc..abda720f98f649d64b17a15848ae99320e11ae3e 100644 --- a/include/vkcv/BufferManager.hpp +++ b/include/vkcv/BufferManager.hpp @@ -6,6 +6,7 @@ namespace vkcv { enum class BufferType { + INDEX, VERTEX, UNIFORM, STORAGE, @@ -61,6 +62,26 @@ namespace vkcv */ uint64_t createBuffer(BufferType type, size_t size, BufferMemoryType memoryType); + /** + * Returns the Vulkan buffer handle of a buffer + * represented by a given buffer handle id. + * + * @param id Buffer handle id + * @return Vulkan buffer handle + */ + [[nodiscard]] + vk::Buffer getBuffer(uint64_t id) const; + + /** + * Returns the Vulkan device memory handle of a buffer + * represented by a given buffer handle id. + * + * @param id Buffer handle id + * @return Vulkan device memory handle + */ + [[nodiscard]] + vk::DeviceMemory getDeviceMemory(uint64_t id) const; + /** * Fills a buffer represented by a given buffer * handle id with custom data. diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp index e2fc5c587247a915f5bca345f8077f33ee5d8977..66db4776d053352d8ccb2eea5e09c3fb77b68561 100644 --- a/include/vkcv/Core.hpp +++ b/include/vkcv/Core.hpp @@ -187,7 +187,7 @@ namespace vkcv * @brief render a beautiful triangle */ void renderTriangle(const PassHandle renderpassHandle, const PipelineHandle pipelineHandle, - const int width, const int height); + const int width, const int height, const size_t pushConstantSize, const void* pushConstantData); /** * @brief end recording and present image diff --git a/include/vkcv/Event.hpp b/include/vkcv/Event.hpp index 092f271c025e63be80b994b6b3921795c3e99671..0836e836e84ff7dfc4931a7cedd65497bf9a89cf 100644 --- a/include/vkcv/Event.hpp +++ b/include/vkcv/Event.hpp @@ -34,8 +34,9 @@ namespace vkcv { * adds a function handle to the event to be called * @param handle of the function */ - void add(typename event_function<T...>::type handle) { + typename event_function<T...>::type add(typename event_function<T...>::type handle) { this->m_handles.push_back(handle); + return handle; } /** diff --git a/include/vkcv/Window.hpp b/include/vkcv/Window.hpp index bb8081c166360e97595ff6994971390e53d6aa32..7428c7c73eb481f7352821faed36257211dfd5bf 100644 --- a/include/vkcv/Window.hpp +++ b/include/vkcv/Window.hpp @@ -12,7 +12,7 @@ struct GLFWwindow; namespace vkcv { - + class Window final { private: GLFWwindow *m_window; @@ -32,6 +32,16 @@ namespace vkcv { */ 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); + + 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. @@ -81,7 +91,9 @@ namespace vkcv { /** * basic events of the window */ + event< int, int, int> e_mouseButton; event< double, double > e_mouseMove; + event< double, double > e_mouseScroll; event< int, int > e_resize; event< int, int, int, int > e_key; @@ -129,5 +141,5 @@ namespace vkcv { */ virtual ~Window(); }; - + } \ No newline at end of file diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index 3309d860b300dd378460338d9fece48066c38993..f29ff2fc86c88aa8bae2560f199d3882c9919b65 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -1,3 +1,5 @@ # Add new modules here: +add_subdirectory(asset_loader) +add_subdirectory(camera) add_subdirectory(testing) diff --git a/modules/asset_loader/CMakeLists.txt b/modules/asset_loader/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8d4c0d6c104187de2d807cceceff529d83d236d6 --- /dev/null +++ b/modules/asset_loader/CMakeLists.txt @@ -0,0 +1,40 @@ +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}) diff --git a/modules/asset_loader/config/FX_GLTF.cmake b/modules/asset_loader/config/FX_GLTF.cmake new file mode 100644 index 0000000000000000000000000000000000000000..37cd162422d8277022067498f5d5ba3e26e2ae1b --- /dev/null +++ b/modules/asset_loader/config/FX_GLTF.cmake @@ -0,0 +1,12 @@ + +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 () diff --git a/modules/asset_loader/config/NLOHMANN_JSON.cmake b/modules/asset_loader/config/NLOHMANN_JSON.cmake new file mode 100644 index 0000000000000000000000000000000000000000..018f6a19809fd3e53e6e790a6fe6447348e43c09 --- /dev/null +++ b/modules/asset_loader/config/NLOHMANN_JSON.cmake @@ -0,0 +1,10 @@ + +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 () diff --git a/modules/asset_loader/config/STB.cmake b/modules/asset_loader/config/STB.cmake new file mode 100644 index 0000000000000000000000000000000000000000..da20d3ec07f98c865b4c6e38518f668b226cbfb9 --- /dev/null +++ b/modules/asset_loader/config/STB.cmake @@ -0,0 +1,6 @@ + +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 () diff --git a/modules/asset_loader/include/vkcv/asset/asset_loader.hpp b/modules/asset_loader/include/vkcv/asset/asset_loader.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6df014563a8991e464e02eb10f210c079913d3cb --- /dev/null +++ b/modules/asset_loader/include/vkcv/asset/asset_loader.hpp @@ -0,0 +1,119 @@ +#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); + + +} diff --git a/modules/asset_loader/lib/fx-gltf b/modules/asset_loader/lib/fx-gltf new file mode 160000 index 0000000000000000000000000000000000000000..f4f18f2017a049a23748c9c9aad42ba2de20bfd5 --- /dev/null +++ b/modules/asset_loader/lib/fx-gltf @@ -0,0 +1 @@ +Subproject commit f4f18f2017a049a23748c9c9aad42ba2de20bfd5 diff --git a/modules/asset_loader/lib/json b/modules/asset_loader/lib/json new file mode 160000 index 0000000000000000000000000000000000000000..0972f7ff0e651f09a306dba791cc42024b8642c1 --- /dev/null +++ b/modules/asset_loader/lib/json @@ -0,0 +1 @@ +Subproject commit 0972f7ff0e651f09a306dba791cc42024b8642c1 diff --git a/modules/asset_loader/lib/stb b/modules/asset_loader/lib/stb new file mode 160000 index 0000000000000000000000000000000000000000..c9064e317699d2e495f36ba4f9ac037e88ee371a --- /dev/null +++ b/modules/asset_loader/lib/stb @@ -0,0 +1 @@ +Subproject commit c9064e317699d2e495f36ba4f9ac037e88ee371a diff --git a/modules/asset_loader/src/vkcv/asset/asset_loader.cpp b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d0949cbc3454283b0084c57abf95a06a036c4e9b --- /dev/null +++ b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp @@ -0,0 +1,207 @@ +#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; +} + +} diff --git a/modules/camera/CMakeLists.txt b/modules/camera/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..28080bf2b1cd3bbc88d6c13d7ef26a43d7c3e19a --- /dev/null +++ b/modules/camera/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.16) +project(vkcv_camera) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(vkcv_camera_source ${PROJECT_SOURCE_DIR}/src) +set(vkcv_camera_include ${PROJECT_SOURCE_DIR}/include) + +set(vkcv_camera_sources + ${vkcv_camera_include}/vkcv/camera/Camera.hpp + ${vkcv_camera_source}/vkcv/camera/Camera.cpp + ${vkcv_camera_include}/vkcv/camera/TrackballCamera.hpp + ${vkcv_camera_source}/vkcv/camera/TrackballCamera.cpp + ${vkcv_camera_include}/vkcv/camera/CameraManager.hpp + ${vkcv_camera_source}/vkcv/camera/CameraManager.cpp +) + +# adding source files to the project +add_library(vkcv_camera STATIC ${vkcv_camera_sources}) + +# Setup some path variables to load libraries +set(vkcv_camera_lib lib) +set(vkcv_camera_lib_path ${PROJECT_SOURCE_DIR}/${vkcv_camera_lib}) + +include(config/GLM.cmake) + +target_link_libraries(vkcv_camera PUBLIC ${vkcv_camera_libraries} vkcv) + +target_include_directories(vkcv_camera SYSTEM BEFORE PRIVATE ${vkcv_camera_includes} ${vkcv_include}) + +# add the own include directory for public headers +target_include_directories(vkcv_camera BEFORE PUBLIC ${vkcv_camera_include} ${vkcv_camera_includes}) + diff --git a/modules/camera/config/GLM.cmake b/modules/camera/config/GLM.cmake new file mode 100644 index 0000000000000000000000000000000000000000..c4d14392ef0ea24243a45b19cd8583d90d3267be --- /dev/null +++ b/modules/camera/config/GLM.cmake @@ -0,0 +1,15 @@ + +find_package(glm QUIET) + +if (glm_FOUND) + list(APPEND vkcv_camera_includes ${GLM_INCLUDE_DIRS}) + list(APPEND vkcv_camera_libraries glm) +else() + if (EXISTS "${vkcv_camera_lib_path}/glm") + add_subdirectory(${vkcv_camera_lib}/glm) + + list(APPEND vkcv_camera_libraries glm) + else() + message(WARNING "GLM is required..! Update the submodules!") + endif () +endif () diff --git a/modules/camera/include/vkcv/camera/Camera.hpp b/modules/camera/include/vkcv/camera/Camera.hpp new file mode 100644 index 0000000000000000000000000000000000000000..7e177b9a2fbde0890e0c8ea6a1d9a19d6e277c7c --- /dev/null +++ b/modules/camera/include/vkcv/camera/Camera.hpp @@ -0,0 +1,101 @@ +#pragma once + +#include <glm/glm.hpp> +#include <glm/gtc/matrix_transform.hpp> +#include <glm/gtc/matrix_access.hpp> + +namespace vkcv { + + class Camera { + protected: + glm::mat4 m_view; + glm::mat4 m_projection; + + int m_width; + int m_height; + + float m_near; + float m_far; + float m_fov; + float m_ratio; + + glm::vec3 m_up; + glm::vec3 m_position; + float m_cameraSpeed; + float m_pitch; + float m_yaw; + + int m_fov_nsteps; + float m_fov_min; + float m_fov_max; + + bool m_forward; + bool m_backward; + bool m_left; + bool m_right; + + public: + Camera(); + + virtual ~Camera(); + + void setPerspective(float fov, float ratio, float near, float far); + + const glm::mat4 getView() const; + + void getView(glm::vec3 &x, glm::vec3 &y, glm::vec3 &z, glm::vec3 &pos); + + glm::mat4 updateView(double deltatime); + + void lookAt(glm::vec3 position, glm::vec3 center, glm::vec3 up); + + const glm::mat4& getProjection() const; + + void setProjection(const glm::mat4 projection); + + void getNearFar(float &near, float &far) const; + + void setUp(const glm::vec3 &Up); + + float getFov() const; + + void setFov(float fov); + + void changeFov(double fov); + + void updateRatio(float ratio); + + float getRatio() const; + + void setNearFar(float near, float far); + + glm::vec3 getFront() const; + + glm::vec3 getPosition() const; + + void setPosition( glm::vec3 position ); + + float getPitch() const; + + void setPitch(float pitch); + + float getYaw() const; + + void setYaw(float yaw); + + void panView( double xOffset, double yOffset ); + + void updatePosition(double deltatime); + + void moveForward(int action); + + void moveBackward(int action); + + void moveLeft(int action); + + void moveRight(int action); + + + }; + +} diff --git a/modules/camera/include/vkcv/camera/CameraManager.hpp b/modules/camera/include/vkcv/camera/CameraManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9511b752e972afb1e10f41a118433a4e8933fd65 --- /dev/null +++ b/modules/camera/include/vkcv/camera/CameraManager.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include "TrackballCamera.hpp" +#include "vkcv/Window.hpp" +#include <GLFW/glfw3.h> +#include <functional> + +namespace vkcv{ + + class CameraManager{ + private: + std::function<void(int, int, int, int)> m_keyHandle; + std::function<void(double, double)> m_mouseMoveHandle; + std::function<void(double, double)> m_mouseScrollHandle; + std::function<void(int, int, int)> m_mouseButtonHandle; + + Window &m_window; + Camera m_camera; + float m_width; + float m_height; +// std::shared_ptr<vkcv::TrackballCamera> m_trackball; + + bool m_roationActive = false; + double m_lastX ; + double m_lastY ; + + void bindCamera(); + void keyCallback(int key, int scancode, int action, int mods); + void scrollCallback( double offsetX, double offsetY); + void mouseMoveCallback( double offsetX, double offsetY); + void mouseButtonCallback(int button, int action, int mods); + + public: + CameraManager(Window &window, float width, float height, glm::vec3 up = glm::vec3(0.0f,-1.0f,0.0f), glm::vec3 position = glm::vec3(0.0f,0.0f,0.0f)); + + Camera &getCamera(); + }; +} diff --git a/modules/camera/include/vkcv/camera/TrackballCamera.hpp b/modules/camera/include/vkcv/camera/TrackballCamera.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c9e269e9f7ad708c68158d5b358efbf37c5bb7a9 --- /dev/null +++ b/modules/camera/include/vkcv/camera/TrackballCamera.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include "Camera.hpp" + +namespace vkcv { + + class TrackballCamera : public vkcv::Camera { + public: + + TrackballCamera( int width, int height, glm::mat4 projection); + + TrackballCamera(int width, int height); + + ~TrackballCamera(); + + float getSensitivity() const; + + void setSensitivity(float sensitivity); + + float getStepSize() const; + + void setStepSize(float stepSize); + + float getTheta() const; + + void setTheta(float theta); + + float getPhi() const; + + void setPhi(float phi); + + float getRadius() const; + + void setRadius(float radius); + + const glm::vec3& getCenter() const; + + void setCenter(const glm::vec3 ¢er); + + private: + float m_sensitivity; + float m_stepSize, m_theta, m_phi, m_radius; + glm::vec3 m_center; + + float m_oldX; + float m_oldY; + }; + +} \ No newline at end of file diff --git a/modules/camera/lib/glm b/modules/camera/lib/glm new file mode 160000 index 0000000000000000000000000000000000000000..66062497b104ca7c297321bd0e970869b1e6ece5 --- /dev/null +++ b/modules/camera/lib/glm @@ -0,0 +1 @@ +Subproject commit 66062497b104ca7c297321bd0e970869b1e6ece5 diff --git a/modules/camera/src/vkcv/camera/Camera.cpp b/modules/camera/src/vkcv/camera/Camera.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bc8a8498e67a6bd751f5a6ed1d4c4fba0279a68d --- /dev/null +++ b/modules/camera/src/vkcv/camera/Camera.cpp @@ -0,0 +1,177 @@ +#include "vkcv/camera/Camera.hpp" +#include <iostream> + +namespace vkcv { + + Camera::Camera(){ + m_up = glm::vec3(0.0f, -1.0f, 0.0f); + m_position = glm::vec3(0.0f, 0.0f, 0.0f); + m_cameraSpeed = 2.f; + // front + m_pitch = 0.0; + m_yaw = 180.0; + + m_fov_nsteps = 100; + m_fov_min = 10; + m_fov_max = 120; + + m_forward = false; + m_backward = false; + m_left = false; + m_right = false; + } + + Camera::~Camera() = default; + + void Camera::lookAt(glm::vec3 position, glm::vec3 center, glm::vec3 up){ + m_view = glm::lookAt(position, center, up); + } + + glm::mat4 Camera::updateView(double deltatime){ + updatePosition(deltatime); + return m_view = glm::lookAt(m_position, m_position + getFront() , m_up); + } + + void Camera::getView(glm::vec3 &x, glm::vec3 &y, glm::vec3 &z, glm::vec3 &pos){ + x = glm::vec3( glm::row(m_view, 0)); + y = glm::vec3( glm::row(m_view, 1)); + z = glm::vec3( glm::row(m_view, 2)); + pos = glm::vec3( glm::column(m_view, 3)); + glm::mat3 mat_inv = glm::inverse( glm::mat3(m_view)); + pos = -mat_inv * pos; + } + + void Camera::getNearFar( float &near, float &far) const { + near = m_near; + far = m_far; + } + + + const glm::mat4 Camera::getView() const { + return m_view; + } + + const glm::mat4& Camera::getProjection() const { + return m_projection; + } + + void Camera::setProjection(const glm::mat4 projection){ + m_projection = projection; + } + + float Camera::getFov() const { + return m_fov; + } + + void Camera::setFov( float fov){ + m_fov = fov; + setPerspective( m_fov, m_ratio, m_near, m_far); + } + + void Camera::changeFov(double offset){ + float fov = m_fov; + float fov_range = m_fov_max - m_fov_min; + float fov_stepsize = glm::radians(fov_range)/m_fov_nsteps; + fov -= (float) offset*fov_stepsize; + if (fov < glm::radians(m_fov_min)) { + fov = glm::radians(m_fov_min); + } + if (fov > glm::radians(m_fov_max)) { + fov = glm::radians(m_fov_max); + } + setFov(fov); + } + + void Camera::updateRatio( float ratio){ + m_ratio = ratio; + setPerspective( m_fov, m_ratio, m_near, m_far); + } + + float Camera::getRatio() const { + return 0.0f; + } + + void Camera::setNearFar( float near, float far){ + m_near = near; + m_far = far; + setPerspective(m_fov, m_ratio, m_near, m_far); + } + + void Camera::setPerspective(float fov, float ratio, float near, float far){ + m_fov = fov; + m_ratio = ratio; + m_near = near; + m_far = far; + m_projection = glm::perspective( m_fov, ratio, m_near, m_far); + } + + glm::vec3 Camera::getFront() const { + glm::vec3 direction; + direction.x = sin(glm::radians(m_yaw)) * cos(glm::radians(m_pitch)); + direction.y = sin(glm::radians(m_pitch)); + direction.z = cos(glm::radians(m_yaw)) * cos(glm::radians(m_pitch)); + return glm::normalize(direction); + } + + glm::vec3 Camera::getPosition() const { + return m_position; + } + + void Camera::setPosition( glm::vec3 position ){ + m_position = position; + } + + void Camera::setUp(const glm::vec3 &up) { + m_up = up; + } + + float Camera::getPitch() const { + return m_pitch; + } + + void Camera::setPitch(float pitch) { + if (pitch > 89.0f) { + pitch = 89.0f; + } + if (pitch < -89.0f) { + pitch = -89.0f; + } + m_pitch = pitch; + } + + float Camera::getYaw() const { + return m_yaw; + } + + void Camera::setYaw(float yaw) { + m_yaw = yaw; + } + + void Camera::panView(double xOffset, double yOffset) { + m_yaw += xOffset; + m_pitch += yOffset; + } + + void Camera::updatePosition(double deltatime ){ + m_position += (m_cameraSpeed * getFront() * static_cast<float> (m_forward) * static_cast<float>(deltatime)); + m_position -= (m_cameraSpeed * getFront() * static_cast<float> (m_backward) * static_cast<float>(deltatime)); + m_position -= (glm::normalize(glm::cross(getFront(), m_up)) * m_cameraSpeed * static_cast<float> (m_left) * static_cast<float>(deltatime)); + m_position += (glm::normalize(glm::cross(getFront(), m_up)) * m_cameraSpeed * static_cast<float> (m_right) * static_cast<float>(deltatime)); + } + + void Camera::moveForward(int action){ + m_forward = static_cast<bool>(action); + } + + void Camera::moveBackward(int action){ + m_backward = static_cast<bool>(action); + } + + void Camera::moveLeft(int action){ + m_left = static_cast<bool>(action); + } + + void Camera::moveRight(int action){ + m_right = static_cast<bool>(action); + } +} \ No newline at end of file diff --git a/modules/camera/src/vkcv/camera/CameraManager.cpp b/modules/camera/src/vkcv/camera/CameraManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..18f499a2b34b64c1442c5d9e267d6476b8d69199 --- /dev/null +++ b/modules/camera/src/vkcv/camera/CameraManager.cpp @@ -0,0 +1,82 @@ +#include <iostream> +#include "vkcv/camera/CameraManager.hpp" + +namespace vkcv{ + + CameraManager::CameraManager(Window &window, float width, float height, glm::vec3 up, glm::vec3 position): + m_window(window), m_width(width), m_height(height) + { + m_camera.setUp(up); + m_camera.setPosition(position); + m_camera.setPerspective( glm::radians(60.0f), m_width / m_height, 0.1f, 10.f); + m_lastX = width/2.0; + m_lastY = height/2.0; + bindCamera(); + } + + void CameraManager::bindCamera(){ + m_keyHandle = m_window.e_key.add( [&](int key, int scancode, int action, int mods) { this->keyCallback(key, scancode, action, mods); }); + m_mouseMoveHandle = m_window.e_mouseMove.add( [&]( double offsetX, double offsetY) {this->mouseMoveCallback( offsetX, offsetY);} ); + m_mouseScrollHandle = m_window.e_mouseScroll.add([&](double offsetX, double offsetY) {this->scrollCallback( offsetX, offsetY);} ); + m_mouseButtonHandle = m_window.e_mouseButton.add([&] (int button, int action, int mods) {this->mouseButtonCallback( button, action, mods);}); + } + + void CameraManager::mouseButtonCallback(int button, int action, int mods){ + if(button == GLFW_MOUSE_BUTTON_2 && m_roationActive == false && action == GLFW_PRESS){ + glfwSetInputMode(m_window.getWindow(), GLFW_CURSOR, GLFW_CURSOR_DISABLED); + m_roationActive = true; + }else if(button == GLFW_MOUSE_BUTTON_2 && m_roationActive == true && action == GLFW_RELEASE){ + glfwSetInputMode(m_window.getWindow(), GLFW_CURSOR, GLFW_CURSOR_NORMAL); + m_roationActive = false; + } + } + + void CameraManager::mouseMoveCallback(double x, double y){ + + float xoffset = x - m_lastX; + float yoffset = m_lastY - y; + m_lastX = x; + m_lastY = y; + + if(!m_roationActive){ + return; + } + + float sensitivity = 0.05f; + xoffset *= sensitivity; + yoffset *= sensitivity; + + m_camera.panView( xoffset , yoffset ); + } + + void CameraManager::scrollCallback(double offsetX, double offsetY) { + m_camera.changeFov(offsetY); + } + + void CameraManager::keyCallback(int key, int scancode, int action, int mods) { + + switch (key) { + case GLFW_KEY_W: + m_camera.moveForward(action); + break; + case GLFW_KEY_S: + m_camera.moveBackward(action); + break; + case GLFW_KEY_A: + m_camera.moveLeft(action); + break; + case GLFW_KEY_D: + m_camera.moveRight(action); + break; + case GLFW_KEY_ESCAPE: + glfwSetWindowShouldClose(m_window.getWindow(), 1); + break; + default: + break; + } + } + Camera &CameraManager::getCamera(){ + return m_camera; + } + +} \ No newline at end of file diff --git a/modules/camera/src/vkcv/camera/TrackballCamera.cpp b/modules/camera/src/vkcv/camera/TrackballCamera.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3bbb8611dd234499fb9ba08ba87009c8c76660f6 --- /dev/null +++ b/modules/camera/src/vkcv/camera/TrackballCamera.cpp @@ -0,0 +1,146 @@ +#include "vkcv/camera/TrackballCamera.hpp" + +namespace vkcv{ + + TrackballCamera::TrackballCamera( int width, int height, glm::mat4 projection) + { + setPosition( glm::vec3(0.0f, 0.0f, 5.0) ); + m_center = glm::vec3( 0.0f, 0.0f, 0.0f); + m_up = glm::vec3(0.0f, 1.0f, 0.0f); + + m_width = width; + m_height = height; + + m_sensitivity = 0.010f; + m_stepSize = 0.1f; + m_theta = glm::pi<float>() / 2.0f; + m_phi = 0.f; + m_radius = 1.5; + + m_view = glm::lookAt( m_center + getPosition(), m_center, m_up); + + m_oldX = width/2.f; + m_oldY = height/2.f; + + setProjection(projection); + } + + + TrackballCamera::TrackballCamera(int width, int height) + { + setPosition( glm::vec3(0.0f, 0.0f, 5.0)); + m_center = glm::vec3( 0.0f, 0.0f, 0.0f); + m_up = glm::vec3(0.0f, 1.0f, 0.0f); + + m_width = width; + m_height = height; + + m_sensitivity = 0.010f; + m_stepSize = 0.1f; + m_theta = glm::pi<float>() / 2.0f; + m_phi = 0.f; + m_radius = 1.5; + + m_view = glm::lookAt( m_center + getPosition(), m_center, m_up); + + m_oldX = width/2.f; + m_oldY = height/2.f; + + m_fov = glm::radians(60.f); + m_ratio = m_width / (float) m_height; + m_near = 0.001f; + m_far = 10.f; + glm::mat4 projection = glm::perspective(m_fov, m_ratio, m_near, m_far); + + setProjection(projection); + } + + TrackballCamera::~TrackballCamera() + { + } + + // TODO: Can be done as events... (mouseMove, mouseDown, mouseUp) + /*void TrackballCamera::update( GLFWwindow* window) { + + double x, y; + + glfwGetCursorPos( window, &x, &y); + if (glfwGetMouseButton( window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) + { + float changeX = ((float) x - m_oldX) * m_sensitivity; + float changeY = ((float) y - m_oldY) * m_sensitivity; + + m_theta -= changeY; + if (m_theta < 0.01f) m_theta = 0.01f; + else if (m_theta > glm::pi<float>() - 0.01f) m_theta = glm::pi<float>() - 0.01f; + + m_phi -= changeX; + if (m_phi < 0) m_phi += 2*glm::pi<float>(); + else if (m_phi > 2*glm::pi<float>()) m_phi -= 2*glm::pi<float>(); + } + + m_oldX = (float) x; + m_oldY = (float) y; + + if (glfwGetKey( window, GLFW_KEY_UP) == GLFW_PRESS) + m_radius -= m_stepSize; + if (glfwGetKey( window, GLFW_KEY_DOWN) == GLFW_PRESS) + m_radius += m_stepSize; + if (m_radius < 0.1f) m_radius = 0.1f; + + m_position.x = m_center.x + m_radius * sin(m_theta) * sin(m_phi); + m_position.y = m_center.y + m_radius * cos(m_theta); + m_position.z = m_center.z + m_radius * sin(m_theta) * cos(m_phi); + + m_view = glm::lookAt( m_position, m_center, m_up); + + }*/ + + float TrackballCamera::getSensitivity() const { + return m_sensitivity; + } + + void TrackballCamera::setSensitivity(float sensitivity) { + m_sensitivity = sensitivity; + } + + float TrackballCamera::getStepSize() const { + return m_stepSize; + } + + void TrackballCamera::setStepSize(float stepSize) { + m_stepSize = stepSize; + } + + float TrackballCamera::getTheta() const { + return m_theta; + } + + void TrackballCamera::setTheta(float theta) { + m_theta = theta; + } + + float TrackballCamera::getPhi() const { + return m_phi; + } + + void TrackballCamera::setPhi(float phi) { + m_phi = phi; + } + + float TrackballCamera::getRadius() const { + return m_radius; + } + + void TrackballCamera::setRadius(float radius) { + m_radius = radius; + } + + const glm::vec3& TrackballCamera::getCenter() const { + return m_center; + } + + void TrackballCamera::setCenter(const glm::vec3 ¢er) { + m_center = center; + } +} \ No newline at end of file diff --git a/modules/testing/CMakeLists.txt b/modules/testing/CMakeLists.txt index e869c7974fb0e91894d796e045c9104564173873..a22e547646fd4ef59860245d51365b98df59b578 100644 --- a/modules/testing/CMakeLists.txt +++ b/modules/testing/CMakeLists.txt @@ -18,3 +18,4 @@ add_library(vkcv_testing STATIC ${vkcv_testing_sources}) # add the own include directory for public headers target_include_directories(vkcv_testing BEFORE PUBLIC ${vkcv_testing_include}) + diff --git a/projects/CMakeLists.txt b/projects/CMakeLists.txt index f7687bca4ba7cb05835c8afda1396ad319a035c7..7ca73a0811df7f1568508b56312ce3348237a695 100644 --- a/projects/CMakeLists.txt +++ b/projects/CMakeLists.txt @@ -1,3 +1,4 @@ # Add new projects/examples here: add_subdirectory(first_triangle) +add_subdirectory(first_mesh) diff --git a/projects/first_mesh/.gitignore b/projects/first_mesh/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..85ce58a41ce7dcbe4f4a41bbf4779d82cede0106 --- /dev/null +++ b/projects/first_mesh/.gitignore @@ -0,0 +1 @@ +first_mesh \ No newline at end of file diff --git a/projects/first_mesh/CMakeLists.txt b/projects/first_mesh/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ae9c5604cb83c6f3a16773e896521117460839f7 --- /dev/null +++ b/projects/first_mesh/CMakeLists.txt @@ -0,0 +1,28 @@ +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}) diff --git a/projects/first_mesh/resources/cube/boards2_vcyc_jpg.jpg b/projects/first_mesh/resources/cube/boards2_vcyc_jpg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2636039e272289c0fba3fa2d88a060b857501248 --- /dev/null +++ b/projects/first_mesh/resources/cube/boards2_vcyc_jpg.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564 +size 1192476 diff --git a/projects/first_mesh/resources/cube/cube.bin b/projects/first_mesh/resources/cube/cube.bin new file mode 100644 index 0000000000000000000000000000000000000000..3303cd8635848bee18e10ab8754d5e4e7218db92 --- /dev/null +++ b/projects/first_mesh/resources/cube/cube.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9bb9b6b8bbe50a0aaa517057f245ee844f80afa7426dacb2aed4128f71629ce4 +size 840 diff --git a/projects/first_mesh/resources/cube/cube.blend b/projects/first_mesh/resources/cube/cube.blend new file mode 100644 index 0000000000000000000000000000000000000000..62ccb2c742094bcfb5ed194ab905bffae86bfd65 --- /dev/null +++ b/projects/first_mesh/resources/cube/cube.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6c1e245f259c610528c9485db6688928faac0ab2addee9e3c2dde7740e4dd09 +size 774920 diff --git a/projects/first_mesh/resources/cube/cube.blend1 b/projects/first_mesh/resources/cube/cube.blend1 new file mode 100644 index 0000000000000000000000000000000000000000..13f21dcca218d7bc7a07a8a9682b5e1d9e607736 --- /dev/null +++ b/projects/first_mesh/resources/cube/cube.blend1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4496f423569b8ca81f3b3a55fad00f925557e0193fb9dbe6cdce7e71fb48f7b +size 774920 diff --git a/projects/first_mesh/resources/cube/cube.glb b/projects/first_mesh/resources/cube/cube.glb new file mode 100644 index 0000000000000000000000000000000000000000..66a42c65e71dcf375e04cc378256024dd3c7834d --- /dev/null +++ b/projects/first_mesh/resources/cube/cube.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:198568b715f397d78f7c358c0f709a419e7fd677e54cdec7c19f71b5ed264897 +size 1194508 diff --git a/projects/first_mesh/resources/cube/cube.gltf b/projects/first_mesh/resources/cube/cube.gltf new file mode 100644 index 0000000000000000000000000000000000000000..428176144843dd06c78fe1d11a6392a0ea02b22d --- /dev/null +++ b/projects/first_mesh/resources/cube/cube.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f82f455647a84ca6242882ae26a79a499d3ce594f8de317ab89488c5b79721ac +size 2823 diff --git a/projects/first_mesh/resources/triangle/Triangle.bin b/projects/first_mesh/resources/triangle/Triangle.bin new file mode 100644 index 0000000000000000000000000000000000000000..57f26ad96592b64377e6aa93823d96a94e6c5022 --- /dev/null +++ b/projects/first_mesh/resources/triangle/Triangle.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:412ebd5f7242c266b4957e7e26be13aa331dbcb7bbb854ab334a2437ae8ed959 +size 104 diff --git a/projects/first_mesh/resources/triangle/Triangle.blend b/projects/first_mesh/resources/triangle/Triangle.blend new file mode 100644 index 0000000000000000000000000000000000000000..2421dc5e1bb029d73a9ec09cc4530c5196851fd7 --- /dev/null +++ b/projects/first_mesh/resources/triangle/Triangle.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:387e544df173219fbf292a64a6656d1d782bbf71a5a9e9fdef0a308f47b05477 +size 758144 diff --git a/projects/first_mesh/resources/triangle/Triangle.glb b/projects/first_mesh/resources/triangle/Triangle.glb new file mode 100644 index 0000000000000000000000000000000000000000..4148620cd6af0dadbc791aa1c52bb5431a40884b --- /dev/null +++ b/projects/first_mesh/resources/triangle/Triangle.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4be087a605212d139416b5352a018283b26b99260cbcddb7013a1beeb331227 +size 980 diff --git a/projects/first_mesh/resources/triangle/Triangle.gltf b/projects/first_mesh/resources/triangle/Triangle.gltf new file mode 100644 index 0000000000000000000000000000000000000000..a188e6ee16a5e8486cf307c7bda8cfd99bdbeea6 --- /dev/null +++ b/projects/first_mesh/resources/triangle/Triangle.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d5fc354e040f79cff329e919677b194c75e3a522c6406f75c1108ad9575f12ec +size 2202 diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b37429e1f7080b70aaab514125f86fbe03f329b5 --- /dev/null +++ b/projects/first_mesh/src/main.cpp @@ -0,0 +1,76 @@ +#include <iostream> +#include <stdio.h> +#include <vkcv/asset/asset_loader.hpp> + + +int main(int argc, const char** argv) { + vkcv::asset::Mesh mesh; + + const char *path = argc > 1 ? argv[1] : "resources/cube/cube.gltf"; + int result = vkcv::asset::loadMesh(path, mesh); + + if (result == 1) { + std::cout << "Mesh loading successful!" << std::endl; + } else { + std::cout << "Mesh loading failed: " << result << std::endl; + return 1; + } + + /* Demonstration of how to use the vkcv::asset::Mesh struct. */ + const char *primitive_modes[] = { + "points", "lines", "line loop", "line strip", "triangles", + "triangle strip", "triangle fan" + }; + const char *primitive_types[] = { + "unknown", "position", "normal", "texcoord0" + }; + printf("Mesh %s (%s) has %lu vertex group(s) and %lu material(s):\n", + mesh.name.c_str(), path, mesh.vertexGroups.size(), + mesh.materials.size()); + for (size_t i = 0; i < mesh.vertexGroups.size(); i++) { + printf("--- vertex group %lu ---\n", i); + const auto &vg = mesh.vertexGroups[i]; + printf("primitive mode: %d (%s)\n", vg.mode, + primitive_modes[vg.mode]); + printf("index buffer: %lu bytes for %lu indices ", + vg.indexBuffer.data.size(), vg.numIndices); + const auto itype = vg.indexBuffer.type; + printf("(%s @ %p)\n", + itype == vkcv::asset::UINT32 ? "UINT32" : + itype == vkcv::asset::UINT16 ? "UINT16" : + "UINT8", vg.indexBuffer.data.data()); + printf("\tindices: "); + const size_t n = vg.numIndices; + if (vg.indexBuffer.type == vkcv::asset::UINT32) { + uint32_t *idx = (uint32_t*)vg.indexBuffer.data.data(); + for (size_t j = 0; j < n; j++) printf("%u ", idx[j]); + } else + if (vg.indexBuffer.type == vkcv::asset::UINT16) { + uint16_t *idx = (uint16_t*)vg.indexBuffer.data.data(); + for (size_t j = 0; j < n; j++) printf("%u ", idx[j]); + } else + if (vg.indexBuffer.type == vkcv::asset::UINT8) { + uint8_t *idx = (uint8_t*)vg.indexBuffer.data.data(); + for (size_t j = 0; j < n; j++) printf("%u ", idx[j]); + } else { + fprintf(stderr, "ERROR Invalid IndexType: %d\n", + vg.indexBuffer.type); + return 0; + } + printf("\n"); + printf("vertex buffer: %lu bytes for %lu vertices with %lu " + "attributes (starting at %p)\n", + vg.vertexBuffer.data.size(), vg.numVertices, + vg.vertexBuffer.attributes.size(), + vg.vertexBuffer.data.data()); + printf("attributes:\toffset\tlength\tstride\tcomponents\n"); + for (const auto att : vg.vertexBuffer.attributes) { + printf("%11s\t%u\t%u\t%u\t%hhux%hu\n", + primitive_types[att.type], + att.offset, att.length, att.stride, + att.componentCount, att.componentType); + } + } + + return 0; +} diff --git a/projects/first_triangle/CMakeLists.txt b/projects/first_triangle/CMakeLists.txt index 400f7b1be1e5063777acb4c489d247ec8db34b1d..e7c8373e085df6497060b8d1d8164cf740dfb01f 100644 --- a/projects/first_triangle/CMakeLists.txt +++ b/projects/first_triangle/CMakeLists.txt @@ -22,7 +22,7 @@ if(MSVC) endif() # including headers of dependencies and the VkCV framework -target_include_directories(first_triangle SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_testing_include}) +target_include_directories(first_triangle SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_testing_include} ${vkcv_camera_include}) # linking with libraries from all dependencies and the VkCV framework -target_link_libraries(first_triangle vkcv ${vkcv_libraries} vkcv_testing) +target_link_libraries(first_triangle vkcv vkcv_testing vkcv_camera) diff --git a/projects/first_triangle/shaders/shader.vert b/projects/first_triangle/shaders/shader.vert index 1d278d5f41f803bb657a303dcc95ffcd2a92fd6e..e129186a4f9b9f8ceca180391210ccaf2953c08e 100644 --- a/projects/first_triangle/shaders/shader.vert +++ b/projects/first_triangle/shaders/shader.vert @@ -3,11 +3,15 @@ layout(location = 0) out vec3 fragColor; +layout( push_constant ) uniform constants{ + mat4 mvp; +}; + void main() { vec3 positions[3] = { - vec3(-0.5, 0.5, 0), - vec3( 0.5, 0.5, 0), - vec3(0, -0.5, 0) + vec3(-0.5, 0.5, -1), + vec3( 0.5, 0.5, -1), + vec3(0, -0.5, -1) }; vec3 colors[3] = { @@ -16,6 +20,6 @@ void main() { vec3(0, 0, 1) }; - gl_Position = vec4(positions[gl_VertexIndex], 1.0); + gl_Position = mvp * vec4(positions[gl_VertexIndex], 1.0); fragColor = colors[gl_VertexIndex]; } \ No newline at end of file diff --git a/projects/first_triangle/shaders/vert.spv b/projects/first_triangle/shaders/vert.spv index bd1e0e682c52e6e38a5f5aba4eeaf8e73a70d741..03af5758ffff1b5b6505fe98b02044849026832d 100644 Binary files a/projects/first_triangle/shaders/vert.spv and b/projects/first_triangle/shaders/vert.spv differ diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp index a515005e18ddaee99959a5c97b6978a0054ee574..e7664abd3f2bd74e0ad04629391d5f3ad108d4c2 100644 --- a/projects/first_triangle/src/main.cpp +++ b/projects/first_triangle/src/main.cpp @@ -1,9 +1,8 @@ #include <iostream> #include <vkcv/Core.hpp> -#include <vkcv/Window.hpp> -#include <vkcv/ShaderProgram.hpp> #include <GLFW/glfw3.h> -#include <vkcv/DescriptorConfig.hpp> +#include <vkcv/camera/CameraManager.hpp> +#include <chrono> int main(int argc, const char** argv) { const char* applicationName = "First Triangle"; @@ -17,6 +16,8 @@ int main(int argc, const char** argv) { false ); + vkcv::CameraManager cameraManager(window, windowWidth, windowHeight); + window.initEvents(); vkcv::Core core = vkcv::Core::create( @@ -130,11 +131,18 @@ int main(int argc, const char** argv) { * * PipelineHandle trianglePipeline = core.CreatePipeline(trianglePipeline); */ - + auto start = std::chrono::system_clock::now(); while (window.isWindowOpen()) { core.beginFrame(); - core.renderTriangle(trianglePass, trianglePipeline, windowWidth, windowHeight); + window.pollEvents(); + auto end = std::chrono::system_clock::now(); + auto deltatime = end - start; + start = end; + cameraManager.getCamera().updateView(std::chrono::duration<double>(deltatime).count()); + const glm::mat4 mvp = cameraManager.getCamera().getProjection() * cameraManager.getCamera().getView(); + + core.renderTriangle(trianglePass, trianglePipeline, windowWidth, windowHeight, sizeof(mvp), &mvp); core.endFrame(); } return 0; diff --git a/src/vkcv/BufferManager.cpp b/src/vkcv/BufferManager.cpp index cb2425a51ee177d062120b0eb61ae089f6f1692f..cb7c7356aef589ee8aef03b904d48021bdb75057 100644 --- a/src/vkcv/BufferManager.cpp +++ b/src/vkcv/BufferManager.cpp @@ -178,6 +178,26 @@ namespace vkcv { ); } + vk::Buffer BufferManager::getBuffer(uint64_t id) const { + if (id >= m_buffers.size()) { + return nullptr; + } + + auto& buffer = m_buffers[id]; + + return buffer.m_handle; + } + + vk::DeviceMemory BufferManager::getDeviceMemory(uint64_t id) const { + if (id >= m_buffers.size()) { + return nullptr; + } + + auto& buffer = m_buffers[id]; + + return buffer.m_memory; + } + void BufferManager::fillBuffer(uint64_t id, void *data, size_t size, size_t offset) { if (size == 0) { size = SIZE_MAX; diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp index fa53b4faa666134bde9230863f78c96e648c9037..0ee414fb974676b1d2df820e69218f5bb247341b 100644 --- a/src/vkcv/Core.cpp +++ b/src/vkcv/Core.cpp @@ -158,13 +158,13 @@ namespace vkcv if (acquireSwapchainImage() != Result::SUCCESS) { return; } - m_window.pollEvents(); m_Context.getDevice().waitIdle(); // FIMXE: this is a sin against graphics programming, but its getting late - Alex destroyTemporaryFramebuffers(); } void Core::renderTriangle(const PassHandle renderpassHandle, const PipelineHandle pipelineHandle, - const int width, const int height) { + const int width, const int height, const size_t pushConstantSize, const void *pushConstantData) { + if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) { return; } @@ -172,6 +172,7 @@ namespace vkcv const vk::RenderPass renderpass = m_PassManager->getVkPass(renderpassHandle); const vk::ImageView imageView = m_swapchainImageViews[m_currentSwapchainImageIndex]; const vk::Pipeline pipeline = m_PipelineManager->getVkPipeline(pipelineHandle); + const vk::PipelineLayout pipelineLayout = m_PipelineManager->getVkPipelineLayout(pipelineHandle); const vk::Rect2D renderArea(vk::Offset2D(0, 0), vk::Extent2D(width, height)); const vk::Framebuffer framebuffer = createFramebuffer(m_Context.getDevice(), renderpass, width, height, imageView); @@ -180,16 +181,17 @@ namespace vkcv SubmitInfo submitInfo; submitInfo.queueType = QueueType::Graphics; submitInfo.signalSemaphores = { m_SyncResources.renderFinished }; - submitCommands(submitInfo, [renderpass, renderArea, imageView, framebuffer, pipeline](const vk::CommandBuffer& cmdBuffer) { + submitCommands(submitInfo, [renderpass, renderArea, imageView, framebuffer, pipeline, pipelineLayout, pushConstantSize, pushConstantData](const vk::CommandBuffer& cmdBuffer) { const std::array<float, 4> clearColor = { 0.f, 0.f, 0.f, 1.f }; const vk::ClearValue clearValues(clearColor); - + const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, 1, &clearValues); const vk::SubpassContents subpassContents = {}; cmdBuffer.beginRenderPass(beginInfo, subpassContents, {}); - + cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {}); + cmdBuffer.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eAll, 0, pushConstantSize, pushConstantData); cmdBuffer.draw(3, 1, 0, 0, {}); cmdBuffer.endRenderPass(); }, nullptr); diff --git a/src/vkcv/PipelineManager.cpp b/src/vkcv/PipelineManager.cpp index 84b302867744ff750d8f1554b333de4e03a2dfbe..238a4ad73607d0d47f08b62a9541594cf57783da 100644 --- a/src/vkcv/PipelineManager.cpp +++ b/src/vkcv/PipelineManager.cpp @@ -160,14 +160,14 @@ namespace vkcv { 1.f,1.f,1.f,1.f } ); + const size_t matrixPushConstantSize = 4 * 4 * sizeof(float); + const vk::PushConstantRange pushConstantRange(vk::ShaderStageFlagBits::eAll, 0, matrixPushConstantSize); + // pipeline layout vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo( - {}, - 0, - {}, - 0, - {} - ); + {}, + {}, + (pushConstantRange)); vk::PipelineLayout vkPipelineLayout{}; if (m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &vkPipelineLayout) != vk::Result::eSuccess) { diff --git a/src/vkcv/Window.cpp b/src/vkcv/Window.cpp index 87f302c146f79f82a9b5334fd6a651fa00a92616..c21271b78f7501721d5c0496d0344dd68e2e7e52 100644 --- a/src/vkcv/Window.cpp +++ b/src/vkcv/Window.cpp @@ -46,17 +46,30 @@ namespace vkcv { glfwSetWindowUserPointer(m_window, this); // combine Callbacks with Events + glfwSetMouseButtonCallback(m_window, Window::onMouseButtonEvent); + glfwSetCursorPosCallback(m_window, Window::onMouseMoveEvent); glfwSetWindowSizeCallback(m_window, Window::onResize); glfwSetKeyCallback(m_window, Window::onKeyEvent); + + glfwSetScrollCallback(m_window, Window::onMouseScrollEvent); } void Window::pollEvents() { glfwPollEvents(); } + void Window::onMouseButtonEvent(GLFWwindow *callbackWindow, int button, int action, int mods) { + + auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow)); + + if (window != nullptr) { + window->e_mouseButton(button, action, mods); + } + } + void Window::onMouseMoveEvent(GLFWwindow *callbackWindow, double x, double y) { auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow)); @@ -66,6 +79,14 @@ namespace vkcv { } } + void Window::onMouseScrollEvent(GLFWwindow *callbackWindow, double xoffset, double yoffset) { + auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow)); + + if (window != nullptr) { + window->e_mouseScroll(xoffset, yoffset); + } + } + void Window::onResize(GLFWwindow *callbackWindow, int width, int height) { auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow)); @@ -103,4 +124,4 @@ namespace vkcv { GLFWwindow *Window::getWindow() const { return m_window; } -} +} \ No newline at end of file