├── .gitignore ├── README.md ├── scripts ├── flatgltf_unittests.lua ├── generate.make └── flatgltf.lua ├── include └── flatgltf │ ├── 2.0 │ ├── glTFcapi.h │ ├── glTFmath_types.hpp │ ├── glTFio.hpp │ └── glTFapi.hpp │ └── common │ ├── glTFapiexports.h │ └── glTFutils.hpp ├── src ├── common │ ├── glTFcommonio.cpp │ └── glTFuri.cpp └── 2.0 │ ├── glTFasset.cpp │ ├── glTFdocument.cpp │ ├── glTFbufferview.cpp │ ├── glTFnode.cpp │ ├── glTFserialize.cpp │ ├── glTFget_or_create.cpp │ ├── glTFcreate.cpp │ ├── glTFbuffer.cpp │ ├── glTFinternal.hpp │ ├── glTFstatic_check.cpp │ ├── glTFquery.cpp │ ├── glTFimage.cpp │ ├── glTFio_glb.cpp │ ├── glTFid.cpp │ ├── glTFget.cpp │ ├── glTFutility.cpp │ ├── glTFio.cpp │ └── glTFaccessor.cpp └── test └── flatgltf_2_0_loader.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flatGLTF 2 | Flatbuffers-based glTF 2.0 asset API. 3 | 4 | ## WARNING - HEAVY WIP 5 | warning, this project is still in heavy WIP: `push --force` et al might happen to overwrite HEAD 6 | use at own risk. 7 | 8 | ## DEPENDENCIES 9 | 10 | - [GLM](https://glm.g-truc.net) 11 | - [flatGLM](https://github.com/KageKirin/flatGLM) 12 | 13 | ## Project status 14 | 15 | - current version kind of worked when last updated 6 months ago. 16 | - project overhaul in planning 17 | - waiting for a few [improvements](https://github.com/google/flatbuffers/pull/4585) in flatbuffers to streamline API and data layout. 18 | -------------------------------------------------------------------------------- /scripts/flatgltf_unittests.lua: -------------------------------------------------------------------------------- 1 | -- premake for flatgltf_unittests 2 | 3 | --- 4 | 5 | if generate_thirdparty_projects and generate_thirdparty_projects == true then 6 | 7 | --- 8 | 9 | project "flatgltf_2_0_loader" 10 | kind "ConsoleApp" 11 | language "C++" 12 | flags {} 13 | 14 | includedirs { 15 | flatgltf_includedirs, 16 | flatbuffers_includedirs, 17 | glm_includedirs, 18 | khutils_includedirs, 19 | bandit_includedirs, 20 | boost_includedirs, 21 | glew_includedirs, 22 | } 23 | 24 | defines { 25 | } 26 | 27 | files { 28 | path.join(flatgltf_root, "test", "flatgltf_2_0_loader.cpp"), 29 | } 30 | 31 | links { 32 | "flatgltf_2_0", 33 | "flatbuffers", 34 | "boost.filesystem", 35 | "boost.system", 36 | } 37 | 38 | build_cppfwd("flatgltf_2_0_loader") 39 | 40 | end -- _OPTIONS["with-thirdparty"] ~= nil 41 | 42 | --- 43 | 44 | -------------------------------------------------------------------------------- /include/flatgltf/2.0/glTFcapi.h: -------------------------------------------------------------------------------- 1 | #ifndef FLATGLTF_2_0_CAPI_H_INC 2 | #define FLATGLTF_2_0_CAPI_H_INC 3 | 4 | #include "flatgltf/common/glTFapiexports.h" 5 | 6 | #include 7 | 8 | #if defined(__cplusplus) 9 | extern "C" { 10 | #endif // defined(__cplusplus) 11 | 12 | ///----------------------------------------------------------------------- 13 | /// id type alias 14 | ///----------------------------------------------------------------------- 15 | 16 | typedef int32_t glTFid_t; 17 | 18 | ///----------------------------------------------------------------------- 19 | /// document structure (PIMPL) 20 | ///----------------------------------------------------------------------- 21 | typedef struct Document glTF_2_0_Document; 22 | 23 | 24 | /// TODO: implement API 25 | 26 | #if defined(__cplusplus) 27 | } // extern "C" 28 | #endif // defined(__cplusplus) 29 | 30 | 31 | #endif // ! FLATGLTF_2_0_CAPI_H_INC 32 | -------------------------------------------------------------------------------- /scripts/generate.make: -------------------------------------------------------------------------------- 1 | ##################################################################### 2 | # include this file in your master makefile 3 | # rules to call from master.generate: 4 | 5 | ##--- 6 | 7 | FLATGLTF_2_0_FLATC_CPP_FLAGS=\ 8 | --cpp --scoped-enums --gen-name-strings --gen-object-api --no-includes --gen-mutable \ 9 | -I $(PROJECT_SCAFFOLDING)/thirdparty/flatgltf/schemas/2.0 \ 10 | -o $(PROJECT_SCAFFOLDING)/thirdparty/flatgltf/include/flatgltf/2.0/ 11 | 12 | ##--- 13 | 14 | gen_flatgltf_headers: \ 15 | gen_flatgltf_2_0_headers \ 16 | ; 17 | 18 | gen_flatgltf_2_0_headers: 19 | @$(PROJECT_SCAFFOLDING)/tools/bin/darwin/flatc \ 20 | $(FLATGLTF_2_0_FLATC_CPP_FLAGS) \ 21 | $(PROJECT_SCAFFOLDING)/thirdparty/flatgltf/schemas/2.0/glTF.fbs 22 | @clang-format -i $(PROJECT_SCAFFOLDING)/thirdparty/flatgltf/include/flatgltf/2.0/*_generated.h 23 | 24 | gen_flatgltf_2_0_schema_headers: 25 | @$(BIN2CPPSTRING) \ 26 | -f $(PROJECT_SCAFFOLDING)/thirdparty/flatgltf/schemas/2.0/glTF.fbs \ 27 | -o $(PROJECT_SCAFFOLDING)/thirdparty/flatgltf/src/2.0/glTF.cxx \ 28 | -n flatgltf_2_0_schema 29 | 30 | ##--- 31 | 32 | # just being verbose 33 | prn_flatgltf_verbose: 34 | @echo "generating flatgltf" 35 | 36 | # rule to invoke 37 | gen_flatgltf_flatbuffers: \ 38 | prn_flatgltf_verbose \ 39 | gen_flatgltf_headers \ 40 | gen_flatgltf_2_0_schema_headers \ 41 | ; 42 | -------------------------------------------------------------------------------- /src/common/glTFcommonio.cpp: -------------------------------------------------------------------------------- 1 | #include "flatgltf/common/glTFutils.hpp" 2 | 3 | #define KHUTILS_ASSERTION_INLINE 4 | 5 | #include "khutils/assertion.hpp" 6 | #include "khutils/file.hpp" 7 | #include "khutils/runtime_exceptions.hpp" 8 | 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace glTF_common 17 | { 18 | ///----------------------------------------------------------------------- 19 | /// Common IO 20 | ///----------------------------------------------------------------------- 21 | 22 | bool writeData(const char* location, const std::vector& data) 23 | { 24 | auto file = khutils::openLocalFilePtr(location, "wb"); 25 | if (file) 26 | { 27 | khutils::dumpBufferToFile(data, file); 28 | return true; 29 | } 30 | return false; 31 | } 32 | 33 | bool readData(const char* location, std::vector& data) 34 | { 35 | auto file = khutils::openLocalFilePtr(location, "rb"); 36 | if (file) 37 | { 38 | auto buf = khutils::openBufferFromFile(file); 39 | data.assign(buf.begin(), buf.end()); 40 | return true; 41 | } 42 | return false; 43 | } 44 | 45 | ///----------------------------------------------------------------------- 46 | ///----------------------------------------------------------------------- 47 | 48 | } // namespace glTF_common 49 | -------------------------------------------------------------------------------- /include/flatgltf/common/glTFapiexports.h: -------------------------------------------------------------------------------- 1 | #ifndef GLTF_API_EXPORTS_H_INC 2 | #define GLTF_API_EXPORTS_H_INC 3 | 4 | // Emscripten compilation 5 | #if defined(__EMSCRIPTEN__) 6 | #include 7 | #define FLATGLTF_API EMSCRIPTEN_KEEPALIVE 8 | #define FLATGLTF_DLL_FUNC 9 | #endif // defined(__EMSCRIPTEN__) 10 | 11 | 12 | // VS compilation 13 | #if defined(_MSC_VER) 14 | #ifndef FLATGLTF_API 15 | #define FLATGLTF_API __cdecl 16 | #endif // !FLATGLTF_API 17 | #ifndef FLATGLTF_DLL_FUNC 18 | #if defined(FLATGLTF_BUILD_DLL) 19 | #define FLATGLTF_DLL_FUNC __declspec(dllexport) 20 | #elif defined(FLATGLTF_USE_DLL) 21 | #define FLATGLTF_DLL_FUNC __declspec(dllimport) 22 | #else 23 | #define FLATGLTF_DLL_FUNC 24 | #endif // defined(FLATGLTF_BUILD_DLL) || defined(FLATGLTF_USE_DLL) 25 | #endif // !FLATGLTF_DLL_FUNC 26 | #endif // defined(_MSC_VER) 27 | 28 | 29 | // GCC compilation //TODO expand for clang 30 | #if defined(__GNUC__) 31 | #ifndef FLATGLTF_API 32 | #define FLATGLTF_API __cdecl 33 | #endif // !FLATGLTF_API 34 | #ifndef FLATGLTF_DLL_FUNC 35 | #if defined(FLATGLTF_BUILD_DLL) 36 | #define FLATGLTF_DLL_FUNC __attribute__((dllexport)) __attribute__((visibility("default"))) 37 | #elif defined(FLATGLTF_USE_DLL) 38 | #define FLATGLTF_DLL_FUNC __attribute__((dllimport)) 39 | #else 40 | #define FLATGLTF_DLL_FUNC 41 | #endif // defined(FLATGLTF_BUILD_DLL) || defined(FLATGLTF_USE_DLL) 42 | #endif // !FLATGLTF_DLL_FUNC 43 | #endif // defined(__GNUC__) 44 | 45 | 46 | // Define empty macros for other cases 47 | #ifndef FLATGLTF_API 48 | #define FLATGLTF_API 49 | #endif // !FLATGLTF_API 50 | 51 | #ifndef FLATGLTF_DLL_FUNC 52 | #define FLATGLTF_DLL_FUNC 53 | #endif // !FLATGLTF_DLL_FUNC 54 | 55 | #endif // GLTF_API_EXPORTS_H_INC 56 | -------------------------------------------------------------------------------- /src/2.0/glTFasset.cpp: -------------------------------------------------------------------------------- 1 | #include "flatgltf/2.0/glTF_generated.h" 2 | #include "flatgltf/2.0/glTFapi.hpp" 3 | 4 | #include "glTFinternal.hpp" 5 | 6 | #define KHUTILS_ASSERTION_INLINE 7 | 8 | #include "khutils/assertion.hpp" 9 | #include "khutils/runtime_exceptions.hpp" 10 | 11 | namespace glTF_2_0 12 | { 13 | //------------------------------------------------------------------------- 14 | 15 | AssetT* createAsset_default(const char* name) 16 | { 17 | auto instance = Asset_t{new AssetT}; 18 | instance->name = name; 19 | instance->version = "2.0"; 20 | instance->minVersion = "2.0"; 21 | instance->copyright = "NONE, unless specified otherwise"; 22 | instance->generator = "flatgltf, unless specified otherwise"; 23 | return instance.release(); 24 | } 25 | 26 | ///----------------------------------------------------------------------- 27 | /// simple get for unique elements 28 | ///----------------------------------------------------------------------- 29 | 30 | AssetT* const getAsset(const Document* const doc) 31 | { 32 | return (doc && doc->root) ? doc->root->asset.get() : nullptr; 33 | } 34 | 35 | //--- 36 | 37 | SceneT* const getMainScene(const Document* const doc) 38 | { 39 | return (doc && doc->root) ? getScene(doc, doc->root->scene) : nullptr; 40 | } 41 | 42 | //--- 43 | 44 | void setMainScene(Document* const doc, SceneT* const scene) 45 | { 46 | KHUTILS_ASSERT_PTR(doc); 47 | KHUTILS_ASSERT_PTR(scene); 48 | setMainScene(doc, getId(doc, scene)); 49 | } 50 | 51 | //--- 52 | 53 | void setMainScene(Document* const doc, glTFid_t id) 54 | { 55 | KHUTILS_ASSERT_PTR(doc); 56 | KHUTILS_ASSERT_GREATEREQ(id, 0); 57 | doc->root->scene = id; 58 | } 59 | 60 | //--- 61 | 62 | //------------------------------------------------------------------------- 63 | //------------------------------------------------------------------------- 64 | 65 | } // namespace glTF_2_0 66 | -------------------------------------------------------------------------------- /scripts/flatgltf.lua: -------------------------------------------------------------------------------- 1 | -- premake for flatgltf 2 | 3 | flatgltf_root = path.join(SCAFFOLDING_THIRDPARTY_DIR, "flatgltf") 4 | 5 | flatgltf_includedirs = { 6 | flatgltf_root, 7 | path.join(flatgltf_root, "include"), 8 | } 9 | 10 | flatgltf_libdirs = {} 11 | flatgltf_links = {} 12 | flatgltf_defines = {} 13 | 14 | --- 15 | 16 | if generate_thirdparty_projects and generate_thirdparty_projects == true then 17 | 18 | project "flatgltf_common" 19 | kind "StaticLib" 20 | language "C++" 21 | flags {} 22 | 23 | includedirs { 24 | flatgltf_includedirs, 25 | flatbuffers_includedirs, 26 | glm_includedirs, 27 | khutils_includedirs, 28 | bandit_includedirs, 29 | boost_includedirs, 30 | glew_includedirs, 31 | } 32 | 33 | defines { 34 | } 35 | 36 | files { 37 | path.join(flatgltf_root, "include", "flatgltf/common", "**.h"), 38 | path.join(flatgltf_root, "include", "flatgltf/common", "**.hpp"), 39 | path.join(flatgltf_root, "include", "flatgltf/common", "**.hxx"), 40 | path.join(flatgltf_root, "schemas/common", "**.fbs"), 41 | path.join(flatgltf_root, "src/common", "**.cpp"), 42 | path.join(flatgltf_root, "src/common", "**.cxx"), 43 | } 44 | 45 | links { 46 | "flatbuffers", 47 | } 48 | 49 | build_cppfwd("flatgltf_common") 50 | 51 | --- 52 | 53 | project "flatgltf_2_0" 54 | kind "StaticLib" 55 | language "C++" 56 | flags {} 57 | 58 | includedirs { 59 | flatgltf_includedirs, 60 | flatbuffers_includedirs, 61 | glm_includedirs, 62 | khutils_includedirs, 63 | bandit_includedirs, 64 | boost_includedirs, 65 | glew_includedirs, 66 | } 67 | 68 | defines { 69 | } 70 | 71 | files { 72 | path.join(flatgltf_root, "include", "flatgltf/2.0", "**.h"), 73 | path.join(flatgltf_root, "include", "flatgltf/2.0", "**.hpp"), 74 | path.join(flatgltf_root, "include", "flatgltf/2.0", "**.hxx"), 75 | path.join(flatgltf_root, "schemas/2.0", "**.fbs"), 76 | path.join(flatgltf_root, "src/2.0", "**.cpp"), 77 | path.join(flatgltf_root, "src/2.0", "**.cxx"), 78 | } 79 | 80 | links { 81 | "flatbuffers", 82 | "flatgltf_common", 83 | } 84 | 85 | build_cppfwd("flatgltf_2_0") 86 | 87 | 88 | dofile("flatgltf_unittests.lua") 89 | 90 | end -- _OPTIONS["with-thirdparty"] ~= nil 91 | 92 | --- 93 | 94 | -------------------------------------------------------------------------------- /src/2.0/glTFdocument.cpp: -------------------------------------------------------------------------------- 1 | #include "flatgltf/2.0/glTF_generated.h" 2 | #include "flatgltf/2.0/glTFapi.hpp" 3 | 4 | #include "glTFinternal.hpp" 5 | 6 | #define KHUTILS_ASSERTION_INLINE 7 | 8 | #include "khutils/assertion.hpp" 9 | #include "khutils/runtime_exceptions.hpp" 10 | 11 | #include 12 | 13 | namespace glTF_2_0 14 | { 15 | //------------------------------------------------------------------------- 16 | 17 | AssetT* createAsset_default(const char* name); 18 | 19 | Document* const createDocument(const char* name) 20 | { 21 | auto instance = std::unique_ptr{new Document}; 22 | instance->root = Root_t{new RootT}; 23 | instance->root->asset = Asset_t{createAsset_default(name)}; 24 | 25 | return instance.release(); 26 | } 27 | 28 | //--- 29 | 30 | void destroyDocument(Document* const doc) 31 | { 32 | if (doc) 33 | { 34 | delete doc; 35 | } 36 | } 37 | 38 | //--- 39 | // document creation 40 | 41 | std::unique_ptr createDocumentPtr(const std::string& name) 42 | { 43 | return std::unique_ptr{createDocument(name.c_str()), &destroyDocument}; 44 | } 45 | 46 | //--- 47 | 48 | bool verifyDocument(const Document* const doc) 49 | { 50 | KHUTILS_ASSERT_PTR(doc); 51 | KHUTILS_ASSERT_PTR(doc->root); 52 | auto& root = doc->root; 53 | 54 | auto viewOk = std::all_of(root->bufferViews.begin(), root->bufferViews.end(), [&](auto& view) { 55 | KHUTILS_ASSERT_PTR(view); 56 | return isValidBufferId(doc, view->buffer); 57 | }); 58 | 59 | auto accOk = std::all_of(root->accessors.begin(), root->accessors.end(), [&](auto& acc) { 60 | KHUTILS_ASSERT_PTR(acc); 61 | return isValidBufferViewId(doc, acc->bufferView); 62 | }); 63 | 64 | std::vector accessors; 65 | accessors.reserve(root->accessors.size()); 66 | std::transform(root->accessors.begin(), root->accessors.end(), std::back_inserter(accessors), [](auto& acc) { 67 | return acc.get(); 68 | }); 69 | 70 | std::sort(accessors.begin(), accessors.end(), [](auto lhv, auto rhv) { return lhv->bufferView < rhv->bufferView; }); 71 | accessors.resize(std::distance(accessors.begin(), std::unique(accessors.begin(), accessors.end(), [](auto lhv, auto rhv) { return lhv->bufferView == rhv->bufferView; }))); 72 | auto uniqueBufferViews = accessors.size() == root->accessors.size(); 73 | 74 | return viewOk && accOk && uniqueBufferViews; 75 | } 76 | 77 | //------------------------------------------------------------------------- 78 | //------------------------------------------------------------------------- 79 | 80 | } // namespace glTF_2_0 81 | -------------------------------------------------------------------------------- /include/flatgltf/common/glTFutils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FLATGLTF_COMMON_UTILS_H_INC 2 | #define FLATGLTF_COMMON_UTILS_H_INC 3 | 4 | #include "flatgltf/common/glTFapiexports.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace glTF_common 11 | { 12 | ///----------------------------------------------------------------------- 13 | /// Base64 tools 14 | ///----------------------------------------------------------------------- 15 | 16 | // returns true if uri is data uri ('data:') 17 | bool isDataUri(const char* uri); 18 | bool isDataUri(const std::string& uri); 19 | 20 | // get mime type from data uri 21 | // returns empty string if not set 22 | std::string getUriMimeType(const char* uri); 23 | std::string getUriMimeType(const std::string& uri); 24 | 25 | // get base64 part from data uri 26 | // returns empty string if not set 27 | std::string getUriBase64(const char* uri); 28 | std::string getUriBase64(const std::string& uri); 29 | 30 | // set base64 part of data uri 31 | // returns complete string 32 | std::string setUriBase64(const char* uri, const char* base64); 33 | std::string setUriBase64(const std::string& uri, const std::string& base64); 34 | 35 | // converts data uri to contained data 36 | std::vector convertUriToData(const char* uri); 37 | std::vector convertUriToData(const std::string& uri); 38 | 39 | // converts data to data uri following optional mimetype 40 | // returns complete data uri string 41 | std::string convertDataToUri(const uint8_t* const, size_t, const char* mimeType = nullptr); 42 | std::string convertDataToUri(const std::vector&, const char* mimeType = nullptr); 43 | 44 | // converts base64 string to contained data 45 | // NOTE: only base64 string, NOT data uri 46 | std::vector decodeBase64(const char* b64sz); 47 | std::vector decodeBase64(const std::string& b64sz); 48 | 49 | // converts data to base64 string 50 | // NOTE: only base64 string, NOT data uri 51 | std::string encodeBase64(const uint8_t* const, size_t); 52 | std::string encodeBase64(const std::vector&); 53 | 54 | ///----------------------------------------------------------------------- 55 | ///----------------------------------------------------------------------- 56 | 57 | bool writeData(const char* location, const std::vector&); 58 | bool readData(const char* location, std::vector&); 59 | 60 | ///----------------------------------------------------------------------- 61 | ///----------------------------------------------------------------------- 62 | 63 | } // namespace glTF_common 64 | 65 | #endif // ! FLATGLTF_COMMON_UTILS_H_INC 66 | -------------------------------------------------------------------------------- /test/flatgltf_2_0_loader.cpp: -------------------------------------------------------------------------------- 1 | //! test fot flatgltf / glTF 2.0 loading 2 | 3 | #include "flatgltf/2.0/glTF_generated.h" 4 | #include "flatgltf/2.0/glTFapi.hpp" 5 | #include "flatgltf/2.0/glTFio.hpp" 6 | 7 | #include "flatbuffers/flatbuffers.h" 8 | #include "flatbuffers/idl.h" 9 | #include "flatbuffers/util.h" 10 | 11 | #define KHUTILS_FILE_IMPL 12 | #define KHUTILS_LOGGING_IMPL 13 | #define KHUTILS_ASSERTION_INLINE 14 | #define KHUTILS_ASSERTION_IMPL 15 | #define KHUTILS_RUNTIME_EXCEPTIONS_IMPL 16 | #include "khutils/assertion.hpp" 17 | #include "khutils/file.hpp" 18 | #include "khutils/logging.hpp" 19 | #include "khutils/runtime_exceptions.hpp" 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | extern const std::string flatgltf_2_0_schema; 26 | using namespace glTF_2_0; 27 | 28 | int main(int argc, char** argv) 29 | { 30 | if (argc != 2) 31 | { 32 | std::cerr << argv[0] << " .gltf" << std::endl; 33 | return -1; 34 | } 35 | 36 | std::string gltfFileContents; 37 | bool gotFile = flatbuffers::LoadFile(argv[1], false, &gltfFileContents); 38 | 39 | if (!gotFile) 40 | { 41 | std::cerr << argv[1] << " does not seem to exist" << std::endl; 42 | return -1; 43 | } 44 | 45 | 46 | flatbuffers::Parser parser; 47 | bool schemaOk = parser.Parse(flatgltf_2_0_schema.c_str(), nullptr, "glTF.fbs"); 48 | if (!schemaOk) 49 | { 50 | std::cerr << "schema error: " << parser.error_ << std::endl; 51 | return 1; 52 | } 53 | 54 | bool gltfOk = parser.Parse(gltfFileContents.c_str(), nullptr, argv[1]); 55 | if (!gltfOk) 56 | { 57 | std::cerr << "json error: " << parser.error_ << std::endl; 58 | return 2; 59 | } 60 | 61 | std::cout << "gltf json file could be loaded" << std::endl; 62 | std::cout << gltfFileContents << std::endl; 63 | { 64 | auto doc = std::unique_ptr(createDocument("test"), &destroyDocument); 65 | std::cout << "----------------------" << std::endl; 66 | auto iss = std::istringstream(gltfFileContents); 67 | bool loadOk = loadDocument_json(doc.get(), 68 | "", // 69 | [&](const char*, std::vector& data) { 70 | data= khutils::openBufferFromStream(iss); 71 | return true; 72 | }); 73 | if (!loadOk) 74 | { 75 | std::cerr << "loading failed" << std::endl; 76 | } 77 | 78 | std::cout << "----------------------" << std::endl; 79 | auto oss = std::ostringstream(); 80 | bool saveOk = saveDocument_json(doc.get(), 81 | "", // 82 | [&](const char*, const std::vector& data) { 83 | khutils::dumpBufferToStream(data, oss); 84 | return true; 85 | }); 86 | std::cout << oss.str() << std::endl; 87 | if (!saveOk) 88 | { 89 | std::cerr << "saving failed" << std::endl; 90 | } 91 | } 92 | 93 | 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /src/2.0/glTFbufferview.cpp: -------------------------------------------------------------------------------- 1 | #include "flatgltf/2.0/glTF_generated.h" 2 | #include "flatgltf/2.0/glTFapi.hpp" 3 | 4 | #include "glTFinternal.hpp" 5 | 6 | #define KHUTILS_ASSERTION_INLINE 7 | 8 | #include "khutils/assertion.hpp" 9 | #include "khutils/runtime_exceptions.hpp" 10 | 11 | 12 | namespace glTF_2_0 13 | { 14 | //------------------------------------------------------------------------- 15 | 16 | 17 | BufferViewT* const createBufferView(Document* const doc, const char* name) 18 | { 19 | auto instance = BufferView_t{new BufferViewT}; 20 | if (name) 21 | { 22 | instance->name = name; 23 | } 24 | doc->root->bufferViews.push_back(std::move(instance)); 25 | return doc->root->bufferViews.back().get(); 26 | } 27 | 28 | //--- 29 | 30 | BufferViewT* const createBufferView(const uint8_t* const data, size_t length, Document* const doc, BufferT* const buf, const char* name) 31 | { 32 | KHUTILS_ASSERT_PTR(buf); 33 | 34 | auto view = createBufferView(doc, name); 35 | KHUTILS_ASSERT_PTR(view); 36 | 37 | view->buffer = getId(doc, buf); 38 | 39 | setBufferViewData(data, length, doc, view); 40 | 41 | return view; 42 | } 43 | 44 | //--- 45 | 46 | BufferViewT* const createBufferView(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 47 | { 48 | return createBufferView(data.data(), data.size(), doc, buf); 49 | } 50 | 51 | //--- 52 | 53 | size_t setBufferViewData(const std::vector& data, Document* const doc, BufferViewT* const view) 54 | { 55 | KHUTILS_ASSERT_CNTR_NOT_EMPTY(data); 56 | return setBufferViewData(data.data(), data.size(), doc, view); 57 | } 58 | 59 | //--- 60 | 61 | size_t setBufferViewData(const uint8_t* const data, size_t length, Document* const doc, BufferViewT* const view) 62 | { 63 | KHUTILS_ASSERT_PTR(data); 64 | KHUTILS_ASSERT_PTR(doc); 65 | KHUTILS_ASSERT_PTR(view); 66 | KHUTILS_ASSERT_LESSEREQ(view->byteLength, 0); 67 | 68 | auto buf = getBuffer(doc, view->buffer); 69 | KHUTILS_ASSERT_PTR(buf); 70 | 71 | view->byteOffset = buf->byteLength; 72 | appendBufferData(data, length, doc, buf); 73 | 74 | view->byteLength = length; 75 | view->byteStride = 0; 76 | 77 | return view->byteLength; 78 | } 79 | 80 | //--- 81 | 82 | std::vector getBufferViewData(const Document* const doc, const BufferViewT* const view) 83 | { 84 | KHUTILS_ASSERT_PTR(doc); 85 | KHUTILS_ASSERT_PTR(view); 86 | 87 | auto buf = getBuffer(doc, view->buffer); 88 | KHUTILS_ASSERT_PTR(buf); 89 | 90 | auto bufdata = getBufferData(doc, buf); 91 | 92 | std::vector viewdata; 93 | viewdata.reserve(view->byteLength); 94 | std::copy_n(bufdata.begin() + view->byteOffset, view->byteLength, std::back_inserter(viewdata)); 95 | return viewdata; 96 | } 97 | 98 | //------------------------------------------------------------------------- 99 | //------------------------------------------------------------------------- 100 | 101 | } // namespace glTF_2_0 102 | -------------------------------------------------------------------------------- /src/2.0/glTFnode.cpp: -------------------------------------------------------------------------------- 1 | #include "flatgltf/2.0/glTF_generated.h" 2 | #include "flatgltf/2.0/glTFapi.hpp" 3 | 4 | #include "glTFinternal.hpp" 5 | 6 | #define KHUTILS_ASSERTION_INLINE 7 | 8 | #include "khutils/assertion.hpp" 9 | #include "khutils/glm/glm_isnan.hpp" 10 | #include "khutils/isnan.hpp" 11 | #include "khutils/runtime_exceptions.hpp" 12 | 13 | namespace glTF_2_0 14 | { 15 | //------------------------------------------------------------------------- 16 | 17 | NodeT* const createNode(Document* const doc, CameraT* const cam) 18 | { 19 | KHUTILS_ASSERT_PTR(doc); 20 | KHUTILS_ASSERT_PTR(cam); 21 | 22 | auto node = createNode(doc); 23 | KHUTILS_ASSERT_PTR(node); 24 | 25 | node->camera = getId(doc, cam); 26 | 27 | return node; 28 | } 29 | 30 | //--- 31 | 32 | NodeT* const createNode(Document* const doc, MeshT* const mesh) 33 | { 34 | KHUTILS_ASSERT_PTR(doc); 35 | KHUTILS_ASSERT_PTR(mesh); 36 | 37 | auto node = createNode(doc); 38 | KHUTILS_ASSERT_PTR(node); 39 | 40 | node->mesh = getId(doc, mesh); 41 | 42 | return node; 43 | } 44 | 45 | //--- 46 | 47 | NodeT* const createNode(Document* const doc, SkinT* const skin) 48 | { 49 | KHUTILS_ASSERT_PTR(doc); 50 | KHUTILS_ASSERT_PTR(skin); 51 | 52 | auto node = createNode(doc); 53 | KHUTILS_ASSERT_PTR(node); 54 | 55 | node->skin = getId(doc, skin); 56 | 57 | return node; 58 | } 59 | 60 | //--- 61 | 62 | ///----------------------------------------------------------------------- 63 | /// set-transform for nodes 64 | ///----------------------------------------------------------------------- 65 | 66 | void setNodeMatrix(NodeT* const node, const mat4_t& m) 67 | { 68 | khutils::check_NaN(m); 69 | if (node) 70 | { 71 | auto msize = m.length() * mat4_t::col_type::length(); 72 | node->matrix.resize(msize); 73 | std::copy_n(&m[0][0], msize, node->matrix.begin()); 74 | 75 | // reset RST 76 | node->rotation.clear(); 77 | node->scale.clear(); 78 | node->translation.clear(); 79 | } 80 | } 81 | 82 | //--- 83 | 84 | void setNodeRotation(NodeT* const node, const quat_t& q) 85 | { 86 | khutils::check_NaN(q); 87 | if (node) 88 | { 89 | node->rotation.resize(q.length()); 90 | std::copy_n(&q.x, q.length(), node->rotation.begin()); 91 | 92 | // reset matrix 93 | node->matrix.clear(); 94 | } 95 | } 96 | 97 | //--- 98 | 99 | void setNodeScale(NodeT* const node, const vec3_t& v) 100 | { 101 | khutils::check_NaN(v); 102 | if (node) 103 | { 104 | node->scale.resize(v.length()); 105 | std::copy_n(&v.x, v.length(), node->scale.begin()); 106 | 107 | // reset matrix 108 | node->matrix.clear(); 109 | } 110 | } 111 | 112 | //--- 113 | 114 | void setNodeTranslation(NodeT* const node, const vec3_t& v) 115 | { 116 | khutils::check_NaN(v); 117 | if (node) 118 | { 119 | node->translation.resize(v.length()); 120 | std::copy_n(&v.x, v.length(), node->translation.begin()); 121 | 122 | // reset matrix 123 | node->matrix.clear(); 124 | } 125 | } 126 | 127 | //--- 128 | 129 | //------------------------------------------------------------------------- 130 | //------------------------------------------------------------------------- 131 | 132 | } // namespace glTF_2_0 133 | -------------------------------------------------------------------------------- /include/flatgltf/2.0/glTFmath_types.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FLATGLTF_2_0_MATH_TYPES_H_INC 2 | #define FLATGLTF_2_0_MATH_TYPES_H_INC 3 | 4 | #include "glTF_generated.h" 5 | 6 | #include 7 | #include 8 | //#include 9 | //#include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | namespace glTF_2_0 16 | { 17 | 18 | //--- 19 | // matrix types with fitting padding 20 | template 21 | using tmat2_t = glm::tmat2x4; 22 | 23 | template 24 | using tmat3_t = glm::tmat3x4; 25 | 26 | template 27 | using tmat4_t = glm::tmat4x4; 28 | 29 | template 30 | using tvec2_t = glm::tvec2; 31 | 32 | template 33 | using tvec3_t = glm::tvec3; 34 | 35 | template 36 | using tvec4_t = glm::tvec4; 37 | 38 | template 39 | using tquat_t = glm::tquat; 40 | 41 | using mat2_t = glm::mat2x4; 42 | using mat3_t = glm::mat3x4; 43 | using mat4_t = glm::mat4x4; 44 | using vec2_t = glm::vec2; 45 | using vec3_t = glm::vec3; 46 | using vec4_t = glm::vec4; 47 | using quat_t = glm::quat; 48 | using imat2_t = glm::tmat2x4; 49 | using imat3_t = glm::tmat3x4; 50 | using imat4_t = glm::tmat4x4; 51 | using ivec2_t = glm::ivec2; 52 | using ivec3_t = glm::ivec3; 53 | using ivec4_t = glm::ivec4; 54 | using i8vec2_t = glm::i8vec2; 55 | using i8vec3_t = glm::i8vec3; 56 | using i8vec4_t = glm::i8vec4; 57 | using i16vec2_t = glm::i16vec2; 58 | using i16vec3_t = glm::i16vec3; 59 | using i16vec4_t = glm::i16vec4; 60 | using uvec2_t = glm::uvec2; 61 | using uvec3_t = glm::uvec3; 62 | using uvec4_t = glm::uvec4; 63 | using u8vec2_t = glm::u8vec2; 64 | using u8vec3_t = glm::u8vec3; 65 | using u8vec4_t = glm::u8vec4; 66 | using u16vec2_t = glm::u16vec2; 67 | using u16vec3_t = glm::u16vec3; 68 | using u16vec4_t = glm::u16vec4; 69 | 70 | //-- 71 | template 72 | inline tmat2_t glm_cast(const glm::tmat2x2& m) 73 | { 74 | return tmat2_t{ 75 | {m[0].x, m[0].y, 0, 0}, // 76 | {m[1].x, m[1].y, 0, 0}, 77 | }; 78 | } 79 | 80 | template 81 | inline tmat3_t glm_cast(const glm::tmat3x3& m) 82 | { 83 | return tmat3_t{ 84 | {m[0].x, m[0].y, m[0].z, 0}, // 85 | {m[1].x, m[1].y, m[1].z, 0}, 86 | {m[2].x, m[2].y, m[2].z, 0}, 87 | }; 88 | } 89 | 90 | template 91 | inline tmat4_t glm_cast(const glm::tmat4x4& m) 92 | { 93 | return m; 94 | } 95 | 96 | template 97 | inline glm::tmat2x2 glm_cast(const tmat2_t& m) 98 | { 99 | return glm::tmat2x2{ 100 | {m[0].x, m[0].y}, // 101 | {m[1].x, m[1].y}, 102 | }; 103 | } 104 | 105 | template 106 | inline glm::tmat3x3 glm_cast(const tmat3_t& m) 107 | { 108 | return glm::tmat3x3{ 109 | {m[0].x, m[0].y, m[0].z}, // 110 | {m[1].x, m[1].y, m[1].z}, 111 | {m[2].x, m[2].y, m[2].z}, 112 | }; 113 | } 114 | 115 | // template 116 | // inline glm::tmat4x4 glm_cast(const tmat4_t& m) 117 | //{ 118 | // return m; 119 | //} 120 | 121 | } // namespace glTF_2_0 122 | 123 | #endif // ! FLATGLTF_2_0_MATH_TYPES_H_INC 124 | -------------------------------------------------------------------------------- /src/2.0/glTFserialize.cpp: -------------------------------------------------------------------------------- 1 | #include "flatgltf/2.0/glTF_generated.h" 2 | #include "flatgltf/2.0/glTFapi.hpp" 3 | #include "flatgltf/2.0/glTFio.hpp" 4 | #include "flatgltf/common/glTFutils.hpp" 5 | #include "glTFinternal.hpp" 6 | 7 | #include "flatbuffers/flatbuffers.h" 8 | #include "flatbuffers/idl.h" 9 | 10 | #define KHUTILS_ASSERTION_INLINE 11 | #include "khutils/assertion.hpp" 12 | #include "khutils/file.hpp" 13 | #include "khutils/logging.hpp" 14 | #include "khutils/runtime_exceptions.hpp" 15 | 16 | /// terribly sorry for this dependency. will remove asap 17 | #include 18 | 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | // flatbuffer schema string for parser 27 | extern const std::string flatgltf_2_0_schema; 28 | 29 | 30 | namespace glTF_2_0 31 | { 32 | using namespace glTF_common; 33 | 34 | ///------------------------------------------------------------------------ 35 | /// JSON serialization/deserialization 36 | ///------------------------------------------------------------------------ 37 | 38 | static const flatbuffers::IDLOptions& getIDLOptions() 39 | { 40 | static flatbuffers::IDLOptions opts; 41 | 42 | opts.strict_json = true; 43 | return opts; 44 | } 45 | 46 | //--- 47 | 48 | std::string to_json(const RootT* const instance) 49 | { 50 | // KHUTILS_ASSERT_PTR(instance); 51 | 52 | flatbuffers::Parser parser(getIDLOptions()); 53 | bool parseOk = parser.Parse(flatgltf_2_0_schema.c_str(), nullptr, "glTF_2.0.fbs"); 54 | KHUTILS_ASSERT_MSG(parseOk, parser.error_.c_str()); 55 | if (!parseOk) 56 | { 57 | return parser.error_; 58 | } 59 | 60 | std::string jsongen; 61 | auto root = CreateRoot(parser.builder_, instance); 62 | FinishRootBuffer(parser.builder_, root); 63 | GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen); 64 | return jsongen; 65 | } 66 | 67 | //--- 68 | 69 | std::string to_json(const Root_t& instance) 70 | { 71 | // KHUTILS_ASSERT_PTR(instance); 72 | return to_json(instance.get()); 73 | } 74 | 75 | //--- 76 | 77 | RootT* const from_json(const std::string& json) 78 | { 79 | puts(json.c_str()); 80 | 81 | flatbuffers::Parser parser; 82 | bool parseOk = parser.Parse(flatgltf_2_0_schema.c_str(), nullptr, "glTF_2.0.fbs"); 83 | KHUTILS_ASSERT_MSG(parseOk, parser.error_.c_str()); 84 | if (!parseOk) 85 | { 86 | return nullptr; 87 | } 88 | 89 | parseOk = parseOk && parser.Parse(json.c_str()); 90 | KHUTILS_ASSERT_MSG(parseOk, parser.error_.c_str()); 91 | if (!parseOk) 92 | { 93 | return nullptr; 94 | } 95 | 96 | auto root = flatbuffers::GetRoot(parser.builder_.GetBufferPointer()); 97 | return root->UnPack(); 98 | } 99 | 100 | //--- 101 | 102 | Root_t from_json_internal(const std::string& json) 103 | { 104 | return Root_t{from_json(json)}; 105 | } 106 | 107 | ///------------------------------------------------------------------------ 108 | /// flatbuffer marshalling/unmarshalling 109 | ///------------------------------------------------------------------------ 110 | 111 | std::vector to_flatbuffer(const RootT* const instance) 112 | { 113 | // KHUTILS_ASSERT_PTR(instance); 114 | 115 | flatbuffers::FlatBufferBuilder builder_; 116 | auto root = CreateRoot(builder_, instance); 117 | FinishRootBuffer(builder_, root); 118 | return std::vector{builder_.GetBufferPointer(), builder_.GetBufferPointer() + builder_.GetSize()}; 119 | } 120 | 121 | //--- 122 | 123 | std::vector to_flatbuffer(const Root_t& instance) 124 | { 125 | // KHUTILS_ASSERT_PTR(instance); 126 | return to_flatbuffer(instance.get()); 127 | } 128 | 129 | //--- 130 | 131 | RootT* const from_flatbuffer(const std::vector& buffer) 132 | { 133 | auto root = flatbuffers::GetRoot(buffer.data()); 134 | return root->UnPack(); 135 | } 136 | 137 | //--- 138 | 139 | Root_t from_flatbuffer_internal(const std::vector& buffer) 140 | { 141 | return Root_t{from_flatbuffer(buffer)}; 142 | } 143 | 144 | //------------------------------------------------------------------------- 145 | //------------------------------------------------------------------------- 146 | 147 | } // namespace glTF_2_0 148 | -------------------------------------------------------------------------------- /src/2.0/glTFget_or_create.cpp: -------------------------------------------------------------------------------- 1 | #include "flatgltf/2.0/glTF_generated.h" 2 | #include "flatgltf/2.0/glTFapi.hpp" 3 | 4 | #include "glTFinternal.hpp" 5 | 6 | #define KHUTILS_ASSERTION_INLINE 7 | 8 | #include "khutils/assertion.hpp" 9 | #include "khutils/runtime_exceptions.hpp" 10 | 11 | namespace glTF_2_0 12 | { 13 | //------------------------------------------------------------------------- 14 | 15 | AccessorT* const getOrCreateAccessor(Document* const doc, const char* name) 16 | { 17 | KHUTILS_ASSERT_PTR(doc); 18 | 19 | auto elem = getAccessor(doc, name); 20 | if (!elem) 21 | { 22 | elem = createAccessor(doc, name); 23 | } 24 | return elem; 25 | } 26 | 27 | //--- 28 | 29 | AnimationT* const getOrCreateAnimation(Document* const doc, const char* name) 30 | { 31 | KHUTILS_ASSERT_PTR(doc); 32 | 33 | auto elem = getAnimation(doc, name); 34 | if (!elem) 35 | { 36 | elem = createAnimation(doc, name); 37 | } 38 | return elem; 39 | } 40 | 41 | //--- 42 | 43 | BufferT* const getOrCreateBuffer(Document* const doc, const char* name) 44 | { 45 | KHUTILS_ASSERT_PTR(doc); 46 | 47 | auto elem = getBuffer(doc, name); 48 | if (!elem) 49 | { 50 | elem = createBuffer(doc, name); 51 | } 52 | return elem; 53 | } 54 | 55 | //--- 56 | 57 | BufferViewT* const getOrCreateBufferView(Document* const doc, const char* name) 58 | { 59 | KHUTILS_ASSERT_PTR(doc); 60 | 61 | auto elem = getBufferView(doc, name); 62 | if (!elem) 63 | { 64 | elem = createBufferView(doc, name); 65 | } 66 | return elem; 67 | } 68 | 69 | //--- 70 | 71 | CameraT* const getOrCreateCamera(Document* const doc, const char* name) 72 | { 73 | KHUTILS_ASSERT_PTR(doc); 74 | 75 | auto elem = getCamera(doc, name); 76 | if (!elem) 77 | { 78 | elem = createCamera(doc, name); 79 | } 80 | return elem; 81 | } 82 | 83 | //--- 84 | 85 | ImageT* const getOrCreateImage(Document* const doc, const char* name) 86 | { 87 | KHUTILS_ASSERT_PTR(doc); 88 | 89 | auto elem = getImage(doc, name); 90 | if (!elem) 91 | { 92 | elem = createImage(doc, name); 93 | } 94 | return elem; 95 | } 96 | 97 | //--- 98 | 99 | MaterialT* const getOrCreateMaterial(Document* const doc, const char* name) 100 | { 101 | KHUTILS_ASSERT_PTR(doc); 102 | 103 | auto elem = getMaterial(doc, name); 104 | if (!elem) 105 | { 106 | elem = createMaterial(doc, name); 107 | } 108 | return elem; 109 | } 110 | 111 | //--- 112 | 113 | MeshT* const getOrCreateMesh(Document* const doc, const char* name) 114 | { 115 | KHUTILS_ASSERT_PTR(doc); 116 | 117 | auto elem = getMesh(doc, name); 118 | if (!elem) 119 | { 120 | elem = createMesh(doc, name); 121 | } 122 | return elem; 123 | } 124 | 125 | //--- 126 | 127 | NodeT* const getOrCreateNode(Document* const doc, const char* name) 128 | { 129 | KHUTILS_ASSERT_PTR(doc); 130 | 131 | auto elem = getNode(doc, name); 132 | if (!elem) 133 | { 134 | elem = createNode(doc, name); 135 | } 136 | return elem; 137 | } 138 | 139 | //--- 140 | 141 | SceneT* const getOrCreateScene(Document* const doc, const char* name) 142 | { 143 | KHUTILS_ASSERT_PTR(doc); 144 | 145 | auto elem = getScene(doc, name); 146 | if (!elem) 147 | { 148 | elem = createScene(doc, name); 149 | } 150 | return elem; 151 | } 152 | 153 | //--- 154 | 155 | SamplerT* const getOrCreateSampler(Document* const doc, const char* name) 156 | { 157 | KHUTILS_ASSERT_PTR(doc); 158 | 159 | auto elem = getSampler(doc, name); 160 | if (!elem) 161 | { 162 | elem = createSampler(doc, name); 163 | } 164 | return elem; 165 | } 166 | 167 | //--- 168 | 169 | TextureT* const getOrCreateTexture(Document* const doc, const char* name) 170 | { 171 | KHUTILS_ASSERT_PTR(doc); 172 | 173 | auto elem = getTexture(doc, name); 174 | if (!elem) 175 | { 176 | elem = createTexture(doc, name); 177 | } 178 | return elem; 179 | } 180 | 181 | //--- 182 | 183 | SkinT* const getOrCreateSkin(Document* const doc, const char* name) 184 | { 185 | KHUTILS_ASSERT_PTR(doc); 186 | 187 | auto elem = getSkin(doc, name); 188 | if (!elem) 189 | { 190 | elem = createSkin(doc, name); 191 | } 192 | return elem; 193 | } 194 | 195 | //--- 196 | 197 | 198 | //------------------------------------------------------------------------- 199 | //------------------------------------------------------------------------- 200 | 201 | } // namespace glTF_2_0 202 | -------------------------------------------------------------------------------- /src/2.0/glTFcreate.cpp: -------------------------------------------------------------------------------- 1 | #include "flatgltf/2.0/glTF_generated.h" 2 | #include "flatgltf/2.0/glTFapi.hpp" 3 | 4 | #include "glTFinternal.hpp" 5 | 6 | #define KHUTILS_ASSERTION_INLINE 7 | 8 | #include "khutils/assertion.hpp" 9 | #include "khutils/runtime_exceptions.hpp" 10 | 11 | 12 | namespace glTF_2_0 13 | { 14 | //------------------------------------------------------------------------- 15 | 16 | AnimationT* const createAnimation(Document* const doc, const char* name) 17 | { 18 | auto instance = Animation_t{new AnimationT}; 19 | if (name) 20 | { 21 | instance->name = name; 22 | } 23 | doc->root->animations.push_back(std::move(instance)); 24 | return doc->root->animations.back().get(); 25 | } 26 | 27 | //--- 28 | 29 | CameraT* const createCamera(Document* const doc, const char* name) 30 | { 31 | auto instance = Camera_t{new CameraT}; 32 | if (name) 33 | { 34 | instance->name = name; 35 | } 36 | doc->root->cameras.push_back(std::move(instance)); 37 | return doc->root->cameras.back().get(); 38 | } 39 | 40 | //--- 41 | 42 | MaterialT* const createMaterial(Document* const doc, const char* name) 43 | { 44 | auto instance = Material_t{new MaterialT}; 45 | if (name) 46 | { 47 | instance->name = name; 48 | } 49 | doc->root->materials.push_back(std::move(instance)); 50 | return doc->root->materials.back().get(); 51 | } 52 | 53 | //--- 54 | 55 | MeshPrimitiveT* const createMeshPrimitive(MeshT* const mesh) 56 | { 57 | auto instance = MeshPrimitive_t{new MeshPrimitiveT}; 58 | mesh->primitives.push_back(std::move(instance)); 59 | return mesh->primitives.back().get(); 60 | } 61 | 62 | //--- 63 | 64 | MeshT* const createMesh(Document* const doc, const char* name) 65 | { 66 | auto instance = Mesh_t{new MeshT}; 67 | if (name) 68 | { 69 | instance->name = name; 70 | } 71 | doc->root->meshes.push_back(std::move(instance)); 72 | return doc->root->meshes.back().get(); 73 | } 74 | 75 | //--- 76 | 77 | NodeT* const createNode(Document* const doc, const char* name) 78 | { 79 | auto instance = Node_t{new NodeT}; 80 | if (name) 81 | { 82 | instance->name = name; 83 | } 84 | doc->root->nodes.push_back(std::move(instance)); 85 | return doc->root->nodes.back().get(); 86 | } 87 | 88 | //--- 89 | 90 | SceneT* const createScene(Document* const doc, const char* name) 91 | { 92 | auto instance = Scene_t{new SceneT}; 93 | if (name) 94 | { 95 | instance->name = name; 96 | } 97 | doc->root->scenes.push_back(std::move(instance)); 98 | return doc->root->scenes.back().get(); 99 | } 100 | 101 | //--- 102 | 103 | SamplerT* const createSampler(Document* const doc, const char* name) 104 | { 105 | auto instance = Sampler_t{new SamplerT}; 106 | if (name) 107 | { 108 | instance->name = name; 109 | } 110 | doc->root->samplers.push_back(std::move(instance)); 111 | return doc->root->samplers.back().get(); 112 | } 113 | 114 | //--- 115 | 116 | TextureT* const createTexture(Document* const doc, const char* name) 117 | { 118 | auto instance = Texture_t{new TextureT}; 119 | if (name) 120 | { 121 | instance->name = name; 122 | } 123 | doc->root->textures.push_back(std::move(instance)); 124 | return doc->root->textures.back().get(); 125 | } 126 | 127 | //--- 128 | 129 | SkinT* const createSkin(Document* const doc, const char* name) 130 | { 131 | auto instance = Skin_t{new SkinT}; 132 | if (name) 133 | { 134 | instance->name = name; 135 | } 136 | doc->root->skins.push_back(std::move(instance)); 137 | return doc->root->skins.back().get(); 138 | } 139 | 140 | //------------------------------------------------------------------------- 141 | //------------------------------------------------------------------------- 142 | // glTFmesh.cpp 143 | 144 | MeshPrimitiveT* const createMeshPrimitive(Document* const doc, MeshT* const mesh, AccessorT* const accessor, MaterialT* const material) 145 | { 146 | KHUTILS_ASSERT_PTR(doc); 147 | KHUTILS_ASSERT_PTR(mesh); 148 | KHUTILS_ASSERT_PTR(accessor); 149 | 150 | auto meshPrim = createMeshPrimitive(mesh); 151 | meshPrim->indices = getId(doc, accessor); 152 | meshPrim->mode = MeshPrimitiveMode::TRIANGLES; 153 | 154 | if (material) 155 | { 156 | meshPrim->material = getId(doc, material); 157 | } 158 | 159 | return meshPrim; 160 | } 161 | 162 | //--------------------------------------------------------------------- 163 | //--------------------------------------------------------------------- 164 | 165 | } // namespace glTF_2_0 166 | -------------------------------------------------------------------------------- /src/2.0/glTFbuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "flatgltf/2.0/glTF_generated.h" 2 | #include "flatgltf/2.0/glTFapi.hpp" 3 | #include "flatgltf/common/glTFutils.hpp" 4 | 5 | #include "glTFinternal.hpp" 6 | 7 | #define KHUTILS_ASSERTION_INLINE 8 | 9 | #include "khutils/assertion.hpp" 10 | #include "khutils/runtime_exceptions.hpp" 11 | 12 | namespace glTF_2_0 13 | { 14 | using namespace glTF_common; 15 | 16 | //------------------------------------------------------------------------- 17 | 18 | BufferT* const createBuffer(Document* const doc, const char* name) 19 | { 20 | KHUTILS_ASSERT_PTR(doc); 21 | 22 | auto instance = Buffer_t{new BufferT}; 23 | if (name) 24 | { 25 | instance->name = name; 26 | } 27 | doc->root->buffers.push_back(std::move(instance)); 28 | 29 | auto createOk = createBufferData(doc, doc->root->buffers.back().get()); 30 | KHUTILS_ASSERT(createOk); 31 | 32 | return doc->root->buffers.back().get(); 33 | } 34 | 35 | //--- 36 | 37 | BufferT* const createBuffer(Document* const doc, const char* uri, const char* name) 38 | { 39 | KHUTILS_ASSERT_PTR(doc); 40 | KHUTILS_ASSERT_PTR(uri); 41 | 42 | auto buffer = createBuffer(doc, name); 43 | buffer->uri = uri; 44 | 45 | if (isDataUri(buffer->uri)) 46 | { 47 | setBufferData(convertUriToData(uri), doc, buffer); 48 | buffer->uri = setUriBase64(uri, ""); // remove data, as its decoded in buffer now 49 | } 50 | 51 | return buffer; 52 | } 53 | 54 | //--- 55 | 56 | BufferT* const getOrCreateBuffer(Document* const doc, const char* uri, const char* name) 57 | { 58 | KHUTILS_ASSERT_PTR(doc); 59 | 60 | auto elem = getBuffer(doc, name); 61 | if (!elem) 62 | { 63 | elem = createBuffer(doc, uri, name); 64 | } 65 | return elem; 66 | } 67 | 68 | ///----------------------------------------------------------------------- 69 | /// bindata 70 | ///----------------------------------------------------------------------- 71 | 72 | bool createBufferData(Document* const doc, BufferT* const buf) 73 | { 74 | KHUTILS_ASSERT_PTR(doc); 75 | KHUTILS_ASSERT_PTR(buf); 76 | 77 | return createBufferData(doc, getId(doc, buf)); 78 | } 79 | 80 | //--- 81 | 82 | bool createBufferData(Document* const doc, glTFid_t id) 83 | { 84 | KHUTILS_ASSERT_PTR(doc); 85 | KHUTILS_ASSERT(isValidBufferId(doc, id)); 86 | 87 | if (std::find_if(doc->bindata.begin(), 88 | doc->bindata.end(), 89 | [&id](auto& bdp) { // 90 | return bdp.first == id; 91 | }) 92 | != doc->bindata.end()) 93 | { 94 | return false; // data already exist 95 | } 96 | 97 | doc->bindata[id].clear(); 98 | return true; 99 | } 100 | 101 | //--- 102 | 103 | std::vector getBufferData(const Document* const doc, const BufferT* const buf) 104 | { 105 | KHUTILS_ASSERT_PTR(doc); 106 | KHUTILS_ASSERT_PTR(buf); 107 | 108 | auto id = getId(doc, buf); 109 | KHUTILS_ASSERT(isValidBufferId(doc, id)); 110 | 111 | auto it = std::find_if(doc->bindata.begin(), doc->bindata.end(), [&](auto& bdp) { // 112 | return bdp.first == id; 113 | }); 114 | KHUTILS_ASSERT_NOT(it, doc->bindata.end()); 115 | 116 | return it->second; 117 | } 118 | 119 | //--- 120 | // sets buffer data, overwriting existing 121 | // returns new size 122 | // returns 0 if: buffer does not exist 123 | 124 | size_t setBufferData(const std::vector& data, Document* const doc, BufferT* const buf) 125 | { 126 | KHUTILS_ASSERT_CNTR_NOT_EMPTY(data); 127 | return setBufferData(data.data(), data.size(), doc, buf); 128 | } 129 | 130 | //--- 131 | 132 | size_t setBufferData(const uint8_t* data, size_t length, Document* const doc, BufferT* const buf) 133 | { 134 | KHUTILS_ASSERT_PTR(doc); 135 | KHUTILS_ASSERT_PTR(buf); 136 | 137 | auto id = getId(doc, buf); 138 | if (isValidBufferId(doc, id)) 139 | { 140 | auto& bufdata = doc->bindata[id]; 141 | bufdata.reserve(length); 142 | bufdata.assign(data, data + length); 143 | 144 | buf->byteLength = bufdata.size(); 145 | return bufdata.size(); 146 | } 147 | 148 | return 0; 149 | } 150 | 151 | //--- 152 | // appends buffer data to existing buffer data 153 | // returns new size 154 | // returns 0 if: buffer does not exist 155 | 156 | size_t appendBufferData(const std::vector& data, Document* const doc, BufferT* const buf) 157 | { 158 | KHUTILS_ASSERT_CNTR_NOT_EMPTY(data); 159 | return appendBufferData(data.data(), data.size(), doc, buf); 160 | } 161 | 162 | //--- 163 | 164 | size_t appendBufferData(const uint8_t* data, size_t length, Document* const doc, BufferT* const buf) 165 | { 166 | KHUTILS_ASSERT_PTR(doc); 167 | KHUTILS_ASSERT_PTR(buf); 168 | 169 | auto id = getId(doc, buf); 170 | if (isValidBufferId(doc, id)) 171 | { 172 | auto& bufdata = doc->bindata[id]; 173 | bufdata.reserve(bufdata.size() + length); 174 | std::copy_n(data, length, std::back_inserter(bufdata)); 175 | 176 | buf->byteLength = bufdata.size(); 177 | return bufdata.size(); 178 | } 179 | 180 | return 0; 181 | } 182 | 183 | //--- 184 | 185 | //------------------------------------------------------------------------- 186 | //------------------------------------------------------------------------- 187 | 188 | } // namespace glTF_2_0 189 | -------------------------------------------------------------------------------- /src/2.0/glTFinternal.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FLATGLTF_2_0_INTERNAL_TYPES_H_INC 2 | #define FLATGLTF_2_0_INTERNAL_TYPES_H_INC 3 | 4 | #include "glTF_generated.h" 5 | #include "glTFmath_types.hpp" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace glTF_2_0 13 | { 14 | //--- 15 | using AccessorSparseIndices_t = std::unique_ptr; 16 | using AccessorSparseValues_t = std::unique_ptr; 17 | using AccessorSparse_t = std::unique_ptr; 18 | using Accessor_t = std::unique_ptr; 19 | using Asset_t = std::unique_ptr; 20 | using AnimationChannelTarget_t = std::unique_ptr; 21 | using AnimationChannel_t = std::unique_ptr; 22 | using AnimationSampler_t = std::unique_ptr; 23 | using Animation_t = std::unique_ptr; 24 | using Buffer_t = std::unique_ptr; 25 | using BufferView_t = std::unique_ptr; 26 | using CameraOrthographic_t = std::unique_ptr; 27 | using CameraPerspective_t = std::unique_ptr; 28 | using Camera_t = std::unique_ptr; 29 | using Image_t = std::unique_ptr; 30 | using TextureInfo_t = std::unique_ptr; 31 | using MaterialNormalTextureInfo_t = std::unique_ptr; 32 | using MaterialOcclusionTextureInfo_t = std::unique_ptr; 33 | using MaterialPbrMetallicRoughness_t = std::unique_ptr; 34 | using Material_t = std::unique_ptr; 35 | using MeshPrimitive_t = std::unique_ptr; 36 | using Mesh_t = std::unique_ptr; 37 | using Node_t = std::unique_ptr; 38 | using Scene_t = std::unique_ptr; 39 | using Sampler_t = std::unique_ptr; 40 | using Texture_t = std::unique_ptr; 41 | using Skin_t = std::unique_ptr; 42 | using Root_t = std::unique_ptr; 43 | 44 | //--- 45 | //! container structure to keep glTF data along with buffers 46 | struct Document 47 | { 48 | /// json root element for glTF data 49 | Root_t root; 50 | 51 | /// binary data mapping bufferID -> data referenced by buffer elements 52 | std::map> bindata; 53 | 54 | /// binary data mapping imageID -> image data referenced by image elements 55 | /// NOTE: images can rerefence bufferViews, and thus buffer, 56 | /// in which case the image data will be stored in the referenced buffer 57 | std::map> imgdata; 58 | }; 59 | 60 | std::unique_ptr createDocumentPtr(const std::string& name); 61 | 62 | // creates new bindata 63 | bool createBufferData(Document* const, BufferT* const); 64 | bool createBufferData(Document* const, glTFid_t); 65 | 66 | // creates new imgdata 67 | bool createImageData(Document* const, ImageT* const); 68 | bool createImageData(Document* const, glTFid_t); 69 | 70 | ///----------------------------------------------------------------------- 71 | 72 | //! marshal glTF object to JSON 73 | std::string to_json(const Root_t&); 74 | //! unmarshal glTF object form JSON 75 | Root_t from_json_internal(const std::string&); 76 | 77 | //! flatbuffer-serialize glTF object to buffer 78 | std::vector to_flatbuffer(const Root_t&); 79 | //! flatbuffer-deserialize glTF object from buffer 80 | Root_t from_flatbuffer_internal(const std::vector&); 81 | 82 | ///----------------------------------------------------------------------- 83 | 84 | Root_t cloneRoot(const Root_t& orig); 85 | 86 | Root_t embedDocumentImages(const Document* const); 87 | Root_t embedDocumentBuffersAndImages(const Document* const); 88 | 89 | std::tuple> binarizeDocumentBuffers(const Document* const _doc, bool withImages); 90 | std::tuple> binarizeDocumentBuffers_embedImages(const Document* const _doc, bool withImages); 91 | 92 | 93 | /// prepares glTF data for GLB serialization 94 | //- changes URIs 95 | //- merges all mapped bindata into a single blob 96 | //- changes bufferViews accordingly 97 | bool binarizeDocument(Document* const); 98 | 99 | /// prepares glTF data for glTF-embedded format 100 | //- changes URIs to contain base64 encoded buffers 101 | //- changes bufferViews accordingly 102 | bool embedDocument(Document* const); 103 | 104 | /// prepares glTF data for glTF-multi format 105 | //- changes URIs to reference external buffers 106 | //- changes bufferViews accordingly 107 | bool dislodgeDocument(Document* const); 108 | 109 | /// prepares glTF data for glTF-multi format 110 | //- splits buffers into multiple buffers according to bufferViews 111 | //- changes bufferViews accordingly 112 | bool splitDocument(Document* const); 113 | 114 | ///----------------------------------------------------------------------- 115 | ///----------------------------------------------------------------------- 116 | 117 | } // namespace glTF_2_0 118 | 119 | #endif // ! FLATGLTF_2_0_INTERNAL_TYPES_H_INC 120 | -------------------------------------------------------------------------------- /include/flatgltf/2.0/glTFio.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FLATGLTF_2_0_IO_API_H_INC 2 | #define FLATGLTF_2_0_IO_API_H_INC 3 | 4 | #include "flatgltf/2.0/glTF_generated.h" 5 | #include "flatgltf/2.0/glTFapi.hpp" 6 | #include "flatgltf/2.0/glTFmath_types.hpp" 7 | #include "flatgltf/common/glTFapiexports.h" 8 | #include "flatgltf/common/glTFutils.hpp" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace glTF_2_0 18 | { 19 | ///----------------------------------------------------------------------- 20 | /// utility 21 | ///----------------------------------------------------------------------- 22 | 23 | /// clone a document into a new one 24 | FLATGLTF_DLL_FUNC bool FLATGLTF_API cloneDocument(const Document* const source, Document* const target); 25 | 26 | ///----------------------------------------------------------------------- 27 | /// file I/O 28 | ///----------------------------------------------------------------------- 29 | 30 | bool loadDocument(Document* const, const char* location); 31 | bool saveDocument(const Document* const, const char* location); 32 | 33 | // requried IO funcs 34 | // - read/write JSON (only) 35 | // - read/write JSON + external 36 | // - read/write GLF (only) 37 | // - read/write GLF + external 38 | // - read/write GLB (only) 39 | // - read/write GLB + external (images) 40 | 41 | // typedef bool (*data_writer_t)(const char* location, const std::vector&); 42 | using data_writer_t = std::function&)>; 43 | // typedef bool (*data_reader_t)(const char* location, std::vector&); 44 | using data_reader_t = std::function&)>; 45 | 46 | // load document as-is, without modification or external data 47 | bool loadDocument_json(Document* const, const char* location, data_reader_t = glTF_common::readData); 48 | bool loadDocument_glf(Document* const, const char* location, data_reader_t = glTF_common::readData); 49 | 50 | // load document: transform data uris to buffers, load external resources 51 | bool loadDocument_json_plus(Document* const, const char* location, data_reader_t = glTF_common::readData); 52 | bool loadDocument_glf_plus(Document* const, const char* location, data_reader_t = glTF_common::readData); 53 | 54 | 55 | // write document as-is, without modification or external data 56 | bool saveDocument_json(const Document* const, const char* location, data_writer_t = glTF_common::writeData); 57 | bool saveDocument_glf(const Document* const, const char* location, data_writer_t = glTF_common::writeData); 58 | 59 | // write document as-is, write buffers and images set to external URIs 60 | bool saveDocument_json_plus(const Document* const, const char* location, data_writer_t = glTF_common::writeData); 61 | bool saveDocument_glf_plus(const Document* const, const char* location, data_writer_t = glTF_common::writeData); 62 | 63 | // write document: ALL buffers/images will be embedded base64 URIs 64 | bool saveDocument_json_embed(const Document* const, const char* location, data_writer_t = glTF_common::writeData); 65 | bool saveDocument_glf_embed(const Document* const, const char* location, data_writer_t = glTF_common::writeData); 66 | 67 | // write document: ALL buffers/images will be external files named after URI or name or counter 68 | bool saveDocument_json_external(const Document* const, const char* location, data_writer_t = glTF_common::writeData); 69 | bool saveDocument_glf_external(const Document* const, const char* location, data_writer_t = glTF_common::writeData); 70 | 71 | 72 | // write document to glTF-binary: all buffers merged into 1, without external data 73 | bool saveDocument_glb(const Document* const, const char* location, data_writer_t = glTF_common::writeData); 74 | 75 | // write document to glTF-binary: all buffers merged into 1, ALL external images will be embedded base64 URIs 76 | bool saveDocument_glb_embed(const Document* const, const char* location, data_writer_t = glTF_common::writeData); 77 | 78 | // write document to glTF-binary: all buffers merged into 1, ALL external images also transformed to use bufferView 79 | bool saveDocument_glb_buffer(const Document* const, const char* location, data_writer_t = glTF_common::writeData); 80 | 81 | // write document to glTF-binary: all buffers merged into 1, with external data 82 | bool saveDocument_glb_plus(const Document* const, const char* location, data_writer_t = glTF_common::writeData); 83 | 84 | 85 | // load document from glTF-binary: all buffers merged into 1, without external data 86 | bool loadDocument_glb(Document* const, const char* location, data_reader_t = glTF_common::readData); 87 | 88 | // load document from glTF-binary: all buffers merged into 1, with external data 89 | bool loadDocument_glb_plus(Document* const, const char* location, data_reader_t = glTF_common::readData); 90 | 91 | ///----------------------------------------------------------------------- 92 | 93 | //! marshal glTF object to JSON 94 | FLATGLTF_DLL_FUNC std::string FLATGLTF_API to_json(const RootT* const); 95 | //! unmarshal glTF object form JSON 96 | FLATGLTF_DLL_FUNC RootT* const FLATGLTF_API from_json(const std::string&); 97 | 98 | //! flatbuffer-serialize glTF object to buffer 99 | FLATGLTF_DLL_FUNC std::vector FLATGLTF_API to_flatbuffer(const RootT* const); 100 | //! flatbuffer-deserialize glTF object from buffer 101 | FLATGLTF_DLL_FUNC RootT* const FLATGLTF_API from_flatbuffer(const std::vector&); 102 | 103 | ///----------------------------------------------------------------------- 104 | ///----------------------------------------------------------------------- 105 | 106 | } // namespace glTF_2_0 107 | 108 | #endif // ! FLATGLTF_2_0_IO_API_H_INC 109 | -------------------------------------------------------------------------------- /src/2.0/glTFstatic_check.cpp: -------------------------------------------------------------------------------- 1 | #include "flatgltf/2.0/glTF_generated.h" 2 | #include "flatgltf/2.0/glTFmath_types.hpp" 3 | 4 | #include 5 | #if defined(__APPLE__) 6 | #include 7 | #else 8 | #include 9 | #endif // defined(__APPLE__) 10 | 11 | //------------------------------------------------------------------------- 12 | 13 | static_assert(static_cast(glTF_2_0::ComponentType::BYTE) == GL_BYTE, "enum value mismatch"); 14 | static_assert(static_cast(glTF_2_0::ComponentType::UNSIGNED_BYTE) == GL_UNSIGNED_BYTE, "enum value mismatch"); 15 | static_assert(static_cast(glTF_2_0::ComponentType::SHORT) == GL_SHORT, "enum value mismatch"); 16 | static_assert(static_cast(glTF_2_0::ComponentType::UNSIGNED_SHORT) == GL_UNSIGNED_SHORT, "enum value mismatch"); 17 | static_assert(static_cast(glTF_2_0::ComponentType::UNSIGNED_INT) == GL_UNSIGNED_INT, "enum value mismatch"); 18 | static_assert(static_cast(glTF_2_0::ComponentType::FLOAT) == GL_FLOAT, "enum value mismatch"); 19 | 20 | 21 | static_assert(static_cast(glTF_2_0::BufferViewTarget::ARRAY_BUFFER) == GL_ARRAY_BUFFER, "enum value mismatch"); 22 | static_assert(static_cast(glTF_2_0::BufferViewTarget::ELEMENT_ARRAY_BUFFER) == GL_ELEMENT_ARRAY_BUFFER, 23 | "enum value mismatch"); 24 | static_assert(static_cast(glTF_2_0::BufferViewTarget::UNIFORM_BUFFER) == GL_UNIFORM_BUFFER, 25 | "enum value mismatch"); 26 | 27 | 28 | static_assert(static_cast(glTF_2_0::SamplerFilter::NEAREST) == GL_NEAREST, "enum value mismatch"); 29 | static_assert(static_cast(glTF_2_0::SamplerFilter::LINEAR) == GL_LINEAR, "enum value mismatch"); 30 | static_assert(static_cast(glTF_2_0::SamplerFilter::NEAREST_MIPMAP_NEAREST) == GL_NEAREST_MIPMAP_NEAREST, 31 | "enum value mismatch"); 32 | static_assert(static_cast(glTF_2_0::SamplerFilter::LINEAR_MIPMAP_NEAREST) == GL_LINEAR_MIPMAP_NEAREST, 33 | "enum value mismatch"); 34 | static_assert(static_cast(glTF_2_0::SamplerFilter::NEAREST_MIPMAP_LINEAR) == GL_NEAREST_MIPMAP_LINEAR, 35 | "enum value mismatch"); 36 | static_assert(static_cast(glTF_2_0::SamplerFilter::LINEAR_MIPMAP_LINEAR) == GL_LINEAR_MIPMAP_LINEAR, 37 | "enum value mismatch"); 38 | 39 | //------------------------------------------------------------------------- 40 | 41 | static_assert(sizeof(glTF_2_0::vec4_t) == glTF_2_0::vec4_t::length() * sizeof(glTF_2_0::vec4_t::value_type), 42 | "value type size mismatch"); 43 | static_assert(sizeof(glTF_2_0::vec3_t) == glTF_2_0::vec3_t::length() * sizeof(glTF_2_0::vec3_t::value_type), 44 | "value type size mismatch"); 45 | static_assert(sizeof(glTF_2_0::vec2_t) == glTF_2_0::vec2_t::length() * sizeof(glTF_2_0::vec2_t::value_type), 46 | "value type size mismatch"); 47 | static_assert(sizeof(glTF_2_0::i16vec4_t) == glTF_2_0::i16vec4_t::length() * sizeof(glTF_2_0::i16vec4_t::value_type), 48 | "value type size mismatch"); 49 | static_assert(sizeof(glTF_2_0::i16vec3_t) == glTF_2_0::i16vec3_t::length() * sizeof(glTF_2_0::i16vec3_t::value_type), 50 | "value type size mismatch"); 51 | static_assert(sizeof(glTF_2_0::i16vec2_t) == glTF_2_0::i16vec2_t::length() * sizeof(glTF_2_0::i16vec2_t::value_type), 52 | "value type size mismatch"); 53 | static_assert(sizeof(glTF_2_0::i8vec4_t) == glTF_2_0::i8vec4_t::length() * sizeof(glTF_2_0::i8vec4_t::value_type), 54 | "value type size mismatch"); 55 | static_assert(sizeof(glTF_2_0::i8vec3_t) == glTF_2_0::i8vec3_t::length() * sizeof(glTF_2_0::i8vec3_t::value_type), 56 | "value type size mismatch"); 57 | static_assert(sizeof(glTF_2_0::i8vec2_t) == glTF_2_0::i8vec2_t::length() * sizeof(glTF_2_0::i8vec2_t::value_type), 58 | "value type size mismatch"); 59 | static_assert(sizeof(glTF_2_0::u16vec4_t) == glTF_2_0::u16vec4_t::length() * sizeof(glTF_2_0::u16vec4_t::value_type), 60 | "value type size mismatch"); 61 | static_assert(sizeof(glTF_2_0::u16vec3_t) == glTF_2_0::u16vec3_t::length() * sizeof(glTF_2_0::u16vec3_t::value_type), 62 | "value type size mismatch"); 63 | static_assert(sizeof(glTF_2_0::u16vec2_t) == glTF_2_0::u16vec2_t::length() * sizeof(glTF_2_0::u16vec2_t::value_type), 64 | "value type size mismatch"); 65 | static_assert(sizeof(glTF_2_0::u8vec4_t) == glTF_2_0::u8vec4_t::length() * sizeof(glTF_2_0::u8vec4_t::value_type), 66 | "value type size mismatch"); 67 | static_assert(sizeof(glTF_2_0::u8vec3_t) == glTF_2_0::u8vec3_t::length() * sizeof(glTF_2_0::u8vec3_t::value_type), 68 | "value type size mismatch"); 69 | static_assert(sizeof(glTF_2_0::u8vec2_t) == glTF_2_0::u8vec2_t::length() * sizeof(glTF_2_0::u8vec2_t::value_type), 70 | "value type size mismatch"); 71 | 72 | static_assert(sizeof(glTF_2_0::mat4_t) == glTF_2_0::mat4_t::length() * sizeof(glTF_2_0::mat4_t::col_type), 73 | "value type size mismatch"); 74 | static_assert(sizeof(glTF_2_0::mat3_t) == glTF_2_0::mat3_t::length() * sizeof(glTF_2_0::mat3_t::col_type), 75 | "value type size mismatch"); 76 | static_assert(sizeof(glTF_2_0::mat2_t) == glTF_2_0::mat2_t::length() * sizeof(glTF_2_0::mat2_t::col_type), 77 | "value type size mismatch"); 78 | 79 | static_assert(sizeof(glTF_2_0::mat4_t) 80 | == glTF_2_0::mat4_t::length() * glTF_2_0::mat4_t::col_type::length() * sizeof(glTF_2_0::mat4_t::value_type), 81 | "value type size mismatch"); 82 | static_assert(sizeof(glTF_2_0::mat3_t) 83 | == glTF_2_0::mat3_t::length() * glTF_2_0::mat3_t::col_type::length() * sizeof(glTF_2_0::mat3_t::value_type), 84 | "value type size mismatch"); 85 | static_assert(sizeof(glTF_2_0::mat2_t) 86 | == glTF_2_0::mat2_t::length() * glTF_2_0::mat2_t::col_type::length() * sizeof(glTF_2_0::mat2_t::value_type), 87 | "value type size mismatch"); 88 | 89 | 90 | //------------------------------------------------------------------------- 91 | //------------------------------------------------------------------------- 92 | -------------------------------------------------------------------------------- /src/common/glTFuri.cpp: -------------------------------------------------------------------------------- 1 | #include "flatgltf/common/glTFutils.hpp" 2 | 3 | #define KHUTILS_ASSERTION_INLINE 4 | 5 | #include "khutils/assertion.hpp" 6 | #include "khutils/runtime_exceptions.hpp" 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace glTF_common 15 | { 16 | ///----------------------------------------------------------------------- 17 | /// Base64 tools 18 | ///----------------------------------------------------------------------- 19 | 20 | static constexpr char reDataUri[] = R"(/^data:(.*?)(;base64)?,(.*)$/;)"; 21 | 22 | bool isDataUri(const char* uri) 23 | { 24 | return std::regex_match(uri, std::regex(reDataUri)); 25 | } 26 | 27 | bool isDataUri(const std::string& uri) 28 | { 29 | return isDataUri(uri.c_str()); 30 | } 31 | 32 | //--- 33 | 34 | inline std::string getUriPart(const char* uri, size_t part) 35 | { 36 | std::cmatch matches; 37 | if (std::regex_match(uri, matches, std::regex(reDataUri))) 38 | { 39 | if (part < matches.size()) 40 | { 41 | return matches.str(part); 42 | } 43 | } 44 | return std::string(); 45 | } 46 | 47 | // get mime type from data uri 48 | // returns empty string if not set 49 | std::string getMimeType(const char* uri) 50 | { 51 | return getUriPart(uri, 1); 52 | } 53 | 54 | std::string getMimeType(const std::string& uri) 55 | { 56 | return getMimeType(uri.c_str()); 57 | } 58 | 59 | // get base64 part from data uri 60 | // returns empty string if not set 61 | std::string getUriBase64(const char* uri) 62 | { 63 | return getUriPart(uri, 2); 64 | } 65 | 66 | std::string getUriBase64(const std::string& uri) 67 | { 68 | return getUriBase64(uri.c_str()); 69 | } 70 | 71 | //--- 72 | 73 | // set base64 part of data uri 74 | // returns complete string 75 | std::string setUriBase64(const char* uri, const char* base64) 76 | { 77 | return std::string(uri) + "," + base64; 78 | } 79 | 80 | std::string setUriBase64(const std::string& uri, const std::string& base64) 81 | { 82 | return setUriBase64(uri.c_str(), base64.c_str()); 83 | } 84 | 85 | //--- 86 | 87 | // converts data uri to contained data 88 | std::vector convertUriToData(const char* uri) 89 | { 90 | return decodeBase64(getUriBase64(uri)); 91 | } 92 | 93 | std::vector convertUriToData(const std::string& uri) 94 | { 95 | return convertUriToData(uri.c_str()); 96 | } 97 | 98 | //--- 99 | 100 | // converts data to data uri following optional mimetype 101 | // returns complete data uri string 102 | std::string convertDataToUri(const uint8_t* const data, size_t length, const char* mimeType) 103 | { 104 | // data:application/octet-stream;base64, 105 | return std::string("data:") + mimeType + ";base64," + encodeBase64(data, length); 106 | } 107 | 108 | std::string convertDataToUri(const std::vector& data, const char* mimeType) 109 | { 110 | return convertDataToUri(data.data(), data.size(), mimeType); 111 | } 112 | 113 | 114 | //--- 115 | static const std::string base64Characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 116 | 117 | // converts base64 string to contained data 118 | // NOTE: only base64 string, NOT data uri 119 | 120 | std::vector decodeBase64(const char* encoded) 121 | { 122 | return decodeBase64(std::string(encoded)); 123 | } 124 | 125 | std::vector decodeBase64(const std::string& encoded) 126 | { 127 | std::vector decoded; 128 | size_t encodedIndex = 0; 129 | while (encodedIndex < encoded.size()) 130 | { 131 | uint8_t out[3] = {0, 0, 0}; 132 | size_t gathered = 0; 133 | for (gathered = 0; gathered < 4; gathered++) 134 | { 135 | // Check, if out of bounds. If yes, invalid encoded. 136 | if (encodedIndex >= encoded.size()) 137 | { 138 | return std::vector(); 139 | } 140 | 141 | size_t index = base64Characters.find(encoded[encodedIndex]); 142 | 143 | // Check, if BASE64 character. If not, invalid encoded. 144 | if (index == base64Characters.npos && encoded[encodedIndex] != '=') 145 | { 146 | return std::vector(); 147 | } 148 | 149 | if (encoded[encodedIndex] == '=') 150 | { 151 | break; 152 | } 153 | 154 | uint8_t in = (uint8_t)index; 155 | 156 | if (gathered == 0) 157 | { 158 | out[0] = in << 2; 159 | } 160 | else if (gathered == 1) 161 | { 162 | out[0] |= in >> 4; 163 | 164 | out[1] = (in << 4) & 0xF0; 165 | } 166 | else if (gathered == 2) 167 | { 168 | out[1] |= in >> 2; 169 | 170 | out[2] = (in << 6) & 0xC0; 171 | } 172 | else if (gathered == 3) 173 | { 174 | out[2] |= in; 175 | } 176 | 177 | encodedIndex++; 178 | } 179 | 180 | // 181 | 182 | for (size_t i = 0; i < 3; i++) 183 | { 184 | if (gathered != 4 && i == 2) 185 | { 186 | return decoded; 187 | } 188 | 189 | decoded.push_back(out[i]); 190 | } 191 | } 192 | 193 | return decoded; 194 | } 195 | 196 | //--- 197 | // converts data to base64 string 198 | // NOTE: only base64 string, NOT data uri 199 | 200 | std::string encodeBase64(const uint8_t* const data, size_t size) 201 | { 202 | return encodeBase64(std::vector(data, data + size)); 203 | } 204 | 205 | std::string encodeBase64(const std::vector& data) 206 | { 207 | std::string encoded = ""; 208 | size_t dataIndex = 0; 209 | while (dataIndex < data.size()) 210 | { 211 | uint8_t in[3] = {0, 0, 0}; 212 | size_t gathered = 0; 213 | for (gathered = 0; gathered < 3; gathered++) 214 | { 215 | if (dataIndex == data.size()) 216 | { 217 | break; 218 | } 219 | 220 | in[gathered] = data[dataIndex]; 221 | 222 | // 223 | 224 | dataIndex++; 225 | } 226 | 227 | // 228 | 229 | uint8_t out[4] = {0, 0, 0, 0}; 230 | 231 | out[0] = (in[0] >> 2) & 0x3F; 232 | out[1] = ((in[0] << 4) & 0x30) | ((in[1] >> 4) & 0x0F); 233 | 234 | if (gathered >= 2) 235 | { 236 | out[2] = (in[1] << 2) & 0x3C; 237 | 238 | if (gathered == 3) 239 | { 240 | out[2] |= (in[2] >> 6) & 0x03; 241 | out[3] = in[2] & 0x3F; 242 | } 243 | } 244 | 245 | // 246 | 247 | for (uint32_t i = 0; i < 4; i++) 248 | { 249 | if (gathered >= i) 250 | { 251 | encoded.push_back(base64Characters[out[i]]); 252 | } 253 | else 254 | { 255 | encoded.push_back('='); 256 | } 257 | } 258 | } 259 | 260 | return encoded; 261 | } 262 | 263 | ///----------------------------------------------------------------------- 264 | ///----------------------------------------------------------------------- 265 | 266 | } // namespace glTF_common 267 | -------------------------------------------------------------------------------- /src/2.0/glTFquery.cpp: -------------------------------------------------------------------------------- 1 | #include "flatgltf/2.0/glTF_generated.h" 2 | #include "flatgltf/2.0/glTFapi.hpp" 3 | 4 | #include "glTFinternal.hpp" 5 | 6 | #define KHUTILS_ASSERTION_INLINE 7 | 8 | #include "khutils/assertion.hpp" 9 | #include "khutils/runtime_exceptions.hpp" 10 | 11 | namespace glTF_2_0 12 | { 13 | //------------------------------------------------------------------------- 14 | 15 | std::vector queryAccessors(const Document* const doc, AccessorQuery_t query) 16 | { 17 | KHUTILS_ASSERT_PTR(doc); 18 | KHUTILS_ASSERT_PTR(query); 19 | 20 | std::vector results; 21 | results.reserve(doc->root->accessors.size()); 22 | auto resIt = std::back_inserter(results); 23 | 24 | for (auto& elem : doc->root->accessors) 25 | { 26 | if (query(doc, elem.get())) 27 | { 28 | *resIt++ = elem.get(); 29 | } 30 | } 31 | 32 | return results; 33 | } 34 | 35 | //--- 36 | 37 | std::vector queryAnimations(const Document* const doc, AnimationQuery_t query) 38 | { 39 | KHUTILS_ASSERT_PTR(doc); 40 | KHUTILS_ASSERT_PTR(query); 41 | 42 | std::vector results; 43 | results.reserve(doc->root->animations.size()); 44 | auto resIt = std::back_inserter(results); 45 | 46 | for (auto& elem : doc->root->animations) 47 | { 48 | if (query(doc, elem.get())) 49 | { 50 | *resIt++ = elem.get(); 51 | } 52 | } 53 | 54 | return results; 55 | } 56 | 57 | //--- 58 | 59 | std::vector queryBuffers(const Document* const doc, BufferQuery_t query) 60 | { 61 | KHUTILS_ASSERT_PTR(doc); 62 | KHUTILS_ASSERT_PTR(query); 63 | 64 | std::vector results; 65 | results.reserve(doc->root->buffers.size()); 66 | auto resIt = std::back_inserter(results); 67 | 68 | for (auto& elem : doc->root->buffers) 69 | { 70 | if (query(doc, elem.get())) 71 | { 72 | *resIt++ = elem.get(); 73 | } 74 | } 75 | 76 | return results; 77 | } 78 | 79 | //--- 80 | 81 | std::vector queryBufferViews(const Document* const doc, BufferViewQuery_t query) 82 | { 83 | KHUTILS_ASSERT_PTR(doc); 84 | KHUTILS_ASSERT_PTR(query); 85 | 86 | std::vector results; 87 | results.reserve(doc->root->bufferViews.size()); 88 | auto resIt = std::back_inserter(results); 89 | 90 | for (auto& elem : doc->root->bufferViews) 91 | { 92 | if (query(doc, elem.get())) 93 | { 94 | *resIt++ = elem.get(); 95 | } 96 | } 97 | 98 | return results; 99 | } 100 | 101 | //--- 102 | 103 | std::vector queryCameras(const Document* const doc, CameraQuery_t query) 104 | { 105 | KHUTILS_ASSERT_PTR(doc); 106 | KHUTILS_ASSERT_PTR(query); 107 | 108 | std::vector results; 109 | results.reserve(doc->root->cameras.size()); 110 | auto resIt = std::back_inserter(results); 111 | 112 | for (auto& elem : doc->root->cameras) 113 | { 114 | if (query(doc, elem.get())) 115 | { 116 | *resIt++ = elem.get(); 117 | } 118 | } 119 | 120 | return results; 121 | } 122 | 123 | //--- 124 | 125 | std::vector queryImages(const Document* const doc, ImageQuery_t query) 126 | { 127 | KHUTILS_ASSERT_PTR(doc); 128 | KHUTILS_ASSERT_PTR(query); 129 | 130 | std::vector results; 131 | results.reserve(doc->root->images.size()); 132 | auto resIt = std::back_inserter(results); 133 | 134 | for (auto& elem : doc->root->images) 135 | { 136 | if (query(doc, elem.get())) 137 | { 138 | *resIt++ = elem.get(); 139 | } 140 | } 141 | 142 | return results; 143 | } 144 | 145 | //--- 146 | 147 | std::vector queryMaterials(const Document* const doc, MaterialQuery_t query) 148 | { 149 | KHUTILS_ASSERT_PTR(doc); 150 | KHUTILS_ASSERT_PTR(query); 151 | 152 | std::vector results; 153 | results.reserve(doc->root->materials.size()); 154 | auto resIt = std::back_inserter(results); 155 | 156 | for (auto& elem : doc->root->materials) 157 | { 158 | if (query(doc, elem.get())) 159 | { 160 | *resIt++ = elem.get(); 161 | } 162 | } 163 | 164 | return results; 165 | } 166 | 167 | //--- 168 | 169 | std::vector queryMeshs(const Document* const doc, MeshQuery_t query) 170 | { 171 | KHUTILS_ASSERT_PTR(doc); 172 | KHUTILS_ASSERT_PTR(query); 173 | 174 | std::vector results; 175 | results.reserve(doc->root->meshes.size()); 176 | auto resIt = std::back_inserter(results); 177 | 178 | for (auto& elem : doc->root->meshes) 179 | { 180 | if (query(doc, elem.get())) 181 | { 182 | *resIt++ = elem.get(); 183 | } 184 | } 185 | 186 | return results; 187 | } 188 | 189 | //--- 190 | 191 | std::vector queryNodes(const Document* const doc, NodeQuery_t query) 192 | { 193 | KHUTILS_ASSERT_PTR(doc); 194 | KHUTILS_ASSERT_PTR(query); 195 | 196 | std::vector results; 197 | results.reserve(doc->root->nodes.size()); 198 | auto resIt = std::back_inserter(results); 199 | 200 | for (auto& elem : doc->root->nodes) 201 | { 202 | if (query(doc, elem.get())) 203 | { 204 | *resIt++ = elem.get(); 205 | } 206 | } 207 | 208 | return results; 209 | } 210 | 211 | //--- 212 | 213 | std::vector queryScenes(const Document* const doc, SceneQuery_t query) 214 | { 215 | KHUTILS_ASSERT_PTR(doc); 216 | KHUTILS_ASSERT_PTR(query); 217 | 218 | std::vector results; 219 | results.reserve(doc->root->scenes.size()); 220 | auto resIt = std::back_inserter(results); 221 | 222 | for (auto& elem : doc->root->scenes) 223 | { 224 | if (query(doc, elem.get())) 225 | { 226 | *resIt++ = elem.get(); 227 | } 228 | } 229 | 230 | return results; 231 | } 232 | 233 | //--- 234 | 235 | std::vector querySamplers(const Document* const doc, SamplerQuery_t query) 236 | { 237 | KHUTILS_ASSERT_PTR(doc); 238 | KHUTILS_ASSERT_PTR(query); 239 | 240 | std::vector results; 241 | results.reserve(doc->root->samplers.size()); 242 | auto resIt = std::back_inserter(results); 243 | 244 | for (auto& elem : doc->root->samplers) 245 | { 246 | if (query(doc, elem.get())) 247 | { 248 | *resIt++ = elem.get(); 249 | } 250 | } 251 | 252 | return results; 253 | } 254 | 255 | //--- 256 | 257 | std::vector queryTextures(const Document* const doc, TextureQuery_t query) 258 | { 259 | KHUTILS_ASSERT_PTR(doc); 260 | KHUTILS_ASSERT_PTR(query); 261 | 262 | std::vector results; 263 | results.reserve(doc->root->textures.size()); 264 | auto resIt = std::back_inserter(results); 265 | 266 | for (auto& elem : doc->root->textures) 267 | { 268 | if (query(doc, elem.get())) 269 | { 270 | *resIt++ = elem.get(); 271 | } 272 | } 273 | 274 | return results; 275 | } 276 | 277 | //--- 278 | 279 | std::vector querySkins(const Document* const doc, SkinQuery_t query) 280 | { 281 | KHUTILS_ASSERT_PTR(doc); 282 | KHUTILS_ASSERT_PTR(query); 283 | 284 | std::vector results; 285 | results.reserve(doc->root->skins.size()); 286 | auto resIt = std::back_inserter(results); 287 | 288 | for (auto& elem : doc->root->skins) 289 | { 290 | if (query(doc, elem.get())) 291 | { 292 | *resIt++ = elem.get(); 293 | } 294 | } 295 | 296 | return results; 297 | } 298 | 299 | //--- 300 | 301 | 302 | //------------------------------------------------------------------------- 303 | //------------------------------------------------------------------------- 304 | 305 | } // namespace glTF_2_0 306 | -------------------------------------------------------------------------------- /src/2.0/glTFimage.cpp: -------------------------------------------------------------------------------- 1 | #include "flatgltf/2.0/glTF_generated.h" 2 | #include "flatgltf/2.0/glTFapi.hpp" 3 | #include "flatgltf/common/glTFutils.hpp" 4 | 5 | #include "glTFinternal.hpp" 6 | 7 | #define KHUTILS_ASSERTION_INLINE 8 | 9 | #include "khutils/assertion.hpp" 10 | #include "khutils/runtime_exceptions.hpp" 11 | 12 | namespace glTF_2_0 13 | { 14 | using namespace glTF_common; 15 | 16 | //------------------------------------------------------------------------- 17 | 18 | SamplerT* const createSampler_withDefaults(Document* const doc) 19 | { 20 | KHUTILS_ASSERT_PTR(doc); 21 | 22 | auto sam = createSampler(doc); 23 | KHUTILS_ASSERT_PTR(sam); 24 | 25 | sam->magFilter = SamplerFilter::LINEAR; 26 | sam->minFilter = SamplerFilter::LINEAR; 27 | sam->wrapS = WrapMode::CLAMP_TO_EDGE; 28 | sam->wrapT = WrapMode::CLAMP_TO_EDGE; 29 | 30 | return sam; 31 | } 32 | 33 | //--- 34 | 35 | TextureT* const createTexture(Document* const doc, ImageT* const image) 36 | { 37 | KHUTILS_ASSERT_PTR(doc); 38 | KHUTILS_ASSERT_PTR(image); 39 | 40 | auto tex = createTexture(doc, (image->uri + "tex").c_str()); 41 | KHUTILS_ASSERT_PTR(tex); 42 | tex->source = getId(doc, image); 43 | 44 | return tex; 45 | } 46 | 47 | //--- 48 | 49 | ImageT* const createImage(Document* const doc, const char* name) 50 | { 51 | KHUTILS_ASSERT_PTR(doc); 52 | 53 | auto instance = Image_t{new ImageT}; 54 | if (name) 55 | { 56 | instance->name = name; 57 | } 58 | doc->root->images.push_back(std::move(instance)); 59 | 60 | auto createOk = createImageData(doc, doc->root->images.back().get()); 61 | KHUTILS_ASSERT(createOk); 62 | 63 | return doc->root->images.back().get(); 64 | } 65 | 66 | //--- 67 | 68 | //! create an image referring to a uri 69 | ImageT* const createImage(Document* const doc, const char* uri, const char* name) 70 | { 71 | KHUTILS_ASSERT_PTR(doc); 72 | KHUTILS_ASSERT_PTR(uri); 73 | 74 | auto image = createImage(doc, name); 75 | image->uri = uri; 76 | 77 | if (isDataUri(image->uri)) 78 | { 79 | setImageData(convertUriToData(uri), doc, image); 80 | image->uri = setUriBase64(uri, ""); // remove data, as its decoded in image now 81 | } 82 | 83 | return image; 84 | } 85 | 86 | //--- 87 | 88 | ImageT* const getOrCreateImage(Document* const doc, const char* uri, const char* name) 89 | { 90 | KHUTILS_ASSERT_PTR(doc); 91 | 92 | auto elem = getImage(doc, name); 93 | if (!elem) 94 | { 95 | elem = createImage(doc, uri, name); 96 | } 97 | return elem; 98 | } 99 | 100 | //--- 101 | //! create an image referring to a buffer view for data 102 | ImageT* const createImage(Document* const doc, BufferViewT* const view, const char* mimeType, const char* name) 103 | { 104 | KHUTILS_ASSERT_PTR(doc); 105 | KHUTILS_ASSERT_PTR(view); 106 | KHUTILS_ASSERT_PTR(mimeType); 107 | 108 | auto img = createImage(doc, name); 109 | KHUTILS_ASSERT_PTR(img); 110 | 111 | img->mimeType = mimeType; 112 | img->bufferView = getId(doc, view); 113 | 114 | return img; 115 | } 116 | 117 | //--- 118 | 119 | //! create an image referring to an external file uri, data kept in imgdata 120 | ImageT* const createImage(const std::vector& data, Document* const doc, const char* uri, const char* name) 121 | { 122 | KHUTILS_ASSERT_CNTR_NOT_EMPTY(data); 123 | return createImage(data.data(), data.size(), doc, uri, name); 124 | } 125 | 126 | //--- 127 | 128 | ImageT* const createImage(const uint8_t* const data, size_t length, Document* const doc, const char* uri, const char* name) 129 | { 130 | KHUTILS_ASSERT_PTR(doc); 131 | KHUTILS_ASSERT_PTR(data); 132 | KHUTILS_ASSERT_PTR(uri); 133 | 134 | auto img = createImage(doc, uri, name); 135 | KHUTILS_ASSERT_PTR(img); 136 | 137 | auto setLength = setImageData(data, length, doc, img); 138 | KHUTILS_ASSERT_EQUALS(setLength, length); 139 | 140 | return img; 141 | } 142 | 143 | //--- 144 | 145 | ///----------------------------------------------------------------------- 146 | /// imgdata 147 | ///----------------------------------------------------------------------- 148 | 149 | bool createImageData(Document* const doc, ImageT* const img) 150 | { 151 | KHUTILS_ASSERT_PTR(doc); 152 | KHUTILS_ASSERT_PTR(img); 153 | 154 | return createImageData(doc, getId(doc, img)); 155 | } 156 | 157 | //--- 158 | 159 | bool createImageData(Document* const doc, glTFid_t id) 160 | { 161 | KHUTILS_ASSERT_PTR(doc); 162 | KHUTILS_ASSERT(isValidImageId(doc, id)); 163 | 164 | if (std::find_if(doc->imgdata.begin(), 165 | doc->imgdata.end(), 166 | [&id](auto& bdp) { // 167 | return bdp.first == id; 168 | }) 169 | != doc->imgdata.end()) 170 | { 171 | return false; // data already exist 172 | } 173 | 174 | doc->imgdata[id].clear(); 175 | return true; 176 | } 177 | 178 | //--- 179 | 180 | std::vector getImageData(const Document* const doc, const ImageT* const img) 181 | { 182 | KHUTILS_ASSERT_PTR(doc); 183 | KHUTILS_ASSERT_PTR(img); 184 | 185 | auto id = getId(doc, img); 186 | KHUTILS_ASSERT(isValidImageId(doc, id)); 187 | 188 | if (isValidBufferViewId(doc, img->bufferView)) 189 | { 190 | return getBufferViewData(doc, getBufferView(doc, img->bufferView)); 191 | } 192 | 193 | auto it = std::find_if(doc->imgdata.begin(), doc->imgdata.end(), [&](auto& bdp) { // 194 | return bdp.first == id; 195 | }); 196 | KHUTILS_ASSERT_NOT(it, doc->imgdata.end()); 197 | 198 | return it->second; 199 | } 200 | 201 | //--- 202 | // sets image data, overwriting existing 203 | // returns new size 204 | // returns 0 if: image does not exist 205 | 206 | size_t setImageData(const std::vector& data, Document* const doc, ImageT* const img) 207 | { 208 | KHUTILS_ASSERT_CNTR_NOT_EMPTY(data); 209 | return setImageData(data.data(), data.size(), doc, img); 210 | } 211 | 212 | //--- 213 | 214 | size_t setImageData(const uint8_t* data, size_t length, Document* const doc, ImageT* const img) 215 | { 216 | KHUTILS_ASSERT_PTR(doc); 217 | KHUTILS_ASSERT_PTR(img); 218 | 219 | auto id = getId(doc, img); 220 | if (isValidImageId(doc, id)) 221 | { 222 | if (isValidBufferViewId(doc, img->bufferView)) 223 | { 224 | return setBufferViewData(data, length, doc, getBufferView(doc, img->bufferView)); 225 | } 226 | 227 | auto& imgdata = doc->imgdata[id]; 228 | imgdata.reserve(length); 229 | imgdata.assign(data, data + length); 230 | return imgdata.size(); 231 | } 232 | 233 | return 0; 234 | } 235 | 236 | //--- 237 | // appends image data to existing image data 238 | // returns new size 239 | // returns 0 if: image does not exist 240 | 241 | size_t appendImageData(const std::vector& data, Document* const doc, ImageT* const img) 242 | { 243 | KHUTILS_ASSERT_CNTR_NOT_EMPTY(data); 244 | return appendImageData(data.data(), data.size(), doc, img); 245 | } 246 | 247 | //--- 248 | 249 | size_t appendImageData(const uint8_t* data, size_t length, Document* const doc, ImageT* const img) 250 | { 251 | KHUTILS_ASSERT_PTR(doc); 252 | KHUTILS_ASSERT_PTR(img); 253 | 254 | auto id = getId(doc, img); 255 | if (isValidImageId(doc, id)) 256 | { 257 | auto imgdata = getImageData(doc, img); // expensive, but safe 258 | imgdata.reserve(imgdata.size() + length); 259 | std::copy_n(data, length, std::back_inserter(imgdata)); 260 | 261 | return setImageData(imgdata, doc, img); 262 | } 263 | 264 | return 0; 265 | } 266 | 267 | //--- 268 | 269 | //------------------------------------------------------------------------- 270 | //------------------------------------------------------------------------- 271 | 272 | } // namespace glTF_2_0 273 | -------------------------------------------------------------------------------- /src/2.0/glTFio_glb.cpp: -------------------------------------------------------------------------------- 1 | #include "flatgltf/2.0/glTF_generated.h" 2 | #include "flatgltf/2.0/glTFapi.hpp" 3 | #include "flatgltf/2.0/glTFio.hpp" 4 | #include "flatgltf/common/glTFutils.hpp" 5 | #include "glTFinternal.hpp" 6 | 7 | #include "flatbuffers/flatbuffers.h" 8 | #include "flatbuffers/idl.h" 9 | 10 | #define KHUTILS_ASSERTION_INLINE 11 | #include "khutils/assertion.hpp" 12 | #include "khutils/file.hpp" 13 | #include "khutils/logging.hpp" 14 | #include "khutils/runtime_exceptions.hpp" 15 | 16 | /// terribly sorry for this dependency. will remove asap 17 | #include 18 | 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | // flatbuffer schema string for parser 27 | extern const std::string flatgltf_2_0_schema; 28 | 29 | 30 | namespace glTF_2_0 31 | { 32 | using namespace glTF_common; 33 | 34 | ///------------------------------------------------------------------------ 35 | /// types for GLB files 36 | ///------------------------------------------------------------------------ 37 | 38 | MANUALLY_ALIGNED_STRUCT(4) GLBHeader 39 | { 40 | /// magic equals 0x46546C67. It is ASCII string 'glTF', and can be used to identify data as Binary glTF. 41 | uint32_t magic = (uint32_t)GLBConstant::magic; 42 | 43 | /// version indicates the version of the Binary glTF container format. This specification defines version 2. 44 | uint32_t version = (uint32_t)GLBConstant::version; 45 | 46 | /// length is the total length of the Binary glTF, including Header and all Chunks, in bytes. 47 | uint32_t length; 48 | }; 49 | static_assert(sizeof(GLBHeader) == 12, "GLBHeader is not 12 bytes"); 50 | 51 | 52 | MANUALLY_ALIGNED_STRUCT(4) GLBChunkHeader 53 | { 54 | /// chunkLength is the length of chunkData, in bytes. 55 | /// NOTE: must be modulo 4 56 | uint32_t chunkLength; 57 | 58 | /// chunkType indicates the type of chunk. See Table 1 for details. 59 | /// chunkType equals 0x4E4F534A, ASCII string 'JSON' -> GLBConstant::json 60 | /// chunkType equals 0x004E4942 , ASCII string 'BIN' -> GLBConstant::binary 61 | uint32_t chunkType; 62 | }; 63 | static_assert(sizeof(GLBChunkHeader) == 8, "GLBChunkHeader is not 8 bytes"); 64 | 65 | //------------------------------------------------------------------------- 66 | 67 | ///------------------------------------------------------------------------ 68 | /// helper function to create GLB file buffer 69 | ///------------------------------------------------------------------------ 70 | 71 | std::vector createGLBBuffer(const Root_t& root, const std::vector& bindata) 72 | { 73 | KHUTILS_ASSERT_PTR(root); 74 | KHUTILS_ASSERT_CNTR_NOT_EMPTY(bindata); 75 | 76 | auto jsonString = to_json(root); 77 | jsonString.resize(jsonString.size() + jsonString.size() % 4, 0x20); // pad to alignment with spaces 78 | 79 | auto jsonStringSize = jsonString.size(); 80 | auto bindataSize = bindata.size() + bindata.size() % 4; 81 | 82 | std::vector wbuffer; 83 | wbuffer.reserve(sizeof(GLBHeader) + 2 * sizeof(GLBChunkHeader) + jsonStringSize + bindataSize); 84 | 85 | GLBHeader header; 86 | KHUTILS_ASSERT_EQUALS(header.magic, (uint32_t)GLBConstant::magic); 87 | KHUTILS_ASSERT_EQUALS(header.version, (uint32_t)GLBConstant::version); 88 | header.length = wbuffer.size(); 89 | 90 | GLBChunkHeader jsChunk{(uint32_t)jsonStringSize, (uint32_t)GLBConstant::json}; 91 | GLBChunkHeader binChunk{(uint32_t)bindataSize, (uint32_t)GLBConstant::binary}; 92 | 93 | auto bi = std::back_inserter(wbuffer); 94 | std::copy_n(reinterpret_cast(&header), sizeof(GLBHeader), bi); 95 | std::copy_n(reinterpret_cast(&jsChunk), sizeof(GLBChunkHeader), bi); 96 | std::transform(jsonString.begin(), jsonString.end(), bi, [](auto& c) { return static_cast(c); }); 97 | std::copy_n(reinterpret_cast(&binChunk), sizeof(GLBChunkHeader), bi); 98 | std::copy(bindata.begin(), bindata.end(), bi); 99 | wbuffer.resize(wbuffer.size() + wbuffer.size() % 4, 0x0); // pad to alignment with 0 100 | 101 | KHUTILS_ASSERT_CNTR_NOT_EMPTY(wbuffer); 102 | return wbuffer; 103 | } 104 | 105 | //--- 106 | 107 | bool readGLBBuffer(Document* const doc, const std::vector& buffer) 108 | { 109 | KHUTILS_ASSERT_PTR(doc); 110 | KHUTILS_ASSERT_CNTR_NOT_EMPTY(buffer); 111 | if (buffer.empty()) 112 | { 113 | return false; 114 | } 115 | 116 | const GLBHeader& header = *reinterpret_cast(&*buffer.begin()); 117 | KHUTILS_ASSERT_EQUALS(header.magic, (uint32_t)GLBConstant::magic); 118 | KHUTILS_ASSERT_EQUALS(header.version, (uint32_t)GLBConstant::version); 119 | KHUTILS_ASSERT_EQUALS(header.length, (uint32_t)buffer.size()); 120 | 121 | const GLBChunkHeader& jsChunk = *reinterpret_cast(&*(buffer.begin() + sizeof(GLBHeader))); 122 | assert(jsChunk.chunkType == (uint32_t)GLBConstant::json); 123 | assert(jsChunk.chunkLength >= sizeof(GLBChunkHeader)); 124 | assert((jsChunk.chunkLength % 4) == 0); 125 | 126 | std::string jsonString(reinterpret_cast(&*(buffer.begin() + sizeof(GLBHeader) + sizeof(GLBChunkHeader)))); 127 | KHUTILS_ASSERT_CNTR_NOT_EMPTY(jsonString); 128 | 129 | doc->root = from_json_internal(jsonString); 130 | 131 | // check if there's more data, i.e. binary chunk 132 | if (header.length > sizeof(GLBHeader) + sizeof(GLBChunkHeader) + jsChunk.chunkLength) 133 | { 134 | const GLBChunkHeader& binChunk = *reinterpret_cast( 135 | &*(buffer.begin() + sizeof(GLBHeader) + sizeof(GLBChunkHeader) + jsChunk.chunkLength)); 136 | assert(binChunk.chunkType == (uint32_t)GLBConstant::binary); 137 | assert(binChunk.chunkLength >= sizeof(GLBChunkHeader)); 138 | assert((binChunk.chunkLength % 4) == 0); 139 | 140 | auto buf = getBuffer(doc, 0); 141 | KHUTILS_ASSERT_PTR(buf); 142 | KHUTILS_ASSERT(!isDataUri(buf->uri)); 143 | 144 | auto bindata = std::vector{buffer.begin() + sizeof(GLBHeader) + sizeof(GLBChunkHeader) 145 | + jsChunk.chunkLength + sizeof(GLBChunkHeader), 146 | buffer.end()}; 147 | auto bufSize = setBufferData(bindata, doc, buf); 148 | return bufSize == bindata.size(); 149 | } 150 | 151 | return true; 152 | } 153 | 154 | 155 | //------------------------------------------------------------------------- 156 | 157 | ///------------------------------------------------------------------------ 158 | /// load document from glTF-binary: all buffers merged into 1, without external data 159 | ///------------------------------------------------------------------------ 160 | 161 | bool loadDocument_glb(Document* const doc, const char* location, data_reader_t reader) 162 | { 163 | KHUTILS_ASSERT_PTR(doc); 164 | 165 | std::vector buffer; 166 | return reader(location, buffer) && readGLBBuffer(doc, buffer); 167 | } 168 | 169 | //--- 170 | 171 | ///------------------------------------------------------------------------ 172 | /// write document to glTF-binary: all buffers merged into 1, external or data uris as-is 173 | ///------------------------------------------------------------------------ 174 | 175 | bool saveDocument_glb(const Document* const doc, const char* location, data_writer_t writer) 176 | { 177 | KHUTILS_ASSERT_PTR(doc); 178 | 179 | Root_t root; 180 | std::vector bindata; 181 | std::tie(root, bindata) = binarizeDocumentBuffers(doc, false); 182 | 183 | auto wbuffer = createGLBBuffer(root, bindata); 184 | return writer(location, wbuffer); 185 | } 186 | 187 | //--- 188 | 189 | ///------------------------------------------------------------------------ 190 | /// write document to glTF-binary: all buffers merged into 1, ALL external images will be embedded base64 URIs 191 | ///------------------------------------------------------------------------ 192 | 193 | bool saveDocument_glb_embed(const Document* const doc, const char* location, data_writer_t writer) 194 | { 195 | KHUTILS_ASSERT_PTR(doc); 196 | 197 | Root_t root; 198 | std::vector bindata; 199 | std::tie(root, bindata) = binarizeDocumentBuffers_embedImages(doc, true); 200 | 201 | auto wbuffer = createGLBBuffer(root, bindata); 202 | return writer(location, wbuffer); 203 | } 204 | 205 | //--- 206 | 207 | ///------------------------------------------------------------------------ 208 | /// write document to glTF-binary: all buffers merged into 1, ALL external images also transformed to use bufferView 209 | ///------------------------------------------------------------------------ 210 | 211 | bool saveDocument_glb_buffer(const Document* const doc, const char* location, data_writer_t writer) 212 | { 213 | KHUTILS_ASSERT_PTR(doc); 214 | 215 | Root_t root; 216 | std::vector bindata; 217 | std::tie(root, bindata) = binarizeDocumentBuffers(doc, true); 218 | 219 | auto wbuffer = createGLBBuffer(root, bindata); 220 | return writer(location, wbuffer); 221 | } 222 | 223 | //------------------------------------------------------------------------- 224 | //------------------------------------------------------------------------- 225 | 226 | } // namespace glTF_2_0 227 | -------------------------------------------------------------------------------- /src/2.0/glTFid.cpp: -------------------------------------------------------------------------------- 1 | #include "flatgltf/2.0/glTF_generated.h" 2 | #include "flatgltf/2.0/glTFapi.hpp" 3 | 4 | #include "glTFinternal.hpp" 5 | 6 | #define KHUTILS_ASSERTION_INLINE 7 | 8 | #include "khutils/assertion.hpp" 9 | #include "khutils/runtime_exceptions.hpp" 10 | 11 | #include 12 | #include 13 | 14 | namespace glTF_2_0 15 | { 16 | ///----------------------------------------------------------------------- 17 | /// get-id 18 | ///----------------------------------------------------------------------- 19 | // all functions return a glTFid_t >= 0 if found, < 0 if not 20 | 21 | glTFid_t getId(const Document* const doc, const AccessorT* const elem) 22 | { 23 | if (!elem) 24 | { 25 | return -2; 26 | } 27 | if (doc && doc->root) 28 | { 29 | auto it = std::find_if(doc->root->accessors.begin(), doc->root->accessors.end(), [&elem](auto& inst) { 30 | return inst.get() == elem; 31 | }); 32 | if (it != doc->root->accessors.end()) 33 | { 34 | return glTFid_t(std::distance(doc->root->accessors.begin(), it)); 35 | } 36 | } 37 | return -1; 38 | } 39 | 40 | //--- 41 | 42 | glTFid_t getId(const Document* const doc, const AnimationT* const elem) 43 | { 44 | if (!elem) 45 | { 46 | return -2; 47 | } 48 | if (doc && doc->root) 49 | { 50 | auto it = std::find_if(doc->root->animations.begin(), doc->root->animations.end(), [&elem](auto& inst) { 51 | return inst.get() == elem; 52 | }); 53 | if (it != doc->root->animations.end()) 54 | { 55 | return glTFid_t(std::distance(doc->root->animations.begin(), it)); 56 | } 57 | } 58 | return -1; 59 | } 60 | 61 | //--- 62 | 63 | glTFid_t getId(const Document* const doc, const BufferT* const elem) 64 | { 65 | if (!elem) 66 | { 67 | return -2; 68 | } 69 | if (doc && doc->root) 70 | { 71 | auto it = std::find_if(doc->root->buffers.begin(), doc->root->buffers.end(), [&elem](auto& inst) { 72 | return inst.get() == elem; 73 | }); 74 | if (it != doc->root->buffers.end()) 75 | { 76 | return glTFid_t(std::distance(doc->root->buffers.begin(), it)); 77 | } 78 | } 79 | return -1; 80 | } 81 | 82 | //--- 83 | 84 | glTFid_t getId(const Document* const doc, const BufferViewT* const elem) 85 | { 86 | if (!elem) 87 | { 88 | return -2; 89 | } 90 | if (doc && doc->root) 91 | { 92 | auto it = std::find_if(doc->root->bufferViews.begin(), doc->root->bufferViews.end(), [&elem](auto& inst) { 93 | return inst.get() == elem; 94 | }); 95 | if (it != doc->root->bufferViews.end()) 96 | { 97 | return glTFid_t(std::distance(doc->root->bufferViews.begin(), it)); 98 | } 99 | } 100 | return -1; 101 | } 102 | 103 | //--- 104 | 105 | glTFid_t getId(const Document* const doc, const CameraT* const elem) 106 | { 107 | if (!elem) 108 | { 109 | return -2; 110 | } 111 | if (doc && doc->root) 112 | { 113 | auto it = std::find_if(doc->root->cameras.begin(), doc->root->cameras.end(), [&elem](auto& inst) { 114 | return inst.get() == elem; 115 | }); 116 | if (it != doc->root->cameras.end()) 117 | { 118 | return glTFid_t(std::distance(doc->root->cameras.begin(), it)); 119 | } 120 | } 121 | return -1; 122 | } 123 | 124 | //--- 125 | 126 | glTFid_t getId(const Document* const doc, const ImageT* const elem) 127 | { 128 | if (!elem) 129 | { 130 | return -2; 131 | } 132 | if (doc && doc->root) 133 | { 134 | auto it = std::find_if(doc->root->images.begin(), doc->root->images.end(), [&elem](auto& inst) { 135 | return inst.get() == elem; 136 | }); 137 | if (it != doc->root->images.end()) 138 | { 139 | return glTFid_t(std::distance(doc->root->images.begin(), it)); 140 | } 141 | } 142 | return -1; 143 | } 144 | 145 | //--- 146 | 147 | glTFid_t getId(const Document* const doc, const MaterialT* const elem) 148 | { 149 | if (!elem) 150 | { 151 | return -2; 152 | } 153 | if (doc && doc->root) 154 | { 155 | auto it = std::find_if(doc->root->materials.begin(), doc->root->materials.end(), [&elem](auto& inst) { 156 | return inst.get() == elem; 157 | }); 158 | if (it != doc->root->materials.end()) 159 | { 160 | return glTFid_t(std::distance(doc->root->materials.begin(), it)); 161 | } 162 | } 163 | return -1; 164 | } 165 | 166 | //--- 167 | 168 | glTFid_t getId(const Document* const doc, const MeshT* const elem) 169 | { 170 | if (!elem) 171 | { 172 | return -2; 173 | } 174 | if (doc && doc->root) 175 | { 176 | auto it = std::find_if(doc->root->meshes.begin(), doc->root->meshes.end(), [&elem](auto& inst) { 177 | return inst.get() == elem; 178 | }); 179 | if (it != doc->root->meshes.end()) 180 | { 181 | return glTFid_t(std::distance(doc->root->meshes.begin(), it)); 182 | } 183 | } 184 | return -1; 185 | } 186 | 187 | //--- 188 | 189 | glTFid_t getId(const Document* const doc, const NodeT* const elem) 190 | { 191 | if (!elem) 192 | { 193 | return -2; 194 | } 195 | if (doc && doc->root) 196 | { 197 | auto it = std::find_if(doc->root->nodes.begin(), doc->root->nodes.end(), [&elem](auto& inst) { 198 | return inst.get() == elem; 199 | }); 200 | if (it != doc->root->nodes.end()) 201 | { 202 | return glTFid_t(std::distance(doc->root->nodes.begin(), it)); 203 | } 204 | } 205 | return -1; 206 | } 207 | 208 | //--- 209 | 210 | glTFid_t getId(const Document* const doc, const SceneT* const elem) 211 | { 212 | if (!elem) 213 | { 214 | return -2; 215 | } 216 | if (doc && doc->root) 217 | { 218 | auto it = std::find_if(doc->root->scenes.begin(), doc->root->scenes.end(), [&elem](auto& inst) { 219 | return inst.get() == elem; 220 | }); 221 | if (it != doc->root->scenes.end()) 222 | { 223 | return glTFid_t(std::distance(doc->root->scenes.begin(), it)); 224 | } 225 | } 226 | return -1; 227 | } 228 | 229 | //--- 230 | 231 | glTFid_t getId(const Document* const doc, const SamplerT* const elem) 232 | { 233 | if (!elem) 234 | { 235 | return -2; 236 | } 237 | if (doc && doc->root) 238 | { 239 | auto it = std::find_if(doc->root->samplers.begin(), doc->root->samplers.end(), [&elem](auto& inst) { 240 | return inst.get() == elem; 241 | }); 242 | if (it != doc->root->samplers.end()) 243 | { 244 | return glTFid_t(std::distance(doc->root->samplers.begin(), it)); 245 | } 246 | } 247 | return -1; 248 | } 249 | 250 | //--- 251 | 252 | glTFid_t getId(const Document* const doc, const TextureT* const elem) 253 | { 254 | if (!elem) 255 | { 256 | return -2; 257 | } 258 | if (doc && doc->root) 259 | { 260 | auto it = std::find_if(doc->root->textures.begin(), doc->root->textures.end(), [&elem](auto& inst) { 261 | return inst.get() == elem; 262 | }); 263 | if (it != doc->root->textures.end()) 264 | { 265 | return glTFid_t(std::distance(doc->root->textures.begin(), it)); 266 | } 267 | } 268 | return -1; 269 | } 270 | 271 | //--- 272 | 273 | glTFid_t getId(const Document* const doc, const SkinT* const elem) 274 | { 275 | if (!elem) 276 | { 277 | return -2; 278 | } 279 | if (doc && doc->root) 280 | { 281 | auto it = std::find_if(doc->root->skins.begin(), doc->root->skins.end(), [&elem](auto& inst) { 282 | return inst.get() == elem; 283 | }); 284 | if (it != doc->root->skins.end()) 285 | { 286 | return glTFid_t(std::distance(doc->root->skins.begin(), it)); 287 | } 288 | } 289 | return -1; 290 | } 291 | 292 | //--- 293 | 294 | ///----------------------------------------------------------------------- 295 | /// validate-id 296 | ///----------------------------------------------------------------------- 297 | // all functions return true if id is valid for this document and type 298 | 299 | bool isValidAccessorId(const Document* const doc, glTFid_t id) 300 | { 301 | KHUTILS_ASSERT_PTR(doc); 302 | return (id >= 0) && (id < (glTFid_t)doc->root->accessors.size()); 303 | } 304 | 305 | //--- 306 | 307 | bool isValidAnimationId(const Document* const doc, glTFid_t id) 308 | { 309 | KHUTILS_ASSERT_PTR(doc); 310 | return (id >= 0) && (id < (glTFid_t)doc->root->animations.size()); 311 | } 312 | 313 | //--- 314 | 315 | bool isValidBufferId(const Document* const doc, glTFid_t id) 316 | { 317 | KHUTILS_ASSERT_PTR(doc); 318 | return (id >= 0) && (id < (glTFid_t)doc->root->buffers.size()); 319 | } 320 | 321 | //--- 322 | 323 | bool isValidBufferViewId(const Document* const doc, glTFid_t id) 324 | { 325 | KHUTILS_ASSERT_PTR(doc); 326 | return (id >= 0) && (id < (glTFid_t)doc->root->bufferViews.size()); 327 | } 328 | 329 | //--- 330 | 331 | bool isValidCameraId(const Document* const doc, glTFid_t id) 332 | { 333 | KHUTILS_ASSERT_PTR(doc); 334 | return (id >= 0) && (id < (glTFid_t)doc->root->cameras.size()); 335 | } 336 | 337 | //--- 338 | 339 | bool isValidImageId(const Document* const doc, glTFid_t id) 340 | { 341 | KHUTILS_ASSERT_PTR(doc); 342 | return (id >= 0) && (id < (glTFid_t)doc->root->images.size()); 343 | } 344 | 345 | //--- 346 | 347 | bool isValidMaterialId(const Document* const doc, glTFid_t id) 348 | { 349 | KHUTILS_ASSERT_PTR(doc); 350 | return (id >= 0) && (id < (glTFid_t)doc->root->materials.size()); 351 | } 352 | 353 | //--- 354 | 355 | bool isValidMeshId(const Document* const doc, glTFid_t id) 356 | { 357 | KHUTILS_ASSERT_PTR(doc); 358 | return (id >= 0) && (id < (glTFid_t)doc->root->meshes.size()); 359 | } 360 | 361 | //--- 362 | 363 | bool isValidNodeId(const Document* const doc, glTFid_t id) 364 | { 365 | KHUTILS_ASSERT_PTR(doc); 366 | return (id >= 0) && (id < (glTFid_t)doc->root->nodes.size()); 367 | } 368 | 369 | //--- 370 | 371 | bool isValidSceneId(const Document* const doc, glTFid_t id) 372 | { 373 | KHUTILS_ASSERT_PTR(doc); 374 | return (id >= 0) && (id < (glTFid_t)doc->root->scenes.size()); 375 | } 376 | 377 | //--- 378 | 379 | bool isValidSamplerId(const Document* const doc, glTFid_t id) 380 | { 381 | KHUTILS_ASSERT_PTR(doc); 382 | return (id >= 0) && (id < (glTFid_t)doc->root->samplers.size()); 383 | } 384 | 385 | //--- 386 | 387 | bool isValidTextureId(const Document* const doc, glTFid_t id) 388 | { 389 | KHUTILS_ASSERT_PTR(doc); 390 | return (id >= 0) && (id < (glTFid_t)doc->root->textures.size()); 391 | } 392 | 393 | //--- 394 | 395 | bool isValidSkinId(const Document* const doc, glTFid_t id) 396 | { 397 | KHUTILS_ASSERT_PTR(doc); 398 | return (id >= 0) && (id < (glTFid_t)doc->root->skins.size()); 399 | } 400 | 401 | //--- 402 | 403 | 404 | //------------------------------------------------------------------------- 405 | //------------------------------------------------------------------------- 406 | 407 | } // namespace glTF_2_0 408 | -------------------------------------------------------------------------------- /src/2.0/glTFget.cpp: -------------------------------------------------------------------------------- 1 | #include "flatgltf/2.0/glTF_generated.h" 2 | #include "flatgltf/2.0/glTFapi.hpp" 3 | 4 | #include "glTFinternal.hpp" 5 | 6 | #define KHUTILS_ASSERTION_INLINE 7 | 8 | #include "khutils/assertion.hpp" 9 | #include "khutils/runtime_exceptions.hpp" 10 | 11 | #include 12 | #include 13 | 14 | namespace glTF_2_0 15 | { 16 | ///----------------------------------------------------------------------- 17 | /// get-by-id 18 | ///----------------------------------------------------------------------- 19 | 20 | AccessorT* const getAccessor(const Document* const doc, glTFid_t id) 21 | { 22 | if (doc && doc->root) 23 | { 24 | if (id >= 0 && id < doc->root->accessors.size()) 25 | return doc->root->accessors[id].get(); 26 | } 27 | return nullptr; 28 | } 29 | 30 | //--- 31 | 32 | AnimationT* const getAnimation(const Document* const doc, glTFid_t id) 33 | { 34 | if (doc && doc->root) 35 | { 36 | if (id >= 0 && id < doc->root->animations.size()) 37 | return doc->root->animations[id].get(); 38 | } 39 | return nullptr; 40 | } 41 | 42 | //--- 43 | 44 | BufferT* const getBuffer(const Document* const doc, glTFid_t id) 45 | { 46 | if (doc && doc->root) 47 | { 48 | if (id >= 0 && id < doc->root->buffers.size()) 49 | return doc->root->buffers[id].get(); 50 | } 51 | return nullptr; 52 | } 53 | 54 | //--- 55 | 56 | BufferViewT* const getBufferView(const Document* const doc, glTFid_t id) 57 | { 58 | if (doc && doc->root) 59 | { 60 | if (id >= 0 && id < doc->root->bufferViews.size()) 61 | return doc->root->bufferViews[id].get(); 62 | } 63 | return nullptr; 64 | } 65 | 66 | //--- 67 | 68 | CameraT* const getCamera(const Document* const doc, glTFid_t id) 69 | { 70 | if (doc && doc->root) 71 | { 72 | if (id >= 0 && id < doc->root->cameras.size()) 73 | return doc->root->cameras[id].get(); 74 | } 75 | return nullptr; 76 | } 77 | 78 | //--- 79 | 80 | ImageT* const getImage(const Document* const doc, glTFid_t id) 81 | { 82 | if (doc && doc->root) 83 | { 84 | if (id >= 0 && id < doc->root->images.size()) 85 | return doc->root->images[id].get(); 86 | } 87 | return nullptr; 88 | } 89 | 90 | //--- 91 | 92 | MaterialT* const getMaterial(const Document* const doc, glTFid_t id) 93 | { 94 | if (doc && doc->root) 95 | { 96 | if (id >= 0 && id < doc->root->materials.size()) 97 | return doc->root->materials[id].get(); 98 | } 99 | return nullptr; 100 | } 101 | 102 | //--- 103 | 104 | // MeshPrimitiveT *const getMeshPrimitive(const Document* const doc, glTFid_t id) 105 | //{ 106 | // if (doc && doc->root) 107 | // { 108 | // if (id >= 0 && id < doc->root->meshprimitives.size()) 109 | // return doc->root->meshprimitives[id].get(); 110 | // } 111 | // return nullptr; 112 | //} 113 | 114 | //--- 115 | 116 | MeshT* const getMesh(const Document* const doc, glTFid_t id) 117 | { 118 | if (doc && doc->root) 119 | { 120 | if (id >= 0 && id < doc->root->meshes.size()) 121 | return doc->root->meshes[id].get(); 122 | } 123 | return nullptr; 124 | } 125 | 126 | //--- 127 | 128 | NodeT* const getNode(const Document* const doc, glTFid_t id) 129 | { 130 | if (doc && doc->root) 131 | { 132 | if (id >= 0 && id < doc->root->nodes.size()) 133 | return doc->root->nodes[id].get(); 134 | } 135 | return nullptr; 136 | } 137 | 138 | //--- 139 | 140 | SceneT* const getScene(const Document* const doc, glTFid_t id) 141 | { 142 | if (doc && doc->root) 143 | { 144 | if (id >= 0 && id < doc->root->scenes.size()) 145 | return doc->root->scenes[id].get(); 146 | } 147 | return nullptr; 148 | } 149 | 150 | //--- 151 | 152 | SamplerT* const getSampler(const Document* const doc, glTFid_t id) 153 | { 154 | if (doc && doc->root) 155 | { 156 | if (id >= 0 && id < doc->root->samplers.size()) 157 | return doc->root->samplers[id].get(); 158 | } 159 | return nullptr; 160 | } 161 | 162 | //--- 163 | 164 | TextureT* const getTexture(const Document* const doc, glTFid_t id) 165 | { 166 | if (doc && doc->root) 167 | { 168 | if (id >= 0 && id < doc->root->textures.size()) 169 | return doc->root->textures[id].get(); 170 | } 171 | return nullptr; 172 | } 173 | 174 | //--- 175 | 176 | SkinT* const getSkin(const Document* const doc, glTFid_t id) 177 | { 178 | if (doc && doc->root) 179 | { 180 | if (id >= 0 && id < doc->root->skins.size()) 181 | return doc->root->skins[id].get(); 182 | } 183 | return nullptr; 184 | } 185 | 186 | //--- 187 | 188 | 189 | ///----------------------------------------------------------------------- 190 | /// get-by-name 191 | /// note that names are optional and this might not work 192 | ///----------------------------------------------------------------------- 193 | 194 | AccessorT* const getAccessor(const Document* const doc, const char* name) 195 | { 196 | if (doc && doc->root) 197 | { 198 | auto it = std::find_if(doc->root->accessors.begin(), doc->root->accessors.end(), [&name](auto& elem) { 199 | return elem && elem->name == name; 200 | }); 201 | if (it != doc->root->accessors.end()) 202 | { 203 | return it->get(); 204 | } 205 | } 206 | return nullptr; 207 | } 208 | 209 | //--- 210 | 211 | AnimationT* const getAnimation(const Document* const doc, const char* name) 212 | { 213 | if (doc && doc->root) 214 | { 215 | auto it = std::find_if(doc->root->animations.begin(), doc->root->animations.end(), [&name](auto& elem) { 216 | return elem && elem->name == name; 217 | }); 218 | if (it != doc->root->animations.end()) 219 | { 220 | return it->get(); 221 | } 222 | } 223 | return nullptr; 224 | } 225 | 226 | //--- 227 | 228 | BufferT* const getBuffer(const Document* const doc, const char* name) 229 | { 230 | if (doc && doc->root) 231 | { 232 | auto it = std::find_if(doc->root->buffers.begin(), doc->root->buffers.end(), [&name](auto& elem) { 233 | return elem && elem->name == name; 234 | }); 235 | if (it != doc->root->buffers.end()) 236 | { 237 | return it->get(); 238 | } 239 | } 240 | return nullptr; 241 | } 242 | 243 | //--- 244 | 245 | BufferViewT* const getBufferView(const Document* const doc, const char* name) 246 | { 247 | if (doc && doc->root) 248 | { 249 | auto it = std::find_if(doc->root->bufferViews.begin(), doc->root->bufferViews.end(), [&name](auto& elem) { 250 | return elem && elem->name == name; 251 | }); 252 | if (it != doc->root->bufferViews.end()) 253 | { 254 | return it->get(); 255 | } 256 | } 257 | return nullptr; 258 | } 259 | 260 | //--- 261 | 262 | CameraT* const getCamera(const Document* const doc, const char* name) 263 | { 264 | if (doc && doc->root) 265 | { 266 | auto it = std::find_if(doc->root->cameras.begin(), doc->root->cameras.end(), [&name](auto& elem) { 267 | return elem && elem->name == name; 268 | }); 269 | if (it != doc->root->cameras.end()) 270 | { 271 | return it->get(); 272 | } 273 | } 274 | return nullptr; 275 | } 276 | 277 | //--- 278 | 279 | ImageT* const getImage(const Document* const doc, const char* name) 280 | { 281 | if (doc && doc->root) 282 | { 283 | auto it = std::find_if(doc->root->images.begin(), doc->root->images.end(), [&name](auto& elem) { 284 | return elem && elem->name == name; 285 | }); 286 | if (it != doc->root->images.end()) 287 | { 288 | return it->get(); 289 | } 290 | } 291 | return nullptr; 292 | } 293 | 294 | //--- 295 | 296 | MaterialT* const getMaterial(const Document* const doc, const char* name) 297 | { 298 | if (doc && doc->root) 299 | { 300 | auto it = std::find_if(doc->root->materials.begin(), doc->root->materials.end(), [&name](auto& elem) { 301 | return elem && elem->name == name; 302 | }); 303 | if (it != doc->root->materials.end()) 304 | { 305 | return it->get(); 306 | } 307 | } 308 | return nullptr; 309 | } 310 | 311 | //--- 312 | 313 | MeshT* const getMesh(const Document* const doc, const char* name) 314 | { 315 | if (doc && doc->root) 316 | { 317 | auto it = std::find_if(doc->root->meshes.begin(), doc->root->meshes.end(), [&name](auto& elem) { 318 | return elem && elem->name == name; 319 | }); 320 | if (it != doc->root->meshes.end()) 321 | { 322 | return it->get(); 323 | } 324 | } 325 | return nullptr; 326 | } 327 | 328 | //--- 329 | 330 | NodeT* const getNode(const Document* const doc, const char* name) 331 | { 332 | if (doc && doc->root) 333 | { 334 | auto it = std::find_if(doc->root->nodes.begin(), doc->root->nodes.end(), [&name](auto& elem) { 335 | return elem && elem->name == name; 336 | }); 337 | if (it != doc->root->nodes.end()) 338 | { 339 | return it->get(); 340 | } 341 | } 342 | return nullptr; 343 | } 344 | 345 | //--- 346 | 347 | SceneT* const getScene(const Document* const doc, const char* name) 348 | { 349 | if (doc && doc->root) 350 | { 351 | auto it = std::find_if(doc->root->scenes.begin(), doc->root->scenes.end(), [&name](auto& elem) { 352 | return elem && elem->name == name; 353 | }); 354 | if (it != doc->root->scenes.end()) 355 | { 356 | return it->get(); 357 | } 358 | } 359 | return nullptr; 360 | } 361 | 362 | //--- 363 | 364 | SamplerT* const getSampler(const Document* const doc, const char* name) 365 | { 366 | if (doc && doc->root) 367 | { 368 | auto it = std::find_if(doc->root->samplers.begin(), doc->root->samplers.end(), [&name](auto& elem) { 369 | return elem && elem->name == name; 370 | }); 371 | if (it != doc->root->samplers.end()) 372 | { 373 | return it->get(); 374 | } 375 | } 376 | return nullptr; 377 | } 378 | 379 | //--- 380 | 381 | TextureT* const getTexture(const Document* const doc, const char* name) 382 | { 383 | if (doc && doc->root) 384 | { 385 | auto it = std::find_if(doc->root->textures.begin(), doc->root->textures.end(), [&name](auto& elem) { 386 | return elem && elem->name == name; 387 | }); 388 | if (it != doc->root->textures.end()) 389 | { 390 | return it->get(); 391 | } 392 | } 393 | return nullptr; 394 | } 395 | 396 | //--- 397 | 398 | SkinT* const getSkin(const Document* const doc, const char* name) 399 | { 400 | if (doc && doc->root) 401 | { 402 | auto it = std::find_if(doc->root->skins.begin(), doc->root->skins.end(), [&name](auto& elem) { 403 | return elem && elem->name == name; 404 | }); 405 | if (it != doc->root->skins.end()) 406 | { 407 | return it->get(); 408 | } 409 | } 410 | return nullptr; 411 | } 412 | 413 | //--- 414 | 415 | //------------------------------------------------------------------------- 416 | //------------------------------------------------------------------------- 417 | 418 | } // namespace glTF_2_0 419 | -------------------------------------------------------------------------------- /src/2.0/glTFutility.cpp: -------------------------------------------------------------------------------- 1 | #include "flatgltf/2.0/glTF_generated.h" 2 | #include "flatgltf/2.0/glTFapi.hpp" 3 | #include "flatgltf/common/glTFutils.hpp" 4 | 5 | #include "glTFinternal.hpp" 6 | 7 | #define KHUTILS_ASSERTION_INLINE 8 | 9 | #include "khutils/assertion.hpp" 10 | #include "khutils/runtime_exceptions.hpp" 11 | 12 | 13 | namespace glTF_2_0 14 | { 15 | using namespace glTF_common; 16 | 17 | ///----------------------------------------------------------------------- 18 | /// image data type check 19 | ///----------------------------------------------------------------------- 20 | 21 | inline bool isImageDataJPEG(const uint8_t* buffer, size_t len) 22 | { 23 | constexpr uint8_t magic[] = {0xff, 0xd8, 0xff}; 24 | constexpr size_t sizeOfMagic = sizeof(magic); 25 | 26 | if (len < sizeOfMagic) 27 | return false; 28 | 29 | return buffer[0] == magic[0] && // 30 | buffer[1] == magic[1] && // 31 | buffer[2] == magic[2]; 32 | } 33 | 34 | inline bool isImageDataJPEG(const std::vector& buffer) 35 | { 36 | return isImageDataJPEG(buffer.data(), buffer.size()); 37 | } 38 | 39 | //--- 40 | 41 | inline bool isImageDataPNG(const uint8_t* buffer, size_t len) 42 | { 43 | constexpr uint8_t magic[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a}; 44 | constexpr size_t sizeOfMagic = sizeof(magic); 45 | 46 | if (len < sizeOfMagic) 47 | return false; 48 | 49 | return buffer[0] == magic[0] && // 50 | buffer[1] == magic[1] && // 51 | buffer[2] == magic[2] && // 52 | buffer[3] == magic[3] && // 53 | buffer[4] == magic[4] && // 54 | buffer[5] == magic[5] && // 55 | buffer[6] == magic[6] && // 56 | buffer[7] == magic[7]; 57 | } 58 | 59 | inline bool isImageDataPNG(const std::vector& buffer) 60 | { 61 | return isImageDataPNG(buffer.data(), buffer.size()); 62 | } 63 | 64 | ///----------------------------------------------------------------------- 65 | /// cloning 66 | ///----------------------------------------------------------------------- 67 | 68 | Root_t cloneRoot(const Root_t& orig) 69 | { 70 | return from_flatbuffer_internal(to_flatbuffer(orig)); 71 | } 72 | 73 | //--- 74 | 75 | /// clone a document into a new one 76 | bool cloneDocument(const Document* const source, Document* const target) 77 | { 78 | KHUTILS_ASSERT_PTR(source); 79 | KHUTILS_ASSERT_PTR(target); 80 | 81 | auto newRoot = cloneRoot(source->root); 82 | target->root.swap(newRoot); 83 | target->bindata.clear(); 84 | std::copy(source->bindata.begin(), 85 | source->bindata.end(), 86 | std::insert_iteratorbindata)>(target->bindata, target->bindata.end())); 87 | 88 | return true; 89 | } 90 | 91 | //------------------------------------------------------------------------- 92 | 93 | Root_t embedDocumentImages(const Document* const _doc) 94 | { 95 | KHUTILS_ASSERT_PTR(_doc); 96 | auto doc = createDocumentPtr("copy"); 97 | KHUTILS_ASSERT_PTR(doc); 98 | auto copyOk = cloneDocument(_doc, doc.get()); 99 | KHUTILS_ASSERT(copyOk); 100 | 101 | for (auto& idp : doc->imgdata) 102 | { 103 | KHUTILS_ASSERT(isValidImageId(doc.get(), idp.first)); 104 | auto img = getImage(doc.get(), idp.first); 105 | KHUTILS_ASSERT_PTR(img); 106 | 107 | if (isImageDataPNG(idp.second)) 108 | img->uri = convertDataToUri(idp.second, "image/png"); 109 | else if (isImageDataJPEG(idp.second)) 110 | img->uri = convertDataToUri(idp.second, "image/jpeg"); 111 | else // actually not in spec 112 | img->uri = convertDataToUri(idp.second, "application/octet-stream"); 113 | } 114 | 115 | return std::move(doc->root); 116 | } 117 | 118 | //--- 119 | 120 | Root_t embedDocumentBuffersAndImages(const Document* const _doc) 121 | { 122 | KHUTILS_ASSERT_PTR(_doc); 123 | auto doc = createDocumentPtr("copy"); 124 | KHUTILS_ASSERT_PTR(doc); 125 | auto copyOk = cloneDocument(_doc, doc.get()); 126 | KHUTILS_ASSERT(copyOk); 127 | 128 | for (auto& bdp : doc->bindata) 129 | { 130 | KHUTILS_ASSERT(isValidBufferId(doc.get(), bdp.first)); 131 | auto buf = getBuffer(doc.get(), bdp.first); 132 | KHUTILS_ASSERT_PTR(buf); 133 | 134 | buf->uri = convertDataToUri(bdp.second, "application/octet-stream"); 135 | } 136 | 137 | for (auto& idp : doc->imgdata) 138 | { 139 | KHUTILS_ASSERT(isValidImageId(doc.get(), idp.first)); 140 | auto img = getImage(doc.get(), idp.first); 141 | KHUTILS_ASSERT_PTR(img); 142 | 143 | if (isImageDataPNG(idp.second)) 144 | img->uri = convertDataToUri(idp.second, "image/png"); 145 | else if (isImageDataJPEG(idp.second)) 146 | img->uri = convertDataToUri(idp.second, "image/jpeg"); 147 | else // actually not in spec 148 | img->uri = convertDataToUri(idp.second, "application/octet-stream"); 149 | } 150 | 151 | return std::move(doc->root); 152 | } 153 | 154 | //------------------------------------------------------------------------- 155 | /// prepares glTF data for GLB serialization 156 | //- changes URIs 157 | //- merges all mapped bindata into a single blob 158 | //- changes bufferViews accordingly 159 | 160 | std::tuple> binarizeDocumentBuffers(const Document* const _doc, bool withImages) 161 | { 162 | KHUTILS_ASSERT_PTR(_doc); 163 | auto doc = createDocumentPtr("copy"); 164 | KHUTILS_ASSERT_PTR(doc); 165 | auto copyOk = cloneDocument(_doc, doc.get()); 166 | KHUTILS_ASSERT(copyOk); 167 | 168 | if (withImages) 169 | { 170 | auto buf = createBuffer(doc.get(), "extra_imagedatabuffer"); 171 | for (auto& img : doc->root->images) 172 | { 173 | // check if already handled 174 | if (isValidBufferViewId(doc.get(), img->bufferView)) 175 | continue; 176 | 177 | // create new bufferviews for image 178 | auto imgdata = getImageData(doc.get(), img.get()); 179 | KHUTILS_ASSERT_CNTR_NOT_EMPTY(imgdata); 180 | 181 | auto view = createBufferView(imgdata, doc.get(), buf, (img->name + "_buffer").c_str()); 182 | KHUTILS_ASSERT_PTR(view); 183 | 184 | img->bufferView = getId(doc.get(), view); 185 | } 186 | } 187 | 188 | std::vector buffer; 189 | for (auto& view : doc->root->bufferViews) 190 | { 191 | auto viewdata = getBufferViewData(doc.get(), view.get()); 192 | 193 | view->buffer = 0; 194 | view->byteOffset = buffer.size(); 195 | buffer.reserve(buffer.size() + viewdata.size()); 196 | std::copy(viewdata.begin(), viewdata.end(), std::back_inserter(buffer)); 197 | } 198 | 199 | auto root = cloneRoot(doc->root); 200 | 201 | if(root->buffers.size() > 1) 202 | { 203 | root->buffers.resize(1); 204 | } 205 | 206 | if(root->buffers.size() >= 1) 207 | { 208 | root->buffers[0]->uri = "bindata"; 209 | root->buffers[0]->byteLength = buffer.size(); 210 | } 211 | 212 | return std::tuple>{std::move(root), std::move(buffer)}; 213 | } 214 | 215 | //--- 216 | 217 | std::tuple> binarizeDocumentBuffers_embedImages(const Document* const _doc, bool withImages) 218 | { 219 | KHUTILS_ASSERT_PTR(_doc); 220 | auto doc = createDocumentPtr("copy"); 221 | KHUTILS_ASSERT_PTR(doc); 222 | auto copyOk = cloneDocument(_doc, doc.get()); 223 | KHUTILS_ASSERT(copyOk); 224 | 225 | if (withImages) 226 | { 227 | for (auto& idp : doc->imgdata) 228 | { 229 | KHUTILS_ASSERT(isValidImageId(doc.get(), idp.first)); 230 | auto img = getImage(doc.get(), idp.first); 231 | KHUTILS_ASSERT_PTR(img); 232 | 233 | if (isImageDataPNG(idp.second)) 234 | img->uri = convertDataToUri(idp.second, "image/png"); 235 | else if (isImageDataJPEG(idp.second)) 236 | img->uri = convertDataToUri(idp.second, "image/jpeg"); 237 | else // actually not in spec 238 | img->uri = convertDataToUri(idp.second, "application/octet-stream"); 239 | } 240 | } 241 | 242 | std::vector buffer; 243 | for (auto& view : doc->root->bufferViews) 244 | { 245 | auto viewdata = getBufferViewData(doc.get(), view.get()); 246 | 247 | view->buffer = 0; 248 | view->byteOffset = buffer.size(); 249 | buffer.reserve(buffer.size() + viewdata.size()); 250 | std::copy(viewdata.begin(), viewdata.end(), std::back_inserter(buffer)); 251 | } 252 | 253 | auto root = cloneRoot(doc->root); 254 | if (root->buffers.size() > 1) 255 | { 256 | std::remove_if(root->buffers.begin() + 1, root->buffers.end(), [](auto&) { return true; }); 257 | } 258 | root->buffers[0]->uri = ""; 259 | root->buffers[0]->byteLength = buffer.size(); 260 | 261 | return std::tuple>{std::move(root), std::move(buffer)}; 262 | } 263 | 264 | 265 | //------------------------------------------------------------------------- 266 | 267 | bool binarizeDocument(Document* const doc) 268 | { 269 | KHUTILS_ASSERT_PTR(doc); 270 | 271 | Root_t root; 272 | std::vector bindata; 273 | std::tie(root, bindata) = binarizeDocumentBuffers(doc, false); 274 | KHUTILS_ASSERT_PTR(root); 275 | 276 | doc->root.swap(root); 277 | doc->bindata.clear(); 278 | doc->bindata[0].assign(bindata.begin(), bindata.end()); 279 | 280 | return true; 281 | } 282 | 283 | //--- 284 | 285 | /// prepares glTF data for glTF-embedded format 286 | //- changes URIs to contain base64 encoded buffers 287 | //- changes bufferViews accordingly 288 | bool embedDocument(Document* const doc) 289 | { 290 | KHUTILS_ASSERT_PTR(doc); 291 | auto root = embedDocumentBuffersAndImages(doc); 292 | KHUTILS_ASSERT_PTR(root); 293 | 294 | doc->root.swap(root); 295 | doc->bindata.clear(); 296 | doc->imgdata.clear(); 297 | 298 | return true; 299 | } 300 | 301 | //--- 302 | 303 | /// prepares glTF data for glTF-multi format 304 | //- changes URIs to reference external buffers 305 | //- changes bufferViews accordingly 306 | bool dislodgeDocument(Document* const doc) 307 | { 308 | KHUTILS_ASSERT_PTR(doc); 309 | 310 | size_t bindataCounter = 0; 311 | 312 | for (auto& bdp : doc->bindata) 313 | { 314 | KHUTILS_ASSERT(isValidBufferId(doc, bdp.first)); 315 | auto buf = getBuffer(doc, bdp.first); 316 | KHUTILS_ASSERT_PTR(buf); 317 | if (isDataUri(buf->uri)) 318 | { 319 | buf->uri = std::string("bindata") + std::to_string(bindataCounter++) + ".bin"; 320 | } 321 | } 322 | 323 | size_t imgdataCounter = 0; 324 | for (auto& idp : doc->imgdata) 325 | { 326 | KHUTILS_ASSERT(isValidImageId(doc, idp.first)); 327 | auto img = getImage(doc, idp.first); 328 | KHUTILS_ASSERT_PTR(img); 329 | if (isDataUri(img->uri)) 330 | { 331 | img->uri = std::string("imgdata") + std::to_string(imgdataCounter++); 332 | 333 | if (isImageDataPNG(idp.second)) 334 | img->uri += ".png"; 335 | else if (isImageDataJPEG(idp.second)) 336 | img->uri += ".jpeg"; 337 | else // actually not in spec 338 | img->uri += ".bin"; 339 | } 340 | } 341 | 342 | return true; 343 | } 344 | 345 | //--- 346 | 347 | /// prepares glTF data for glTF-multi format 348 | //- splits buffers into multiple buffers according to bufferViews 349 | //- changes bufferViews accordingly 350 | bool splitDocument(Document* const doc) 351 | { 352 | KHUTILS_ASSERT_PTR(doc); 353 | 354 | 355 | return false; 356 | } 357 | 358 | //--- 359 | 360 | //------------------------------------------------------------------------- 361 | //------------------------------------------------------------------------- 362 | 363 | } // namespace glTF_2_0 364 | -------------------------------------------------------------------------------- /src/2.0/glTFio.cpp: -------------------------------------------------------------------------------- 1 | #include "flatgltf/2.0/glTFio.hpp" 2 | #include "flatgltf/2.0/glTF_generated.h" 3 | #include "flatgltf/2.0/glTFapi.hpp" 4 | #include "flatgltf/common/glTFutils.hpp" 5 | #include "glTFinternal.hpp" 6 | 7 | #include "flatbuffers/flatbuffers.h" 8 | #include "flatbuffers/idl.h" 9 | 10 | #define KHUTILS_ASSERTION_INLINE 11 | #include "khutils/assertion.hpp" 12 | #include "khutils/file.hpp" 13 | #include "khutils/logging.hpp" 14 | #include "khutils/runtime_exceptions.hpp" 15 | 16 | /// terribly sorry for this dependency. will remove asap 17 | #include 18 | 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | // flatbuffer schema string for parser 27 | extern const std::string flatgltf_2_0_schema; 28 | 29 | 30 | namespace glTF_2_0 31 | { 32 | using namespace glTF_common; 33 | namespace fs = boost::filesystem; 34 | 35 | ///------------------------------------------------------------------------ 36 | /// load document as-is, without modification or external data 37 | ///------------------------------------------------------------------------ 38 | 39 | inline void initializeDocument(Document* const doc, Root_t&& root) 40 | { 41 | KHUTILS_ASSERT_PTR(doc); 42 | KHUTILS_ASSERT_PTR(root); 43 | 44 | doc->root.swap(root); 45 | doc->bindata.clear(); 46 | doc->imgdata.clear(); 47 | } 48 | 49 | //--- 50 | 51 | // load document as-is, without modification or external data 52 | bool loadDocument_json(Document* const doc, const char* location, data_reader_t reader) 53 | { 54 | KHUTILS_ASSERT_PTR(doc); 55 | 56 | std::vector buffer; 57 | auto readOk = reader(location, buffer); 58 | 59 | if (readOk) 60 | { 61 | KHUTILS_ASSERT_CNTR_NOT_EMPTY(buffer); 62 | std::string jsonString(reinterpret_cast(buffer.data()), buffer.size()); 63 | initializeDocument(doc, from_json_internal(jsonString)); 64 | return true; 65 | } 66 | 67 | return false; 68 | } 69 | 70 | //--- 71 | 72 | bool loadDocument_glf(Document* const doc, const char* location, data_reader_t reader) 73 | { 74 | std::vector buffer; 75 | auto readOk = reader(location, buffer); 76 | 77 | if (readOk) 78 | { 79 | KHUTILS_ASSERT_CNTR_NOT_EMPTY(buffer); 80 | initializeDocument(doc, from_flatbuffer_internal(buffer)); 81 | return true; 82 | } 83 | 84 | return false; 85 | } 86 | 87 | //--- 88 | 89 | // loadDocument_glb implemented further below 90 | 91 | //------------------------------------------------------------------------- 92 | 93 | ///------------------------------------------------------------------------ 94 | /// load document: transform data uris to buffers, load external resources 95 | ///------------------------------------------------------------------------ 96 | 97 | bool loadDocument_dataAndReferences(Document* const doc, const char* location, data_reader_t reader) 98 | { 99 | KHUTILS_ASSERT_PTR(doc); 100 | 101 | bool allBuffersLoadOk = std::all_of(doc->root->buffers.begin(), 102 | doc->root->buffers.end(), 103 | [&, opath = fs::path(location).parent_path()](auto& buf) { 104 | if (isDataUri(buf->uri)) 105 | { 106 | setBufferData(convertUriToData(buf->uri), doc, buf.get()); 107 | return true; 108 | } 109 | 110 | std::vectorbufData; 111 | if (reader((opath / buf->uri).c_str(), bufData)) 112 | { 113 | setBufferData(bufData, doc, buf.get()); 114 | return true; 115 | } 116 | return false; 117 | }); 118 | 119 | 120 | bool allImagesLoadOk = std::all_of(doc->root->images.begin(), 121 | doc->root->images.end(), 122 | [&, opath = fs::path(location).parent_path()](auto& img) { 123 | if (isValidBufferViewId(doc, img->bufferView)) 124 | return true; 125 | 126 | if (isDataUri(img->uri)) 127 | { 128 | setImageData(convertUriToData(img->uri), doc, img.get()); 129 | return true; 130 | } 131 | 132 | std::vectorbufData; 133 | if (reader((opath / img->uri).c_str(), bufData)) 134 | { 135 | setImageData(bufData, doc, img.get()); 136 | return true; 137 | } 138 | return false; 139 | }); 140 | 141 | return allBuffersLoadOk && allImagesLoadOk; 142 | } 143 | 144 | //--- 145 | 146 | // load document: transform data uris to buffers, load external resources 147 | bool loadDocument_json_plus(Document* const doc, const char* location, data_reader_t reader) 148 | { 149 | return loadDocument_json(doc, location, reader) && // 150 | loadDocument_dataAndReferences(doc, location, reader); 151 | } 152 | 153 | //--- 154 | 155 | bool loadDocument_glf_plus(Document* const doc, const char* location, data_reader_t reader) 156 | { 157 | return loadDocument_glf(doc, location, reader) && // 158 | loadDocument_dataAndReferences(doc, location, reader); 159 | } 160 | 161 | //--- 162 | 163 | // load document from glTF-binary: all buffers merged into 1, with external data 164 | bool loadDocument_glb_plus(Document* const doc, const char* location, data_reader_t reader) 165 | { 166 | return loadDocument_glb(doc, location, reader) && // 167 | loadDocument_dataAndReferences(doc, location, reader); 168 | } 169 | 170 | //------------------------------------------------------------------------- 171 | 172 | 173 | ///------------------------------------------------------------------------ 174 | /// write document as-is, without modification or external data 175 | ///------------------------------------------------------------------------ 176 | 177 | bool saveDocument_json(const Document* const doc, const char* location, data_writer_t writer) 178 | { 179 | KHUTILS_ASSERT_PTR(doc); 180 | 181 | auto jsonString = to_json(doc->root); 182 | KHUTILS_ASSERT_CNTR_NOT_EMPTY(jsonString); 183 | 184 | std::vector buffer; 185 | buffer.reserve(jsonString.size()); 186 | std::transform(jsonString.begin(), jsonString.end(), std::back_inserter(buffer), [](auto& c) { 187 | return static_cast(c); 188 | }); 189 | 190 | return writer(location, buffer); 191 | } 192 | 193 | //--- 194 | 195 | bool saveDocument_glf(const Document* const doc, const char* location, data_writer_t writer) 196 | { 197 | KHUTILS_ASSERT_PTR(doc); 198 | 199 | auto buffer = to_flatbuffer(doc->root); 200 | KHUTILS_ASSERT_CNTR_NOT_EMPTY(buffer); 201 | 202 | return writer(location, buffer); 203 | } 204 | 205 | //--- 206 | 207 | // bool saveDocument_glb implemented further below 208 | 209 | ///------------------------------------------------------------------------ 210 | /// write document as-is, write buffers and images set to external URIs 211 | ///------------------------------------------------------------------------ 212 | 213 | bool saveDocument_buffers(const Document* const doc, const char* location, data_writer_t writer) 214 | { 215 | KHUTILS_ASSERT_PTR(doc); 216 | 217 | return std::all_of(doc->root->buffers.begin(), 218 | doc->root->buffers.end(), 219 | [&, opath = fs::path(location).parent_path()](auto& buf) { 220 | if (isDataUri(buf->uri)) 221 | return true; 222 | 223 | return writer((opath / buf->uri).c_str(), getBufferData(doc, buf.get())); 224 | }); 225 | } 226 | 227 | //--- 228 | 229 | bool saveDocument_images(const Document* const doc, const char* location, data_writer_t writer) 230 | { 231 | KHUTILS_ASSERT_PTR(doc); 232 | 233 | return std::all_of(doc->root->images.begin(), 234 | doc->root->images.end(), 235 | [&, opath = fs::path(location).parent_path()](auto& img) { 236 | if (isDataUri(img->uri)) 237 | return true; 238 | 239 | if (isValidBufferViewId(doc, img->bufferView)) 240 | return true; 241 | 242 | return writer((opath / img->uri).c_str(), getImageData(doc, img.get())); 243 | }); 244 | } 245 | 246 | //--- 247 | 248 | bool saveDocument_dataAndReferences(const Document* const doc, const char* location, data_writer_t writer) 249 | { 250 | KHUTILS_ASSERT_PTR(doc); 251 | return saveDocument_buffers(doc, location, writer) && saveDocument_images(doc, location, writer); 252 | } 253 | 254 | //--- 255 | 256 | bool saveDocument_json_plus(const Document* const doc, const char* location, data_writer_t writer) 257 | { 258 | return saveDocument_json(doc, location, writer) && // 259 | saveDocument_dataAndReferences(doc, location, writer); 260 | } 261 | 262 | //--- 263 | 264 | bool saveDocument_glf_plus(const Document* const doc, const char* location, data_writer_t writer) 265 | { 266 | return saveDocument_glf(doc, location, writer) && // 267 | saveDocument_dataAndReferences(doc, location, writer); 268 | } 269 | 270 | //--- 271 | 272 | bool saveDocument_glb_plus(const Document* const doc, const char* location, data_writer_t writer) 273 | { 274 | return saveDocument_glb(doc, location, writer) && // 275 | saveDocument_images(doc, location, writer); // only external images, buffer is already written 276 | } 277 | 278 | //------------------------------------------------------------------------- 279 | 280 | 281 | ///------------------------------------------------------------------------ 282 | /// write document: ALL buffers/images will be embedded base64 URIs 283 | ///------------------------------------------------------------------------ 284 | 285 | bool saveDocument_json_embed(const Document* const doc, const char* location, data_writer_t writer) 286 | { 287 | KHUTILS_ASSERT_PTR(doc); 288 | 289 | auto embeddedRoot = embedDocumentBuffersAndImages(doc); 290 | KHUTILS_ASSERT_PTR(embeddedRoot); 291 | 292 | auto jsonString = to_json(embeddedRoot); 293 | KHUTILS_ASSERT_CNTR_NOT_EMPTY(jsonString); 294 | 295 | std::vector buffer; 296 | buffer.reserve(jsonString.size()); 297 | std::transform(jsonString.begin(), jsonString.end(), std::back_inserter(buffer), [](auto& c) { 298 | return static_cast(c); 299 | }); 300 | 301 | return writer(location, buffer); 302 | } 303 | 304 | //--- 305 | 306 | bool saveDocument_glf_embed(const Document* const doc, const char* location, data_writer_t writer) 307 | { 308 | KHUTILS_ASSERT_PTR(doc); 309 | 310 | auto embeddedRoot = embedDocumentBuffersAndImages(doc); 311 | KHUTILS_ASSERT_PTR(embeddedRoot); 312 | 313 | auto buffer = to_flatbuffer(embeddedRoot); 314 | KHUTILS_ASSERT_CNTR_NOT_EMPTY(buffer); 315 | 316 | return writer(location, buffer); 317 | } 318 | 319 | //------------------------------------------------------------------------- 320 | 321 | 322 | ///------------------------------------------------------------------------ 323 | /// write document: ALL buffers/images will be external files named after URI or name or counter 324 | ///------------------------------------------------------------------------ 325 | 326 | bool saveDocument_json_external(const Document* const _doc, const char* location, data_writer_t writer) 327 | { 328 | KHUTILS_ASSERT_PTR(_doc); 329 | auto doc = createDocument("copy"); 330 | 331 | return cloneDocument(_doc, doc) && // 332 | dislodgeDocument(doc) && // 333 | saveDocument_json_plus(doc, location, writer); 334 | } 335 | 336 | //--- 337 | 338 | bool saveDocument_glf_external(const Document* const _doc, const char* location, data_writer_t writer) 339 | { 340 | KHUTILS_ASSERT_PTR(_doc); 341 | auto doc = createDocument("copy"); 342 | 343 | return cloneDocument(_doc, doc) && // 344 | dislodgeDocument(doc) && // 345 | saveDocument_glf_plus(doc, location, writer); 346 | } 347 | 348 | //------------------------------------------------------------------------- 349 | 350 | ///------------------------------------------------------------------------ 351 | /// ease-of-use load/save functions, to file by default 352 | ///------------------------------------------------------------------------ 353 | 354 | bool loadDocument(Document* const doc, const char* uri) 355 | { 356 | KHUTILS_ASSERT_PTR(doc); 357 | 358 | auto p = fs::path(uri); 359 | if (p.extension() == ".glb") 360 | { 361 | return loadDocument_glb_plus(doc, uri); 362 | } 363 | 364 | if (p.extension() == ".glf") 365 | { 366 | return loadDocument_glf_plus(doc, uri); 367 | } 368 | 369 | if (p.extension() == ".gltf") 370 | { 371 | return loadDocument_json_plus(doc, uri); 372 | } 373 | 374 | return false; 375 | } 376 | 377 | //--- 378 | 379 | bool saveDocument(const Document* const doc, const char* uri) 380 | { 381 | KHUTILS_ASSERT_PTR(doc); 382 | 383 | auto p = fs::path(uri); 384 | if (p.extension() == ".glb") 385 | { 386 | return saveDocument_glb_plus(doc, uri); 387 | } 388 | 389 | if (p.extension() == ".glf") 390 | { 391 | return saveDocument_glf_plus(doc, uri); 392 | } 393 | 394 | if (p.extension() == ".gltf") 395 | { 396 | return saveDocument_json_plus(doc, uri); 397 | } 398 | 399 | return false; 400 | } 401 | 402 | //------------------------------------------------------------------------- 403 | //------------------------------------------------------------------------- 404 | 405 | } // namespace glTF_2_0 406 | -------------------------------------------------------------------------------- /include/flatgltf/2.0/glTFapi.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FLATGLTF_2_0_API_H_INC 2 | #define FLATGLTF_2_0_API_H_INC 3 | 4 | #include "flatgltf/2.0/glTF_generated.h" 5 | #include "flatgltf/2.0/glTFmath_types.hpp" 6 | #include "flatgltf/common/glTFapiexports.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace glTF_2_0 16 | { 17 | ///----------------------------------------------------------------------- 18 | /// id type alias 19 | ///----------------------------------------------------------------------- 20 | 21 | using glTFid_t = int32_t; 22 | 23 | ///----------------------------------------------------------------------- 24 | /// document structure (PIMPL) 25 | ///----------------------------------------------------------------------- 26 | 27 | struct Document; 28 | 29 | 30 | Document* const createDocument(const char* name); 31 | void destroyDocument(Document* const); 32 | bool verifyDocument(const Document* const); 33 | 34 | ///----------------------------------------------------------------------- 35 | /// instantiation 36 | ///----------------------------------------------------------------------- 37 | // all functions return a const-pointer to mutable-object 38 | // nullptr if error 39 | // else, returned object is only set to internal default values 40 | 41 | AccessorT* const createAccessor(Document* const, const char* name = nullptr); 42 | AnimationT* const createAnimation(Document* const, const char* name = nullptr); 43 | BufferT* const createBuffer(Document* const, const char* name = nullptr); 44 | BufferViewT* const createBufferView(Document* const, const char* name = nullptr); 45 | CameraT* const createCamera(Document* const, const char* name = nullptr); 46 | ImageT* const createImage(Document* const, const char* name = nullptr); 47 | MaterialT* const createMaterial(Document* const, const char* name = nullptr); 48 | MeshT* const createMesh(Document* const, const char* name = nullptr); 49 | NodeT* const createNode(Document* const, const char* name = nullptr); 50 | SceneT* const createScene(Document* const, const char* name = nullptr); 51 | SamplerT* const createSampler(Document* const, const char* name = nullptr); 52 | TextureT* const createTexture(Document* const, const char* name = nullptr); 53 | SkinT* const createSkin(Document* const, const char* name = nullptr); 54 | 55 | MeshPrimitiveT* const createMeshPrimitive(MeshT* const); 56 | 57 | ///----------------------------------------------------------------------- 58 | /// extended instantiation 59 | ///----------------------------------------------------------------------- 60 | // all functions return a const-pointer to mutable-object 61 | // nullptr if error 62 | 63 | //! create buffer 64 | // if uri is a data uri (beginning see below), buffer will be created for embedded base64 storage 65 | // else, uri is used as _base_ to create a unique buffer data buffer 66 | constexpr char dataUri[] = "data:"; 67 | BufferT* const createBuffer(Document* const, const char* uri, const char* name); 68 | 69 | //! create buffer view 70 | // data will be appended to buffer 71 | // buffer must exist 72 | BufferViewT* const createBufferView(const uint8_t* const data, // 73 | size_t length, 74 | Document* const doc, 75 | BufferT* const buf, 76 | const char* name = nullptr); 77 | BufferViewT* const createBufferView(const std::vector& data, Document* const doc, BufferT* const buf, const char* name = nullptr); 78 | 79 | //! create accessor on data in buffer 80 | // data will be appended to buffer 81 | // corresponding buffer view will be created as well 82 | AccessorT* const createAccessor(const uint8_t* const data, size_t length, Document* const, BufferT* const, const char* name = nullptr); 83 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferT* const, const char* name = nullptr); 84 | 85 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferT* const, const char* name = nullptr); 86 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferT* const, const char* name = nullptr); 87 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferT* const, const char* name = nullptr); 88 | 89 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferT* const, const char* name = nullptr); 90 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferT* const, const char* name = nullptr); 91 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferT* const, const char* name = nullptr); 92 | 93 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferT* const, const char* name = nullptr); 94 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferT* const, const char* name = nullptr); 95 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferT* const, const char* name = nullptr); 96 | 97 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferT* const, const char* name = nullptr); 98 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferT* const, const char* name = nullptr); 99 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferT* const, const char* name = nullptr); 100 | 101 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferT* const, const char* name = nullptr); 102 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferT* const, const char* name = nullptr); 103 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferT* const, const char* name = nullptr); 104 | 105 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferT* const, const char* name = nullptr); 106 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferT* const, const char* name = nullptr); 107 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferT* const, const char* name = nullptr); 108 | 109 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferT* const, const char* name = nullptr); 110 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferT* const, const char* name = nullptr); 111 | 112 | //! create accessor on data in bufferView 113 | // data will be appended to bufferView->buffer 114 | // bufferView will be updated accordingly 115 | AccessorT* const createAccessor(const uint8_t* const data, size_t length, Document* const, BufferViewT* const, const char* name = nullptr); 116 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferViewT* const, const char* name = nullptr); 117 | 118 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferViewT* const, const char* name = nullptr); 119 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferViewT* const, const char* name = nullptr); 120 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferViewT* const, const char* name = nullptr); 121 | 122 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferViewT* const, const char* name = nullptr); 123 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferViewT* const, const char* name = nullptr); 124 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferViewT* const, const char* name = nullptr); 125 | 126 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferViewT* const, const char* name = nullptr); 127 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferViewT* const, const char* name = nullptr); 128 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferViewT* const, const char* name = nullptr); 129 | 130 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferViewT* const, const char* name = nullptr); 131 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferViewT* const, const char* name = nullptr); 132 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferViewT* const, const char* name = nullptr); 133 | 134 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferViewT* const, const char* name = nullptr); 135 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferViewT* const, const char* name = nullptr); 136 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferViewT* const, const char* name = nullptr); 137 | 138 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferViewT* const, const char* name = nullptr); 139 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferViewT* const, const char* name = nullptr); 140 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferViewT* const, const char* name = nullptr); 141 | 142 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferViewT* const, const char* name = nullptr); 143 | AccessorT* const createAccessor(const std::vector& data, Document* const, BufferViewT* const, const char* name = nullptr); 144 | 145 | 146 | //! create a sampler with Good Defaults settings 147 | SamplerT* const createSampler_withDefaults(Document* const); 148 | 149 | //! create a texture to access given image 150 | // creates sampler for access 151 | TextureT* const createTexture(Document* const, ImageT* const); 152 | 153 | //! create an image referring to a buffer view for data 154 | ImageT* const createImage(Document* const, BufferViewT* const, const char* mimeType, const char* name = nullptr); 155 | 156 | //! create an image referring to a uri 157 | ImageT* const createImage(Document* const, const char* uri, const char* name); 158 | 159 | //! create an image referring to an external file uri, data kept in imgdata 160 | ImageT* const createImage(const std::vector& data, Document* const, const char* uri, const char* name = nullptr); 161 | ImageT* const createImage(const uint8_t* const, size_t length, Document* const, const char* uri, const char* name = nullptr); 162 | 163 | //! create meshprimitive 164 | // create meshprimitives and add them to given mesh 165 | MeshPrimitiveT* const createMeshPrimitive(Document* const, MeshT* const, AccessorT* const, MaterialT* const = nullptr); 166 | 167 | //! create camera node 168 | NodeT* const createNode(Document* const, CameraT* const); 169 | 170 | //! create mesh node 171 | NodeT* const createNode(Document* const, MeshT* const); 172 | 173 | //! create skin node 174 | NodeT* const createNode(Document* const, SkinT* const); 175 | 176 | 177 | ///----------------------------------------------------------------------- 178 | /// buffer data getter setter 179 | ///----------------------------------------------------------------------- 180 | 181 | // returns COPY of buffer data if buffer exists, empty vector elese 182 | std::vector getBufferData(const Document* const, const BufferT* const); 183 | 184 | // sets buffer data, overwriting existing 185 | // returns new size 186 | // returns 0 if: buffer does not exist 187 | size_t setBufferData(const std::vector&, Document* const, BufferT* const); 188 | size_t setBufferData(const uint8_t* const, size_t length, Document* const, BufferT* const); 189 | 190 | // appends buffer data to existing buffer data 191 | // returns new size 192 | // returns 0 if: buffer does not exist 193 | size_t appendBufferData(const std::vector&, Document* const, BufferT* const); 194 | size_t appendBufferData(const uint8_t* const, size_t length, Document* const, BufferT* const); 195 | 196 | // appends data to buffer 197 | // if data uri, decode base64, add data, encode base64 will happen 198 | size_t setBufferViewData(const std::vector&, Document* const, BufferViewT* const); 199 | size_t setBufferViewData(const uint8_t* const, size_t length, Document* const, BufferViewT* const); 200 | 201 | // returns COPY of data pointed to by bufferView 202 | std::vector getBufferViewData(const Document* const, const BufferViewT* const); 203 | 204 | ///----------------------------------------------------------------------- 205 | /// image data getter setter 206 | ///----------------------------------------------------------------------- 207 | 208 | // returns COPY of image data if image exists, empty vector else 209 | std::vector getImageData(const Document* const, const ImageT* const); 210 | 211 | // sets image data, overwriting existing 212 | // returns new size 213 | // returns 0 if: buffer does not exist 214 | size_t setImageData(const std::vector&, Document* const, ImageT* const); 215 | size_t setImageData(const uint8_t* const, size_t length, Document* const, ImageT* const); 216 | 217 | // appends image data to existing image data 218 | // returns new size 219 | // returns 0 if: image does not exis 220 | size_t appendImageData(const std::vector&, Document* const, ImageT* const); 221 | size_t appendImageData(const uint8_t* const, size_t length, Document* const, ImageT* const); 222 | 223 | ///----------------------------------------------------------------------- 224 | /// simple get for unique elements 225 | ///----------------------------------------------------------------------- 226 | // all functions return a const-pointer to mutable-object 227 | // nullptr if not found 228 | 229 | AssetT* const getAsset(const Document* const); // has no name 230 | SceneT* const getMainScene(const Document* const); // return main scene if set, nullptr else 231 | void setMainScene(Document* const, SceneT* const); // set main scene 232 | void setMainScene(Document* const, glTFid_t); // set main scene 233 | 234 | ///----------------------------------------------------------------------- 235 | /// get-by-id 236 | ///----------------------------------------------------------------------- 237 | // all functions return a const-pointer to mutable-object 238 | // nullptr if not found 239 | AccessorT* const getAccessor(const Document* const, glTFid_t); 240 | AnimationT* const getAnimation(const Document* const, glTFid_t); 241 | BufferT* const getBuffer(const Document* const, glTFid_t); 242 | BufferViewT* const getBufferView(const Document* const, glTFid_t); 243 | CameraT* const getCamera(const Document* const, glTFid_t); 244 | ImageT* const getImage(const Document* const, glTFid_t); 245 | MaterialT* const getMaterial(const Document* const, glTFid_t); 246 | MeshT* const getMesh(const Document* const, glTFid_t); 247 | NodeT* const getNode(const Document* const, glTFid_t); 248 | SceneT* const getScene(const Document* const, glTFid_t); 249 | SamplerT* const getSampler(const Document* const, glTFid_t); 250 | TextureT* const getTexture(const Document* const, glTFid_t); 251 | SkinT* const getSkin(const Document* const, glTFid_t); 252 | 253 | MeshPrimitiveT* const getMeshPrimitive(const MeshT* const, glTFid_t); 254 | 255 | ///----------------------------------------------------------------------- 256 | /// get-by-name 257 | /// note that names are optional and this might not work 258 | ///----------------------------------------------------------------------- 259 | // all functions return a const-pointer to mutable-object 260 | // nullptr if not found 261 | // CAVEAT: names are optional, so this might not work 262 | AccessorT* const getAccessor(const Document* const, const char* name); 263 | AnimationT* const getAnimation(const Document* const, const char* name); 264 | BufferT* const getBuffer(const Document* const, const char* name); 265 | BufferViewT* const getBufferView(const Document* const, const char* name); 266 | CameraT* const getCamera(const Document* const, const char* name); 267 | ImageT* const getImage(const Document* const, const char* name); 268 | MaterialT* const getMaterial(const Document* const, const char* name); 269 | MeshT* const getMesh(const Document* const, const char* name); 270 | NodeT* const getNode(const Document* const, const char* name); 271 | SceneT* const getScene(const Document* const, const char* name); 272 | SamplerT* const getSampler(const Document* const, const char* name); 273 | TextureT* const getTexture(const Document* const, const char* name); 274 | SkinT* const getSkin(const Document* const, const char* name); 275 | 276 | ///----------------------------------------------------------------------- 277 | /// get-by-name-or-create-new 278 | /// note that names are optional and this might not work as expected 279 | ///----------------------------------------------------------------------- 280 | // all functions return a const-pointer to mutable-object 281 | // either the existing object or a new one 282 | AccessorT* const getOrCreateAccessor(Document* const, const char* name); 283 | AnimationT* const getOrCreateAnimation(Document* const, const char* name); 284 | BufferT* const getOrCreateBuffer(Document* const, const char* name); 285 | BufferViewT* const getOrCreateBufferView(Document* const, const char* name); 286 | CameraT* const getOrCreateCamera(Document* const, const char* name); 287 | ImageT* const getOrCreateImage(Document* const, const char* name); 288 | MaterialT* const getOrCreateMaterial(Document* const, const char* name); 289 | MeshT* const getOrCreateMesh(Document* const, const char* name); 290 | NodeT* const getOrCreateNode(Document* const, const char* name); 291 | SceneT* const getOrCreateScene(Document* const, const char* name); 292 | SamplerT* const getOrCreateSampler(Document* const, const char* name); 293 | TextureT* const getOrCreateTexture(Document* const, const char* name); 294 | SkinT* const getOrCreateSkin(Document* const, const char* name); 295 | 296 | BufferT* const getOrCreateBuffer(Document* const, const char* uri, const char* name); 297 | ImageT* const getOrCreateImage(Document* const, const char* uri, const char* name); 298 | 299 | ///----------------------------------------------------------------------- 300 | /// get-id 301 | ///----------------------------------------------------------------------- 302 | // all functions return a glTFid_t >= 0 if found, < 0 if not 303 | glTFid_t getId(const Document* const, const AccessorT* const); 304 | glTFid_t getId(const Document* const, const AnimationT* const); 305 | glTFid_t getId(const Document* const, const BufferT* const); 306 | glTFid_t getId(const Document* const, const BufferViewT* const); 307 | glTFid_t getId(const Document* const, const CameraT* const); 308 | glTFid_t getId(const Document* const, const ImageT* const); 309 | glTFid_t getId(const Document* const, const MaterialT* const); 310 | glTFid_t getId(const Document* const, const MeshT* const); 311 | glTFid_t getId(const Document* const, const NodeT* const); 312 | glTFid_t getId(const Document* const, const SceneT* const); 313 | glTFid_t getId(const Document* const, const SamplerT* const); 314 | glTFid_t getId(const Document* const, const TextureT* const); 315 | glTFid_t getId(const Document* const, const SkinT* const); 316 | 317 | 318 | ///----------------------------------------------------------------------- 319 | /// validate-id 320 | ///----------------------------------------------------------------------- 321 | // all functions return true if id is valid for this document and type 322 | bool isValidAccessorId(const Document* const, glTFid_t); 323 | bool isValidAnimationId(const Document* const, glTFid_t); 324 | bool isValidBufferId(const Document* const, glTFid_t); 325 | bool isValidBufferViewId(const Document* const, glTFid_t); 326 | bool isValidCameraId(const Document* const, glTFid_t); 327 | bool isValidImageId(const Document* const, glTFid_t); 328 | bool isValidMaterialId(const Document* const, glTFid_t); 329 | bool isValidMeshId(const Document* const, glTFid_t); 330 | bool isValidNodeId(const Document* const, glTFid_t); 331 | bool isValidSceneId(const Document* const, glTFid_t); 332 | bool isValidSamplerId(const Document* const, glTFid_t); 333 | bool isValidTextureId(const Document* const, glTFid_t); 334 | bool isValidSkinId(const Document* const, glTFid_t); 335 | 336 | 337 | ///----------------------------------------------------------------------- 338 | /// query 339 | /// returns vector of elements that matched query 340 | ///----------------------------------------------------------------------- 341 | typedef bool (*AccessorQuery_t)(const Document* const, AccessorT* const); 342 | std::vector queryAccessors(const Document* const, AccessorQuery_t); 343 | 344 | typedef bool (*AnimationQuery_t)(const Document* const, AnimationT* const); 345 | std::vector queryAnimations(const Document* const, AnimationQuery_t); 346 | 347 | typedef bool (*BufferQuery_t)(const Document* const, BufferT* const); 348 | std::vector queryBuffers(const Document* const, BufferQuery_t); 349 | 350 | typedef bool (*BufferViewQuery_t)(const Document* const, BufferViewT* const); 351 | std::vector queryBufferViews(const Document* const, BufferViewQuery_t); 352 | 353 | typedef bool (*CameraQuery_t)(const Document* const, CameraT* const); 354 | std::vector queryCameras(const Document* const, CameraQuery_t); 355 | 356 | typedef bool (*ImageQuery_t)(const Document* const, ImageT* const); 357 | std::vector queryImages(const Document* const, ImageQuery_t); 358 | 359 | typedef bool (*MaterialQuery_t)(const Document* const, MaterialT* const); 360 | std::vector queryMaterials(const Document* const, MaterialQuery_t); 361 | 362 | typedef bool (*MeshQuery_t)(const Document* const, MeshT* const); 363 | std::vector queryMeshs(const Document* const, MeshQuery_t); 364 | 365 | typedef bool (*NodeQuery_t)(const Document* const, NodeT* const); 366 | std::vector queryNodes(const Document* const, NodeQuery_t); 367 | 368 | typedef bool (*SceneQuery_t)(const Document* const, SceneT* const); 369 | std::vector queryScenes(const Document* const, SceneQuery_t); 370 | 371 | typedef bool (*SamplerQuery_t)(const Document* const, SamplerT* const); 372 | std::vector querySamplers(const Document* const, SamplerQuery_t); 373 | 374 | typedef bool (*TextureQuery_t)(const Document* const, TextureT* const); 375 | std::vector queryTextures(const Document* const, TextureQuery_t); 376 | 377 | typedef bool (*SkinQuery_t)(const Document* const, SkinT* const); 378 | std::vector querySkins(const Document* const, SkinQuery_t); 379 | 380 | ///----------------------------------------------------------------------- 381 | /// set-transform for nodes 382 | ///----------------------------------------------------------------------- 383 | //! CAVEAT: matrix and RST are MUTUALLY exclusive 384 | //! to reflect this property, the other one WILL GET RESET when called 385 | void setNodeMatrix(NodeT* const, const mat4_t&); 386 | void setNodeRotation(NodeT* const, const quat_t&); 387 | void setNodeScale(NodeT* const, const vec3_t&); 388 | void setNodeTranslation(NodeT* const, const vec3_t&); 389 | 390 | ///----------------------------------------------------------------------- 391 | ///----------------------------------------------------------------------- 392 | 393 | } // namespace glTF_2_0 394 | 395 | #endif // ! FLATGLTF_2_0_API_H_INC 396 | -------------------------------------------------------------------------------- /src/2.0/glTFaccessor.cpp: -------------------------------------------------------------------------------- 1 | #include "flatgltf/2.0/glTF_generated.h" 2 | #include "flatgltf/2.0/glTFapi.hpp" 3 | #include "flatgltf/2.0/glTFmath_types.hpp" 4 | 5 | #include "glTFinternal.hpp" 6 | 7 | #define KHUTILS_ASSERTION_INLINE 8 | 9 | #include "khutils/assertion.hpp" 10 | #include "khutils/runtime_exceptions.hpp" 11 | 12 | namespace glTF_2_0 13 | { 14 | //------------------------------------------------------------------------- 15 | 16 | AccessorT* const createAccessor(Document* const doc, const char* name) 17 | { 18 | auto instance = Accessor_t{new AccessorT}; 19 | if (name) 20 | { 21 | instance->name = name; 22 | } 23 | doc->root->accessors.push_back(std::move(instance)); 24 | return doc->root->accessors.back().get(); 25 | } 26 | 27 | //--- 28 | 29 | //! create accessor on data in buffer 30 | // data will be appended to buffer 31 | AccessorT* const createAccessor(const uint8_t* const data, size_t length, Document* const doc, BufferT* const buf, const char* name) 32 | { 33 | auto view = createBufferView(data, length, doc, buf, name); 34 | auto accessor = createAccessor(doc, name); 35 | KHUTILS_ASSERT_PTR(accessor); 36 | 37 | accessor->bufferView = getId(doc, view); 38 | accessor->byteOffset = 0; 39 | accessor->componentType = static_cast(ComponentType::UNSIGNED_BYTE); 40 | accessor->normalized = false; 41 | accessor->count = length; 42 | accessor->type = AccessorType::SCALAR; 43 | return accessor; 44 | } 45 | 46 | //--- 47 | 48 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 49 | { 50 | auto accessor = createAccessor(data.data(), data.size(), doc, buf, name); 51 | KHUTILS_ASSERT_PTR(accessor); 52 | return accessor; 53 | } 54 | 55 | //--- 56 | 57 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 58 | { 59 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 60 | data.size() * sizeof(vec4_t), 61 | doc, 62 | buf, 63 | name); 64 | KHUTILS_ASSERT_PTR(accessor); 65 | accessor->componentType = static_cast(ComponentType::FLOAT); 66 | accessor->normalized = false; 67 | accessor->count = data.size(); 68 | accessor->type = AccessorType::VEC4; 69 | 70 | auto bufferView = getBufferView(doc, accessor->bufferView); 71 | KHUTILS_ASSERT_PTR(bufferView); 72 | bufferView->target = static_cast(BufferViewTarget::ARRAY_BUFFER); 73 | 74 | return accessor; 75 | } 76 | 77 | //--- 78 | 79 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 80 | { 81 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 82 | data.size() * sizeof(vec3_t), 83 | doc, 84 | buf, 85 | name); 86 | KHUTILS_ASSERT_PTR(accessor); 87 | accessor->componentType = static_cast(ComponentType::FLOAT); 88 | accessor->normalized = false; 89 | accessor->count = data.size(); 90 | accessor->type = AccessorType::VEC3; 91 | 92 | auto bufferView = getBufferView(doc, accessor->bufferView); 93 | KHUTILS_ASSERT_PTR(bufferView); 94 | bufferView->target = static_cast(BufferViewTarget::ARRAY_BUFFER); 95 | 96 | return accessor; 97 | } 98 | 99 | //--- 100 | 101 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 102 | { 103 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 104 | data.size() * sizeof(vec2_t), 105 | doc, 106 | buf, 107 | name); 108 | KHUTILS_ASSERT_PTR(accessor); 109 | accessor->componentType = static_cast(ComponentType::FLOAT); 110 | accessor->normalized = false; 111 | accessor->count = data.size(); 112 | accessor->type = AccessorType::VEC2; 113 | 114 | auto bufferView = getBufferView(doc, accessor->bufferView); 115 | KHUTILS_ASSERT_PTR(bufferView); 116 | bufferView->target = static_cast(BufferViewTarget::ARRAY_BUFFER); 117 | 118 | return accessor; 119 | } 120 | 121 | //--- 122 | 123 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 124 | { 125 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 126 | data.size() * sizeof(int32_t), 127 | doc, 128 | buf, 129 | name); 130 | KHUTILS_ASSERT_PTR(accessor); 131 | accessor->componentType = static_cast(ComponentType::UNSIGNED_INT); 132 | accessor->normalized = false; 133 | accessor->count = data.size(); 134 | accessor->type = AccessorType::SCALAR; 135 | 136 | auto bufferView = getBufferView(doc, accessor->bufferView); 137 | KHUTILS_ASSERT_PTR(bufferView); 138 | bufferView->target = static_cast(BufferViewTarget::ELEMENT_ARRAY_BUFFER); 139 | 140 | return accessor; 141 | } 142 | 143 | //--- 144 | 145 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 146 | { 147 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 148 | data.size() * sizeof(int32_t), 149 | doc, 150 | buf, 151 | name); 152 | KHUTILS_ASSERT_PTR(accessor); 153 | accessor->componentType = static_cast(ComponentType::UNSIGNED_INT); 154 | accessor->normalized = false; 155 | accessor->count = data.size(); 156 | accessor->type = AccessorType::SCALAR; 157 | 158 | auto bufferView = getBufferView(doc, accessor->bufferView); 159 | KHUTILS_ASSERT_PTR(bufferView); 160 | bufferView->target = static_cast(BufferViewTarget::ELEMENT_ARRAY_BUFFER); 161 | 162 | return accessor; 163 | } 164 | 165 | ////////////////////////////////////////////////////////////////////////// 166 | 167 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 168 | { 169 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 170 | data.size() * sizeof(i16vec4_t), 171 | doc, 172 | buf, 173 | name); 174 | KHUTILS_ASSERT_PTR(accessor); 175 | accessor->componentType = static_cast(ComponentType::SHORT); 176 | accessor->normalized = false; 177 | accessor->count = data.size(); 178 | accessor->type = AccessorType::VEC4; 179 | 180 | auto bufferView = getBufferView(doc, accessor->bufferView); 181 | KHUTILS_ASSERT_PTR(bufferView); 182 | bufferView->target = static_cast(BufferViewTarget::ARRAY_BUFFER); 183 | 184 | return accessor; 185 | } 186 | 187 | //--- 188 | 189 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 190 | { 191 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 192 | data.size() * sizeof(i16vec3_t), 193 | doc, 194 | buf, 195 | name); 196 | KHUTILS_ASSERT_PTR(accessor); 197 | accessor->componentType = static_cast(ComponentType::SHORT); 198 | accessor->normalized = false; 199 | accessor->count = data.size(); 200 | accessor->type = AccessorType::VEC3; 201 | 202 | auto bufferView = getBufferView(doc, accessor->bufferView); 203 | KHUTILS_ASSERT_PTR(bufferView); 204 | bufferView->target = static_cast(BufferViewTarget::ARRAY_BUFFER); 205 | 206 | return accessor; 207 | } 208 | 209 | //--- 210 | 211 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 212 | { 213 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 214 | data.size() * sizeof(i16vec2_t), 215 | doc, 216 | buf, 217 | name); 218 | KHUTILS_ASSERT_PTR(accessor); 219 | accessor->componentType = static_cast(ComponentType::SHORT); 220 | accessor->normalized = false; 221 | accessor->count = data.size(); 222 | accessor->type = AccessorType::VEC2; 223 | 224 | auto bufferView = getBufferView(doc, accessor->bufferView); 225 | KHUTILS_ASSERT_PTR(bufferView); 226 | bufferView->target = static_cast(BufferViewTarget::ARRAY_BUFFER); 227 | 228 | return accessor; 229 | } 230 | 231 | //--- 232 | 233 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 234 | { 235 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 236 | data.size() * sizeof(i8vec4_t), 237 | doc, 238 | buf, 239 | name); 240 | KHUTILS_ASSERT_PTR(accessor); 241 | accessor->componentType = static_cast(ComponentType::BYTE); 242 | accessor->normalized = false; 243 | accessor->count = data.size(); 244 | accessor->type = AccessorType::VEC4; 245 | 246 | auto bufferView = getBufferView(doc, accessor->bufferView); 247 | KHUTILS_ASSERT_PTR(bufferView); 248 | bufferView->target = static_cast(BufferViewTarget::ARRAY_BUFFER); 249 | 250 | return accessor; 251 | } 252 | 253 | //--- 254 | 255 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 256 | { 257 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 258 | data.size() * sizeof(i8vec3_t), 259 | doc, 260 | buf, 261 | name); 262 | KHUTILS_ASSERT_PTR(accessor); 263 | accessor->componentType = static_cast(ComponentType::BYTE); 264 | accessor->normalized = false; 265 | accessor->count = data.size(); 266 | accessor->type = AccessorType::VEC3; 267 | 268 | auto bufferView = getBufferView(doc, accessor->bufferView); 269 | KHUTILS_ASSERT_PTR(bufferView); 270 | bufferView->target = static_cast(BufferViewTarget::ARRAY_BUFFER); 271 | 272 | return accessor; 273 | } 274 | 275 | //--- 276 | 277 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 278 | { 279 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 280 | data.size() * sizeof(i8vec2_t), 281 | doc, 282 | buf, 283 | name); 284 | KHUTILS_ASSERT_PTR(accessor); 285 | accessor->componentType = static_cast(ComponentType::BYTE); 286 | accessor->normalized = false; 287 | accessor->count = data.size(); 288 | accessor->type = AccessorType::VEC2; 289 | 290 | auto bufferView = getBufferView(doc, accessor->bufferView); 291 | KHUTILS_ASSERT_PTR(bufferView); 292 | bufferView->target = static_cast(BufferViewTarget::ARRAY_BUFFER); 293 | 294 | return accessor; 295 | } 296 | 297 | //--- 298 | 299 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 300 | { 301 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 302 | data.size() * sizeof(u16vec4_t), 303 | doc, 304 | buf, 305 | name); 306 | KHUTILS_ASSERT_PTR(accessor); 307 | accessor->componentType = static_cast(ComponentType::UNSIGNED_SHORT); 308 | accessor->normalized = false; 309 | accessor->count = data.size(); 310 | accessor->type = AccessorType::VEC4; 311 | 312 | auto bufferView = getBufferView(doc, accessor->bufferView); 313 | KHUTILS_ASSERT_PTR(bufferView); 314 | bufferView->target = static_cast(BufferViewTarget::ARRAY_BUFFER); 315 | 316 | return accessor; 317 | } 318 | 319 | //--- 320 | 321 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 322 | { 323 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 324 | data.size() * sizeof(u16vec3_t), 325 | doc, 326 | buf, 327 | name); 328 | KHUTILS_ASSERT_PTR(accessor); 329 | accessor->componentType = static_cast(ComponentType::UNSIGNED_SHORT); 330 | accessor->normalized = false; 331 | accessor->count = data.size(); 332 | accessor->type = AccessorType::VEC3; 333 | 334 | auto bufferView = getBufferView(doc, accessor->bufferView); 335 | KHUTILS_ASSERT_PTR(bufferView); 336 | bufferView->target = static_cast(BufferViewTarget::ARRAY_BUFFER); 337 | 338 | return accessor; 339 | } 340 | 341 | //--- 342 | 343 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 344 | { 345 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 346 | data.size() * sizeof(u16vec2_t), 347 | doc, 348 | buf, 349 | name); 350 | KHUTILS_ASSERT_PTR(accessor); 351 | accessor->componentType = static_cast(ComponentType::UNSIGNED_SHORT); 352 | accessor->normalized = false; 353 | accessor->count = data.size(); 354 | accessor->type = AccessorType::VEC2; 355 | 356 | auto bufferView = getBufferView(doc, accessor->bufferView); 357 | KHUTILS_ASSERT_PTR(bufferView); 358 | bufferView->target = static_cast(BufferViewTarget::ARRAY_BUFFER); 359 | 360 | return accessor; 361 | } 362 | 363 | //--- 364 | 365 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 366 | { 367 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 368 | data.size() * sizeof(u8vec4_t), 369 | doc, 370 | buf, 371 | name); 372 | KHUTILS_ASSERT_PTR(accessor); 373 | accessor->componentType = static_cast(ComponentType::UNSIGNED_BYTE); 374 | accessor->normalized = false; 375 | accessor->count = data.size(); 376 | accessor->type = AccessorType::VEC4; 377 | 378 | auto bufferView = getBufferView(doc, accessor->bufferView); 379 | KHUTILS_ASSERT_PTR(bufferView); 380 | bufferView->target = static_cast(BufferViewTarget::ARRAY_BUFFER); 381 | 382 | return accessor; 383 | } 384 | 385 | //--- 386 | 387 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 388 | { 389 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 390 | data.size() * sizeof(u8vec3_t), 391 | doc, 392 | buf, 393 | name); 394 | KHUTILS_ASSERT_PTR(accessor); 395 | accessor->componentType = static_cast(ComponentType::UNSIGNED_BYTE); 396 | accessor->normalized = false; 397 | accessor->count = data.size(); 398 | accessor->type = AccessorType::VEC3; 399 | 400 | auto bufferView = getBufferView(doc, accessor->bufferView); 401 | KHUTILS_ASSERT_PTR(bufferView); 402 | bufferView->target = static_cast(BufferViewTarget::ARRAY_BUFFER); 403 | 404 | return accessor; 405 | } 406 | 407 | //--- 408 | 409 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 410 | { 411 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 412 | data.size() * sizeof(u8vec2_t), 413 | doc, 414 | buf, 415 | name); 416 | KHUTILS_ASSERT_PTR(accessor); 417 | accessor->componentType = static_cast(ComponentType::UNSIGNED_BYTE); 418 | accessor->normalized = false; 419 | accessor->count = data.size(); 420 | accessor->type = AccessorType::VEC2; 421 | 422 | auto bufferView = getBufferView(doc, accessor->bufferView); 423 | KHUTILS_ASSERT_PTR(bufferView); 424 | bufferView->target = static_cast(BufferViewTarget::ARRAY_BUFFER); 425 | 426 | return accessor; 427 | } 428 | 429 | //--- 430 | 431 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 432 | { 433 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 434 | data.size() * sizeof(mat4_t), 435 | doc, 436 | buf, 437 | name); 438 | KHUTILS_ASSERT_PTR(accessor); 439 | accessor->componentType = static_cast(ComponentType::FLOAT); 440 | accessor->normalized = false; 441 | accessor->count = data.size(); 442 | accessor->type = AccessorType::MAT4; 443 | 444 | // auto bufferView = getBufferView(doc, accessor->bufferView); 445 | // KHUTILS_ASSERT_PTR(bufferView); 446 | // bufferView->target = static_cast(BufferViewTarget::ARRAY_BUFFER); 447 | 448 | return accessor; 449 | } 450 | 451 | //--- 452 | 453 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 454 | { 455 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 456 | data.size() * sizeof(mat3_t), 457 | doc, 458 | buf, 459 | name); 460 | KHUTILS_ASSERT_PTR(accessor); 461 | accessor->componentType = static_cast(ComponentType::FLOAT); 462 | accessor->normalized = false; 463 | accessor->count = data.size(); 464 | accessor->type = AccessorType::MAT3; 465 | 466 | // auto bufferView = getBufferView(doc, accessor->bufferView); 467 | // KHUTILS_ASSERT_PTR(bufferView); 468 | // bufferView->target = static_cast(BufferViewTarget::ARRAY_BUFFER); 469 | 470 | return accessor; 471 | } 472 | 473 | //--- 474 | 475 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferT* const buf, const char* name) 476 | { 477 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 478 | data.size() * sizeof(mat2_t), 479 | doc, 480 | buf, 481 | name); 482 | KHUTILS_ASSERT_PTR(accessor); 483 | accessor->componentType = static_cast(ComponentType::FLOAT); 484 | accessor->normalized = false; 485 | accessor->count = data.size(); 486 | accessor->type = AccessorType::MAT2; 487 | 488 | // auto bufferView = getBufferView(doc, accessor->bufferView); 489 | // KHUTILS_ASSERT_PTR(bufferView); 490 | // bufferView->target = static_cast(BufferViewTarget::ARRAY_BUFFER); 491 | 492 | return accessor; 493 | } 494 | 495 | 496 | ////////////////////////////////////////////////////////////////////////// 497 | 498 | //! create accessor on data in bufferView 499 | AccessorT* const createAccessor(const uint8_t* const data, size_t length, Document* const doc, BufferViewT* const view, const char* name) 500 | { 501 | KHUTILS_ASSERT_PTR(doc); 502 | KHUTILS_ASSERT_PTR(view); 503 | auto sz = setBufferViewData(data, length, doc, view); 504 | auto accessor = createAccessor(doc); 505 | KHUTILS_ASSERT_PTR(accessor); 506 | 507 | accessor->bufferView = getId(doc, view); 508 | accessor->byteOffset = 0; 509 | accessor->componentType = static_cast(ComponentType::UNSIGNED_BYTE); 510 | accessor->normalized = false; 511 | accessor->count = length; 512 | accessor->type = AccessorType::SCALAR; 513 | return accessor; 514 | } 515 | 516 | //--- 517 | 518 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferViewT* const view, const char* name) 519 | { 520 | auto accessor = createAccessor(data.data(), data.size(), doc, view); 521 | KHUTILS_ASSERT_PTR(accessor); 522 | return accessor; 523 | } 524 | 525 | //--- 526 | 527 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferViewT* const view, const char* name) 528 | { 529 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 530 | data.size() * sizeof(vec4_t), 531 | doc, 532 | view); 533 | KHUTILS_ASSERT_PTR(accessor); 534 | accessor->componentType = static_cast(ComponentType::FLOAT); 535 | accessor->normalized = false; 536 | accessor->count = data.size(); 537 | accessor->type = AccessorType::VEC4; 538 | 539 | return accessor; 540 | } 541 | 542 | //--- 543 | 544 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferViewT* const view, const char* name) 545 | { 546 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 547 | data.size() * sizeof(vec3_t), 548 | doc, 549 | view); 550 | KHUTILS_ASSERT_PTR(accessor); 551 | accessor->componentType = static_cast(ComponentType::FLOAT); 552 | accessor->normalized = false; 553 | accessor->count = data.size(); 554 | accessor->type = AccessorType::VEC3; 555 | 556 | return accessor; 557 | } 558 | 559 | //--- 560 | 561 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferViewT* const view, const char* name) 562 | { 563 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 564 | data.size() * sizeof(vec2_t), 565 | doc, 566 | view); 567 | KHUTILS_ASSERT_PTR(accessor); 568 | accessor->componentType = static_cast(ComponentType::FLOAT); 569 | accessor->normalized = false; 570 | accessor->count = data.size(); 571 | accessor->type = AccessorType::VEC2; 572 | 573 | return accessor; 574 | } 575 | 576 | //--- 577 | 578 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferViewT* const view, const char* name) 579 | { 580 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 581 | data.size() * sizeof(int32_t), 582 | doc, 583 | view); 584 | KHUTILS_ASSERT_PTR(accessor); 585 | accessor->componentType = static_cast(ComponentType::UNSIGNED_INT); 586 | accessor->normalized = false; 587 | accessor->count = data.size(); 588 | accessor->type = AccessorType::SCALAR; 589 | 590 | 591 | KHUTILS_ASSERT_PTR(view); 592 | view->target = static_cast(BufferViewTarget::ELEMENT_ARRAY_BUFFER); 593 | return accessor; 594 | } 595 | 596 | //--- 597 | 598 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferViewT* const view, const char* name) 599 | { 600 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 601 | data.size() * sizeof(int32_t), 602 | doc, 603 | view); 604 | KHUTILS_ASSERT_PTR(accessor); 605 | accessor->componentType = static_cast(ComponentType::UNSIGNED_INT); 606 | accessor->normalized = false; 607 | accessor->count = data.size(); 608 | accessor->type = AccessorType::SCALAR; 609 | 610 | 611 | KHUTILS_ASSERT_PTR(view); 612 | view->target = static_cast(BufferViewTarget::ELEMENT_ARRAY_BUFFER); 613 | return accessor; 614 | } 615 | 616 | ////////////////////////////////////////////////////////////////////////// 617 | 618 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferViewT* const view, const char* name) 619 | { 620 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 621 | data.size() * sizeof(i16vec4_t), 622 | doc, 623 | view); 624 | KHUTILS_ASSERT_PTR(accessor); 625 | accessor->componentType = static_cast(ComponentType::SHORT); 626 | accessor->normalized = false; 627 | accessor->count = data.size(); 628 | accessor->type = AccessorType::VEC4; 629 | 630 | return accessor; 631 | } 632 | 633 | //--- 634 | 635 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferViewT* const view, const char* name) 636 | { 637 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 638 | data.size() * sizeof(i16vec3_t), 639 | doc, 640 | view); 641 | KHUTILS_ASSERT_PTR(accessor); 642 | accessor->componentType = static_cast(ComponentType::SHORT); 643 | accessor->normalized = false; 644 | accessor->count = data.size(); 645 | accessor->type = AccessorType::VEC3; 646 | 647 | return accessor; 648 | } 649 | 650 | //--- 651 | 652 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferViewT* const view, const char* name) 653 | { 654 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 655 | data.size() * sizeof(i16vec2_t), 656 | doc, 657 | view); 658 | KHUTILS_ASSERT_PTR(accessor); 659 | accessor->componentType = static_cast(ComponentType::SHORT); 660 | accessor->normalized = false; 661 | accessor->count = data.size(); 662 | accessor->type = AccessorType::VEC2; 663 | 664 | return accessor; 665 | } 666 | 667 | //--- 668 | 669 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferViewT* const view, const char* name) 670 | { 671 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 672 | data.size() * sizeof(i8vec4_t), 673 | doc, 674 | view); 675 | KHUTILS_ASSERT_PTR(accessor); 676 | accessor->componentType = static_cast(ComponentType::BYTE); 677 | accessor->normalized = false; 678 | accessor->count = data.size(); 679 | accessor->type = AccessorType::VEC4; 680 | 681 | return accessor; 682 | } 683 | 684 | //--- 685 | 686 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferViewT* const view, const char* name) 687 | { 688 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 689 | data.size() * sizeof(i8vec3_t), 690 | doc, 691 | view); 692 | KHUTILS_ASSERT_PTR(accessor); 693 | accessor->componentType = static_cast(ComponentType::BYTE); 694 | accessor->normalized = false; 695 | accessor->count = data.size(); 696 | accessor->type = AccessorType::VEC3; 697 | 698 | return accessor; 699 | } 700 | 701 | //--- 702 | 703 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferViewT* const view, const char* name) 704 | { 705 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 706 | data.size() * sizeof(i8vec2_t), 707 | doc, 708 | view); 709 | KHUTILS_ASSERT_PTR(accessor); 710 | accessor->componentType = static_cast(ComponentType::BYTE); 711 | accessor->normalized = false; 712 | accessor->count = data.size(); 713 | accessor->type = AccessorType::VEC2; 714 | 715 | return accessor; 716 | } 717 | 718 | //--- 719 | 720 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferViewT* const view, const char* name) 721 | { 722 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 723 | data.size() * sizeof(u16vec4_t), 724 | doc, 725 | view); 726 | KHUTILS_ASSERT_PTR(accessor); 727 | accessor->componentType = static_cast(ComponentType::UNSIGNED_SHORT); 728 | accessor->normalized = false; 729 | accessor->count = data.size(); 730 | accessor->type = AccessorType::VEC4; 731 | 732 | return accessor; 733 | } 734 | 735 | //--- 736 | 737 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferViewT* const view, const char* name) 738 | { 739 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 740 | data.size() * sizeof(u16vec3_t), 741 | doc, 742 | view); 743 | KHUTILS_ASSERT_PTR(accessor); 744 | accessor->componentType = static_cast(ComponentType::UNSIGNED_SHORT); 745 | accessor->normalized = false; 746 | accessor->count = data.size(); 747 | accessor->type = AccessorType::VEC3; 748 | 749 | return accessor; 750 | } 751 | 752 | //--- 753 | 754 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferViewT* const view, const char* name) 755 | { 756 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 757 | data.size() * sizeof(u16vec2_t), 758 | doc, 759 | view); 760 | KHUTILS_ASSERT_PTR(accessor); 761 | accessor->componentType = static_cast(ComponentType::UNSIGNED_SHORT); 762 | accessor->normalized = false; 763 | accessor->count = data.size(); 764 | accessor->type = AccessorType::VEC2; 765 | 766 | return accessor; 767 | } 768 | 769 | //--- 770 | 771 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferViewT* const view, const char* name) 772 | { 773 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 774 | data.size() * sizeof(u8vec4_t), 775 | doc, 776 | view); 777 | KHUTILS_ASSERT_PTR(accessor); 778 | accessor->componentType = static_cast(ComponentType::UNSIGNED_BYTE); 779 | accessor->normalized = false; 780 | accessor->count = data.size(); 781 | accessor->type = AccessorType::VEC4; 782 | 783 | return accessor; 784 | } 785 | 786 | //--- 787 | 788 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferViewT* const view, const char* name) 789 | { 790 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 791 | data.size() * sizeof(u8vec3_t), 792 | doc, 793 | view); 794 | KHUTILS_ASSERT_PTR(accessor); 795 | accessor->componentType = static_cast(ComponentType::UNSIGNED_BYTE); 796 | accessor->normalized = false; 797 | accessor->count = data.size(); 798 | accessor->type = AccessorType::VEC3; 799 | 800 | return accessor; 801 | } 802 | 803 | //--- 804 | 805 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferViewT* const view, const char* name) 806 | { 807 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 808 | data.size() * sizeof(u8vec2_t), 809 | doc, 810 | view); 811 | KHUTILS_ASSERT_PTR(accessor); 812 | accessor->componentType = static_cast(ComponentType::UNSIGNED_BYTE); 813 | accessor->normalized = false; 814 | accessor->count = data.size(); 815 | accessor->type = AccessorType::VEC2; 816 | 817 | return accessor; 818 | } 819 | 820 | //--- 821 | 822 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferViewT* const view, const char* name) 823 | { 824 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 825 | data.size() * sizeof(mat4_t), 826 | doc, 827 | view); 828 | KHUTILS_ASSERT_PTR(accessor); 829 | accessor->componentType = static_cast(ComponentType::FLOAT); 830 | accessor->normalized = false; 831 | accessor->count = data.size(); 832 | accessor->type = AccessorType::MAT4; 833 | 834 | return accessor; 835 | } 836 | 837 | //--- 838 | 839 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferViewT* const view, const char* name) 840 | { 841 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 842 | data.size() * sizeof(mat3_t), 843 | doc, 844 | view); 845 | KHUTILS_ASSERT_PTR(accessor); 846 | accessor->componentType = static_cast(ComponentType::FLOAT); 847 | accessor->normalized = false; 848 | accessor->count = data.size(); 849 | accessor->type = AccessorType::MAT3; 850 | 851 | return accessor; 852 | } 853 | 854 | //--- 855 | 856 | AccessorT* const createAccessor(const std::vector& data, Document* const doc, BufferViewT* const view, const char* name) 857 | { 858 | auto accessor = createAccessor(reinterpret_cast(data.data()), // 859 | data.size() * sizeof(mat2_t), 860 | doc, 861 | view); 862 | KHUTILS_ASSERT_PTR(accessor); 863 | accessor->componentType = static_cast(ComponentType::FLOAT); 864 | accessor->normalized = false; 865 | accessor->count = data.size(); 866 | accessor->type = AccessorType::MAT2; 867 | 868 | return accessor; 869 | } 870 | 871 | //------------------------------------------------------------------------- 872 | //------------------------------------------------------------------------- 873 | 874 | } // namespace glTF_2_0 875 | --------------------------------------------------------------------------------