diff --git a/Dependencies/jolt/jolt.lua b/Dependencies/jolt/jolt.lua index 8f351cb..c3d9d37 100644 --- a/Dependencies/jolt/jolt.lua +++ b/Dependencies/jolt/jolt.lua @@ -494,9 +494,14 @@ Solution.Util.CreateStaticLib(dep.Name, Solution.Projects.Current.BinDir, dep.De end) Solution.Util.CreateDep(dep.NameLow, dep.Dependencies, function() + -- get our own local cache + local additionalDefines = Solution.Util.GetDepCache(dep.NameLow, "cache") + if not additionalDefines then + additionalDefines = GetJoltDefines() + Solution.Util.SetDepCache(dep.NameLow, "cache", additionalDefines) + end + Solution.Util.SetIncludes(dep.Path) Solution.Util.SetLinks(dep.Name) - - local additionalDefines = GetJoltDefines() Solution.Util.SetDefines(additionalDefines) end) \ No newline at end of file diff --git a/Dependencies/meshoptimizer/.clang-format b/Dependencies/meshoptimizer/.clang-format deleted file mode 100644 index 039f510..0000000 --- a/Dependencies/meshoptimizer/.clang-format +++ /dev/null @@ -1,13 +0,0 @@ -Standard: Cpp03 -UseTab: ForIndentation -TabWidth: 4 -IndentWidth: 4 -AccessModifierOffset: -4 -BreakBeforeBraces: Allman -IndentCaseLabels: false -ColumnLimit: 0 -PointerAlignment: Left -BreakConstructorInitializersBeforeComma: true -NamespaceIndentation: None -AlignEscapedNewlines: DontAlign -AlignAfterOpenBracket: DontAlign diff --git a/Dependencies/meshoptimizer/.editorconfig b/Dependencies/meshoptimizer/.editorconfig deleted file mode 100644 index 09eb07c..0000000 --- a/Dependencies/meshoptimizer/.editorconfig +++ /dev/null @@ -1,8 +0,0 @@ -# See https://editorconfig.org/ for more info - -[*] -charset = utf-8 -indent_style = tab -indent_size = 4 -trim_trailing_whitespace = true -insert_final_newline = true diff --git a/Dependencies/meshoptimizer/.github/ISSUE_TEMPLATE/bug_report.md b/Dependencies/meshoptimizer/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index ab8ecea..0000000 --- a/Dependencies/meshoptimizer/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: Bug report -about: Create a report if you believe you've found a bug in this project; please use GitHub Discussions instead if you think the bug may be in your code. -title: '' -labels: bug -assignees: '' - ---- diff --git a/Dependencies/meshoptimizer/.github/ISSUE_TEMPLATE/config.yml b/Dependencies/meshoptimizer/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 28b9018..0000000 --- a/Dependencies/meshoptimizer/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,5 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: Help and support - url: https://github.com/zeux/meshoptimizer/discussions - about: Please use GitHub Discussions if you have questions or need help. diff --git a/Dependencies/meshoptimizer/.github/ISSUE_TEMPLATE/feature_request.md b/Dependencies/meshoptimizer/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 83826c6..0000000 --- a/Dependencies/meshoptimizer/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: enhancement -assignees: '' - ---- diff --git a/Dependencies/meshoptimizer/.github/workflows/build.yml b/Dependencies/meshoptimizer/.github/workflows/build.yml deleted file mode 100644 index 43b5f8b..0000000 --- a/Dependencies/meshoptimizer/.github/workflows/build.yml +++ /dev/null @@ -1,149 +0,0 @@ -name: build - -on: - push: - branches: - - 'master' - pull_request: - -jobs: - unix: - strategy: - matrix: - os: [ubuntu, macos] - name: ${{matrix.os}} - runs-on: ${{matrix.os}}-latest - steps: - - uses: actions/checkout@v1 - - name: make test - run: | - make -j2 config=sanitize test - make -j2 config=debug test - make -j2 config=release test - make -j2 config=coverage test - - name: make gltfpack - run: | - make -j2 config=release gltfpack - strip gltfpack - - name: upload coverage - run: | - find . -type f -name '*.gcno' -exec gcov -p {} + - sed -i -e "s/#####\(.*\)\(\/\/ unreachable.*\)/ -\1\2/" *.gcov - bash <(curl -s https://codecov.io/bash) -f './src*.gcov' -X search -t ${{secrets.CODECOV_TOKEN}} -B ${{github.ref}} - - windows: - runs-on: windows-latest - strategy: - matrix: - arch: [Win32, x64] - steps: - - uses: actions/checkout@v1 - - name: cmake configure - run: cmake . -DMESHOPT_BUILD_DEMO=ON -DMESHOPT_BUILD_GLTFPACK=ON -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$:Debug>" -A ${{matrix.arch}} - - name: cmake test - shell: bash # necessary for fail-fast - run: | - cmake --build . -- -property:Configuration=Debug -verbosity:minimal - Debug/demo.exe demo/pirate.obj - cmake --build . -- -property:Configuration=Release -verbosity:minimal - Release/demo.exe demo/pirate.obj - - nodejs: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-node@v1 - with: - node-version: '16' - - name: test decoder - run: node js/meshopt_decoder.test.js - - name: test simd decoder - run: node --experimental-wasm-simd js/meshopt_decoder.test.js - - name: test encoder - run: node js/meshopt_encoder.test.js - - gltfpack: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - uses: actions/checkout@v2 - with: - repository: KhronosGroup/glTF-Sample-Models - path: glTF-Sample-Models - - name: make - run: make -j2 config=sanitize gltfpack - - name: test - run: find glTF-Sample-Models/2.0/ -name *.gltf -or -name *.glb | xargs -d '\n' ./gltfpack -cc -test - - name: pack - run: find glTF-Sample-Models/2.0/ -name *.gltf | grep -v glTF-Draco | grep -v glTF-KTX-BasisU | xargs -d '\n' -I '{}' ./gltfpack -i '{}' -o '{}pack.gltf' - - name: validate - run: | - curl -sL $VALIDATOR | tar xJ - find glTF-Sample-Models/2.0/ -name *.gltfpack.gltf | xargs -d '\n' -L 1 ./gltf_validator -r -a - env: - VALIDATOR: https://github.com/KhronosGroup/glTF-Validator/releases/download/2.0.0-dev.3.3/gltf_validator-2.0.0-dev.3.3-linux64.tar.xz - - gltfpackjs: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-node@v1 - with: - node-version: '14.x' - - name: install wasi - run: | - curl -sL https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-$VERSION/wasi-sdk-$VERSION.0-linux.tar.gz | tar xz - curl -sL https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-$VERSION/libclang_rt.builtins-wasm32-wasi-$VERSION.0.tar.gz | tar xz -C wasi-sdk-$VERSION.0 - curl -sL https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-$VERSION/wasi-sysroot-$VERSION.0.tar.gz | tar xz - mv wasi-sdk-$VERSION.0 wasi-sdk - env: - VERSION: 14 - - name: build - run: | - make -B WASMCC=wasi-sdk/bin/clang++ WASI_SDK=./wasi-sysroot gltf/library.wasm - make -B WASMCC=wasi-sdk/bin/clang++ WASI_SDK=./wasi-sysroot js/meshopt_decoder.js js/meshopt_decoder.module.js - make -B WASMCC=wasi-sdk/bin/clang++ WASI_SDK=./wasi-sysroot js/meshopt_encoder.js js/meshopt_encoder.module.js - - name: test - run: | - node gltf/cli.js -i demo/pirate.obj -o pirate.glb -v - node gltf/cli.js -i `pwd`/pirate.glb -o pirate-repack.glb -cc -v - wc -c pirate.glb pirate-repack.glb - node js/meshopt_decoder.test.js - node js/meshopt_encoder.test.js - - name: npm pack - run: | - cd gltf && npm pack && cd .. - cd js && npm pack && cd .. - - uses: actions/upload-artifact@v2 - with: - name: gltfpack-npm - path: gltf/gltfpack-*.tgz - - uses: actions/upload-artifact@v2 - with: - name: meshoptimizer-npm - path: js/meshoptimizer-*.tgz - - arm64: - runs-on: ubuntu-latest - steps: - - run: docker run --rm --privileged multiarch/qemu-user-static:register --reset - - uses: docker://multiarch/ubuntu-core:arm64-focal - with: - args: 'uname -a' - - uses: actions/checkout@v1 - - name: make test - uses: docker://multiarch/ubuntu-core:arm64-focal - with: - args: 'bash -c "apt-get update && apt-get install -y build-essential && make -j2 config=coverage test"' - - name: upload coverage - run: | - find . -type f -name '*.gcno' -exec gcov -p {} + - sed -i -e "s/#####\(.*\)\(\/\/ unreachable.*\)/ -\1\2/" *.gcov - bash <(curl -s https://codecov.io/bash) -f './src*.gcov' -X search -t ${{secrets.CODECOV_TOKEN}} -B ${{github.ref}} - - iphone: - runs-on: macos-latest - steps: - - uses: actions/checkout@v1 - - name: make - run: make -j2 config=iphone diff --git a/Dependencies/meshoptimizer/.github/workflows/release.yml b/Dependencies/meshoptimizer/.github/workflows/release.yml deleted file mode 100644 index 92b3274..0000000 --- a/Dependencies/meshoptimizer/.github/workflows/release.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: release - -on: - push: - branches: - - 'master' - pull_request: - -jobs: - gltfpack: - strategy: - matrix: - os: [windows, ubuntu, macos] - name: gltfpack-${{matrix.os}} - runs-on: ${{matrix.os}}-latest - steps: - - uses: actions/checkout@v1 - - uses: actions/checkout@v2 - with: - repository: zeux/basis_universal - ref: gltfpack - path: basis_universal - - name: cmake configure - run: cmake . -DMESHOPT_BUILD_GLTFPACK=ON -DMESHOPT_BASISU_PATH=basis_universal -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded" -DCMAKE_BUILD_TYPE=Release - - name: cmake build - run: cmake --build . --target gltfpack --config Release - - uses: actions/upload-artifact@v1 - with: - name: gltfpack-windows - path: Release/gltfpack.exe - if: matrix.os == 'windows' - - uses: actions/upload-artifact@v1 - with: - name: gltfpack-${{matrix.os}} - path: gltfpack - if: matrix.os != 'windows' diff --git a/Dependencies/meshoptimizer/.gitignore b/Dependencies/meshoptimizer/.gitignore deleted file mode 100644 index 6519d25..0000000 --- a/Dependencies/meshoptimizer/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/build/ -/data/ -/gltf/library.wasm diff --git a/Dependencies/meshoptimizer/CMakeLists.txt b/Dependencies/meshoptimizer/CMakeLists.txt deleted file mode 100644 index ed1a84c..0000000 --- a/Dependencies/meshoptimizer/CMakeLists.txt +++ /dev/null @@ -1,151 +0,0 @@ -cmake_minimum_required(VERSION 3.0) - -if(POLICY CMP0077) - cmake_policy(SET CMP0077 NEW) # Enables override of options from parent CMakeLists.txt -endif() - -if(POLICY CMP0091) - cmake_policy(SET CMP0091 NEW) # Enables use of MSVC_RUNTIME_LIBRARY -endif() -if(POLICY CMP0092) - cmake_policy(SET CMP0092 NEW) # Enables clean /W4 override for MSVC -endif() - -project(meshoptimizer VERSION 0.17 LANGUAGES CXX) - -option(MESHOPT_BUILD_DEMO "Build demo" OFF) -option(MESHOPT_BUILD_GLTFPACK "Build gltfpack" OFF) -option(MESHOPT_BUILD_SHARED_LIBS "Build shared libraries" OFF) -set(MESHOPT_BASISU_PATH "" CACHE STRING "") - -set(SOURCES - src/meshoptimizer.h - src/allocator.cpp - src/clusterizer.cpp - src/indexcodec.cpp - src/indexgenerator.cpp - src/overdrawanalyzer.cpp - src/overdrawoptimizer.cpp - src/simplifier.cpp - src/spatialorder.cpp - src/stripifier.cpp - src/vcacheanalyzer.cpp - src/vcacheoptimizer.cpp - src/vertexcodec.cpp - src/vertexfilter.cpp - src/vfetchanalyzer.cpp - src/vfetchoptimizer.cpp -) - -set(GLTF_SOURCES - gltf/animation.cpp - gltf/basisenc.cpp - gltf/basislib.cpp - gltf/fileio.cpp - gltf/gltfpack.cpp - gltf/image.cpp - gltf/json.cpp - gltf/material.cpp - gltf/mesh.cpp - gltf/node.cpp - gltf/parseobj.cpp - gltf/parsegltf.cpp - gltf/stream.cpp - gltf/write.cpp -) - -if(MSVC) - add_compile_options(/W4 /WX) -else() - add_compile_options(-Wall -Wextra -Wshadow -Wno-missing-field-initializers -Werror) -endif() - -if(MESHOPT_BUILD_SHARED_LIBS) - add_library(meshoptimizer SHARED ${SOURCES}) -else() - add_library(meshoptimizer STATIC ${SOURCES}) -endif() - -target_include_directories(meshoptimizer INTERFACE "$") - -if(MESHOPT_BUILD_SHARED_LIBS) - set_target_properties(meshoptimizer PROPERTIES CXX_VISIBILITY_PRESET hidden) - set_target_properties(meshoptimizer PROPERTIES VISIBILITY_INLINES_HIDDEN ON) - - if(WIN32) - target_compile_definitions(meshoptimizer INTERFACE "MESHOPTIMIZER_API=__declspec(dllimport)") - target_compile_definitions(meshoptimizer PRIVATE "MESHOPTIMIZER_API=__declspec(dllexport)") - else() - target_compile_definitions(meshoptimizer PUBLIC "MESHOPTIMIZER_API=__attribute__((visibility(\"default\")))") - endif() -endif() - -set(TARGETS meshoptimizer) - -if(MESHOPT_BUILD_DEMO) - add_executable(demo demo/main.cpp demo/tests.cpp tools/meshloader.cpp) - target_link_libraries(demo meshoptimizer) -endif() - -if(MESHOPT_BUILD_GLTFPACK) - add_executable(gltfpack ${GLTF_SOURCES} tools/meshloader.cpp) - set_target_properties(gltfpack PROPERTIES CXX_STANDARD 11) - target_link_libraries(gltfpack meshoptimizer) - list(APPEND TARGETS gltfpack) - - if(MESHOPT_BUILD_SHARED_LIBS) - string(CONCAT RPATH "$ORIGIN/../" ${CMAKE_INSTALL_LIBDIR}) - set_target_properties(gltfpack PROPERTIES INSTALL_RPATH ${RPATH}) - endif() - - if(NOT MESHOPT_BASISU_PATH STREQUAL "") - get_filename_component(BASISU_PATH ${MESHOPT_BASISU_PATH} ABSOLUTE) - - target_compile_definitions(gltfpack PRIVATE WITH_BASISU) - set_source_files_properties(gltf/basisenc.cpp gltf/basislib.cpp PROPERTIES INCLUDE_DIRECTORIES ${BASISU_PATH}) - - if(NOT MSVC AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64") - set_source_files_properties(gltf/basislib.cpp PROPERTIES COMPILE_OPTIONS -msse4.1) - endif() - - if(UNIX) - target_link_libraries(gltfpack pthread) - endif() - endif() -endif() - -include(GNUInstallDirs) - -install(TARGETS ${TARGETS} EXPORT meshoptimizerTargets - COMPONENT meshoptimizer - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - -install(FILES src/meshoptimizer.h COMPONENT meshoptimizer DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -install(EXPORT meshoptimizerTargets COMPONENT meshoptimizer DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/meshoptimizer NAMESPACE meshoptimizer::) - -# TARGET_PDB_FILE is available since 3.1 -if(MSVC AND NOT (CMAKE_VERSION VERSION_LESS "3.1")) - foreach(TARGET ${TARGETS}) - get_target_property(TARGET_TYPE ${TARGET} TYPE) - if(NOT ${TARGET_TYPE} STREQUAL "STATIC_LIBRARY") - install(FILES $ COMPONENT meshoptimizer DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL) - endif() - endforeach(TARGET) -endif() - -include(CMakePackageConfigHelpers) - -configure_package_config_file(config.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/meshoptimizerConfig.cmake - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/meshoptimizer NO_SET_AND_CHECK_MACRO) - -write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/meshoptimizerConfigVersion.cmake COMPATIBILITY ExactVersion) - -install(FILES - ${CMAKE_CURRENT_BINARY_DIR}/meshoptimizerConfig.cmake - ${CMAKE_CURRENT_BINARY_DIR}/meshoptimizerConfigVersion.cmake - COMPONENT meshoptimizer - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/meshoptimizer) diff --git a/Dependencies/meshoptimizer/CONTRIBUTING.md b/Dependencies/meshoptimizer/CONTRIBUTING.md deleted file mode 100644 index 8d40ee7..0000000 --- a/Dependencies/meshoptimizer/CONTRIBUTING.md +++ /dev/null @@ -1,54 +0,0 @@ -Thanks for deciding to contribute to meshoptimizer! These guidelines will try to help make the process painless and efficient. - -## Questions - -If you have a question regarding the library usage, please [open a GitHub issue](https://github.com/zeux/meshoptimizer/issues/new). -Some questions just need answers, but it's nice to keep them for future reference in case other people want to know the same thing. -Some questions help improve the library interface or documentation by inspiring future changes. - -## Bugs - -If the library doesn't compile on your system, compiles with warnings, doesn't seem to run correctly for your input data or if anything else is amiss, please [open a GitHub issue](https://github.com/zeux/meshoptimizer/issues/new). -It helps if you note the version of the library this issue happens in, the version of your compiler for compilation issues, and a reproduction case for runtime bugs. - -Of course, feel free to [create a pull request](https://help.github.com/articles/about-pull-requests/) to fix the bug yourself. - -## Features - -New algorithms and improvements to existing algorithms are always welcome; you can open an issue or make the change yourself and submit a pull request. - -For major features, consider opening an issue describing an improvement you'd like to see or make before opening a pull request. -This will give us a chance to discuss the idea before implementing it - some algorithms may not be easy to integrate into existing programs, may not be robust to arbitrary meshes or may be expensive to run or implement/maintain, so a discussion helps make sure these don't block the algorithm development. - -## Code style - -Contributions to this project are expected to follow the existing code style. -`.clang-format` file mostly defines syntactic styling rules (you can run `make format` to format the code accordingly). - -As for naming conventions, this library uses `snake_case` for variables, `lowerCamelCase` for functions, `UpperCamelCase` for types, `kCamelCase` for global constants and `SCARY_CASE` for macros. All public functions/types must additionally have an extra `meshopt_` prefix to avoid symbol conflicts. - -## Dependencies - -Please note that this library uses C89 interface for all APIs and a C++98 implementation - C++11 features can not be used. -This choice is made to maximize compatibility to make sure that any toolchain, including legacy proprietary gaming console toolchains, can compile this code. - -Additionally, the library code has zero external dependencies, does not depend on STL and does not use RTTI or exceptions. -This, again, maximizes compatibility and makes sure the library can be used in environments where STL use is discouraged or prohibited, as well as maximizing runtime performance and minimizing compilation times. - -The demo program uses STL since it serves as an example of usage and as a test harness, not as production-ready code. - -## Testing - -All pull requests will run through a continuous integration pipeline using GitHub Actions that will run the built-in unit tests and integration tests on Windows, macOS and Linux with gcc, clang and msvc compilers. -You can run the tests yourself using `make test` or building the demo program with `cmake -DBUILD_DEMO=ON` and running it. - -Unit tests can be found in `demo/tests.cpp` and functional tests - in `demo/main.cpp`; when making code changes please try to make sure they are covered by an existing test or add a new test accordingly. - -## Documentation - -Documentation for this library resides in the `meshoptimizer.h` header, with examples as part of a usage manual available in `README.md`. -Changes to documentation are always welcome and should use issues/pull requests as outlined above; please note that `README.md` only contains documentation for stable algorithms, as experimental algorithms may change the interface without concern for backwards compatibility. - -## Sensitive communication - -If you prefer to not disclose the issues or information relevant to the issue such as reproduction case to the public, you can always contact the author via e-mail (arseny.kapoulkine@gmail.com). diff --git a/Dependencies/meshoptimizer/LICENSE.md b/Dependencies/meshoptimizer/LICENSE.md deleted file mode 100644 index 3c52415..0000000 --- a/Dependencies/meshoptimizer/LICENSE.md +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016-2021 Arseny Kapoulkine - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/Dependencies/meshoptimizer/Makefile b/Dependencies/meshoptimizer/Makefile deleted file mode 100644 index 69cf867..0000000 --- a/Dependencies/meshoptimizer/Makefile +++ /dev/null @@ -1,189 +0,0 @@ -MAKEFLAGS+=-r -j - -config=debug -files=demo/pirate.obj - -BUILD=build/$(config) - -LIBRARY_SOURCES=$(wildcard src/*.cpp) -LIBRARY_OBJECTS=$(LIBRARY_SOURCES:%=$(BUILD)/%.o) - -DEMO_SOURCES=$(wildcard demo/*.c demo/*.cpp) tools/meshloader.cpp -DEMO_OBJECTS=$(DEMO_SOURCES:%=$(BUILD)/%.o) - -GLTFPACK_SOURCES=$(wildcard gltf/*.cpp) tools/meshloader.cpp -GLTFPACK_OBJECTS=$(GLTFPACK_SOURCES:%=$(BUILD)/%.o) - -OBJECTS=$(LIBRARY_OBJECTS) $(DEMO_OBJECTS) $(GLTFPACK_OBJECTS) - -LIBRARY=$(BUILD)/libmeshoptimizer.a -DEMO=$(BUILD)/meshoptimizer - -CFLAGS=-g -Wall -Wextra -Werror -std=c89 -CXXFLAGS=-g -Wall -Wextra -Wshadow -Wno-missing-field-initializers -Werror -std=c++98 -LDFLAGS= - -$(GLTFPACK_OBJECTS): CXXFLAGS+=-std=c++11 - -ifdef BASISU - $(GLTFPACK_OBJECTS): CXXFLAGS+=-DWITH_BASISU - $(BUILD)/gltf/basis%.cpp.o: CXXFLAGS+=-I$(BASISU) - gltfpack: LDFLAGS+=-lpthread - - ifeq ($(HOSTTYPE),x86_64) - $(BUILD)/gltf/basislib.cpp.o: CXXFLAGS+=-msse4.1 - endif -endif - -WASMCC=clang++ -WASI_SDK= - -WASM_FLAGS=--target=wasm32-wasi --sysroot=$(WASI_SDK) -WASM_FLAGS+=-O3 -DNDEBUG -nostartfiles -nostdlib -Wl,--no-entry -Wl,-s -WASM_FLAGS+=-fno-slp-vectorize -fno-vectorize -fno-unroll-loops -WASM_FLAGS+=-Wl,-z -Wl,stack-size=24576 -Wl,--initial-memory=65536 -WASM_EXPORT_PREFIX=-Wl,--export - -WASM_DECODER_SOURCES=src/vertexcodec.cpp src/indexcodec.cpp src/vertexfilter.cpp tools/wasmstubs.cpp -WASM_DECODER_EXPORTS=meshopt_decodeVertexBuffer meshopt_decodeIndexBuffer meshopt_decodeIndexSequence meshopt_decodeFilterOct meshopt_decodeFilterQuat meshopt_decodeFilterExp sbrk __wasm_call_ctors - -WASM_ENCODER_SOURCES=src/vertexcodec.cpp src/indexcodec.cpp src/vertexfilter.cpp src/vcacheoptimizer.cpp src/vfetchoptimizer.cpp tools/wasmstubs.cpp -WASM_ENCODER_EXPORTS=meshopt_encodeVertexBuffer meshopt_encodeVertexBufferBound meshopt_encodeIndexBuffer meshopt_encodeIndexBufferBound meshopt_encodeIndexSequence meshopt_encodeIndexSequenceBound meshopt_encodeVertexVersion meshopt_encodeIndexVersion meshopt_encodeFilterOct meshopt_encodeFilterQuat meshopt_encodeFilterExp meshopt_optimizeVertexCache meshopt_optimizeVertexCacheStrip meshopt_optimizeVertexFetchRemap sbrk __wasm_call_ctors - -WASM_SIMPLIFIER_SOURCES=src/simplifier.cpp tools/wasmstubs.cpp -WASM_SIMPLIFIER_EXPORTS=meshopt_simplify meshopt_simplifySloppy meshopt_simplifyPoints sbrk __wasm_call_ctors - -ifeq ($(config),iphone) - IPHONESDK=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk - CFLAGS+=-arch armv7 -arch arm64 -isysroot $(IPHONESDK) - CXXFLAGS+=-arch armv7 -arch arm64 -isysroot $(IPHONESDK) -stdlib=libc++ - LDFLAGS+=-arch armv7 -arch arm64 -isysroot $(IPHONESDK) -L $(IPHONESDK)/usr/lib -mios-version-min=7.0 -endif - -ifeq ($(config),trace) - CXXFLAGS+=-DTRACE=1 -endif - -ifeq ($(config),scalar) - CXXFLAGS+=-O3 -DNDEBUG -DMESHOPTIMIZER_NO_SIMD -endif - -ifeq ($(config),release) - CXXFLAGS+=-O3 -DNDEBUG -endif - -ifeq ($(config),coverage) - CXXFLAGS+=-coverage - LDFLAGS+=-coverage -endif - -ifeq ($(config),sanitize) - CXXFLAGS+=-fsanitize=address,undefined -fno-sanitize-recover=all - LDFLAGS+=-fsanitize=address,undefined -endif - -ifeq ($(config),analyze) - CXXFLAGS+=--analyze -endif - -all: $(DEMO) - -test: $(DEMO) - $(DEMO) $(files) - -check: $(DEMO) - $(DEMO) - -dev: $(DEMO) - $(DEMO) -d $(files) - -format: - clang-format -i $(LIBRARY_SOURCES) $(DEMO_SOURCES) $(GLTFPACK_SOURCES) - -js: js/meshopt_decoder.js js/meshopt_decoder.module.js js/meshopt_encoder.js js/meshopt_encoder.module.js js/meshopt_simplifier.js js/meshopt_simplifier.module.js - -gltfpack: $(BUILD)/gltfpack - ln -fs $^ $@ - -$(BUILD)/gltfpack: $(GLTFPACK_OBJECTS) $(LIBRARY) - $(CXX) $^ $(LDFLAGS) -o $@ - -gltfpack.wasm: gltf/library.wasm - -gltf/library.wasm: ${LIBRARY_SOURCES} ${GLTFPACK_SOURCES} tools/meshloader.cpp - $(WASMCC) $^ -o $@ -Os -DNDEBUG --target=wasm32-wasi --sysroot=$(WASI_SDK) -nostartfiles -Wl,--no-entry -Wl,--export=pack -Wl,--export=malloc -Wl,--export=free -Wl,--export=__wasm_call_ctors -Wl,-s -Wl,--allow-undefined-file=gltf/wasistubs.txt - -build/decoder_base.wasm: $(WASM_DECODER_SOURCES) - @mkdir -p build - $(WASMCC) $^ $(WASM_FLAGS) $(patsubst %,$(WASM_EXPORT_PREFIX)=%,$(WASM_DECODER_EXPORTS)) -o $@ - -build/decoder_simd.wasm: $(WASM_DECODER_SOURCES) - @mkdir -p build - $(WASMCC) $^ $(WASM_FLAGS) $(patsubst %,$(WASM_EXPORT_PREFIX)=%,$(WASM_DECODER_EXPORTS)) -o $@ -msimd128 -mbulk-memory - -js/meshopt_decoder.js: build/decoder_base.wasm build/decoder_simd.wasm - sed -i "s#Built with clang.*#Built with $$($(WASMCC) --version | head -n 1)#" $@ - sed -i "s#\(var wasm_base = \)\".*\";#\\1\"$$(cat build/decoder_base.wasm | python3 tools/wasmpack.py)\";#" $@ - sed -i "s#\(var wasm_simd = \)\".*\";#\\1\"$$(cat build/decoder_simd.wasm | python3 tools/wasmpack.py)\";#" $@ - -build/encoder.wasm: $(WASM_ENCODER_SOURCES) - @mkdir -p build - $(WASMCC) $^ $(WASM_FLAGS) $(patsubst %,$(WASM_EXPORT_PREFIX)=%,$(WASM_ENCODER_EXPORTS)) -lc -o $@ - -js/meshopt_encoder.js: build/encoder.wasm - sed -i "s#Built with clang.*#Built with $$($(WASMCC) --version | head -n 1)#" $@ - sed -i "s#\(var wasm = \)\".*\";#\\1\"$$(cat build/encoder.wasm | python3 tools/wasmpack.py)\";#" $@ - -build/simplifier.wasm: $(WASM_SIMPLIFIER_SOURCES) - @mkdir -p build - $(WASMCC) $^ $(WASM_FLAGS) $(patsubst %,$(WASM_EXPORT_PREFIX)=%,$(WASM_SIMPLIFIER_EXPORTS)) -lc -o $@ - -js/meshopt_simplifier.js: build/simplifier.wasm - sed -i "s#Built with clang.*#Built with $$($(WASMCC) --version | head -n 1)#" $@ - sed -i "s#\(var wasm = \)\".*\";#\\1\"$$(cat build/simplifier.wasm | python3 tools/wasmpack.py)\";#" $@ - -js/%.module.js: js/%.js - sed '/UMD-style export/,$$d' <$< >$@ - sed -n "s#\s*module.exports = \(.*\);#export { \\1 };#p" <$< >>$@ - -$(DEMO): $(DEMO_OBJECTS) $(LIBRARY) - $(CXX) $^ $(LDFLAGS) -o $@ - -vcachetuner: tools/vcachetuner.cpp $(BUILD)/tools/meshloader.cpp.o $(BUILD)/demo/miniz.cpp.o $(LIBRARY) - $(CXX) $^ -fopenmp $(CXXFLAGS) -std=c++11 $(LDFLAGS) -o $@ - -codecbench: tools/codecbench.cpp $(LIBRARY) - $(CXX) $^ $(CXXFLAGS) $(LDFLAGS) -o $@ - -codecbench.js: tools/codecbench.cpp ${LIBRARY_SOURCES} - emcc $^ -O3 -g -DNDEBUG -s TOTAL_MEMORY=268435456 -s SINGLE_FILE=1 -o $@ - -codecbench-simd.js: tools/codecbench.cpp ${LIBRARY_SOURCES} - emcc $^ -O3 -g -DNDEBUG -s TOTAL_MEMORY=268435456 -s SINGLE_FILE=1 -msimd128 -o $@ - -codecbench.wasm: tools/codecbench.cpp ${LIBRARY_SOURCES} - $(WASMCC) $^ -fno-exceptions --target=wasm32-wasi --sysroot=$(WASI_SDK) -lc++ -lc++abi -O3 -g -DNDEBUG -o $@ - -codecbench-simd.wasm: tools/codecbench.cpp ${LIBRARY_SOURCES} - $(WASMCC) $^ -fno-exceptions --target=wasm32-wasi --sysroot=$(WASI_SDK) -lc++ -lc++abi -O3 -g -DNDEBUG -msimd128 -o $@ - -codecfuzz: tools/codecfuzz.cpp src/vertexcodec.cpp src/indexcodec.cpp - $(CXX) $^ -fsanitize=fuzzer,address,undefined -O1 -g -o $@ - -$(LIBRARY): $(LIBRARY_OBJECTS) - ar rcs $@ $^ - -$(BUILD)/%.cpp.o: %.cpp - @mkdir -p $(dir $@) - $(CXX) $< $(CXXFLAGS) -c -MMD -MP -o $@ - -$(BUILD)/%.c.o: %.c - @mkdir -p $(dir $@) - $(CC) $< $(CFLAGS) -c -MMD -MP -o $@ - --include $(OBJECTS:.o=.d) - -clean: - rm -rf $(BUILD) - -.PHONY: all clean format diff --git a/Dependencies/meshoptimizer/README.md b/Dependencies/meshoptimizer/README.md deleted file mode 100644 index 43585b9..0000000 --- a/Dependencies/meshoptimizer/README.md +++ /dev/null @@ -1,372 +0,0 @@ -# 🐇 meshoptimizer [![Actions Status](https://github.com/zeux/meshoptimizer/workflows/build/badge.svg)](https://github.com/zeux/meshoptimizer/actions) [![codecov.io](https://codecov.io/github/zeux/meshoptimizer/coverage.svg?branch=master)](https://codecov.io/github/zeux/meshoptimizer?branch=master) ![MIT](https://img.shields.io/badge/license-MIT-blue.svg) [![GitHub](https://img.shields.io/badge/repo-github-green.svg)](https://github.com/zeux/meshoptimizer) - -## Purpose - -When a GPU renders triangle meshes, various stages of the GPU pipeline have to process vertex and index data. The efficiency of these stages depends on the data you feed to them; this library provides algorithms to help optimize meshes for these stages, as well as algorithms to reduce the mesh complexity and storage overhead. - -The library provides a C and C++ interface for all algorithms; you can use it from C/C++ or from other languages via FFI (such as P/Invoke). If you want to use this library from Rust, you should use [meshopt crate](https://crates.io/crates/meshopt). - -[gltfpack](gltf), which is a tool that can automatically optimize glTF files, is developed and distributed alongside the library. - -## Installing - -meshoptimizer is hosted on GitHub; you can download the latest release using git: - -``` -git clone -b v0.17 https://github.com/zeux/meshoptimizer.git -``` - -Alternatively you can [download the .zip archive from GitHub](https://github.com/zeux/meshoptimizer/archive/v0.15.zip). - -The library is also available as a package ([ArchLinux](https://aur.archlinux.org/packages/meshoptimizer/), [Debian](https://packages.debian.org/libmeshoptimizer), [Ubuntu](https://packages.ubuntu.com/libmeshoptimizer), [Vcpkg](https://github.com/microsoft/vcpkg/tree/master/ports/meshoptimizer)). - -### Installing gltfpack - -`gltfpack` is a CLI tool for optimizing meshes using meshoptimizer. - -You can download a pre-built binary for gltfpack on [Releases page](https://github.com/zeux/meshoptimizer/releases), or install [npm package](https://www.npmjs.com/package/gltfpack) as follows: - -``` -npm install -g gltfpack -``` - -You can also find prebuilt binaries of `gltfpack` built from master on [Actions page](https://github.com/zeux/meshoptimizer/actions). - -[Learn more about gltfpack](./gltf/README.md) - -## Building - -meshoptimizer is distributed as a set of C++ source files. To include it into your project, you can use one of the two options: - -* Use CMake to build the library (either as a standalone project or as part of your project) -* Add source files to your project's build system - -The source files are organized in such a way that you don't need to change your build-system settings, and you only need to add the files for the algorithms you use. - -## Pipeline - -When optimizing a mesh, you should typically feed it through a set of optimizations (the order is important!): - -1. Indexing -2. (optional, discussed last) Simplification -3. Vertex cache optimization -4. Overdraw optimization -5. Vertex fetch optimization -6. Vertex quantization -7. (optional) Vertex/index buffer compression - -## Indexing - -Most algorithms in this library assume that a mesh has a vertex buffer and an index buffer. For algorithms to work well and also for GPU to render your mesh efficiently, the vertex buffer has to have no redundant vertices; you can generate an index buffer from an unindexed vertex buffer or reindex an existing (potentially redundant) index buffer as follows: - -First, generate a remap table from your existing vertex (and, optionally, index) data: - -```c++ -size_t index_count = face_count * 3; -std::vector remap(index_count); // allocate temporary memory for the remap table -size_t vertex_count = meshopt_generateVertexRemap(&remap[0], NULL, index_count, &unindexed_vertices[0], index_count, sizeof(Vertex)); -``` - -Note that in this case we only have an unindexed vertex buffer; the remap table is generated based on binary equivalence of the input vertices, so the resulting mesh will render the same way. Binary equivalence considers all input bytes, including padding which should be zero-initialized if the vertex structure has gaps. - -After generating the remap table, you can allocate space for the target vertex buffer (`vertex_count` elements) and index buffer (`index_count` elements) and generate them: - -```c++ -meshopt_remapIndexBuffer(indices, NULL, index_count, &remap[0]); -meshopt_remapVertexBuffer(vertices, &unindexed_vertices[0], index_count, sizeof(Vertex), &remap[0]); -``` - -You can then further optimize the resulting buffers by calling the other functions on them in-place. - -## Vertex cache optimization - -When the GPU renders the mesh, it has to run the vertex shader for each vertex; usually GPUs have a built-in fixed size cache that stores the transformed vertices (the result of running the vertex shader), and uses this cache to reduce the number of vertex shader invocations. This cache is usually small, 16-32 vertices, and can have different replacement policies; to use this cache efficiently, you have to reorder your triangles to maximize the locality of reused vertex references like so: - -```c++ -meshopt_optimizeVertexCache(indices, indices, index_count, vertex_count); -``` - -## Overdraw optimization - -After transforming the vertices, GPU sends the triangles for rasterization which results in generating pixels that are usually first ran through the depth test, and pixels that pass it get the pixel shader executed to generate the final color. As pixel shaders get more expensive, it becomes more and more important to reduce overdraw. While in general improving overdraw requires view-dependent operations, this library provides an algorithm to reorder triangles to minimize the overdraw from all directions, which you should run after vertex cache optimization like this: - -```c++ -meshopt_optimizeOverdraw(indices, indices, index_count, &vertices[0].x, vertex_count, sizeof(Vertex), 1.05f); -``` - -The overdraw optimizer needs to read vertex positions as a float3 from the vertex; the code snippet above assumes that the vertex stores position as `float x, y, z`. - -When performing the overdraw optimization you have to specify a floating-point threshold parameter. The algorithm tries to maintain a balance between vertex cache efficiency and overdraw; the threshold determines how much the algorithm can compromise the vertex cache hit ratio, with 1.05 meaning that the resulting ratio should be at most 5% worse than before the optimization. - -## Vertex fetch optimization - -After the final triangle order has been established, we still can optimize the vertex buffer for memory efficiency. Before running the vertex shader GPU has to fetch the vertex attributes from the vertex buffer; the fetch is usually backed by a memory cache, and as such optimizing the data for the locality of memory access is important. You can do this by running this code: - -To optimize the index/vertex buffers for vertex fetch efficiency, call: - -```c++ -meshopt_optimizeVertexFetch(vertices, indices, index_count, vertices, vertex_count, sizeof(Vertex)); -``` - -This will reorder the vertices in the vertex buffer to try to improve the locality of reference, and rewrite the indices in place to match; if the vertex data is stored using multiple streams, you should use `meshopt_optimizeVertexFetchRemap` instead. This optimization has to be performed on the final index buffer since the optimal vertex order depends on the triangle order. - -Note that the algorithm does not try to model cache replacement precisely and instead just orders vertices in the order of use, which generally produces results that are close to optimal. - -## Vertex quantization - -To optimize memory bandwidth when fetching the vertex data even further, and to reduce the amount of memory required to store the mesh, it is often beneficial to quantize the vertex attributes to smaller types. While this optimization can technically run at any part of the pipeline (and sometimes doing quantization as the first step can improve indexing by merging almost identical vertices), it generally is easier to run this after all other optimizations since some of them require access to float3 positions. - -Quantization is usually domain specific; it's common to quantize normals using 3 8-bit integers but you can use higher-precision quantization (for example using 10 bits per component in a 10_10_10_2 format), or a different encoding to use just 2 components. For positions and texture coordinate data the two most common storage formats are half precision floats, and 16-bit normalized integers that encode the position relative to the AABB of the mesh or the UV bounding rectangle. - -The number of possible combinations here is very large but this library does provide the building blocks, specifically functions to quantize floating point values to normalized integers, as well as half-precision floats. For example, here's how you can quantize a normal: - -```c++ -unsigned int normal = - (meshopt_quantizeUnorm(v.nx, 10) << 20) | - (meshopt_quantizeUnorm(v.ny, 10) << 10) | - meshopt_quantizeUnorm(v.nz, 10); -``` - -and here's how you can quantize a position: - -```c++ -unsigned short px = meshopt_quantizeHalf(v.x); -unsigned short py = meshopt_quantizeHalf(v.y); -unsigned short pz = meshopt_quantizeHalf(v.z); -``` - -## Vertex/index buffer compression - -In case storage size or transmission bandwidth is of importance, you might want to additionally compress vertex and index data. While several mesh compression libraries, like Google Draco, are available, they typically are designed to maximize the compression ratio at the cost of disturbing the vertex/index order (which makes the meshes inefficient to render on GPU) or decompression performance. They also frequently don't support custom game-ready quantized vertex formats and thus require to re-quantize the data after loading it, introducing extra quantization errors and making decoding slower. - -Alternatively you can use general purpose compression libraries like zstd or Oodle to compress vertex/index data - however these compressors aren't designed to exploit redundancies in vertex/index data and as such compression rates can be unsatisfactory. - -To that end, this library provides algorithms to "encode" vertex and index data. The result of the encoding is generally significantly smaller than initial data, and remains compressible with general purpose compressors - so you can either store encoded data directly (for modest compression ratios and maximum decoding performance), or further compress it with zstd/Oodle to maximize compression ratio. - -> Note: this compression scheme is available as a glTF extension [EXT_meshopt_compression](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Vendor/EXT_meshopt_compression/README.md). - -To encode, you need to allocate target buffers (preferably using the worst case bound) and call encoding functions: - -```c++ -std::vector vbuf(meshopt_encodeVertexBufferBound(vertex_count, sizeof(Vertex))); -vbuf.resize(meshopt_encodeVertexBuffer(&vbuf[0], vbuf.size(), vertices, vertex_count, sizeof(Vertex))); - -std::vector ibuf(meshopt_encodeIndexBufferBound(index_count, vertex_count)); -ibuf.resize(meshopt_encodeIndexBuffer(&ibuf[0], ibuf.size(), indices, index_count)); -``` - -You can then either serialize `vbuf`/`ibuf` as is, or compress them further. To decode the data at runtime, call decoding functions: - -```c++ -int resvb = meshopt_decodeVertexBuffer(vertices, vertex_count, sizeof(Vertex), &vbuf[0], vbuf.size()); -int resib = meshopt_decodeIndexBuffer(indices, index_count, &ibuf[0], ibuf.size()); -assert(resvb == 0 && resib == 0); -``` - -Note that vertex encoding assumes that vertex buffer was optimized for vertex fetch, and that vertices are quantized; index encoding assumes that the vertex/index buffers were optimized for vertex cache and vertex fetch. Feeding unoptimized data into the encoders will produce poor compression ratios. Both codecs are lossless - the only lossy step is quantization that happens before encoding. - -To reduce the data size further, it's recommended to use `meshopt_optimizeVertexCacheStrip` instead of `meshopt_optimizeVertexCache` when optimizing for vertex cache, and to use new index codec version (`meshopt_encodeIndexVersion(1)`). This trades off some efficiency in vertex transform for smaller vertex and index data. - -Decoding functions are heavily optimized and can directly target write-combined memory; you can expect both decoders to run at 1-3 GB/s on modern desktop CPUs. Compression ratios depend on the data; vertex data compression ratio is typically around 2-4x (compared to already quantized data), index data compression ratio is around 5-6x (compared to raw 16-bit index data). General purpose lossless compressors can further improve on these results. - -Index buffer codec only supports triangle list topology; when encoding triangle strips or line lists, use `meshopt_encodeIndexSequence`/`meshopt_decodeIndexSequence` instead. This codec typically encodes indices into ~1 byte per index, but compressing the results further with a general purpose compressor can improve the results to 1-3 bits per index. - -The following guarantees on data compatibility are provided for point releases (*no* guarantees are given for development branch): - -- Data encoded with older versions of the library can always be decoded with newer versions; -- Data encoded with newer versions of the library can be decoded with older versions, provided that encoding versions are set correctly; if binary stability of encoded data is important, use `meshopt_encodeVertexVersion` and `meshopt_encodeIndexVersion` to 'pin' the data versions. - -Due to a very high decoding performance and compatibility with general purpose lossless compressors, the compression is a good fit for the use on the web. To that end, meshoptimizer provides both vertex and index decoders compiled into WebAssembly and wrapped into a module with JavaScript-friendly interface, `js/meshopt_decoder.js`, that you can use to decode meshes that were encoded offline: - -```js -// ready is a Promise that is resolved when (asynchronous) WebAssembly compilation finishes -await MeshoptDecoder.ready; - -// decode from *Data (Uint8Array) into *Buffer (Uint8Array) -MeshoptDecoder.decodeVertexBuffer(vertexBuffer, vertexCount, vertexSize, vertexData); -MeshoptDecoder.decodeIndexBuffer(indexBuffer, indexCount, indexSize, indexData); -``` - -[Usage example](https://meshoptimizer.org/demo/) is available, with source in `demo/index.html`; this example uses .GLB files encoded using `gltfpack`. - -## Point cloud compression - -The vertex encoding algorithms can be used to compress arbitrary streams of attribute data; one other use case besides triangle meshes is point cloud data. Typically point clouds come with position, color and possibly other attributes but don't have an implied point order. - -To compress point clouds efficiently, it's recommended to first preprocess the points by sorting them using the spatial sort algorithm: - -```c++ -std::vector remap(point_count); -meshopt_spatialSortRemap(&remap[0], positions, point_count, sizeof(vec3)); - -// for each attribute stream -meshopt_remapVertexBuffer(positions, positions, point_count, sizeof(vec3), &remap[0]); -``` - -After this the resulting arrays should be quantized (e.g. using 16-bit fixed point numbers for positions and 8-bit color components), and the result can be compressed using `meshopt_encodeVertexBuffer` as described in the previous section. To decompress, `meshopt_decodeVertexBuffer` will recover the quantized data that can be used directly or converted back to original floating-point data. The compression ratio depends on the nature of source data, for colored points it's typical to get 35-40 bits per point as a result. - -## Triangle strip conversion - -On most hardware, indexed triangle lists are the most efficient way to drive the GPU. However, in some cases triangle strips might prove beneficial: - -- On some older GPUs, triangle strips may be a bit more efficient to render -- On extremely memory constrained systems, index buffers for triangle strips could save a bit of memory - -This library provides an algorithm for converting a vertex cache optimized triangle list to a triangle strip: - -```c++ -std::vector strip(meshopt_stripifyBound(index_count)); -unsigned int restart_index = ~0u; -size_t strip_size = meshopt_stripify(&strip[0], indices, index_count, vertex_count, restart_index); -``` - -Typically you should expect triangle strips to have ~50-60% of indices compared to triangle lists (~1.5-1.8 indices per triangle) and have ~5% worse ACMR. -Note that triangle strips can be stitched with or without restart index support. Using restart indices can result in ~10% smaller index buffers, but on some GPUs restart indices may result in decreased performance. - -To reduce the triangle strip size further, it's recommended to use `meshopt_optimizeVertexCacheStrip` instead of `meshopt_optimizeVertexCache` when optimizing for vertex cache. This trades off some efficiency in vertex transform for smaller index buffers. - -## Deinterleaved geometry - -All of the examples above assume that geometry is represented as a single vertex buffer and a single index buffer. This requires storing all vertex attributes - position, normal, texture coordinate, skinning weights etc. - in a single contiguous struct. However, in some cases using multiple vertex streams may be preferable. In particular, if some passes require only positional data - such as depth pre-pass or shadow map - then it may be beneficial to split it from the rest of the vertex attributes to make sure the bandwidth use during these passes is optimal. On some mobile GPUs a position-only attribute stream also improves efficiency of tiling algorithms. - -Most of the functions in this library either only need the index buffer (such as vertex cache optimization) or only need positional information (such as overdraw optimization). However, several tasks require knowledge about all vertex attributes. - -For indexing, `meshopt_generateVertexRemap` assumes that there's just one vertex stream; when multiple vertex streams are used, it's necessary to use `meshopt_generateVertexRemapMulti` as follows: - -```c++ -meshopt_Stream streams[] = { - {&unindexed_pos[0], sizeof(float) * 3, sizeof(float) * 3}, - {&unindexed_nrm[0], sizeof(float) * 3, sizeof(float) * 3}, - {&unindexed_uv[0], sizeof(float) * 2, sizeof(float) * 2}, -}; - -std::vector remap(index_count); -size_t vertex_count = meshopt_generateVertexRemapMulti(&remap[0], NULL, index_count, index_count, streams, sizeof(streams) / sizeof(streams[0])); -``` - -After this `meshopt_remapVertexBuffer` needs to be called once for each vertex stream to produce the correctly reindexed stream. - -Instead of calling `meshopt_optimizeVertexFetch` for reordering vertices in a single vertex buffer for efficiency, calling `meshopt_optimizeVertexFetchRemap` and then calling `meshopt_remapVertexBuffer` for each stream again is recommended. - -Finally, when compressing vertex data, `meshopt_encodeVertexBuffer` should be used on each vertex stream separately - this allows the encoder to best utilize corellation between attribute values for different vertices. - -## Simplification - -All algorithms presented so far don't affect visual appearance at all, with the exception of quantization that has minimal controlled impact. However, fundamentally the most effective way at reducing the rendering or transmission cost of a mesh is to make the mesh simpler. - -This library provides two simplification algorithms that reduce the number of triangles in the mesh. Given a vertex and an index buffer, they generate a second index buffer that uses existing vertices in the vertex buffer. This index buffer can be used directly for rendering with the original vertex buffer (preferably after vertex cache optimization), or a new compact vertex/index buffer can be generated using `meshopt_optimizeVertexFetch` that uses the optimal number and order of vertices. - -The first simplification algorithm, `meshopt_simplify`, follows the topology of the original mesh in an attempt to preserve attribute seams, borders and overall appearance. For meshes with inconsistent topology or many seams, such as faceted meshes, it can result in simplifier getting "stuck" and not being able to simplify the mesh fully; it's recommended to preprocess the index buffer with `meshopt_generateShadowIndexBuffer` to discard any vertex attributes that aren't critical and can be rebuilt later such as normals. - -```c++ -float threshold = 0.2f; -size_t target_index_count = size_t(index_count * threshold); -float target_error = 1e-2f; - -std::vector lod(index_count); -float lod_error = 0.f; -lod.resize(meshopt_simplify(&lod[0], indices, index_count, &vertices[0].x, vertex_count, sizeof(Vertex), - target_index_count, target_error, &lod_error)); -``` - -Target error is an approximate measure of the deviation from the original mesh using distance normalized to 0..1 (so 1e-2f means that simplifier will try to maintain the error to be below 1% of the mesh extents). Note that because of topological restrictions and error bounds simplifier isn't guaranteed to reach the target index count and can stop earlier. - -The second simplification algorithm, `meshopt_simplifySloppy`, doesn't follow the topology of the original mesh. This means that it doesn't preserve attribute seams or borders, but it can collapse internal details that are too small to matter better because it can merge mesh features that are topologically disjoint but spatially close. - -```c++ -float threshold = 0.2f; -size_t target_index_count = size_t(index_count * threshold); -float target_error = 1e-1f; - -std::vector lod(index_count); -float lod_error = 0.f; -lod.resize(meshopt_simplifySloppy(&lod[0], indices, index_count, &vertices[0].x, vertex_count, sizeof(Vertex), - target_index_count, target_error, &lod_error)); -``` - -This algorithm will not stop early due to topology restrictions but can still do so if target index count can't be reached without introducing an error larger than target. It is 5-6x faster than `meshopt_simplify` when simplification ratio is large, and is able to reach ~20M triangles/sec on a desktop CPU (`meshopt_simplify` works at ~3M triangles/sec). - -When a sequence of LOD meshes is generated that all use the original vertex buffer, care must be taken to order vertices optimally to not penalize mobile GPU architectures that are only capable of transforming a sequential vertex buffer range. It's recommended in this case to first optimize each LOD for vertex cache, then assemble all LODs in one large index buffer starting from the coarsest LOD (the one with fewest triangles), and call `meshopt_optimizeVertexFetch` on the final large index buffer. This will make sure that coarser LODs require a smaller vertex range and are efficient wrt vertex fetch and transform. - -Both algorithms can also return the resulting normalized deviation that can be used to choose the correct level of detail based on screen size or solid angle; the error can be converted to world space by multiplying by the scaling factor returned by `meshopt_simplifyScale`. - -## Mesh shading - -Modern GPUs are beginning to deviate from the traditional rasterization model. NVidia GPUs starting from Turing and AMD GPUs starting from RDNA2 provide a new programmable geometry pipeline that, instead of being built around index buffers and vertex shaders, is built around mesh shaders - a new shader type that allows to provide a batch of work to the rasterizer. - -Using mesh shaders in context of traditional mesh rendering provides an opportunity to use a variety of optimization techniques, starting from more efficient vertex reuse, using various forms of culling (e.g. cluster frustum or occlusion culling) and in-memory compression to maximize the utilization of GPU hardware. Beyond traditional rendering mesh shaders provide a richer programming model that can synthesize new geometry more efficiently than common alternatives such as geometry shaders. Mesh shading can be accessed via Vulkan or Direct3D 12 APIs; please refer to [Introduction to Turing Mesh Shaders](https://developer.nvidia.com/blog/introduction-turing-mesh-shaders/) and [Mesh Shaders and Amplification Shaders: Reinventing the Geometry Pipeline](https://devblogs.microsoft.com/directx/coming-to-directx-12-mesh-shaders-and-amplification-shaders-reinventing-the-geometry-pipeline/) for additional information. - -To use mesh shaders for conventional rendering efficiently, geometry needs to be converted into a series of meshlets; each meshlet represents a small subset of the original mesh and comes with a small set of vertices and a separate micro-index buffer that references vertices in the meshlet. This information can be directly fed to the rasterizer from the mesh shader. This library provides algorithms to create meshlet data for a mesh, and - assuming geometry is static - can compute bounding information that can be used to perform cluster culling, a technique that can reject a meshlet if it's invisible on screen. - -To generate meshlet data, this library provides two algorithms - `meshopt_buildMeshletsScan`, which creates the meshlet data using a vertex cache-optimized index buffer as a starting point by greedily aggregating consecutive triangles until they go over the meshlet limits, and `meshopt_buildMeshlets`, which doesn't depend on any other algorithms and tries to balance topological efficiency (by maximizing vertex reuse inside meshlets) with culling efficiency (by minimizing meshlet radius and triangle direction divergence). `meshopt_buildMeshlets` is recommended in cases when the resulting meshlet data will be used in cluster culling algorithms. - -```c++ -const size_t max_vertices = 64; -const size_t max_triangles = 124; -const float cone_weight = 0.0f; - -size_t max_meshlets = meshopt_buildMeshletsBound(indices.size(), max_vertices, max_triangles); -std::vector meshlets(max_meshlets); -std::vector meshlet_vertices(max_meshlets * max_vertices); -std::vector meshlet_triangles(max_meshlets * max_triangles * 3); - -size_t meshlet_count = meshopt_buildMeshlets(meshlets.data(), meshlet_vertices.data(), meshlet_triangles.data(), indices.data(), - indices.size(), &vertices[0].x, vertices.size(), sizeof(Vertex), max_vertices, max_triangles, cone_weight); -``` - -To generate the meshlet data, `max_vertices` and `max_triangles` need to be set within limits supported by the hardware; for NVidia the values of 64 and 124 are recommended. `cone_weight` should be left as 0 if cluster cone culling is not used, and set to a value between 0 and 1 to balance cone culling efficiency with other forms of culling like frustum or occlusion culling. - -Each resulting meshlet refers to a portion of `meshlet_vertices` and `meshlet_triangles` arrays; this data can be uploaded to GPU and used directly after trimming: - -```c++ -const meshopt_Meshlet& last = meshlets[meshlet_count - 1]; - -meshlet_vertices.resize(last.vertex_offset + last.vertex_count); -meshlet_triangles.resize(last.triangle_offset + ((last.triangle_count * 3 + 3) & ~3)); -meshlets.resize(meshlet_count); -``` - -However depending on the application other strategies of storing the data can be useful; for example, `meshlet_vertices` serves as indices into the original vertex buffer but it might be worthwhile to generate a mini vertex buffer for each meshlet to remove the extra indirection when accessing vertex data, or it might be desirable to compress vertex data as vertices in each meshlet are likely to be very spatially coherent. - -After generating the meshlet data, it's also possible to generate extra data for each meshlet that can be saved and used at runtime to perform cluster culling, where each meshlet can be discarded if it's guaranteed to be invisible. To generate the data, `meshlet_computeMeshletBounds` can be used: - -```c++ -meshopt_Bounds bounds = meshopt_computeMeshletBounds(&meshlet_vertices[m.vertex_offset], &meshlet_triangles[m.triangle_offset], - m.triangle_count, &vertices[0].x, vertices.size(), sizeof(Vertex)); -``` - -The resulting `bounds` values can be used to perform frustum or occlusion culling using the bounding sphere, or cone culling using the cone axis/angle (which will reject the entire meshlet if all triangles are guaranteed to be back-facing from the camera point of view): - -```c++ -if (dot(normalize(cone_apex - camera_position), cone_axis) >= cone_cutoff) reject(); -``` - -## Efficiency analyzers - -While the only way to get precise performance data is to measure performance on the target GPU, it can be valuable to measure the impact of these optimization in a GPU-independent manner. To this end, the library provides analyzers for all three major optimization routines. For each optimization there is a corresponding analyze function, like `meshopt_analyzeOverdraw`, that returns a struct with statistics. - -`meshopt_analyzeVertexCache` returns vertex cache statistics. The common metric to use is ACMR - average cache miss ratio, which is the ratio of the total number of vertex invocations to the triangle count. The worst-case ACMR is 3 (GPU has to process 3 vertices for each triangle); on regular grids the optimal ACMR approaches 0.5. On real meshes it usually is in [0.5..1.5] range depending on the amount of vertex splits. One other useful metric is ATVR - average transformed vertex ratio - which represents the ratio of vertex shader invocations to the total vertices, and has the best case of 1.0 regardless of mesh topology (each vertex is transformed once). - -`meshopt_analyzeVertexFetch` returns vertex fetch statistics. The main metric it uses is overfetch - the ratio between the number of bytes read from the vertex buffer to the total number of bytes in the vertex buffer. Assuming non-redundant vertex buffers, the best case is 1.0 - each byte is fetched once. - -`meshopt_analyzeOverdraw` returns overdraw statistics. The main metric it uses is overdraw - the ratio between the number of pixel shader invocations to the total number of covered pixels, as measured from several different orthographic cameras. The best case for overdraw is 1.0 - each pixel is shaded once. - -Note that all analyzers use approximate models for the relevant GPU units, so the numbers you will get as the result are only a rough approximation of the actual performance. - -## Memory management - -Many algorithms allocate temporary memory to store intermediate results or accelerate processing. The amount of memory allocated is a function of various input parameters such as vertex count and index count. By default memory is allocated using `operator new` and `operator delete`; if these operators are overloaded by the application, the overloads will be used instead. Alternatively it's possible to specify custom allocation/deallocation functions using `meshopt_setAllocator`, e.g. - -```c++ -meshopt_setAllocator(malloc, free); -``` - -> Note that the library expects the allocation function to either throw in case of out-of-memory (in which case the exception will propagate to the caller) or abort, so technically the use of `malloc` above isn't safe. If you want to handle out-of-memory errors without using C++ exceptions, you can use `setjmp`/`longjmp` instead. - -Vertex and index decoders (`meshopt_decodeVertexBuffer`, `meshopt_decodeIndexBuffer`, `meshopt_decodeIndexSequence`) do not allocate memory and work completely within the buffer space provided via arguments. - -All functions have bounded stack usage that does not exceed 32 KB for any algorithms. - -## License - -This library is available to anybody free of charge, under the terms of MIT License (see LICENSE.md). diff --git a/Dependencies/meshoptimizer/codecov.yml b/Dependencies/meshoptimizer/codecov.yml deleted file mode 100644 index 3ed6eb4..0000000 --- a/Dependencies/meshoptimizer/codecov.yml +++ /dev/null @@ -1,11 +0,0 @@ -comment: false - -coverage: - status: - project: off - patch: off - -ignore: - - demo - - extern - - tools diff --git a/Dependencies/meshoptimizer/config.cmake.in b/Dependencies/meshoptimizer/config.cmake.in deleted file mode 100644 index 7a07fb7..0000000 --- a/Dependencies/meshoptimizer/config.cmake.in +++ /dev/null @@ -1,4 +0,0 @@ -@PACKAGE_INIT@ - -include("${CMAKE_CURRENT_LIST_DIR}/meshoptimizerTargets.cmake") -check_required_components(meshoptimizer) diff --git a/Dependencies/meshoptimizer/demo/ansi.c b/Dependencies/meshoptimizer/demo/ansi.c deleted file mode 100644 index 9e4f347..0000000 --- a/Dependencies/meshoptimizer/demo/ansi.c +++ /dev/null @@ -1,2 +0,0 @@ -/* This file makes sure the library can be used by C89 code */ -#include "../src/meshoptimizer.h" diff --git a/Dependencies/meshoptimizer/demo/demo.html b/Dependencies/meshoptimizer/demo/demo.html deleted file mode 100644 index 3b32876..0000000 --- a/Dependencies/meshoptimizer/demo/demo.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - meshoptimizer - demo - - - - - - - - - - - diff --git a/Dependencies/meshoptimizer/demo/index.html b/Dependencies/meshoptimizer/demo/index.html deleted file mode 100644 index 7f1063e..0000000 --- a/Dependencies/meshoptimizer/demo/index.html +++ /dev/null @@ -1,128 +0,0 @@ - - - - meshoptimizer - demo - - - - - - - - - - - diff --git a/Dependencies/meshoptimizer/demo/main.cpp b/Dependencies/meshoptimizer/demo/main.cpp deleted file mode 100644 index 1e46f89..0000000 --- a/Dependencies/meshoptimizer/demo/main.cpp +++ /dev/null @@ -1,1249 +0,0 @@ -#include "../src/meshoptimizer.h" - -#include -#include -#include -#include -#include - -#include - -#include "../extern/fast_obj.h" - -#define SDEFL_IMPLEMENTATION -#include "../extern/sdefl.h" - -// This file uses assert() to verify algorithm correctness -#undef NDEBUG -#include - -#if defined(__linux__) -double timestamp() -{ - timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return double(ts.tv_sec) + 1e-9 * double(ts.tv_nsec); -} -#elif defined(_WIN32) -struct LARGE_INTEGER -{ - __int64 QuadPart; -}; -extern "C" __declspec(dllimport) int __stdcall QueryPerformanceCounter(LARGE_INTEGER* lpPerformanceCount); -extern "C" __declspec(dllimport) int __stdcall QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency); - -double timestamp() -{ - LARGE_INTEGER freq, counter; - QueryPerformanceFrequency(&freq); - QueryPerformanceCounter(&counter); - return double(counter.QuadPart) / double(freq.QuadPart); -} -#else -double timestamp() -{ - return double(clock()) / double(CLOCKS_PER_SEC); -} -#endif - -const size_t kCacheSize = 16; - -struct Vertex -{ - float px, py, pz; - float nx, ny, nz; - float tx, ty; -}; - -struct Mesh -{ - std::vector vertices; - std::vector indices; -}; - -union Triangle -{ - Vertex v[3]; - char data[sizeof(Vertex) * 3]; -}; - -Mesh parseObj(const char* path, double& reindex) -{ - fastObjMesh* obj = fast_obj_read(path); - if (!obj) - { - printf("Error loading %s: file not found\n", path); - return Mesh(); - } - - size_t total_indices = 0; - - for (unsigned int i = 0; i < obj->face_count; ++i) - total_indices += 3 * (obj->face_vertices[i] - 2); - - std::vector vertices(total_indices); - - size_t vertex_offset = 0; - size_t index_offset = 0; - - for (unsigned int i = 0; i < obj->face_count; ++i) - { - for (unsigned int j = 0; j < obj->face_vertices[i]; ++j) - { - fastObjIndex gi = obj->indices[index_offset + j]; - - Vertex v = - { - obj->positions[gi.p * 3 + 0], - obj->positions[gi.p * 3 + 1], - obj->positions[gi.p * 3 + 2], - obj->normals[gi.n * 3 + 0], - obj->normals[gi.n * 3 + 1], - obj->normals[gi.n * 3 + 2], - obj->texcoords[gi.t * 2 + 0], - obj->texcoords[gi.t * 2 + 1], - }; - - // triangulate polygon on the fly; offset-3 is always the first polygon vertex - if (j >= 3) - { - vertices[vertex_offset + 0] = vertices[vertex_offset - 3]; - vertices[vertex_offset + 1] = vertices[vertex_offset - 1]; - vertex_offset += 2; - } - - vertices[vertex_offset] = v; - vertex_offset++; - } - - index_offset += obj->face_vertices[i]; - } - - fast_obj_destroy(obj); - - reindex = timestamp(); - - Mesh result; - - std::vector remap(total_indices); - - size_t total_vertices = meshopt_generateVertexRemap(&remap[0], NULL, total_indices, &vertices[0], total_indices, sizeof(Vertex)); - - result.indices.resize(total_indices); - meshopt_remapIndexBuffer(&result.indices[0], NULL, total_indices, &remap[0]); - - result.vertices.resize(total_vertices); - meshopt_remapVertexBuffer(&result.vertices[0], &vertices[0], total_indices, sizeof(Vertex), &remap[0]); - - return result; -} - -void dumpObj(const Mesh& mesh, bool recomputeNormals = false) -{ - std::vector normals; - - if (recomputeNormals) - { - normals.resize(mesh.vertices.size() * 3); - - for (size_t i = 0; i < mesh.indices.size(); i += 3) - { - unsigned int a = mesh.indices[i], b = mesh.indices[i + 1], c = mesh.indices[i + 2]; - - const Vertex& va = mesh.vertices[a]; - const Vertex& vb = mesh.vertices[b]; - const Vertex& vc = mesh.vertices[c]; - - float nx = (vb.py - va.py) * (vc.pz - va.pz) - (vb.pz - va.pz) * (vc.py - va.py); - float ny = (vb.pz - va.pz) * (vc.px - va.px) - (vb.px - va.px) * (vc.pz - va.pz); - float nz = (vb.px - va.px) * (vc.py - va.py) - (vb.py - va.py) * (vc.px - va.px); - - for (int k = 0; k < 3; ++k) - { - unsigned int index = mesh.indices[i + k]; - - normals[index * 3 + 0] += nx; - normals[index * 3 + 1] += ny; - normals[index * 3 + 2] += nz; - } - } - } - - for (size_t i = 0; i < mesh.vertices.size(); ++i) - { - const Vertex& v = mesh.vertices[i]; - - float nx = v.nx, ny = v.ny, nz = v.nz; - - if (recomputeNormals) - { - nx = normals[i * 3 + 0]; - ny = normals[i * 3 + 1]; - nz = normals[i * 3 + 2]; - - float l = sqrtf(nx * nx + ny * ny + nz * nz); - float s = l == 0.f ? 0.f : 1.f / l; - - nx *= s; - ny *= s; - nz *= s; - } - - fprintf(stderr, "v %f %f %f\n", v.px, v.py, v.pz); - fprintf(stderr, "vn %f %f %f\n", nx, ny, nz); - } - - for (size_t i = 0; i < mesh.indices.size(); i += 3) - { - unsigned int a = mesh.indices[i], b = mesh.indices[i + 1], c = mesh.indices[i + 2]; - - fprintf(stderr, "f %d %d %d\n", a + 1, b + 1, c + 1); - } -} - -bool isMeshValid(const Mesh& mesh) -{ - size_t index_count = mesh.indices.size(); - size_t vertex_count = mesh.vertices.size(); - - if (index_count % 3 != 0) - return false; - - const unsigned int* indices = &mesh.indices[0]; - - for (size_t i = 0; i < index_count; ++i) - if (indices[i] >= vertex_count) - return false; - - return true; -} - -bool rotateTriangle(Triangle& t) -{ - int c01 = memcmp(&t.v[0], &t.v[1], sizeof(Vertex)); - int c02 = memcmp(&t.v[0], &t.v[2], sizeof(Vertex)); - int c12 = memcmp(&t.v[1], &t.v[2], sizeof(Vertex)); - - if (c12 < 0 && c01 > 0) - { - // 1 is minimum, rotate 012 => 120 - Vertex tv = t.v[0]; - t.v[0] = t.v[1], t.v[1] = t.v[2], t.v[2] = tv; - } - else if (c02 > 0 && c12 > 0) - { - // 2 is minimum, rotate 012 => 201 - Vertex tv = t.v[2]; - t.v[2] = t.v[1], t.v[1] = t.v[0], t.v[0] = tv; - } - - return c01 != 0 && c02 != 0 && c12 != 0; -} - -unsigned int hashRange(const char* key, size_t len) -{ - // MurmurHash2 - const unsigned int m = 0x5bd1e995; - const int r = 24; - - unsigned int h = 0; - - while (len >= 4) - { - unsigned int k = *reinterpret_cast(key); - - k *= m; - k ^= k >> r; - k *= m; - - h *= m; - h ^= k; - - key += 4; - len -= 4; - } - - return h; -} - -unsigned int hashMesh(const Mesh& mesh) -{ - size_t triangle_count = mesh.indices.size() / 3; - - const Vertex* vertices = &mesh.vertices[0]; - const unsigned int* indices = &mesh.indices[0]; - - unsigned int h1 = 0; - unsigned int h2 = 0; - - for (size_t i = 0; i < triangle_count; ++i) - { - Triangle t; - t.v[0] = vertices[indices[i * 3 + 0]]; - t.v[1] = vertices[indices[i * 3 + 1]]; - t.v[2] = vertices[indices[i * 3 + 2]]; - - // skip degenerate triangles since some algorithms don't preserve them - if (rotateTriangle(t)) - { - unsigned int hash = hashRange(t.data, sizeof(t.data)); - - h1 ^= hash; - h2 += hash; - } - } - - return h1 * 0x5bd1e995 + h2; -} - -void optNone(Mesh& mesh) -{ - (void)mesh; -} - -void optRandomShuffle(Mesh& mesh) -{ - size_t triangle_count = mesh.indices.size() / 3; - - unsigned int* indices = &mesh.indices[0]; - - unsigned int rng = 0; - - for (size_t i = triangle_count - 1; i > 0; --i) - { - // Fisher-Yates shuffle - size_t j = rng % (i + 1); - - unsigned int t; - t = indices[3 * j + 0], indices[3 * j + 0] = indices[3 * i + 0], indices[3 * i + 0] = t; - t = indices[3 * j + 1], indices[3 * j + 1] = indices[3 * i + 1], indices[3 * i + 1] = t; - t = indices[3 * j + 2], indices[3 * j + 2] = indices[3 * i + 2], indices[3 * i + 2] = t; - - // LCG RNG, constants from Numerical Recipes - rng = rng * 1664525 + 1013904223; - } -} - -void optCache(Mesh& mesh) -{ - meshopt_optimizeVertexCache(&mesh.indices[0], &mesh.indices[0], mesh.indices.size(), mesh.vertices.size()); -} - -void optCacheFifo(Mesh& mesh) -{ - meshopt_optimizeVertexCacheFifo(&mesh.indices[0], &mesh.indices[0], mesh.indices.size(), mesh.vertices.size(), kCacheSize); -} - -void optCacheStrip(Mesh& mesh) -{ - meshopt_optimizeVertexCacheStrip(&mesh.indices[0], &mesh.indices[0], mesh.indices.size(), mesh.vertices.size()); -} - -void optOverdraw(Mesh& mesh) -{ - // use worst-case ACMR threshold so that overdraw optimizer can sort *all* triangles - // warning: this significantly deteriorates the vertex cache efficiency so it is not advised; look at optComplete for the recommended method - const float kThreshold = 3.f; - meshopt_optimizeOverdraw(&mesh.indices[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex), kThreshold); -} - -void optFetch(Mesh& mesh) -{ - meshopt_optimizeVertexFetch(&mesh.vertices[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0], mesh.vertices.size(), sizeof(Vertex)); -} - -void optFetchRemap(Mesh& mesh) -{ - // this produces results equivalent to optFetch, but can be used to remap multiple vertex streams - std::vector remap(mesh.vertices.size()); - meshopt_optimizeVertexFetchRemap(&remap[0], &mesh.indices[0], mesh.indices.size(), mesh.vertices.size()); - - meshopt_remapIndexBuffer(&mesh.indices[0], &mesh.indices[0], mesh.indices.size(), &remap[0]); - meshopt_remapVertexBuffer(&mesh.vertices[0], &mesh.vertices[0], mesh.vertices.size(), sizeof(Vertex), &remap[0]); -} - -void optComplete(Mesh& mesh) -{ - // vertex cache optimization should go first as it provides starting order for overdraw - meshopt_optimizeVertexCache(&mesh.indices[0], &mesh.indices[0], mesh.indices.size(), mesh.vertices.size()); - - // reorder indices for overdraw, balancing overdraw and vertex cache efficiency - const float kThreshold = 1.01f; // allow up to 1% worse ACMR to get more reordering opportunities for overdraw - meshopt_optimizeOverdraw(&mesh.indices[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex), kThreshold); - - // vertex fetch optimization should go last as it depends on the final index order - meshopt_optimizeVertexFetch(&mesh.vertices[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0], mesh.vertices.size(), sizeof(Vertex)); -} - -struct PackedVertex -{ - unsigned short px, py, pz; - unsigned short pw; // padding to 4b boundary - signed char nx, ny, nz, nw; - unsigned short tx, ty; -}; - -void packMesh(std::vector& pv, const std::vector& vertices) -{ - for (size_t i = 0; i < vertices.size(); ++i) - { - const Vertex& vi = vertices[i]; - PackedVertex& pvi = pv[i]; - - pvi.px = meshopt_quantizeHalf(vi.px); - pvi.py = meshopt_quantizeHalf(vi.py); - pvi.pz = meshopt_quantizeHalf(vi.pz); - pvi.pw = 0; - - pvi.nx = char(meshopt_quantizeSnorm(vi.nx, 8)); - pvi.ny = char(meshopt_quantizeSnorm(vi.ny, 8)); - pvi.nz = char(meshopt_quantizeSnorm(vi.nz, 8)); - pvi.nw = 0; - - pvi.tx = meshopt_quantizeHalf(vi.tx); - pvi.ty = meshopt_quantizeHalf(vi.ty); - } -} - -struct PackedVertexOct -{ - unsigned short px, py, pz; - signed char nu, nv; // octahedron encoded normal, aliases .pw - unsigned short tx, ty; -}; - -void packMesh(std::vector& pv, const std::vector& vertices) -{ - for (size_t i = 0; i < vertices.size(); ++i) - { - const Vertex& vi = vertices[i]; - PackedVertexOct& pvi = pv[i]; - - pvi.px = meshopt_quantizeHalf(vi.px); - pvi.py = meshopt_quantizeHalf(vi.py); - pvi.pz = meshopt_quantizeHalf(vi.pz); - - float nsum = fabsf(vi.nx) + fabsf(vi.ny) + fabsf(vi.nz); - float nx = vi.nx / nsum; - float ny = vi.ny / nsum; - float nz = vi.nz; - - float nu = nz >= 0 ? nx : (1 - fabsf(ny)) * (nx >= 0 ? 1 : -1); - float nv = nz >= 0 ? ny : (1 - fabsf(nx)) * (ny >= 0 ? 1 : -1); - - pvi.nu = char(meshopt_quantizeSnorm(nu, 8)); - pvi.nv = char(meshopt_quantizeSnorm(nv, 8)); - - pvi.tx = meshopt_quantizeHalf(vi.tx); - pvi.ty = meshopt_quantizeHalf(vi.ty); - } -} - -void simplify(const Mesh& mesh, float threshold = 0.2f) -{ - Mesh lod; - - double start = timestamp(); - - size_t target_index_count = size_t(mesh.indices.size() * threshold); - float target_error = 1e-2f; - float result_error = 0; - - lod.indices.resize(mesh.indices.size()); // note: simplify needs space for index_count elements in the destination array, not target_index_count - lod.indices.resize(meshopt_simplify(&lod.indices[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex), target_index_count, target_error, 0, &result_error)); - - lod.vertices.resize(lod.indices.size() < mesh.vertices.size() ? lod.indices.size() : mesh.vertices.size()); // note: this is just to reduce the cost of resize() - lod.vertices.resize(meshopt_optimizeVertexFetch(&lod.vertices[0], &lod.indices[0], lod.indices.size(), &mesh.vertices[0], mesh.vertices.size(), sizeof(Vertex))); - - double end = timestamp(); - - printf("%-9s: %d triangles => %d triangles (%.2f%% deviation) in %.2f msec\n", - "Simplify", - int(mesh.indices.size() / 3), int(lod.indices.size() / 3), - result_error * 100, - (end - start) * 1000); -} - -void simplifySloppy(const Mesh& mesh, float threshold = 0.2f) -{ - Mesh lod; - - double start = timestamp(); - - size_t target_index_count = size_t(mesh.indices.size() * threshold); - float target_error = 1e-1f; - float result_error = 0; - - lod.indices.resize(mesh.indices.size()); // note: simplify needs space for index_count elements in the destination array, not target_index_count - lod.indices.resize(meshopt_simplifySloppy(&lod.indices[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex), target_index_count, target_error, &result_error)); - - lod.vertices.resize(lod.indices.size() < mesh.vertices.size() ? lod.indices.size() : mesh.vertices.size()); // note: this is just to reduce the cost of resize() - lod.vertices.resize(meshopt_optimizeVertexFetch(&lod.vertices[0], &lod.indices[0], lod.indices.size(), &mesh.vertices[0], mesh.vertices.size(), sizeof(Vertex))); - - double end = timestamp(); - - printf("%-9s: %d triangles => %d triangles (%.2f%% deviation) in %.2f msec\n", - "SimplifyS", - int(mesh.indices.size() / 3), int(lod.indices.size() / 3), - result_error * 100, - (end - start) * 1000); -} - -void simplifyPoints(const Mesh& mesh, float threshold = 0.2f) -{ - double start = timestamp(); - - size_t target_vertex_count = size_t(mesh.vertices.size() * threshold); - - std::vector indices(target_vertex_count); - indices.resize(meshopt_simplifyPoints(&indices[0], &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex), target_vertex_count)); - - double end = timestamp(); - - printf("%-9s: %d points => %d points in %.2f msec\n", - "SimplifyP", - int(mesh.vertices.size()), int(indices.size()), (end - start) * 1000); -} - -void simplifyComplete(const Mesh& mesh) -{ - static const size_t lod_count = 5; - - double start = timestamp(); - - // generate 4 LOD levels (1-4), with each subsequent LOD using 70% triangles - // note that each LOD uses the same (shared) vertex buffer - std::vector lods[lod_count]; - - lods[0] = mesh.indices; - - for (size_t i = 1; i < lod_count; ++i) - { - std::vector& lod = lods[i]; - - float threshold = powf(0.7f, float(i)); - size_t target_index_count = size_t(mesh.indices.size() * threshold) / 3 * 3; - float target_error = 1e-2f; - - // we can simplify all the way from base level or from the last result - // simplifying from the base level sometimes produces better results, but simplifying from last level is faster - const std::vector& source = lods[i - 1]; - - if (source.size() < target_index_count) - target_index_count = source.size(); - - lod.resize(source.size()); - lod.resize(meshopt_simplify(&lod[0], &source[0], source.size(), &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex), target_index_count, target_error)); - } - - double middle = timestamp(); - - // optimize each individual LOD for vertex cache & overdraw - for (size_t i = 0; i < lod_count; ++i) - { - std::vector& lod = lods[i]; - - meshopt_optimizeVertexCache(&lod[0], &lod[0], lod.size(), mesh.vertices.size()); - meshopt_optimizeOverdraw(&lod[0], &lod[0], lod.size(), &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex), 1.0f); - } - - // concatenate all LODs into one IB - // note: the order of concatenation is important - since we optimize the entire IB for vertex fetch, - // putting coarse LODs first makes sure that the vertex range referenced by them is as small as possible - // some GPUs process the entire range referenced by the index buffer region so doing this optimizes the vertex transform - // cost for coarse LODs - // this order also produces much better vertex fetch cache coherency for coarse LODs (since they're essentially optimized first) - // somewhat surprisingly, the vertex fetch cache coherency for fine LODs doesn't seem to suffer that much. - size_t lod_index_offsets[lod_count] = {}; - size_t lod_index_counts[lod_count] = {}; - size_t total_index_count = 0; - - for (int i = lod_count - 1; i >= 0; --i) - { - lod_index_offsets[i] = total_index_count; - lod_index_counts[i] = lods[i].size(); - - total_index_count += lods[i].size(); - } - - std::vector indices(total_index_count); - - for (size_t i = 0; i < lod_count; ++i) - { - memcpy(&indices[lod_index_offsets[i]], &lods[i][0], lods[i].size() * sizeof(lods[i][0])); - } - - std::vector vertices = mesh.vertices; - - // vertex fetch optimization should go last as it depends on the final index order - // note that the order of LODs above affects vertex fetch results - meshopt_optimizeVertexFetch(&vertices[0], &indices[0], indices.size(), &vertices[0], vertices.size(), sizeof(Vertex)); - - double end = timestamp(); - - printf("%-9s: %d triangles => %d LOD levels down to %d triangles in %.2f msec, optimized in %.2f msec\n", - "SimplifyC", - int(lod_index_counts[0]) / 3, int(lod_count), int(lod_index_counts[lod_count - 1]) / 3, - (middle - start) * 1000, (end - middle) * 1000); - - // for using LOD data at runtime, in addition to vertices and indices you have to save lod_index_offsets/lod_index_counts. - - { - meshopt_VertexCacheStatistics vcs0 = meshopt_analyzeVertexCache(&indices[lod_index_offsets[0]], lod_index_counts[0], vertices.size(), kCacheSize, 0, 0); - meshopt_VertexFetchStatistics vfs0 = meshopt_analyzeVertexFetch(&indices[lod_index_offsets[0]], lod_index_counts[0], vertices.size(), sizeof(Vertex)); - meshopt_VertexCacheStatistics vcsN = meshopt_analyzeVertexCache(&indices[lod_index_offsets[lod_count - 1]], lod_index_counts[lod_count - 1], vertices.size(), kCacheSize, 0, 0); - meshopt_VertexFetchStatistics vfsN = meshopt_analyzeVertexFetch(&indices[lod_index_offsets[lod_count - 1]], lod_index_counts[lod_count - 1], vertices.size(), sizeof(Vertex)); - - typedef PackedVertexOct PV; - - std::vector pv(vertices.size()); - packMesh(pv, vertices); - - std::vector vbuf(meshopt_encodeVertexBufferBound(vertices.size(), sizeof(PV))); - vbuf.resize(meshopt_encodeVertexBuffer(&vbuf[0], vbuf.size(), &pv[0], vertices.size(), sizeof(PV))); - - std::vector ibuf(meshopt_encodeIndexBufferBound(indices.size(), vertices.size())); - ibuf.resize(meshopt_encodeIndexBuffer(&ibuf[0], ibuf.size(), &indices[0], indices.size())); - - printf("%-9s ACMR %f...%f Overfetch %f..%f Codec VB %.1f bits/vertex IB %.1f bits/triangle\n", - "", - vcs0.acmr, vcsN.acmr, vfs0.overfetch, vfsN.overfetch, - double(vbuf.size()) / double(vertices.size()) * 8, - double(ibuf.size()) / double(indices.size() / 3) * 8); - } -} - -void optimize(const Mesh& mesh, const char* name, void (*optf)(Mesh& mesh)) -{ - Mesh copy = mesh; - - double start = timestamp(); - optf(copy); - double end = timestamp(); - - assert(isMeshValid(copy)); - assert(hashMesh(mesh) == hashMesh(copy)); - - meshopt_VertexCacheStatistics vcs = meshopt_analyzeVertexCache(©.indices[0], copy.indices.size(), copy.vertices.size(), kCacheSize, 0, 0); - meshopt_VertexFetchStatistics vfs = meshopt_analyzeVertexFetch(©.indices[0], copy.indices.size(), copy.vertices.size(), sizeof(Vertex)); - meshopt_OverdrawStatistics os = meshopt_analyzeOverdraw(©.indices[0], copy.indices.size(), ©.vertices[0].px, copy.vertices.size(), sizeof(Vertex)); - - meshopt_VertexCacheStatistics vcs_nv = meshopt_analyzeVertexCache(©.indices[0], copy.indices.size(), copy.vertices.size(), 32, 32, 32); - meshopt_VertexCacheStatistics vcs_amd = meshopt_analyzeVertexCache(©.indices[0], copy.indices.size(), copy.vertices.size(), 14, 64, 128); - meshopt_VertexCacheStatistics vcs_intel = meshopt_analyzeVertexCache(©.indices[0], copy.indices.size(), copy.vertices.size(), 128, 0, 0); - - printf("%-9s: ACMR %f ATVR %f (NV %f AMD %f Intel %f) Overfetch %f Overdraw %f in %.2f msec\n", name, vcs.acmr, vcs.atvr, vcs_nv.atvr, vcs_amd.atvr, vcs_intel.atvr, vfs.overfetch, os.overdraw, (end - start) * 1000); -} - -template -size_t compress(const std::vector& data, int level = SDEFL_LVL_DEF) -{ - std::vector cbuf(sdefl_bound(int(data.size() * sizeof(T)))); - sdefl s = {}; - return sdeflate(&s, &cbuf[0], reinterpret_cast(&data[0]), int(data.size() * sizeof(T)), level); -} - -void encodeIndex(const Mesh& mesh, char desc) -{ - // allocate result outside of the timing loop to exclude memset() from decode timing - std::vector result(mesh.indices.size()); - - double start = timestamp(); - - std::vector buffer(meshopt_encodeIndexBufferBound(mesh.indices.size(), mesh.vertices.size())); - buffer.resize(meshopt_encodeIndexBuffer(&buffer[0], buffer.size(), &mesh.indices[0], mesh.indices.size())); - - double middle = timestamp(); - - int res = meshopt_decodeIndexBuffer(&result[0], mesh.indices.size(), &buffer[0], buffer.size()); - assert(res == 0); - (void)res; - - double end = timestamp(); - - size_t csize = compress(buffer); - - for (size_t i = 0; i < mesh.indices.size(); i += 3) - { - assert( - (result[i + 0] == mesh.indices[i + 0] && result[i + 1] == mesh.indices[i + 1] && result[i + 2] == mesh.indices[i + 2]) || - (result[i + 1] == mesh.indices[i + 0] && result[i + 2] == mesh.indices[i + 1] && result[i + 0] == mesh.indices[i + 2]) || - (result[i + 2] == mesh.indices[i + 0] && result[i + 0] == mesh.indices[i + 1] && result[i + 1] == mesh.indices[i + 2])); - } - - printf("IdxCodec%c: %.1f bits/triangle (post-deflate %.1f bits/triangle); encode %.2f msec, decode %.2f msec (%.2f GB/s)\n", - desc, - double(buffer.size() * 8) / double(mesh.indices.size() / 3), - double(csize * 8) / double(mesh.indices.size() / 3), - (middle - start) * 1000, - (end - middle) * 1000, - (double(result.size() * 4) / (1 << 30)) / (end - middle)); -} - -void encodeIndexSequence(const std::vector& data, size_t vertex_count, char desc) -{ - // allocate result outside of the timing loop to exclude memset() from decode timing - std::vector result(data.size()); - - double start = timestamp(); - - std::vector buffer(meshopt_encodeIndexSequenceBound(data.size(), vertex_count)); - buffer.resize(meshopt_encodeIndexSequence(&buffer[0], buffer.size(), &data[0], data.size())); - - double middle = timestamp(); - - int res = meshopt_decodeIndexSequence(&result[0], data.size(), &buffer[0], buffer.size()); - assert(res == 0); - (void)res; - - double end = timestamp(); - - size_t csize = compress(buffer); - - assert(memcmp(&data[0], &result[0], data.size() * sizeof(unsigned int)) == 0); - - printf("IdxCodec%c: %.1f bits/index (post-deflate %.1f bits/index); encode %.2f msec, decode %.2f msec (%.2f GB/s)\n", - desc, - double(buffer.size() * 8) / double(data.size()), - double(csize * 8) / double(data.size()), - (middle - start) * 1000, - (end - middle) * 1000, - (double(result.size() * 4) / (1 << 30)) / (end - middle)); -} - -template -void packVertex(const Mesh& mesh, const char* pvn) -{ - std::vector pv(mesh.vertices.size()); - packMesh(pv, mesh.vertices); - - size_t csize = compress(pv); - - printf("VtxPack%s : %.1f bits/vertex (post-deflate %.1f bits/vertex)\n", pvn, - double(pv.size() * sizeof(PV) * 8) / double(mesh.vertices.size()), - double(csize * 8) / double(mesh.vertices.size())); -} - -template -void encodeVertex(const Mesh& mesh, const char* pvn) -{ - std::vector pv(mesh.vertices.size()); - packMesh(pv, mesh.vertices); - - // allocate result outside of the timing loop to exclude memset() from decode timing - std::vector result(mesh.vertices.size()); - - double start = timestamp(); - - std::vector vbuf(meshopt_encodeVertexBufferBound(mesh.vertices.size(), sizeof(PV))); - vbuf.resize(meshopt_encodeVertexBuffer(&vbuf[0], vbuf.size(), &pv[0], mesh.vertices.size(), sizeof(PV))); - - double middle = timestamp(); - - int res = meshopt_decodeVertexBuffer(&result[0], mesh.vertices.size(), sizeof(PV), &vbuf[0], vbuf.size()); - assert(res == 0); - (void)res; - - double end = timestamp(); - - assert(memcmp(&pv[0], &result[0], pv.size() * sizeof(PV)) == 0); - - size_t csize = compress(vbuf); - - printf("VtxCodec%1s: %.1f bits/vertex (post-deflate %.1f bits/vertex); encode %.2f msec, decode %.2f msec (%.2f GB/s)\n", pvn, - double(vbuf.size() * 8) / double(mesh.vertices.size()), - double(csize * 8) / double(mesh.vertices.size()), - (middle - start) * 1000, - (end - middle) * 1000, - (double(result.size() * sizeof(PV)) / (1 << 30)) / (end - middle)); -} - -void stripify(const Mesh& mesh, bool use_restart, char desc) -{ - unsigned int restart_index = use_restart ? ~0u : 0; - - // note: input mesh is assumed to be optimized for vertex cache and vertex fetch - double start = timestamp(); - std::vector strip(meshopt_stripifyBound(mesh.indices.size())); - strip.resize(meshopt_stripify(&strip[0], &mesh.indices[0], mesh.indices.size(), mesh.vertices.size(), restart_index)); - double end = timestamp(); - - Mesh copy = mesh; - copy.indices.resize(meshopt_unstripify(©.indices[0], &strip[0], strip.size(), restart_index)); - assert(copy.indices.size() <= meshopt_unstripifyBound(strip.size())); - - assert(isMeshValid(copy)); - assert(hashMesh(mesh) == hashMesh(copy)); - - meshopt_VertexCacheStatistics vcs = meshopt_analyzeVertexCache(©.indices[0], mesh.indices.size(), mesh.vertices.size(), kCacheSize, 0, 0); - meshopt_VertexCacheStatistics vcs_nv = meshopt_analyzeVertexCache(©.indices[0], mesh.indices.size(), mesh.vertices.size(), 32, 32, 32); - meshopt_VertexCacheStatistics vcs_amd = meshopt_analyzeVertexCache(©.indices[0], mesh.indices.size(), mesh.vertices.size(), 14, 64, 128); - meshopt_VertexCacheStatistics vcs_intel = meshopt_analyzeVertexCache(©.indices[0], mesh.indices.size(), mesh.vertices.size(), 128, 0, 0); - - printf("Stripify%c: ACMR %f ATVR %f (NV %f AMD %f Intel %f); %d strip indices (%.1f%%) in %.2f msec\n", - desc, - vcs.acmr, vcs.atvr, vcs_nv.atvr, vcs_amd.atvr, vcs_intel.atvr, - int(strip.size()), double(strip.size()) / double(mesh.indices.size()) * 100, - (end - start) * 1000); -} - -void shadow(const Mesh& mesh) -{ - // note: input mesh is assumed to be optimized for vertex cache and vertex fetch - - double start = timestamp(); - // this index buffer can be used for position-only rendering using the same vertex data that the original index buffer uses - std::vector shadow_indices(mesh.indices.size()); - meshopt_generateShadowIndexBuffer(&shadow_indices[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0], mesh.vertices.size(), sizeof(float) * 3, sizeof(Vertex)); - double end = timestamp(); - - // while you can't optimize the vertex data after shadow IB was constructed, you can and should optimize the shadow IB for vertex cache - // this is valuable even if the original indices array was optimized for vertex cache! - meshopt_optimizeVertexCache(&shadow_indices[0], &shadow_indices[0], shadow_indices.size(), mesh.vertices.size()); - - meshopt_VertexCacheStatistics vcs = meshopt_analyzeVertexCache(&mesh.indices[0], mesh.indices.size(), mesh.vertices.size(), kCacheSize, 0, 0); - meshopt_VertexCacheStatistics vcss = meshopt_analyzeVertexCache(&shadow_indices[0], shadow_indices.size(), mesh.vertices.size(), kCacheSize, 0, 0); - - std::vector shadow_flags(mesh.vertices.size()); - size_t shadow_vertices = 0; - - for (size_t i = 0; i < shadow_indices.size(); ++i) - { - unsigned int index = shadow_indices[i]; - shadow_vertices += 1 - shadow_flags[index]; - shadow_flags[index] = 1; - } - - printf("ShadowIB : ACMR %f (%.2fx improvement); %d shadow vertices (%.2fx improvement) in %.2f msec\n", - vcss.acmr, double(vcs.vertices_transformed) / double(vcss.vertices_transformed), - int(shadow_vertices), double(mesh.vertices.size()) / double(shadow_vertices), - (end - start) * 1000); -} - -void meshlets(const Mesh& mesh, bool scan) -{ - const size_t max_vertices = 64; - const size_t max_triangles = 124; // NVidia-recommended 126, rounded down to a multiple of 4 - const float cone_weight = 0.5f; // note: should be set to 0 unless cone culling is used at runtime! - - // note: input mesh is assumed to be optimized for vertex cache and vertex fetch - double start = timestamp(); - size_t max_meshlets = meshopt_buildMeshletsBound(mesh.indices.size(), max_vertices, max_triangles); - std::vector meshlets(max_meshlets); - std::vector meshlet_vertices(max_meshlets * max_vertices); - std::vector meshlet_triangles(max_meshlets * max_triangles * 3); - - if (scan) - meshlets.resize(meshopt_buildMeshletsScan(&meshlets[0], &meshlet_vertices[0], &meshlet_triangles[0], &mesh.indices[0], mesh.indices.size(), mesh.vertices.size(), max_vertices, max_triangles)); - else - meshlets.resize(meshopt_buildMeshlets(&meshlets[0], &meshlet_vertices[0], &meshlet_triangles[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex), max_vertices, max_triangles, cone_weight)); - - if (meshlets.size()) - { - const meshopt_Meshlet& last = meshlets.back(); - - // this is an example of how to trim the vertex/triangle arrays when copying data out to GPU storage - meshlet_vertices.resize(last.vertex_offset + last.vertex_count); - meshlet_triangles.resize(last.triangle_offset + ((last.triangle_count * 3 + 3) & ~3)); - } - - double end = timestamp(); - - double avg_vertices = 0; - double avg_triangles = 0; - size_t not_full = 0; - - for (size_t i = 0; i < meshlets.size(); ++i) - { - const meshopt_Meshlet& m = meshlets[i]; - - avg_vertices += m.vertex_count; - avg_triangles += m.triangle_count; - not_full += m.vertex_count < max_vertices; - } - - avg_vertices /= double(meshlets.size()); - avg_triangles /= double(meshlets.size()); - - printf("Meshlets%c: %d meshlets (avg vertices %.1f, avg triangles %.1f, not full %d) in %.2f msec\n", - scan ? 'S' : ' ', - int(meshlets.size()), avg_vertices, avg_triangles, int(not_full), (end - start) * 1000); - - float camera[3] = {100, 100, 100}; - - size_t rejected = 0; - size_t rejected_s8 = 0; - size_t rejected_alt = 0; - size_t rejected_alt_s8 = 0; - size_t accepted = 0; - size_t accepted_s8 = 0; - - std::vector radii(meshlets.size()); - - double startc = timestamp(); - for (size_t i = 0; i < meshlets.size(); ++i) - { - const meshopt_Meshlet& m = meshlets[i]; - - meshopt_Bounds bounds = meshopt_computeMeshletBounds(&meshlet_vertices[m.vertex_offset], &meshlet_triangles[m.triangle_offset], m.triangle_count, &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex)); - - radii[i] = bounds.radius; - - // trivial accept: we can't ever backface cull this meshlet - accepted += (bounds.cone_cutoff >= 1); - accepted_s8 += (bounds.cone_cutoff_s8 >= 127); - - // perspective projection: dot(normalize(cone_apex - camera_position), cone_axis) > cone_cutoff - float mview[3] = {bounds.cone_apex[0] - camera[0], bounds.cone_apex[1] - camera[1], bounds.cone_apex[2] - camera[2]}; - float mviewlength = sqrtf(mview[0] * mview[0] + mview[1] * mview[1] + mview[2] * mview[2]); - - rejected += mview[0] * bounds.cone_axis[0] + mview[1] * bounds.cone_axis[1] + mview[2] * bounds.cone_axis[2] >= bounds.cone_cutoff * mviewlength; - rejected_s8 += mview[0] * (bounds.cone_axis_s8[0] / 127.f) + mview[1] * (bounds.cone_axis_s8[1] / 127.f) + mview[2] * (bounds.cone_axis_s8[2] / 127.f) >= (bounds.cone_cutoff_s8 / 127.f) * mviewlength; - - // alternative formulation for perspective projection that doesn't use apex (and uses cluster bounding sphere instead): - // dot(normalize(center - camera_position), cone_axis) > cone_cutoff + radius / length(center - camera_position) - float cview[3] = {bounds.center[0] - camera[0], bounds.center[1] - camera[1], bounds.center[2] - camera[2]}; - float cviewlength = sqrtf(cview[0] * cview[0] + cview[1] * cview[1] + cview[2] * cview[2]); - - rejected_alt += cview[0] * bounds.cone_axis[0] + cview[1] * bounds.cone_axis[1] + cview[2] * bounds.cone_axis[2] >= bounds.cone_cutoff * cviewlength + bounds.radius; - rejected_alt_s8 += cview[0] * (bounds.cone_axis_s8[0] / 127.f) + cview[1] * (bounds.cone_axis_s8[1] / 127.f) + cview[2] * (bounds.cone_axis_s8[2] / 127.f) >= (bounds.cone_cutoff_s8 / 127.f) * cviewlength + bounds.radius; - } - double endc = timestamp(); - - double radius_mean = 0; - - for (size_t i = 0; i < meshlets.size(); ++i) - radius_mean += radii[i]; - - radius_mean /= double(meshlets.size()); - - double radius_variance = 0; - - for (size_t i = 0; i < meshlets.size(); ++i) - radius_variance += (radii[i] - radius_mean) * (radii[i] - radius_mean); - - radius_variance /= double(meshlets.size() - 1); - - double radius_stddev = sqrt(radius_variance); - - size_t meshlets_std = 0; - - for (size_t i = 0; i < meshlets.size(); ++i) - meshlets_std += radii[i] < radius_mean + radius_stddev; - - printf("BoundDist: mean %f stddev %f; %.1f%% meshlets are under mean+stddev\n", - radius_mean, - radius_stddev, - double(meshlets_std) / double(meshlets.size()) * 100); - - printf("ConeCull : rejected apex %d (%.1f%%) / center %d (%.1f%%), trivially accepted %d (%.1f%%) in %.2f msec\n", - int(rejected), double(rejected) / double(meshlets.size()) * 100, - int(rejected_alt), double(rejected_alt) / double(meshlets.size()) * 100, - int(accepted), double(accepted) / double(meshlets.size()) * 100, - (endc - startc) * 1000); - printf("ConeCull8: rejected apex %d (%.1f%%) / center %d (%.1f%%), trivially accepted %d (%.1f%%) in %.2f msec\n", - int(rejected_s8), double(rejected_s8) / double(meshlets.size()) * 100, - int(rejected_alt_s8), double(rejected_alt_s8) / double(meshlets.size()) * 100, - int(accepted_s8), double(accepted_s8) / double(meshlets.size()) * 100, - (endc - startc) * 1000); -} - -void spatialSort(const Mesh& mesh) -{ - typedef PackedVertexOct PV; - - std::vector pv(mesh.vertices.size()); - packMesh(pv, mesh.vertices); - - double start = timestamp(); - - std::vector remap(mesh.vertices.size()); - meshopt_spatialSortRemap(&remap[0], &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex)); - - double end = timestamp(); - - meshopt_remapVertexBuffer(&pv[0], &pv[0], mesh.vertices.size(), sizeof(PV), &remap[0]); - - std::vector vbuf(meshopt_encodeVertexBufferBound(mesh.vertices.size(), sizeof(PV))); - vbuf.resize(meshopt_encodeVertexBuffer(&vbuf[0], vbuf.size(), &pv[0], mesh.vertices.size(), sizeof(PV))); - - size_t csize = compress(vbuf); - - printf("Spatial : %.1f bits/vertex (post-deflate %.1f bits/vertex); sort %.2f msec\n", - double(vbuf.size() * 8) / double(mesh.vertices.size()), - double(csize * 8) / double(mesh.vertices.size()), - (end - start) * 1000); -} - -void spatialSortTriangles(const Mesh& mesh) -{ - typedef PackedVertexOct PV; - - Mesh copy = mesh; - - double start = timestamp(); - - meshopt_spatialSortTriangles(©.indices[0], ©.indices[0], mesh.indices.size(), ©.vertices[0].px, copy.vertices.size(), sizeof(Vertex)); - - double end = timestamp(); - - meshopt_optimizeVertexCache(©.indices[0], ©.indices[0], copy.indices.size(), copy.vertices.size()); - meshopt_optimizeVertexFetch(©.vertices[0], ©.indices[0], copy.indices.size(), ©.vertices[0], copy.vertices.size(), sizeof(Vertex)); - - std::vector pv(mesh.vertices.size()); - packMesh(pv, copy.vertices); - - std::vector vbuf(meshopt_encodeVertexBufferBound(mesh.vertices.size(), sizeof(PV))); - vbuf.resize(meshopt_encodeVertexBuffer(&vbuf[0], vbuf.size(), &pv[0], mesh.vertices.size(), sizeof(PV))); - - std::vector ibuf(meshopt_encodeIndexBufferBound(mesh.indices.size(), mesh.vertices.size())); - ibuf.resize(meshopt_encodeIndexBuffer(&ibuf[0], ibuf.size(), ©.indices[0], mesh.indices.size())); - - size_t csizev = compress(vbuf); - size_t csizei = compress(ibuf); - - printf("SpatialT : %.1f bits/vertex (post-deflate %.1f bits/vertex); %.1f bits/triangle (post-deflate %.1f bits/triangle); sort %.2f msec\n", - double(vbuf.size() * 8) / double(mesh.vertices.size()), - double(csizev * 8) / double(mesh.vertices.size()), - double(ibuf.size() * 8) / double(mesh.indices.size() / 3), - double(csizei * 8) / double(mesh.indices.size() / 3), - (end - start) * 1000); -} - -void tessellationAdjacency(const Mesh& mesh) -{ - double start = timestamp(); - - // 12 indices per input triangle - std::vector tessib(mesh.indices.size() * 4); - meshopt_generateTessellationIndexBuffer(&tessib[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex)); - - double middle = timestamp(); - - // 6 indices per input triangle - std::vector adjib(mesh.indices.size() * 2); - meshopt_generateAdjacencyIndexBuffer(&adjib[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex)); - - double end = timestamp(); - - printf("Tesselltn: %d patches in %.2f msec\n", int(mesh.indices.size() / 3), (middle - start) * 1000); - printf("Adjacency: %d patches in %.2f msec\n", int(mesh.indices.size() / 3), (end - middle) * 1000); -} - -bool loadMesh(Mesh& mesh, const char* path) -{ - double start = timestamp(); - double middle; - mesh = parseObj(path, middle); - double end = timestamp(); - - if (mesh.vertices.empty()) - { - printf("Mesh %s is empty, skipping\n", path); - return false; - } - - printf("# %s: %d vertices, %d triangles; read in %.2f msec; indexed in %.2f msec\n", path, int(mesh.vertices.size()), int(mesh.indices.size() / 3), (middle - start) * 1000, (end - middle) * 1000); - return true; -} - -void processDeinterleaved(const char* path) -{ - // Most algorithms in the library work out of the box with deinterleaved geometry, but some require slightly special treatment; - // this code runs a simplified version of complete opt. pipeline using deinterleaved geo. There's no compression performed but you - // can trivially run it by quantizing all elements and running meshopt_encodeVertexBuffer once for each vertex stream. - fastObjMesh* obj = fast_obj_read(path); - if (!obj) - { - printf("Error loading %s: file not found\n", path); - return; - } - - size_t total_indices = 0; - - for (unsigned int i = 0; i < obj->face_count; ++i) - total_indices += 3 * (obj->face_vertices[i] - 2); - - std::vector unindexed_pos(total_indices * 3); - std::vector unindexed_nrm(total_indices * 3); - std::vector unindexed_uv(total_indices * 2); - - size_t vertex_offset = 0; - size_t index_offset = 0; - - for (unsigned int i = 0; i < obj->face_count; ++i) - { - for (unsigned int j = 0; j < obj->face_vertices[i]; ++j) - { - fastObjIndex gi = obj->indices[index_offset + j]; - - // triangulate polygon on the fly; offset-3 is always the first polygon vertex - if (j >= 3) - { - memcpy(&unindexed_pos[(vertex_offset + 0) * 3], &unindexed_pos[(vertex_offset - 3) * 3], 3 * sizeof(float)); - memcpy(&unindexed_nrm[(vertex_offset + 0) * 3], &unindexed_nrm[(vertex_offset - 3) * 3], 3 * sizeof(float)); - memcpy(&unindexed_uv[(vertex_offset + 0) * 2], &unindexed_uv[(vertex_offset - 3) * 2], 2 * sizeof(float)); - memcpy(&unindexed_pos[(vertex_offset + 1) * 3], &unindexed_pos[(vertex_offset - 1) * 3], 3 * sizeof(float)); - memcpy(&unindexed_nrm[(vertex_offset + 1) * 3], &unindexed_nrm[(vertex_offset - 1) * 3], 3 * sizeof(float)); - memcpy(&unindexed_uv[(vertex_offset + 1) * 2], &unindexed_uv[(vertex_offset - 1) * 2], 2 * sizeof(float)); - vertex_offset += 2; - } - - memcpy(&unindexed_pos[vertex_offset * 3], &obj->positions[gi.p * 3], 3 * sizeof(float)); - memcpy(&unindexed_nrm[vertex_offset * 3], &obj->normals[gi.n * 3], 3 * sizeof(float)); - memcpy(&unindexed_uv[vertex_offset * 2], &obj->texcoords[gi.t * 2], 2 * sizeof(float)); - vertex_offset++; - } - - index_offset += obj->face_vertices[i]; - } - - fast_obj_destroy(obj); - - double start = timestamp(); - - meshopt_Stream streams[] = { - {&unindexed_pos[0], sizeof(float) * 3, sizeof(float) * 3}, - {&unindexed_nrm[0], sizeof(float) * 3, sizeof(float) * 3}, - {&unindexed_uv[0], sizeof(float) * 2, sizeof(float) * 2}, - }; - - std::vector remap(total_indices); - - size_t total_vertices = meshopt_generateVertexRemapMulti(&remap[0], NULL, total_indices, total_indices, streams, sizeof(streams) / sizeof(streams[0])); - - std::vector indices(total_indices); - meshopt_remapIndexBuffer(&indices[0], NULL, total_indices, &remap[0]); - - std::vector pos(total_vertices * 3); - meshopt_remapVertexBuffer(&pos[0], &unindexed_pos[0], total_indices, sizeof(float) * 3, &remap[0]); - - std::vector nrm(total_vertices * 3); - meshopt_remapVertexBuffer(&nrm[0], &unindexed_nrm[0], total_indices, sizeof(float) * 3, &remap[0]); - - std::vector uv(total_vertices * 2); - meshopt_remapVertexBuffer(&uv[0], &unindexed_uv[0], total_indices, sizeof(float) * 2, &remap[0]); - - double reindex = timestamp(); - - meshopt_optimizeVertexCache(&indices[0], &indices[0], total_indices, total_vertices); - - meshopt_optimizeVertexFetchRemap(&remap[0], &indices[0], total_indices, total_vertices); - meshopt_remapVertexBuffer(&pos[0], &pos[0], total_vertices, sizeof(float) * 3, &remap[0]); - meshopt_remapVertexBuffer(&nrm[0], &nrm[0], total_vertices, sizeof(float) * 3, &remap[0]); - meshopt_remapVertexBuffer(&uv[0], &uv[0], total_vertices, sizeof(float) * 2, &remap[0]); - - double optimize = timestamp(); - - // note: since shadow index buffer is computed based on regular vertex/index buffer, the stream points at the indexed data - not unindexed_pos - meshopt_Stream shadow_stream = {&pos[0], sizeof(float) * 3, sizeof(float) * 3}; - - std::vector shadow_indices(total_indices); - meshopt_generateShadowIndexBufferMulti(&shadow_indices[0], &indices[0], total_indices, total_vertices, &shadow_stream, 1); - - meshopt_optimizeVertexCache(&shadow_indices[0], &shadow_indices[0], total_indices, total_vertices); - - double shadow = timestamp(); - - printf("Deintrlvd: %d vertices, reindexed in %.2f msec, optimized in %.2f msec, generated & optimized shadow indices in %.2f msec\n", - int(total_vertices), (reindex - start) * 1000, (optimize - reindex) * 1000, (shadow - optimize) * 1000); -} - -void process(const char* path) -{ - Mesh mesh; - if (!loadMesh(mesh, path)) - return; - - optimize(mesh, "Original", optNone); - optimize(mesh, "Random", optRandomShuffle); - optimize(mesh, "Cache", optCache); - optimize(mesh, "CacheFifo", optCacheFifo); - optimize(mesh, "CacheStrp", optCacheStrip); - optimize(mesh, "Overdraw", optOverdraw); - optimize(mesh, "Fetch", optFetch); - optimize(mesh, "FetchMap", optFetchRemap); - optimize(mesh, "Complete", optComplete); - - Mesh copy = mesh; - meshopt_optimizeVertexCache(©.indices[0], ©.indices[0], copy.indices.size(), copy.vertices.size()); - meshopt_optimizeVertexFetch(©.vertices[0], ©.indices[0], copy.indices.size(), ©.vertices[0], copy.vertices.size(), sizeof(Vertex)); - - Mesh copystrip = mesh; - meshopt_optimizeVertexCacheStrip(©strip.indices[0], ©strip.indices[0], copystrip.indices.size(), copystrip.vertices.size()); - meshopt_optimizeVertexFetch(©strip.vertices[0], ©strip.indices[0], copystrip.indices.size(), ©strip.vertices[0], copystrip.vertices.size(), sizeof(Vertex)); - - stripify(copy, false, ' '); - stripify(copy, true, 'R'); - stripify(copystrip, true, 'S'); - - meshlets(copy, false); - meshlets(copy, true); - - shadow(copy); - tessellationAdjacency(copy); - - encodeIndex(copy, ' '); - encodeIndex(copystrip, 'S'); - - std::vector strip(meshopt_stripifyBound(copystrip.indices.size())); - strip.resize(meshopt_stripify(&strip[0], ©strip.indices[0], copystrip.indices.size(), copystrip.vertices.size(), 0)); - - encodeIndexSequence(strip, copystrip.vertices.size(), 'D'); - - packVertex(copy, ""); - encodeVertex(copy, ""); - encodeVertex(copy, "O"); - - simplify(mesh); - simplifySloppy(mesh); - simplifyComplete(mesh); - simplifyPoints(mesh); - - spatialSort(mesh); - spatialSortTriangles(mesh); - - if (path) - processDeinterleaved(path); -} - -void processDev(const char* path) -{ - Mesh mesh; - if (!loadMesh(mesh, path)) - return; - - tessellationAdjacency(mesh); -} - -int main(int argc, char** argv) -{ - void runTests(); - - meshopt_encodeVertexVersion(0); - meshopt_encodeIndexVersion(1); - - if (argc == 1) - { - runTests(); - } - else - { - if (strcmp(argv[1], "-d") == 0) - { - for (int i = 2; i < argc; ++i) - { - processDev(argv[i]); - } - } - else - { - for (int i = 1; i < argc; ++i) - { - process(argv[i]); - } - - runTests(); - } - } -} diff --git a/Dependencies/meshoptimizer/demo/pirate.glb b/Dependencies/meshoptimizer/demo/pirate.glb deleted file mode 100644 index 261f086..0000000 Binary files a/Dependencies/meshoptimizer/demo/pirate.glb and /dev/null differ diff --git a/Dependencies/meshoptimizer/demo/tests.cpp b/Dependencies/meshoptimizer/demo/tests.cpp deleted file mode 100644 index 4d816af..0000000 --- a/Dependencies/meshoptimizer/demo/tests.cpp +++ /dev/null @@ -1,1147 +0,0 @@ -#include "../src/meshoptimizer.h" - -#include -#include -#include -#include - -#include - -// This file uses assert() to verify algorithm correctness -#undef NDEBUG -#include - -struct PV -{ - unsigned short px, py, pz; - unsigned char nu, nv; // octahedron encoded normal, aliases .pw - unsigned short tx, ty; -}; - -// note: 4 6 5 triangle here is a combo-breaker: -// we encode it without rotating, a=next, c=next - this means we do *not* bump next to 6 -// which means that the next triangle can't be encoded via next sequencing! -static const unsigned int kIndexBuffer[] = {0, 1, 2, 2, 1, 3, 4, 6, 5, 7, 8, 9}; - -static const unsigned char kIndexDataV0[] = { - 0xe0, 0xf0, 0x10, 0xfe, 0xff, 0xf0, 0x0c, 0xff, 0x02, 0x02, 0x02, 0x00, 0x76, 0x87, 0x56, 0x67, - 0x78, 0xa9, 0x86, 0x65, 0x89, 0x68, 0x98, 0x01, 0x69, 0x00, 0x00, // clang-format :-/ -}; - -// note: this exercises two features of v1 format, restarts (0 1 2) and last -static const unsigned int kIndexBufferTricky[] = {0, 1, 2, 2, 1, 3, 0, 1, 2, 2, 1, 5, 2, 1, 4}; - -static const unsigned char kIndexDataV1[] = { - 0xe1, 0xf0, 0x10, 0xfe, 0x1f, 0x3d, 0x00, 0x0a, 0x00, 0x76, 0x87, 0x56, 0x67, 0x78, 0xa9, 0x86, - 0x65, 0x89, 0x68, 0x98, 0x01, 0x69, 0x00, 0x00, // clang-format :-/ -}; - -static const unsigned int kIndexSequence[] = {0, 1, 51, 2, 49, 1000}; - -static const unsigned char kIndexSequenceV1[] = { - 0xd1, 0x00, 0x04, 0xcd, 0x01, 0x04, 0x07, 0x98, 0x1f, 0x00, 0x00, 0x00, 0x00, // clang-format :-/ -}; - -static const PV kVertexBuffer[] = { - {0, 0, 0, 0, 0, 0, 0}, - {300, 0, 0, 0, 0, 500, 0}, - {0, 300, 0, 0, 0, 0, 500}, - {300, 300, 0, 0, 0, 500, 500}, -}; - -static const unsigned char kVertexDataV0[] = { - 0xa0, 0x01, 0x3f, 0x00, 0x00, 0x00, 0x58, 0x57, 0x58, 0x01, 0x26, 0x00, 0x00, 0x00, 0x01, - 0x0c, 0x00, 0x00, 0x00, 0x58, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x3f, 0x00, 0x00, 0x00, 0x17, 0x18, 0x17, 0x01, 0x26, 0x00, 0x00, 0x00, 0x01, 0x0c, 0x00, - 0x00, 0x00, 0x17, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // clang-format :-/ -}; - -static void decodeIndexV0() -{ - const size_t index_count = sizeof(kIndexBuffer) / sizeof(kIndexBuffer[0]); - - std::vector buffer(kIndexDataV0, kIndexDataV0 + sizeof(kIndexDataV0)); - - unsigned int decoded[index_count]; - assert(meshopt_decodeIndexBuffer(decoded, index_count, &buffer[0], buffer.size()) == 0); - assert(memcmp(decoded, kIndexBuffer, sizeof(kIndexBuffer)) == 0); -} - -static void decodeIndexV1() -{ - const size_t index_count = sizeof(kIndexBufferTricky) / sizeof(kIndexBufferTricky[0]); - - std::vector buffer(kIndexDataV1, kIndexDataV1 + sizeof(kIndexDataV1)); - - unsigned int decoded[index_count]; - assert(meshopt_decodeIndexBuffer(decoded, index_count, &buffer[0], buffer.size()) == 0); - assert(memcmp(decoded, kIndexBufferTricky, sizeof(kIndexBufferTricky)) == 0); -} - -static void decodeIndex16() -{ - const size_t index_count = sizeof(kIndexBuffer) / sizeof(kIndexBuffer[0]); - const size_t vertex_count = 10; - - std::vector buffer(meshopt_encodeIndexBufferBound(index_count, vertex_count)); - buffer.resize(meshopt_encodeIndexBuffer(&buffer[0], buffer.size(), kIndexBuffer, index_count)); - - unsigned short decoded[index_count]; - assert(meshopt_decodeIndexBuffer(decoded, index_count, &buffer[0], buffer.size()) == 0); - - for (size_t i = 0; i < index_count; ++i) - assert(decoded[i] == kIndexBuffer[i]); -} - -static void encodeIndexMemorySafe() -{ - const size_t index_count = sizeof(kIndexBuffer) / sizeof(kIndexBuffer[0]); - const size_t vertex_count = 10; - - std::vector buffer(meshopt_encodeIndexBufferBound(index_count, vertex_count)); - buffer.resize(meshopt_encodeIndexBuffer(&buffer[0], buffer.size(), kIndexBuffer, index_count)); - - // check that encode is memory-safe; note that we reallocate the buffer for each try to make sure ASAN can verify buffer access - for (size_t i = 0; i <= buffer.size(); ++i) - { - std::vector shortbuffer(i); - size_t result = meshopt_encodeIndexBuffer(i == 0 ? 0 : &shortbuffer[0], i, kIndexBuffer, index_count); - - if (i == buffer.size()) - assert(result == buffer.size()); - else - assert(result == 0); - } -} - -static void decodeIndexMemorySafe() -{ - const size_t index_count = sizeof(kIndexBuffer) / sizeof(kIndexBuffer[0]); - const size_t vertex_count = 10; - - std::vector buffer(meshopt_encodeIndexBufferBound(index_count, vertex_count)); - buffer.resize(meshopt_encodeIndexBuffer(&buffer[0], buffer.size(), kIndexBuffer, index_count)); - - // check that decode is memory-safe; note that we reallocate the buffer for each try to make sure ASAN can verify buffer access - unsigned int decoded[index_count]; - - for (size_t i = 0; i <= buffer.size(); ++i) - { - std::vector shortbuffer(buffer.begin(), buffer.begin() + i); - int result = meshopt_decodeIndexBuffer(decoded, index_count, i == 0 ? 0 : &shortbuffer[0], i); - - if (i == buffer.size()) - assert(result == 0); - else - assert(result < 0); - } -} - -static void decodeIndexRejectExtraBytes() -{ - const size_t index_count = sizeof(kIndexBuffer) / sizeof(kIndexBuffer[0]); - const size_t vertex_count = 10; - - std::vector buffer(meshopt_encodeIndexBufferBound(index_count, vertex_count)); - buffer.resize(meshopt_encodeIndexBuffer(&buffer[0], buffer.size(), kIndexBuffer, index_count)); - - // check that decoder doesn't accept extra bytes after a valid stream - std::vector largebuffer(buffer); - largebuffer.push_back(0); - - unsigned int decoded[index_count]; - assert(meshopt_decodeIndexBuffer(decoded, index_count, &largebuffer[0], largebuffer.size()) < 0); -} - -static void decodeIndexRejectMalformedHeaders() -{ - const size_t index_count = sizeof(kIndexBuffer) / sizeof(kIndexBuffer[0]); - const size_t vertex_count = 10; - - std::vector buffer(meshopt_encodeIndexBufferBound(index_count, vertex_count)); - buffer.resize(meshopt_encodeIndexBuffer(&buffer[0], buffer.size(), kIndexBuffer, index_count)); - - // check that decoder doesn't accept malformed headers - std::vector brokenbuffer(buffer); - brokenbuffer[0] = 0; - - unsigned int decoded[index_count]; - assert(meshopt_decodeIndexBuffer(decoded, index_count, &brokenbuffer[0], brokenbuffer.size()) < 0); -} - -static void decodeIndexRejectInvalidVersion() -{ - const size_t index_count = sizeof(kIndexBuffer) / sizeof(kIndexBuffer[0]); - const size_t vertex_count = 10; - - std::vector buffer(meshopt_encodeIndexBufferBound(index_count, vertex_count)); - buffer.resize(meshopt_encodeIndexBuffer(&buffer[0], buffer.size(), kIndexBuffer, index_count)); - - // check that decoder doesn't accept invalid version - std::vector brokenbuffer(buffer); - brokenbuffer[0] |= 0x0f; - - unsigned int decoded[index_count]; - assert(meshopt_decodeIndexBuffer(decoded, index_count, &brokenbuffer[0], brokenbuffer.size()) < 0); -} - -static void decodeIndexMalformedVByte() -{ - const unsigned char input[] = { - 0xe1, 0x20, 0x20, 0x20, 0xff, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0xff, 0xff, 0xff, 0xff, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, // clang-format :-/ - }; - - unsigned int decoded[66]; - assert(meshopt_decodeIndexBuffer(decoded, 66, input, sizeof(input)) < 0); -} - -static void roundtripIndexTricky() -{ - const size_t index_count = sizeof(kIndexBufferTricky) / sizeof(kIndexBufferTricky[0]); - const size_t vertex_count = 6; - - std::vector buffer(meshopt_encodeIndexBufferBound(index_count, vertex_count)); - buffer.resize(meshopt_encodeIndexBuffer(&buffer[0], buffer.size(), kIndexBufferTricky, index_count)); - - unsigned int decoded[index_count]; - assert(meshopt_decodeIndexBuffer(decoded, index_count, &buffer[0], buffer.size()) == 0); - assert(memcmp(decoded, kIndexBufferTricky, sizeof(kIndexBufferTricky)) == 0); -} - -static void encodeIndexEmpty() -{ - std::vector buffer(meshopt_encodeIndexBufferBound(0, 0)); - buffer.resize(meshopt_encodeIndexBuffer(&buffer[0], buffer.size(), NULL, 0)); - - assert(meshopt_decodeIndexBuffer(static_cast(NULL), 0, &buffer[0], buffer.size()) == 0); -} - -static void decodeIndexSequence() -{ - const size_t index_count = sizeof(kIndexSequence) / sizeof(kIndexSequence[0]); - - std::vector buffer(kIndexSequenceV1, kIndexSequenceV1 + sizeof(kIndexSequenceV1)); - - unsigned int decoded[index_count]; - assert(meshopt_decodeIndexSequence(decoded, index_count, &buffer[0], buffer.size()) == 0); - assert(memcmp(decoded, kIndexSequence, sizeof(kIndexSequence)) == 0); -} - -static void decodeIndexSequence16() -{ - const size_t index_count = sizeof(kIndexSequence) / sizeof(kIndexSequence[0]); - const size_t vertex_count = 1001; - - std::vector buffer(meshopt_encodeIndexSequenceBound(index_count, vertex_count)); - buffer.resize(meshopt_encodeIndexSequence(&buffer[0], buffer.size(), kIndexSequence, index_count)); - - unsigned short decoded[index_count]; - assert(meshopt_decodeIndexSequence(decoded, index_count, &buffer[0], buffer.size()) == 0); - - for (size_t i = 0; i < index_count; ++i) - assert(decoded[i] == kIndexSequence[i]); -} - -static void encodeIndexSequenceMemorySafe() -{ - const size_t index_count = sizeof(kIndexSequence) / sizeof(kIndexSequence[0]); - const size_t vertex_count = 1001; - - std::vector buffer(meshopt_encodeIndexSequenceBound(index_count, vertex_count)); - buffer.resize(meshopt_encodeIndexSequence(&buffer[0], buffer.size(), kIndexSequence, index_count)); - - // check that encode is memory-safe; note that we reallocate the buffer for each try to make sure ASAN can verify buffer access - for (size_t i = 0; i <= buffer.size(); ++i) - { - std::vector shortbuffer(i); - size_t result = meshopt_encodeIndexSequence(i == 0 ? 0 : &shortbuffer[0], i, kIndexSequence, index_count); - - if (i == buffer.size()) - assert(result == buffer.size()); - else - assert(result == 0); - } -} - -static void decodeIndexSequenceMemorySafe() -{ - const size_t index_count = sizeof(kIndexSequence) / sizeof(kIndexSequence[0]); - const size_t vertex_count = 1001; - - std::vector buffer(meshopt_encodeIndexSequenceBound(index_count, vertex_count)); - buffer.resize(meshopt_encodeIndexSequence(&buffer[0], buffer.size(), kIndexSequence, index_count)); - - // check that decode is memory-safe; note that we reallocate the buffer for each try to make sure ASAN can verify buffer access - unsigned int decoded[index_count]; - - for (size_t i = 0; i <= buffer.size(); ++i) - { - std::vector shortbuffer(buffer.begin(), buffer.begin() + i); - int result = meshopt_decodeIndexSequence(decoded, index_count, i == 0 ? 0 : &shortbuffer[0], i); - - if (i == buffer.size()) - assert(result == 0); - else - assert(result < 0); - } -} - -static void decodeIndexSequenceRejectExtraBytes() -{ - const size_t index_count = sizeof(kIndexSequence) / sizeof(kIndexSequence[0]); - const size_t vertex_count = 1001; - - std::vector buffer(meshopt_encodeIndexSequenceBound(index_count, vertex_count)); - buffer.resize(meshopt_encodeIndexSequence(&buffer[0], buffer.size(), kIndexSequence, index_count)); - - // check that decoder doesn't accept extra bytes after a valid stream - std::vector largebuffer(buffer); - largebuffer.push_back(0); - - unsigned int decoded[index_count]; - assert(meshopt_decodeIndexSequence(decoded, index_count, &largebuffer[0], largebuffer.size()) < 0); -} - -static void decodeIndexSequenceRejectMalformedHeaders() -{ - const size_t index_count = sizeof(kIndexSequence) / sizeof(kIndexSequence[0]); - const size_t vertex_count = 1001; - - std::vector buffer(meshopt_encodeIndexSequenceBound(index_count, vertex_count)); - buffer.resize(meshopt_encodeIndexSequence(&buffer[0], buffer.size(), kIndexSequence, index_count)); - - // check that decoder doesn't accept malformed headers - std::vector brokenbuffer(buffer); - brokenbuffer[0] = 0; - - unsigned int decoded[index_count]; - assert(meshopt_decodeIndexSequence(decoded, index_count, &brokenbuffer[0], brokenbuffer.size()) < 0); -} - -static void decodeIndexSequenceRejectInvalidVersion() -{ - const size_t index_count = sizeof(kIndexSequence) / sizeof(kIndexSequence[0]); - const size_t vertex_count = 1001; - - std::vector buffer(meshopt_encodeIndexSequenceBound(index_count, vertex_count)); - buffer.resize(meshopt_encodeIndexSequence(&buffer[0], buffer.size(), kIndexSequence, index_count)); - - // check that decoder doesn't accept invalid version - std::vector brokenbuffer(buffer); - brokenbuffer[0] |= 0x0f; - - unsigned int decoded[index_count]; - assert(meshopt_decodeIndexSequence(decoded, index_count, &brokenbuffer[0], brokenbuffer.size()) < 0); -} - -static void encodeIndexSequenceEmpty() -{ - std::vector buffer(meshopt_encodeIndexSequenceBound(0, 0)); - buffer.resize(meshopt_encodeIndexSequence(&buffer[0], buffer.size(), NULL, 0)); - - assert(meshopt_decodeIndexSequence(static_cast(NULL), 0, &buffer[0], buffer.size()) == 0); -} - -static void decodeVertexV0() -{ - const size_t vertex_count = sizeof(kVertexBuffer) / sizeof(kVertexBuffer[0]); - - std::vector buffer(kVertexDataV0, kVertexDataV0 + sizeof(kVertexDataV0)); - - PV decoded[vertex_count]; - assert(meshopt_decodeVertexBuffer(decoded, vertex_count, sizeof(PV), &buffer[0], buffer.size()) == 0); - assert(memcmp(decoded, kVertexBuffer, sizeof(kVertexBuffer)) == 0); -} - -static void encodeVertexMemorySafe() -{ - const size_t vertex_count = sizeof(kVertexBuffer) / sizeof(kVertexBuffer[0]); - - std::vector buffer(meshopt_encodeVertexBufferBound(vertex_count, sizeof(PV))); - buffer.resize(meshopt_encodeVertexBuffer(&buffer[0], buffer.size(), kVertexBuffer, vertex_count, sizeof(PV))); - - // check that encode is memory-safe; note that we reallocate the buffer for each try to make sure ASAN can verify buffer access - for (size_t i = 0; i <= buffer.size(); ++i) - { - std::vector shortbuffer(i); - size_t result = meshopt_encodeVertexBuffer(i == 0 ? 0 : &shortbuffer[0], i, kVertexBuffer, vertex_count, sizeof(PV)); - - if (i == buffer.size()) - assert(result == buffer.size()); - else - assert(result == 0); - } -} - -static void decodeVertexMemorySafe() -{ - const size_t vertex_count = sizeof(kVertexBuffer) / sizeof(kVertexBuffer[0]); - - std::vector buffer(meshopt_encodeVertexBufferBound(vertex_count, sizeof(PV))); - buffer.resize(meshopt_encodeVertexBuffer(&buffer[0], buffer.size(), kVertexBuffer, vertex_count, sizeof(PV))); - - // check that decode is memory-safe; note that we reallocate the buffer for each try to make sure ASAN can verify buffer access - PV decoded[vertex_count]; - - for (size_t i = 0; i <= buffer.size(); ++i) - { - std::vector shortbuffer(buffer.begin(), buffer.begin() + i); - int result = meshopt_decodeVertexBuffer(decoded, vertex_count, sizeof(PV), i == 0 ? 0 : &shortbuffer[0], i); - (void)result; - - if (i == buffer.size()) - assert(result == 0); - else - assert(result < 0); - } -} - -static void decodeVertexRejectExtraBytes() -{ - const size_t vertex_count = sizeof(kVertexBuffer) / sizeof(kVertexBuffer[0]); - - std::vector buffer(meshopt_encodeVertexBufferBound(vertex_count, sizeof(PV))); - buffer.resize(meshopt_encodeVertexBuffer(&buffer[0], buffer.size(), kVertexBuffer, vertex_count, sizeof(PV))); - - // check that decoder doesn't accept extra bytes after a valid stream - std::vector largebuffer(buffer); - largebuffer.push_back(0); - - PV decoded[vertex_count]; - assert(meshopt_decodeVertexBuffer(decoded, vertex_count, sizeof(PV), &largebuffer[0], largebuffer.size()) < 0); -} - -static void decodeVertexRejectMalformedHeaders() -{ - const size_t vertex_count = sizeof(kVertexBuffer) / sizeof(kVertexBuffer[0]); - - std::vector buffer(meshopt_encodeVertexBufferBound(vertex_count, sizeof(PV))); - buffer.resize(meshopt_encodeVertexBuffer(&buffer[0], buffer.size(), kVertexBuffer, vertex_count, sizeof(PV))); - - // check that decoder doesn't accept malformed headers - std::vector brokenbuffer(buffer); - brokenbuffer[0] = 0; - - PV decoded[vertex_count]; - assert(meshopt_decodeVertexBuffer(decoded, vertex_count, sizeof(PV), &brokenbuffer[0], brokenbuffer.size()) < 0); -} - -static void decodeVertexBitGroups() -{ - unsigned char data[16 * 4]; - - // this tests 0/2/4/8 bit groups in one stream - for (size_t i = 0; i < 16; ++i) - { - data[i * 4 + 0] = 0; - data[i * 4 + 1] = (unsigned char)(i * 1); - data[i * 4 + 2] = (unsigned char)(i * 2); - data[i * 4 + 3] = (unsigned char)(i * 8); - } - - std::vector buffer(meshopt_encodeVertexBufferBound(16, 4)); - buffer.resize(meshopt_encodeVertexBuffer(&buffer[0], buffer.size(), data, 16, 4)); - - unsigned char decoded[16 * 4]; - assert(meshopt_decodeVertexBuffer(decoded, 16, 4, &buffer[0], buffer.size()) == 0); - assert(memcmp(decoded, data, sizeof(data)) == 0); -} - -static void decodeVertexBitGroupSentinels() -{ - unsigned char data[16 * 4]; - - // this tests 0/2/4/8 bit groups and sentinels in one stream - for (size_t i = 0; i < 16; ++i) - { - if (i == 7 || i == 13) - { - data[i * 4 + 0] = 42; - data[i * 4 + 1] = 42; - data[i * 4 + 2] = 42; - data[i * 4 + 3] = 42; - } - else - { - data[i * 4 + 0] = 0; - data[i * 4 + 1] = (unsigned char)(i * 1); - data[i * 4 + 2] = (unsigned char)(i * 2); - data[i * 4 + 3] = (unsigned char)(i * 8); - } - } - - std::vector buffer(meshopt_encodeVertexBufferBound(16, 4)); - buffer.resize(meshopt_encodeVertexBuffer(&buffer[0], buffer.size(), data, 16, 4)); - - unsigned char decoded[16 * 4]; - assert(meshopt_decodeVertexBuffer(decoded, 16, 4, &buffer[0], buffer.size()) == 0); - assert(memcmp(decoded, data, sizeof(data)) == 0); -} - -static void decodeVertexLarge() -{ - unsigned char data[128 * 4]; - - // this tests 0/2/4/8 bit groups in one stream - for (size_t i = 0; i < 128; ++i) - { - data[i * 4 + 0] = 0; - data[i * 4 + 1] = (unsigned char)(i * 1); - data[i * 4 + 2] = (unsigned char)(i * 2); - data[i * 4 + 3] = (unsigned char)(i * 8); - } - - std::vector buffer(meshopt_encodeVertexBufferBound(128, 4)); - buffer.resize(meshopt_encodeVertexBuffer(&buffer[0], buffer.size(), data, 128, 4)); - - unsigned char decoded[128 * 4]; - assert(meshopt_decodeVertexBuffer(decoded, 128, 4, &buffer[0], buffer.size()) == 0); - assert(memcmp(decoded, data, sizeof(data)) == 0); -} - -static void encodeVertexEmpty() -{ - std::vector buffer(meshopt_encodeVertexBufferBound(0, 16)); - buffer.resize(meshopt_encodeVertexBuffer(&buffer[0], buffer.size(), NULL, 0, 16)); - - assert(meshopt_decodeVertexBuffer(NULL, 0, 16, &buffer[0], buffer.size()) == 0); -} - -static void decodeFilterOct8() -{ - const unsigned char data[4 * 4] = { - 0, 1, 127, 0, - 0, 187, 127, 1, - 255, 1, 127, 0, - 14, 130, 127, 1, // clang-format :-/ - }; - - const unsigned char expected[4 * 4] = { - 0, 1, 127, 0, - 0, 159, 82, 1, - 255, 1, 127, 0, - 1, 130, 241, 1, // clang-format :-/ - }; - - // Aligned by 4 - unsigned char full[4 * 4]; - memcpy(full, data, sizeof(full)); - meshopt_decodeFilterOct(full, 4, 4); - assert(memcmp(full, expected, sizeof(full)) == 0); - - // Tail processing for unaligned data - unsigned char tail[3 * 4]; - memcpy(tail, data, sizeof(tail)); - meshopt_decodeFilterOct(tail, 3, 4); - assert(memcmp(tail, expected, sizeof(tail)) == 0); -} - -static void decodeFilterOct12() -{ - const unsigned short data[4 * 4] = { - 0, 1, 2047, 0, - 0, 1870, 2047, 1, - 2017, 1, 2047, 0, - 14, 1300, 2047, 1, // clang-format :-/ - }; - - const unsigned short expected[4 * 4] = { - 0, 16, 32767, 0, - 0, 32621, 3088, 1, - 32764, 16, 471, 0, - 307, 28541, 16093, 1, // clang-format :-/ - }; - - // Aligned by 4 - unsigned short full[4 * 4]; - memcpy(full, data, sizeof(full)); - meshopt_decodeFilterOct(full, 4, 8); - assert(memcmp(full, expected, sizeof(full)) == 0); - - // Tail processing for unaligned data - unsigned short tail[3 * 4]; - memcpy(tail, data, sizeof(tail)); - meshopt_decodeFilterOct(tail, 3, 8); - assert(memcmp(tail, expected, sizeof(tail)) == 0); -} - -static void decodeFilterQuat12() -{ - const unsigned short data[4 * 4] = { - 0, 1, 0, 0x7fc, - 0, 1870, 0, 0x7fd, - 2017, 1, 0, 0x7fe, - 14, 1300, 0, 0x7ff, // clang-format :-/ - }; - - const unsigned short expected[4 * 4] = { - 32767, 0, 11, 0, - 0, 25013, 0, 21166, - 11, 0, 23504, 22830, - 158, 14715, 0, 29277, // clang-format :-/ - }; - - // Aligned by 4 - unsigned short full[4 * 4]; - memcpy(full, data, sizeof(full)); - meshopt_decodeFilterQuat(full, 4, 8); - assert(memcmp(full, expected, sizeof(full)) == 0); - - // Tail processing for unaligned data - unsigned short tail[3 * 4]; - memcpy(tail, data, sizeof(tail)); - meshopt_decodeFilterQuat(tail, 3, 8); - assert(memcmp(tail, expected, sizeof(tail)) == 0); -} - -static void decodeFilterExp() -{ - const unsigned int data[4] = { - 0, - 0xff000003, - 0x02fffff7, - 0xfe7fffff, // clang-format :-/ - }; - - const unsigned int expected[4] = { - 0, - 0x3fc00000, - 0xc2100000, - 0x49fffffe, // clang-format :-/ - }; - - // Aligned by 4 - unsigned int full[4]; - memcpy(full, data, sizeof(full)); - meshopt_decodeFilterExp(full, 4, 4); - assert(memcmp(full, expected, sizeof(full)) == 0); - - // Tail processing for unaligned data - unsigned int tail[3]; - memcpy(tail, data, sizeof(tail)); - meshopt_decodeFilterExp(tail, 3, 4); - assert(memcmp(tail, expected, sizeof(tail)) == 0); -} - -void encodeFilterOct8() -{ - const float data[4 * 4] = { - 1, 0, 0, 0, - 0, -1, 0, 0, - 0.7071068f, 0, 0.707168f, 1, - -0.7071068f, 0, -0.707168f, 1, // clang-format :-/ - }; - - const unsigned char expected[4 * 4] = { - 0x7f, 0, 0x7f, 0, - 0, 0x81, 0x7f, 0, - 0x3f, 0, 0x7f, 0x7f, - 0x81, 0x40, 0x7f, 0x7f, // clang-format :-/ - }; - - unsigned char encoded[4 * 4]; - meshopt_encodeFilterOct(encoded, 4, 4, 8, data); - - assert(memcmp(encoded, expected, sizeof(expected)) == 0); - - signed char decoded[4 * 4]; - memcpy(decoded, encoded, sizeof(decoded)); - meshopt_decodeFilterOct(decoded, 4, 4); - - for (size_t i = 0; i < 4 * 4; ++i) - assert(fabsf(decoded[i] / 127.f - data[i]) < 1e-2f); -} - -void encodeFilterOct12() -{ - const float data[4 * 4] = { - 1, 0, 0, 0, - 0, -1, 0, 0, - 0.7071068f, 0, 0.707168f, 1, - -0.7071068f, 0, -0.707168f, 1, // clang-format :-/ - }; - - const unsigned short expected[4 * 4] = { - 0x7ff, 0, 0x7ff, 0, - 0x0, 0xf801, 0x7ff, 0, - 0x3ff, 0, 0x7ff, 0x7fff, - 0xf801, 0x400, 0x7ff, 0x7fff, // clang-format :-/ - }; - - unsigned short encoded[4 * 4]; - meshopt_encodeFilterOct(encoded, 4, 8, 12, data); - - assert(memcmp(encoded, expected, sizeof(expected)) == 0); - - short decoded[4 * 4]; - memcpy(decoded, encoded, sizeof(decoded)); - meshopt_decodeFilterOct(decoded, 4, 8); - - for (size_t i = 0; i < 4 * 4; ++i) - assert(fabsf(decoded[i] / 32767.f - data[i]) < 1e-3f); -} - -void encodeFilterQuat12() -{ - const float data[4 * 4] = { - 1, 0, 0, 0, - 0, -1, 0, 0, - 0.7071068f, 0, 0, 0.707168f, - -0.7071068f, 0, 0, -0.707168f, // clang-format :-/ - }; - - const unsigned short expected[4 * 4] = { - 0, 0, 0, 0x7fc, - 0, 0, 0, 0x7fd, - 0x7ff, 0, 0, 0x7ff, - 0x7ff, 0, 0, 0x7ff, // clang-format :-/ - }; - - unsigned short encoded[4 * 4]; - meshopt_encodeFilterQuat(encoded, 4, 8, 12, data); - - assert(memcmp(encoded, expected, sizeof(expected)) == 0); - - short decoded[4 * 4]; - memcpy(decoded, encoded, sizeof(decoded)); - meshopt_decodeFilterQuat(decoded, 4, 8); - - for (size_t i = 0; i < 4; ++i) - { - float dx = decoded[i * 4 + 0] / 32767.f; - float dy = decoded[i * 4 + 1] / 32767.f; - float dz = decoded[i * 4 + 2] / 32767.f; - float dw = decoded[i * 4 + 3] / 32767.f; - - float dp = - data[i * 4 + 0] * dx + - data[i * 4 + 1] * dy + - data[i * 4 + 2] * dz + - data[i * 4 + 3] * dw; - - assert(fabsf(fabsf(dp) - 1.f) < 1e-4f); - } -} - -void encodeFilterExp() -{ - const float data[3] = { - 1, - -23.4f, - -0.1f, - }; - - const unsigned int expected[3] = { - 0xf7000200, - 0xf7ffd133, - 0xf7ffffcd, - }; - - unsigned int encoded[3]; - meshopt_encodeFilterExp(encoded, 1, 12, 15, data); - - assert(memcmp(encoded, expected, sizeof(expected)) == 0); - - float decoded[3]; - memcpy(decoded, encoded, sizeof(decoded)); - meshopt_decodeFilterExp(decoded, 3, 4); - - for (size_t i = 0; i < 3; ++i) - assert(fabsf(decoded[i] - data[i]) < 1e-3f); -} - -static void clusterBoundsDegenerate() -{ - const float vbd[] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; - const unsigned int ibd[] = {0, 0, 0}; - const unsigned int ib1[] = {0, 1, 2}; - - // all of the bounds below are degenerate as they use 0 triangles, one topology-degenerate triangle and one position-degenerate triangle respectively - meshopt_Bounds bounds0 = meshopt_computeClusterBounds(0, 0, 0, 0, 12); - meshopt_Bounds boundsd = meshopt_computeClusterBounds(ibd, 3, vbd, 3, 12); - meshopt_Bounds bounds1 = meshopt_computeClusterBounds(ib1, 3, vbd, 3, 12); - - assert(bounds0.center[0] == 0 && bounds0.center[1] == 0 && bounds0.center[2] == 0 && bounds0.radius == 0); - assert(boundsd.center[0] == 0 && boundsd.center[1] == 0 && boundsd.center[2] == 0 && boundsd.radius == 0); - assert(bounds1.center[0] == 0 && bounds1.center[1] == 0 && bounds1.center[2] == 0 && bounds1.radius == 0); - - const float vb1[] = {1, 0, 0, 0, 1, 0, 0, 0, 1}; - const unsigned int ib2[] = {0, 1, 2, 0, 2, 1}; - - // these bounds have a degenerate cone since the cluster has two triangles with opposite normals - meshopt_Bounds bounds2 = meshopt_computeClusterBounds(ib2, 6, vb1, 3, 12); - - assert(bounds2.cone_apex[0] == 0 && bounds2.cone_apex[1] == 0 && bounds2.cone_apex[2] == 0); - assert(bounds2.cone_axis[0] == 0 && bounds2.cone_axis[1] == 0 && bounds2.cone_axis[2] == 0); - assert(bounds2.cone_cutoff == 1); - assert(bounds2.cone_axis_s8[0] == 0 && bounds2.cone_axis_s8[1] == 0 && bounds2.cone_axis_s8[2] == 0); - assert(bounds2.cone_cutoff_s8 == 127); - - // however, the bounding sphere needs to be in tact (here we only check bbox for simplicity) - assert(bounds2.center[0] - bounds2.radius <= 0 && bounds2.center[0] + bounds2.radius >= 1); - assert(bounds2.center[1] - bounds2.radius <= 0 && bounds2.center[1] + bounds2.radius >= 1); - assert(bounds2.center[2] - bounds2.radius <= 0 && bounds2.center[2] + bounds2.radius >= 1); -} - -static size_t allocCount; -static size_t freeCount; - -static void* customAlloc(size_t size) -{ - allocCount++; - - return malloc(size); -} - -static void customFree(void* ptr) -{ - freeCount++; - - free(ptr); -} - -static void customAllocator() -{ - meshopt_setAllocator(customAlloc, customFree); - - assert(allocCount == 0 && freeCount == 0); - - float vb[] = {1, 0, 0, 0, 1, 0, 0, 0, 1}; - unsigned int ib[] = {0, 1, 2}; - unsigned short ibs[] = {0, 1, 2}; - - // meshopt_computeClusterBounds doesn't allocate - meshopt_computeClusterBounds(ib, 3, vb, 3, 12); - assert(allocCount == 0 && freeCount == 0); - - // ... unless IndexAdapter is used - meshopt_computeClusterBounds(ibs, 3, vb, 3, 12); - assert(allocCount == 1 && freeCount == 1); - - // meshopt_optimizeVertexFetch allocates internal remap table and temporary storage for in-place remaps - meshopt_optimizeVertexFetch(vb, ib, 3, vb, 3, 12); - assert(allocCount == 3 && freeCount == 3); - - // ... plus one for IndexAdapter - meshopt_optimizeVertexFetch(vb, ibs, 3, vb, 3, 12); - assert(allocCount == 6 && freeCount == 6); - - meshopt_setAllocator(operator new, operator delete); - - // customAlloc & customFree should not get called anymore - meshopt_optimizeVertexFetch(vb, ib, 3, vb, 3, 12); - assert(allocCount == 6 && freeCount == 6); - - allocCount = freeCount = 0; -} - -static void emptyMesh() -{ - meshopt_optimizeVertexCache(0, 0, 0, 0); - meshopt_optimizeVertexCacheFifo(0, 0, 0, 0, 16); - meshopt_optimizeOverdraw(0, 0, 0, 0, 0, 12, 1.f); -} - -static void simplifyStuck() -{ - // tetrahedron can't be simplified due to collapse error restrictions - float vb1[] = {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1}; - unsigned int ib1[] = {0, 1, 2, 0, 2, 3, 0, 3, 1, 2, 1, 3}; - - assert(meshopt_simplify(ib1, ib1, 12, vb1, 4, 12, 6, 1e-3f) == 12); - - // 5-vertex strip can't be simplified due to topology restriction since middle triangle has flipped winding - float vb2[] = {0, 0, 0, 1, 0, 0, 2, 0, 0, 0.5f, 1, 0, 1.5f, 1, 0}; - unsigned int ib2[] = {0, 1, 3, 3, 1, 4, 1, 2, 4}; // ok - unsigned int ib3[] = {0, 1, 3, 1, 3, 4, 1, 2, 4}; // flipped - - assert(meshopt_simplify(ib2, ib2, 9, vb2, 5, 12, 6, 1e-3f) == 6); - assert(meshopt_simplify(ib3, ib3, 9, vb2, 5, 12, 6, 1e-3f) == 9); - - // 4-vertex quad with a locked corner can't be simplified due to border error-induced restriction - float vb4[] = {0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0}; - unsigned int ib4[] = {0, 1, 3, 0, 3, 2}; - - assert(meshopt_simplify(ib4, ib4, 6, vb4, 4, 12, 3, 1e-3f) == 6); - - // 4-vertex quad with a locked corner can't be simplified due to border error-induced restriction - float vb5[] = {0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0}; - unsigned int ib5[] = {0, 1, 4, 0, 3, 2}; - - assert(meshopt_simplify(ib5, ib5, 6, vb5, 5, 12, 3, 1e-3f) == 6); -} - -static void simplifySloppyStuck() -{ - const float vb[] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; - const unsigned int ib[] = {0, 1, 2, 0, 1, 2}; - - unsigned int* target = NULL; - - // simplifying down to 0 triangles results in 0 immediately - assert(meshopt_simplifySloppy(target, ib, 3, vb, 3, 12, 0, 0.f) == 0); - - // simplifying down to 2 triangles given that all triangles are degenerate results in 0 as well - assert(meshopt_simplifySloppy(target, ib, 6, vb, 3, 12, 6, 0.f) == 0); -} - -static void simplifyPointsStuck() -{ - const float vb[] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; - - // simplifying down to 0 points results in 0 immediately - assert(meshopt_simplifyPoints(0, vb, 3, 12, 0) == 0); -} - -static void simplifyFlip() -{ - // this mesh has been constructed by taking a tessellated irregular grid with a square cutout - // and progressively collapsing edges until the only ones left violate border or flip constraints. - // there is only one valid non-flip collapse, so we validate that we take it; when flips are allowed, - // the wrong collapse is picked instead. - float vb[] = { - 1.000000f, 1.000000f, -1.000000f, - 1.000000f, 1.000000f, 1.000000f, - 1.000000f, -1.000000f, 1.000000f, - 1.000000f, -0.200000f, -0.200000f, - 1.000000f, 0.200000f, -0.200000f, - 1.000000f, -0.200000f, 0.200000f, - 1.000000f, 0.200000f, 0.200000f, - 1.000000f, 0.500000f, -0.500000f, - 1.000000f, -1.000000f, 0.000000f, // clang-format :-/ - }; - - // the collapse we expect is 7 -> 0 - unsigned int ib[] = { - 7, 4, 3, - 1, 2, 5, - 7, 1, 6, - 7, 8, 0, // gets removed - 7, 6, 4, - 8, 5, 2, - 8, 7, 3, - 8, 3, 5, - 5, 6, 1, - 7, 0, 1, // gets removed - }; - - unsigned int expected[] = { - 0, 4, 3, - 1, 2, 5, - 0, 1, 6, - 0, 6, 4, - 8, 5, 2, - 8, 0, 3, - 8, 3, 5, - 5, 6, 1, // clang-format :-/ - }; - - assert(meshopt_simplify(ib, ib, 30, vb, 9, 12, 3, 1e-3f) == 24); - assert(memcmp(ib, expected, sizeof(expected)) == 0); -} - -static void simplifyScale() -{ - const float vb[] = {0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3}; - - assert(meshopt_simplifyScale(vb, 4, 12) == 3.f); -} - -static void simplifyDegenerate() -{ - float vb[] = { - 0.000000f, 0.000000f, 0.000000f, - 0.000000f, 1.000000f, 0.000000f, - 0.000000f, 2.000000f, 0.000000f, - 1.000000f, 0.000000f, 0.000000f, - 2.000000f, 0.000000f, 0.000000f, - 1.000000f, 1.000000f, 0.000000f, // clang-format :-/ - }; - - // 0 1 2 - // 3 5 - // 4 - - unsigned int ib[] = { - 0, 1, 3, - 3, 1, 5, - 1, 2, 5, - 3, 5, 4, - 1, 0, 1, // these two degenerate triangles create a fake reverse edge - 0, 3, 0, // which breaks border classification - }; - - unsigned int expected[] = { - 0, 1, 4, - 4, 1, 2, // clang-format :-/ - }; - - assert(meshopt_simplify(ib, ib, 18, vb, 6, 12, 3, 1e-3f) == 6); - assert(memcmp(ib, expected, sizeof(expected)) == 0); -} - -static void simplifyLockBorder() -{ - float vb[] = { - 0.000000f, 0.000000f, 0.000000f, - 0.000000f, 1.000000f, 0.000000f, - 0.000000f, 2.000000f, 0.000000f, - 1.000000f, 0.000000f, 0.000000f, - 1.000000f, 1.000000f, 0.000000f, - 1.000000f, 2.000000f, 0.000000f, - 2.000000f, 0.000000f, 0.000000f, - 2.000000f, 1.000000f, 0.000000f, - 2.000000f, 2.000000f, 0.000000f, // clang-format :-/ - }; - - // 0 1 2 - // 3 4 5 - // 6 7 8 - - unsigned int ib[] = { - 0, 1, 3, - 3, 1, 4, - 1, 2, 4, - 4, 2, 5, - 3, 4, 6, - 6, 4, 7, - 4, 5, 7, - 7, 5, 8, // clang-format :-/ - }; - - unsigned int expected[] = { - 0, 1, 3, - 1, 2, 3, - 3, 2, 5, - 6, 3, 7, - 3, 5, 7, - 7, 5, 8, // clang-format :-/ - }; - - assert(meshopt_simplify(ib, ib, 24, vb, 9, 12, 3, 1e-3f, meshopt_SimplifyLockBorder) == 18); - assert(memcmp(ib, expected, sizeof(expected)) == 0); -} - -static void adjacency() -{ - // 0 1/4 - // 2/5 3 - const float vb[] = {0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0}; - const unsigned int ib[] = {0, 1, 2, 5, 4, 3}; - - unsigned int adjib[12]; - meshopt_generateAdjacencyIndexBuffer(adjib, ib, 6, vb, 6, 12); - - unsigned int expected[] = { - // patch 0 - 0, 0, - 1, 3, - 2, 2, - - // patch 1 - 5, 0, - 4, 4, - 3, 3, - - // clang-format :-/ - }; - - assert(memcmp(adjib, expected, sizeof(expected)) == 0); -} - -static void tessellation() -{ - // 0 1/4 - // 2/5 3 - const float vb[] = {0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0}; - const unsigned int ib[] = {0, 1, 2, 5, 4, 3}; - - unsigned int tessib[24]; - meshopt_generateTessellationIndexBuffer(tessib, ib, 6, vb, 6, 12); - - unsigned int expected[] = { - // patch 0 - 0, 1, 2, - 0, 1, - 4, 5, - 2, 0, - 0, 1, 2, - - // patch 1 - 5, 4, 3, - 2, 1, - 4, 3, - 3, 5, - 2, 1, 3, - - // clang-format :-/ - }; - - assert(memcmp(tessib, expected, sizeof(expected)) == 0); -} - -void runTests() -{ - decodeIndexV0(); - decodeIndexV1(); - decodeIndex16(); - encodeIndexMemorySafe(); - decodeIndexMemorySafe(); - decodeIndexRejectExtraBytes(); - decodeIndexRejectMalformedHeaders(); - decodeIndexRejectInvalidVersion(); - decodeIndexMalformedVByte(); - roundtripIndexTricky(); - encodeIndexEmpty(); - - decodeIndexSequence(); - decodeIndexSequence16(); - encodeIndexSequenceMemorySafe(); - decodeIndexSequenceMemorySafe(); - decodeIndexSequenceRejectExtraBytes(); - decodeIndexSequenceRejectMalformedHeaders(); - decodeIndexSequenceRejectInvalidVersion(); - encodeIndexSequenceEmpty(); - - decodeVertexV0(); - encodeVertexMemorySafe(); - decodeVertexMemorySafe(); - decodeVertexRejectExtraBytes(); - decodeVertexRejectMalformedHeaders(); - decodeVertexBitGroups(); - decodeVertexBitGroupSentinels(); - decodeVertexLarge(); - encodeVertexEmpty(); - - decodeFilterOct8(); - decodeFilterOct12(); - decodeFilterQuat12(); - decodeFilterExp(); - - encodeFilterOct8(); - encodeFilterOct12(); - encodeFilterQuat12(); - encodeFilterExp(); - - clusterBoundsDegenerate(); - - customAllocator(); - - emptyMesh(); - - simplifyStuck(); - simplifySloppyStuck(); - simplifyPointsStuck(); - simplifyFlip(); - simplifyScale(); - simplifyDegenerate(); - simplifyLockBorder(); - - adjacency(); - tessellation(); -} diff --git a/Dependencies/meshoptimizer/extern/cgltf.h b/Dependencies/meshoptimizer/extern/cgltf.h deleted file mode 100644 index 485a95e..0000000 --- a/Dependencies/meshoptimizer/extern/cgltf.h +++ /dev/null @@ -1,6638 +0,0 @@ -/** - * cgltf - a single-file glTF 2.0 parser written in C99. - * - * Version: 1.11 - * - * Website: https://github.com/jkuhlmann/cgltf - * - * Distributed under the MIT License, see notice at the end of this file. - * - * Building: - * Include this file where you need the struct and function - * declarations. Have exactly one source file where you define - * `CGLTF_IMPLEMENTATION` before including this file to get the - * function definitions. - * - * Reference: - * `cgltf_result cgltf_parse(const cgltf_options*, const void*, - * cgltf_size, cgltf_data**)` parses both glTF and GLB data. If - * this function returns `cgltf_result_success`, you have to call - * `cgltf_free()` on the created `cgltf_data*` variable. - * Note that contents of external files for buffers and images are not - * automatically loaded. You'll need to read these files yourself using - * URIs in the `cgltf_data` structure. - * - * `cgltf_options` is the struct passed to `cgltf_parse()` to control - * parts of the parsing process. You can use it to force the file type - * and provide memory allocation as well as file operation callbacks. - * Should be zero-initialized to trigger default behavior. - * - * `cgltf_data` is the struct allocated and filled by `cgltf_parse()`. - * It generally mirrors the glTF format as described by the spec (see - * https://github.com/KhronosGroup/glTF/tree/master/specification/2.0). - * - * `void cgltf_free(cgltf_data*)` frees the allocated `cgltf_data` - * variable. - * - * `cgltf_result cgltf_load_buffers(const cgltf_options*, cgltf_data*, - * const char* gltf_path)` can be optionally called to open and read buffer - * files using the `FILE*` APIs. The `gltf_path` argument is the path to - * the original glTF file, which allows the parser to resolve the path to - * buffer files. - * - * `cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, - * cgltf_size size, const char* base64, void** out_data)` decodes - * base64-encoded data content. Used internally by `cgltf_load_buffers()`. - * This is useful when decoding data URIs in images. - * - * `cgltf_result cgltf_parse_file(const cgltf_options* options, const - * char* path, cgltf_data** out_data)` can be used to open the given - * file using `FILE*` APIs and parse the data using `cgltf_parse()`. - * - * `cgltf_result cgltf_validate(cgltf_data*)` can be used to do additional - * checks to make sure the parsed glTF data is valid. - * - * `cgltf_node_transform_local` converts the translation / rotation / scale properties of a node - * into a mat4. - * - * `cgltf_node_transform_world` calls `cgltf_node_transform_local` on every ancestor in order - * to compute the root-to-node transformation. - * - * `cgltf_accessor_unpack_floats` reads in the data from an accessor, applies sparse data (if any), - * and converts them to floating point. Assumes that `cgltf_load_buffers` has already been called. - * By passing null for the output pointer, users can find out how many floats are required in the - * output buffer. - * - * `cgltf_accessor_num_components` is a tiny utility that tells you the dimensionality of - * a certain accessor type. This can be used before `cgltf_accessor_unpack_floats` to help allocate - * the necessary amount of memory. - * - * `cgltf_accessor_read_float` reads a certain element from a non-sparse accessor and converts it to - * floating point, assuming that `cgltf_load_buffers` has already been called. The passed-in element - * size is the number of floats in the output buffer, which should be in the range [1, 16]. Returns - * false if the passed-in element_size is too small, or if the accessor is sparse. - * - * `cgltf_accessor_read_uint` is similar to its floating-point counterpart, but limited to reading - * vector types and does not support matrix types. The passed-in element size is the number of uints - * in the output buffer, which should be in the range [1, 4]. Returns false if the passed-in - * element_size is too small, or if the accessor is sparse. - * - * `cgltf_accessor_read_index` is similar to its floating-point counterpart, but it returns size_t - * and only works with single-component data types. - * - * `cgltf_result cgltf_copy_extras_json(const cgltf_data*, const cgltf_extras*, - * char* dest, cgltf_size* dest_size)` allows users to retrieve the "extras" data that - * can be attached to many glTF objects (which can be arbitrary JSON data). The - * `cgltf_extras` struct stores the offsets of the start and end of the extras JSON data - * as it appears in the complete glTF JSON data. This function copies the extras data - * into the provided buffer. If `dest` is NULL, the length of the data is written into - * `dest_size`. You can then parse this data using your own JSON parser - * or, if you've included the cgltf implementation using the integrated JSMN JSON parser. - */ -#ifndef CGLTF_H_INCLUDED__ -#define CGLTF_H_INCLUDED__ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef size_t cgltf_size; -typedef float cgltf_float; -typedef int cgltf_int; -typedef unsigned int cgltf_uint; -typedef int cgltf_bool; - -typedef enum cgltf_file_type -{ - cgltf_file_type_invalid, - cgltf_file_type_gltf, - cgltf_file_type_glb, -} cgltf_file_type; - -typedef enum cgltf_result -{ - cgltf_result_success, - cgltf_result_data_too_short, - cgltf_result_unknown_format, - cgltf_result_invalid_json, - cgltf_result_invalid_gltf, - cgltf_result_invalid_options, - cgltf_result_file_not_found, - cgltf_result_io_error, - cgltf_result_out_of_memory, - cgltf_result_legacy_gltf, -} cgltf_result; - -typedef struct cgltf_memory_options -{ - void* (*alloc)(void* user, cgltf_size size); - void (*free) (void* user, void* ptr); - void* user_data; -} cgltf_memory_options; - -typedef struct cgltf_file_options -{ - cgltf_result(*read)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, const char* path, cgltf_size* size, void** data); - void (*release)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data); - void* user_data; -} cgltf_file_options; - -typedef struct cgltf_options -{ - cgltf_file_type type; /* invalid == auto detect */ - cgltf_size json_token_count; /* 0 == auto */ - cgltf_memory_options memory; - cgltf_file_options file; -} cgltf_options; - -typedef enum cgltf_buffer_view_type -{ - cgltf_buffer_view_type_invalid, - cgltf_buffer_view_type_indices, - cgltf_buffer_view_type_vertices, -} cgltf_buffer_view_type; - -typedef enum cgltf_attribute_type -{ - cgltf_attribute_type_invalid, - cgltf_attribute_type_position, - cgltf_attribute_type_normal, - cgltf_attribute_type_tangent, - cgltf_attribute_type_texcoord, - cgltf_attribute_type_color, - cgltf_attribute_type_joints, - cgltf_attribute_type_weights, -} cgltf_attribute_type; - -typedef enum cgltf_component_type -{ - cgltf_component_type_invalid, - cgltf_component_type_r_8, /* BYTE */ - cgltf_component_type_r_8u, /* UNSIGNED_BYTE */ - cgltf_component_type_r_16, /* SHORT */ - cgltf_component_type_r_16u, /* UNSIGNED_SHORT */ - cgltf_component_type_r_32u, /* UNSIGNED_INT */ - cgltf_component_type_r_32f, /* FLOAT */ -} cgltf_component_type; - -typedef enum cgltf_type -{ - cgltf_type_invalid, - cgltf_type_scalar, - cgltf_type_vec2, - cgltf_type_vec3, - cgltf_type_vec4, - cgltf_type_mat2, - cgltf_type_mat3, - cgltf_type_mat4, -} cgltf_type; - -typedef enum cgltf_primitive_type -{ - cgltf_primitive_type_points, - cgltf_primitive_type_lines, - cgltf_primitive_type_line_loop, - cgltf_primitive_type_line_strip, - cgltf_primitive_type_triangles, - cgltf_primitive_type_triangle_strip, - cgltf_primitive_type_triangle_fan, -} cgltf_primitive_type; - -typedef enum cgltf_alpha_mode -{ - cgltf_alpha_mode_opaque, - cgltf_alpha_mode_mask, - cgltf_alpha_mode_blend, -} cgltf_alpha_mode; - -typedef enum cgltf_animation_path_type { - cgltf_animation_path_type_invalid, - cgltf_animation_path_type_translation, - cgltf_animation_path_type_rotation, - cgltf_animation_path_type_scale, - cgltf_animation_path_type_weights, -} cgltf_animation_path_type; - -typedef enum cgltf_interpolation_type { - cgltf_interpolation_type_linear, - cgltf_interpolation_type_step, - cgltf_interpolation_type_cubic_spline, -} cgltf_interpolation_type; - -typedef enum cgltf_camera_type { - cgltf_camera_type_invalid, - cgltf_camera_type_perspective, - cgltf_camera_type_orthographic, -} cgltf_camera_type; - -typedef enum cgltf_light_type { - cgltf_light_type_invalid, - cgltf_light_type_directional, - cgltf_light_type_point, - cgltf_light_type_spot, -} cgltf_light_type; - -typedef enum cgltf_data_free_method { - cgltf_data_free_method_none, - cgltf_data_free_method_file_release, - cgltf_data_free_method_memory_free, -} cgltf_data_free_method; - -typedef struct cgltf_extras { - cgltf_size start_offset; - cgltf_size end_offset; -} cgltf_extras; - -typedef struct cgltf_extension { - char* name; - char* data; -} cgltf_extension; - -typedef struct cgltf_buffer -{ - char* name; - cgltf_size size; - char* uri; - void* data; /* loaded by cgltf_load_buffers */ - cgltf_data_free_method data_free_method; - cgltf_extras extras; - cgltf_size extensions_count; - cgltf_extension* extensions; -} cgltf_buffer; - -typedef enum cgltf_meshopt_compression_mode { - cgltf_meshopt_compression_mode_invalid, - cgltf_meshopt_compression_mode_attributes, - cgltf_meshopt_compression_mode_triangles, - cgltf_meshopt_compression_mode_indices, -} cgltf_meshopt_compression_mode; - -typedef enum cgltf_meshopt_compression_filter { - cgltf_meshopt_compression_filter_none, - cgltf_meshopt_compression_filter_octahedral, - cgltf_meshopt_compression_filter_quaternion, - cgltf_meshopt_compression_filter_exponential, -} cgltf_meshopt_compression_filter; - -typedef struct cgltf_meshopt_compression -{ - cgltf_buffer* buffer; - cgltf_size offset; - cgltf_size size; - cgltf_size stride; - cgltf_size count; - cgltf_meshopt_compression_mode mode; - cgltf_meshopt_compression_filter filter; -} cgltf_meshopt_compression; - -typedef struct cgltf_buffer_view -{ - char *name; - cgltf_buffer* buffer; - cgltf_size offset; - cgltf_size size; - cgltf_size stride; /* 0 == automatically determined by accessor */ - cgltf_buffer_view_type type; - void* data; /* overrides buffer->data if present, filled by extensions */ - cgltf_bool has_meshopt_compression; - cgltf_meshopt_compression meshopt_compression; - cgltf_extras extras; - cgltf_size extensions_count; - cgltf_extension* extensions; -} cgltf_buffer_view; - -typedef struct cgltf_accessor_sparse -{ - cgltf_size count; - cgltf_buffer_view* indices_buffer_view; - cgltf_size indices_byte_offset; - cgltf_component_type indices_component_type; - cgltf_buffer_view* values_buffer_view; - cgltf_size values_byte_offset; - cgltf_extras extras; - cgltf_extras indices_extras; - cgltf_extras values_extras; - cgltf_size extensions_count; - cgltf_extension* extensions; - cgltf_size indices_extensions_count; - cgltf_extension* indices_extensions; - cgltf_size values_extensions_count; - cgltf_extension* values_extensions; -} cgltf_accessor_sparse; - -typedef struct cgltf_accessor -{ - char* name; - cgltf_component_type component_type; - cgltf_bool normalized; - cgltf_type type; - cgltf_size offset; - cgltf_size count; - cgltf_size stride; - cgltf_buffer_view* buffer_view; - cgltf_bool has_min; - cgltf_float min[16]; - cgltf_bool has_max; - cgltf_float max[16]; - cgltf_bool is_sparse; - cgltf_accessor_sparse sparse; - cgltf_extras extras; - cgltf_size extensions_count; - cgltf_extension* extensions; -} cgltf_accessor; - -typedef struct cgltf_attribute -{ - char* name; - cgltf_attribute_type type; - cgltf_int index; - cgltf_accessor* data; -} cgltf_attribute; - -typedef struct cgltf_image -{ - char* name; - char* uri; - cgltf_buffer_view* buffer_view; - char* mime_type; - cgltf_extras extras; - cgltf_size extensions_count; - cgltf_extension* extensions; -} cgltf_image; - -typedef struct cgltf_sampler -{ - char* name; - cgltf_int mag_filter; - cgltf_int min_filter; - cgltf_int wrap_s; - cgltf_int wrap_t; - cgltf_extras extras; - cgltf_size extensions_count; - cgltf_extension* extensions; -} cgltf_sampler; - -typedef struct cgltf_texture -{ - char* name; - cgltf_image* image; - cgltf_sampler* sampler; - cgltf_bool has_basisu; - cgltf_image* basisu_image; - cgltf_extras extras; - cgltf_size extensions_count; - cgltf_extension* extensions; -} cgltf_texture; - -typedef struct cgltf_texture_transform -{ - cgltf_float offset[2]; - cgltf_float rotation; - cgltf_float scale[2]; - cgltf_bool has_texcoord; - cgltf_int texcoord; -} cgltf_texture_transform; - -typedef struct cgltf_texture_view -{ - cgltf_texture* texture; - cgltf_int texcoord; - cgltf_float scale; /* equivalent to strength for occlusion_texture */ - cgltf_bool has_transform; - cgltf_texture_transform transform; - cgltf_extras extras; - cgltf_size extensions_count; - cgltf_extension* extensions; -} cgltf_texture_view; - -typedef struct cgltf_pbr_metallic_roughness -{ - cgltf_texture_view base_color_texture; - cgltf_texture_view metallic_roughness_texture; - - cgltf_float base_color_factor[4]; - cgltf_float metallic_factor; - cgltf_float roughness_factor; - - cgltf_extras extras; -} cgltf_pbr_metallic_roughness; - -typedef struct cgltf_pbr_specular_glossiness -{ - cgltf_texture_view diffuse_texture; - cgltf_texture_view specular_glossiness_texture; - - cgltf_float diffuse_factor[4]; - cgltf_float specular_factor[3]; - cgltf_float glossiness_factor; -} cgltf_pbr_specular_glossiness; - -typedef struct cgltf_clearcoat -{ - cgltf_texture_view clearcoat_texture; - cgltf_texture_view clearcoat_roughness_texture; - cgltf_texture_view clearcoat_normal_texture; - - cgltf_float clearcoat_factor; - cgltf_float clearcoat_roughness_factor; -} cgltf_clearcoat; - -typedef struct cgltf_transmission -{ - cgltf_texture_view transmission_texture; - cgltf_float transmission_factor; -} cgltf_transmission; - -typedef struct cgltf_ior -{ - cgltf_float ior; -} cgltf_ior; - -typedef struct cgltf_specular -{ - cgltf_texture_view specular_texture; - cgltf_texture_view specular_color_texture; - cgltf_float specular_color_factor[3]; - cgltf_float specular_factor; -} cgltf_specular; - -typedef struct cgltf_volume -{ - cgltf_texture_view thickness_texture; - cgltf_float thickness_factor; - cgltf_float attenuation_color[3]; - cgltf_float attenuation_distance; -} cgltf_volume; - -typedef struct cgltf_sheen -{ - cgltf_texture_view sheen_color_texture; - cgltf_float sheen_color_factor[3]; - cgltf_texture_view sheen_roughness_texture; - cgltf_float sheen_roughness_factor; -} cgltf_sheen; - -typedef struct cgltf_emissive_strength -{ - cgltf_float emissive_strength; -} cgltf_emissive_strength; - -typedef struct cgltf_iridescence -{ - cgltf_float iridescence_factor; - cgltf_texture_view iridescence_texture; - cgltf_float iridescence_ior; - cgltf_float iridescence_thickness_min; - cgltf_float iridescence_thickness_max; - cgltf_texture_view iridescence_thickness_texture; -} cgltf_iridescence; - -typedef struct cgltf_material -{ - char* name; - cgltf_bool has_pbr_metallic_roughness; - cgltf_bool has_pbr_specular_glossiness; - cgltf_bool has_clearcoat; - cgltf_bool has_transmission; - cgltf_bool has_volume; - cgltf_bool has_ior; - cgltf_bool has_specular; - cgltf_bool has_sheen; - cgltf_bool has_emissive_strength; - cgltf_bool has_iridescence; - cgltf_pbr_metallic_roughness pbr_metallic_roughness; - cgltf_pbr_specular_glossiness pbr_specular_glossiness; - cgltf_clearcoat clearcoat; - cgltf_ior ior; - cgltf_specular specular; - cgltf_sheen sheen; - cgltf_transmission transmission; - cgltf_volume volume; - cgltf_emissive_strength emissive_strength; - cgltf_iridescence iridescence; - cgltf_texture_view normal_texture; - cgltf_texture_view occlusion_texture; - cgltf_texture_view emissive_texture; - cgltf_float emissive_factor[3]; - cgltf_alpha_mode alpha_mode; - cgltf_float alpha_cutoff; - cgltf_bool double_sided; - cgltf_bool unlit; - cgltf_extras extras; - cgltf_size extensions_count; - cgltf_extension* extensions; -} cgltf_material; - -typedef struct cgltf_material_mapping -{ - cgltf_size variant; - cgltf_material* material; - cgltf_extras extras; -} cgltf_material_mapping; - -typedef struct cgltf_morph_target { - cgltf_attribute* attributes; - cgltf_size attributes_count; -} cgltf_morph_target; - -typedef struct cgltf_draco_mesh_compression { - cgltf_buffer_view* buffer_view; - cgltf_attribute* attributes; - cgltf_size attributes_count; -} cgltf_draco_mesh_compression; - -typedef struct cgltf_primitive { - cgltf_primitive_type type; - cgltf_accessor* indices; - cgltf_material* material; - cgltf_attribute* attributes; - cgltf_size attributes_count; - cgltf_morph_target* targets; - cgltf_size targets_count; - cgltf_extras extras; - cgltf_bool has_draco_mesh_compression; - cgltf_draco_mesh_compression draco_mesh_compression; - cgltf_material_mapping* mappings; - cgltf_size mappings_count; - cgltf_size extensions_count; - cgltf_extension* extensions; -} cgltf_primitive; - -typedef struct cgltf_mesh { - char* name; - cgltf_primitive* primitives; - cgltf_size primitives_count; - cgltf_float* weights; - cgltf_size weights_count; - char** target_names; - cgltf_size target_names_count; - cgltf_extras extras; - cgltf_size extensions_count; - cgltf_extension* extensions; -} cgltf_mesh; - -typedef struct cgltf_node cgltf_node; - -typedef struct cgltf_skin { - char* name; - cgltf_node** joints; - cgltf_size joints_count; - cgltf_node* skeleton; - cgltf_accessor* inverse_bind_matrices; - cgltf_extras extras; - cgltf_size extensions_count; - cgltf_extension* extensions; -} cgltf_skin; - -typedef struct cgltf_camera_perspective { - cgltf_bool has_aspect_ratio; - cgltf_float aspect_ratio; - cgltf_float yfov; - cgltf_bool has_zfar; - cgltf_float zfar; - cgltf_float znear; - cgltf_extras extras; -} cgltf_camera_perspective; - -typedef struct cgltf_camera_orthographic { - cgltf_float xmag; - cgltf_float ymag; - cgltf_float zfar; - cgltf_float znear; - cgltf_extras extras; -} cgltf_camera_orthographic; - -typedef struct cgltf_camera { - char* name; - cgltf_camera_type type; - union { - cgltf_camera_perspective perspective; - cgltf_camera_orthographic orthographic; - } data; - cgltf_extras extras; - cgltf_size extensions_count; - cgltf_extension* extensions; -} cgltf_camera; - -typedef struct cgltf_light { - char* name; - cgltf_float color[3]; - cgltf_float intensity; - cgltf_light_type type; - cgltf_float range; - cgltf_float spot_inner_cone_angle; - cgltf_float spot_outer_cone_angle; - cgltf_extras extras; -} cgltf_light; - -struct cgltf_node { - char* name; - cgltf_node* parent; - cgltf_node** children; - cgltf_size children_count; - cgltf_skin* skin; - cgltf_mesh* mesh; - cgltf_camera* camera; - cgltf_light* light; - cgltf_float* weights; - cgltf_size weights_count; - cgltf_bool has_translation; - cgltf_bool has_rotation; - cgltf_bool has_scale; - cgltf_bool has_matrix; - cgltf_float translation[3]; - cgltf_float rotation[4]; - cgltf_float scale[3]; - cgltf_float matrix[16]; - cgltf_extras extras; - cgltf_size extensions_count; - cgltf_extension* extensions; -}; - -typedef struct cgltf_scene { - char* name; - cgltf_node** nodes; - cgltf_size nodes_count; - cgltf_extras extras; - cgltf_size extensions_count; - cgltf_extension* extensions; -} cgltf_scene; - -typedef struct cgltf_animation_sampler { - cgltf_accessor* input; - cgltf_accessor* output; - cgltf_interpolation_type interpolation; - cgltf_extras extras; - cgltf_size extensions_count; - cgltf_extension* extensions; -} cgltf_animation_sampler; - -typedef struct cgltf_animation_channel { - cgltf_animation_sampler* sampler; - cgltf_node* target_node; - cgltf_animation_path_type target_path; - cgltf_extras extras; - cgltf_size extensions_count; - cgltf_extension* extensions; -} cgltf_animation_channel; - -typedef struct cgltf_animation { - char* name; - cgltf_animation_sampler* samplers; - cgltf_size samplers_count; - cgltf_animation_channel* channels; - cgltf_size channels_count; - cgltf_extras extras; - cgltf_size extensions_count; - cgltf_extension* extensions; -} cgltf_animation; - -typedef struct cgltf_material_variant -{ - char* name; - cgltf_extras extras; -} cgltf_material_variant; - -typedef struct cgltf_asset { - char* copyright; - char* generator; - char* version; - char* min_version; - cgltf_extras extras; - cgltf_size extensions_count; - cgltf_extension* extensions; -} cgltf_asset; - -typedef struct cgltf_data -{ - cgltf_file_type file_type; - void* file_data; - - cgltf_asset asset; - - cgltf_mesh* meshes; - cgltf_size meshes_count; - - cgltf_material* materials; - cgltf_size materials_count; - - cgltf_accessor* accessors; - cgltf_size accessors_count; - - cgltf_buffer_view* buffer_views; - cgltf_size buffer_views_count; - - cgltf_buffer* buffers; - cgltf_size buffers_count; - - cgltf_image* images; - cgltf_size images_count; - - cgltf_texture* textures; - cgltf_size textures_count; - - cgltf_sampler* samplers; - cgltf_size samplers_count; - - cgltf_skin* skins; - cgltf_size skins_count; - - cgltf_camera* cameras; - cgltf_size cameras_count; - - cgltf_light* lights; - cgltf_size lights_count; - - cgltf_node* nodes; - cgltf_size nodes_count; - - cgltf_scene* scenes; - cgltf_size scenes_count; - - cgltf_scene* scene; - - cgltf_animation* animations; - cgltf_size animations_count; - - cgltf_material_variant* variants; - cgltf_size variants_count; - - cgltf_extras extras; - - cgltf_size data_extensions_count; - cgltf_extension* data_extensions; - - char** extensions_used; - cgltf_size extensions_used_count; - - char** extensions_required; - cgltf_size extensions_required_count; - - const char* json; - cgltf_size json_size; - - const void* bin; - cgltf_size bin_size; - - cgltf_memory_options memory; - cgltf_file_options file; -} cgltf_data; - -cgltf_result cgltf_parse( - const cgltf_options* options, - const void* data, - cgltf_size size, - cgltf_data** out_data); - -cgltf_result cgltf_parse_file( - const cgltf_options* options, - const char* path, - cgltf_data** out_data); - -cgltf_result cgltf_load_buffers( - const cgltf_options* options, - cgltf_data* data, - const char* gltf_path); - -cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data); - -cgltf_size cgltf_decode_string(char* string); -cgltf_size cgltf_decode_uri(char* uri); - -cgltf_result cgltf_validate(cgltf_data* data); - -void cgltf_free(cgltf_data* data); - -void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix); -void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix); - -cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size); -cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size); -cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index); - -cgltf_size cgltf_num_components(cgltf_type type); - -cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count); - -cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size); - -#ifdef __cplusplus -} -#endif - -#endif /* #ifndef CGLTF_H_INCLUDED__ */ - -/* - * - * Stop now, if you are only interested in the API. - * Below, you find the implementation. - * - */ - -#if defined(__INTELLISENSE__) || defined(__JETBRAINS_IDE__) -/* This makes MSVC/CLion intellisense work. */ -#define CGLTF_IMPLEMENTATION -#endif - -#ifdef CGLTF_IMPLEMENTATION - -#include /* For uint8_t, uint32_t */ -#include /* For strncpy */ -#include /* For fopen */ -#include /* For UINT_MAX etc */ -#include /* For FLT_MAX */ - -#if !defined(CGLTF_MALLOC) || !defined(CGLTF_FREE) || !defined(CGLTF_ATOI) || !defined(CGLTF_ATOF) || !defined(CGLTF_ATOLL) -#include /* For malloc, free, atoi, atof */ -#endif - -/* JSMN_PARENT_LINKS is necessary to make parsing large structures linear in input size */ -#define JSMN_PARENT_LINKS - -/* JSMN_STRICT is necessary to reject invalid JSON documents */ -#define JSMN_STRICT - -/* - * -- jsmn.h start -- - * Source: https://github.com/zserge/jsmn - * License: MIT - */ -typedef enum { - JSMN_UNDEFINED = 0, - JSMN_OBJECT = 1, - JSMN_ARRAY = 2, - JSMN_STRING = 3, - JSMN_PRIMITIVE = 4 -} jsmntype_t; -enum jsmnerr { - /* Not enough tokens were provided */ - JSMN_ERROR_NOMEM = -1, - /* Invalid character inside JSON string */ - JSMN_ERROR_INVAL = -2, - /* The string is not a full JSON packet, more bytes expected */ - JSMN_ERROR_PART = -3 -}; -typedef struct { - jsmntype_t type; - int start; - int end; - int size; -#ifdef JSMN_PARENT_LINKS - int parent; -#endif -} jsmntok_t; -typedef struct { - unsigned int pos; /* offset in the JSON string */ - unsigned int toknext; /* next token to allocate */ - int toksuper; /* superior token node, e.g parent object or array */ -} jsmn_parser; -static void jsmn_init(jsmn_parser *parser); -static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens); -/* - * -- jsmn.h end -- - */ - - -static const cgltf_size GlbHeaderSize = 12; -static const cgltf_size GlbChunkHeaderSize = 8; -static const uint32_t GlbVersion = 2; -static const uint32_t GlbMagic = 0x46546C67; -static const uint32_t GlbMagicJsonChunk = 0x4E4F534A; -static const uint32_t GlbMagicBinChunk = 0x004E4942; - -#ifndef CGLTF_MALLOC -#define CGLTF_MALLOC(size) malloc(size) -#endif -#ifndef CGLTF_FREE -#define CGLTF_FREE(ptr) free(ptr) -#endif -#ifndef CGLTF_ATOI -#define CGLTF_ATOI(str) atoi(str) -#endif -#ifndef CGLTF_ATOF -#define CGLTF_ATOF(str) atof(str) -#endif -#ifndef CGLTF_ATOLL -#define CGLTF_ATOLL(str) atoll(str) -#endif -#ifndef CGLTF_VALIDATE_ENABLE_ASSERTS -#define CGLTF_VALIDATE_ENABLE_ASSERTS 0 -#endif - -static void* cgltf_default_alloc(void* user, cgltf_size size) -{ - (void)user; - return CGLTF_MALLOC(size); -} - -static void cgltf_default_free(void* user, void* ptr) -{ - (void)user; - CGLTF_FREE(ptr); -} - -static void* cgltf_calloc(cgltf_options* options, size_t element_size, cgltf_size count) -{ - if (SIZE_MAX / element_size < count) - { - return NULL; - } - void* result = options->memory.alloc(options->memory.user_data, element_size * count); - if (!result) - { - return NULL; - } - memset(result, 0, element_size * count); - return result; -} - -static cgltf_result cgltf_default_file_read(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, const char* path, cgltf_size* size, void** data) -{ - (void)file_options; - void* (*memory_alloc)(void*, cgltf_size) = memory_options->alloc ? memory_options->alloc : &cgltf_default_alloc; - void (*memory_free)(void*, void*) = memory_options->free ? memory_options->free : &cgltf_default_free; - - FILE* file = fopen(path, "rb"); - if (!file) - { - return cgltf_result_file_not_found; - } - - cgltf_size file_size = size ? *size : 0; - - if (file_size == 0) - { - fseek(file, 0, SEEK_END); - -#ifdef _WIN32 - __int64 length = _ftelli64(file); -#else - long length = ftell(file); -#endif - - if (length < 0) - { - fclose(file); - return cgltf_result_io_error; - } - - fseek(file, 0, SEEK_SET); - file_size = (cgltf_size)length; - } - - char* file_data = (char*)memory_alloc(memory_options->user_data, file_size); - if (!file_data) - { - fclose(file); - return cgltf_result_out_of_memory; - } - - cgltf_size read_size = fread(file_data, 1, file_size, file); - - fclose(file); - - if (read_size != file_size) - { - memory_free(memory_options->user_data, file_data); - return cgltf_result_io_error; - } - - if (size) - { - *size = file_size; - } - if (data) - { - *data = file_data; - } - - return cgltf_result_success; -} - -static void cgltf_default_file_release(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data) -{ - (void)file_options; - void (*memfree)(void*, void*) = memory_options->free ? memory_options->free : &cgltf_default_free; - memfree(memory_options->user_data, data); -} - -static cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data** out_data); - -cgltf_result cgltf_parse(const cgltf_options* options, const void* data, cgltf_size size, cgltf_data** out_data) -{ - if (size < GlbHeaderSize) - { - return cgltf_result_data_too_short; - } - - if (options == NULL) - { - return cgltf_result_invalid_options; - } - - cgltf_options fixed_options = *options; - if (fixed_options.memory.alloc == NULL) - { - fixed_options.memory.alloc = &cgltf_default_alloc; - } - if (fixed_options.memory.free == NULL) - { - fixed_options.memory.free = &cgltf_default_free; - } - - uint32_t tmp; - // Magic - memcpy(&tmp, data, 4); - if (tmp != GlbMagic) - { - if (fixed_options.type == cgltf_file_type_invalid) - { - fixed_options.type = cgltf_file_type_gltf; - } - else if (fixed_options.type == cgltf_file_type_glb) - { - return cgltf_result_unknown_format; - } - } - - if (fixed_options.type == cgltf_file_type_gltf) - { - cgltf_result json_result = cgltf_parse_json(&fixed_options, (const uint8_t*)data, size, out_data); - if (json_result != cgltf_result_success) - { - return json_result; - } - - (*out_data)->file_type = cgltf_file_type_gltf; - - return cgltf_result_success; - } - - const uint8_t* ptr = (const uint8_t*)data; - // Version - memcpy(&tmp, ptr + 4, 4); - uint32_t version = tmp; - if (version != GlbVersion) - { - return version < GlbVersion ? cgltf_result_legacy_gltf : cgltf_result_unknown_format; - } - - // Total length - memcpy(&tmp, ptr + 8, 4); - if (tmp > size) - { - return cgltf_result_data_too_short; - } - - const uint8_t* json_chunk = ptr + GlbHeaderSize; - - if (GlbHeaderSize + GlbChunkHeaderSize > size) - { - return cgltf_result_data_too_short; - } - - // JSON chunk: length - uint32_t json_length; - memcpy(&json_length, json_chunk, 4); - if (GlbHeaderSize + GlbChunkHeaderSize + json_length > size) - { - return cgltf_result_data_too_short; - } - - // JSON chunk: magic - memcpy(&tmp, json_chunk + 4, 4); - if (tmp != GlbMagicJsonChunk) - { - return cgltf_result_unknown_format; - } - - json_chunk += GlbChunkHeaderSize; - - const void* bin = 0; - cgltf_size bin_size = 0; - - if (GlbHeaderSize + GlbChunkHeaderSize + json_length + GlbChunkHeaderSize <= size) - { - // We can read another chunk - const uint8_t* bin_chunk = json_chunk + json_length; - - // Bin chunk: length - uint32_t bin_length; - memcpy(&bin_length, bin_chunk, 4); - if (GlbHeaderSize + GlbChunkHeaderSize + json_length + GlbChunkHeaderSize + bin_length > size) - { - return cgltf_result_data_too_short; - } - - // Bin chunk: magic - memcpy(&tmp, bin_chunk + 4, 4); - if (tmp != GlbMagicBinChunk) - { - return cgltf_result_unknown_format; - } - - bin_chunk += GlbChunkHeaderSize; - - bin = bin_chunk; - bin_size = bin_length; - } - - cgltf_result json_result = cgltf_parse_json(&fixed_options, json_chunk, json_length, out_data); - if (json_result != cgltf_result_success) - { - return json_result; - } - - (*out_data)->file_type = cgltf_file_type_glb; - (*out_data)->bin = bin; - (*out_data)->bin_size = bin_size; - - return cgltf_result_success; -} - -cgltf_result cgltf_parse_file(const cgltf_options* options, const char* path, cgltf_data** out_data) -{ - if (options == NULL) - { - return cgltf_result_invalid_options; - } - - cgltf_result (*file_read)(const struct cgltf_memory_options*, const struct cgltf_file_options*, const char*, cgltf_size*, void**) = options->file.read ? options->file.read : &cgltf_default_file_read; - void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data) = options->file.release ? options->file.release : cgltf_default_file_release; - - void* file_data = NULL; - cgltf_size file_size = 0; - cgltf_result result = file_read(&options->memory, &options->file, path, &file_size, &file_data); - if (result != cgltf_result_success) - { - return result; - } - - result = cgltf_parse(options, file_data, file_size, out_data); - - if (result != cgltf_result_success) - { - file_release(&options->memory, &options->file, file_data); - return result; - } - - (*out_data)->file_data = file_data; - - return cgltf_result_success; -} - -static void cgltf_combine_paths(char* path, const char* base, const char* uri) -{ - const char* s0 = strrchr(base, '/'); - const char* s1 = strrchr(base, '\\'); - const char* slash = s0 ? (s1 && s1 > s0 ? s1 : s0) : s1; - - if (slash) - { - size_t prefix = slash - base + 1; - - strncpy(path, base, prefix); - strcpy(path + prefix, uri); - } - else - { - strcpy(path, uri); - } -} - -static cgltf_result cgltf_load_buffer_file(const cgltf_options* options, cgltf_size size, const char* uri, const char* gltf_path, void** out_data) -{ - void* (*memory_alloc)(void*, cgltf_size) = options->memory.alloc ? options->memory.alloc : &cgltf_default_alloc; - void (*memory_free)(void*, void*) = options->memory.free ? options->memory.free : &cgltf_default_free; - cgltf_result (*file_read)(const struct cgltf_memory_options*, const struct cgltf_file_options*, const char*, cgltf_size*, void**) = options->file.read ? options->file.read : &cgltf_default_file_read; - - char* path = (char*)memory_alloc(options->memory.user_data, strlen(uri) + strlen(gltf_path) + 1); - if (!path) - { - return cgltf_result_out_of_memory; - } - - cgltf_combine_paths(path, gltf_path, uri); - - // after combining, the tail of the resulting path is a uri; decode_uri converts it into path - cgltf_decode_uri(path + strlen(path) - strlen(uri)); - - void* file_data = NULL; - cgltf_result result = file_read(&options->memory, &options->file, path, &size, &file_data); - - memory_free(options->memory.user_data, path); - - *out_data = (result == cgltf_result_success) ? file_data : NULL; - - return result; -} - -cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data) -{ - void* (*memory_alloc)(void*, cgltf_size) = options->memory.alloc ? options->memory.alloc : &cgltf_default_alloc; - void (*memory_free)(void*, void*) = options->memory.free ? options->memory.free : &cgltf_default_free; - - unsigned char* data = (unsigned char*)memory_alloc(options->memory.user_data, size); - if (!data) - { - return cgltf_result_out_of_memory; - } - - unsigned int buffer = 0; - unsigned int buffer_bits = 0; - - for (cgltf_size i = 0; i < size; ++i) - { - while (buffer_bits < 8) - { - char ch = *base64++; - - int index = - (unsigned)(ch - 'A') < 26 ? (ch - 'A') : - (unsigned)(ch - 'a') < 26 ? (ch - 'a') + 26 : - (unsigned)(ch - '0') < 10 ? (ch - '0') + 52 : - ch == '+' ? 62 : - ch == '/' ? 63 : - -1; - - if (index < 0) - { - memory_free(options->memory.user_data, data); - return cgltf_result_io_error; - } - - buffer = (buffer << 6) | index; - buffer_bits += 6; - } - - data[i] = (unsigned char)(buffer >> (buffer_bits - 8)); - buffer_bits -= 8; - } - - *out_data = data; - - return cgltf_result_success; -} - -static int cgltf_unhex(char ch) -{ - return - (unsigned)(ch - '0') < 10 ? (ch - '0') : - (unsigned)(ch - 'A') < 6 ? (ch - 'A') + 10 : - (unsigned)(ch - 'a') < 6 ? (ch - 'a') + 10 : - -1; -} - -cgltf_size cgltf_decode_string(char* string) -{ - char* read = string + strcspn(string, "\\"); - if (*read == 0) - { - return read - string; - } - char* write = string; - char* last = string; - - for (;;) - { - // Copy characters since last escaped sequence - cgltf_size written = read - last; - memmove(write, last, written); - write += written; - - if (*read++ == 0) - { - break; - } - - // jsmn already checked that all escape sequences are valid - switch (*read++) - { - case '\"': *write++ = '\"'; break; - case '/': *write++ = '/'; break; - case '\\': *write++ = '\\'; break; - case 'b': *write++ = '\b'; break; - case 'f': *write++ = '\f'; break; - case 'r': *write++ = '\r'; break; - case 'n': *write++ = '\n'; break; - case 't': *write++ = '\t'; break; - case 'u': - { - // UCS-2 codepoint \uXXXX to UTF-8 - int character = 0; - for (cgltf_size i = 0; i < 4; ++i) - { - character = (character << 4) + cgltf_unhex(*read++); - } - - if (character <= 0x7F) - { - *write++ = character & 0xFF; - } - else if (character <= 0x7FF) - { - *write++ = 0xC0 | ((character >> 6) & 0xFF); - *write++ = 0x80 | (character & 0x3F); - } - else - { - *write++ = 0xE0 | ((character >> 12) & 0xFF); - *write++ = 0x80 | ((character >> 6) & 0x3F); - *write++ = 0x80 | (character & 0x3F); - } - break; - } - default: - break; - } - - last = read; - read += strcspn(read, "\\"); - } - - *write = 0; - return write - string; -} - -cgltf_size cgltf_decode_uri(char* uri) -{ - char* write = uri; - char* i = uri; - - while (*i) - { - if (*i == '%') - { - int ch1 = cgltf_unhex(i[1]); - - if (ch1 >= 0) - { - int ch2 = cgltf_unhex(i[2]); - - if (ch2 >= 0) - { - *write++ = (char)(ch1 * 16 + ch2); - i += 3; - continue; - } - } - } - - *write++ = *i++; - } - - *write = 0; - return write - uri; -} - -cgltf_result cgltf_load_buffers(const cgltf_options* options, cgltf_data* data, const char* gltf_path) -{ - if (options == NULL) - { - return cgltf_result_invalid_options; - } - - if (data->buffers_count && data->buffers[0].data == NULL && data->buffers[0].uri == NULL && data->bin) - { - if (data->bin_size < data->buffers[0].size) - { - return cgltf_result_data_too_short; - } - - data->buffers[0].data = (void*)data->bin; - data->buffers[0].data_free_method = cgltf_data_free_method_none; - } - - for (cgltf_size i = 0; i < data->buffers_count; ++i) - { - if (data->buffers[i].data) - { - continue; - } - - const char* uri = data->buffers[i].uri; - - if (uri == NULL) - { - continue; - } - - if (strncmp(uri, "data:", 5) == 0) - { - const char* comma = strchr(uri, ','); - - if (comma && comma - uri >= 7 && strncmp(comma - 7, ";base64", 7) == 0) - { - cgltf_result res = cgltf_load_buffer_base64(options, data->buffers[i].size, comma + 1, &data->buffers[i].data); - data->buffers[i].data_free_method = cgltf_data_free_method_memory_free; - - if (res != cgltf_result_success) - { - return res; - } - } - else - { - return cgltf_result_unknown_format; - } - } - else if (strstr(uri, "://") == NULL && gltf_path) - { - cgltf_result res = cgltf_load_buffer_file(options, data->buffers[i].size, uri, gltf_path, &data->buffers[i].data); - data->buffers[i].data_free_method = cgltf_data_free_method_file_release; - - if (res != cgltf_result_success) - { - return res; - } - } - else - { - return cgltf_result_unknown_format; - } - } - - return cgltf_result_success; -} - -static cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type); - -static cgltf_size cgltf_calc_index_bound(cgltf_buffer_view* buffer_view, cgltf_size offset, cgltf_component_type component_type, cgltf_size count) -{ - char* data = (char*)buffer_view->buffer->data + offset + buffer_view->offset; - cgltf_size bound = 0; - - switch (component_type) - { - case cgltf_component_type_r_8u: - for (size_t i = 0; i < count; ++i) - { - cgltf_size v = ((unsigned char*)data)[i]; - bound = bound > v ? bound : v; - } - break; - - case cgltf_component_type_r_16u: - for (size_t i = 0; i < count; ++i) - { - cgltf_size v = ((unsigned short*)data)[i]; - bound = bound > v ? bound : v; - } - break; - - case cgltf_component_type_r_32u: - for (size_t i = 0; i < count; ++i) - { - cgltf_size v = ((unsigned int*)data)[i]; - bound = bound > v ? bound : v; - } - break; - - default: - ; - } - - return bound; -} - -#if CGLTF_VALIDATE_ENABLE_ASSERTS -#define CGLTF_ASSERT_IF(cond, result) assert(!(cond)); if (cond) return result; -#else -#define CGLTF_ASSERT_IF(cond, result) if (cond) return result; -#endif - -cgltf_result cgltf_validate(cgltf_data* data) -{ - for (cgltf_size i = 0; i < data->accessors_count; ++i) - { - cgltf_accessor* accessor = &data->accessors[i]; - - cgltf_size element_size = cgltf_calc_size(accessor->type, accessor->component_type); - - if (accessor->buffer_view) - { - cgltf_size req_size = accessor->offset + accessor->stride * (accessor->count - 1) + element_size; - - CGLTF_ASSERT_IF(accessor->buffer_view->size < req_size, cgltf_result_data_too_short); - } - - if (accessor->is_sparse) - { - cgltf_accessor_sparse* sparse = &accessor->sparse; - - cgltf_size indices_component_size = cgltf_calc_size(cgltf_type_scalar, sparse->indices_component_type); - cgltf_size indices_req_size = sparse->indices_byte_offset + indices_component_size * sparse->count; - cgltf_size values_req_size = sparse->values_byte_offset + element_size * sparse->count; - - CGLTF_ASSERT_IF(sparse->indices_buffer_view->size < indices_req_size || - sparse->values_buffer_view->size < values_req_size, cgltf_result_data_too_short); - - CGLTF_ASSERT_IF(sparse->indices_component_type != cgltf_component_type_r_8u && - sparse->indices_component_type != cgltf_component_type_r_16u && - sparse->indices_component_type != cgltf_component_type_r_32u, cgltf_result_invalid_gltf); - - if (sparse->indices_buffer_view->buffer->data) - { - cgltf_size index_bound = cgltf_calc_index_bound(sparse->indices_buffer_view, sparse->indices_byte_offset, sparse->indices_component_type, sparse->count); - - CGLTF_ASSERT_IF(index_bound >= accessor->count, cgltf_result_data_too_short); - } - } - } - - for (cgltf_size i = 0; i < data->buffer_views_count; ++i) - { - cgltf_size req_size = data->buffer_views[i].offset + data->buffer_views[i].size; - - CGLTF_ASSERT_IF(data->buffer_views[i].buffer && data->buffer_views[i].buffer->size < req_size, cgltf_result_data_too_short); - - if (data->buffer_views[i].has_meshopt_compression) - { - cgltf_meshopt_compression* mc = &data->buffer_views[i].meshopt_compression; - - CGLTF_ASSERT_IF(mc->buffer == NULL || mc->buffer->size < mc->offset + mc->size, cgltf_result_data_too_short); - - CGLTF_ASSERT_IF(data->buffer_views[i].stride && mc->stride != data->buffer_views[i].stride, cgltf_result_invalid_gltf); - - CGLTF_ASSERT_IF(data->buffer_views[i].size != mc->stride * mc->count, cgltf_result_invalid_gltf); - - CGLTF_ASSERT_IF(mc->mode == cgltf_meshopt_compression_mode_invalid, cgltf_result_invalid_gltf); - - CGLTF_ASSERT_IF(mc->mode == cgltf_meshopt_compression_mode_attributes && !(mc->stride % 4 == 0 && mc->stride <= 256), cgltf_result_invalid_gltf); - - CGLTF_ASSERT_IF(mc->mode == cgltf_meshopt_compression_mode_triangles && mc->count % 3 != 0, cgltf_result_invalid_gltf); - - CGLTF_ASSERT_IF((mc->mode == cgltf_meshopt_compression_mode_triangles || mc->mode == cgltf_meshopt_compression_mode_indices) && mc->stride != 2 && mc->stride != 4, cgltf_result_invalid_gltf); - - CGLTF_ASSERT_IF((mc->mode == cgltf_meshopt_compression_mode_triangles || mc->mode == cgltf_meshopt_compression_mode_indices) && mc->filter != cgltf_meshopt_compression_filter_none, cgltf_result_invalid_gltf); - - CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_octahedral && mc->stride != 4 && mc->stride != 8, cgltf_result_invalid_gltf); - - CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_quaternion && mc->stride != 8, cgltf_result_invalid_gltf); - } - } - - for (cgltf_size i = 0; i < data->meshes_count; ++i) - { - if (data->meshes[i].weights) - { - CGLTF_ASSERT_IF(data->meshes[i].primitives_count && data->meshes[i].primitives[0].targets_count != data->meshes[i].weights_count, cgltf_result_invalid_gltf); - } - - if (data->meshes[i].target_names) - { - CGLTF_ASSERT_IF(data->meshes[i].primitives_count && data->meshes[i].primitives[0].targets_count != data->meshes[i].target_names_count, cgltf_result_invalid_gltf); - } - - for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) - { - CGLTF_ASSERT_IF(data->meshes[i].primitives[j].targets_count != data->meshes[i].primitives[0].targets_count, cgltf_result_invalid_gltf); - - if (data->meshes[i].primitives[j].attributes_count) - { - cgltf_accessor* first = data->meshes[i].primitives[j].attributes[0].data; - - for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k) - { - CGLTF_ASSERT_IF(data->meshes[i].primitives[j].attributes[k].data->count != first->count, cgltf_result_invalid_gltf); - } - - for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k) - { - for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m) - { - CGLTF_ASSERT_IF(data->meshes[i].primitives[j].targets[k].attributes[m].data->count != first->count, cgltf_result_invalid_gltf); - } - } - - cgltf_accessor* indices = data->meshes[i].primitives[j].indices; - - CGLTF_ASSERT_IF(indices && - indices->component_type != cgltf_component_type_r_8u && - indices->component_type != cgltf_component_type_r_16u && - indices->component_type != cgltf_component_type_r_32u, cgltf_result_invalid_gltf); - - if (indices && indices->buffer_view && indices->buffer_view->buffer->data) - { - cgltf_size index_bound = cgltf_calc_index_bound(indices->buffer_view, indices->offset, indices->component_type, indices->count); - - CGLTF_ASSERT_IF(index_bound >= first->count, cgltf_result_data_too_short); - } - - for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k) - { - CGLTF_ASSERT_IF(data->meshes[i].primitives[j].mappings[k].variant >= data->variants_count, cgltf_result_invalid_gltf); - } - } - } - } - - for (cgltf_size i = 0; i < data->nodes_count; ++i) - { - if (data->nodes[i].weights && data->nodes[i].mesh) - { - CGLTF_ASSERT_IF (data->nodes[i].mesh->primitives_count && data->nodes[i].mesh->primitives[0].targets_count != data->nodes[i].weights_count, cgltf_result_invalid_gltf); - } - } - - for (cgltf_size i = 0; i < data->nodes_count; ++i) - { - cgltf_node* p1 = data->nodes[i].parent; - cgltf_node* p2 = p1 ? p1->parent : NULL; - - while (p1 && p2) - { - CGLTF_ASSERT_IF(p1 == p2, cgltf_result_invalid_gltf); - - p1 = p1->parent; - p2 = p2->parent ? p2->parent->parent : NULL; - } - } - - for (cgltf_size i = 0; i < data->scenes_count; ++i) - { - for (cgltf_size j = 0; j < data->scenes[i].nodes_count; ++j) - { - CGLTF_ASSERT_IF(data->scenes[i].nodes[j]->parent, cgltf_result_invalid_gltf); - } - } - - for (cgltf_size i = 0; i < data->animations_count; ++i) - { - for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j) - { - cgltf_animation_channel* channel = &data->animations[i].channels[j]; - - if (!channel->target_node) - { - continue; - } - - cgltf_size components = 1; - - if (channel->target_path == cgltf_animation_path_type_weights) - { - CGLTF_ASSERT_IF(!channel->target_node->mesh || !channel->target_node->mesh->primitives_count, cgltf_result_invalid_gltf); - - components = channel->target_node->mesh->primitives[0].targets_count; - } - - cgltf_size values = channel->sampler->interpolation == cgltf_interpolation_type_cubic_spline ? 3 : 1; - - CGLTF_ASSERT_IF(channel->sampler->input->count * components * values != channel->sampler->output->count, cgltf_result_data_too_short); - } - } - - return cgltf_result_success; -} - -cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size) -{ - cgltf_size json_size = extras->end_offset - extras->start_offset; - - if (!dest) - { - if (dest_size) - { - *dest_size = json_size + 1; - return cgltf_result_success; - } - return cgltf_result_invalid_options; - } - - if (*dest_size + 1 < json_size) - { - strncpy(dest, data->json + extras->start_offset, *dest_size - 1); - dest[*dest_size - 1] = 0; - } - else - { - strncpy(dest, data->json + extras->start_offset, json_size); - dest[json_size] = 0; - } - - return cgltf_result_success; -} - -void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, cgltf_size extensions_count) -{ - for (cgltf_size i = 0; i < extensions_count; ++i) - { - data->memory.free(data->memory.user_data, extensions[i].name); - data->memory.free(data->memory.user_data, extensions[i].data); - } - data->memory.free(data->memory.user_data, extensions); -} - -void cgltf_free(cgltf_data* data) -{ - if (!data) - { - return; - } - - void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data) = data->file.release ? data->file.release : cgltf_default_file_release; - - data->memory.free(data->memory.user_data, data->asset.copyright); - data->memory.free(data->memory.user_data, data->asset.generator); - data->memory.free(data->memory.user_data, data->asset.version); - data->memory.free(data->memory.user_data, data->asset.min_version); - - cgltf_free_extensions(data, data->asset.extensions, data->asset.extensions_count); - - for (cgltf_size i = 0; i < data->accessors_count; ++i) - { - data->memory.free(data->memory.user_data, data->accessors[i].name); - - if(data->accessors[i].is_sparse) - { - cgltf_free_extensions(data, data->accessors[i].sparse.extensions, data->accessors[i].sparse.extensions_count); - cgltf_free_extensions(data, data->accessors[i].sparse.indices_extensions, data->accessors[i].sparse.indices_extensions_count); - cgltf_free_extensions(data, data->accessors[i].sparse.values_extensions, data->accessors[i].sparse.values_extensions_count); - } - cgltf_free_extensions(data, data->accessors[i].extensions, data->accessors[i].extensions_count); - } - data->memory.free(data->memory.user_data, data->accessors); - - for (cgltf_size i = 0; i < data->buffer_views_count; ++i) - { - data->memory.free(data->memory.user_data, data->buffer_views[i].name); - data->memory.free(data->memory.user_data, data->buffer_views[i].data); - - cgltf_free_extensions(data, data->buffer_views[i].extensions, data->buffer_views[i].extensions_count); - } - data->memory.free(data->memory.user_data, data->buffer_views); - - for (cgltf_size i = 0; i < data->buffers_count; ++i) - { - data->memory.free(data->memory.user_data, data->buffers[i].name); - - if (data->buffers[i].data_free_method == cgltf_data_free_method_file_release) - { - file_release(&data->memory, &data->file, data->buffers[i].data); - } - else if (data->buffers[i].data_free_method == cgltf_data_free_method_memory_free) - { - data->memory.free(data->memory.user_data, data->buffers[i].data); - } - - data->memory.free(data->memory.user_data, data->buffers[i].uri); - - cgltf_free_extensions(data, data->buffers[i].extensions, data->buffers[i].extensions_count); - } - - data->memory.free(data->memory.user_data, data->buffers); - - for (cgltf_size i = 0; i < data->meshes_count; ++i) - { - data->memory.free(data->memory.user_data, data->meshes[i].name); - - for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) - { - for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k) - { - data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].attributes[k].name); - } - - data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].attributes); - - for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k) - { - for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m) - { - data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].targets[k].attributes[m].name); - } - - data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].targets[k].attributes); - } - - data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].targets); - - if (data->meshes[i].primitives[j].has_draco_mesh_compression) - { - for (cgltf_size k = 0; k < data->meshes[i].primitives[j].draco_mesh_compression.attributes_count; ++k) - { - data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].draco_mesh_compression.attributes[k].name); - } - - data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].draco_mesh_compression.attributes); - } - - data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].mappings); - - cgltf_free_extensions(data, data->meshes[i].primitives[j].extensions, data->meshes[i].primitives[j].extensions_count); - } - - data->memory.free(data->memory.user_data, data->meshes[i].primitives); - data->memory.free(data->memory.user_data, data->meshes[i].weights); - - for (cgltf_size j = 0; j < data->meshes[i].target_names_count; ++j) - { - data->memory.free(data->memory.user_data, data->meshes[i].target_names[j]); - } - - cgltf_free_extensions(data, data->meshes[i].extensions, data->meshes[i].extensions_count); - - data->memory.free(data->memory.user_data, data->meshes[i].target_names); - } - - data->memory.free(data->memory.user_data, data->meshes); - - for (cgltf_size i = 0; i < data->materials_count; ++i) - { - data->memory.free(data->memory.user_data, data->materials[i].name); - - if(data->materials[i].has_pbr_metallic_roughness) - { - cgltf_free_extensions(data, data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.extensions, data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.extensions_count); - cgltf_free_extensions(data, data->materials[i].pbr_metallic_roughness.base_color_texture.extensions, data->materials[i].pbr_metallic_roughness.base_color_texture.extensions_count); - } - if(data->materials[i].has_pbr_specular_glossiness) - { - cgltf_free_extensions(data, data->materials[i].pbr_specular_glossiness.diffuse_texture.extensions, data->materials[i].pbr_specular_glossiness.diffuse_texture.extensions_count); - cgltf_free_extensions(data, data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.extensions, data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.extensions_count); - } - if(data->materials[i].has_clearcoat) - { - cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_texture.extensions, data->materials[i].clearcoat.clearcoat_texture.extensions_count); - cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_roughness_texture.extensions, data->materials[i].clearcoat.clearcoat_roughness_texture.extensions_count); - cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_normal_texture.extensions, data->materials[i].clearcoat.clearcoat_normal_texture.extensions_count); - } - if(data->materials[i].has_specular) - { - cgltf_free_extensions(data, data->materials[i].specular.specular_texture.extensions, data->materials[i].specular.specular_texture.extensions_count); - cgltf_free_extensions(data, data->materials[i].specular.specular_color_texture.extensions, data->materials[i].specular.specular_color_texture.extensions_count); - } - if(data->materials[i].has_transmission) - { - cgltf_free_extensions(data, data->materials[i].transmission.transmission_texture.extensions, data->materials[i].transmission.transmission_texture.extensions_count); - } - if (data->materials[i].has_volume) - { - cgltf_free_extensions(data, data->materials[i].volume.thickness_texture.extensions, data->materials[i].volume.thickness_texture.extensions_count); - } - if(data->materials[i].has_sheen) - { - cgltf_free_extensions(data, data->materials[i].sheen.sheen_color_texture.extensions, data->materials[i].sheen.sheen_color_texture.extensions_count); - cgltf_free_extensions(data, data->materials[i].sheen.sheen_roughness_texture.extensions, data->materials[i].sheen.sheen_roughness_texture.extensions_count); - } - if(data->materials[i].has_iridescence) - { - cgltf_free_extensions(data, data->materials[i].iridescence.iridescence_texture.extensions, data->materials[i].iridescence.iridescence_texture.extensions_count); - cgltf_free_extensions(data, data->materials[i].iridescence.iridescence_thickness_texture.extensions, data->materials[i].iridescence.iridescence_thickness_texture.extensions_count); - } - - cgltf_free_extensions(data, data->materials[i].normal_texture.extensions, data->materials[i].normal_texture.extensions_count); - cgltf_free_extensions(data, data->materials[i].occlusion_texture.extensions, data->materials[i].occlusion_texture.extensions_count); - cgltf_free_extensions(data, data->materials[i].emissive_texture.extensions, data->materials[i].emissive_texture.extensions_count); - - cgltf_free_extensions(data, data->materials[i].extensions, data->materials[i].extensions_count); - } - - data->memory.free(data->memory.user_data, data->materials); - - for (cgltf_size i = 0; i < data->images_count; ++i) - { - data->memory.free(data->memory.user_data, data->images[i].name); - data->memory.free(data->memory.user_data, data->images[i].uri); - data->memory.free(data->memory.user_data, data->images[i].mime_type); - - cgltf_free_extensions(data, data->images[i].extensions, data->images[i].extensions_count); - } - - data->memory.free(data->memory.user_data, data->images); - - for (cgltf_size i = 0; i < data->textures_count; ++i) - { - data->memory.free(data->memory.user_data, data->textures[i].name); - cgltf_free_extensions(data, data->textures[i].extensions, data->textures[i].extensions_count); - } - - data->memory.free(data->memory.user_data, data->textures); - - for (cgltf_size i = 0; i < data->samplers_count; ++i) - { - data->memory.free(data->memory.user_data, data->samplers[i].name); - cgltf_free_extensions(data, data->samplers[i].extensions, data->samplers[i].extensions_count); - } - - data->memory.free(data->memory.user_data, data->samplers); - - for (cgltf_size i = 0; i < data->skins_count; ++i) - { - data->memory.free(data->memory.user_data, data->skins[i].name); - data->memory.free(data->memory.user_data, data->skins[i].joints); - - cgltf_free_extensions(data, data->skins[i].extensions, data->skins[i].extensions_count); - } - - data->memory.free(data->memory.user_data, data->skins); - - for (cgltf_size i = 0; i < data->cameras_count; ++i) - { - data->memory.free(data->memory.user_data, data->cameras[i].name); - cgltf_free_extensions(data, data->cameras[i].extensions, data->cameras[i].extensions_count); - } - - data->memory.free(data->memory.user_data, data->cameras); - - for (cgltf_size i = 0; i < data->lights_count; ++i) - { - data->memory.free(data->memory.user_data, data->lights[i].name); - } - - data->memory.free(data->memory.user_data, data->lights); - - for (cgltf_size i = 0; i < data->nodes_count; ++i) - { - data->memory.free(data->memory.user_data, data->nodes[i].name); - data->memory.free(data->memory.user_data, data->nodes[i].children); - data->memory.free(data->memory.user_data, data->nodes[i].weights); - cgltf_free_extensions(data, data->nodes[i].extensions, data->nodes[i].extensions_count); - } - - data->memory.free(data->memory.user_data, data->nodes); - - for (cgltf_size i = 0; i < data->scenes_count; ++i) - { - data->memory.free(data->memory.user_data, data->scenes[i].name); - data->memory.free(data->memory.user_data, data->scenes[i].nodes); - - cgltf_free_extensions(data, data->scenes[i].extensions, data->scenes[i].extensions_count); - } - - data->memory.free(data->memory.user_data, data->scenes); - - for (cgltf_size i = 0; i < data->animations_count; ++i) - { - data->memory.free(data->memory.user_data, data->animations[i].name); - for (cgltf_size j = 0; j < data->animations[i].samplers_count; ++j) - { - cgltf_free_extensions(data, data->animations[i].samplers[j].extensions, data->animations[i].samplers[j].extensions_count); - } - data->memory.free(data->memory.user_data, data->animations[i].samplers); - - for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j) - { - cgltf_free_extensions(data, data->animations[i].channels[j].extensions, data->animations[i].channels[j].extensions_count); - } - data->memory.free(data->memory.user_data, data->animations[i].channels); - - cgltf_free_extensions(data, data->animations[i].extensions, data->animations[i].extensions_count); - } - - data->memory.free(data->memory.user_data, data->animations); - - for (cgltf_size i = 0; i < data->variants_count; ++i) - { - data->memory.free(data->memory.user_data, data->variants[i].name); - } - - data->memory.free(data->memory.user_data, data->variants); - - cgltf_free_extensions(data, data->data_extensions, data->data_extensions_count); - - for (cgltf_size i = 0; i < data->extensions_used_count; ++i) - { - data->memory.free(data->memory.user_data, data->extensions_used[i]); - } - - data->memory.free(data->memory.user_data, data->extensions_used); - - for (cgltf_size i = 0; i < data->extensions_required_count; ++i) - { - data->memory.free(data->memory.user_data, data->extensions_required[i]); - } - - data->memory.free(data->memory.user_data, data->extensions_required); - - file_release(&data->memory, &data->file, data->file_data); - - data->memory.free(data->memory.user_data, data); -} - -void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix) -{ - cgltf_float* lm = out_matrix; - - if (node->has_matrix) - { - memcpy(lm, node->matrix, sizeof(float) * 16); - } - else - { - float tx = node->translation[0]; - float ty = node->translation[1]; - float tz = node->translation[2]; - - float qx = node->rotation[0]; - float qy = node->rotation[1]; - float qz = node->rotation[2]; - float qw = node->rotation[3]; - - float sx = node->scale[0]; - float sy = node->scale[1]; - float sz = node->scale[2]; - - lm[0] = (1 - 2 * qy*qy - 2 * qz*qz) * sx; - lm[1] = (2 * qx*qy + 2 * qz*qw) * sx; - lm[2] = (2 * qx*qz - 2 * qy*qw) * sx; - lm[3] = 0.f; - - lm[4] = (2 * qx*qy - 2 * qz*qw) * sy; - lm[5] = (1 - 2 * qx*qx - 2 * qz*qz) * sy; - lm[6] = (2 * qy*qz + 2 * qx*qw) * sy; - lm[7] = 0.f; - - lm[8] = (2 * qx*qz + 2 * qy*qw) * sz; - lm[9] = (2 * qy*qz - 2 * qx*qw) * sz; - lm[10] = (1 - 2 * qx*qx - 2 * qy*qy) * sz; - lm[11] = 0.f; - - lm[12] = tx; - lm[13] = ty; - lm[14] = tz; - lm[15] = 1.f; - } -} - -void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix) -{ - cgltf_float* lm = out_matrix; - cgltf_node_transform_local(node, lm); - - const cgltf_node* parent = node->parent; - - while (parent) - { - float pm[16]; - cgltf_node_transform_local(parent, pm); - - for (int i = 0; i < 4; ++i) - { - float l0 = lm[i * 4 + 0]; - float l1 = lm[i * 4 + 1]; - float l2 = lm[i * 4 + 2]; - - float r0 = l0 * pm[0] + l1 * pm[4] + l2 * pm[8]; - float r1 = l0 * pm[1] + l1 * pm[5] + l2 * pm[9]; - float r2 = l0 * pm[2] + l1 * pm[6] + l2 * pm[10]; - - lm[i * 4 + 0] = r0; - lm[i * 4 + 1] = r1; - lm[i * 4 + 2] = r2; - } - - lm[12] += pm[12]; - lm[13] += pm[13]; - lm[14] += pm[14]; - - parent = parent->parent; - } -} - -static cgltf_size cgltf_component_read_index(const void* in, cgltf_component_type component_type) -{ - switch (component_type) - { - case cgltf_component_type_r_16: - return *((const int16_t*) in); - case cgltf_component_type_r_16u: - return *((const uint16_t*) in); - case cgltf_component_type_r_32u: - return *((const uint32_t*) in); - case cgltf_component_type_r_32f: - return (cgltf_size)*((const float*) in); - case cgltf_component_type_r_8: - return *((const int8_t*) in); - case cgltf_component_type_r_8u: - return *((const uint8_t*) in); - default: - return 0; - } -} - -static cgltf_float cgltf_component_read_float(const void* in, cgltf_component_type component_type, cgltf_bool normalized) -{ - if (component_type == cgltf_component_type_r_32f) - { - return *((const float*) in); - } - - if (normalized) - { - switch (component_type) - { - // note: glTF spec doesn't currently define normalized conversions for 32-bit integers - case cgltf_component_type_r_16: - return *((const int16_t*) in) / (cgltf_float)32767; - case cgltf_component_type_r_16u: - return *((const uint16_t*) in) / (cgltf_float)65535; - case cgltf_component_type_r_8: - return *((const int8_t*) in) / (cgltf_float)127; - case cgltf_component_type_r_8u: - return *((const uint8_t*) in) / (cgltf_float)255; - default: - return 0; - } - } - - return (cgltf_float)cgltf_component_read_index(in, component_type); -} - -static cgltf_size cgltf_component_size(cgltf_component_type component_type); - -static cgltf_bool cgltf_element_read_float(const uint8_t* element, cgltf_type type, cgltf_component_type component_type, cgltf_bool normalized, cgltf_float* out, cgltf_size element_size) -{ - cgltf_size num_components = cgltf_num_components(type); - - if (element_size < num_components) { - return 0; - } - - // There are three special cases for component extraction, see #data-alignment in the 2.0 spec. - - cgltf_size component_size = cgltf_component_size(component_type); - - if (type == cgltf_type_mat2 && component_size == 1) - { - out[0] = cgltf_component_read_float(element, component_type, normalized); - out[1] = cgltf_component_read_float(element + 1, component_type, normalized); - out[2] = cgltf_component_read_float(element + 4, component_type, normalized); - out[3] = cgltf_component_read_float(element + 5, component_type, normalized); - return 1; - } - - if (type == cgltf_type_mat3 && component_size == 1) - { - out[0] = cgltf_component_read_float(element, component_type, normalized); - out[1] = cgltf_component_read_float(element + 1, component_type, normalized); - out[2] = cgltf_component_read_float(element + 2, component_type, normalized); - out[3] = cgltf_component_read_float(element + 4, component_type, normalized); - out[4] = cgltf_component_read_float(element + 5, component_type, normalized); - out[5] = cgltf_component_read_float(element + 6, component_type, normalized); - out[6] = cgltf_component_read_float(element + 8, component_type, normalized); - out[7] = cgltf_component_read_float(element + 9, component_type, normalized); - out[8] = cgltf_component_read_float(element + 10, component_type, normalized); - return 1; - } - - if (type == cgltf_type_mat3 && component_size == 2) - { - out[0] = cgltf_component_read_float(element, component_type, normalized); - out[1] = cgltf_component_read_float(element + 2, component_type, normalized); - out[2] = cgltf_component_read_float(element + 4, component_type, normalized); - out[3] = cgltf_component_read_float(element + 8, component_type, normalized); - out[4] = cgltf_component_read_float(element + 10, component_type, normalized); - out[5] = cgltf_component_read_float(element + 12, component_type, normalized); - out[6] = cgltf_component_read_float(element + 16, component_type, normalized); - out[7] = cgltf_component_read_float(element + 18, component_type, normalized); - out[8] = cgltf_component_read_float(element + 20, component_type, normalized); - return 1; - } - - for (cgltf_size i = 0; i < num_components; ++i) - { - out[i] = cgltf_component_read_float(element + component_size * i, component_type, normalized); - } - return 1; -} - -const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view) -{ - if (view->data) - return (const uint8_t*)view->data; - - if (!view->buffer->data) - return NULL; - - const uint8_t* result = (const uint8_t*)view->buffer->data; - result += view->offset; - return result; -} - -cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size) -{ - if (accessor->is_sparse) - { - return 0; - } - if (accessor->buffer_view == NULL) - { - memset(out, 0, element_size * sizeof(cgltf_float)); - return 1; - } - const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); - if (element == NULL) - { - return 0; - } - element += accessor->offset + accessor->stride * index; - return cgltf_element_read_float(element, accessor->type, accessor->component_type, accessor->normalized, out, element_size); -} - -cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count) -{ - cgltf_size floats_per_element = cgltf_num_components(accessor->type); - cgltf_size available_floats = accessor->count * floats_per_element; - if (out == NULL) - { - return available_floats; - } - - float_count = available_floats < float_count ? available_floats : float_count; - cgltf_size element_count = float_count / floats_per_element; - - // First pass: convert each element in the base accessor. - cgltf_float* dest = out; - cgltf_accessor dense = *accessor; - dense.is_sparse = 0; - for (cgltf_size index = 0; index < element_count; index++, dest += floats_per_element) - { - if (!cgltf_accessor_read_float(&dense, index, dest, floats_per_element)) - { - return 0; - } - } - - // Second pass: write out each element in the sparse accessor. - if (accessor->is_sparse) - { - const cgltf_accessor_sparse* sparse = &dense.sparse; - - const uint8_t* index_data = cgltf_buffer_view_data(sparse->indices_buffer_view); - const uint8_t* reader_head = cgltf_buffer_view_data(sparse->values_buffer_view); - - if (index_data == NULL || reader_head == NULL) - { - return 0; - } - - index_data += sparse->indices_byte_offset; - reader_head += sparse->values_byte_offset; - - cgltf_size index_stride = cgltf_component_size(sparse->indices_component_type); - for (cgltf_size reader_index = 0; reader_index < sparse->count; reader_index++, index_data += index_stride) - { - size_t writer_index = cgltf_component_read_index(index_data, sparse->indices_component_type); - float* writer_head = out + writer_index * floats_per_element; - - if (!cgltf_element_read_float(reader_head, dense.type, dense.component_type, dense.normalized, writer_head, floats_per_element)) - { - return 0; - } - - reader_head += dense.stride; - } - } - - return element_count * floats_per_element; -} - -static cgltf_uint cgltf_component_read_uint(const void* in, cgltf_component_type component_type) -{ - switch (component_type) - { - case cgltf_component_type_r_8: - return *((const int8_t*) in); - - case cgltf_component_type_r_8u: - return *((const uint8_t*) in); - - case cgltf_component_type_r_16: - return *((const int16_t*) in); - - case cgltf_component_type_r_16u: - return *((const uint16_t*) in); - - case cgltf_component_type_r_32u: - return *((const uint32_t*) in); - - default: - return 0; - } -} - -static cgltf_bool cgltf_element_read_uint(const uint8_t* element, cgltf_type type, cgltf_component_type component_type, cgltf_uint* out, cgltf_size element_size) -{ - cgltf_size num_components = cgltf_num_components(type); - - if (element_size < num_components) - { - return 0; - } - - // Reading integer matrices is not a valid use case - if (type == cgltf_type_mat2 || type == cgltf_type_mat3 || type == cgltf_type_mat4) - { - return 0; - } - - cgltf_size component_size = cgltf_component_size(component_type); - - for (cgltf_size i = 0; i < num_components; ++i) - { - out[i] = cgltf_component_read_uint(element + component_size * i, component_type); - } - return 1; -} - -cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size) -{ - if (accessor->is_sparse) - { - return 0; - } - if (accessor->buffer_view == NULL) - { - memset(out, 0, element_size * sizeof( cgltf_uint )); - return 1; - } - const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); - if (element == NULL) - { - return 0; - } - element += accessor->offset + accessor->stride * index; - return cgltf_element_read_uint(element, accessor->type, accessor->component_type, out, element_size); -} - -cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index) -{ - if (accessor->is_sparse) - { - return 0; // This is an error case, but we can't communicate the error with existing interface. - } - if (accessor->buffer_view == NULL) - { - return 0; - } - const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); - if (element == NULL) - { - return 0; // This is an error case, but we can't communicate the error with existing interface. - } - element += accessor->offset + accessor->stride * index; - return cgltf_component_read_index(element, accessor->component_type); -} - -#define CGLTF_ERROR_JSON -1 -#define CGLTF_ERROR_NOMEM -2 -#define CGLTF_ERROR_LEGACY -3 - -#define CGLTF_CHECK_TOKTYPE(tok_, type_) if ((tok_).type != (type_)) { return CGLTF_ERROR_JSON; } -#define CGLTF_CHECK_TOKTYPE_RETTYPE(tok_, type_, ret_) if ((tok_).type != (type_)) { return (ret_)CGLTF_ERROR_JSON; } -#define CGLTF_CHECK_KEY(tok_) if ((tok_).type != JSMN_STRING || (tok_).size == 0) { return CGLTF_ERROR_JSON; } /* checking size for 0 verifies that a value follows the key */ - -#define CGLTF_PTRINDEX(type, idx) (type*)((cgltf_size)idx + 1) -#define CGLTF_PTRFIXUP(var, data, size) if (var) { if ((cgltf_size)var > size) { return CGLTF_ERROR_JSON; } var = &data[(cgltf_size)var-1]; } -#define CGLTF_PTRFIXUP_REQ(var, data, size) if (!var || (cgltf_size)var > size) { return CGLTF_ERROR_JSON; } var = &data[(cgltf_size)var-1]; - -static int cgltf_json_strcmp(jsmntok_t const* tok, const uint8_t* json_chunk, const char* str) -{ - CGLTF_CHECK_TOKTYPE(*tok, JSMN_STRING); - size_t const str_len = strlen(str); - size_t const name_length = tok->end - tok->start; - return (str_len == name_length) ? strncmp((const char*)json_chunk + tok->start, str, str_len) : 128; -} - -static int cgltf_json_to_int(jsmntok_t const* tok, const uint8_t* json_chunk) -{ - CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE); - char tmp[128]; - int size = (cgltf_size)(tok->end - tok->start) < sizeof(tmp) ? tok->end - tok->start : (int)(sizeof(tmp) - 1); - strncpy(tmp, (const char*)json_chunk + tok->start, size); - tmp[size] = 0; - return CGLTF_ATOI(tmp); -} - -static cgltf_size cgltf_json_to_size(jsmntok_t const* tok, const uint8_t* json_chunk) -{ - CGLTF_CHECK_TOKTYPE_RETTYPE(*tok, JSMN_PRIMITIVE, cgltf_size); - char tmp[128]; - int size = (cgltf_size)(tok->end - tok->start) < sizeof(tmp) ? tok->end - tok->start : (int)(sizeof(tmp) - 1); - strncpy(tmp, (const char*)json_chunk + tok->start, size); - tmp[size] = 0; - return (cgltf_size)CGLTF_ATOLL(tmp); -} - -static cgltf_float cgltf_json_to_float(jsmntok_t const* tok, const uint8_t* json_chunk) -{ - CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE); - char tmp[128]; - int size = (cgltf_size)(tok->end - tok->start) < sizeof(tmp) ? tok->end - tok->start : (int)(sizeof(tmp) - 1); - strncpy(tmp, (const char*)json_chunk + tok->start, size); - tmp[size] = 0; - return (cgltf_float)CGLTF_ATOF(tmp); -} - -static cgltf_bool cgltf_json_to_bool(jsmntok_t const* tok, const uint8_t* json_chunk) -{ - int size = tok->end - tok->start; - return size == 4 && memcmp(json_chunk + tok->start, "true", 4) == 0; -} - -static int cgltf_skip_json(jsmntok_t const* tokens, int i) -{ - int end = i + 1; - - while (i < end) - { - switch (tokens[i].type) - { - case JSMN_OBJECT: - end += tokens[i].size * 2; - break; - - case JSMN_ARRAY: - end += tokens[i].size; - break; - - case JSMN_PRIMITIVE: - case JSMN_STRING: - break; - - default: - return -1; - } - - i++; - } - - return i; -} - -static void cgltf_fill_float_array(float* out_array, int size, float value) -{ - for (int j = 0; j < size; ++j) - { - out_array[j] = value; - } -} - -static int cgltf_parse_json_float_array(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, float* out_array, int size) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); - if (tokens[i].size != size) - { - return CGLTF_ERROR_JSON; - } - ++i; - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); - out_array[j] = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - return i; -} - -static int cgltf_parse_json_string(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, char** out_string) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_STRING); - if (*out_string) - { - return CGLTF_ERROR_JSON; - } - int size = tokens[i].end - tokens[i].start; - char* result = (char*)options->memory.alloc(options->memory.user_data, size + 1); - if (!result) - { - return CGLTF_ERROR_NOMEM; - } - strncpy(result, (const char*)json_chunk + tokens[i].start, size); - result[size] = 0; - *out_string = result; - return i + 1; -} - -static int cgltf_parse_json_array(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, size_t element_size, void** out_array, cgltf_size* out_size) -{ - (void)json_chunk; - if (tokens[i].type != JSMN_ARRAY) - { - return tokens[i].type == JSMN_OBJECT ? CGLTF_ERROR_LEGACY : CGLTF_ERROR_JSON; - } - if (*out_array) - { - return CGLTF_ERROR_JSON; - } - int size = tokens[i].size; - void* result = cgltf_calloc(options, element_size, size); - if (!result) - { - return CGLTF_ERROR_NOMEM; - } - *out_array = result; - *out_size = size; - return i + 1; -} - -static int cgltf_parse_json_string_array(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, char*** out_array, cgltf_size* out_size) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); - i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(char*), (void**)out_array, out_size); - if (i < 0) - { - return i; - } - - for (cgltf_size j = 0; j < *out_size; ++j) - { - i = cgltf_parse_json_string(options, tokens, i, json_chunk, j + (*out_array)); - if (i < 0) - { - return i; - } - } - return i; -} - -static void cgltf_parse_attribute_type(const char* name, cgltf_attribute_type* out_type, int* out_index) -{ - const char* us = strchr(name, '_'); - size_t len = us ? (size_t)(us - name) : strlen(name); - - if (len == 8 && strncmp(name, "POSITION", 8) == 0) - { - *out_type = cgltf_attribute_type_position; - } - else if (len == 6 && strncmp(name, "NORMAL", 6) == 0) - { - *out_type = cgltf_attribute_type_normal; - } - else if (len == 7 && strncmp(name, "TANGENT", 7) == 0) - { - *out_type = cgltf_attribute_type_tangent; - } - else if (len == 8 && strncmp(name, "TEXCOORD", 8) == 0) - { - *out_type = cgltf_attribute_type_texcoord; - } - else if (len == 5 && strncmp(name, "COLOR", 5) == 0) - { - *out_type = cgltf_attribute_type_color; - } - else if (len == 6 && strncmp(name, "JOINTS", 6) == 0) - { - *out_type = cgltf_attribute_type_joints; - } - else if (len == 7 && strncmp(name, "WEIGHTS", 7) == 0) - { - *out_type = cgltf_attribute_type_weights; - } - else - { - *out_type = cgltf_attribute_type_invalid; - } - - if (us && *out_type != cgltf_attribute_type_invalid) - { - *out_index = CGLTF_ATOI(us + 1); - } -} - -static int cgltf_parse_json_attribute_list(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_attribute** out_attributes, cgltf_size* out_attributes_count) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - if (*out_attributes) - { - return CGLTF_ERROR_JSON; - } - - *out_attributes_count = tokens[i].size; - *out_attributes = (cgltf_attribute*)cgltf_calloc(options, sizeof(cgltf_attribute), *out_attributes_count); - ++i; - - if (!*out_attributes) - { - return CGLTF_ERROR_NOMEM; - } - - for (cgltf_size j = 0; j < *out_attributes_count; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - i = cgltf_parse_json_string(options, tokens, i, json_chunk, &(*out_attributes)[j].name); - if (i < 0) - { - return CGLTF_ERROR_JSON; - } - - cgltf_parse_attribute_type((*out_attributes)[j].name, &(*out_attributes)[j].type, &(*out_attributes)[j].index); - - (*out_attributes)[j].data = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - - return i; -} - -static int cgltf_parse_json_extras(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extras* out_extras) -{ - (void)json_chunk; - out_extras->start_offset = tokens[i].start; - out_extras->end_offset = tokens[i].end; - i = cgltf_skip_json(tokens, i); - return i; -} - -static int cgltf_parse_json_unprocessed_extension(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extension* out_extension) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_STRING); - CGLTF_CHECK_TOKTYPE(tokens[i+1], JSMN_OBJECT); - if (out_extension->name) - { - return CGLTF_ERROR_JSON; - } - - cgltf_size name_length = tokens[i].end - tokens[i].start; - out_extension->name = (char*)options->memory.alloc(options->memory.user_data, name_length + 1); - if (!out_extension->name) - { - return CGLTF_ERROR_NOMEM; - } - strncpy(out_extension->name, (const char*)json_chunk + tokens[i].start, name_length); - out_extension->name[name_length] = 0; - i++; - - size_t start = tokens[i].start; - size_t size = tokens[i].end - start; - out_extension->data = (char*)options->memory.alloc(options->memory.user_data, size + 1); - if (!out_extension->data) - { - return CGLTF_ERROR_NOMEM; - } - strncpy(out_extension->data, (const char*)json_chunk + start, size); - out_extension->data[size] = '\0'; - - i = cgltf_skip_json(tokens, i); - - return i; -} - -static int cgltf_parse_json_unprocessed_extensions(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_size* out_extensions_count, cgltf_extension** out_extensions) -{ - ++i; - - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - if(*out_extensions) - { - return CGLTF_ERROR_JSON; - } - - int extensions_size = tokens[i].size; - *out_extensions_count = 0; - *out_extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); - - if (!*out_extensions) - { - return CGLTF_ERROR_NOMEM; - } - - ++i; - - for (int j = 0; j < extensions_size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - cgltf_size extension_index = (*out_extensions_count)++; - cgltf_extension* extension = &((*out_extensions)[extension_index]); - i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, extension); - - if (i < 0) - { - return i; - } - } - return i; -} - -static int cgltf_parse_json_draco_mesh_compression(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_draco_mesh_compression* out_draco_mesh_compression) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens + i, json_chunk, "attributes") == 0) - { - i = cgltf_parse_json_attribute_list(options, tokens, i + 1, json_chunk, &out_draco_mesh_compression->attributes, &out_draco_mesh_compression->attributes_count); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "bufferView") == 0) - { - ++i; - out_draco_mesh_compression->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material_mapping* out_mappings, cgltf_size* offset) -{ - (void)options; - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int obj_size = tokens[i].size; - ++i; - - int material = -1; - int variants_tok = -1; - cgltf_extras extras = {0, 0}; - - for (int k = 0; k < obj_size; ++k) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens + i, json_chunk, "material") == 0) - { - ++i; - material = cgltf_json_to_int(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "variants") == 0) - { - variants_tok = i+1; - CGLTF_CHECK_TOKTYPE(tokens[variants_tok], JSMN_ARRAY); - - i = cgltf_skip_json(tokens, i+1); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &extras); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - if (material < 0 || variants_tok < 0) - { - return CGLTF_ERROR_JSON; - } - - if (out_mappings) - { - for (int k = 0; k < tokens[variants_tok].size; ++k) - { - int variant = cgltf_json_to_int(&tokens[variants_tok + 1 + k], json_chunk); - if (variant < 0) - return variant; - - out_mappings[*offset].material = CGLTF_PTRINDEX(cgltf_material, material); - out_mappings[*offset].variant = variant; - out_mappings[*offset].extras = extras; - - (*offset)++; - } - } - else - { - (*offset) += tokens[variants_tok].size; - } - } - - return i; -} - -static int cgltf_parse_json_material_mappings(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_primitive* out_prim) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens + i, json_chunk, "mappings") == 0) - { - if (out_prim->mappings) - { - return CGLTF_ERROR_JSON; - } - - cgltf_size mappings_offset = 0; - int k = cgltf_parse_json_material_mapping_data(options, tokens, i + 1, json_chunk, NULL, &mappings_offset); - if (k < 0) - { - return k; - } - - out_prim->mappings_count = mappings_offset; - out_prim->mappings = (cgltf_material_mapping*)cgltf_calloc(options, sizeof(cgltf_material_mapping), out_prim->mappings_count); - - mappings_offset = 0; - i = cgltf_parse_json_material_mapping_data(options, tokens, i + 1, json_chunk, out_prim->mappings, &mappings_offset); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_primitive* out_prim) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - out_prim->type = cgltf_primitive_type_triangles; - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0) - { - ++i; - out_prim->type - = (cgltf_primitive_type) - cgltf_json_to_int(tokens+i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0) - { - ++i; - out_prim->indices = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "material") == 0) - { - ++i; - out_prim->material = CGLTF_PTRINDEX(cgltf_material, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "attributes") == 0) - { - i = cgltf_parse_json_attribute_list(options, tokens, i + 1, json_chunk, &out_prim->attributes, &out_prim->attributes_count); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "targets") == 0) - { - i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_morph_target), (void**)&out_prim->targets, &out_prim->targets_count); - if (i < 0) - { - return i; - } - - for (cgltf_size k = 0; k < out_prim->targets_count; ++k) - { - i = cgltf_parse_json_attribute_list(options, tokens, i, json_chunk, &out_prim->targets[k].attributes, &out_prim->targets[k].attributes_count); - if (i < 0) - { - return i; - } - } - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_prim->extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - ++i; - - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - if(out_prim->extensions) - { - return CGLTF_ERROR_JSON; - } - - int extensions_size = tokens[i].size; - out_prim->extensions_count = 0; - out_prim->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); - - if (!out_prim->extensions) - { - return CGLTF_ERROR_NOMEM; - } - - ++i; - for (int k = 0; k < extensions_size; ++k) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_draco_mesh_compression") == 0) - { - out_prim->has_draco_mesh_compression = 1; - i = cgltf_parse_json_draco_mesh_compression(options, tokens, i + 1, json_chunk, &out_prim->draco_mesh_compression); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_variants") == 0) - { - i = cgltf_parse_json_material_mappings(options, tokens, i + 1, json_chunk, out_prim); - } - else - { - i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_prim->extensions[out_prim->extensions_count++])); - } - - if (i < 0) - { - return i; - } - } - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_mesh(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_mesh* out_mesh) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_mesh->name); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "primitives") == 0) - { - i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_primitive), (void**)&out_mesh->primitives, &out_mesh->primitives_count); - if (i < 0) - { - return i; - } - - for (cgltf_size prim_index = 0; prim_index < out_mesh->primitives_count; ++prim_index) - { - i = cgltf_parse_json_primitive(options, tokens, i, json_chunk, &out_mesh->primitives[prim_index]); - if (i < 0) - { - return i; - } - } - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "weights") == 0) - { - i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_float), (void**)&out_mesh->weights, &out_mesh->weights_count); - if (i < 0) - { - return i; - } - - i = cgltf_parse_json_float_array(tokens, i - 1, json_chunk, out_mesh->weights, (int)out_mesh->weights_count); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - ++i; - - out_mesh->extras.start_offset = tokens[i].start; - out_mesh->extras.end_offset = tokens[i].end; - - if (tokens[i].type == JSMN_OBJECT) - { - int extras_size = tokens[i].size; - ++i; - - for (int k = 0; k < extras_size; ++k) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "targetNames") == 0 && tokens[i+1].type == JSMN_ARRAY) - { - i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_mesh->target_names, &out_mesh->target_names_count); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - } - else - { - i = cgltf_skip_json(tokens, i); - } - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_mesh->extensions_count, &out_mesh->extensions); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_meshes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) -{ - i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_mesh), (void**)&out_data->meshes, &out_data->meshes_count); - if (i < 0) - { - return i; - } - - for (cgltf_size j = 0; j < out_data->meshes_count; ++j) - { - i = cgltf_parse_json_mesh(options, tokens, i, json_chunk, &out_data->meshes[j]); - if (i < 0) - { - return i; - } - } - return i; -} - -static cgltf_component_type cgltf_json_to_component_type(jsmntok_t const* tok, const uint8_t* json_chunk) -{ - int type = cgltf_json_to_int(tok, json_chunk); - - switch (type) - { - case 5120: - return cgltf_component_type_r_8; - case 5121: - return cgltf_component_type_r_8u; - case 5122: - return cgltf_component_type_r_16; - case 5123: - return cgltf_component_type_r_16u; - case 5125: - return cgltf_component_type_r_32u; - case 5126: - return cgltf_component_type_r_32f; - default: - return cgltf_component_type_invalid; - } -} - -static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_accessor_sparse* out_sparse) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) - { - ++i; - out_sparse->count = cgltf_json_to_int(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0) - { - ++i; - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int indices_size = tokens[i].size; - ++i; - - for (int k = 0; k < indices_size; ++k) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) - { - ++i; - out_sparse->indices_buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) - { - ++i; - out_sparse->indices_byte_offset = cgltf_json_to_size(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0) - { - ++i; - out_sparse->indices_component_type = cgltf_json_to_component_type(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->indices_extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sparse->indices_extensions_count, &out_sparse->indices_extensions); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "values") == 0) - { - ++i; - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int values_size = tokens[i].size; - ++i; - - for (int k = 0; k < values_size; ++k) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) - { - ++i; - out_sparse->values_buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) - { - ++i; - out_sparse->values_byte_offset = cgltf_json_to_size(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->values_extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sparse->values_extensions_count, &out_sparse->values_extensions); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sparse->extensions_count, &out_sparse->extensions); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_accessor(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_accessor* out_accessor) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_accessor->name); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) - { - ++i; - out_accessor->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) - { - ++i; - out_accessor->offset = - cgltf_json_to_size(tokens+i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0) - { - ++i; - out_accessor->component_type = cgltf_json_to_component_type(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "normalized") == 0) - { - ++i; - out_accessor->normalized = cgltf_json_to_bool(tokens+i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) - { - ++i; - out_accessor->count = - cgltf_json_to_int(tokens+i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0) - { - ++i; - if (cgltf_json_strcmp(tokens+i, json_chunk, "SCALAR") == 0) - { - out_accessor->type = cgltf_type_scalar; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC2") == 0) - { - out_accessor->type = cgltf_type_vec2; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC3") == 0) - { - out_accessor->type = cgltf_type_vec3; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC4") == 0) - { - out_accessor->type = cgltf_type_vec4; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT2") == 0) - { - out_accessor->type = cgltf_type_mat2; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT3") == 0) - { - out_accessor->type = cgltf_type_mat3; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT4") == 0) - { - out_accessor->type = cgltf_type_mat4; - } - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "min") == 0) - { - ++i; - out_accessor->has_min = 1; - // note: we can't parse the precise number of elements since type may not have been computed yet - int min_size = tokens[i].size > 16 ? 16 : tokens[i].size; - i = cgltf_parse_json_float_array(tokens, i, json_chunk, out_accessor->min, min_size); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "max") == 0) - { - ++i; - out_accessor->has_max = 1; - // note: we can't parse the precise number of elements since type may not have been computed yet - int max_size = tokens[i].size > 16 ? 16 : tokens[i].size; - i = cgltf_parse_json_float_array(tokens, i, json_chunk, out_accessor->max, max_size); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "sparse") == 0) - { - out_accessor->is_sparse = 1; - i = cgltf_parse_json_accessor_sparse(options, tokens, i + 1, json_chunk, &out_accessor->sparse); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_accessor->extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_accessor->extensions_count, &out_accessor->extensions); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_texture_transform(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture_transform* out_texture_transform) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens + i, json_chunk, "offset") == 0) - { - i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_texture_transform->offset, 2); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "rotation") == 0) - { - ++i; - out_texture_transform->rotation = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0) - { - i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_texture_transform->scale, 2); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "texCoord") == 0) - { - ++i; - out_texture_transform->has_texcoord = 1; - out_texture_transform->texcoord = cgltf_json_to_int(tokens + i, json_chunk); - ++i; - } - else - { - i = cgltf_skip_json(tokens, i + 1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_texture_view(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture_view* out_texture_view) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - out_texture_view->scale = 1.0f; - cgltf_fill_float_array(out_texture_view->transform.scale, 2, 1.0f); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens + i, json_chunk, "index") == 0) - { - ++i; - out_texture_view->texture = CGLTF_PTRINDEX(cgltf_texture, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "texCoord") == 0) - { - ++i; - out_texture_view->texcoord = cgltf_json_to_int(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0) - { - ++i; - out_texture_view->scale = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "strength") == 0) - { - ++i; - out_texture_view->scale = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_texture_view->extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - ++i; - - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - if(out_texture_view->extensions) - { - return CGLTF_ERROR_JSON; - } - - int extensions_size = tokens[i].size; - out_texture_view->extensions_count = 0; - out_texture_view->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); - - if (!out_texture_view->extensions) - { - return CGLTF_ERROR_NOMEM; - } - - ++i; - - for (int k = 0; k < extensions_size; ++k) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_texture_transform") == 0) - { - out_texture_view->has_transform = 1; - i = cgltf_parse_json_texture_transform(tokens, i + 1, json_chunk, &out_texture_view->transform); - } - else - { - i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_texture_view->extensions[out_texture_view->extensions_count++])); - } - - if (i < 0) - { - return i; - } - } - } - else - { - i = cgltf_skip_json(tokens, i + 1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_pbr_metallic_roughness(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_pbr_metallic_roughness* out_pbr) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "metallicFactor") == 0) - { - ++i; - out_pbr->metallic_factor = - cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "roughnessFactor") == 0) - { - ++i; - out_pbr->roughness_factor = - cgltf_json_to_float(tokens+i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorFactor") == 0) - { - i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->base_color_factor, 4); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorTexture") == 0) - { - i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, - &out_pbr->base_color_texture); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "metallicRoughnessTexture") == 0) - { - i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, - &out_pbr->metallic_roughness_texture); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_pbr->extras); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_pbr_specular_glossiness(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_pbr_specular_glossiness* out_pbr) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "diffuseFactor") == 0) - { - i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->diffuse_factor, 4); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularFactor") == 0) - { - i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->specular_factor, 3); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "glossinessFactor") == 0) - { - ++i; - out_pbr->glossiness_factor = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "diffuseTexture") == 0) - { - i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->diffuse_texture); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularGlossinessTexture") == 0) - { - i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->specular_glossiness_texture); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_clearcoat(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_clearcoat* out_clearcoat) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatFactor") == 0) - { - ++i; - out_clearcoat->clearcoat_factor = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatRoughnessFactor") == 0) - { - ++i; - out_clearcoat->clearcoat_roughness_factor = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatTexture") == 0) - { - i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_texture); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatRoughnessTexture") == 0) - { - i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_roughness_texture); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatNormalTexture") == 0) - { - i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_normal_texture); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_ior(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_ior* out_ior) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - int size = tokens[i].size; - ++i; - - // Default values - out_ior->ior = 1.5f; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "ior") == 0) - { - ++i; - out_ior->ior = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_specular(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_specular* out_specular) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - int size = tokens[i].size; - ++i; - - // Default values - out_specular->specular_factor = 1.0f; - cgltf_fill_float_array(out_specular->specular_color_factor, 3, 1.0f); - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "specularFactor") == 0) - { - ++i; - out_specular->specular_factor = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularColorFactor") == 0) - { - i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_specular->specular_color_factor, 3); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularTexture") == 0) - { - i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_specular->specular_texture); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "specularColorTexture") == 0) - { - i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_specular->specular_color_texture); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_transmission(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_transmission* out_transmission) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "transmissionFactor") == 0) - { - ++i; - out_transmission->transmission_factor = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "transmissionTexture") == 0) - { - i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_transmission->transmission_texture); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_volume(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_volume* out_volume) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens + i, json_chunk, "thicknessFactor") == 0) - { - ++i; - out_volume->thickness_factor = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "thicknessTexture") == 0) - { - i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_volume->thickness_texture); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "attenuationColor") == 0) - { - i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_volume->attenuation_color, 3); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "attenuationDistance") == 0) - { - ++i; - out_volume->attenuation_distance = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else - { - i = cgltf_skip_json(tokens, i + 1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_sheen(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_sheen* out_sheen) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenColorFactor") == 0) - { - i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_sheen->sheen_color_factor, 3); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenColorTexture") == 0) - { - i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_sheen->sheen_color_texture); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenRoughnessFactor") == 0) - { - ++i; - out_sheen->sheen_roughness_factor = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenRoughnessTexture") == 0) - { - i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_sheen->sheen_roughness_texture); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_emissive_strength(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_emissive_strength* out_emissive_strength) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - int size = tokens[i].size; - ++i; - - // Default - out_emissive_strength->emissive_strength = 1.f; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens + i, json_chunk, "emissiveStrength") == 0) - { - ++i; - out_emissive_strength->emissive_strength = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else - { - i = cgltf_skip_json(tokens, i + 1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_iridescence(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_iridescence* out_iridescence) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - int size = tokens[i].size; - ++i; - - // Default - out_iridescence->iridescence_ior = 1.3f; - out_iridescence->iridescence_thickness_min = 100.f; - out_iridescence->iridescence_thickness_max = 400.f; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceFactor") == 0) - { - ++i; - out_iridescence->iridescence_factor = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceTexture") == 0) - { - i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_iridescence->iridescence_texture); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceIor") == 0) - { - ++i; - out_iridescence->iridescence_ior = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceThicknessMinimum") == 0) - { - ++i; - out_iridescence->iridescence_thickness_min = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceThicknessMaximum") == 0) - { - ++i; - out_iridescence->iridescence_thickness_max = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceThicknessTexture") == 0) - { - i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_iridescence->iridescence_thickness_texture); - } - else - { - i = cgltf_skip_json(tokens, i + 1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_image* out_image) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens + i, json_chunk, "uri") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->uri); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) - { - ++i; - out_image->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "mimeType") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->mime_type); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->name); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_image->extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_image->extensions_count, &out_image->extensions); - } - else - { - i = cgltf_skip_json(tokens, i + 1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_sampler* out_sampler) -{ - (void)options; - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - out_sampler->wrap_s = 10497; - out_sampler->wrap_t = 10497; - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_sampler->name); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "magFilter") == 0) - { - ++i; - out_sampler->mag_filter - = cgltf_json_to_int(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "minFilter") == 0) - { - ++i; - out_sampler->min_filter - = cgltf_json_to_int(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapS") == 0) - { - ++i; - out_sampler->wrap_s - = cgltf_json_to_int(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapT") == 0) - { - ++i; - out_sampler->wrap_t - = cgltf_json_to_int(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sampler->extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sampler->extensions_count, &out_sampler->extensions); - } - else - { - i = cgltf_skip_json(tokens, i + 1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture* out_texture) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_texture->name); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "sampler") == 0) - { - ++i; - out_texture->sampler = CGLTF_PTRINDEX(cgltf_sampler, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) - { - ++i; - out_texture->image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_texture->extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - ++i; - - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - if (out_texture->extensions) - { - return CGLTF_ERROR_JSON; - } - - int extensions_size = tokens[i].size; - ++i; - out_texture->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); - out_texture->extensions_count = 0; - - if (!out_texture->extensions) - { - return CGLTF_ERROR_NOMEM; - } - - for (int k = 0; k < extensions_size; ++k) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_texture_basisu") == 0) - { - out_texture->has_basisu = 1; - ++i; - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - int num_properties = tokens[i].size; - ++i; - - for (int t = 0; t < num_properties; ++t) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) - { - ++i; - out_texture->basisu_image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else - { - i = cgltf_skip_json(tokens, i + 1); - } - } - } - else - { - i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_texture->extensions[out_texture->extensions_count++])); - } - - if (i < 0) - { - return i; - } - } - } - else - { - i = cgltf_skip_json(tokens, i + 1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material* out_material) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - cgltf_fill_float_array(out_material->pbr_metallic_roughness.base_color_factor, 4, 1.0f); - out_material->pbr_metallic_roughness.metallic_factor = 1.0f; - out_material->pbr_metallic_roughness.roughness_factor = 1.0f; - - cgltf_fill_float_array(out_material->pbr_specular_glossiness.diffuse_factor, 4, 1.0f); - cgltf_fill_float_array(out_material->pbr_specular_glossiness.specular_factor, 3, 1.0f); - out_material->pbr_specular_glossiness.glossiness_factor = 1.0f; - - cgltf_fill_float_array(out_material->volume.attenuation_color, 3, 1.0f); - out_material->volume.attenuation_distance = FLT_MAX; - - out_material->alpha_cutoff = 0.5f; - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_material->name); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "pbrMetallicRoughness") == 0) - { - out_material->has_pbr_metallic_roughness = 1; - i = cgltf_parse_json_pbr_metallic_roughness(options, tokens, i + 1, json_chunk, &out_material->pbr_metallic_roughness); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "emissiveFactor") == 0) - { - i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_material->emissive_factor, 3); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "normalTexture") == 0) - { - i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, - &out_material->normal_texture); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "occlusionTexture") == 0) - { - i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, - &out_material->occlusion_texture); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "emissiveTexture") == 0) - { - i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, - &out_material->emissive_texture); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "alphaMode") == 0) - { - ++i; - if (cgltf_json_strcmp(tokens + i, json_chunk, "OPAQUE") == 0) - { - out_material->alpha_mode = cgltf_alpha_mode_opaque; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "MASK") == 0) - { - out_material->alpha_mode = cgltf_alpha_mode_mask; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "BLEND") == 0) - { - out_material->alpha_mode = cgltf_alpha_mode_blend; - } - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "alphaCutoff") == 0) - { - ++i; - out_material->alpha_cutoff = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "doubleSided") == 0) - { - ++i; - out_material->double_sided = - cgltf_json_to_bool(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_material->extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - ++i; - - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - if(out_material->extensions) - { - return CGLTF_ERROR_JSON; - } - - int extensions_size = tokens[i].size; - ++i; - out_material->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); - out_material->extensions_count= 0; - - if (!out_material->extensions) - { - return CGLTF_ERROR_NOMEM; - } - - for (int k = 0; k < extensions_size; ++k) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_pbrSpecularGlossiness") == 0) - { - out_material->has_pbr_specular_glossiness = 1; - i = cgltf_parse_json_pbr_specular_glossiness(options, tokens, i + 1, json_chunk, &out_material->pbr_specular_glossiness); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_unlit") == 0) - { - out_material->unlit = 1; - i = cgltf_skip_json(tokens, i+1); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_clearcoat") == 0) - { - out_material->has_clearcoat = 1; - i = cgltf_parse_json_clearcoat(options, tokens, i + 1, json_chunk, &out_material->clearcoat); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_ior") == 0) - { - out_material->has_ior = 1; - i = cgltf_parse_json_ior(tokens, i + 1, json_chunk, &out_material->ior); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_specular") == 0) - { - out_material->has_specular = 1; - i = cgltf_parse_json_specular(options, tokens, i + 1, json_chunk, &out_material->specular); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_transmission") == 0) - { - out_material->has_transmission = 1; - i = cgltf_parse_json_transmission(options, tokens, i + 1, json_chunk, &out_material->transmission); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_volume") == 0) - { - out_material->has_volume = 1; - i = cgltf_parse_json_volume(options, tokens, i + 1, json_chunk, &out_material->volume); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_sheen") == 0) - { - out_material->has_sheen = 1; - i = cgltf_parse_json_sheen(options, tokens, i + 1, json_chunk, &out_material->sheen); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_emissive_strength") == 0) - { - out_material->has_emissive_strength = 1; - i = cgltf_parse_json_emissive_strength(tokens, i + 1, json_chunk, &out_material->emissive_strength); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_iridescence") == 0) - { - out_material->has_iridescence = 1; - i = cgltf_parse_json_iridescence(options, tokens, i + 1, json_chunk, &out_material->iridescence); - } - else - { - i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_material->extensions[out_material->extensions_count++])); - } - - if (i < 0) - { - return i; - } - } - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_accessors(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) -{ - i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_accessor), (void**)&out_data->accessors, &out_data->accessors_count); - if (i < 0) - { - return i; - } - - for (cgltf_size j = 0; j < out_data->accessors_count; ++j) - { - i = cgltf_parse_json_accessor(options, tokens, i, json_chunk, &out_data->accessors[j]); - if (i < 0) - { - return i; - } - } - return i; -} - -static int cgltf_parse_json_materials(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) -{ - i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_material), (void**)&out_data->materials, &out_data->materials_count); - if (i < 0) - { - return i; - } - - for (cgltf_size j = 0; j < out_data->materials_count; ++j) - { - i = cgltf_parse_json_material(options, tokens, i, json_chunk, &out_data->materials[j]); - if (i < 0) - { - return i; - } - } - return i; -} - -static int cgltf_parse_json_images(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) -{ - i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_image), (void**)&out_data->images, &out_data->images_count); - if (i < 0) - { - return i; - } - - for (cgltf_size j = 0; j < out_data->images_count; ++j) - { - i = cgltf_parse_json_image(options, tokens, i, json_chunk, &out_data->images[j]); - if (i < 0) - { - return i; - } - } - return i; -} - -static int cgltf_parse_json_textures(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) -{ - i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_texture), (void**)&out_data->textures, &out_data->textures_count); - if (i < 0) - { - return i; - } - - for (cgltf_size j = 0; j < out_data->textures_count; ++j) - { - i = cgltf_parse_json_texture(options, tokens, i, json_chunk, &out_data->textures[j]); - if (i < 0) - { - return i; - } - } - return i; -} - -static int cgltf_parse_json_samplers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) -{ - i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_sampler), (void**)&out_data->samplers, &out_data->samplers_count); - if (i < 0) - { - return i; - } - - for (cgltf_size j = 0; j < out_data->samplers_count; ++j) - { - i = cgltf_parse_json_sampler(options, tokens, i, json_chunk, &out_data->samplers[j]); - if (i < 0) - { - return i; - } - } - return i; -} - -static int cgltf_parse_json_meshopt_compression(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_meshopt_compression* out_meshopt_compression) -{ - (void)options; - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "buffer") == 0) - { - ++i; - out_meshopt_compression->buffer = CGLTF_PTRINDEX(cgltf_buffer, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) - { - ++i; - out_meshopt_compression->offset = cgltf_json_to_size(tokens+i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) - { - ++i; - out_meshopt_compression->size = cgltf_json_to_size(tokens+i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteStride") == 0) - { - ++i; - out_meshopt_compression->stride = cgltf_json_to_size(tokens+i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) - { - ++i; - out_meshopt_compression->count = cgltf_json_to_int(tokens+i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0) - { - ++i; - if (cgltf_json_strcmp(tokens+i, json_chunk, "ATTRIBUTES") == 0) - { - out_meshopt_compression->mode = cgltf_meshopt_compression_mode_attributes; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "TRIANGLES") == 0) - { - out_meshopt_compression->mode = cgltf_meshopt_compression_mode_triangles; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "INDICES") == 0) - { - out_meshopt_compression->mode = cgltf_meshopt_compression_mode_indices; - } - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "filter") == 0) - { - ++i; - if (cgltf_json_strcmp(tokens+i, json_chunk, "NONE") == 0) - { - out_meshopt_compression->filter = cgltf_meshopt_compression_filter_none; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "OCTAHEDRAL") == 0) - { - out_meshopt_compression->filter = cgltf_meshopt_compression_filter_octahedral; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "QUATERNION") == 0) - { - out_meshopt_compression->filter = cgltf_meshopt_compression_filter_quaternion; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "EXPONENTIAL") == 0) - { - out_meshopt_compression->filter = cgltf_meshopt_compression_filter_exponential; - } - ++i; - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_buffer_view(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_buffer_view* out_buffer_view) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer_view->name); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "buffer") == 0) - { - ++i; - out_buffer_view->buffer = CGLTF_PTRINDEX(cgltf_buffer, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) - { - ++i; - out_buffer_view->offset = - cgltf_json_to_size(tokens+i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) - { - ++i; - out_buffer_view->size = - cgltf_json_to_size(tokens+i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteStride") == 0) - { - ++i; - out_buffer_view->stride = - cgltf_json_to_size(tokens+i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "target") == 0) - { - ++i; - int type = cgltf_json_to_int(tokens+i, json_chunk); - switch (type) - { - case 34962: - type = cgltf_buffer_view_type_vertices; - break; - case 34963: - type = cgltf_buffer_view_type_indices; - break; - default: - type = cgltf_buffer_view_type_invalid; - break; - } - out_buffer_view->type = (cgltf_buffer_view_type)type; - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_buffer_view->extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - ++i; - - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - if(out_buffer_view->extensions) - { - return CGLTF_ERROR_JSON; - } - - int extensions_size = tokens[i].size; - out_buffer_view->extensions_count = 0; - out_buffer_view->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); - - if (!out_buffer_view->extensions) - { - return CGLTF_ERROR_NOMEM; - } - - ++i; - for (int k = 0; k < extensions_size; ++k) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "EXT_meshopt_compression") == 0) - { - out_buffer_view->has_meshopt_compression = 1; - i = cgltf_parse_json_meshopt_compression(options, tokens, i + 1, json_chunk, &out_buffer_view->meshopt_compression); - } - else - { - i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_buffer_view->extensions[out_buffer_view->extensions_count++])); - } - - if (i < 0) - { - return i; - } - } - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_buffer_views(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) -{ - i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_buffer_view), (void**)&out_data->buffer_views, &out_data->buffer_views_count); - if (i < 0) - { - return i; - } - - for (cgltf_size j = 0; j < out_data->buffer_views_count; ++j) - { - i = cgltf_parse_json_buffer_view(options, tokens, i, json_chunk, &out_data->buffer_views[j]); - if (i < 0) - { - return i; - } - } - return i; -} - -static int cgltf_parse_json_buffer(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_buffer* out_buffer) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer->name); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) - { - ++i; - out_buffer->size = - cgltf_json_to_size(tokens+i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "uri") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer->uri); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_buffer->extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_buffer->extensions_count, &out_buffer->extensions); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_buffers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) -{ - i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_buffer), (void**)&out_data->buffers, &out_data->buffers_count); - if (i < 0) - { - return i; - } - - for (cgltf_size j = 0; j < out_data->buffers_count; ++j) - { - i = cgltf_parse_json_buffer(options, tokens, i, json_chunk, &out_data->buffers[j]); - if (i < 0) - { - return i; - } - } - return i; -} - -static int cgltf_parse_json_skin(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_skin* out_skin) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_skin->name); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "joints") == 0) - { - i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_skin->joints, &out_skin->joints_count); - if (i < 0) - { - return i; - } - - for (cgltf_size k = 0; k < out_skin->joints_count; ++k) - { - out_skin->joints[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "skeleton") == 0) - { - ++i; - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); - out_skin->skeleton = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "inverseBindMatrices") == 0) - { - ++i; - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); - out_skin->inverse_bind_matrices = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_skin->extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_skin->extensions_count, &out_skin->extensions); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_skins(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) -{ - i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_skin), (void**)&out_data->skins, &out_data->skins_count); - if (i < 0) - { - return i; - } - - for (cgltf_size j = 0; j < out_data->skins_count; ++j) - { - i = cgltf_parse_json_skin(options, tokens, i, json_chunk, &out_data->skins[j]); - if (i < 0) - { - return i; - } - } - return i; -} - -static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_camera* out_camera) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_camera->name); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0) - { - ++i; - if (cgltf_json_strcmp(tokens + i, json_chunk, "perspective") == 0) - { - out_camera->type = cgltf_camera_type_perspective; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "orthographic") == 0) - { - out_camera->type = cgltf_camera_type_orthographic; - } - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "perspective") == 0) - { - ++i; - - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int data_size = tokens[i].size; - ++i; - - out_camera->type = cgltf_camera_type_perspective; - - for (int k = 0; k < data_size; ++k) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "aspectRatio") == 0) - { - ++i; - out_camera->data.perspective.has_aspect_ratio = 1; - out_camera->data.perspective.aspect_ratio = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "yfov") == 0) - { - ++i; - out_camera->data.perspective.yfov = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "zfar") == 0) - { - ++i; - out_camera->data.perspective.has_zfar = 1; - out_camera->data.perspective.zfar = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "znear") == 0) - { - ++i; - out_camera->data.perspective.znear = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->data.perspective.extras); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "orthographic") == 0) - { - ++i; - - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int data_size = tokens[i].size; - ++i; - - out_camera->type = cgltf_camera_type_orthographic; - - for (int k = 0; k < data_size; ++k) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "xmag") == 0) - { - ++i; - out_camera->data.orthographic.xmag = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "ymag") == 0) - { - ++i; - out_camera->data.orthographic.ymag = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "zfar") == 0) - { - ++i; - out_camera->data.orthographic.zfar = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "znear") == 0) - { - ++i; - out_camera->data.orthographic.znear = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->data.orthographic.extras); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_camera->extensions_count, &out_camera->extensions); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_cameras(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) -{ - i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_camera), (void**)&out_data->cameras, &out_data->cameras_count); - if (i < 0) - { - return i; - } - - for (cgltf_size j = 0; j < out_data->cameras_count; ++j) - { - i = cgltf_parse_json_camera(options, tokens, i, json_chunk, &out_data->cameras[j]); - if (i < 0) - { - return i; - } - } - return i; -} - -static int cgltf_parse_json_light(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_light* out_light) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - out_light->color[0] = 1.f; - out_light->color[1] = 1.f; - out_light->color[2] = 1.f; - out_light->intensity = 1.f; - - out_light->spot_inner_cone_angle = 0.f; - out_light->spot_outer_cone_angle = 3.1415926535f / 4.0f; - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_light->name); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "color") == 0) - { - i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_light->color, 3); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "intensity") == 0) - { - ++i; - out_light->intensity = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0) - { - ++i; - if (cgltf_json_strcmp(tokens + i, json_chunk, "directional") == 0) - { - out_light->type = cgltf_light_type_directional; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "point") == 0) - { - out_light->type = cgltf_light_type_point; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "spot") == 0) - { - out_light->type = cgltf_light_type_spot; - } - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "range") == 0) - { - ++i; - out_light->range = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "spot") == 0) - { - ++i; - - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int data_size = tokens[i].size; - ++i; - - for (int k = 0; k < data_size; ++k) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "innerConeAngle") == 0) - { - ++i; - out_light->spot_inner_cone_angle = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "outerConeAngle") == 0) - { - ++i; - out_light->spot_outer_cone_angle = cgltf_json_to_float(tokens + i, json_chunk); - ++i; - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_light->extras); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_lights(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) -{ - i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_light), (void**)&out_data->lights, &out_data->lights_count); - if (i < 0) - { - return i; - } - - for (cgltf_size j = 0; j < out_data->lights_count; ++j) - { - i = cgltf_parse_json_light(options, tokens, i, json_chunk, &out_data->lights[j]); - if (i < 0) - { - return i; - } - } - return i; -} - -static int cgltf_parse_json_node(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_node* out_node) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - out_node->rotation[3] = 1.0f; - out_node->scale[0] = 1.0f; - out_node->scale[1] = 1.0f; - out_node->scale[2] = 1.0f; - out_node->matrix[0] = 1.0f; - out_node->matrix[5] = 1.0f; - out_node->matrix[10] = 1.0f; - out_node->matrix[15] = 1.0f; - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_node->name); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "children") == 0) - { - i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_node->children, &out_node->children_count); - if (i < 0) - { - return i; - } - - for (cgltf_size k = 0; k < out_node->children_count; ++k) - { - out_node->children[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "mesh") == 0) - { - ++i; - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); - out_node->mesh = CGLTF_PTRINDEX(cgltf_mesh, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "skin") == 0) - { - ++i; - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); - out_node->skin = CGLTF_PTRINDEX(cgltf_skin, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "camera") == 0) - { - ++i; - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); - out_node->camera = CGLTF_PTRINDEX(cgltf_camera, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "translation") == 0) - { - out_node->has_translation = 1; - i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->translation, 3); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "rotation") == 0) - { - out_node->has_rotation = 1; - i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->rotation, 4); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "scale") == 0) - { - out_node->has_scale = 1; - i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->scale, 3); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "matrix") == 0) - { - out_node->has_matrix = 1; - i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->matrix, 16); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "weights") == 0) - { - i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_float), (void**)&out_node->weights, &out_node->weights_count); - if (i < 0) - { - return i; - } - - i = cgltf_parse_json_float_array(tokens, i - 1, json_chunk, out_node->weights, (int)out_node->weights_count); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_node->extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - ++i; - - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - if(out_node->extensions) - { - return CGLTF_ERROR_JSON; - } - - int extensions_size = tokens[i].size; - out_node->extensions_count= 0; - out_node->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); - - if (!out_node->extensions) - { - return CGLTF_ERROR_NOMEM; - } - - ++i; - - for (int k = 0; k < extensions_size; ++k) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_lights_punctual") == 0) - { - ++i; - - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int data_size = tokens[i].size; - ++i; - - for (int m = 0; m < data_size; ++m) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens + i, json_chunk, "light") == 0) - { - ++i; - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); - out_node->light = CGLTF_PTRINDEX(cgltf_light, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else - { - i = cgltf_skip_json(tokens, i + 1); - } - - if (i < 0) - { - return i; - } - } - } - else - { - i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_node->extensions[out_node->extensions_count++])); - } - - if (i < 0) - { - return i; - } - } - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_nodes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) -{ - i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_node), (void**)&out_data->nodes, &out_data->nodes_count); - if (i < 0) - { - return i; - } - - for (cgltf_size j = 0; j < out_data->nodes_count; ++j) - { - i = cgltf_parse_json_node(options, tokens, i, json_chunk, &out_data->nodes[j]); - if (i < 0) - { - return i; - } - } - return i; -} - -static int cgltf_parse_json_scene(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_scene* out_scene) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_scene->name); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "nodes") == 0) - { - i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_scene->nodes, &out_scene->nodes_count); - if (i < 0) - { - return i; - } - - for (cgltf_size k = 0; k < out_scene->nodes_count; ++k) - { - out_scene->nodes[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_scene->extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_scene->extensions_count, &out_scene->extensions); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_scenes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) -{ - i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_scene), (void**)&out_data->scenes, &out_data->scenes_count); - if (i < 0) - { - return i; - } - - for (cgltf_size j = 0; j < out_data->scenes_count; ++j) - { - i = cgltf_parse_json_scene(options, tokens, i, json_chunk, &out_data->scenes[j]); - if (i < 0) - { - return i; - } - } - return i; -} - -static int cgltf_parse_json_animation_sampler(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation_sampler* out_sampler) -{ - (void)options; - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "input") == 0) - { - ++i; - out_sampler->input = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "output") == 0) - { - ++i; - out_sampler->output = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "interpolation") == 0) - { - ++i; - if (cgltf_json_strcmp(tokens + i, json_chunk, "LINEAR") == 0) - { - out_sampler->interpolation = cgltf_interpolation_type_linear; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "STEP") == 0) - { - out_sampler->interpolation = cgltf_interpolation_type_step; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "CUBICSPLINE") == 0) - { - out_sampler->interpolation = cgltf_interpolation_type_cubic_spline; - } - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sampler->extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sampler->extensions_count, &out_sampler->extensions); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_animation_channel(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation_channel* out_channel) -{ - (void)options; - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "sampler") == 0) - { - ++i; - out_channel->sampler = CGLTF_PTRINDEX(cgltf_animation_sampler, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "target") == 0) - { - ++i; - - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int target_size = tokens[i].size; - ++i; - - for (int k = 0; k < target_size; ++k) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "node") == 0) - { - ++i; - out_channel->target_node = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "path") == 0) - { - ++i; - if (cgltf_json_strcmp(tokens+i, json_chunk, "translation") == 0) - { - out_channel->target_path = cgltf_animation_path_type_translation; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "rotation") == 0) - { - out_channel->target_path = cgltf_animation_path_type_rotation; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "scale") == 0) - { - out_channel->target_path = cgltf_animation_path_type_scale; - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "weights") == 0) - { - out_channel->target_path = cgltf_animation_path_type_weights; - } - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_channel->extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_channel->extensions_count, &out_channel->extensions); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_animation(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation* out_animation) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_animation->name); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "samplers") == 0) - { - i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_animation_sampler), (void**)&out_animation->samplers, &out_animation->samplers_count); - if (i < 0) - { - return i; - } - - for (cgltf_size k = 0; k < out_animation->samplers_count; ++k) - { - i = cgltf_parse_json_animation_sampler(options, tokens, i, json_chunk, &out_animation->samplers[k]); - if (i < 0) - { - return i; - } - } - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "channels") == 0) - { - i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_animation_channel), (void**)&out_animation->channels, &out_animation->channels_count); - if (i < 0) - { - return i; - } - - for (cgltf_size k = 0; k < out_animation->channels_count; ++k) - { - i = cgltf_parse_json_animation_channel(options, tokens, i, json_chunk, &out_animation->channels[k]); - if (i < 0) - { - return i; - } - } - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_animation->extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_animation->extensions_count, &out_animation->extensions); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_animations(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) -{ - i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_animation), (void**)&out_data->animations, &out_data->animations_count); - if (i < 0) - { - return i; - } - - for (cgltf_size j = 0; j < out_data->animations_count; ++j) - { - i = cgltf_parse_json_animation(options, tokens, i, json_chunk, &out_data->animations[j]); - if (i < 0) - { - return i; - } - } - return i; -} - -static int cgltf_parse_json_variant(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material_variant* out_variant) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_variant->name); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_variant->extras); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -static int cgltf_parse_json_variants(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) -{ - i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_material_variant), (void**)&out_data->variants, &out_data->variants_count); - if (i < 0) - { - return i; - } - - for (cgltf_size j = 0; j < out_data->variants_count; ++j) - { - i = cgltf_parse_json_variant(options, tokens, i, json_chunk, &out_data->variants[j]); - if (i < 0) - { - return i; - } - } - return i; -} - -static int cgltf_parse_json_asset(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_asset* out_asset) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "copyright") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->copyright); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "generator") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->generator); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "version") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->version); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "minVersion") == 0) - { - i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->min_version); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_asset->extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_asset->extensions_count, &out_asset->extensions); - } - else - { - i = cgltf_skip_json(tokens, i+1); - } - - if (i < 0) - { - return i; - } - } - - if (out_asset->version && CGLTF_ATOF(out_asset->version) < 2) - { - return CGLTF_ERROR_LEGACY; - } - - return i; -} - -cgltf_size cgltf_num_components(cgltf_type type) { - switch (type) - { - case cgltf_type_vec2: - return 2; - case cgltf_type_vec3: - return 3; - case cgltf_type_vec4: - return 4; - case cgltf_type_mat2: - return 4; - case cgltf_type_mat3: - return 9; - case cgltf_type_mat4: - return 16; - case cgltf_type_invalid: - case cgltf_type_scalar: - default: - return 1; - } -} - -static cgltf_size cgltf_component_size(cgltf_component_type component_type) { - switch (component_type) - { - case cgltf_component_type_r_8: - case cgltf_component_type_r_8u: - return 1; - case cgltf_component_type_r_16: - case cgltf_component_type_r_16u: - return 2; - case cgltf_component_type_r_32u: - case cgltf_component_type_r_32f: - return 4; - case cgltf_component_type_invalid: - default: - return 0; - } -} - -static cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type) -{ - cgltf_size component_size = cgltf_component_size(component_type); - if (type == cgltf_type_mat2 && component_size == 1) - { - return 8 * component_size; - } - else if (type == cgltf_type_mat3 && (component_size == 1 || component_size == 2)) - { - return 12 * component_size; - } - return component_size * cgltf_num_components(type); -} - -static int cgltf_fixup_pointers(cgltf_data* out_data); - -static int cgltf_parse_json_root(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) -{ - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int size = tokens[i].size; - ++i; - - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens + i, json_chunk, "asset") == 0) - { - i = cgltf_parse_json_asset(options, tokens, i + 1, json_chunk, &out_data->asset); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "meshes") == 0) - { - i = cgltf_parse_json_meshes(options, tokens, i + 1, json_chunk, out_data); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "accessors") == 0) - { - i = cgltf_parse_json_accessors(options, tokens, i + 1, json_chunk, out_data); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "bufferViews") == 0) - { - i = cgltf_parse_json_buffer_views(options, tokens, i + 1, json_chunk, out_data); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "buffers") == 0) - { - i = cgltf_parse_json_buffers(options, tokens, i + 1, json_chunk, out_data); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "materials") == 0) - { - i = cgltf_parse_json_materials(options, tokens, i + 1, json_chunk, out_data); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "images") == 0) - { - i = cgltf_parse_json_images(options, tokens, i + 1, json_chunk, out_data); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "textures") == 0) - { - i = cgltf_parse_json_textures(options, tokens, i + 1, json_chunk, out_data); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "samplers") == 0) - { - i = cgltf_parse_json_samplers(options, tokens, i + 1, json_chunk, out_data); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "skins") == 0) - { - i = cgltf_parse_json_skins(options, tokens, i + 1, json_chunk, out_data); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "cameras") == 0) - { - i = cgltf_parse_json_cameras(options, tokens, i + 1, json_chunk, out_data); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "nodes") == 0) - { - i = cgltf_parse_json_nodes(options, tokens, i + 1, json_chunk, out_data); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "scenes") == 0) - { - i = cgltf_parse_json_scenes(options, tokens, i + 1, json_chunk, out_data); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "scene") == 0) - { - ++i; - out_data->scene = CGLTF_PTRINDEX(cgltf_scene, cgltf_json_to_int(tokens + i, json_chunk)); - ++i; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "animations") == 0) - { - i = cgltf_parse_json_animations(options, tokens, i + 1, json_chunk, out_data); - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_data->extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - ++i; - - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - if(out_data->data_extensions) - { - return CGLTF_ERROR_JSON; - } - - int extensions_size = tokens[i].size; - out_data->data_extensions_count = 0; - out_data->data_extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); - - if (!out_data->data_extensions) - { - return CGLTF_ERROR_NOMEM; - } - - ++i; - - for (int k = 0; k < extensions_size; ++k) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_lights_punctual") == 0) - { - ++i; - - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int data_size = tokens[i].size; - ++i; - - for (int m = 0; m < data_size; ++m) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens + i, json_chunk, "lights") == 0) - { - i = cgltf_parse_json_lights(options, tokens, i + 1, json_chunk, out_data); - } - else - { - i = cgltf_skip_json(tokens, i + 1); - } - - if (i < 0) - { - return i; - } - } - } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_variants") == 0) - { - ++i; - - CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - - int data_size = tokens[i].size; - ++i; - - for (int m = 0; m < data_size; ++m) - { - CGLTF_CHECK_KEY(tokens[i]); - - if (cgltf_json_strcmp(tokens + i, json_chunk, "variants") == 0) - { - i = cgltf_parse_json_variants(options, tokens, i + 1, json_chunk, out_data); - } - else - { - i = cgltf_skip_json(tokens, i + 1); - } - - if (i < 0) - { - return i; - } - } - } - else - { - i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_data->data_extensions[out_data->data_extensions_count++])); - } - - if (i < 0) - { - return i; - } - } - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensionsUsed") == 0) - { - i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_data->extensions_used, &out_data->extensions_used_count); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensionsRequired") == 0) - { - i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_data->extensions_required, &out_data->extensions_required_count); - } - else - { - i = cgltf_skip_json(tokens, i + 1); - } - - if (i < 0) - { - return i; - } - } - - return i; -} - -cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data** out_data) -{ - jsmn_parser parser = { 0, 0, 0 }; - - if (options->json_token_count == 0) - { - int token_count = jsmn_parse(&parser, (const char*)json_chunk, size, NULL, 0); - - if (token_count <= 0) - { - return cgltf_result_invalid_json; - } - - options->json_token_count = token_count; - } - - jsmntok_t* tokens = (jsmntok_t*)options->memory.alloc(options->memory.user_data, sizeof(jsmntok_t) * (options->json_token_count + 1)); - - if (!tokens) - { - return cgltf_result_out_of_memory; - } - - jsmn_init(&parser); - - int token_count = jsmn_parse(&parser, (const char*)json_chunk, size, tokens, options->json_token_count); - - if (token_count <= 0) - { - options->memory.free(options->memory.user_data, tokens); - return cgltf_result_invalid_json; - } - - // this makes sure that we always have an UNDEFINED token at the end of the stream - // for invalid JSON inputs this makes sure we don't perform out of bound reads of token data - tokens[token_count].type = JSMN_UNDEFINED; - - cgltf_data* data = (cgltf_data*)options->memory.alloc(options->memory.user_data, sizeof(cgltf_data)); - - if (!data) - { - options->memory.free(options->memory.user_data, tokens); - return cgltf_result_out_of_memory; - } - - memset(data, 0, sizeof(cgltf_data)); - data->memory = options->memory; - data->file = options->file; - - int i = cgltf_parse_json_root(options, tokens, 0, json_chunk, data); - - options->memory.free(options->memory.user_data, tokens); - - if (i < 0) - { - cgltf_free(data); - - switch (i) - { - case CGLTF_ERROR_NOMEM: return cgltf_result_out_of_memory; - case CGLTF_ERROR_LEGACY: return cgltf_result_legacy_gltf; - default: return cgltf_result_invalid_gltf; - } - } - - if (cgltf_fixup_pointers(data) < 0) - { - cgltf_free(data); - return cgltf_result_invalid_gltf; - } - - data->json = (const char*)json_chunk; - data->json_size = size; - - *out_data = data; - - return cgltf_result_success; -} - -static int cgltf_fixup_pointers(cgltf_data* data) -{ - for (cgltf_size i = 0; i < data->meshes_count; ++i) - { - for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) - { - CGLTF_PTRFIXUP(data->meshes[i].primitives[j].indices, data->accessors, data->accessors_count); - CGLTF_PTRFIXUP(data->meshes[i].primitives[j].material, data->materials, data->materials_count); - - for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k) - { - CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].attributes[k].data, data->accessors, data->accessors_count); - } - - for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k) - { - for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m) - { - CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].targets[k].attributes[m].data, data->accessors, data->accessors_count); - } - } - - if (data->meshes[i].primitives[j].has_draco_mesh_compression) - { - CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].draco_mesh_compression.buffer_view, data->buffer_views, data->buffer_views_count); - for (cgltf_size m = 0; m < data->meshes[i].primitives[j].draco_mesh_compression.attributes_count; ++m) - { - CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].draco_mesh_compression.attributes[m].data, data->accessors, data->accessors_count); - } - } - - for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k) - { - CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].mappings[k].material, data->materials, data->materials_count); - } - } - } - - for (cgltf_size i = 0; i < data->accessors_count; ++i) - { - CGLTF_PTRFIXUP(data->accessors[i].buffer_view, data->buffer_views, data->buffer_views_count); - - if (data->accessors[i].is_sparse) - { - CGLTF_PTRFIXUP_REQ(data->accessors[i].sparse.indices_buffer_view, data->buffer_views, data->buffer_views_count); - CGLTF_PTRFIXUP_REQ(data->accessors[i].sparse.values_buffer_view, data->buffer_views, data->buffer_views_count); - } - - if (data->accessors[i].buffer_view) - { - data->accessors[i].stride = data->accessors[i].buffer_view->stride; - } - - if (data->accessors[i].stride == 0) - { - data->accessors[i].stride = cgltf_calc_size(data->accessors[i].type, data->accessors[i].component_type); - } - } - - for (cgltf_size i = 0; i < data->textures_count; ++i) - { - CGLTF_PTRFIXUP(data->textures[i].image, data->images, data->images_count); - CGLTF_PTRFIXUP(data->textures[i].basisu_image, data->images, data->images_count); - CGLTF_PTRFIXUP(data->textures[i].sampler, data->samplers, data->samplers_count); - } - - for (cgltf_size i = 0; i < data->images_count; ++i) - { - CGLTF_PTRFIXUP(data->images[i].buffer_view, data->buffer_views, data->buffer_views_count); - } - - for (cgltf_size i = 0; i < data->materials_count; ++i) - { - CGLTF_PTRFIXUP(data->materials[i].normal_texture.texture, data->textures, data->textures_count); - CGLTF_PTRFIXUP(data->materials[i].emissive_texture.texture, data->textures, data->textures_count); - CGLTF_PTRFIXUP(data->materials[i].occlusion_texture.texture, data->textures, data->textures_count); - - CGLTF_PTRFIXUP(data->materials[i].pbr_metallic_roughness.base_color_texture.texture, data->textures, data->textures_count); - CGLTF_PTRFIXUP(data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture, data->textures, data->textures_count); - - CGLTF_PTRFIXUP(data->materials[i].pbr_specular_glossiness.diffuse_texture.texture, data->textures, data->textures_count); - CGLTF_PTRFIXUP(data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.texture, data->textures, data->textures_count); - - CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_texture.texture, data->textures, data->textures_count); - CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_roughness_texture.texture, data->textures, data->textures_count); - CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_normal_texture.texture, data->textures, data->textures_count); - - CGLTF_PTRFIXUP(data->materials[i].specular.specular_texture.texture, data->textures, data->textures_count); - CGLTF_PTRFIXUP(data->materials[i].specular.specular_color_texture.texture, data->textures, data->textures_count); - - CGLTF_PTRFIXUP(data->materials[i].transmission.transmission_texture.texture, data->textures, data->textures_count); - - CGLTF_PTRFIXUP(data->materials[i].volume.thickness_texture.texture, data->textures, data->textures_count); - - CGLTF_PTRFIXUP(data->materials[i].sheen.sheen_color_texture.texture, data->textures, data->textures_count); - CGLTF_PTRFIXUP(data->materials[i].sheen.sheen_roughness_texture.texture, data->textures, data->textures_count); - - CGLTF_PTRFIXUP(data->materials[i].iridescence.iridescence_texture.texture, data->textures, data->textures_count); - CGLTF_PTRFIXUP(data->materials[i].iridescence.iridescence_thickness_texture.texture, data->textures, data->textures_count); - } - - for (cgltf_size i = 0; i < data->buffer_views_count; ++i) - { - CGLTF_PTRFIXUP_REQ(data->buffer_views[i].buffer, data->buffers, data->buffers_count); - - if (data->buffer_views[i].has_meshopt_compression) - { - CGLTF_PTRFIXUP_REQ(data->buffer_views[i].meshopt_compression.buffer, data->buffers, data->buffers_count); - } - } - - for (cgltf_size i = 0; i < data->skins_count; ++i) - { - for (cgltf_size j = 0; j < data->skins[i].joints_count; ++j) - { - CGLTF_PTRFIXUP_REQ(data->skins[i].joints[j], data->nodes, data->nodes_count); - } - - CGLTF_PTRFIXUP(data->skins[i].skeleton, data->nodes, data->nodes_count); - CGLTF_PTRFIXUP(data->skins[i].inverse_bind_matrices, data->accessors, data->accessors_count); - } - - for (cgltf_size i = 0; i < data->nodes_count; ++i) - { - for (cgltf_size j = 0; j < data->nodes[i].children_count; ++j) - { - CGLTF_PTRFIXUP_REQ(data->nodes[i].children[j], data->nodes, data->nodes_count); - - if (data->nodes[i].children[j]->parent) - { - return CGLTF_ERROR_JSON; - } - - data->nodes[i].children[j]->parent = &data->nodes[i]; - } - - CGLTF_PTRFIXUP(data->nodes[i].mesh, data->meshes, data->meshes_count); - CGLTF_PTRFIXUP(data->nodes[i].skin, data->skins, data->skins_count); - CGLTF_PTRFIXUP(data->nodes[i].camera, data->cameras, data->cameras_count); - CGLTF_PTRFIXUP(data->nodes[i].light, data->lights, data->lights_count); - } - - for (cgltf_size i = 0; i < data->scenes_count; ++i) - { - for (cgltf_size j = 0; j < data->scenes[i].nodes_count; ++j) - { - CGLTF_PTRFIXUP_REQ(data->scenes[i].nodes[j], data->nodes, data->nodes_count); - - if (data->scenes[i].nodes[j]->parent) - { - return CGLTF_ERROR_JSON; - } - } - } - - CGLTF_PTRFIXUP(data->scene, data->scenes, data->scenes_count); - - for (cgltf_size i = 0; i < data->animations_count; ++i) - { - for (cgltf_size j = 0; j < data->animations[i].samplers_count; ++j) - { - CGLTF_PTRFIXUP_REQ(data->animations[i].samplers[j].input, data->accessors, data->accessors_count); - CGLTF_PTRFIXUP_REQ(data->animations[i].samplers[j].output, data->accessors, data->accessors_count); - } - - for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j) - { - CGLTF_PTRFIXUP_REQ(data->animations[i].channels[j].sampler, data->animations[i].samplers, data->animations[i].samplers_count); - CGLTF_PTRFIXUP(data->animations[i].channels[j].target_node, data->nodes, data->nodes_count); - } - } - - return 0; -} - -/* - * -- jsmn.c start -- - * Source: https://github.com/zserge/jsmn - * License: MIT - * - * Copyright (c) 2010 Serge A. Zaitsev - - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/** - * Allocates a fresh unused token from the token pull. - */ -static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, - jsmntok_t *tokens, size_t num_tokens) { - jsmntok_t *tok; - if (parser->toknext >= num_tokens) { - return NULL; - } - tok = &tokens[parser->toknext++]; - tok->start = tok->end = -1; - tok->size = 0; -#ifdef JSMN_PARENT_LINKS - tok->parent = -1; -#endif - return tok; -} - -/** - * Fills token type and boundaries. - */ -static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, - int start, int end) { - token->type = type; - token->start = start; - token->end = end; - token->size = 0; -} - -/** - * Fills next available token with JSON primitive. - */ -static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, - size_t len, jsmntok_t *tokens, size_t num_tokens) { - jsmntok_t *token; - int start; - - start = parser->pos; - - for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { - switch (js[parser->pos]) { -#ifndef JSMN_STRICT - /* In strict mode primitive must be followed by "," or "}" or "]" */ - case ':': -#endif - case '\t' : case '\r' : case '\n' : case ' ' : - case ',' : case ']' : case '}' : - goto found; - } - if (js[parser->pos] < 32 || js[parser->pos] >= 127) { - parser->pos = start; - return JSMN_ERROR_INVAL; - } - } -#ifdef JSMN_STRICT - /* In strict mode primitive must be followed by a comma/object/array */ - parser->pos = start; - return JSMN_ERROR_PART; -#endif - -found: - if (tokens == NULL) { - parser->pos--; - return 0; - } - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) { - parser->pos = start; - return JSMN_ERROR_NOMEM; - } - jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); -#ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; -#endif - parser->pos--; - return 0; -} - -/** - * Fills next token with JSON string. - */ -static int jsmn_parse_string(jsmn_parser *parser, const char *js, - size_t len, jsmntok_t *tokens, size_t num_tokens) { - jsmntok_t *token; - - int start = parser->pos; - - parser->pos++; - - /* Skip starting quote */ - for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { - char c = js[parser->pos]; - - /* Quote: end of string */ - if (c == '\"') { - if (tokens == NULL) { - return 0; - } - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) { - parser->pos = start; - return JSMN_ERROR_NOMEM; - } - jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); -#ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; -#endif - return 0; - } - - /* Backslash: Quoted symbol expected */ - if (c == '\\' && parser->pos + 1 < len) { - int i; - parser->pos++; - switch (js[parser->pos]) { - /* Allowed escaped symbols */ - case '\"': case '/' : case '\\' : case 'b' : - case 'f' : case 'r' : case 'n' : case 't' : - break; - /* Allows escaped symbol \uXXXX */ - case 'u': - parser->pos++; - for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { - /* If it isn't a hex character we have an error */ - if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ - (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ - (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ - parser->pos = start; - return JSMN_ERROR_INVAL; - } - parser->pos++; - } - parser->pos--; - break; - /* Unexpected symbol */ - default: - parser->pos = start; - return JSMN_ERROR_INVAL; - } - } - } - parser->pos = start; - return JSMN_ERROR_PART; -} - -/** - * Parse JSON string and fill tokens. - */ -static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, - jsmntok_t *tokens, size_t num_tokens) { - int r; - int i; - jsmntok_t *token; - int count = parser->toknext; - - for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { - char c; - jsmntype_t type; - - c = js[parser->pos]; - switch (c) { - case '{': case '[': - count++; - if (tokens == NULL) { - break; - } - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) - return JSMN_ERROR_NOMEM; - if (parser->toksuper != -1) { - tokens[parser->toksuper].size++; -#ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; -#endif - } - token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); - token->start = parser->pos; - parser->toksuper = parser->toknext - 1; - break; - case '}': case ']': - if (tokens == NULL) - break; - type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); -#ifdef JSMN_PARENT_LINKS - if (parser->toknext < 1) { - return JSMN_ERROR_INVAL; - } - token = &tokens[parser->toknext - 1]; - for (;;) { - if (token->start != -1 && token->end == -1) { - if (token->type != type) { - return JSMN_ERROR_INVAL; - } - token->end = parser->pos + 1; - parser->toksuper = token->parent; - break; - } - if (token->parent == -1) { - if(token->type != type || parser->toksuper == -1) { - return JSMN_ERROR_INVAL; - } - break; - } - token = &tokens[token->parent]; - } -#else - for (i = parser->toknext - 1; i >= 0; i--) { - token = &tokens[i]; - if (token->start != -1 && token->end == -1) { - if (token->type != type) { - return JSMN_ERROR_INVAL; - } - parser->toksuper = -1; - token->end = parser->pos + 1; - break; - } - } - /* Error if unmatched closing bracket */ - if (i == -1) return JSMN_ERROR_INVAL; - for (; i >= 0; i--) { - token = &tokens[i]; - if (token->start != -1 && token->end == -1) { - parser->toksuper = i; - break; - } - } -#endif - break; - case '\"': - r = jsmn_parse_string(parser, js, len, tokens, num_tokens); - if (r < 0) return r; - count++; - if (parser->toksuper != -1 && tokens != NULL) - tokens[parser->toksuper].size++; - break; - case '\t' : case '\r' : case '\n' : case ' ': - break; - case ':': - parser->toksuper = parser->toknext - 1; - break; - case ',': - if (tokens != NULL && parser->toksuper != -1 && - tokens[parser->toksuper].type != JSMN_ARRAY && - tokens[parser->toksuper].type != JSMN_OBJECT) { -#ifdef JSMN_PARENT_LINKS - parser->toksuper = tokens[parser->toksuper].parent; -#else - for (i = parser->toknext - 1; i >= 0; i--) { - if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { - if (tokens[i].start != -1 && tokens[i].end == -1) { - parser->toksuper = i; - break; - } - } - } -#endif - } - break; -#ifdef JSMN_STRICT - /* In strict mode primitives are: numbers and booleans */ - case '-': case '0': case '1' : case '2': case '3' : case '4': - case '5': case '6': case '7' : case '8': case '9': - case 't': case 'f': case 'n' : - /* And they must not be keys of the object */ - if (tokens != NULL && parser->toksuper != -1) { - jsmntok_t *t = &tokens[parser->toksuper]; - if (t->type == JSMN_OBJECT || - (t->type == JSMN_STRING && t->size != 0)) { - return JSMN_ERROR_INVAL; - } - } -#else - /* In non-strict mode every unquoted value is a primitive */ - default: -#endif - r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); - if (r < 0) return r; - count++; - if (parser->toksuper != -1 && tokens != NULL) - tokens[parser->toksuper].size++; - break; - -#ifdef JSMN_STRICT - /* Unexpected char in strict mode */ - default: - return JSMN_ERROR_INVAL; -#endif - } - } - - if (tokens != NULL) { - for (i = parser->toknext - 1; i >= 0; i--) { - /* Unmatched opened object or array */ - if (tokens[i].start != -1 && tokens[i].end == -1) { - return JSMN_ERROR_PART; - } - } - } - - return count; -} - -/** - * Creates a new parser based over a given buffer with an array of tokens - * available. - */ -static void jsmn_init(jsmn_parser *parser) { - parser->pos = 0; - parser->toknext = 0; - parser->toksuper = -1; -} -/* - * -- jsmn.c end -- - */ - -#endif /* #ifdef CGLTF_IMPLEMENTATION */ - -/* cgltf is distributed under MIT license: - * - * Copyright (c) 2018-2021 Johannes Kuhlmann - - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ diff --git a/Dependencies/meshoptimizer/extern/fast_obj.h b/Dependencies/meshoptimizer/extern/fast_obj.h deleted file mode 100644 index 737ea31..0000000 --- a/Dependencies/meshoptimizer/extern/fast_obj.h +++ /dev/null @@ -1,1434 +0,0 @@ -/* - * fast_obj - * - * Version 1.1 - * - * MIT License - * - * Copyright (c) 2018-2020 Richard Knight - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -#ifndef FAST_OBJ_HDR -#define FAST_OBJ_HDR - -#define FAST_OBJ_VERSION_MAJOR 1 -#define FAST_OBJ_VERSION_MINOR 1 -#define FAST_OBJ_VERSION ((FAST_OBJ_VERSION_MAJOR << 8) | FAST_OBJ_VERSION_MINOR) - -#include - - -typedef struct -{ - /* Texture name from .mtl file */ - char* name; - - /* Resolved path to texture */ - char* path; - -} fastObjTexture; - - -typedef struct -{ - /* Material name */ - char* name; - - /* Parameters */ - float Ka[3]; /* Ambient */ - float Kd[3]; /* Diffuse */ - float Ks[3]; /* Specular */ - float Ke[3]; /* Emission */ - float Kt[3]; /* Transmittance */ - float Ns; /* Shininess */ - float Ni; /* Index of refraction */ - float Tf[3]; /* Transmission filter */ - float d; /* Disolve (alpha) */ - int illum; /* Illumination model */ - - /* Texture maps */ - fastObjTexture map_Ka; - fastObjTexture map_Kd; - fastObjTexture map_Ks; - fastObjTexture map_Ke; - fastObjTexture map_Kt; - fastObjTexture map_Ns; - fastObjTexture map_Ni; - fastObjTexture map_d; - fastObjTexture map_bump; - -} fastObjMaterial; - -/* Allows user override to bigger indexable array */ -#ifndef FAST_OBJ_UINT_TYPE -#define FAST_OBJ_UINT_TYPE unsigned int -#endif - -typedef FAST_OBJ_UINT_TYPE fastObjUInt; - -typedef struct -{ - fastObjUInt p; - fastObjUInt t; - fastObjUInt n; - -} fastObjIndex; - - -typedef struct -{ - /* Group name */ - char* name; - - /* Number of faces */ - unsigned int face_count; - - /* First face in fastObjMesh face_* arrays */ - unsigned int face_offset; - - /* First index in fastObjMesh indices array */ - unsigned int index_offset; - -} fastObjGroup; - - -typedef struct -{ - /* Vertex data */ - unsigned int position_count; - float* positions; - - unsigned int texcoord_count; - float* texcoords; - - unsigned int normal_count; - float* normals; - - /* Face data: one element for each face */ - unsigned int face_count; - unsigned int* face_vertices; - unsigned int* face_materials; - - /* Index data: one element for each face vertex */ - fastObjIndex* indices; - - /* Materials */ - unsigned int material_count; - fastObjMaterial* materials; - - /* Mesh groups */ - unsigned int group_count; - fastObjGroup* groups; - -} fastObjMesh; - -typedef struct -{ - void* (*file_open)(const char* path, void* user_data); - void (*file_close)(void* file, void* user_data); - size_t (*file_read)(void* file, void* dst, size_t bytes, void* user_data); - unsigned long (*file_size)(void* file, void* user_data); -} fastObjCallbacks; - -#ifdef __cplusplus -extern "C" { -#endif - -fastObjMesh* fast_obj_read(const char* path); -fastObjMesh* fast_obj_read_with_callbacks(const char* path, const fastObjCallbacks* callbacks, void* user_data); -void fast_obj_destroy(fastObjMesh* mesh); - -#ifdef __cplusplus -} -#endif - -#endif - - -#ifdef FAST_OBJ_IMPLEMENTATION - -#include -#include - -#ifndef FAST_OBJ_REALLOC -#define FAST_OBJ_REALLOC realloc -#endif - -#ifndef FAST_OBJ_FREE -#define FAST_OBJ_FREE free -#endif - -#ifdef _WIN32 -#define FAST_OBJ_SEPARATOR '\\' -#define FAST_OBJ_OTHER_SEP '/' -#else -#define FAST_OBJ_SEPARATOR '/' -#define FAST_OBJ_OTHER_SEP '\\' -#endif - - -/* Size of buffer to read into */ -#define BUFFER_SIZE 65536 - -/* Max supported power when parsing float */ -#define MAX_POWER 20 - -typedef struct -{ - /* Final mesh */ - fastObjMesh* mesh; - - /* Current group */ - fastObjGroup group; - - /* Current material index */ - unsigned int material; - - /* Current line in file */ - unsigned int line; - - /* Base path for materials/textures */ - char* base; - -} fastObjData; - - -static const -double POWER_10_POS[MAX_POWER] = -{ - 1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9, - 1.0e10, 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15, 1.0e16, 1.0e17, 1.0e18, 1.0e19, -}; - -static const -double POWER_10_NEG[MAX_POWER] = -{ - 1.0e0, 1.0e-1, 1.0e-2, 1.0e-3, 1.0e-4, 1.0e-5, 1.0e-6, 1.0e-7, 1.0e-8, 1.0e-9, - 1.0e-10, 1.0e-11, 1.0e-12, 1.0e-13, 1.0e-14, 1.0e-15, 1.0e-16, 1.0e-17, 1.0e-18, 1.0e-19, -}; - - -static void* memory_realloc(void* ptr, size_t bytes) -{ - return FAST_OBJ_REALLOC(ptr, bytes); -} - - -static -void memory_dealloc(void* ptr) -{ - FAST_OBJ_FREE(ptr); -} - - -#define array_clean(_arr) ((_arr) ? memory_dealloc(_array_header(_arr)), 0 : 0) -#define array_push(_arr, _val) (_array_mgrow(_arr, 1) ? ((_arr)[_array_size(_arr)++] = (_val), _array_size(_arr) - 1) : 0) -#define array_size(_arr) ((_arr) ? _array_size(_arr) : 0) -#define array_capacity(_arr) ((_arr) ? _array_capacity(_arr) : 0) -#define array_empty(_arr) (array_size(_arr) == 0) - -#define _array_header(_arr) ((fastObjUInt*)(_arr)-2) -#define _array_size(_arr) (_array_header(_arr)[0]) -#define _array_capacity(_arr) (_array_header(_arr)[1]) -#define _array_ngrow(_arr, _n) ((_arr) == 0 || (_array_size(_arr) + (_n) >= _array_capacity(_arr))) -#define _array_mgrow(_arr, _n) (_array_ngrow(_arr, _n) ? (_array_grow(_arr, _n) != 0) : 1) -#define _array_grow(_arr, _n) (*((void**)&(_arr)) = array_realloc(_arr, _n, sizeof(*(_arr)))) - - -static void* array_realloc(void* ptr, fastObjUInt n, fastObjUInt b) -{ - fastObjUInt sz = array_size(ptr); - fastObjUInt nsz = sz + n; - fastObjUInt cap = array_capacity(ptr); - fastObjUInt ncap = 3 * cap / 2; - fastObjUInt* r; - - - if (ncap < nsz) - ncap = nsz; - ncap = (ncap + 15) & ~15u; - - r = (fastObjUInt*)(memory_realloc(ptr ? _array_header(ptr) : 0, b * ncap + 2 * sizeof(fastObjUInt))); - if (!r) - return 0; - - r[0] = sz; - r[1] = ncap; - - return (r + 2); -} - - -static -void* file_open(const char* path, void* user_data) -{ - (void)(user_data); - return fopen(path, "rb"); -} - - -static -void file_close(void* file, void* user_data) -{ - FILE* f; - (void)(user_data); - - f = (FILE*)(file); - fclose(f); -} - - -static -size_t file_read(void* file, void* dst, size_t bytes, void* user_data) -{ - FILE* f; - (void)(user_data); - - f = (FILE*)(file); - return fread(dst, 1, bytes, f); -} - - -static -unsigned long file_size(void* file, void* user_data) -{ - FILE* f; - long p; - long n; - (void)(user_data); - - f = (FILE*)(file); - - p = ftell(f); - fseek(f, 0, SEEK_END); - n = ftell(f); - fseek(f, p, SEEK_SET); - - if (n > 0) - return (unsigned long)(n); - else - return 0; -} - - -static -char* string_copy(const char* s, const char* e) -{ - size_t n; - char* p; - - n = (size_t)(e - s); - p = (char*)(memory_realloc(0, n + 1)); - if (p) - { - memcpy(p, s, n); - p[n] = '\0'; - } - - return p; -} - - -static -char* string_substr(const char* s, size_t a, size_t b) -{ - return string_copy(s + a, s + b); -} - - -static -char* string_concat(const char* a, const char* s, const char* e) -{ - size_t an; - size_t sn; - char* p; - - an = a ? strlen(a) : 0; - sn = (size_t)(e - s); - p = (char*)(memory_realloc(0, an + sn + 1)); - if (p) - { - if (a) - memcpy(p, a, an); - memcpy(p + an, s, sn); - p[an + sn] = '\0'; - } - - return p; -} - - -static -int string_equal(const char* a, const char* s, const char* e) -{ - size_t an = strlen(a); - size_t sn = (size_t)(e - s); - - return an == sn && memcmp(a, s, an) == 0; -} - - -static -void string_fix_separators(char* s) -{ - while (*s) - { - if (*s == FAST_OBJ_OTHER_SEP) - *s = FAST_OBJ_SEPARATOR; - s++; - } -} - - -static -int is_whitespace(char c) -{ - return (c == ' ' || c == '\t' || c == '\r'); -} - -static -int is_end_of_name(char c) -{ - return (c == '\t' || c == '\r' || c == '\n'); -} - -static -int is_newline(char c) -{ - return (c == '\n'); -} - - -static -int is_digit(char c) -{ - return (c >= '0' && c <= '9'); -} - - -static -int is_exponent(char c) -{ - return (c == 'e' || c == 'E'); -} - - -static -const char* skip_whitespace(const char* ptr) -{ - while (is_whitespace(*ptr)) - ptr++; - - return ptr; -} - - -static -const char* skip_line(const char* ptr) -{ - while (!is_newline(*ptr++)) - ; - - return ptr; -} - - -static -fastObjGroup group_default(void) -{ - fastObjGroup group; - - group.name = 0; - group.face_count = 0; - group.face_offset = 0; - group.index_offset = 0; - - return group; -} - - -static -void group_clean(fastObjGroup* group) -{ - memory_dealloc(group->name); -} - - -static -void flush_output(fastObjData* data) -{ - /* Add group if not empty */ - if (data->group.face_count > 0) - array_push(data->mesh->groups, data->group); - else - group_clean(&data->group); - - /* Reset for more data */ - data->group = group_default(); - data->group.face_offset = array_size(data->mesh->face_vertices); - data->group.index_offset = array_size(data->mesh->indices); -} - - -static -const char* parse_int(const char* ptr, int* val) -{ - int sign; - int num; - - - if (*ptr == '-') - { - sign = -1; - ptr++; - } - else - { - sign = +1; - } - - num = 0; - while (is_digit(*ptr)) - num = 10 * num + (*ptr++ - '0'); - - *val = sign * num; - - return ptr; -} - - -static -const char* parse_float(const char* ptr, float* val) -{ - double sign; - double num; - double fra; - double div; - int eval; - const double* powers; - - - ptr = skip_whitespace(ptr); - - switch (*ptr) - { - case '+': - sign = 1.0; - ptr++; - break; - - case '-': - sign = -1.0; - ptr++; - break; - - default: - sign = 1.0; - break; - } - - - num = 0.0; - while (is_digit(*ptr)) - num = 10.0 * num + (double)(*ptr++ - '0'); - - if (*ptr == '.') - ptr++; - - fra = 0.0; - div = 1.0; - - while (is_digit(*ptr)) - { - fra = 10.0 * fra + (double)(*ptr++ - '0'); - div *= 10.0; - } - - num += fra / div; - - if (is_exponent(*ptr)) - { - ptr++; - - switch (*ptr) - { - case '+': - powers = POWER_10_POS; - ptr++; - break; - - case '-': - powers = POWER_10_NEG; - ptr++; - break; - - default: - powers = POWER_10_POS; - break; - } - - eval = 0; - while (is_digit(*ptr)) - eval = 10 * eval + (*ptr++ - '0'); - - num *= (eval >= MAX_POWER) ? 0.0 : powers[eval]; - } - - *val = (float)(sign * num); - - return ptr; -} - - -static -const char* parse_vertex(fastObjData* data, const char* ptr) -{ - unsigned int ii; - float v; - - - for (ii = 0; ii < 3; ii++) - { - ptr = parse_float(ptr, &v); - array_push(data->mesh->positions, v); - } - - return ptr; -} - - -static -const char* parse_texcoord(fastObjData* data, const char* ptr) -{ - unsigned int ii; - float v; - - - for (ii = 0; ii < 2; ii++) - { - ptr = parse_float(ptr, &v); - array_push(data->mesh->texcoords, v); - } - - return ptr; -} - - -static -const char* parse_normal(fastObjData* data, const char* ptr) -{ - unsigned int ii; - float v; - - - for (ii = 0; ii < 3; ii++) - { - ptr = parse_float(ptr, &v); - array_push(data->mesh->normals, v); - } - - return ptr; -} - - -static -const char* parse_face(fastObjData* data, const char* ptr) -{ - unsigned int count; - fastObjIndex vn; - int v; - int t; - int n; - - - ptr = skip_whitespace(ptr); - - count = 0; - while (!is_newline(*ptr)) - { - v = 0; - t = 0; - n = 0; - - ptr = parse_int(ptr, &v); - if (*ptr == '/') - { - ptr++; - if (*ptr != '/') - ptr = parse_int(ptr, &t); - - if (*ptr == '/') - { - ptr++; - ptr = parse_int(ptr, &n); - } - } - - if (v < 0) - vn.p = (array_size(data->mesh->positions) / 3) - (fastObjUInt)(-v); - else - vn.p = (fastObjUInt)(v); - - if (t < 0) - vn.t = (array_size(data->mesh->texcoords) / 2) - (fastObjUInt)(-t); - else if (t > 0) - vn.t = (fastObjUInt)(t); - else - vn.t = 0; - - if (n < 0) - vn.n = (array_size(data->mesh->normals) / 3) - (fastObjUInt)(-n); - else if (n > 0) - vn.n = (fastObjUInt)(n); - else - vn.n = 0; - - array_push(data->mesh->indices, vn); - count++; - - ptr = skip_whitespace(ptr); - } - - array_push(data->mesh->face_vertices, count); - array_push(data->mesh->face_materials, data->material); - - data->group.face_count++; - - return ptr; -} - - -static -const char* parse_group(fastObjData* data, const char* ptr) -{ - const char* s; - const char* e; - - - ptr = skip_whitespace(ptr); - - s = ptr; - while (!is_end_of_name(*ptr)) - ptr++; - - e = ptr; - - flush_output(data); - data->group.name = string_copy(s, e); - - return ptr; -} - - -static -fastObjTexture map_default(void) -{ - fastObjTexture map; - - map.name = 0; - map.path = 0; - - return map; -} - - -static -fastObjMaterial mtl_default(void) -{ - fastObjMaterial mtl; - - mtl.name = 0; - - mtl.Ka[0] = 0.0; - mtl.Ka[1] = 0.0; - mtl.Ka[2] = 0.0; - mtl.Kd[0] = 1.0; - mtl.Kd[1] = 1.0; - mtl.Kd[2] = 1.0; - mtl.Ks[0] = 0.0; - mtl.Ks[1] = 0.0; - mtl.Ks[2] = 0.0; - mtl.Ke[0] = 0.0; - mtl.Ke[1] = 0.0; - mtl.Ke[2] = 0.0; - mtl.Kt[0] = 0.0; - mtl.Kt[1] = 0.0; - mtl.Kt[2] = 0.0; - mtl.Ns = 1.0; - mtl.Ni = 1.0; - mtl.Tf[0] = 1.0; - mtl.Tf[1] = 1.0; - mtl.Tf[2] = 1.0; - mtl.d = 1.0; - mtl.illum = 1; - - mtl.map_Ka = map_default(); - mtl.map_Kd = map_default(); - mtl.map_Ks = map_default(); - mtl.map_Ke = map_default(); - mtl.map_Kt = map_default(); - mtl.map_Ns = map_default(); - mtl.map_Ni = map_default(); - mtl.map_d = map_default(); - mtl.map_bump = map_default(); - - return mtl; -} - - -static -const char* parse_usemtl(fastObjData* data, const char* ptr) -{ - const char* s; - const char* e; - unsigned int idx; - fastObjMaterial* mtl; - - - ptr = skip_whitespace(ptr); - - /* Parse the material name */ - s = ptr; - while (!is_end_of_name(*ptr)) - ptr++; - - e = ptr; - - /* Find an existing material with the same name */ - idx = 0; - while (idx < array_size(data->mesh->materials)) - { - mtl = &data->mesh->materials[idx]; - if (mtl->name && string_equal(mtl->name, s, e)) - break; - - idx++; - } - - /* If doesn't exists, create a default one with this name - Note: this case happens when OBJ doesn't have its MTL */ - if (idx == array_size(data->mesh->materials)) - { - fastObjMaterial new_mtl = mtl_default(); - new_mtl.name = string_copy(s, e); - array_push(data->mesh->materials, new_mtl); - } - - data->material = idx; - - return ptr; -} - - -static -void map_clean(fastObjTexture* map) -{ - memory_dealloc(map->name); - memory_dealloc(map->path); -} - - -static -void mtl_clean(fastObjMaterial* mtl) -{ - map_clean(&mtl->map_Ka); - map_clean(&mtl->map_Kd); - map_clean(&mtl->map_Ks); - map_clean(&mtl->map_Ke); - map_clean(&mtl->map_Kt); - map_clean(&mtl->map_Ns); - map_clean(&mtl->map_Ni); - map_clean(&mtl->map_d); - map_clean(&mtl->map_bump); - - memory_dealloc(mtl->name); -} - - -static -const char* read_mtl_int(const char* p, int* v) -{ - return parse_int(p, v); -} - - -static -const char* read_mtl_single(const char* p, float* v) -{ - return parse_float(p, v); -} - - -static -const char* read_mtl_triple(const char* p, float v[3]) -{ - p = read_mtl_single(p, &v[0]); - p = read_mtl_single(p, &v[1]); - p = read_mtl_single(p, &v[2]); - - return p; -} - - -static -const char* read_map(fastObjData* data, const char* ptr, fastObjTexture* map) -{ - const char* s; - const char* e; - char* name; - char* path; - - ptr = skip_whitespace(ptr); - - /* Don't support options at present */ - if (*ptr == '-') - return ptr; - - - /* Read name */ - s = ptr; - while (!is_end_of_name(*ptr)) - ptr++; - - e = ptr; - - name = string_copy(s, e); - - path = string_concat(data->base, s, e); - string_fix_separators(path); - - map->name = name; - map->path = path; - - return e; -} - - -static -int read_mtllib(fastObjData* data, void* file, const fastObjCallbacks* callbacks, void* user_data) -{ - unsigned long n; - const char* s; - char* contents; - size_t l; - const char* p; - const char* e; - int found_d; - fastObjMaterial mtl; - - - /* Read entire file */ - n = callbacks->file_size(file, user_data); - - contents = (char*)(memory_realloc(0, n + 1)); - if (!contents) - return 0; - - l = callbacks->file_read(file, contents, n, user_data); - contents[l] = '\n'; - - mtl = mtl_default(); - - found_d = 0; - - p = contents; - e = contents + l; - while (p < e) - { - p = skip_whitespace(p); - - switch (*p) - { - case 'n': - p++; - if (p[0] == 'e' && - p[1] == 'w' && - p[2] == 'm' && - p[3] == 't' && - p[4] == 'l' && - is_whitespace(p[5])) - { - /* Push previous material (if there is one) */ - if (mtl.name) - { - array_push(data->mesh->materials, mtl); - mtl = mtl_default(); - } - - - /* Read name */ - p += 5; - - while (is_whitespace(*p)) - p++; - - s = p; - while (!is_end_of_name(*p)) - p++; - - mtl.name = string_copy(s, p); - } - break; - - case 'K': - if (p[1] == 'a') - p = read_mtl_triple(p + 2, mtl.Ka); - else if (p[1] == 'd') - p = read_mtl_triple(p + 2, mtl.Kd); - else if (p[1] == 's') - p = read_mtl_triple(p + 2, mtl.Ks); - else if (p[1] == 'e') - p = read_mtl_triple(p + 2, mtl.Ke); - else if (p[1] == 't') - p = read_mtl_triple(p + 2, mtl.Kt); - break; - - case 'N': - if (p[1] == 's') - p = read_mtl_single(p + 2, &mtl.Ns); - else if (p[1] == 'i') - p = read_mtl_single(p + 2, &mtl.Ni); - break; - - case 'T': - if (p[1] == 'r') - { - float Tr; - p = read_mtl_single(p + 2, &Tr); - if (!found_d) - { - /* Ignore Tr if we've already read d */ - mtl.d = 1.0f - Tr; - } - } - else if (p[1] == 'f') - p = read_mtl_triple(p + 2, mtl.Tf); - break; - - case 'd': - if (is_whitespace(p[1])) - { - p = read_mtl_single(p + 1, &mtl.d); - found_d = 1; - } - break; - - case 'i': - p++; - if (p[0] == 'l' && - p[1] == 'l' && - p[2] == 'u' && - p[3] == 'm' && - is_whitespace(p[4])) - { - p = read_mtl_int(p + 4, &mtl.illum); - } - break; - - case 'm': - p++; - if (p[0] == 'a' && - p[1] == 'p' && - p[2] == '_') - { - p += 3; - if (*p == 'K') - { - p++; - if (is_whitespace(p[1])) - { - if (*p == 'a') - p = read_map(data, p + 1, &mtl.map_Ka); - else if (*p == 'd') - p = read_map(data, p + 1, &mtl.map_Kd); - else if (*p == 's') - p = read_map(data, p + 1, &mtl.map_Ks); - else if (*p == 'e') - p = read_map(data, p + 1, &mtl.map_Ke); - else if (*p == 't') - p = read_map(data, p + 1, &mtl.map_Kt); - } - } - else if (*p == 'N') - { - p++; - if (is_whitespace(p[1])) - { - if (*p == 's') - p = read_map(data, p + 1, &mtl.map_Ns); - else if (*p == 'i') - p = read_map(data, p + 1, &mtl.map_Ni); - } - } - else if (*p == 'd') - { - p++; - if (is_whitespace(*p)) - p = read_map(data, p, &mtl.map_d); - } - else if ((p[0] == 'b' || p[0] == 'B') && - p[1] == 'u' && - p[2] == 'm' && - p[3] == 'p' && - is_whitespace(p[4])) - { - p = read_map(data, p + 4, &mtl.map_bump); - } - } - break; - - case '#': - break; - } - - p = skip_line(p); - } - - /* Push final material */ - if (mtl.name) - array_push(data->mesh->materials, mtl); - - memory_dealloc(contents); - - return 1; -} - - -static -const char* parse_mtllib(fastObjData* data, const char* ptr, const fastObjCallbacks* callbacks, void* user_data) -{ - const char* s; - const char* e; - char* lib; - void* file; - - - ptr = skip_whitespace(ptr); - - s = ptr; - while (!is_end_of_name(*ptr)) - ptr++; - - e = ptr; - - lib = string_concat(data->base, s, e); - if (lib) - { - string_fix_separators(lib); - - file = callbacks->file_open(lib, user_data); - if (file) - { - read_mtllib(data, file, callbacks, user_data); - callbacks->file_close(file, user_data); - } - - memory_dealloc(lib); - } - - return ptr; -} - - -static -void parse_buffer(fastObjData* data, const char* ptr, const char* end, const fastObjCallbacks* callbacks, void* user_data) -{ - const char* p; - - - p = ptr; - while (p != end) - { - p = skip_whitespace(p); - - switch (*p) - { - case 'v': - p++; - - switch (*p++) - { - case ' ': - case '\t': - p = parse_vertex(data, p); - break; - - case 't': - p = parse_texcoord(data, p); - break; - - case 'n': - p = parse_normal(data, p); - break; - - default: - p--; /* roll p++ back in case *p was a newline */ - } - break; - - case 'f': - p++; - - switch (*p++) - { - case ' ': - case '\t': - p = parse_face(data, p); - break; - - default: - p--; /* roll p++ back in case *p was a newline */ - } - break; - - case 'g': - p++; - - switch (*p++) - { - case ' ': - case '\t': - p = parse_group(data, p); - break; - - default: - p--; /* roll p++ back in case *p was a newline */ - } - break; - - case 'm': - p++; - if (p[0] == 't' && - p[1] == 'l' && - p[2] == 'l' && - p[3] == 'i' && - p[4] == 'b' && - is_whitespace(p[5])) - p = parse_mtllib(data, p + 5, callbacks, user_data); - break; - - case 'u': - p++; - if (p[0] == 's' && - p[1] == 'e' && - p[2] == 'm' && - p[3] == 't' && - p[4] == 'l' && - is_whitespace(p[5])) - p = parse_usemtl(data, p + 5); - break; - - case '#': - break; - } - - p = skip_line(p); - - data->line++; - } -} - - -void fast_obj_destroy(fastObjMesh* m) -{ - unsigned int ii; - - - for (ii = 0; ii < array_size(m->groups); ii++) - group_clean(&m->groups[ii]); - - for (ii = 0; ii < array_size(m->materials); ii++) - mtl_clean(&m->materials[ii]); - - array_clean(m->positions); - array_clean(m->texcoords); - array_clean(m->normals); - array_clean(m->face_vertices); - array_clean(m->face_materials); - array_clean(m->indices); - array_clean(m->groups); - array_clean(m->materials); - - memory_dealloc(m); -} - - -fastObjMesh* fast_obj_read(const char* path) -{ - fastObjCallbacks callbacks; - callbacks.file_open = file_open; - callbacks.file_close = file_close; - callbacks.file_read = file_read; - callbacks.file_size = file_size; - - return fast_obj_read_with_callbacks(path, &callbacks, 0); -} - - -fastObjMesh* fast_obj_read_with_callbacks(const char* path, const fastObjCallbacks* callbacks, void* user_data) -{ - fastObjData data; - fastObjMesh* m; - void* file; - char* buffer; - char* start; - char* end; - char* last; - fastObjUInt read; - fastObjUInt bytes; - - /* Check if callbacks are valid */ - if(!callbacks) - return 0; - - - /* Open file */ - file = callbacks->file_open(path, user_data); - if (!file) - return 0; - - - /* Empty mesh */ - m = (fastObjMesh*)(memory_realloc(0, sizeof(fastObjMesh))); - if (!m) - return 0; - - m->positions = 0; - m->texcoords = 0; - m->normals = 0; - m->face_vertices = 0; - m->face_materials = 0; - m->indices = 0; - m->materials = 0; - m->groups = 0; - - - /* Add dummy position/texcoord/normal */ - array_push(m->positions, 0.0f); - array_push(m->positions, 0.0f); - array_push(m->positions, 0.0f); - - array_push(m->texcoords, 0.0f); - array_push(m->texcoords, 0.0f); - - array_push(m->normals, 0.0f); - array_push(m->normals, 0.0f); - array_push(m->normals, 1.0f); - - - /* Data needed during parsing */ - data.mesh = m; - data.group = group_default(); - data.material = 0; - data.line = 1; - data.base = 0; - - - /* Find base path for materials/textures */ - { - const char* sep1 = strrchr(path, FAST_OBJ_SEPARATOR); - const char* sep2 = strrchr(path, FAST_OBJ_OTHER_SEP); - - /* Use the last separator in the path */ - const char* sep = sep2 && (!sep1 || sep1 < sep2) ? sep2 : sep1; - - if (sep) - data.base = string_substr(path, 0, sep - path + 1); - } - - - /* Create buffer for reading file */ - buffer = (char*)(memory_realloc(0, 2 * BUFFER_SIZE * sizeof(char))); - if (!buffer) - return 0; - - start = buffer; - for (;;) - { - /* Read another buffer's worth from file */ - read = (fastObjUInt)(callbacks->file_read(file, start, BUFFER_SIZE, user_data)); - if (read == 0 && start == buffer) - break; - - - /* Ensure buffer ends in a newline */ - if (read < BUFFER_SIZE) - { - if (read == 0 || start[read - 1] != '\n') - start[read++] = '\n'; - } - - end = start + read; - if (end == buffer) - break; - - - /* Find last new line */ - last = end; - while (last > buffer) - { - last--; - if (*last == '\n') - break; - } - - - /* Check there actually is a new line */ - if (*last != '\n') - break; - - last++; - - - /* Process buffer */ - parse_buffer(&data, buffer, last, callbacks, user_data); - - - /* Copy overflow for next buffer */ - bytes = (fastObjUInt)(end - last); - memmove(buffer, last, bytes); - start = buffer + bytes; - } - - - /* Flush final group */ - flush_output(&data); - group_clean(&data.group); - - m->position_count = array_size(m->positions) / 3; - m->texcoord_count = array_size(m->texcoords) / 2; - m->normal_count = array_size(m->normals) / 3; - m->face_count = array_size(m->face_vertices); - m->material_count = array_size(m->materials); - m->group_count = array_size(m->groups); - - - /* Clean up */ - memory_dealloc(buffer); - memory_dealloc(data.base); - - callbacks->file_close(file, user_data); - - return m; -} - -#endif diff --git a/Dependencies/meshoptimizer/extern/sdefl.h b/Dependencies/meshoptimizer/extern/sdefl.h deleted file mode 100644 index af17e4a..0000000 --- a/Dependencies/meshoptimizer/extern/sdefl.h +++ /dev/null @@ -1,696 +0,0 @@ -/* -# Small Deflate -`sdefl` is a small bare bone lossless compression library in ANSI C (ISO C90) -which implements the Deflate (RFC 1951) compressed data format specification standard. -It is mainly tuned to get as much speed and compression ratio from as little code -as needed to keep the implementation as concise as possible. - -## Features -- Portable single header and source file duo written in ANSI C (ISO C90) -- Dual license with either MIT or public domain -- Small implementation - - Deflate: 525 LoC - - Inflate: 320 LoC -- Webassembly: - - Deflate ~3.7 KB (~2.2KB compressed) - - Inflate ~3.6 KB (~2.2KB compressed) - -## Usage: -This file behaves differently depending on what symbols you define -before including it. - -Header-File mode: -If you do not define `SDEFL_IMPLEMENTATION` before including this file, it -will operate in header only mode. In this mode it declares all used structs -and the API of the library without including the implementation of the library. - -Implementation mode: -If you define `SDEFL_IMPLEMENTATION` before including this file, it will -compile the implementation . Make sure that you only include -this file implementation in *one* C or C++ file to prevent collisions. - -### Benchmark - -| Compressor name | Compression| Decompress.| Compr. size | Ratio | -| ------------------------| -----------| -----------| ----------- | ----- | -| sdefl 1.0 -0 | 127 MB/s | 233 MB/s | 40004116 | 39.88 | -| sdefl 1.0 -1 | 111 MB/s | 259 MB/s | 38940674 | 38.82 | -| sdefl 1.0 -5 | 45 MB/s | 275 MB/s | 36577183 | 36.46 | -| sdefl 1.0 -7 | 38 MB/s | 276 MB/s | 36523781 | 36.41 | -| zlib 1.2.11 -1 | 72 MB/s | 307 MB/s | 42298774 | 42.30 | -| zlib 1.2.11 -6 | 24 MB/s | 313 MB/s | 36548921 | 36.55 | -| zlib 1.2.11 -9 | 20 MB/s | 314 MB/s | 36475792 | 36.48 | -| miniz 1.0 -1 | 122 MB/s | 208 MB/s | 48510028 | 48.51 | -| miniz 1.0 -6 | 27 MB/s | 260 MB/s | 36513697 | 36.51 | -| miniz 1.0 -9 | 23 MB/s | 261 MB/s | 36460101 | 36.46 | -| libdeflate 1.3 -1 | 147 MB/s | 667 MB/s | 39597378 | 39.60 | -| libdeflate 1.3 -6 | 69 MB/s | 689 MB/s | 36648318 | 36.65 | -| libdeflate 1.3 -9 | 13 MB/s | 672 MB/s | 35197141 | 35.20 | -| libdeflate 1.3 -12 | 8.13 MB/s | 670 MB/s | 35100568 | 35.10 | - -### Compression -Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia): - -| File | Original | `sdefl 0` | `sdefl 5` | `sdefl 7` | -| :------ | ---------: | -----------------: | ---------: | ----------: | -| dickens | 10.192.446 | 4,260,187| 3,845,261| 3,833,657 | -| mozilla | 51.220.480 | 20,774,706 | 19,607,009 | 19,565,867 | -| mr | 9.970.564 | 3,860,531 | 3,673,460 | 3,665,627 | -| nci | 33.553.445 | 4,030,283 | 3,094,526 | 3,006,075 | -| ooffice | 6.152.192 | 3,320,063 | 3,186,373 | 3,183,815 | -| osdb | 10.085.684 | 3,919,646 | 3,649,510 | 3,649,477 | -| reymont | 6.627.202 | 2,263,378 | 1,857,588 | 1,827,237 | -| samba | 21.606.400 | 6,121,797 | 5,462,670 | 5,450,762 | -| sao | 7.251.944 | 5,612,421 | 5,485,380 | 5,481,765 | -| webster | 41.458.703 | 13,972,648 | 12,059,432 | 11,991,421 | -| xml | 5.345.280 | 886,620| 674,009 | 662,141 | -| x-ray | 8.474.240 | 6,304,655 | 6,244,779 | 6,244,779 | - -## License -``` ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2020 Micha Mettke -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -``` -*/ -#ifndef SDEFL_H_INCLUDED -#define SDEFL_H_INCLUDED - -#ifdef __cplusplus -extern "C" { -#endif - -#define SDEFL_MAX_OFF (1 << 15) -#define SDEFL_WIN_SIZ SDEFL_MAX_OFF -#define SDEFL_WIN_MSK (SDEFL_WIN_SIZ-1) - -#define SDEFL_HASH_BITS 15 -#define SDEFL_HASH_SIZ (1 << SDEFL_HASH_BITS) -#define SDEFL_HASH_MSK (SDEFL_HASH_SIZ-1) - -#define SDEFL_MIN_MATCH 4 -#define SDEFL_BLK_MAX (256*1024) -#define SDEFL_SEQ_SIZ ((SDEFL_BLK_MAX + SDEFL_MIN_MATCH)/SDEFL_MIN_MATCH) - -#define SDEFL_SYM_MAX (288) -#define SDEFL_OFF_MAX (32) -#define SDEFL_PRE_MAX (19) - -#define SDEFL_LVL_MIN 0 -#define SDEFL_LVL_DEF 5 -#define SDEFL_LVL_MAX 8 - -struct sdefl_freq { - unsigned lit[SDEFL_SYM_MAX]; - unsigned off[SDEFL_OFF_MAX]; -}; -struct sdefl_code_words { - unsigned lit[SDEFL_SYM_MAX]; - unsigned off[SDEFL_OFF_MAX]; -}; -struct sdefl_lens { - unsigned char lit[SDEFL_SYM_MAX]; - unsigned char off[SDEFL_OFF_MAX]; -}; -struct sdefl_codes { - struct sdefl_code_words word; - struct sdefl_lens len; -}; -struct sdefl_seqt { - int off, len; -}; -struct sdefl { - int bits, bitcnt; - int tbl[SDEFL_HASH_SIZ]; - int prv[SDEFL_WIN_SIZ]; - - int seq_cnt; - struct sdefl_seqt seq[SDEFL_SEQ_SIZ]; - struct sdefl_freq freq; - struct sdefl_codes cod; -}; -extern int sdefl_bound(int in_len); -extern int sdeflate(struct sdefl *s, void *o, const void *i, int n, int lvl); -extern int zsdeflate(struct sdefl *s, void *o, const void *i, int n, int lvl); - -#ifdef __cplusplus -} -#endif - -#endif /* SDEFL_H_INCLUDED */ - -#ifdef SDEFL_IMPLEMENTATION - -#include /* assert */ -#include /* memcpy */ -#include /* CHAR_BIT */ - -#define SDEFL_NIL (-1) -#define SDEFL_MAX_MATCH 258 -#define SDEFL_MAX_CODE_LEN (15) -#define SDEFL_SYM_BITS (10u) -#define SDEFL_SYM_MSK ((1u << SDEFL_SYM_BITS)-1u) -#define SDEFL_LIT_LEN_CODES (14) -#define SDEFL_OFF_CODES (15) -#define SDEFL_PRE_CODES (7) -#define SDEFL_CNT_NUM(n) ((((n)+3u/4u)+3u)&~3u) -#define SDEFL_EOB (256) - -#define sdefl_npow2(n) (1 << (sdefl_ilog2((n)-1) + 1)) - -static int -sdefl_ilog2(int n) { - if (!n) return 0; -#ifdef _MSC_VER - unsigned long msbp = 0; - _BitScanReverse(&msbp, (unsigned long)n); - return (int)msbp; -#elif defined(__GNUC__) || defined(__clang__) - return (int)sizeof(unsigned long) * CHAR_BIT - 1 - __builtin_clzl((unsigned long)n); -#else - #define lt(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n - static const char tbl[256] = { - 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,lt(4), lt(5), lt(5), lt(6), lt(6), lt(6), lt(6), - lt(7), lt(7), lt(7), lt(7), lt(7), lt(7), lt(7), lt(7)}; - int tt, t; - if ((tt = (n >> 16))) { - return (t = (tt >> 8)) ? 24 + tbl[t] : 16 + tbl[tt]; - } else { - return (t = (n >> 8)) ? 8 + tbl[t] : tbl[n]; - } - #undef lt -#endif -} -static unsigned -sdefl_uload32(const void *p) { - /* hopefully will be optimized to an unaligned read */ - unsigned n = 0; - memcpy(&n, p, sizeof(n)); - return n; -} -static unsigned -sdefl_hash32(const void *p) { - unsigned n = sdefl_uload32(p); - return (n * 0x9E377989) >> (32 - SDEFL_HASH_BITS); -} -static void -sdefl_put(unsigned char **dst, struct sdefl *s, int code, int bitcnt) { - s->bits |= (code << s->bitcnt); - s->bitcnt += bitcnt; - while (s->bitcnt >= 8) { - unsigned char *tar = *dst; - *tar = (unsigned char)(s->bits & 0xFF); - s->bits >>= 8; - s->bitcnt -= 8; - *dst = *dst + 1; - } -} -static void -sdefl_heap_sub(unsigned A[], unsigned len, unsigned sub) { - unsigned c, p = sub; - unsigned v = A[sub]; - while ((c = p << 1) <= len) { - if (c < len && A[c + 1] > A[c]) c++; - if (v >= A[c]) break; - A[p] = A[c], p = c; - } - A[p] = v; -} -static void -sdefl_heap_array(unsigned *A, unsigned len) { - unsigned sub; - for (sub = len >> 1; sub >= 1; sub--) - sdefl_heap_sub(A, len, sub); -} -static void -sdefl_heap_sort(unsigned *A, unsigned n) { - A--; - sdefl_heap_array(A, n); - while (n >= 2) { - unsigned tmp = A[n]; - A[n--] = A[1]; - A[1] = tmp; - sdefl_heap_sub(A, n, 1); - } -} -static unsigned -sdefl_sort_sym(unsigned sym_cnt, unsigned *freqs, - unsigned char *lens, unsigned *sym_out) { - unsigned cnts[SDEFL_CNT_NUM(SDEFL_SYM_MAX)] = {0}; - unsigned cnt_num = SDEFL_CNT_NUM(sym_cnt); - unsigned used_sym = 0; - unsigned sym, i; - for (sym = 0; sym < sym_cnt; sym++) - cnts[freqs[sym] < cnt_num-1 ? freqs[sym]: cnt_num-1]++; - for (i = 1; i < cnt_num; i++) { - unsigned cnt = cnts[i]; - cnts[i] = used_sym; - used_sym += cnt; - } - for (sym = 0; sym < sym_cnt; sym++) { - unsigned freq = freqs[sym]; - if (freq) { - unsigned idx = freq < cnt_num-1 ? freq : cnt_num-1; - sym_out[cnts[idx]++] = sym | (freq << SDEFL_SYM_BITS); - } else lens[sym] = 0; - } - sdefl_heap_sort(sym_out + cnts[cnt_num-2], cnts[cnt_num-1] - cnts[cnt_num-2]); - return used_sym; -} -static void -sdefl_build_tree(unsigned *A, unsigned sym_cnt) { - unsigned i = 0, b = 0, e = 0; - do { - unsigned m, n, freq_shift; - if (i != sym_cnt && (b == e || (A[i] >> SDEFL_SYM_BITS) <= (A[b] >> SDEFL_SYM_BITS))) - m = i++; - else m = b++; - if (i != sym_cnt && (b == e || (A[i] >> SDEFL_SYM_BITS) <= (A[b] >> SDEFL_SYM_BITS))) - n = i++; - else n = b++; - - freq_shift = (A[m] & ~SDEFL_SYM_MSK) + (A[n] & ~SDEFL_SYM_MSK); - A[m] = (A[m] & SDEFL_SYM_MSK) | (e << SDEFL_SYM_BITS); - A[n] = (A[n] & SDEFL_SYM_MSK) | (e << SDEFL_SYM_BITS); - A[e] = (A[e] & SDEFL_SYM_MSK) | freq_shift; - } while (sym_cnt - ++e > 1); -} -static void -sdefl_gen_len_cnt(unsigned *A, unsigned root, unsigned *len_cnt, - unsigned max_code_len) { - int n; - unsigned i; - for (i = 0; i <= max_code_len; i++) - len_cnt[i] = 0; - len_cnt[1] = 2; - - A[root] &= SDEFL_SYM_MSK; - for (n = (int)root - 1; n >= 0; n--) { - unsigned p = A[n] >> SDEFL_SYM_BITS; - unsigned pdepth = A[p] >> SDEFL_SYM_BITS; - unsigned depth = pdepth + 1; - unsigned len = depth; - - A[n] = (A[n] & SDEFL_SYM_MSK) | (depth << SDEFL_SYM_BITS); - if (len >= max_code_len) { - len = max_code_len; - do len--; while (!len_cnt[len]); - } - len_cnt[len]--; - len_cnt[len+1] += 2; - } -} -static void -sdefl_gen_codes(unsigned *A, unsigned char *lens, const unsigned *len_cnt, - unsigned max_code_word_len, unsigned sym_cnt) { - unsigned i, sym, len, nxt[SDEFL_MAX_CODE_LEN + 1]; - for (i = 0, len = max_code_word_len; len >= 1; len--) { - unsigned cnt = len_cnt[len]; - while (cnt--) lens[A[i++] & SDEFL_SYM_MSK] = (unsigned char)len; - } - nxt[0] = nxt[1] = 0; - for (len = 2; len <= max_code_word_len; len++) - nxt[len] = (nxt[len-1] + len_cnt[len-1]) << 1; - for (sym = 0; sym < sym_cnt; sym++) - A[sym] = nxt[lens[sym]]++; -} -static unsigned -sdefl_rev(unsigned c, unsigned char n) { - c = ((c & 0x5555) << 1) | ((c & 0xAAAA) >> 1); - c = ((c & 0x3333) << 2) | ((c & 0xCCCC) >> 2); - c = ((c & 0x0F0F) << 4) | ((c & 0xF0F0) >> 4); - c = ((c & 0x00FF) << 8) | ((c & 0xFF00) >> 8); - return c >> (16-n); -} -static void -sdefl_huff(unsigned char *lens, unsigned *codes, unsigned *freqs, - unsigned num_syms, unsigned max_code_len) { - unsigned c, *A = codes; - unsigned len_cnt[SDEFL_MAX_CODE_LEN + 1]; - unsigned used_syms = sdefl_sort_sym(num_syms, freqs, lens, A); - if (!used_syms) return; - if (used_syms == 1) { - unsigned s = A[0] & SDEFL_SYM_MSK; - unsigned i = s ? s : 1; - codes[0] = 0, lens[0] = 1; - codes[i] = 1, lens[i] = 1; - return; - } - sdefl_build_tree(A, used_syms); - sdefl_gen_len_cnt(A, used_syms-2, len_cnt, max_code_len); - sdefl_gen_codes(A, lens, len_cnt, max_code_len, num_syms); - for (c = 0; c < num_syms; c++) { - codes[c] = sdefl_rev(codes[c], lens[c]); - } -} -struct sdefl_symcnt { - int items; - int lit; - int off; -}; -static void -sdefl_precode(struct sdefl_symcnt *cnt, unsigned *freqs, unsigned *items, - const unsigned char *litlen, const unsigned char *offlen) { - unsigned *at = items; - unsigned run_start = 0; - - unsigned total = 0; - unsigned char lens[SDEFL_SYM_MAX + SDEFL_OFF_MAX]; - for (cnt->lit = SDEFL_SYM_MAX; cnt->lit > 257; cnt->lit--) - if (litlen[cnt->lit - 1]) break; - for (cnt->off = SDEFL_OFF_MAX; cnt->off > 1; cnt->off--) - if (offlen[cnt->off - 1]) break; - - total = (unsigned)(cnt->lit + cnt->off); - memcpy(lens, litlen, sizeof(unsigned char) * cnt->lit); - memcpy(lens + cnt->lit, offlen, sizeof(unsigned char) * cnt->off); - do { - unsigned len = lens[run_start]; - unsigned run_end = run_start; - do run_end++; while (run_end != total && len == lens[run_end]); - if (!len) { - while ((run_end - run_start) >= 11) { - unsigned n = (run_end - run_start) - 11; - unsigned xbits = n < 0x7f ? n : 0x7f; - freqs[18]++; - *at++ = 18u | (xbits << 5u); - run_start += 11 + xbits; - } - if ((run_end - run_start) >= 3) { - unsigned n = (run_end - run_start) - 3; - unsigned xbits = n < 0x7 ? n : 0x7; - freqs[17]++; - *at++ = 17u | (xbits << 5u); - run_start += 3 + xbits; - } - } else if ((run_end - run_start) >= 4) { - freqs[len]++; - *at++ = len; - run_start++; - do { - unsigned xbits = (run_end - run_start) - 3; - xbits = xbits < 0x03 ? xbits : 0x03; - *at++ = 16 | (xbits << 5); - run_start += 3 + xbits; - freqs[16]++; - } while ((run_end - run_start) >= 3); - } - while (run_start != run_end) { - freqs[len]++; - *at++ = len; - run_start++; - } - } while (run_start != total); - cnt->items = (int)(at - items); -} -struct sdefl_match_codest { - int ls, lc; - int dc, dx; -}; -static void -sdefl_match_codes(struct sdefl_match_codest *cod, int dist, int len) { - static const short dxmax[] = {0,6,12,24,48,96,192,384,768,1536,3072,6144,12288,24576}; - static const unsigned char lslot[258+1] = { - 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, - 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, - 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, - 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, - 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, - 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 28 - }; - cod->ls = lslot[len]; - cod->lc = 257 + cod->ls; - cod->dx = sdefl_ilog2(sdefl_npow2(dist) >> 2); - cod->dc = cod->dx ? ((cod->dx + 1) << 1) + (dist > dxmax[cod->dx]) : dist-1; -} -static void -sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) { - static const char lxn[] = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; - static const short lmin[] = {3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43, - 51,59,67,83,99,115,131,163,195,227,258}; - static const short dmin[] = {1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257, - 385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577}; - - struct sdefl_match_codest cod; - sdefl_match_codes(&cod, dist, len); - sdefl_put(dst, s, (int)s->cod.word.lit[cod.lc], s->cod.len.lit[cod.lc]); - sdefl_put(dst, s, len - lmin[cod.ls], lxn[cod.ls]); - sdefl_put(dst, s, (int)s->cod.word.off[cod.dc], s->cod.len.off[cod.dc]); - sdefl_put(dst, s, dist - dmin[cod.dc], cod.dx); -} -static void -sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, - const unsigned char *in) { - int j, i = 0, item_cnt = 0; - struct sdefl_symcnt symcnt = {0}; - unsigned codes[SDEFL_PRE_MAX]; - unsigned char lens[SDEFL_PRE_MAX]; - unsigned freqs[SDEFL_PRE_MAX] = {0}; - unsigned items[SDEFL_SYM_MAX + SDEFL_OFF_MAX]; - static const unsigned char perm[SDEFL_PRE_MAX] = {16,17,18,0,8,7,9,6,10,5,11, - 4,12,3,13,2,14,1,15}; - - /* huffman codes */ - s->freq.lit[SDEFL_EOB]++; - sdefl_huff(s->cod.len.lit, s->cod.word.lit, s->freq.lit, SDEFL_SYM_MAX, SDEFL_LIT_LEN_CODES); - sdefl_huff(s->cod.len.off, s->cod.word.off, s->freq.off, SDEFL_OFF_MAX, SDEFL_OFF_CODES); - sdefl_precode(&symcnt, freqs, items, s->cod.len.lit, s->cod.len.off); - sdefl_huff(lens, codes, freqs, SDEFL_PRE_MAX, SDEFL_PRE_CODES); - for (item_cnt = SDEFL_PRE_MAX; item_cnt > 4; item_cnt--) { - if (lens[perm[item_cnt - 1]]) break; - } - /* block header */ - sdefl_put(dst, s, is_last ? 0x01 : 0x00, 1); /* block */ - sdefl_put(dst, s, 0x02, 2); /* dynamic huffman */ - sdefl_put(dst, s, symcnt.lit - 257, 5); - sdefl_put(dst, s, symcnt.off - 1, 5); - sdefl_put(dst, s, item_cnt - 4, 4); - for (i = 0; i < item_cnt; ++i) - sdefl_put(dst, s, lens[perm[i]], 3); - for (i = 0; i < symcnt.items; ++i) { - unsigned sym = items[i] & 0x1F; - sdefl_put(dst, s, (int)codes[sym], lens[sym]); - if (sym < 16) continue; - if (sym == 16) sdefl_put(dst, s, items[i] >> 5, 2); - else if(sym == 17) sdefl_put(dst, s, items[i] >> 5, 3); - else sdefl_put(dst, s, items[i] >> 5, 7); - } - /* block sequences */ - for (i = 0; i < s->seq_cnt; ++i) { - if (s->seq[i].off >= 0) - for (j = 0; j < s->seq[i].len; ++j) { - int c = in[s->seq[i].off + j]; - sdefl_put(dst, s, (int)s->cod.word.lit[c], s->cod.len.lit[c]); - } - else sdefl_match(dst, s, -s->seq[i].off, s->seq[i].len); - } - sdefl_put(dst, s, (int)(s)->cod.word.lit[SDEFL_EOB], (s)->cod.len.lit[SDEFL_EOB]); - memset(&s->freq, 0, sizeof(s->freq)); - s->seq_cnt = 0; -} -static void -sdefl_seq(struct sdefl *s, int off, int len) { - assert(s->seq_cnt + 2 < SDEFL_SEQ_SIZ); - s->seq[s->seq_cnt].off = off; - s->seq[s->seq_cnt].len = len; - s->seq_cnt++; -} -static void -sdefl_reg_match(struct sdefl *s, int off, int len) { - struct sdefl_match_codest cod; - sdefl_match_codes(&cod, off, len); - s->freq.lit[cod.lc]++; - s->freq.off[cod.dc]++; -} -struct sdefl_match { - int off; - int len; -}; -static void -sdefl_fnd(struct sdefl_match *m, const struct sdefl *s, - int chain_len, int max_match, const unsigned char *in, int p) { - int i = s->tbl[sdefl_hash32(&in[p])]; - int limit = ((p-SDEFL_WIN_SIZ) limit) { - if (in[i+m->len] == in[p+m->len] && - (sdefl_uload32(&in[i]) == sdefl_uload32(&in[p]))){ - int n = SDEFL_MIN_MATCH; - while (n < max_match && in[i+n] == in[p+n]) n++; - if (n > m->len) { - m->len = n, m->off = p - i; - if (n == max_match) break; - } - } - if (!(--chain_len)) break; - i = s->prv[i&SDEFL_WIN_MSK]; - } -} -static int -sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in, - int in_len, int lvl) { - unsigned char *q = out; - static const unsigned char pref[] = {8,10,14,24,30,48,65,96,130}; - int max_chain = (lvl < 8) ? (1 << (lvl + 1)): (1 << 13); - int n, i = 0, litlen = 0; - for (n = 0; n < SDEFL_HASH_SIZ; ++n) { - s->tbl[n] = SDEFL_NIL; - } - do {int blk_end = i + SDEFL_BLK_MAX < in_len ? i + SDEFL_BLK_MAX : in_len; - while (i < blk_end) { - struct sdefl_match m = {0}; - int max_match = ((in_len-i)>SDEFL_MAX_MATCH) ? SDEFL_MAX_MATCH:(in_len-i); - int nice_match = pref[lvl] < max_match ? pref[lvl] : max_match; - int run = 1, inc = 1, run_inc; - if (max_match > SDEFL_MIN_MATCH) { - sdefl_fnd(&m, s, max_chain, max_match, in, i); - } - if (lvl >= 5 && m.len >= SDEFL_MIN_MATCH && m.len < nice_match){ - struct sdefl_match m2 = {0}; - sdefl_fnd(&m2, s, max_chain, m.len+1, in, i+1); - m.len = (m2.len > m.len) ? 0 : m.len; - } - if (m.len >= SDEFL_MIN_MATCH) { - if (litlen) { - sdefl_seq(s, i - litlen, litlen); - litlen = 0; - } - sdefl_seq(s, -m.off, m.len); - sdefl_reg_match(s, m.off, m.len); - if (lvl < 2 && m.len >= nice_match) { - inc = m.len; - } else { - run = m.len; - } - } else { - s->freq.lit[in[i]]++; - litlen++; - } - run_inc = run * inc; - if (in_len - (i + run_inc) > SDEFL_MIN_MATCH) { - while (run-- > 0) { - unsigned h = sdefl_hash32(&in[i]); - s->prv[i&SDEFL_WIN_MSK] = s->tbl[h]; - s->tbl[h] = i, i += inc; - } - } else { - i += run_inc; - } - } - if (litlen) { - sdefl_seq(s, i - litlen, litlen); - litlen = 0; - } - sdefl_flush(&q, s, blk_end == in_len, in); - } while (i < in_len); - - if (s->bitcnt) - sdefl_put(&q, s, 0x00, 8 - s->bitcnt); - return (int)(q - out); -} -extern int -sdeflate(struct sdefl *s, void *out, const void *in, int n, int lvl) { - s->bits = s->bitcnt = 0; - return sdefl_compr(s, (unsigned char*)out, (const unsigned char*)in, n, lvl); -} -static unsigned -sdefl_adler32(unsigned adler32, const unsigned char *in, int in_len) { - #define SDEFL_ADLER_INIT (1) - const unsigned ADLER_MOD = 65521; - unsigned s1 = adler32 & 0xffff; - unsigned s2 = adler32 >> 16; - unsigned blk_len, i; - - blk_len = in_len % 5552; - while (in_len) { - for (i = 0; i + 7 < blk_len; i += 8) { - s1 += in[0]; s2 += s1; - s1 += in[1]; s2 += s1; - s1 += in[2]; s2 += s1; - s1 += in[3]; s2 += s1; - s1 += in[4]; s2 += s1; - s1 += in[5]; s2 += s1; - s1 += in[6]; s2 += s1; - s1 += in[7]; s2 += s1; - in += 8; - } - for (; i < blk_len; ++i) { - s1 += *in++, s2 += s1; - } - s1 %= ADLER_MOD; - s2 %= ADLER_MOD; - in_len -= blk_len; - blk_len = 5552; - } - return (unsigned)(s2 << 16) + (unsigned)s1; -} -extern int -zsdeflate(struct sdefl *s, void *out, const void *in, int n, int lvl) { - int p = 0; - unsigned a = 0; - unsigned char *q = (unsigned char*)out; - - s->bits = s->bitcnt = 0; - sdefl_put(&q, s, 0x78, 8); /* deflate, 32k window */ - sdefl_put(&q, s, 0x01, 8); /* fast compression */ - q += sdefl_compr(s, q, (const unsigned char*)in, n, lvl); - - /* append adler checksum */ - a = sdefl_adler32(SDEFL_ADLER_INIT, (const unsigned char*)in, n); - for (p = 0; p < 4; ++p) { - sdefl_put(&q, s, (a >> 24) & 0xFF, 8); - a <<= 8; - } - return (int)(q - (unsigned char*)out); -} -extern int -sdefl_bound(int len) { - int a = 128 + (len * 110) / 100; - int b = 128 + len + ((len / (31 * 1024)) + 1) * 5; - return (a > b) ? a : b; -} -#endif /* SDEFL_IMPLEMENTATION */ diff --git a/Dependencies/meshoptimizer/gltf/README.md b/Dependencies/meshoptimizer/gltf/README.md deleted file mode 100644 index f6de8c5..0000000 --- a/Dependencies/meshoptimizer/gltf/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# 📦 gltfpack - -gltfpack is a tool that can automatically optimize glTF files to reduce the download size and improve loading and rendering speed. - -## Installation - -You can download a pre-built binary for gltfpack on [Releases page](https://github.com/zeux/meshoptimizer/releases), or install [npm package](https://www.npmjs.com/package/gltfpack). Native binaries are recommended over npm since they can work with larger files, run faster, and support texture compression. - -## Usage - -To convert a glTF file using gltfpack, run the command-line binary like this on an input `.gltf`/`.glb`/`.obj` file (run it without arguments for a list of options): - -``` -gltfpack -i scene.gltf -o scene.glb -``` - -gltfpack substantially changes the glTF data by optimizing the meshes for vertex fetch and transform cache, quantizing the geometry to reduce the memory consumption and size, merging meshes to reduce the draw call count, quantizing and resampling animations to reduce animation size and simplify playback, and pruning the node tree by removing or collapsing redundant nodes. It will also simplify the meshes when requested to do so. - -By default gltfpack outputs regular `.glb`/`.gltf` files that have been optimized for GPU consumption using various cache optimizers and quantization. These files can be loaded by GLTF loaders that support `KHR_mesh_quantization` extension such as [three.js](https://threejs.org/) (r111+) and [Babylon.js](https://www.babylonjs.com/) (4.1+). - -When using `-c` option, gltfpack outputs compressed `.glb`/`.gltf` files that use meshoptimizer codecs to reduce the download size further. Loading these files requires extending GLTF loaders with support for [EXT_meshopt_compression](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Vendor/EXT_meshopt_compression/README.md) extension; three.js supports it in r122+ (requires calling `GLTFLoader.setMeshoptDecoder`), Babylon.js supports it in 5.0+ without further setup. - -For better compression, you can use `-cc` option which applies additional compression; additionally make sure that your content delivery method is configured to use deflate (gzip) - meshoptimizer codecs are designed to produce output that can be compressed further with general purpose compressors. - -gltfpack can also compress textures using Basis Universal format stored in a KTX2 container (`-tc` flag, requires support for `KHR_texture_basisu`). Textures can also be embedded into `.bin`/`.glb` output using `-te` flag. - -## Decompression - -When using compressed files, [js/meshopt_decoder.js](https://github.com/zeux/meshoptimizer/blob/master/js/meshopt_decoder.js) or `js/meshopt_decoder.module.js` needs to be loaded to provide the WebAssembly decoder module like this: - -```js -import { MeshoptDecoder } from './meshopt_decoder.module.js'; - -... - -var loader = new GLTFLoader(); -loader.setMeshoptDecoder(MeshoptDecoder); -loader.load('pirate.glb', function (gltf) { scene.add(gltf.scene); }); -``` - -When using Three.js, this module can be imported from three.js repository from `examples/jsm/libs/meshopt_decoder.module.js`. - -Note that `meshopt_decoder` assumes that WebAssembly is supported. This is the case for all modern browsers; if support for legacy browsers such as Internet Explorer 11 is desired, it's recommended to use `-cf` flag when creating the glTF content. This will create and load fallback uncompressed buffers, but only on browsers that don't support WebAssembly. - -## Options - -By default gltfpack makes certain assumptions when optimizing the scenes, for example meshes that belong to nodes that aren't animated can be merged together, and has some defaults that represent a tradeoff between precision and size that are picked to fit most use cases. However, in some cases the resulting `.gltf` file needs to retain some way for the application to manipulate individual scene elements, and in other cases precision or size are more important to optimize for. gltfpack has a rich set of command line options to control various aspects of its behavior, with the full list available via `gltfpack -h`. - -The following settings are frequently used to reduce the resulting data size: - -* `-cc`: produce compressed gltf/glb files (requires `EXT_meshopt_compression`) -* `-tc`: convert all textures to KTX2 with BasisU supercompression (requires `KHR_texture_basisu` and may require `-tp` flag for compatibility with WebGL 1) -* `-mi`: use mesh instancing when serializing references to the same meshes (requires `EXT_mesh_gpu_instancing`) -* `-si R`: simplify meshes targeting triangle count ratio R (default: 1; R should be between 0 and 1) - -The following settings are frequently used to restrict some optimizations: - -* `-kn`: keep named nodes and meshes attached to named nodes so that named nodes can be transformed externally -* `-km`: keep named materials and disable named material merging -* `-ke`: keep extras data - -## License - -gltfpack is available to anybody free of charge, under the terms of MIT License (see LICENSE.md). diff --git a/Dependencies/meshoptimizer/gltf/animation.cpp b/Dependencies/meshoptimizer/gltf/animation.cpp deleted file mode 100644 index a761f74..0000000 --- a/Dependencies/meshoptimizer/gltf/animation.cpp +++ /dev/null @@ -1,336 +0,0 @@ -// This file is part of gltfpack; see gltfpack.h for version/license details -#include "gltfpack.h" - -#include - -#include -#include -#include - -static float getDelta(const Attr& l, const Attr& r, cgltf_animation_path_type type) -{ - switch (type) - { - case cgltf_animation_path_type_translation: - return std::max(std::max(fabsf(l.f[0] - r.f[0]), fabsf(l.f[1] - r.f[1])), fabsf(l.f[2] - r.f[2])); - - case cgltf_animation_path_type_rotation: - return acosf(std::min(1.f, fabsf(l.f[0] * r.f[0] + l.f[1] * r.f[1] + l.f[2] * r.f[2] + l.f[3] * r.f[3]))); - - case cgltf_animation_path_type_scale: - return std::max(std::max(fabsf(l.f[0] / r.f[0] - 1), fabsf(l.f[1] / r.f[1] - 1)), fabsf(l.f[2] / r.f[2] - 1)); - - case cgltf_animation_path_type_weights: - return fabsf(l.f[0] - r.f[0]); - - default: - assert(!"Uknown animation path"); - return 0; - } -} - -static float getDeltaTolerance(cgltf_animation_path_type type) -{ - switch (type) - { - case cgltf_animation_path_type_translation: - return 0.0001f; // 0.1mm linear - - case cgltf_animation_path_type_rotation: - return 0.1f * (3.1415926f / 180.f); // 0.1 degrees - - case cgltf_animation_path_type_scale: - return 0.001f; // 0.1% ratio - - case cgltf_animation_path_type_weights: - return 0.001f; // 0.1% linear - - default: - assert(!"Uknown animation path"); - return 0; - } -} - -static Attr interpolateLinear(const Attr& l, const Attr& r, float t, cgltf_animation_path_type type) -{ - if (type == cgltf_animation_path_type_rotation) - { - // Approximating slerp, https://zeux.io/2015/07/23/approximating-slerp/ - // We also handle quaternion double-cover - float ca = l.f[0] * r.f[0] + l.f[1] * r.f[1] + l.f[2] * r.f[2] + l.f[3] * r.f[3]; - - float d = fabsf(ca); - float A = 1.0904f + d * (-3.2452f + d * (3.55645f - d * 1.43519f)); - float B = 0.848013f + d * (-1.06021f + d * 0.215638f); - float k = A * (t - 0.5f) * (t - 0.5f) + B; - float ot = t + t * (t - 0.5f) * (t - 1) * k; - - float t0 = 1 - ot; - float t1 = ca > 0 ? ot : -ot; - - Attr lerp = {{ - l.f[0] * t0 + r.f[0] * t1, - l.f[1] * t0 + r.f[1] * t1, - l.f[2] * t0 + r.f[2] * t1, - l.f[3] * t0 + r.f[3] * t1, - }}; - - float len = sqrtf(lerp.f[0] * lerp.f[0] + lerp.f[1] * lerp.f[1] + lerp.f[2] * lerp.f[2] + lerp.f[3] * lerp.f[3]); - - if (len > 0.f) - { - lerp.f[0] /= len; - lerp.f[1] /= len; - lerp.f[2] /= len; - lerp.f[3] /= len; - } - - return lerp; - } - else - { - Attr lerp = {{ - l.f[0] * (1 - t) + r.f[0] * t, - l.f[1] * (1 - t) + r.f[1] * t, - l.f[2] * (1 - t) + r.f[2] * t, - l.f[3] * (1 - t) + r.f[3] * t, - }}; - - return lerp; - } -} - -static Attr interpolateHermite(const Attr& v0, const Attr& t0, const Attr& v1, const Attr& t1, float t, float dt, cgltf_animation_path_type type) -{ - float s0 = 1 + t * t * (2 * t - 3); - float s1 = t + t * t * (t - 2); - float s2 = 1 - s0; - float s3 = t * t * (t - 1); - - float ts1 = dt * s1; - float ts3 = dt * s3; - - Attr lerp = {{ - s0 * v0.f[0] + ts1 * t0.f[0] + s2 * v1.f[0] + ts3 * t1.f[0], - s0 * v0.f[1] + ts1 * t0.f[1] + s2 * v1.f[1] + ts3 * t1.f[1], - s0 * v0.f[2] + ts1 * t0.f[2] + s2 * v1.f[2] + ts3 * t1.f[2], - s0 * v0.f[3] + ts1 * t0.f[3] + s2 * v1.f[3] + ts3 * t1.f[3], - }}; - - if (type == cgltf_animation_path_type_rotation) - { - float len = sqrtf(lerp.f[0] * lerp.f[0] + lerp.f[1] * lerp.f[1] + lerp.f[2] * lerp.f[2] + lerp.f[3] * lerp.f[3]); - - if (len > 0.f) - { - lerp.f[0] /= len; - lerp.f[1] /= len; - lerp.f[2] /= len; - lerp.f[3] /= len; - } - } - - return lerp; -} - -static void resampleKeyframes(std::vector& data, const std::vector& input, const std::vector& output, cgltf_animation_path_type type, cgltf_interpolation_type interpolation, size_t components, int frames, float mint, int freq) -{ - size_t cursor = 0; - - for (int i = 0; i < frames; ++i) - { - float time = mint + float(i) / freq; - - while (cursor + 1 < input.size()) - { - float next_time = input[cursor + 1]; - - if (next_time > time) - break; - - cursor++; - } - - if (cursor + 1 < input.size()) - { - float cursor_time = input[cursor + 0]; - float next_time = input[cursor + 1]; - - float range = next_time - cursor_time; - float inv_range = (range == 0.f) ? 0.f : 1.f / (next_time - cursor_time); - float t = std::max(0.f, std::min(1.f, (time - cursor_time) * inv_range)); - - for (size_t j = 0; j < components; ++j) - { - switch (interpolation) - { - case cgltf_interpolation_type_linear: - { - const Attr& v0 = output[(cursor + 0) * components + j]; - const Attr& v1 = output[(cursor + 1) * components + j]; - data.push_back(interpolateLinear(v0, v1, t, type)); - } - break; - - case cgltf_interpolation_type_step: - { - const Attr& v = output[cursor * components + j]; - data.push_back(v); - } - break; - - case cgltf_interpolation_type_cubic_spline: - { - const Attr& v0 = output[(cursor * 3 + 1) * components + j]; - const Attr& b0 = output[(cursor * 3 + 2) * components + j]; - const Attr& a1 = output[(cursor * 3 + 3) * components + j]; - const Attr& v1 = output[(cursor * 3 + 4) * components + j]; - data.push_back(interpolateHermite(v0, b0, v1, a1, t, range, type)); - } - break; - - default: - assert(!"Unknown interpolation type"); - } - } - } - else - { - size_t offset = (interpolation == cgltf_interpolation_type_cubic_spline) ? cursor * 3 + 1 : cursor; - - for (size_t j = 0; j < components; ++j) - { - const Attr& v = output[offset * components + j]; - data.push_back(v); - } - } - } -} - -static float getMaxDelta(const std::vector& data, cgltf_animation_path_type type, int frames, const Attr* value, size_t components) -{ - assert(data.size() == frames * components); - - float result = 0; - - for (int i = 0; i < frames; ++i) - { - for (size_t j = 0; j < components; ++j) - { - float delta = getDelta(value[j], data[i * components + j], type); - - result = (result < delta) ? delta : result; - } - } - - return result; -} - -static void getBaseTransform(Attr* result, size_t components, cgltf_animation_path_type type, cgltf_node* node) -{ - switch (type) - { - case cgltf_animation_path_type_translation: - memcpy(result->f, node->translation, 3 * sizeof(float)); - break; - - case cgltf_animation_path_type_rotation: - memcpy(result->f, node->rotation, 4 * sizeof(float)); - break; - - case cgltf_animation_path_type_scale: - memcpy(result->f, node->scale, 3 * sizeof(float)); - break; - - case cgltf_animation_path_type_weights: - if (node->weights_count) - { - assert(node->weights_count == components); - memcpy(result->f, node->weights, components * sizeof(float)); - } - else if (node->mesh && node->mesh->weights_count) - { - assert(node->mesh->weights_count == components); - memcpy(result->f, node->mesh->weights, components * sizeof(float)); - } - break; - - default: - assert(!"Unknown animation path"); - } -} - -static float getWorldScale(cgltf_node* node) -{ - float transform[16]; - cgltf_node_transform_world(node, transform); - - // 3x3 determinant computes scale^3 - float a0 = transform[5] * transform[10] - transform[6] * transform[9]; - float a1 = transform[4] * transform[10] - transform[6] * transform[8]; - float a2 = transform[4] * transform[9] - transform[5] * transform[8]; - float det = transform[0] * a0 - transform[1] * a1 + transform[2] * a2; - - return powf(fabsf(det), 1.f / 3.f); -} - -void processAnimation(Animation& animation, const Settings& settings) -{ - float mint = FLT_MAX, maxt = 0; - - for (size_t i = 0; i < animation.tracks.size(); ++i) - { - const Track& track = animation.tracks[i]; - assert(!track.time.empty()); - - mint = std::min(mint, track.time.front()); - maxt = std::max(maxt, track.time.back()); - } - - mint = std::min(mint, maxt); - - // round the number of frames to nearest but favor the "up" direction - // this means that at 10 Hz resampling, we will try to preserve the last frame <10ms - // but if the last frame is <2ms we favor just removing this data - int frames = 1 + int((maxt - mint) * settings.anim_freq + 0.8f); - - animation.start = mint; - animation.frames = frames; - - std::vector base; - - for (size_t i = 0; i < animation.tracks.size(); ++i) - { - Track& track = animation.tracks[i]; - - std::vector result; - resampleKeyframes(result, track.time, track.data, track.path, track.interpolation, track.components, frames, mint, settings.anim_freq); - - track.time.clear(); - track.data.swap(result); - - float tolerance = getDeltaTolerance(track.path); - - // translation tracks use world space tolerance; in the future, we should compute all errors as linear using hierarchy - if (track.node && track.path == cgltf_animation_path_type_translation) - { - float scale = getWorldScale(track.node); - tolerance /= scale == 0.f ? 1.f : scale; - } - - float deviation = getMaxDelta(track.data, track.path, frames, &track.data[0], track.components); - - if (deviation <= tolerance) - { - // track is constant (equal to first keyframe), we only need the first keyframe - track.constant = true; - track.data.resize(track.components); - - // track.dummy is true iff track redundantly sets up the value to be equal to default node transform - base.resize(track.components); - getBaseTransform(&base[0], track.components, track.path, track.node); - - track.dummy = getMaxDelta(track.data, track.path, 1, &base[0], track.components) <= tolerance; - } - } -} diff --git a/Dependencies/meshoptimizer/gltf/basisenc.cpp b/Dependencies/meshoptimizer/gltf/basisenc.cpp deleted file mode 100644 index dbaf8f2..0000000 --- a/Dependencies/meshoptimizer/gltf/basisenc.cpp +++ /dev/null @@ -1,173 +0,0 @@ -#ifdef WITH_BASISU - -#define BASISU_NO_ITERATOR_DEBUG_LEVEL - -#ifdef __clang__ -#pragma GCC diagnostic ignored "-Wunknown-warning-option" -#endif - -#ifdef __GNUC__ -#pragma GCC diagnostic ignored "-Wclass-memaccess" -#pragma GCC diagnostic ignored "-Wunused-value" -#endif - -#include "encoder/basisu_comp.h" - -#include "gltfpack.h" - -struct BasisSettings -{ - int etc1s_l; - int etc1s_q; - int uastc_l; - float uastc_q; -}; - -static const BasisSettings kBasisSettings[10] = { - {1, 1, 0, 1.5f}, - {1, 6, 0, 1.f}, - {1, 20, 1, 1.0f}, - {1, 50, 1, 0.75f}, - {1, 90, 1, 0.5f}, - {1, 128, 1, 0.4f}, - {1, 160, 1, 0.34f}, - {1, 192, 1, 0.29f}, // default - {1, 224, 2, 0.26f}, - {1, 255, 2, 0.f}, -}; - -static std::unique_ptr gJobPool; - -void encodeInit(int jobs) -{ - using namespace basisu; - - basisu_encoder_init(); - - uint32_t num_threads = jobs == 0 ? std::thread::hardware_concurrency() : jobs; - - gJobPool.reset(new job_pool(num_threads)); -} - -static bool encodeInternal(const char* input, const char* output, bool yflip, bool normal_map, bool linear, bool uastc, int uastc_l, float uastc_q, int etc1s_l, int etc1s_q, int zstd_l, int width, int height) -{ - using namespace basisu; - - basis_compressor_params params; - - params.m_multithreading = gJobPool->get_total_threads() > 1; - params.m_pJob_pool = gJobPool.get(); - - if (uastc) - { - static const uint32_t s_level_flags[TOTAL_PACK_UASTC_LEVELS] = {cPackUASTCLevelFastest, cPackUASTCLevelFaster, cPackUASTCLevelDefault, cPackUASTCLevelSlower, cPackUASTCLevelVerySlow}; - - params.m_uastc = true; - - params.m_pack_uastc_flags &= ~cPackUASTCLevelMask; - params.m_pack_uastc_flags |= s_level_flags[uastc_l]; - - params.m_rdo_uastc = uastc_q > 0; - params.m_rdo_uastc_quality_scalar = uastc_q; - params.m_rdo_uastc_dict_size = 1024; - } - else - { - params.m_compression_level = etc1s_l; - params.m_quality_level = etc1s_q; - params.m_max_endpoint_clusters = 0; - params.m_max_selector_clusters = 0; - - params.m_no_selector_rdo = normal_map; - params.m_no_endpoint_rdo = normal_map; - } - - params.m_perceptual = !linear; - - params.m_mip_gen = true; - params.m_mip_srgb = !linear; - - params.m_resample_width = width; - params.m_resample_height = height; - - params.m_y_flip = yflip; - - params.m_create_ktx2_file = true; - params.m_ktx2_srgb_transfer_func = !linear; - - if (zstd_l) - { - params.m_ktx2_uastc_supercompression = basist::KTX2_SS_ZSTANDARD; - params.m_ktx2_zstd_supercompression_level = zstd_l; - } - - params.m_read_source_images = true; - params.m_write_output_basis_files = true; - - params.m_source_filenames.resize(1); - params.m_source_filenames[0] = input; - - params.m_out_filename = output; - - params.m_status_output = false; - - basis_compressor c; - - if (!c.init(params)) - return false; - - return c.process() == basis_compressor::cECSuccess; -} - -static bool encodeImage(const std::string& data, const char* mime_type, std::string& result, const ImageInfo& info, const Settings& settings) -{ - TempFile temp_input(mimeExtension(mime_type)); - TempFile temp_output(".ktx2"); - - if (!writeFile(temp_input.path.c_str(), data)) - return false; - - int quality = settings.texture_quality[info.kind]; - bool uastc = settings.texture_uastc[info.kind]; - - const BasisSettings& bs = kBasisSettings[quality - 1]; - - int width = 0, height = 0; - if (!getDimensions(data, mime_type, width, height)) - return false; - - adjustDimensions(width, height, settings); - - int zstd = uastc ? 9 : 0; - - bool ok = encodeInternal(temp_input.path.c_str(), temp_output.path.c_str(), settings.texture_flipy, info.normal_map, !info.srgb, uastc, bs.uastc_l, bs.uastc_q, bs.etc1s_l, bs.etc1s_q, zstd, width, height); - - return ok && readFile(temp_output.path.c_str(), result); -} - -void encodeImages(std::string* encoded, const cgltf_data* data, const std::vector& images, const char* input_path, const Settings& settings) -{ - assert(gJobPool); - - for (size_t i = 0; i < data->images_count; ++i) - { - const cgltf_image& image = data->images[i]; - ImageInfo info = images[i]; - - encoded[i].clear(); - - gJobPool->add_job([=]() { - std::string img_data; - std::string mime_type; - std::string result; - - if (readImage(image, input_path, img_data, mime_type) && encodeImage(img_data, mime_type.c_str(), result, info, settings)) - { - encoded[i].swap(result); - } - }, nullptr); // explicitly pass token to make sure we're using thread-safe job_pool implementation - } - - gJobPool->wait_for_all(); -} -#endif diff --git a/Dependencies/meshoptimizer/gltf/basislib.cpp b/Dependencies/meshoptimizer/gltf/basislib.cpp deleted file mode 100644 index 505729c..0000000 --- a/Dependencies/meshoptimizer/gltf/basislib.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#ifdef WITH_BASISU - -#ifdef __clang__ -#pragma GCC diagnostic ignored "-Wunknown-warning-option" -#pragma GCC diagnostic ignored "-Wuninitialized-const-reference" -#endif - -#ifdef __GNUC__ -#pragma GCC diagnostic ignored "-Wclass-memaccess" -#pragma GCC diagnostic ignored "-Wdeprecated-copy" -#pragma GCC diagnostic ignored "-Wextra" -#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" -#pragma GCC diagnostic ignored "-Wmisleading-indentation" -#pragma GCC diagnostic ignored "-Wparentheses" -#pragma GCC diagnostic ignored "-Wshadow" -#pragma GCC diagnostic ignored "-Wsign-compare" -#pragma GCC diagnostic ignored "-Wunused-value" -#pragma GCC diagnostic ignored "-Wunused-variable" -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" -#pragma GCC diagnostic ignored "-Wstrict-aliasing" // TODO: https://github.com/BinomialLLC/basis_universal/pull/275 -#pragma GCC diagnostic ignored "-Wstringop-overflow" -#endif - -#ifdef _MSC_VER -#pragma warning(disable : 4702) // unreachable code -#pragma warning(disable : 4005) // macro redefinition -#endif - -#define BASISU_NO_ITERATOR_DEBUG_LEVEL - -#if defined(_MSC_VER) && !defined(__clang__) && (defined(_M_IX86) || defined(_M_X64)) -#define BASISU_SUPPORT_SSE 1 -#endif - -#if defined(__SSE4_1__) -#define BASISU_SUPPORT_SSE 1 -#endif - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#endif - -#include "encoder/basisu_backend.cpp" -#include "encoder/basisu_basis_file.cpp" -#include "encoder/basisu_bc7enc.cpp" -#include "encoder/basisu_comp.cpp" -#include "encoder/basisu_enc.cpp" -#include "encoder/basisu_etc.cpp" -#include "encoder/basisu_frontend.cpp" -#include "encoder/basisu_gpu_texture.cpp" -#include "encoder/basisu_kernels_sse.cpp" -#include "encoder/basisu_opencl.cpp" -#include "encoder/basisu_pvrtc1_4.cpp" -#include "encoder/basisu_resample_filters.cpp" -#include "encoder/basisu_resampler.cpp" -#include "encoder/basisu_ssim.cpp" -#include "encoder/basisu_uastc_enc.cpp" -#include "encoder/jpgd.cpp" -#include "encoder/pvpngreader.cpp" -#include "transcoder/basisu_transcoder.cpp" - -#undef CLAMP -#include "zstd/zstd.c" - -#endif diff --git a/Dependencies/meshoptimizer/gltf/cli.js b/Dependencies/meshoptimizer/gltf/cli.js deleted file mode 100644 index a7bab55..0000000 --- a/Dependencies/meshoptimizer/gltf/cli.js +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env node -// This file is part of gltfpack and is distributed under the terms of MIT License. -var gltfpack = require('./library.js'); - -var fs = require('fs'); - -var args = process.argv.slice(2); - -var interface = { - read: function (path) { - return fs.readFileSync(path); - }, - write: function (path, data) { - fs.writeFileSync(path, data); - }, -}; - -gltfpack.pack(args, interface) - .then(function (log) { - process.stdout.write(log); - process.exit(0); - }) - .catch(function (err) { - process.stderr.write(err.message); - process.exit(1); - }); diff --git a/Dependencies/meshoptimizer/gltf/fileio.cpp b/Dependencies/meshoptimizer/gltf/fileio.cpp deleted file mode 100644 index ec09552..0000000 --- a/Dependencies/meshoptimizer/gltf/fileio.cpp +++ /dev/null @@ -1,126 +0,0 @@ -// This file is part of gltfpack; see gltfpack.h for version/license details -#include "gltfpack.h" - -#include -#include -#include - -#ifdef _WIN32 -#include -#else -#include -#endif - -TempFile::TempFile(const char* suffix) - : fd(-1) -{ -#if defined(_WIN32) - const char* temp_dir = getenv("TEMP"); - path = temp_dir ? temp_dir : "."; - path += "\\gltfpack-XXXXXX"; - (void)_mktemp(&path[0]); - path += suffix; -#elif defined(__wasi__) - static int id = 0; - char ids[16]; - sprintf(ids, "%d", id++); - - path = "gltfpack-temp-"; - path += ids; - path += suffix; -#else - path = "/tmp/gltfpack-XXXXXX"; - path += suffix; - fd = mkstemps(&path[0], strlen(suffix)); -#endif -} - -TempFile::~TempFile() -{ - remove(path.c_str()); - -#ifndef _WIN32 - close(fd); -#endif -} - -std::string getFullPath(const char* path, const char* base_path) -{ - std::string result = base_path; - - std::string::size_type slash = result.find_last_of("/\\"); - result.erase(slash == std::string::npos ? 0 : slash + 1); - - result += path; - - return result; -} - -std::string getFileName(const char* path) -{ - std::string result = path; - - std::string::size_type slash = result.find_last_of("/\\"); - if (slash != std::string::npos) - result.erase(0, slash + 1); - - std::string::size_type dot = result.find_last_of('.'); - if (dot != std::string::npos) - result.erase(dot); - - return result; -} - -std::string getExtension(const char* path) -{ - std::string result = path; - - std::string::size_type slash = result.find_last_of("/\\"); - std::string::size_type dot = result.find_last_of('.'); - - if (slash != std::string::npos && dot != std::string::npos && dot < slash) - dot = std::string::npos; - - result.erase(0, dot); - - for (size_t i = 0; i < result.length(); ++i) - if (unsigned(result[i] - 'A') < 26) - result[i] = (result[i] - 'A') + 'a'; - - return result; -} - -bool readFile(const char* path, std::string& data) -{ - FILE* file = fopen(path, "rb"); - if (!file) - return false; - - fseek(file, 0, SEEK_END); - long length = ftell(file); - fseek(file, 0, SEEK_SET); - - if (length <= 0) - { - fclose(file); - return false; - } - - data.resize(length); - size_t result = fread(&data[0], 1, data.size(), file); - int rc = fclose(file); - - return rc == 0 && result == data.size(); -} - -bool writeFile(const char* path, const std::string& data) -{ - FILE* file = fopen(path, "wb"); - if (!file) - return false; - - size_t result = fwrite(&data[0], 1, data.size(), file); - int rc = fclose(file); - - return rc == 0 && result == data.size(); -} diff --git a/Dependencies/meshoptimizer/gltf/gltfpack.cpp b/Dependencies/meshoptimizer/gltf/gltfpack.cpp deleted file mode 100644 index c8a4c6a..0000000 --- a/Dependencies/meshoptimizer/gltf/gltfpack.cpp +++ /dev/null @@ -1,1497 +0,0 @@ -// This file is part of gltfpack; see gltfpack.h for version/license details -#include "gltfpack.h" - -#include - -#include -#include -#include -#include -#include - -#ifdef __wasi__ -#include -#endif - -#include "../src/meshoptimizer.h" - -std::string getVersion() -{ - char result[32]; - sprintf(result, "%d.%d", MESHOPTIMIZER_VERSION / 1000, (MESHOPTIMIZER_VERSION % 1000) / 10); - return result; -} - -static void finalizeBufferViews(std::string& json, std::vector& views, std::string& bin, std::string* fallback, size_t& fallback_size) -{ - for (size_t i = 0; i < views.size(); ++i) - { - BufferView& view = views[i]; - - size_t bin_offset = bin.size(); - size_t fallback_offset = fallback_size; - - size_t count = view.data.size() / view.stride; - - if (view.compression == BufferView::Compression_None) - { - bin += view.data; - } - else - { - switch (view.compression) - { - case BufferView::Compression_Attribute: - compressVertexStream(bin, view.data, count, view.stride); - break; - case BufferView::Compression_Index: - compressIndexStream(bin, view.data, count, view.stride); - break; - case BufferView::Compression_IndexSequence: - compressIndexSequence(bin, view.data, count, view.stride); - break; - default: - assert(!"Unknown compression type"); - } - - if (fallback) - *fallback += view.data; - fallback_size += view.data.size(); - } - - size_t raw_offset = (view.compression != BufferView::Compression_None) ? fallback_offset : bin_offset; - - comma(json); - writeBufferView(json, view.kind, view.filter, count, view.stride, raw_offset, view.data.size(), view.compression, bin_offset, bin.size() - bin_offset); - - // record written bytes for statistics - view.bytes = bin.size() - bin_offset; - - // align each bufferView by 4 bytes - bin.resize((bin.size() + 3) & ~3); - if (fallback) - fallback->resize((fallback->size() + 3) & ~3); - fallback_size = (fallback_size + 3) & ~3; - } -} - -static void printMeshStats(const std::vector& meshes, const char* name) -{ - size_t mesh_triangles = 0; - size_t mesh_vertices = 0; - size_t total_triangles = 0; - size_t total_instances = 0; - size_t total_draws = 0; - - for (size_t i = 0; i < meshes.size(); ++i) - { - const Mesh& mesh = meshes[i]; - - mesh_triangles += mesh.indices.size() / 3; - mesh_vertices += mesh.streams.empty() ? 0 : mesh.streams[0].data.size(); - - size_t instances = std::max(size_t(1), mesh.nodes.size() + mesh.instances.size()); - - total_triangles += mesh.indices.size() / 3 * instances; - total_instances += instances; - total_draws += std::max(size_t(1), mesh.nodes.size()); - } - - printf("%s: %d mesh primitives (%d triangles, %d vertices); %d draw calls (%d instances, %lld triangles)\n", name, - int(meshes.size()), int(mesh_triangles), int(mesh_vertices), - int(total_draws), int(total_instances), (long long)total_triangles); -} - -static void printSceneStats(const std::vector& views, const std::vector& meshes, size_t node_offset, size_t mesh_offset, size_t material_offset, size_t json_size, size_t bin_size) -{ - size_t bytes[BufferView::Kind_Count] = {}; - - for (size_t i = 0; i < views.size(); ++i) - { - const BufferView& view = views[i]; - bytes[view.kind] += view.bytes; - } - - printf("output: %d nodes, %d meshes (%d primitives), %d materials\n", int(node_offset), int(mesh_offset), int(meshes.size()), int(material_offset)); - printf("output: JSON %d bytes, buffers %d bytes\n", int(json_size), int(bin_size)); - printf("output: buffers: vertex %d bytes, index %d bytes, skin %d bytes, time %d bytes, keyframe %d bytes, instance %d bytes, image %d bytes\n", - int(bytes[BufferView::Kind_Vertex]), int(bytes[BufferView::Kind_Index]), int(bytes[BufferView::Kind_Skin]), - int(bytes[BufferView::Kind_Time]), int(bytes[BufferView::Kind_Keyframe]), int(bytes[BufferView::Kind_Instance]), - int(bytes[BufferView::Kind_Image])); -} - -static void printAttributeStats(const std::vector& views, BufferView::Kind kind, const char* name) -{ - for (size_t i = 0; i < views.size(); ++i) - { - const BufferView& view = views[i]; - - if (view.kind != kind) - continue; - - const char* variant = "unknown"; - - switch (kind) - { - case BufferView::Kind_Vertex: - variant = attributeType(cgltf_attribute_type(view.variant)); - break; - - case BufferView::Kind_Index: - variant = "index"; - break; - - case BufferView::Kind_Keyframe: - case BufferView::Kind_Instance: - variant = animationPath(cgltf_animation_path_type(view.variant)); - break; - - default:; - } - - size_t count = view.data.size() / view.stride; - - printf("stats: %s %s: compressed %d bytes (%.1f bits), raw %d bytes (%d bits)\n", - name, variant, - int(view.bytes), double(view.bytes) / double(count) * 8, - int(view.data.size()), int(view.stride * 8)); - } -} - -static void printImageStats(const std::vector& views, TextureKind kind, const char* name) -{ - size_t bytes = 0; - size_t count = 0; - - for (size_t i = 0; i < views.size(); ++i) - { - const BufferView& view = views[i]; - - if (view.kind != BufferView::Kind_Image) - continue; - - if (view.variant != -1 - kind) - continue; - - count += 1; - bytes += view.data.size(); - } - - if (count) - printf("stats: image %s: %d bytes in %d images\n", name, int(bytes), int(count)); -} - -static bool printReport(const char* path, cgltf_data* data, const std::vector& views, const std::vector& meshes, size_t node_count, size_t mesh_count, size_t material_count, size_t animation_count, size_t json_size, size_t bin_size) -{ - size_t bytes[BufferView::Kind_Count] = {}; - - for (size_t i = 0; i < views.size(); ++i) - { - const BufferView& view = views[i]; - bytes[view.kind] += view.bytes; - } - - size_t total_triangles = 0; - size_t total_instances = 0; - size_t total_draws = 0; - - for (size_t i = 0; i < meshes.size(); ++i) - { - const Mesh& mesh = meshes[i]; - - size_t instances = std::max(size_t(1), mesh.nodes.size() + mesh.instances.size()); - - total_triangles += mesh.indices.size() / 3 * instances; - total_instances += instances; - total_draws += std::max(size_t(1), mesh.nodes.size()); - } - - FILE* out = fopen(path, "wb"); - if (!out) - return false; - - fprintf(out, "{\n"); - fprintf(out, "\t\"generator\": \"gltfpack %s\",\n", getVersion().c_str()); - fprintf(out, "\t\"scene\": {\n"); - fprintf(out, "\t\t\"nodeCount\": %d,\n", int(node_count)); - fprintf(out, "\t\t\"meshCount\": %d,\n", int(mesh_count)); - fprintf(out, "\t\t\"materialCount\": %d,\n", int(material_count)); - fprintf(out, "\t\t\"textureCount\": %d,\n", int(data->textures_count)); - fprintf(out, "\t\t\"animationCount\": %d\n", int(animation_count)); - fprintf(out, "\t},\n"); - fprintf(out, "\t\"render\": {\n"); - fprintf(out, "\t\t\"drawCount\": %d,\n", int(total_draws)); - fprintf(out, "\t\t\"instanceCount\": %d,\n", int(total_instances)); - fprintf(out, "\t\t\"triangleCount\": %lld\n", (long long)total_triangles); - fprintf(out, "\t},\n"); - fprintf(out, "\t\"data\": {\n"); - fprintf(out, "\t\t\"json\": %d,\n", int(json_size)); - fprintf(out, "\t\t\"binary\": %d,\n", int(bin_size)); - fprintf(out, "\t\t\"buffers\": {\n"); - fprintf(out, "\t\t\t\"vertex\": %d,\n", int(bytes[BufferView::Kind_Vertex])); - fprintf(out, "\t\t\t\"index\": %d,\n", int(bytes[BufferView::Kind_Index])); - fprintf(out, "\t\t\t\"animation\": %d,\n", int(bytes[BufferView::Kind_Time] + bytes[BufferView::Kind_Keyframe])); - fprintf(out, "\t\t\t\"transform\": %d,\n", int(bytes[BufferView::Kind_Skin] + bytes[BufferView::Kind_Instance])); - fprintf(out, "\t\t\t\"image\": %d\n", int(bytes[BufferView::Kind_Image])); - fprintf(out, "\t\t}\n"); - fprintf(out, "\t}\n"); - fprintf(out, "}\n"); - - int rc = fclose(out); - return rc == 0; -} - -static bool canTransformMesh(const Mesh& mesh) -{ - // volume thickness is specified in mesh coordinate space; to avoid modifying materials we prohibit transforming meshes with volume materials - if (mesh.material && mesh.material->has_volume && mesh.material->volume.thickness_factor > 0.f) - return false; - - return true; -} - -static void process(cgltf_data* data, const char* input_path, const char* output_path, const char* report_path, std::vector& meshes, std::vector& animations, const std::string& extras, const Settings& settings, std::string& json, std::string& bin, std::string& fallback, size_t& fallback_size) -{ - if (settings.verbose) - { - printf("input: %d nodes, %d meshes (%d primitives), %d materials, %d skins, %d animations\n", - int(data->nodes_count), int(data->meshes_count), int(meshes.size()), int(data->materials_count), int(data->skins_count), int(animations.size())); - printMeshStats(meshes, "input"); - } - - for (size_t i = 0; i < animations.size(); ++i) - { - processAnimation(animations[i], settings); - } - - std::vector nodes(data->nodes_count); - - markScenes(data, nodes); - markAnimated(data, nodes, animations); - - for (size_t i = 0; i < meshes.size(); ++i) - { - Mesh& mesh = meshes[i]; - assert(mesh.instances.empty()); - - // mesh is already world space, skip - if (mesh.nodes.empty()) - continue; - - // note: when -kn is specified, we keep mesh-node attachment so that named nodes can be transformed - if (settings.keep_nodes) - continue; - - // we keep skinned meshes or meshes with morph targets as is - // in theory we could transform both, but in practice transforming morph target meshes is more involved, - // and reparenting skinned meshes leads to incorrect bounding box generated in three.js - if (mesh.skin || mesh.targets) - continue; - - bool any_animated = false; - for (size_t j = 0; j < mesh.nodes.size(); ++j) - any_animated |= nodes[mesh.nodes[j] - data->nodes].animated; - - // animated meshes will be anchored to the same node that they used to be in to retain the animation - if (any_animated) - continue; - - int scene = nodes[mesh.nodes[0] - data->nodes].scene; - bool any_other_scene = false; - for (size_t j = 0; j < mesh.nodes.size(); ++j) - any_other_scene |= scene != nodes[mesh.nodes[j] - data->nodes].scene; - - // we only merge instances when all nodes have a single consistent scene - if (scene < 0 || any_other_scene) - continue; - - // we only merge multiple instances together if requested - // this often makes the scenes faster to render by reducing the draw call count, but can result in larger files - if (mesh.nodes.size() > 1 && !settings.mesh_merge && !settings.mesh_instancing) - continue; - - // prefer instancing if possible, use merging otherwise - if (mesh.nodes.size() > 1 && settings.mesh_instancing) - { - mesh.instances.resize(mesh.nodes.size()); - - for (size_t j = 0; j < mesh.nodes.size(); ++j) - cgltf_node_transform_world(mesh.nodes[j], mesh.instances[j].data); - - mesh.nodes.clear(); - mesh.scene = scene; - } - else if (canTransformMesh(mesh)) - { - mergeMeshInstances(mesh); - - assert(mesh.nodes.empty()); - mesh.scene = scene; - } - } - - // material information is required for mesh and image processing - std::vector materials(data->materials_count); - std::vector images(data->images_count); - - analyzeMaterials(data, materials, images); - - optimizeMaterials(data, input_path, images); - - // streams need to be filtered before mesh merging (or processing) to make sure we can merge meshes with redundant streams - for (size_t i = 0; i < meshes.size(); ++i) - { - Mesh& mesh = meshes[i]; - MaterialInfo mi = mesh.material ? materials[mesh.material - data->materials] : MaterialInfo(); - - // merge material requirements across all variants - for (size_t j = 0; j < mesh.variants.size(); ++j) - { - MaterialInfo vi = materials[mesh.variants[j].material - data->materials]; - - mi.needsTangents |= vi.needsTangents; - mi.textureSetMask |= vi.textureSetMask; - } - - filterStreams(mesh, mi); - } - - mergeMeshMaterials(data, meshes, settings); - mergeMeshes(meshes, settings); - filterEmptyMeshes(meshes); - - markNeededNodes(data, nodes, meshes, animations, settings); - markNeededMaterials(data, materials, meshes, settings); - -#ifndef NDEBUG - std::vector debug_meshes; - - for (size_t i = 0; i < meshes.size(); ++i) - { - const Mesh& mesh = meshes[i]; - - if (settings.simplify_debug > 0) - { - Mesh kinds = {}; - Mesh loops = {}; - debugSimplify(mesh, kinds, loops, settings.simplify_debug); - debug_meshes.push_back(kinds); - debug_meshes.push_back(loops); - } - - if (settings.meshlet_debug > 0) - { - Mesh meshlets = {}; - Mesh bounds = {}; - debugMeshlets(mesh, meshlets, bounds, settings.meshlet_debug, /* scan= */ false); - debug_meshes.push_back(meshlets); - debug_meshes.push_back(bounds); - } - } -#endif - - for (size_t i = 0; i < meshes.size(); ++i) - { - processMesh(meshes[i], settings); - } - -#ifndef NDEBUG - meshes.insert(meshes.end(), debug_meshes.begin(), debug_meshes.end()); -#endif - - filterEmptyMeshes(meshes); // some meshes may become empty after processing - - QuantizationPosition qp = prepareQuantizationPosition(meshes, settings); - - std::vector qt_materials(materials.size()); - std::vector qt_meshes(meshes.size(), size_t(-1)); - prepareQuantizationTexture(data, qt_materials, qt_meshes, meshes, settings); - - QuantizationTexture qt_dummy = {}; - qt_dummy.bits = settings.tex_bits; - - std::string json_images; - std::string json_samplers; - std::string json_textures; - std::string json_materials; - std::string json_accessors; - std::string json_meshes; - std::string json_nodes; - std::string json_skins; - std::vector json_roots(data->scenes_count); - std::string json_animations; - std::string json_cameras; - std::string json_extensions; - - std::vector views; - - bool ext_pbr_specular_glossiness = false; - bool ext_clearcoat = false; - bool ext_transmission = false; - bool ext_ior = false; - bool ext_specular = false; - bool ext_sheen = false; - bool ext_volume = false; - bool ext_emissive_strength = false; - bool ext_iridescence = false; - bool ext_unlit = false; - bool ext_instancing = false; - bool ext_texture_transform = false; - - size_t accr_offset = 0; - size_t node_offset = 0; - size_t mesh_offset = 0; - size_t material_offset = 0; - - for (size_t i = 0; i < data->samplers_count; ++i) - { - const cgltf_sampler& sampler = data->samplers[i]; - - comma(json_samplers); - append(json_samplers, "{"); - writeSampler(json_samplers, sampler); - append(json_samplers, "}"); - } - - std::vector encoded_images; - -#ifdef WITH_BASISU - if (data->images_count && settings.texture_ktx2) - { - encoded_images.resize(data->images_count); - - encodeImages(encoded_images.data(), data, images, input_path, settings); - } -#endif - - for (size_t i = 0; i < data->images_count; ++i) - { - const cgltf_image& image = data->images[i]; - - comma(json_images); - append(json_images, "{"); - if (encoded_images.size()) - { - if (encoded_images[i].empty()) - fprintf(stderr, "Warning: unable to encode image %d (%s), skipping\n", int(i), image.uri ? image.uri : "?"); - else - writeEncodedImage(json_images, views, image, encoded_images[i], images[i], output_path, settings); - - encoded_images[i] = std::string(); // reclaim memory early - } - else - { - writeImage(json_images, views, image, images[i], i, input_path, settings); - } - append(json_images, "}"); - } - - for (size_t i = 0; i < data->textures_count; ++i) - { - const cgltf_texture& texture = data->textures[i]; - - comma(json_textures); - append(json_textures, "{"); - writeTexture(json_textures, texture, data, settings); - append(json_textures, "}"); - } - - for (size_t i = 0; i < data->materials_count; ++i) - { - MaterialInfo& mi = materials[i]; - - if (!mi.keep) - continue; - - const cgltf_material& material = data->materials[i]; - - comma(json_materials); - append(json_materials, "{"); - writeMaterial(json_materials, data, material, settings.quantize ? &qp : NULL, settings.quantize ? &qt_materials[i] : NULL); - if (settings.keep_extras) - writeExtras(json_materials, extras, material.extras); - append(json_materials, "}"); - - mi.remap = int(material_offset); - material_offset++; - - ext_pbr_specular_glossiness = ext_pbr_specular_glossiness || material.has_pbr_specular_glossiness; - ext_clearcoat = ext_clearcoat || material.has_clearcoat; - ext_transmission = ext_transmission || material.has_transmission; - ext_ior = ext_ior || material.has_ior; - ext_specular = ext_specular || material.has_specular; - ext_sheen = ext_sheen || material.has_sheen; - ext_volume = ext_volume || material.has_volume; - ext_emissive_strength = ext_emissive_strength || material.has_emissive_strength; - ext_iridescence = ext_iridescence || material.has_iridescence; - ext_unlit = ext_unlit || material.unlit; - ext_texture_transform = ext_texture_transform || mi.usesTextureTransform; - } - - for (size_t i = 0; i < meshes.size(); ++i) - { - const Mesh& mesh = meshes[i]; - - comma(json_meshes); - append(json_meshes, "{\"primitives\":["); - - size_t pi = i; - for (; pi < meshes.size(); ++pi) - { - const Mesh& prim = meshes[pi]; - - if (prim.skin != mesh.skin || prim.targets != mesh.targets) - break; - - if (pi > i && (mesh.instances.size() || prim.instances.size())) - break; - - if (!compareMeshNodes(mesh, prim)) - break; - - if (!compareMeshTargets(mesh, prim)) - break; - - const QuantizationTexture& qt = qt_meshes[pi] == size_t(-1) ? qt_dummy : qt_materials[qt_meshes[pi]]; - - comma(json_meshes); - append(json_meshes, "{\"attributes\":{"); - writeMeshAttributes(json_meshes, views, json_accessors, accr_offset, prim, 0, qp, qt, settings); - append(json_meshes, "}"); - if (prim.type != cgltf_primitive_type_triangles) - { - append(json_meshes, ",\"mode\":"); - append(json_meshes, size_t(prim.type)); - } - if (mesh.targets) - { - append(json_meshes, ",\"targets\":["); - for (size_t j = 0; j < mesh.targets; ++j) - { - comma(json_meshes); - append(json_meshes, "{"); - writeMeshAttributes(json_meshes, views, json_accessors, accr_offset, prim, int(1 + j), qp, qt, settings); - append(json_meshes, "}"); - } - append(json_meshes, "]"); - } - - if (!prim.indices.empty()) - { - size_t index_accr = writeMeshIndices(views, json_accessors, accr_offset, prim, settings); - - append(json_meshes, ",\"indices\":"); - append(json_meshes, index_accr); - } - - if (prim.material) - { - MaterialInfo& mi = materials[prim.material - data->materials]; - - assert(mi.keep); - append(json_meshes, ",\"material\":"); - append(json_meshes, size_t(mi.remap)); - } - - if (prim.variants.size()) - { - append(json_meshes, ",\"extensions\":{\"KHR_materials_variants\":{\"mappings\":["); - - for (size_t j = 0; j < prim.variants.size(); ++j) - { - const cgltf_material_mapping& variant = prim.variants[j]; - MaterialInfo& mi = materials[variant.material - data->materials]; - - assert(mi.keep); - comma(json_meshes); - append(json_meshes, "{\"material\":"); - append(json_meshes, size_t(mi.remap)); - append(json_meshes, ",\"variants\":["); - append(json_meshes, size_t(variant.variant)); - append(json_meshes, "]}"); - } - - append(json_meshes, "]}}"); - } - - append(json_meshes, "}"); - } - - append(json_meshes, "]"); - - if (mesh.target_weights.size()) - { - append(json_meshes, ",\"weights\":["); - for (size_t j = 0; j < mesh.target_weights.size(); ++j) - { - comma(json_meshes); - append(json_meshes, mesh.target_weights[j]); - } - append(json_meshes, "]"); - } - - if (mesh.target_names.size()) - { - append(json_meshes, ",\"extras\":{\"targetNames\":["); - for (size_t j = 0; j < mesh.target_names.size(); ++j) - { - comma(json_meshes); - append(json_meshes, "\""); - append(json_meshes, mesh.target_names[j]); - append(json_meshes, "\""); - } - append(json_meshes, "]}"); - } - - append(json_meshes, "}"); - - assert(mesh.nodes.empty() || mesh.instances.empty()); - ext_instancing = ext_instancing || !mesh.instances.empty(); - - if (mesh.nodes.size()) - { - for (size_t j = 0; j < mesh.nodes.size(); ++j) - { - NodeInfo& ni = nodes[mesh.nodes[j] - data->nodes]; - - assert(ni.keep); - ni.meshes.push_back(node_offset); - - writeMeshNode(json_nodes, mesh_offset, mesh.nodes[j], mesh.skin, data, settings.quantize ? &qp : NULL); - - node_offset++; - } - } - else if (mesh.instances.size()) - { - assert(mesh.scene >= 0); - comma(json_roots[mesh.scene]); - append(json_roots[mesh.scene], node_offset); - - size_t instance_accr = writeInstances(views, json_accessors, accr_offset, mesh.instances, qp, settings); - - assert(!mesh.skin); - writeMeshNodeInstanced(json_nodes, mesh_offset, instance_accr); - - node_offset++; - } - else - { - assert(mesh.scene >= 0); - comma(json_roots[mesh.scene]); - append(json_roots[mesh.scene], node_offset); - - writeMeshNode(json_nodes, mesh_offset, NULL, mesh.skin, data, settings.quantize ? &qp : NULL); - - node_offset++; - } - - mesh_offset++; - - // skip all meshes that we've written in this iteration - assert(pi > i); - i = pi - 1; - } - - remapNodes(data, nodes, node_offset); - - for (size_t i = 0; i < data->nodes_count; ++i) - { - NodeInfo& ni = nodes[i]; - - if (!ni.keep) - continue; - - const cgltf_node& node = data->nodes[i]; - - comma(json_nodes); - append(json_nodes, "{"); - writeNode(json_nodes, node, nodes, data); - if (settings.keep_extras) - writeExtras(json_nodes, extras, node.extras); - append(json_nodes, "}"); - } - - for (size_t i = 0; i < data->scenes_count; ++i) - { - for (size_t j = 0; j < data->scenes[i].nodes_count; ++j) - { - NodeInfo& ni = nodes[data->scenes[i].nodes[j] - data->nodes]; - - if (ni.keep) - { - comma(json_roots[i]); - append(json_roots[i], size_t(ni.remap)); - } - } - } - - for (size_t i = 0; i < data->skins_count; ++i) - { - const cgltf_skin& skin = data->skins[i]; - - size_t matrix_accr = writeJointBindMatrices(views, json_accessors, accr_offset, skin, qp, settings); - - writeSkin(json_skins, skin, matrix_accr, nodes, data); - } - - for (size_t i = 0; i < animations.size(); ++i) - { - const Animation& animation = animations[i]; - - writeAnimation(json_animations, views, json_accessors, accr_offset, animation, i, data, nodes, settings); - } - - for (size_t i = 0; i < data->cameras_count; ++i) - { - const cgltf_camera& camera = data->cameras[i]; - - writeCamera(json_cameras, camera); - } - - if (data->lights_count > 0) - { - comma(json_extensions); - append(json_extensions, "\"KHR_lights_punctual\":{\"lights\":["); - - for (size_t i = 0; i < data->lights_count; ++i) - { - const cgltf_light& light = data->lights[i]; - - writeLight(json_extensions, light); - } - - append(json_extensions, "]}"); - } - - if (data->variants_count > 0) - { - comma(json_extensions); - append(json_extensions, "\"KHR_materials_variants\":{\"variants\":["); - - for (size_t i = 0; i < data->variants_count; ++i) - { - const cgltf_material_variant& variant = data->variants[i]; - - comma(json_extensions); - append(json_extensions, "{\"name\":\""); - append(json_extensions, variant.name); - append(json_extensions, "\"}"); - } - - append(json_extensions, "]}"); - } - - append(json, "\"asset\":{"); - append(json, "\"version\":\"2.0\",\"generator\":\"gltfpack "); - append(json, getVersion()); - append(json, "\""); - writeExtras(json, extras, data->asset.extras); - append(json, "}"); - - const ExtensionInfo extensions[] = { - {"KHR_mesh_quantization", settings.quantize, true}, - {"EXT_meshopt_compression", settings.compress, !settings.fallback}, - {"KHR_texture_transform", (settings.quantize && !json_textures.empty()) || ext_texture_transform, false}, - {"KHR_materials_pbrSpecularGlossiness", ext_pbr_specular_glossiness, false}, - {"KHR_materials_clearcoat", ext_clearcoat, false}, - {"KHR_materials_transmission", ext_transmission, false}, - {"KHR_materials_ior", ext_ior, false}, - {"KHR_materials_specular", ext_specular, false}, - {"KHR_materials_sheen", ext_sheen, false}, - {"KHR_materials_volume", ext_volume, false}, - {"KHR_materials_emissive_strength", ext_emissive_strength, false}, - {"KHR_materials_iridescence", ext_iridescence, false}, - {"KHR_materials_unlit", ext_unlit, false}, - {"KHR_materials_variants", data->variants_count > 0, false}, - {"KHR_lights_punctual", data->lights_count > 0, false}, - {"KHR_texture_basisu", !json_textures.empty() && settings.texture_ktx2, true}, - {"EXT_mesh_gpu_instancing", ext_instancing, true}, - }; - - writeExtensions(json, extensions, sizeof(extensions) / sizeof(extensions[0])); - - std::string json_views; - finalizeBufferViews(json_views, views, bin, settings.fallback ? &fallback : NULL, fallback_size); - - writeArray(json, "bufferViews", json_views); - writeArray(json, "accessors", json_accessors); - writeArray(json, "samplers", json_samplers); - writeArray(json, "images", json_images); - writeArray(json, "textures", json_textures); - writeArray(json, "materials", json_materials); - writeArray(json, "meshes", json_meshes); - writeArray(json, "skins", json_skins); - writeArray(json, "animations", json_animations); - writeArray(json, "nodes", json_nodes); - - if (!json_roots.empty()) - { - append(json, ",\"scenes\":["); - - for (size_t i = 0; i < data->scenes_count; ++i) - writeScene(json, data->scenes[i], json_roots[i]); - - append(json, "]"); - } - - writeArray(json, "cameras", json_cameras); - - if (data->scene) - { - append(json, ",\"scene\":"); - append(json, size_t(data->scene - data->scenes)); - } - - if (!json_extensions.empty()) - { - append(json, ",\"extensions\":{"); - append(json, json_extensions); - append(json, "}"); - } - - if (settings.verbose) - { - printMeshStats(meshes, "output"); - printSceneStats(views, meshes, node_offset, mesh_offset, material_offset, json.size(), bin.size()); - } - - if (settings.verbose > 1) - { - printAttributeStats(views, BufferView::Kind_Vertex, "vertex"); - printAttributeStats(views, BufferView::Kind_Index, "index"); - printAttributeStats(views, BufferView::Kind_Keyframe, "keyframe"); - printAttributeStats(views, BufferView::Kind_Instance, "instance"); - - printImageStats(views, TextureKind_Generic, "generic"); - printImageStats(views, TextureKind_Color, "color"); - printImageStats(views, TextureKind_Normal, "normal"); - printImageStats(views, TextureKind_Attrib, "attrib"); - } - - if (report_path) - { - if (!printReport(report_path, data, views, meshes, node_offset, mesh_offset, material_offset, animations.size(), json.size(), bin.size())) - { - fprintf(stderr, "Warning: cannot save report to %s\n", report_path); - } - } -} - -static void writeU32(FILE* out, uint32_t data) -{ - fwrite(&data, 4, 1, out); -} - -static const char* getBaseName(const char* path) -{ - const char* slash = strrchr(path, '/'); - const char* backslash = strrchr(path, '\\'); - - const char* rs = slash ? slash + 1 : path; - const char* bs = backslash ? backslash + 1 : path; - - return std::max(rs, bs); -} - -static std::string getBufferSpec(const char* bin_path, size_t bin_size, const char* fallback_path, size_t fallback_size, bool fallback_ref) -{ - std::string json; - append(json, "\"buffers\":["); - append(json, "{"); - if (bin_path) - { - append(json, "\"uri\":\""); - append(json, bin_path); - append(json, "\""); - } - comma(json); - append(json, "\"byteLength\":"); - append(json, bin_size); - append(json, "}"); - if (fallback_ref) - { - comma(json); - append(json, "{"); - if (fallback_path) - { - append(json, "\"uri\":\""); - append(json, fallback_path); - append(json, "\""); - } - comma(json); - append(json, "\"byteLength\":"); - append(json, fallback_size); - append(json, ",\"extensions\":{"); - append(json, "\"EXT_meshopt_compression\":{"); - append(json, "\"fallback\":true"); - append(json, "}}"); - append(json, "}"); - } - append(json, "]"); - - return json; -} - -int gltfpack(const char* input, const char* output, const char* report, Settings settings) -{ - cgltf_data* data = 0; - std::vector meshes; - std::vector animations; - std::string extras; - - std::string iext = getExtension(input); - std::string oext = output ? getExtension(output) : ""; - - if (iext == ".gltf" || iext == ".glb") - { - const char* error = 0; - data = parseGltf(input, meshes, animations, extras, &error); - - if (error) - { - fprintf(stderr, "Error loading %s: %s\n", input, error); - return 2; - } - } - else if (iext == ".obj") - { - const char* error = 0; - data = parseObj(input, meshes, &error); - - if (!data) - { - fprintf(stderr, "Error loading %s: %s\n", input, error); - return 2; - } - } - else - { - fprintf(stderr, "Error loading %s: unknown extension (expected .gltf or .glb or .obj)\n", input); - return 2; - } - -#ifndef WITH_BASISU - if (data->images_count && settings.texture_ktx2) - { - fprintf(stderr, "Error: gltfpack was built without BasisU support, texture compression is not available\n"); -#ifdef __wasi__ - fprintf(stderr, "Note: node.js builds do not support BasisU due to lack of platform features; download a native build from https://github.com/zeux/meshoptimizer/releases\n"); -#endif - return 3; - } -#endif - - if (oext == ".glb") - { - settings.texture_embed = true; - } - - std::string json, bin, fallback; - size_t fallback_size = 0; - process(data, input, output, report, meshes, animations, extras, settings, json, bin, fallback, fallback_size); - - cgltf_free(data); - - if (!output) - { - return 0; - } - - if (oext == ".gltf") - { - std::string binpath = output; - binpath.replace(binpath.size() - 5, 5, ".bin"); - - std::string fbpath = output; - fbpath.replace(fbpath.size() - 5, 5, ".fallback.bin"); - - FILE* outjson = fopen(output, "wb"); - FILE* outbin = fopen(binpath.c_str(), "wb"); - FILE* outfb = settings.fallback ? fopen(fbpath.c_str(), "wb") : NULL; - if (!outjson || !outbin || (!outfb && settings.fallback)) - { - fprintf(stderr, "Error saving %s\n", output); - return 4; - } - - std::string bufferspec = getBufferSpec(getBaseName(binpath.c_str()), bin.size(), settings.fallback ? getBaseName(fbpath.c_str()) : NULL, fallback_size, settings.compress); - - fprintf(outjson, "{"); - fwrite(bufferspec.c_str(), bufferspec.size(), 1, outjson); - fprintf(outjson, ","); - fwrite(json.c_str(), json.size(), 1, outjson); - fprintf(outjson, "}"); - - fwrite(bin.c_str(), bin.size(), 1, outbin); - - if (settings.fallback) - fwrite(fallback.c_str(), fallback.size(), 1, outfb); - - int rc = 0; - rc |= fclose(outjson); - rc |= fclose(outbin); - if (outfb) - rc |= fclose(outfb); - - if (rc) - { - fprintf(stderr, "Error saving %s\n", output); - return 4; - } - } - else if (oext == ".glb") - { - std::string fbpath = output; - fbpath.replace(fbpath.size() - 4, 4, ".fallback.bin"); - - FILE* out = fopen(output, "wb"); - FILE* outfb = settings.fallback ? fopen(fbpath.c_str(), "wb") : NULL; - if (!out || (!outfb && settings.fallback)) - { - fprintf(stderr, "Error saving %s\n", output); - return 4; - } - - std::string bufferspec = getBufferSpec(NULL, bin.size(), settings.fallback ? getBaseName(fbpath.c_str()) : NULL, fallback_size, settings.compress); - - json.insert(0, "{" + bufferspec + ","); - json.push_back('}'); - - while (json.size() % 4) - json.push_back(' '); - - while (bin.size() % 4) - bin.push_back('\0'); - - writeU32(out, 0x46546C67); - writeU32(out, 2); - writeU32(out, uint32_t(12 + 8 + json.size() + 8 + bin.size())); - - writeU32(out, uint32_t(json.size())); - writeU32(out, 0x4E4F534A); - fwrite(json.c_str(), json.size(), 1, out); - - writeU32(out, uint32_t(bin.size())); - writeU32(out, 0x004E4942); - fwrite(bin.c_str(), bin.size(), 1, out); - - if (settings.fallback) - fwrite(fallback.c_str(), fallback.size(), 1, outfb); - - int rc = 0; - rc |= fclose(out); - if (outfb) - rc |= fclose(outfb); - - if (rc) - { - fprintf(stderr, "Error saving %s\n", output); - return 4; - } - } - else - { - fprintf(stderr, "Error saving %s: unknown extension (expected .gltf or .glb)\n", output); - return 4; - } - - return 0; -} - -Settings defaults() -{ - Settings settings = {}; - settings.quantize = true; - settings.pos_bits = 14; - settings.tex_bits = 12; - settings.nrm_bits = 8; - settings.col_bits = 8; - settings.trn_bits = 16; - settings.rot_bits = 12; - settings.scl_bits = 16; - settings.anim_freq = 30; - settings.simplify_threshold = 1.f; - settings.texture_scale = 1.f; - for (int kind = 0; kind < TextureKind__Count; ++kind) - settings.texture_quality[kind] = 8; - - return settings; -} - -template -T clamp(T v, T min, T max) -{ - return v < min ? min : v > max ? max : v; -} - -unsigned int textureMask(const char* arg) -{ - unsigned int result = 0; - - while (arg) - { - const char* comma = strchr(arg, ','); - size_t seg = comma ? comma - arg - 1 : strlen(arg); - - if (strncmp(arg, "color", seg) == 0) - result |= 1 << TextureKind_Color; - else if (strncmp(arg, "normal", seg) == 0) - result |= 1 << TextureKind_Normal; - else if (strncmp(arg, "attrib", seg) == 0) - result |= 1 << TextureKind_Attrib; - else - fprintf(stderr, "Warning: unrecognized texture class %.*s\n", int(seg), arg); - - arg = comma ? comma + 1 : NULL; - } - - return result; -} - -int main(int argc, char** argv) -{ -#ifndef __wasi__ - setlocale(LC_ALL, "C"); // disable locale specific convention for number parsing/printing -#endif - - meshopt_encodeIndexVersion(1); - - Settings settings = defaults(); - - const char* input = 0; - const char* output = 0; - const char* report = 0; - bool help = false; - bool test = false; - - std::vector testinputs; - - for (int i = 1; i < argc; ++i) - { - const char* arg = argv[i]; - - if (strcmp(arg, "-vp") == 0 && i + 1 < argc && isdigit(argv[i + 1][0])) - { - settings.pos_bits = clamp(atoi(argv[++i]), 1, 16); - } - else if (strcmp(arg, "-vt") == 0 && i + 1 < argc && isdigit(argv[i + 1][0])) - { - settings.tex_bits = clamp(atoi(argv[++i]), 1, 16); - } - else if (strcmp(arg, "-vn") == 0 && i + 1 < argc && isdigit(argv[i + 1][0])) - { - settings.nrm_bits = clamp(atoi(argv[++i]), 1, 16); - } - else if (strcmp(arg, "-vc") == 0 && i + 1 < argc && isdigit(argv[i + 1][0])) - { - settings.col_bits = clamp(atoi(argv[++i]), 1, 16); - } - else if (strcmp(arg, "-at") == 0 && i + 1 < argc && isdigit(argv[i + 1][0])) - { - settings.trn_bits = clamp(atoi(argv[++i]), 1, 24); - } - else if (strcmp(arg, "-ar") == 0 && i + 1 < argc && isdigit(argv[i + 1][0])) - { - settings.rot_bits = clamp(atoi(argv[++i]), 4, 16); - } - else if (strcmp(arg, "-as") == 0 && i + 1 < argc && isdigit(argv[i + 1][0])) - { - settings.scl_bits = clamp(atoi(argv[++i]), 1, 24); - } - else if (strcmp(arg, "-af") == 0 && i + 1 < argc && isdigit(argv[i + 1][0])) - { - settings.anim_freq = clamp(atoi(argv[++i]), 1, 100); - } - else if (strcmp(arg, "-ac") == 0) - { - settings.anim_const = true; - } - else if (strcmp(arg, "-kn") == 0) - { - settings.keep_nodes = true; - } - else if (strcmp(arg, "-km") == 0) - { - settings.keep_materials = true; - } - else if (strcmp(arg, "-ke") == 0) - { - settings.keep_extras = true; - } - else if (strcmp(arg, "-mm") == 0) - { - settings.mesh_merge = true; - } - else if (strcmp(arg, "-mi") == 0) - { - settings.mesh_instancing = true; - } - else if (strcmp(arg, "-si") == 0 && i + 1 < argc && isdigit(argv[i + 1][0])) - { - settings.simplify_threshold = clamp(float(atof(argv[++i])), 0.f, 1.f); - } - else if (strcmp(arg, "-sa") == 0) - { - settings.simplify_aggressive = true; - } -#ifndef NDEBUG - else if (strcmp(arg, "-sd") == 0 && i + 1 < argc && isdigit(argv[i + 1][0])) - { - settings.simplify_debug = clamp(float(atof(argv[++i])), 0.f, 1.f); - } - else if (strcmp(arg, "-md") == 0 && i + 1 < argc && isdigit(argv[i + 1][0])) - { - settings.meshlet_debug = clamp(atoi(argv[++i]), 3, 255); - } -#endif - else if (strcmp(arg, "-tu") == 0) - { - settings.texture_ktx2 = true; - - unsigned int mask = ~0u; - if (i + 1 < argc && isalpha(argv[i + 1][0])) - mask = textureMask(argv[++i]); - - for (int kind = 0; kind < TextureKind__Count; ++kind) - if (mask & (1 << kind)) - settings.texture_uastc[kind] = true; - } - else if (strcmp(arg, "-tc") == 0) - { - settings.texture_ktx2 = true; - } - else if (strcmp(arg, "-tq") == 0 && i + 1 < argc && isdigit(argv[i + 1][0])) - { - int quality = clamp(atoi(argv[++i]), 1, 10); - for (int kind = 0; kind < TextureKind__Count; ++kind) - settings.texture_quality[kind] = quality; - } - else if (strcmp(arg, "-tq") == 0 && i + 2 < argc && isalpha(argv[i + 1][0]) && isdigit(argv[i + 2][0])) - { - unsigned int mask = textureMask(argv[++i]); - int quality = clamp(atoi(argv[++i]), 1, 10); - - for (int kind = 0; kind < TextureKind__Count; ++kind) - if (mask & (1 << kind)) - settings.texture_quality[kind] = quality; - } - else if (strcmp(arg, "-ts") == 0 && i + 1 < argc && isdigit(argv[i + 1][0])) - { - settings.texture_scale = clamp(float(atof(argv[++i])), 0.f, 1.f); - } - else if (strcmp(arg, "-tl") == 0 && i + 1 < argc && isdigit(argv[i + 1][0])) - { - settings.texture_limit = atoi(argv[++i]); - } - else if (strcmp(arg, "-tp") == 0) - { - settings.texture_pow2 = true; - } - else if (strcmp(arg, "-tfy") == 0) - { - settings.texture_flipy = true; - } - else if (strcmp(arg, "-te") == 0) - { - fprintf(stderr, "Warning: -te is deprecated and will be removed in the future; gltfpack now automatically embeds textures into GLB files\n"); - } - else if (strcmp(arg, "-tj") == 0 && i + 1 < argc && isdigit(argv[i + 1][0])) - { - settings.texture_jobs = clamp(atoi(argv[++i]), 0, 128); - } - else if (strcmp(arg, "-noq") == 0) - { - settings.quantize = false; - } - else if (strcmp(arg, "-i") == 0 && i + 1 < argc && !input) - { - input = argv[++i]; - } - else if (strcmp(arg, "-o") == 0 && i + 1 < argc && !output) - { - output = argv[++i]; - } - else if (strcmp(arg, "-r") == 0 && i + 1 < argc && !report) - { - report = argv[++i]; - } - else if (strcmp(arg, "-c") == 0) - { - settings.compress = true; - } - else if (strcmp(arg, "-cc") == 0) - { - settings.compress = true; - settings.compressmore = true; - } - else if (strcmp(arg, "-cf") == 0) - { - settings.compress = true; - settings.fallback = true; - } - else if (strcmp(arg, "-v") == 0) - { - settings.verbose = 1; - } - else if (strcmp(arg, "-vv") == 0) - { - settings.verbose = 2; - } - else if (strcmp(arg, "-h") == 0) - { - help = true; - } - else if (strcmp(arg, "-test") == 0) - { - test = true; - } - else if (arg[0] == '-') - { - fprintf(stderr, "Unrecognized option %s\n", arg); - return 1; - } - else if (test) - { - testinputs.push_back(arg); - } - else - { - fprintf(stderr, "Expected option, got %s instead\n", arg); - return 1; - } - } - - // shortcut for gltfpack -v - if (settings.verbose && argc == 2) - { - printf("gltfpack %s\n", getVersion().c_str()); - return 0; - } - -#ifdef WITH_BASISU - if (settings.texture_ktx2) - encodeInit(settings.texture_jobs); -#endif - - if (test) - { - for (size_t i = 0; i < testinputs.size(); ++i) - { - const char* path = testinputs[i]; - - printf("%s\n", path); - gltfpack(path, NULL, NULL, settings); - } - - return 0; - } - - if (!input || !output || help) - { - fprintf(stderr, "gltfpack %s\n", getVersion().c_str()); - fprintf(stderr, "Usage: gltfpack [options] -i input -o output\n"); - - if (help) - { - fprintf(stderr, "\nBasics:\n"); - fprintf(stderr, "\t-i file: input file to process, .obj/.gltf/.glb\n"); - fprintf(stderr, "\t-o file: output file path, .gltf/.glb\n"); - fprintf(stderr, "\t-c: produce compressed gltf/glb files (-cc for higher compression ratio)\n"); - fprintf(stderr, "\nTextures:\n"); - fprintf(stderr, "\t-tc: convert all textures to KTX2 with BasisU supercompression\n"); - fprintf(stderr, "\t-tu: use UASTC when encoding textures (much higher quality and much larger size)\n"); - fprintf(stderr, "\t-tq N: set texture encoding quality (default: 8; N should be between 1 and 10\n"); - fprintf(stderr, "\t-ts R: scale texture dimensions by the ratio R (default: 1; R should be between 0 and 1)\n"); - fprintf(stderr, "\t-tl N: limit texture dimensions to N pixels (default: 0 = no limit)\n"); - fprintf(stderr, "\t-tp: resize textures to nearest power of 2 to conform to WebGL1 restrictions\n"); - fprintf(stderr, "\t-tfy: flip textures along Y axis during BasisU supercompression\n"); - fprintf(stderr, "\t-tj N: use N threads when compressing textures\n"); - fprintf(stderr, "\tTexture classes:\n"); - fprintf(stderr, "\t-tu C: use UASTC when encoding textures of class C\n"); - fprintf(stderr, "\t-tq C N: set texture encoding quality for class C\n"); - fprintf(stderr, "\t... where C is a comma-separated list (no spaces) with valid values color,normal,attrib\n"); - fprintf(stderr, "\nSimplification:\n"); - fprintf(stderr, "\t-si R: simplify meshes targeting triangle count ratio R (default: 1; R should be between 0 and 1)\n"); - fprintf(stderr, "\t-sa: aggressively simplify to the target ratio disregarding quality\n"); - fprintf(stderr, "\nVertices:\n"); - fprintf(stderr, "\t-vp N: use N-bit quantization for positions (default: 14; N should be between 1 and 16)\n"); - fprintf(stderr, "\t-vt N: use N-bit quantization for texture coordinates (default: 12; N should be between 1 and 16)\n"); - fprintf(stderr, "\t-vn N: use N-bit quantization for normals and tangents (default: 8; N should be between 1 and 16)\n"); - fprintf(stderr, "\t-vc N: use N-bit quantization for colors (default: 8; N should be between 1 and 16)\n"); - fprintf(stderr, "\nAnimations:\n"); - fprintf(stderr, "\t-at N: use N-bit quantization for translations (default: 16; N should be between 1 and 24)\n"); - fprintf(stderr, "\t-ar N: use N-bit quantization for rotations (default: 12; N should be between 4 and 16)\n"); - fprintf(stderr, "\t-as N: use N-bit quantization for scale (default: 16; N should be between 1 and 24)\n"); - fprintf(stderr, "\t-af N: resample animations at N Hz (default: 30)\n"); - fprintf(stderr, "\t-ac: keep constant animation tracks even if they don't modify the node transform\n"); - fprintf(stderr, "\nScene:\n"); - fprintf(stderr, "\t-kn: keep named nodes and meshes attached to named nodes so that named nodes can be transformed externally\n"); - fprintf(stderr, "\t-km: keep named materials and disable named material merging\n"); - fprintf(stderr, "\t-ke: keep extras data\n"); - fprintf(stderr, "\t-mm: merge instances of the same mesh together when possible\n"); - fprintf(stderr, "\t-mi: use EXT_mesh_gpu_instancing when serializing multiple mesh instances\n"); - fprintf(stderr, "\nMiscellaneous:\n"); - fprintf(stderr, "\t-cf: produce compressed gltf/glb files with fallback for loaders that don't support compression\n"); - fprintf(stderr, "\t-noq: disable quantization; produces much larger glTF files with no extensions\n"); - fprintf(stderr, "\t-v: verbose output (print version when used without other options)\n"); - fprintf(stderr, "\t-r file: output a JSON report to file\n"); - fprintf(stderr, "\t-h: display this help and exit\n"); - } - else - { - fprintf(stderr, "\nBasics:\n"); - fprintf(stderr, "\t-i file: input file to process, .obj/.gltf/.glb\n"); - fprintf(stderr, "\t-o file: output file path, .gltf/.glb\n"); - fprintf(stderr, "\t-c: produce compressed gltf/glb files (-cc for higher compression ratio)\n"); - fprintf(stderr, "\t-tc: convert all textures to KTX2 with BasisU supercompression\n"); - fprintf(stderr, "\t-si R: simplify meshes targeting triangle count ratio R (default: 1; R should be between 0 and 1)\n"); - fprintf(stderr, "\nRun gltfpack -h to display a full list of options\n"); - } - - return 1; - } - - if (settings.texture_limit && !settings.texture_ktx2) - { - fprintf(stderr, "Option -tl is only supported when -tc is set as well\n"); - return 1; - } - - if (settings.texture_pow2 && (settings.texture_limit & (settings.texture_limit - 1)) != 0) - { - fprintf(stderr, "Option -tp requires the limit specified via -tl to be a power of 2\n"); - return 1; - } - - if (settings.texture_scale < 1 && !settings.texture_ktx2) - { - fprintf(stderr, "Option -ts is only supported when -tc is set as well\n"); - return 1; - } - - if (settings.texture_pow2 && !settings.texture_ktx2) - { - fprintf(stderr, "Option -tp is only supported when -tc is set as well\n"); - return 1; - } - - if (settings.texture_flipy && !settings.texture_ktx2) - { - fprintf(stderr, "Option -tfy is only supported when -tc is set as well\n"); - return 1; - } - - return gltfpack(input, output, report, settings); -} - -#ifdef __wasi__ -extern "C" int pack(int argc, char** argv) -{ - chdir("/gltfpack-$pwd"); - - int result = main(argc, argv); - fflush(NULL); - return result; -} -#endif diff --git a/Dependencies/meshoptimizer/gltf/gltfpack.h b/Dependencies/meshoptimizer/gltf/gltfpack.h deleted file mode 100644 index 69aba1a..0000000 --- a/Dependencies/meshoptimizer/gltf/gltfpack.h +++ /dev/null @@ -1,381 +0,0 @@ -/** - * gltfpack - version 0.17 - * - * Copyright (C) 2016-2021, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) - * Report bugs and download new versions at https://github.com/zeux/meshoptimizer - * - * This application is distributed under the MIT License. See notice at the end of this file. - */ - -#ifndef _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include "../extern/cgltf.h" - -#include - -#include -#include - -struct Attr -{ - float f[4]; -}; - -struct Stream -{ - cgltf_attribute_type type; - int index; - int target; // 0 = base mesh, 1+ = morph target - - std::vector data; -}; - -struct Transform -{ - float data[16]; -}; - -struct Mesh -{ - int scene; - std::vector nodes; - std::vector instances; - - cgltf_material* material; - cgltf_skin* skin; - - cgltf_primitive_type type; - - std::vector streams; - std::vector indices; - - size_t targets; - std::vector target_weights; - std::vector target_names; - - std::vector variants; -}; - -struct Track -{ - cgltf_node* node; - cgltf_animation_path_type path; - - bool constant; - bool dummy; - - size_t components; // 1 unless path is cgltf_animation_path_type_weights - - cgltf_interpolation_type interpolation; - - std::vector time; // empty for resampled or constant animations - std::vector data; -}; - -struct Animation -{ - const char* name; - - float start; - int frames; - - std::vector tracks; -}; - -enum TextureKind -{ - TextureKind_Generic, - TextureKind_Color, - TextureKind_Normal, - TextureKind_Attrib, - - TextureKind__Count -}; - -struct Settings -{ - int pos_bits; - int tex_bits; - int nrm_bits; - int col_bits; - - int trn_bits; - int rot_bits; - int scl_bits; - - int anim_freq; - bool anim_const; - - bool keep_nodes; - bool keep_materials; - bool keep_extras; - - bool mesh_merge; - bool mesh_instancing; - - float simplify_threshold; - bool simplify_aggressive; - float simplify_debug; - - int meshlet_debug; - - bool texture_ktx2; - bool texture_embed; - - bool texture_pow2; - bool texture_flipy; - float texture_scale; - int texture_limit; - - bool texture_uastc[TextureKind__Count]; - int texture_quality[TextureKind__Count]; - - int texture_jobs; - - bool quantize; - - bool compress; - bool compressmore; - bool fallback; - - int verbose; -}; - -struct QuantizationPosition -{ - float offset[3]; - float scale; - int bits; -}; - -struct QuantizationTexture -{ - float offset[2]; - float scale[2]; - int bits; -}; - -struct StreamFormat -{ - enum Filter - { - Filter_None = 0, - Filter_Oct = 1, - Filter_Quat = 2, - Filter_Exp = 3, - }; - - cgltf_type type; - cgltf_component_type component_type; - bool normalized; - size_t stride; - Filter filter; -}; - -struct NodeInfo -{ - int scene; - - bool keep; - bool animated; - - unsigned int animated_paths; - - int remap; - std::vector meshes; -}; - -struct MaterialInfo -{ - bool keep; - - bool usesTextureTransform; - bool needsTangents; - unsigned int textureSetMask; - - int remap; -}; - -struct ImageInfo -{ - TextureKind kind; - bool normal_map; - bool srgb; - - int channels; -}; - -struct ExtensionInfo -{ - const char* name; - - bool used; - bool required; -}; - -struct BufferView -{ - enum Kind - { - Kind_Vertex, - Kind_Index, - Kind_Skin, - Kind_Time, - Kind_Keyframe, - Kind_Instance, - Kind_Image, - Kind_Count - }; - - enum Compression - { - Compression_None = -1, - Compression_Attribute, - Compression_Index, - Compression_IndexSequence, - }; - - Kind kind; - StreamFormat::Filter filter; - Compression compression; - size_t stride; - int variant; - - std::string data; - - size_t bytes; -}; - -struct TempFile -{ - std::string path; - int fd; - - TempFile(const char* suffix); - ~TempFile(); -}; - -std::string getFullPath(const char* path, const char* base_path); -std::string getFileName(const char* path); -std::string getExtension(const char* path); - -bool readFile(const char* path, std::string& data); -bool writeFile(const char* path, const std::string& data); - -cgltf_data* parseObj(const char* path, std::vector& meshes, const char** error); -cgltf_data* parseGltf(const char* path, std::vector& meshes, std::vector& animations, std::string& extras, const char** error); - -void processAnimation(Animation& animation, const Settings& settings); -void processMesh(Mesh& mesh, const Settings& settings); - -void debugSimplify(const Mesh& mesh, Mesh& kinds, Mesh& loops, float ratio); -void debugMeshlets(const Mesh& mesh, Mesh& meshlets, Mesh& bounds, int max_vertices, bool scan); - -bool compareMeshTargets(const Mesh& lhs, const Mesh& rhs); -bool compareMeshVariants(const Mesh& lhs, const Mesh& rhs); -bool compareMeshNodes(const Mesh& lhs, const Mesh& rhs); - -void mergeMeshInstances(Mesh& mesh); -void mergeMeshes(std::vector& meshes, const Settings& settings); -void filterEmptyMeshes(std::vector& meshes); -void filterStreams(Mesh& mesh, const MaterialInfo& mi); - -void mergeMeshMaterials(cgltf_data* data, std::vector& meshes, const Settings& settings); -void markNeededMaterials(cgltf_data* data, std::vector& materials, const std::vector& meshes, const Settings& settings); - -bool hasValidTransform(const cgltf_texture_view& view); - -void analyzeMaterials(cgltf_data* data, std::vector& materials, std::vector& images); -void optimizeMaterials(cgltf_data* data, const char* input_path, std::vector& images); - -bool readImage(const cgltf_image& image, const char* input_path, std::string& data, std::string& mime_type); -bool hasAlpha(const std::string& data, const char* mime_type); -bool getDimensions(const std::string& data, const char* mime_type, int& width, int& height); -void adjustDimensions(int& width, int& height, const Settings& settings); -const char* mimeExtension(const char* mime_type); - -#ifdef WITH_BASISU -void encodeInit(int jobs); -bool encodeBasis(const std::string& data, const char* mime_type, std::string& result, const ImageInfo& info, const Settings& settings); -void encodeImages(std::string* encoded, const cgltf_data* data, const std::vector& images, const char* input_path, const Settings& settings); -#endif - -void markScenes(cgltf_data* data, std::vector& nodes); -void markAnimated(cgltf_data* data, std::vector& nodes, const std::vector& animations); -void markNeededNodes(cgltf_data* data, std::vector& nodes, const std::vector& meshes, const std::vector& animations, const Settings& settings); -void remapNodes(cgltf_data* data, std::vector& nodes, size_t& node_offset); -void decomposeTransform(float translation[3], float rotation[4], float scale[3], const float* transform); - -QuantizationPosition prepareQuantizationPosition(const std::vector& meshes, const Settings& settings); -void prepareQuantizationTexture(cgltf_data* data, std::vector& result, std::vector& indices, const std::vector& meshes, const Settings& settings); -void getPositionBounds(float min[3], float max[3], const Stream& stream, const QuantizationPosition* qp); - -StreamFormat writeVertexStream(std::string& bin, const Stream& stream, const QuantizationPosition& qp, const QuantizationTexture& qt, const Settings& settings); -StreamFormat writeIndexStream(std::string& bin, const std::vector& stream); -StreamFormat writeTimeStream(std::string& bin, const std::vector& data); -StreamFormat writeKeyframeStream(std::string& bin, cgltf_animation_path_type type, const std::vector& data, const Settings& settings); - -void compressVertexStream(std::string& bin, const std::string& data, size_t count, size_t stride); -void compressIndexStream(std::string& bin, const std::string& data, size_t count, size_t stride); -void compressIndexSequence(std::string& bin, const std::string& data, size_t count, size_t stride); - -size_t getBufferView(std::vector& views, BufferView::Kind kind, StreamFormat::Filter filter, BufferView::Compression compression, size_t stride, int variant = 0); - -void comma(std::string& s); -void append(std::string& s, size_t v); -void append(std::string& s, float v); -void append(std::string& s, const char* v); -void append(std::string& s, const std::string& v); -void appendJson(std::string& s, const char* begin, const char* end); - -const char* attributeType(cgltf_attribute_type type); -const char* animationPath(cgltf_animation_path_type type); - -void writeMaterial(std::string& json, const cgltf_data* data, const cgltf_material& material, const QuantizationPosition* qp, const QuantizationTexture* qt); -void writeBufferView(std::string& json, BufferView::Kind kind, StreamFormat::Filter filter, size_t count, size_t stride, size_t bin_offset, size_t bin_size, BufferView::Compression compression, size_t compressed_offset, size_t compressed_size); -void writeSampler(std::string& json, const cgltf_sampler& sampler); -void writeImage(std::string& json, std::vector& views, const cgltf_image& image, const ImageInfo& info, size_t index, const char* input_path, const Settings& settings); -void writeEncodedImage(std::string& json, std::vector& views, const cgltf_image& image, const std::string& encoded, const ImageInfo& info, const char* output_path, const Settings& settings); -void writeTexture(std::string& json, const cgltf_texture& texture, cgltf_data* data, const Settings& settings); -void writeMeshAttributes(std::string& json, std::vector& views, std::string& json_accessors, size_t& accr_offset, const Mesh& mesh, int target, const QuantizationPosition& qp, const QuantizationTexture& qt, const Settings& settings); -size_t writeMeshIndices(std::vector& views, std::string& json_accessors, size_t& accr_offset, const Mesh& mesh, const Settings& settings); -size_t writeJointBindMatrices(std::vector& views, std::string& json_accessors, size_t& accr_offset, const cgltf_skin& skin, const QuantizationPosition& qp, const Settings& settings); -size_t writeInstances(std::vector& views, std::string& json_accessors, size_t& accr_offset, const std::vector& transforms, const QuantizationPosition& qp, const Settings& settings); -void writeMeshNode(std::string& json, size_t mesh_offset, cgltf_node* node, cgltf_skin* skin, cgltf_data* data, const QuantizationPosition* qp); -void writeMeshNodeInstanced(std::string& json, size_t mesh_offset, size_t accr_offset); -void writeSkin(std::string& json, const cgltf_skin& skin, size_t matrix_accr, const std::vector& nodes, cgltf_data* data); -void writeNode(std::string& json, const cgltf_node& node, const std::vector& nodes, cgltf_data* data); -void writeAnimation(std::string& json, std::vector& views, std::string& json_accessors, size_t& accr_offset, const Animation& animation, size_t i, cgltf_data* data, const std::vector& nodes, const Settings& settings); -void writeCamera(std::string& json, const cgltf_camera& camera); -void writeLight(std::string& json, const cgltf_light& light); -void writeArray(std::string& json, const char* name, const std::string& contents); -void writeExtensions(std::string& json, const ExtensionInfo* extensions, size_t count); -void writeExtras(std::string& json, const std::string& data, const cgltf_extras& extras); -void writeScene(std::string& json, const cgltf_scene& scene, const std::string& roots); - -/** - * Copyright (c) 2016-2021 Arseny Kapoulkine - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ diff --git a/Dependencies/meshoptimizer/gltf/image.cpp b/Dependencies/meshoptimizer/gltf/image.cpp deleted file mode 100644 index e0d05d6..0000000 --- a/Dependencies/meshoptimizer/gltf/image.cpp +++ /dev/null @@ -1,268 +0,0 @@ -// This file is part of gltfpack; see gltfpack.h for version/license details -#include "gltfpack.h" - -#include - -static const char* kMimeTypes[][2] = { - {"image/jpeg", ".jpg"}, - {"image/jpeg", ".jpeg"}, - {"image/png", ".png"}, -}; - -static const char* inferMimeType(const char* path) -{ - std::string ext = getExtension(path); - - for (size_t i = 0; i < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]); ++i) - if (ext == kMimeTypes[i][1]) - return kMimeTypes[i][0]; - - return ""; -} - -static bool parseDataUri(const char* uri, std::string& mime_type, std::string& result) -{ - if (strncmp(uri, "data:", 5) == 0) - { - const char* comma = strchr(uri, ','); - - if (comma && comma - uri >= 7 && strncmp(comma - 7, ";base64", 7) == 0) - { - const char* base64 = comma + 1; - size_t base64_size = strlen(base64); - size_t size = base64_size - base64_size / 4; - - if (base64_size >= 2) - { - size -= base64[base64_size - 2] == '='; - size -= base64[base64_size - 1] == '='; - } - - void* data = 0; - - cgltf_options options = {}; - cgltf_result res = cgltf_load_buffer_base64(&options, size, base64, &data); - - if (res != cgltf_result_success) - return false; - - mime_type = std::string(uri + 5, comma - 7); - result = std::string(static_cast(data), size); - - free(data); - - return true; - } - } - - return false; -} - -bool readImage(const cgltf_image& image, const char* input_path, std::string& data, std::string& mime_type) -{ - if (image.uri && parseDataUri(image.uri, mime_type, data)) - { - return true; - } - else if (image.buffer_view && image.buffer_view->buffer->data && image.mime_type) - { - const cgltf_buffer_view* view = image.buffer_view; - - data.assign(static_cast(view->buffer->data) + view->offset, view->size); - mime_type = image.mime_type; - return true; - } - else if (image.uri && *image.uri) - { - std::string path = image.uri; - - cgltf_decode_uri(&path[0]); - path.resize(strlen(&path[0])); - - mime_type = image.mime_type ? image.mime_type : inferMimeType(path.c_str()); - - return readFile(getFullPath(path.c_str(), input_path).c_str(), data); - } - else - { - return false; - } -} - -static int readInt16(const std::string& data, size_t offset) -{ - return (unsigned char)data[offset] * 256 + (unsigned char)data[offset + 1]; -} - -static int readInt32(const std::string& data, size_t offset) -{ - return (unsigned((unsigned char)data[offset]) << 24) | - (unsigned((unsigned char)data[offset + 1]) << 16) | - (unsigned((unsigned char)data[offset + 2]) << 8) | - unsigned((unsigned char)data[offset + 3]); -} - -static bool getDimensionsPng(const std::string& data, int& width, int& height) -{ - if (data.size() < 8 + 8 + 13 + 4) - return false; - - const char* signature = "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a"; - if (data.compare(0, 8, signature) != 0) - return false; - - if (data.compare(12, 4, "IHDR") != 0) - return false; - - width = readInt32(data, 16); - height = readInt32(data, 20); - - return true; -} - -static bool getDimensionsJpeg(const std::string& data, int& width, int& height) -{ - size_t offset = 0; - - // note, this can stop parsing before reaching the end but we stop at SOF anyway - while (offset + 4 <= data.size()) - { - if (data[offset] != '\xff') - return false; - - char marker = data[offset + 1]; - - if (marker == '\xff') - { - offset++; - continue; // padding - } - - // d0..d9 correspond to SOI, RSTn, EOI - if (marker == 0 || unsigned(marker - '\xd0') <= 9) - { - offset += 2; - continue; // no payload - } - - // c0..c1 correspond to SOF0, SOF1 - if (marker == '\xc0' || marker == '\xc2') - { - if (offset + 10 > data.size()) - return false; - - width = readInt16(data, offset + 7); - height = readInt16(data, offset + 5); - - return true; - } - - offset += 2 + readInt16(data, offset + 2); - } - - return false; -} - -static bool hasTransparencyPng(const std::string& data) -{ - if (data.size() < 8 + 8 + 13 + 4) - return false; - - const char* signature = "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a"; - if (data.compare(0, 8, signature) != 0) - return false; - - if (data.compare(12, 4, "IHDR") != 0) - return false; - - int ctype = data[25]; - - if (ctype != 3) - return ctype == 4 || ctype == 6; - - size_t offset = 8; // reparse IHDR chunk for simplicity - - while (offset + 12 <= data.size()) - { - int length = readInt32(data, offset); - - if (length < 0) - return false; - - if (data.compare(offset + 4, 4, "tRNS") == 0) - return true; - - offset += 12 + length; - } - - return false; -} - -bool hasAlpha(const std::string& data, const char* mime_type) -{ - if (strcmp(mime_type, "image/png") == 0) - return hasTransparencyPng(data); - else - return false; -} - -bool getDimensions(const std::string& data, const char* mime_type, int& width, int& height) -{ - if (strcmp(mime_type, "image/png") == 0) - return getDimensionsPng(data, width, height); - if (strcmp(mime_type, "image/jpeg") == 0) - return getDimensionsJpeg(data, width, height); - - return false; -} - -static int roundPow2(int value) -{ - int result = 1; - - while (result < value) - result <<= 1; - - // to prevent odd texture sizes from increasing the size too much, we round to nearest power of 2 above a certain size - if (value > 128 && result * 3 / 4 > value) - result >>= 1; - - return result; -} - -static int roundBlock(int value, bool pow2) -{ - if (value == 0) - return 4; - - if (pow2 && value > 4) - return roundPow2(value); - - return (value + 3) & ~3; -} - -void adjustDimensions(int& width, int& height, const Settings& settings) -{ - width = int(width * settings.texture_scale); - height = int(height * settings.texture_scale); - - if (settings.texture_limit && (width > settings.texture_limit || height > settings.texture_limit)) - { - float limit_scale = float(settings.texture_limit) / float(width > height ? width : height); - - width = int(width * limit_scale); - height = int(height * limit_scale); - } - - width = roundBlock(width, settings.texture_pow2); - height = roundBlock(height, settings.texture_pow2); -} - -const char* mimeExtension(const char* mime_type) -{ - for (size_t i = 0; i < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]); ++i) - if (strcmp(kMimeTypes[i][0], mime_type) == 0) - return kMimeTypes[i][1]; - - return ".raw"; -} diff --git a/Dependencies/meshoptimizer/gltf/json.cpp b/Dependencies/meshoptimizer/gltf/json.cpp deleted file mode 100644 index fa06adf..0000000 --- a/Dependencies/meshoptimizer/gltf/json.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// This file is part of gltfpack; see gltfpack.h for version/license details -#include "gltfpack.h" - -#include - -void comma(std::string& s) -{ - char ch = s.empty() ? 0 : s[s.size() - 1]; - - if (ch != 0 && ch != '[' && ch != '{') - s += ","; -} - -void append(std::string& s, size_t v) -{ - char buf[32]; - sprintf(buf, "%zu", v); - s += buf; -} - -void append(std::string& s, float v) -{ - char buf[512]; - sprintf(buf, "%.9g", v); - s += buf; -} - -void append(std::string& s, const char* v) -{ - s += v; -} - -void append(std::string& s, const std::string& v) -{ - s += v; -} - -void appendJson(std::string& s, const char* begin, const char* end) -{ - enum State - { - None, - Escape, - Quoted - } state = None; - - for (const char* it = begin; it != end; ++it) - { - char ch = *it; - - // whitespace outside of quoted strings can be ignored - if (state != None || !isspace(ch)) - s += ch; - - // the finite automata tracks whether we're inside a quoted string - switch (state) - { - case None: - state = (ch == '"') ? Quoted : None; - break; - - case Quoted: - state = (ch == '"') ? None : (ch == '\\') ? Escape : Quoted; - break; - - case Escape: - state = Quoted; - break; - - default: - assert(!"Unexpected parsing state"); - } - } -} diff --git a/Dependencies/meshoptimizer/gltf/library.js b/Dependencies/meshoptimizer/gltf/library.js deleted file mode 100644 index ec5b641..0000000 --- a/Dependencies/meshoptimizer/gltf/library.js +++ /dev/null @@ -1,374 +0,0 @@ -// This file is part of gltfpack and is distributed under the terms of MIT License. - -/** - * Initialize the library with the Wasm module (library.wasm) - * - * @param wasm Promise with contents of library.wasm - * - * Note: this is called automatically in node.js - */ -function init(wasm) { - if (ready) { - throw new Error("init must be called once"); - } - - ready = Promise.resolve(wasm) - .then(function (buffer) { - return WebAssembly.instantiate(buffer, { wasi_snapshot_preview1: wasi }); - }) - .then(function (result) { - instance = result.instance; - instance.exports.__wasm_call_ctors(); - }); -} - -/** - * Pack the requested glTF data using the requested command line and access interface. - * - * @param args An array of strings with the input arguments; the paths for input and output files are interpreted by the interface - * @param iface An interface to the system that will be used to service file requests and other system calls - * @return Promise that indicates completion of the operation - * - * iface should contain the following methods: - * read(path): Given a path, return a Uint8Array with the contents of that path - * write(path, data): Write the specified Uint8Array to the provided path - */ -function pack(args, iface) { - if (!ready) { - throw new Error("init must be called before pack"); - } - - var argv = args.slice(); - argv.unshift("gltfpack"); - - return ready.then(function () { - var buf = uploadArgv(argv); - - output.position = 0; - output.size = 0; - - interface = iface; - var result = instance.exports.pack(argv.length, buf); - interface = undefined; - - instance.exports.free(buf); - - var log = getString(output.data.buffer, 0, output.size); - - if (result != 0) { - throw new Error(log); - } else { - return log; - } - }); -} - -// Library implementation (here be dragons) -var WASI_EBADF = 8; -var WASI_EINVAL = 28; -var WASI_EIO = 29; -var WASI_ENOSYS = 52; - -var ready; -var instance; -var interface; - -var output = { data: new Uint8Array(), position: 0, size: 0 }; -var fds = { 1: output, 2: output, 3: { mount: "/", path: "/" }, 4: { mount: "/gltfpack-$pwd", path: "" } }; - -var wasi = { - proc_exit: function(rval) { - }, - - fd_close: function(fd) { - if (!fds[fd]) { - return WASI_EBADF; - } - - try { - if (fds[fd].close) { - fds[fd].close(); - } - fds[fd] = undefined; - return 0; - } catch (err) { - fds[fd] = undefined; - return WASI_EIO; - } - }, - - fd_fdstat_get: function(fd, stat) { - if (!fds[fd]) { - return WASI_EBADF; - } - - var heap = getHeap(); - heap.setUint8(stat + 0, fds[fd].path !== undefined ? 3 : 4); - heap.setUint16(stat + 2, 0, true); - heap.setUint32(stat + 8, 0, true); - heap.setUint32(stat + 12, 0, true); - heap.setUint32(stat + 16, 0, true); - heap.setUint32(stat + 20, 0, true); - return 0; - }, - - path_open32: function(parent_fd, dirflags, path, path_len, oflags, fs_rights_base, fs_rights_inheriting, fdflags, opened_fd) { - if (!fds[parent_fd] || fds[parent_fd].path === undefined) { - return WASI_EBADF; - } - - var heap = getHeap(); - - var file = {}; - file.name = fds[parent_fd].path + getString(heap.buffer, path, path_len); - file.position = 0; - - if (oflags & 1) { - file.data = new Uint8Array(4096); - file.size = 0; - file.close = function () { - interface.write(file.name, new Uint8Array(file.data.buffer, 0, file.size)); - }; - } else { - try { - file.data = interface.read(file.name); - - if (!file.data) { - return WASI_EIO; - } - - file.size = file.data.length; - } catch (err) { - return WASI_EIO; - } - } - - var fd = nextFd(); - fds[fd] = file; - - heap.setUint32(opened_fd, fd, true); - return 0; - }, - - path_filestat_get: function(parent_fd, flags, path, path_len, buf) { - if (!fds[parent_fd] || fds[parent_fd].path === undefined) { - return WASI_EBADF; - } - - var heap = getHeap(); - var name = getString(heap.buffer, path, path_len); - - var heap = getHeap(); - for (var i = 0; i < 64; ++i) - heap.setUint8(buf + i, 0); - - heap.setUint8(buf + 16, name == "." ? 3 : 4); - return 0; - }, - - fd_prestat_get: function(fd, buf) { - if (!fds[fd] || fds[fd].path === undefined) { - return WASI_EBADF; - } - - var path_buf = stringBuffer(fds[fd].mount); - - var heap = getHeap(); - heap.setUint8(buf, 0); - heap.setUint32(buf + 4, path_buf.length, true); - return 0; - }, - - fd_prestat_dir_name: function(fd, path, path_len) { - if (!fds[fd] || fds[fd].path === undefined) { - return WASI_EBADF; - } - - var path_buf = stringBuffer(fds[fd].mount); - - if (path_len != path_buf.length) { - return WASI_EINVAL; - } - - var heap = getHeap(); - new Uint8Array(heap.buffer).set(path_buf, path); - return 0; - }, - - path_remove_directory: function(parent_fd, path, path_len) { - return WASI_EINVAL; - }, - - fd_fdstat_set_flags: function(fd, flags) { - return WASI_ENOSYS; - }, - - fd_seek32: function(fd, offset, whence, newoffset) { - if (!fds[fd]) { - return WASI_EBADF; - } - - var newposition; - - switch (whence) { - case 0: - newposition = offset; - break; - - case 1: - newposition = fds[fd].position + offset; - break; - - case 2: - newposition = fds[fd].size; - break; - - default: - return WASI_EINVAL; - } - - if (newposition > fds[fd].size) { - return WASI_EINVAL; - } - - fds[fd].position = newposition; - - var heap = getHeap(); - heap.setUint32(newoffset, newposition, true); - return 0; - }, - - fd_read: function(fd, iovs, iovs_len, nread) { - if (!fds[fd]) { - return WASI_EBADF; - } - - var heap = getHeap(); - var read = 0; - - for (var i = 0; i < iovs_len; ++i) { - var buf = heap.getUint32(iovs + 8 * i + 0, true); - var buf_len = heap.getUint32(iovs + 8 * i + 4, true); - - var readi = Math.min(fds[fd].size - fds[fd].position, buf_len); - - new Uint8Array(heap.buffer).set(fds[fd].data.subarray(fds[fd].position, fds[fd].position + readi), buf); - - fds[fd].position += readi; - read += readi; - } - - heap.setUint32(nread, read, true); - return 0; - }, - - fd_write: function(fd, iovs, iovs_len, nwritten) { - if (!fds[fd]) { - return WASI_EBADF; - } - - var heap = getHeap(); - var written = 0; - - for (var i = 0; i < iovs_len; ++i) { - var buf = heap.getUint32(iovs + 8 * i + 0, true); - var buf_len = heap.getUint32(iovs + 8 * i + 4, true); - - if (fds[fd].position + buf_len > fds[fd].data.length) { - fds[fd].data = growArray(fds[fd].data, fds[fd].position + buf_len); - } - - fds[fd].data.set(new Uint8Array(heap.buffer, buf, buf_len), fds[fd].position); - fds[fd].position += buf_len; - fds[fd].size = Math.max(fds[fd].position, fds[fd].size); - - written += buf_len; - } - - heap.setUint32(nwritten, written, true); - return 0; - }, -}; - -function nextFd() { - for (var i = 1; ; ++i) { - if (fds[i] === undefined) { - return i; - } - } -} - -function getHeap() { - return new DataView(instance.exports.memory.buffer); -} - -function getString(buffer, offset, length) { - return new TextDecoder().decode(new Uint8Array(buffer, offset, length)); -} - -function stringBuffer(string) { - return new TextEncoder().encode(string); -} - -function growArray(data, len) { - var new_length = Math.max(1, data.length); - while (new_length < len) { - new_length *= 2; - } - - var new_data = new Uint8Array(new_length); - new_data.set(data); - - return new_data; -} - -function uploadArgv(argv) { - var buf_size = argv.length * 4; - for (var i = 0; i < argv.length; ++i) { - buf_size += stringBuffer(argv[i]).length + 1; - } - - var buf = instance.exports.malloc(buf_size); - var argp = buf + argv.length * 4; - - var heap = getHeap(); - - for (var i = 0; i < argv.length; ++i) { - var item = stringBuffer(argv[i]); - - heap.setUint32(buf + i * 4, argp, true); - new Uint8Array(heap.buffer).set(item, argp); - heap.setUint8(argp + item.length, 0); - - argp += item.length + 1; - } - - return buf; -} - -// Automatic initialization for node.js -if (typeof window === 'undefined' && typeof process !== 'undefined' && process.release.name === 'node') { - var fs = require('fs'); - var util = require('util'); - - // Node versions before v12 don't support TextEncoder/TextDecoder natively, but util. provides compatible replacements - if (typeof TextEncoder === 'undefined' && typeof TextDecoder === 'undefined') { - TextEncoder = util.TextEncoder; - TextDecoder = util.TextDecoder; - } - - init(fs.readFileSync(__dirname + '/library.wasm')); -} - -// UMD -(function (root, factory) { - if (typeof define === 'function' && define.amd) { - define([], factory); - } else if (typeof module === 'object' && module.exports) { - module.exports = factory(); - } else { - root.gltfpack = factory(); - } -}(typeof self !== 'undefined' ? self : this, function () { - return { init, pack }; -})); diff --git a/Dependencies/meshoptimizer/gltf/material.cpp b/Dependencies/meshoptimizer/gltf/material.cpp deleted file mode 100644 index 68b952c..0000000 --- a/Dependencies/meshoptimizer/gltf/material.cpp +++ /dev/null @@ -1,530 +0,0 @@ -// This file is part of gltfpack; see gltfpack.h for version/license details -#include "gltfpack.h" - -#include - -static bool areTextureViewsEqual(const cgltf_texture_view& lhs, const cgltf_texture_view& rhs) -{ - if (lhs.has_transform != rhs.has_transform) - return false; - - if (lhs.has_transform) - { - const cgltf_texture_transform& lt = lhs.transform; - const cgltf_texture_transform& rt = rhs.transform; - - if (memcmp(lt.offset, rt.offset, sizeof(cgltf_float) * 2) != 0) - return false; - - if (lt.rotation != rt.rotation) - return false; - - if (memcmp(lt.scale, rt.scale, sizeof(cgltf_float) * 2) != 0) - return false; - - if (lt.texcoord != rt.texcoord) - return false; - } - - if (lhs.texture != rhs.texture) - return false; - - if (lhs.texcoord != rhs.texcoord) - return false; - - if (lhs.scale != rhs.scale) - return false; - - return true; -} - -static bool areExtrasEqual(cgltf_data* data, const cgltf_extras& lhs, const cgltf_extras& rhs) -{ - if (lhs.end_offset - lhs.start_offset != rhs.end_offset - rhs.start_offset) - return false; - - if (memcmp(data->json + lhs.start_offset, data->json + rhs.start_offset, lhs.end_offset - lhs.start_offset) != 0) - return false; - - return true; -} - -static bool areMaterialComponentsEqual(const cgltf_pbr_metallic_roughness& lhs, const cgltf_pbr_metallic_roughness& rhs) -{ - if (!areTextureViewsEqual(lhs.base_color_texture, rhs.base_color_texture)) - return false; - - if (!areTextureViewsEqual(lhs.metallic_roughness_texture, rhs.metallic_roughness_texture)) - return false; - - if (memcmp(lhs.base_color_factor, rhs.base_color_factor, sizeof(cgltf_float) * 4) != 0) - return false; - - if (lhs.metallic_factor != rhs.metallic_factor) - return false; - - if (lhs.roughness_factor != rhs.roughness_factor) - return false; - - return true; -} - -static bool areMaterialComponentsEqual(const cgltf_pbr_specular_glossiness& lhs, const cgltf_pbr_specular_glossiness& rhs) -{ - if (!areTextureViewsEqual(lhs.diffuse_texture, rhs.diffuse_texture)) - return false; - - if (!areTextureViewsEqual(lhs.specular_glossiness_texture, rhs.specular_glossiness_texture)) - return false; - - if (memcmp(lhs.diffuse_factor, rhs.diffuse_factor, sizeof(cgltf_float) * 4) != 0) - return false; - - if (memcmp(lhs.specular_factor, rhs.specular_factor, sizeof(cgltf_float) * 3) != 0) - return false; - - if (lhs.glossiness_factor != rhs.glossiness_factor) - return false; - - return true; -} - -static bool areMaterialComponentsEqual(const cgltf_clearcoat& lhs, const cgltf_clearcoat& rhs) -{ - if (!areTextureViewsEqual(lhs.clearcoat_texture, rhs.clearcoat_texture)) - return false; - - if (!areTextureViewsEqual(lhs.clearcoat_roughness_texture, rhs.clearcoat_roughness_texture)) - return false; - - if (!areTextureViewsEqual(lhs.clearcoat_normal_texture, rhs.clearcoat_normal_texture)) - return false; - - if (lhs.clearcoat_factor != rhs.clearcoat_factor) - return false; - - if (lhs.clearcoat_roughness_factor != rhs.clearcoat_roughness_factor) - return false; - - return true; -} - -static bool areMaterialComponentsEqual(const cgltf_transmission& lhs, const cgltf_transmission& rhs) -{ - if (!areTextureViewsEqual(lhs.transmission_texture, rhs.transmission_texture)) - return false; - - if (lhs.transmission_factor != rhs.transmission_factor) - return false; - - return true; -} - -static bool areMaterialComponentsEqual(const cgltf_ior& lhs, const cgltf_ior& rhs) -{ - if (lhs.ior != rhs.ior) - return false; - - return true; -} - -static bool areMaterialComponentsEqual(const cgltf_specular& lhs, const cgltf_specular& rhs) -{ - if (!areTextureViewsEqual(lhs.specular_texture, rhs.specular_texture)) - return false; - - if (!areTextureViewsEqual(lhs.specular_color_texture, rhs.specular_color_texture)) - return false; - - if (lhs.specular_factor != rhs.specular_factor) - return false; - - if (memcmp(lhs.specular_color_factor, rhs.specular_color_factor, sizeof(cgltf_float) * 3) != 0) - return false; - - return true; -} - -static bool areMaterialComponentsEqual(const cgltf_sheen& lhs, const cgltf_sheen& rhs) -{ - if (!areTextureViewsEqual(lhs.sheen_color_texture, rhs.sheen_color_texture)) - return false; - - if (memcmp(lhs.sheen_color_factor, rhs.sheen_color_factor, sizeof(cgltf_float) * 3) != 0) - return false; - - if (!areTextureViewsEqual(lhs.sheen_roughness_texture, rhs.sheen_roughness_texture)) - return false; - - if (lhs.sheen_roughness_factor != rhs.sheen_roughness_factor) - return false; - - return true; -} - -static bool areMaterialComponentsEqual(const cgltf_volume& lhs, const cgltf_volume& rhs) -{ - if (!areTextureViewsEqual(lhs.thickness_texture, rhs.thickness_texture)) - return false; - - if (lhs.thickness_factor != rhs.thickness_factor) - return false; - - if (memcmp(lhs.attenuation_color, rhs.attenuation_color, sizeof(cgltf_float) * 3) != 0) - return false; - - if (lhs.attenuation_distance != rhs.attenuation_distance) - return false; - - return true; -} - -static bool areMaterialComponentsEqual(const cgltf_emissive_strength& lhs, const cgltf_emissive_strength& rhs) -{ - if (lhs.emissive_strength != rhs.emissive_strength) - return false; - - return true; -} - -static bool areMaterialComponentsEqual(const cgltf_iridescence& lhs, const cgltf_iridescence& rhs) -{ - if (lhs.iridescence_factor != rhs.iridescence_factor) - return false; - - if (!areTextureViewsEqual(lhs.iridescence_texture, rhs.iridescence_texture)) - return false; - - if (lhs.iridescence_ior != rhs.iridescence_ior) - return false; - - if (lhs.iridescence_thickness_min != rhs.iridescence_thickness_min) - return false; - - if (lhs.iridescence_thickness_max != rhs.iridescence_thickness_max) - return false; - - if (!areTextureViewsEqual(lhs.iridescence_thickness_texture, rhs.iridescence_thickness_texture)) - return false; - - return true; -} - -static bool areMaterialsEqual(cgltf_data* data, const cgltf_material& lhs, const cgltf_material& rhs, const Settings& settings) -{ - if (lhs.has_pbr_metallic_roughness != rhs.has_pbr_metallic_roughness) - return false; - - if (lhs.has_pbr_metallic_roughness && !areMaterialComponentsEqual(lhs.pbr_metallic_roughness, rhs.pbr_metallic_roughness)) - return false; - - if (lhs.has_pbr_specular_glossiness != rhs.has_pbr_specular_glossiness) - return false; - - if (lhs.has_pbr_specular_glossiness && !areMaterialComponentsEqual(lhs.pbr_specular_glossiness, rhs.pbr_specular_glossiness)) - return false; - - if (lhs.has_clearcoat != rhs.has_clearcoat) - return false; - - if (lhs.has_clearcoat && !areMaterialComponentsEqual(lhs.clearcoat, rhs.clearcoat)) - return false; - - if (lhs.has_transmission != rhs.has_transmission) - return false; - - if (lhs.has_transmission && !areMaterialComponentsEqual(lhs.transmission, rhs.transmission)) - return false; - - if (lhs.has_ior != rhs.has_ior) - return false; - - if (lhs.has_ior && !areMaterialComponentsEqual(lhs.ior, rhs.ior)) - return false; - - if (lhs.has_specular != rhs.has_specular) - return false; - - if (lhs.has_specular && !areMaterialComponentsEqual(lhs.specular, rhs.specular)) - return false; - - if (lhs.has_sheen != rhs.has_sheen) - return false; - - if (lhs.has_sheen && !areMaterialComponentsEqual(lhs.sheen, rhs.sheen)) - return false; - - if (lhs.has_volume != rhs.has_volume) - return false; - - if (lhs.has_volume && !areMaterialComponentsEqual(lhs.volume, rhs.volume)) - return false; - - if (lhs.has_emissive_strength != rhs.has_emissive_strength) - return false; - - if (lhs.has_emissive_strength && !areMaterialComponentsEqual(lhs.emissive_strength, rhs.emissive_strength)) - return false; - - if (lhs.has_iridescence != rhs.has_iridescence) - return false; - - if (lhs.has_iridescence && !areMaterialComponentsEqual(lhs.iridescence, rhs.iridescence)) - return false; - - if (!areTextureViewsEqual(lhs.normal_texture, rhs.normal_texture)) - return false; - - if (!areTextureViewsEqual(lhs.occlusion_texture, rhs.occlusion_texture)) - return false; - - if (!areTextureViewsEqual(lhs.emissive_texture, rhs.emissive_texture)) - return false; - - if (memcmp(lhs.emissive_factor, rhs.emissive_factor, sizeof(cgltf_float) * 3) != 0) - return false; - - if (lhs.alpha_mode != rhs.alpha_mode) - return false; - - if (lhs.alpha_cutoff != rhs.alpha_cutoff) - return false; - - if (lhs.double_sided != rhs.double_sided) - return false; - - if (lhs.unlit != rhs.unlit) - return false; - - if (settings.keep_extras && !areExtrasEqual(data, lhs.extras, rhs.extras)) - return false; - - return true; -} - -void mergeMeshMaterials(cgltf_data* data, std::vector& meshes, const Settings& settings) -{ - std::vector material_remap(data->materials_count); - - for (size_t i = 0; i < data->materials_count; ++i) - { - material_remap[i] = &data->materials[i]; - - if (settings.keep_materials && data->materials[i].name && *data->materials[i].name) - continue; - - for (size_t j = 0; j < i; ++j) - { - if (settings.keep_materials && data->materials[j].name && *data->materials[j].name) - continue; - - if (areMaterialsEqual(data, data->materials[i], data->materials[j], settings)) - { - material_remap[i] = &data->materials[j]; - break; - } - } - } - - for (size_t i = 0; i < meshes.size(); ++i) - { - Mesh& mesh = meshes[i]; - - if (mesh.material) - mesh.material = material_remap[mesh.material - data->materials]; - - for (size_t j = 0; j < mesh.variants.size(); ++j) - mesh.variants[j].material = material_remap[mesh.variants[j].material - data->materials]; - } -} - -void markNeededMaterials(cgltf_data* data, std::vector& materials, const std::vector& meshes, const Settings& settings) -{ - // mark all used materials as kept - for (size_t i = 0; i < meshes.size(); ++i) - { - const Mesh& mesh = meshes[i]; - - if (mesh.material) - { - MaterialInfo& mi = materials[mesh.material - data->materials]; - - mi.keep = true; - } - - for (size_t j = 0; j < mesh.variants.size(); ++j) - { - MaterialInfo& mi = materials[mesh.variants[j].material - data->materials]; - - mi.keep = true; - } - } - - // mark all named materials as kept if requested - if (settings.keep_materials) - { - for (size_t i = 0; i < data->materials_count; ++i) - { - cgltf_material& material = data->materials[i]; - - if (material.name && *material.name) - { - materials[i].keep = true; - } - } - } -} - -bool hasValidTransform(const cgltf_texture_view& view) -{ - if (view.has_transform) - { - if (view.transform.offset[0] != 0.0f || view.transform.offset[1] != 0.0f || - view.transform.scale[0] != 1.0f || view.transform.scale[1] != 1.0f || - view.transform.rotation != 0.0f) - return true; - - if (view.transform.has_texcoord && view.transform.texcoord != view.texcoord) - return true; - } - - return false; -} - -static void analyzeMaterialTexture(const cgltf_texture_view& view, TextureKind kind, MaterialInfo& mi, cgltf_data* data, std::vector& images) -{ - mi.usesTextureTransform |= hasValidTransform(view); - - if (view.texture && view.texture->image) - { - ImageInfo& info = images[view.texture->image - data->images]; - - mi.textureSetMask |= 1u << view.texcoord; - mi.needsTangents |= (kind == TextureKind_Normal); - - if (info.kind == TextureKind_Generic) - info.kind = kind; - else if (info.kind > kind) // this is useful to keep color textures that have attrib data in alpha tagged as color - info.kind = kind; - - info.normal_map |= (kind == TextureKind_Normal); - info.srgb |= (kind == TextureKind_Color); - } -} - -static void analyzeMaterial(const cgltf_material& material, MaterialInfo& mi, cgltf_data* data, std::vector& images) -{ - if (material.has_pbr_metallic_roughness) - { - analyzeMaterialTexture(material.pbr_metallic_roughness.base_color_texture, TextureKind_Color, mi, data, images); - analyzeMaterialTexture(material.pbr_metallic_roughness.metallic_roughness_texture, TextureKind_Attrib, mi, data, images); - } - - if (material.has_pbr_specular_glossiness) - { - analyzeMaterialTexture(material.pbr_specular_glossiness.diffuse_texture, TextureKind_Color, mi, data, images); - analyzeMaterialTexture(material.pbr_specular_glossiness.specular_glossiness_texture, TextureKind_Attrib, mi, data, images); - } - - if (material.has_clearcoat) - { - analyzeMaterialTexture(material.clearcoat.clearcoat_texture, TextureKind_Attrib, mi, data, images); - analyzeMaterialTexture(material.clearcoat.clearcoat_roughness_texture, TextureKind_Attrib, mi, data, images); - analyzeMaterialTexture(material.clearcoat.clearcoat_normal_texture, TextureKind_Normal, mi, data, images); - } - - if (material.has_transmission) - { - analyzeMaterialTexture(material.transmission.transmission_texture, TextureKind_Attrib, mi, data, images); - } - - if (material.has_specular) - { - analyzeMaterialTexture(material.specular.specular_texture, TextureKind_Attrib, mi, data, images); - analyzeMaterialTexture(material.specular.specular_color_texture, TextureKind_Color, mi, data, images); - } - - if (material.has_sheen) - { - analyzeMaterialTexture(material.sheen.sheen_color_texture, TextureKind_Color, mi, data, images); - analyzeMaterialTexture(material.sheen.sheen_roughness_texture, TextureKind_Attrib, mi, data, images); - } - - if (material.has_volume) - { - analyzeMaterialTexture(material.volume.thickness_texture, TextureKind_Attrib, mi, data, images); - } - - if (material.has_iridescence) - { - analyzeMaterialTexture(material.iridescence.iridescence_texture, TextureKind_Attrib, mi, data, images); - analyzeMaterialTexture(material.iridescence.iridescence_thickness_texture, TextureKind_Attrib, mi, data, images); - } - - analyzeMaterialTexture(material.normal_texture, TextureKind_Normal, mi, data, images); - analyzeMaterialTexture(material.occlusion_texture, TextureKind_Attrib, mi, data, images); - analyzeMaterialTexture(material.emissive_texture, TextureKind_Color, mi, data, images); -} - -void analyzeMaterials(cgltf_data* data, std::vector& materials, std::vector& images) -{ - for (size_t i = 0; i < data->materials_count; ++i) - { - analyzeMaterial(data->materials[i], materials[i], data, images); - } -} - -static const cgltf_texture_view* getColorTexture(const cgltf_material& material) -{ - if (material.has_pbr_metallic_roughness) - return &material.pbr_metallic_roughness.base_color_texture; - - if (material.has_pbr_specular_glossiness) - return &material.pbr_specular_glossiness.diffuse_texture; - - return NULL; -} - -static float getAlphaFactor(const cgltf_material& material) -{ - if (material.has_pbr_metallic_roughness) - return material.pbr_metallic_roughness.base_color_factor[3]; - - if (material.has_pbr_specular_glossiness) - return material.pbr_specular_glossiness.diffuse_factor[3]; - - return 1.f; -} - -static int getChannels(const cgltf_image& image, ImageInfo& info, const char* input_path) -{ - if (info.channels) - return info.channels; - - std::string img_data; - std::string mime_type; - if (readImage(image, input_path, img_data, mime_type)) - info.channels = hasAlpha(img_data, mime_type.c_str()) ? 4 : 3; - else - info.channels = -1; - - return info.channels; -} - -void optimizeMaterials(cgltf_data* data, const char* input_path, std::vector& images) -{ - for (size_t i = 0; i < data->materials_count; ++i) - { - // remove BLEND/MASK from materials that don't have alpha information - if (data->materials[i].alpha_mode != cgltf_alpha_mode_opaque) - { - const cgltf_texture_view* color = getColorTexture(data->materials[i]); - float alpha = getAlphaFactor(data->materials[i]); - - if (alpha == 1.f && !(color && color->texture && color->texture->image && getChannels(*color->texture->image, images[color->texture->image - data->images], input_path) == 4)) - { - data->materials[i].alpha_mode = cgltf_alpha_mode_opaque; - } - } - } -} diff --git a/Dependencies/meshoptimizer/gltf/mesh.cpp b/Dependencies/meshoptimizer/gltf/mesh.cpp deleted file mode 100644 index 9278f2b..0000000 --- a/Dependencies/meshoptimizer/gltf/mesh.cpp +++ /dev/null @@ -1,996 +0,0 @@ -// This file is part of gltfpack; see gltfpack.h for version/license details -#include "gltfpack.h" - -#include - -#include -#include - -#include "../src/meshoptimizer.h" - -static float inverseTranspose(float* result, const float* transform) -{ - float m[4][4] = {}; - memcpy(m, transform, 16 * sizeof(float)); - - float det = - m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2]) - - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0]) + - m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]); - - float invdet = (det == 0.f) ? 0.f : 1.f / det; - - float r[4][4] = {}; - - r[0][0] = (m[1][1] * m[2][2] - m[2][1] * m[1][2]) * invdet; - r[1][0] = (m[0][2] * m[2][1] - m[0][1] * m[2][2]) * invdet; - r[2][0] = (m[0][1] * m[1][2] - m[0][2] * m[1][1]) * invdet; - r[0][1] = (m[1][2] * m[2][0] - m[1][0] * m[2][2]) * invdet; - r[1][1] = (m[0][0] * m[2][2] - m[0][2] * m[2][0]) * invdet; - r[2][1] = (m[1][0] * m[0][2] - m[0][0] * m[1][2]) * invdet; - r[0][2] = (m[1][0] * m[2][1] - m[2][0] * m[1][1]) * invdet; - r[1][2] = (m[2][0] * m[0][1] - m[0][0] * m[2][1]) * invdet; - r[2][2] = (m[0][0] * m[1][1] - m[1][0] * m[0][1]) * invdet; - - r[3][3] = 1.f; - - memcpy(result, r, 16 * sizeof(float)); - - return det; -} - -static void transformPosition(float* res, const float* ptr, const float* transform) -{ - float x = ptr[0] * transform[0] + ptr[1] * transform[4] + ptr[2] * transform[8] + transform[12]; - float y = ptr[0] * transform[1] + ptr[1] * transform[5] + ptr[2] * transform[9] + transform[13]; - float z = ptr[0] * transform[2] + ptr[1] * transform[6] + ptr[2] * transform[10] + transform[14]; - - res[0] = x; - res[1] = y; - res[2] = z; -} - -static void transformNormal(float* res, const float* ptr, const float* transform) -{ - float x = ptr[0] * transform[0] + ptr[1] * transform[4] + ptr[2] * transform[8]; - float y = ptr[0] * transform[1] + ptr[1] * transform[5] + ptr[2] * transform[9]; - float z = ptr[0] * transform[2] + ptr[1] * transform[6] + ptr[2] * transform[10]; - - float l = sqrtf(x * x + y * y + z * z); - float s = (l == 0.f) ? 0.f : 1 / l; - - res[0] = x * s; - res[1] = y * s; - res[2] = z * s; -} - -// assumes mesh & target are structurally identical -static void transformMesh(Mesh& target, const Mesh& mesh, const cgltf_node* node) -{ - assert(target.streams.size() == mesh.streams.size()); - assert(target.indices.size() == mesh.indices.size()); - - float transform[16]; - cgltf_node_transform_world(node, transform); - - float transforminvt[16]; - float det = inverseTranspose(transforminvt, transform); - - for (size_t si = 0; si < mesh.streams.size(); ++si) - { - const Stream& source = mesh.streams[si]; - Stream& stream = target.streams[si]; - - assert(source.type == stream.type); - assert(source.data.size() == stream.data.size()); - - if (stream.type == cgltf_attribute_type_position) - { - for (size_t i = 0; i < stream.data.size(); ++i) - transformPosition(stream.data[i].f, source.data[i].f, transform); - } - else if (stream.type == cgltf_attribute_type_normal) - { - for (size_t i = 0; i < stream.data.size(); ++i) - transformNormal(stream.data[i].f, source.data[i].f, transforminvt); - } - else if (stream.type == cgltf_attribute_type_tangent) - { - for (size_t i = 0; i < stream.data.size(); ++i) - transformNormal(stream.data[i].f, source.data[i].f, transform); - } - } - - // copy indices so that we can modify them below - target.indices = mesh.indices; - - if (det < 0 && mesh.type == cgltf_primitive_type_triangles) - { - // negative scale means we need to flip face winding - for (size_t i = 0; i < target.indices.size(); i += 3) - std::swap(target.indices[i + 0], target.indices[i + 1]); - } -} - -bool compareMeshTargets(const Mesh& lhs, const Mesh& rhs) -{ - if (lhs.targets != rhs.targets) - return false; - - if (lhs.target_weights.size() != rhs.target_weights.size()) - return false; - - for (size_t i = 0; i < lhs.target_weights.size(); ++i) - if (lhs.target_weights[i] != rhs.target_weights[i]) - return false; - - if (lhs.target_names.size() != rhs.target_names.size()) - return false; - - for (size_t i = 0; i < lhs.target_names.size(); ++i) - if (strcmp(lhs.target_names[i], rhs.target_names[i]) != 0) - return false; - - return true; -} - -bool compareMeshVariants(const Mesh& lhs, const Mesh& rhs) -{ - if (lhs.variants.size() != rhs.variants.size()) - return false; - - for (size_t i = 0; i < lhs.variants.size(); ++i) - { - if (lhs.variants[i].variant != rhs.variants[i].variant) - return false; - - if (lhs.variants[i].material != rhs.variants[i].material) - return false; - } - - return true; -} - -bool compareMeshNodes(const Mesh& lhs, const Mesh& rhs) -{ - if (lhs.nodes.size() != rhs.nodes.size()) - return false; - - for (size_t i = 0; i < lhs.nodes.size(); ++i) - if (lhs.nodes[i] != rhs.nodes[i]) - return false; - - return true; -} - -static bool canMergeMeshNodes(cgltf_node* lhs, cgltf_node* rhs, const Settings& settings) -{ - if (lhs == rhs) - return true; - - if (lhs->parent != rhs->parent) - return false; - - bool lhs_transform = lhs->has_translation | lhs->has_rotation | lhs->has_scale | lhs->has_matrix | (!!lhs->weights); - bool rhs_transform = rhs->has_translation | rhs->has_rotation | rhs->has_scale | rhs->has_matrix | (!!rhs->weights); - - if (lhs_transform || rhs_transform) - return false; - - if (settings.keep_nodes) - { - if (lhs->name && *lhs->name) - return false; - - if (rhs->name && *rhs->name) - return false; - } - - // we can merge nodes that don't have transforms of their own and have the same parent - // this is helpful when instead of splitting mesh into primitives, DCCs split mesh into mesh nodes - return true; -} - -static bool canMergeMeshes(const Mesh& lhs, const Mesh& rhs, const Settings& settings) -{ - if (lhs.scene != rhs.scene) - return false; - - if (lhs.nodes.size() != rhs.nodes.size()) - return false; - - for (size_t i = 0; i < lhs.nodes.size(); ++i) - if (!canMergeMeshNodes(lhs.nodes[i], rhs.nodes[i], settings)) - return false; - - if (lhs.instances.size() || rhs.instances.size()) - return false; - - if (lhs.material != rhs.material) - return false; - - if (lhs.skin != rhs.skin) - return false; - - if (lhs.type != rhs.type) - return false; - - if (!compareMeshTargets(lhs, rhs)) - return false; - - if (!compareMeshVariants(lhs, rhs)) - return false; - - if (lhs.indices.empty() != rhs.indices.empty()) - return false; - - if (lhs.streams.size() != rhs.streams.size()) - return false; - - for (size_t i = 0; i < lhs.streams.size(); ++i) - if (lhs.streams[i].type != rhs.streams[i].type || lhs.streams[i].index != rhs.streams[i].index || lhs.streams[i].target != rhs.streams[i].target) - return false; - - return true; -} - -static void mergeMeshes(Mesh& target, const Mesh& mesh) -{ - assert(target.streams.size() == mesh.streams.size()); - - size_t vertex_offset = target.streams[0].data.size(); - size_t index_offset = target.indices.size(); - - for (size_t i = 0; i < target.streams.size(); ++i) - target.streams[i].data.insert(target.streams[i].data.end(), mesh.streams[i].data.begin(), mesh.streams[i].data.end()); - - target.indices.resize(target.indices.size() + mesh.indices.size()); - - size_t index_count = mesh.indices.size(); - - for (size_t i = 0; i < index_count; ++i) - target.indices[index_offset + i] = unsigned(vertex_offset + mesh.indices[i]); -} - -void mergeMeshInstances(Mesh& mesh) -{ - if (mesh.nodes.empty()) - return; - - // fast-path: for single instance meshes we transform in-place - if (mesh.nodes.size() == 1) - { - transformMesh(mesh, mesh, mesh.nodes[0]); - mesh.nodes.clear(); - return; - } - - Mesh base = mesh; - Mesh transformed = base; - - for (size_t i = 0; i < mesh.streams.size(); ++i) - { - mesh.streams[i].data.clear(); - mesh.streams[i].data.reserve(base.streams[i].data.size() * mesh.nodes.size()); - } - - mesh.indices.clear(); - mesh.indices.reserve(base.indices.size() * mesh.nodes.size()); - - for (size_t i = 0; i < mesh.nodes.size(); ++i) - { - transformMesh(transformed, base, mesh.nodes[i]); - mergeMeshes(mesh, transformed); - } - - mesh.nodes.clear(); -} - -void mergeMeshes(std::vector& meshes, const Settings& settings) -{ - for (size_t i = 0; i < meshes.size(); ++i) - { - Mesh& target = meshes[i]; - - if (target.streams.empty()) - continue; - - size_t target_vertices = target.streams[0].data.size(); - size_t target_indices = target.indices.size(); - - size_t last_merged = i; - - for (size_t j = i + 1; j < meshes.size(); ++j) - { - Mesh& mesh = meshes[j]; - - if (!mesh.streams.empty() && canMergeMeshes(target, mesh, settings)) - { - target_vertices += mesh.streams[0].data.size(); - target_indices += mesh.indices.size(); - last_merged = j; - } - } - - for (size_t j = 0; j < target.streams.size(); ++j) - target.streams[j].data.reserve(target_vertices); - - target.indices.reserve(target_indices); - - for (size_t j = i + 1; j <= last_merged; ++j) - { - Mesh& mesh = meshes[j]; - - if (!mesh.streams.empty() && canMergeMeshes(target, mesh, settings)) - { - mergeMeshes(target, mesh); - - mesh.streams.clear(); - mesh.indices.clear(); - mesh.nodes.clear(); - mesh.instances.clear(); - } - } - - assert(target.streams[0].data.size() == target_vertices); - assert(target.indices.size() == target_indices); - } -} - -void filterEmptyMeshes(std::vector& meshes) -{ - size_t write = 0; - - for (size_t i = 0; i < meshes.size(); ++i) - { - Mesh& mesh = meshes[i]; - - if (mesh.streams.empty()) - continue; - - if (mesh.streams[0].data.empty()) - continue; - - if (mesh.type != cgltf_primitive_type_points && mesh.indices.empty()) - continue; - - // the following code is roughly equivalent to meshes[write] = std::move(mesh) - std::vector streams; - streams.swap(mesh.streams); - - std::vector indices; - indices.swap(mesh.indices); - - meshes[write] = mesh; - meshes[write].streams.swap(streams); - meshes[write].indices.swap(indices); - - write++; - } - - meshes.resize(write); -} - -static bool hasColors(const std::vector& data) -{ - const float threshold = 0.99f; - - for (size_t i = 0; i < data.size(); ++i) - { - const Attr& a = data[i]; - - if (a.f[0] < threshold || a.f[1] < threshold || a.f[2] < threshold || a.f[3] < threshold) - return true; - } - - return false; -} - -static bool hasDeltas(const std::vector& data) -{ - const float threshold = 0.01f; - - for (size_t i = 0; i < data.size(); ++i) - { - const Attr& a = data[i]; - - if (fabsf(a.f[0]) > threshold || fabsf(a.f[1]) > threshold || fabsf(a.f[2]) > threshold) - return true; - } - - return false; -} - -void filterStreams(Mesh& mesh, const MaterialInfo& mi) -{ - bool morph_normal = false; - bool morph_tangent = false; - int keep_texture_set = -1; - - for (size_t i = 0; i < mesh.streams.size(); ++i) - { - Stream& stream = mesh.streams[i]; - - if (stream.target) - { - morph_normal = morph_normal || (stream.type == cgltf_attribute_type_normal && hasDeltas(stream.data)); - morph_tangent = morph_tangent || (stream.type == cgltf_attribute_type_tangent && hasDeltas(stream.data)); - } - - if (stream.type == cgltf_attribute_type_texcoord && (mi.textureSetMask & (1u << stream.index)) != 0) - { - keep_texture_set = std::max(keep_texture_set, stream.index); - } - } - - size_t write = 0; - - for (size_t i = 0; i < mesh.streams.size(); ++i) - { - Stream& stream = mesh.streams[i]; - - if (stream.type == cgltf_attribute_type_texcoord && stream.index > keep_texture_set) - continue; - - if (stream.type == cgltf_attribute_type_tangent && !mi.needsTangents) - continue; - - if ((stream.type == cgltf_attribute_type_joints || stream.type == cgltf_attribute_type_weights) && !mesh.skin) - continue; - - if (stream.type == cgltf_attribute_type_color && !hasColors(stream.data)) - continue; - - if (stream.target && stream.type == cgltf_attribute_type_normal && !morph_normal) - continue; - - if (stream.target && stream.type == cgltf_attribute_type_tangent && !morph_tangent) - continue; - - // the following code is roughly equivalent to streams[write] = std::move(stream) - std::vector data; - data.swap(stream.data); - - mesh.streams[write] = stream; - mesh.streams[write].data.swap(data); - - write++; - } - - mesh.streams.resize(write); -} - -static void reindexMesh(Mesh& mesh) -{ - size_t total_vertices = mesh.streams[0].data.size(); - size_t total_indices = mesh.indices.size(); - - std::vector streams; - for (size_t i = 0; i < mesh.streams.size(); ++i) - { - if (mesh.streams[i].target) - continue; - - assert(mesh.streams[i].data.size() == total_vertices); - - meshopt_Stream stream = {&mesh.streams[i].data[0], sizeof(Attr), sizeof(Attr)}; - streams.push_back(stream); - } - - if (streams.empty()) - return; - - std::vector remap(total_vertices); - size_t unique_vertices = meshopt_generateVertexRemapMulti(&remap[0], &mesh.indices[0], total_indices, total_vertices, &streams[0], streams.size()); - assert(unique_vertices <= total_vertices); - - meshopt_remapIndexBuffer(&mesh.indices[0], &mesh.indices[0], total_indices, &remap[0]); - - for (size_t i = 0; i < mesh.streams.size(); ++i) - { - assert(mesh.streams[i].data.size() == total_vertices); - - meshopt_remapVertexBuffer(&mesh.streams[i].data[0], &mesh.streams[i].data[0], total_vertices, sizeof(Attr), &remap[0]); - mesh.streams[i].data.resize(unique_vertices); - } -} - -static void filterTriangles(Mesh& mesh) -{ - assert(mesh.type == cgltf_primitive_type_triangles); - - unsigned int* indices = &mesh.indices[0]; - size_t total_indices = mesh.indices.size(); - - size_t write = 0; - - for (size_t i = 0; i < total_indices; i += 3) - { - unsigned int a = indices[i + 0], b = indices[i + 1], c = indices[i + 2]; - - if (a != b && a != c && b != c) - { - indices[write + 0] = a; - indices[write + 1] = b; - indices[write + 2] = c; - write += 3; - } - } - - mesh.indices.resize(write); -} - -static Stream* getStream(Mesh& mesh, cgltf_attribute_type type, int index = 0) -{ - for (size_t i = 0; i < mesh.streams.size(); ++i) - if (mesh.streams[i].type == type && mesh.streams[i].index == index) - return &mesh.streams[i]; - - return 0; -} - -static void simplifyMesh(Mesh& mesh, float threshold, bool aggressive) -{ - assert(mesh.type == cgltf_primitive_type_triangles); - - const Stream* positions = getStream(mesh, cgltf_attribute_type_position); - if (!positions) - return; - - size_t vertex_count = mesh.streams[0].data.size(); - - size_t target_index_count = size_t(double(mesh.indices.size() / 3) * threshold) * 3; - float target_error = 1e-2f; - float target_error_aggressive = 1e-1f; - - if (target_index_count < 1) - return; - - std::vector indices(mesh.indices.size()); - indices.resize(meshopt_simplify(&indices[0], &mesh.indices[0], mesh.indices.size(), positions->data[0].f, vertex_count, sizeof(Attr), target_index_count, target_error)); - mesh.indices.swap(indices); - - // Note: if the simplifier got stuck, we can try to reindex without normals/tangents and retry - // For now we simply fall back to aggressive simplifier instead - - // if the precise simplifier got "stuck", we'll try to simplify using the sloppy simplifier; this is only used when aggressive simplification is enabled as it breaks attribute discontinuities - if (aggressive && mesh.indices.size() > target_index_count) - { - indices.resize(meshopt_simplifySloppy(&indices[0], &mesh.indices[0], mesh.indices.size(), positions->data[0].f, vertex_count, sizeof(Attr), target_index_count, target_error_aggressive)); - mesh.indices.swap(indices); - } -} - -static void optimizeMesh(Mesh& mesh, bool compressmore) -{ - assert(mesh.type == cgltf_primitive_type_triangles); - - size_t vertex_count = mesh.streams[0].data.size(); - - if (compressmore) - meshopt_optimizeVertexCacheStrip(&mesh.indices[0], &mesh.indices[0], mesh.indices.size(), vertex_count); - else - meshopt_optimizeVertexCache(&mesh.indices[0], &mesh.indices[0], mesh.indices.size(), vertex_count); - - std::vector remap(vertex_count); - size_t unique_vertices = meshopt_optimizeVertexFetchRemap(&remap[0], &mesh.indices[0], mesh.indices.size(), vertex_count); - assert(unique_vertices <= vertex_count); - - meshopt_remapIndexBuffer(&mesh.indices[0], &mesh.indices[0], mesh.indices.size(), &remap[0]); - - for (size_t i = 0; i < mesh.streams.size(); ++i) - { - assert(mesh.streams[i].data.size() == vertex_count); - - meshopt_remapVertexBuffer(&mesh.streams[i].data[0], &mesh.streams[i].data[0], vertex_count, sizeof(Attr), &remap[0]); - mesh.streams[i].data.resize(unique_vertices); - } -} - -struct BoneInfluence -{ - float i; - float w; -}; - -struct BoneInfluenceWeightPredicate -{ - bool operator()(const BoneInfluence& lhs, const BoneInfluence& rhs) const - { - return lhs.w > rhs.w; - } -}; - -static void filterBones(Mesh& mesh) -{ - const int kMaxGroups = 8; - - std::pair groups[kMaxGroups]; - int group_count = 0; - - // gather all joint/weight groups; each group contains 4 bone influences - for (int i = 0; i < kMaxGroups; ++i) - { - Stream* jg = getStream(mesh, cgltf_attribute_type_joints, int(i)); - Stream* wg = getStream(mesh, cgltf_attribute_type_weights, int(i)); - - if (!jg || !wg) - break; - - groups[group_count++] = std::make_pair(jg, wg); - } - - if (group_count == 0) - return; - - // weights below cutoff can't be represented in quantized 8-bit storage - const float weight_cutoff = 0.5f / 255.f; - - size_t vertex_count = mesh.streams[0].data.size(); - - BoneInfluence inf[kMaxGroups * 4] = {}; - - for (size_t i = 0; i < vertex_count; ++i) - { - int count = 0; - - // gather all bone influences for this vertex - for (int j = 0; j < group_count; ++j) - { - const Attr& ja = groups[j].first->data[i]; - const Attr& wa = groups[j].second->data[i]; - - for (int k = 0; k < 4; ++k) - if (wa.f[k] > weight_cutoff) - { - inf[count].i = ja.f[k]; - inf[count].w = wa.f[k]; - count++; - } - } - - // pick top 4 influences; this also sorts resulting influences by weight which helps renderers that use influence subset in shader LODs - std::sort(inf, inf + count, BoneInfluenceWeightPredicate()); - - // copy the top 4 influences back into stream 0 - we will remove other streams at the end - Attr& ja = groups[0].first->data[i]; - Attr& wa = groups[0].second->data[i]; - - for (int k = 0; k < 4; ++k) - { - if (k < count) - { - ja.f[k] = inf[k].i; - wa.f[k] = inf[k].w; - } - else - { - ja.f[k] = 0.f; - wa.f[k] = 0.f; - } - } - } - - // remove redundant weight/joint streams - for (size_t i = 0; i < mesh.streams.size();) - { - Stream& s = mesh.streams[i]; - - if ((s.type == cgltf_attribute_type_joints || s.type == cgltf_attribute_type_weights) && s.index > 0) - mesh.streams.erase(mesh.streams.begin() + i); - else - ++i; - } -} - -static void simplifyPointMesh(Mesh& mesh, float threshold) -{ - assert(mesh.type == cgltf_primitive_type_points); - - if (threshold >= 1) - return; - - const Stream* positions = getStream(mesh, cgltf_attribute_type_position); - if (!positions) - return; - - size_t vertex_count = mesh.streams[0].data.size(); - - size_t target_vertex_count = size_t(double(vertex_count) * threshold); - - if (target_vertex_count < 1) - return; - - std::vector indices(target_vertex_count); - indices.resize(meshopt_simplifyPoints(&indices[0], positions->data[0].f, vertex_count, sizeof(Attr), target_vertex_count)); - - std::vector scratch(indices.size()); - - for (size_t i = 0; i < mesh.streams.size(); ++i) - { - std::vector& data = mesh.streams[i].data; - - assert(data.size() == vertex_count); - - for (size_t j = 0; j < indices.size(); ++j) - scratch[j] = data[indices[j]]; - - data = scratch; - } -} - -static void sortPointMesh(Mesh& mesh) -{ - assert(mesh.type == cgltf_primitive_type_points); - - const Stream* positions = getStream(mesh, cgltf_attribute_type_position); - if (!positions) - return; - - size_t vertex_count = mesh.streams[0].data.size(); - - std::vector remap(vertex_count); - meshopt_spatialSortRemap(&remap[0], positions->data[0].f, vertex_count, sizeof(Attr)); - - for (size_t i = 0; i < mesh.streams.size(); ++i) - { - assert(mesh.streams[i].data.size() == vertex_count); - - meshopt_remapVertexBuffer(&mesh.streams[i].data[0], &mesh.streams[i].data[0], vertex_count, sizeof(Attr), &remap[0]); - } -} - -void processMesh(Mesh& mesh, const Settings& settings) -{ - switch (mesh.type) - { - case cgltf_primitive_type_points: - assert(mesh.indices.empty()); - simplifyPointMesh(mesh, settings.simplify_threshold); - sortPointMesh(mesh); - break; - - case cgltf_primitive_type_lines: - break; - - case cgltf_primitive_type_triangles: - filterBones(mesh); - reindexMesh(mesh); - filterTriangles(mesh); - if (settings.simplify_threshold < 1) - simplifyMesh(mesh, settings.simplify_threshold, settings.simplify_aggressive); - optimizeMesh(mesh, settings.compressmore); - break; - - default: - assert(!"Unknown primitive type"); - } -} - -#ifndef NDEBUG -extern MESHOPTIMIZER_API unsigned char* meshopt_simplifyDebugKind; -extern MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoop; -extern MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoopBack; - -void debugSimplify(const Mesh& source, Mesh& kinds, Mesh& loops, float ratio) -{ - Mesh mesh = source; - assert(mesh.type == cgltf_primitive_type_triangles); - - // note: it's important to follow the same pipeline as processMesh - // otherwise the result won't match - filterBones(mesh); - reindexMesh(mesh); - filterTriangles(mesh); - - // before simplification we need to setup target kind/loop arrays - size_t vertex_count = mesh.streams[0].data.size(); - - std::vector kind(vertex_count); - std::vector loop(vertex_count); - std::vector loopback(vertex_count); - std::vector live(vertex_count); - - meshopt_simplifyDebugKind = &kind[0]; - meshopt_simplifyDebugLoop = &loop[0]; - meshopt_simplifyDebugLoopBack = &loopback[0]; - - simplifyMesh(mesh, ratio, /* aggressive= */ false); - - meshopt_simplifyDebugKind = 0; - meshopt_simplifyDebugLoop = 0; - meshopt_simplifyDebugLoopBack = 0; - - // fill out live info - for (size_t i = 0; i < mesh.indices.size(); ++i) - live[mesh.indices[i]] = true; - - // color palette for display - static const Attr kPalette[] = { - {0.5f, 0.5f, 0.5f, 1.f}, // manifold - {0.f, 0.f, 1.f, 1.f}, // border - {0.f, 1.f, 0.f, 1.f}, // seam - {0.f, 1.f, 1.f, 1.f}, // complex - {1.f, 0.f, 0.f, 1.f}, // locked - }; - - // prepare meshes - kinds.nodes = mesh.nodes; - kinds.skin = mesh.skin; - - loops.nodes = mesh.nodes; - loops.skin = mesh.skin; - - for (size_t i = 0; i < mesh.streams.size(); ++i) - { - const Stream& stream = mesh.streams[i]; - - if (stream.target == 0 && (stream.type == cgltf_attribute_type_position || stream.type == cgltf_attribute_type_joints || stream.type == cgltf_attribute_type_weights)) - { - kinds.streams.push_back(stream); - loops.streams.push_back(stream); - } - } - - // transform kind/loop data into lines & points - Stream colors = {cgltf_attribute_type_color}; - colors.data.resize(vertex_count); - - for (size_t i = 0; i < vertex_count; ++i) - colors.data[i] = kPalette[kind[i]]; - - kinds.type = cgltf_primitive_type_points; - - kinds.streams.push_back(colors); - - for (size_t i = 0; i < vertex_count; ++i) - if (live[i] && kind[i] != 0) - kinds.indices.push_back(unsigned(i)); - - loops.type = cgltf_primitive_type_lines; - - loops.streams.push_back(colors); - - for (size_t i = 0; i < vertex_count; ++i) - if (live[i] && (kind[i] == 1 || kind[i] == 2)) - { - if (loop[i] != ~0u && live[loop[i]]) - { - loops.indices.push_back(unsigned(i)); - loops.indices.push_back(loop[i]); - } - - if (loopback[i] != ~0u && live[loopback[i]]) - { - loops.indices.push_back(loopback[i]); - loops.indices.push_back(unsigned(i)); - } - } -} - -void debugMeshlets(const Mesh& source, Mesh& meshlets, Mesh& bounds, int max_vertices, bool scan) -{ - Mesh mesh = source; - assert(mesh.type == cgltf_primitive_type_triangles); - - reindexMesh(mesh); - - if (scan) - optimizeMesh(mesh, false); - - const Stream* positions = getStream(mesh, cgltf_attribute_type_position); - assert(positions); - - const float cone_weight = 0.f; - - size_t max_triangles = (max_vertices * 2 + 3) & ~3; - size_t max_meshlets = meshopt_buildMeshletsBound(mesh.indices.size(), max_vertices, max_triangles); - - std::vector ml(max_meshlets); - std::vector mlv(max_meshlets * max_vertices); - std::vector mlt(max_meshlets * max_triangles * 3); - - if (scan) - ml.resize(meshopt_buildMeshletsScan(&ml[0], &mlv[0], &mlt[0], &mesh.indices[0], mesh.indices.size(), positions->data.size(), max_vertices, max_triangles)); - else - ml.resize(meshopt_buildMeshlets(&ml[0], &mlv[0], &mlt[0], &mesh.indices[0], mesh.indices.size(), positions->data[0].f, positions->data.size(), sizeof(Attr), max_vertices, max_triangles, cone_weight)); - - // generate meshlet meshes, using unique colors - meshlets.nodes = mesh.nodes; - - Stream mv = {cgltf_attribute_type_position}; - Stream mc = {cgltf_attribute_type_color}; - - for (size_t i = 0; i < ml.size(); ++i) - { - const meshopt_Meshlet& m = ml[i]; - - unsigned int h = unsigned(i); - h ^= h >> 13; - h *= 0x5bd1e995; - h ^= h >> 15; - - Attr c = {{float(h & 0xff) / 255.f, float((h >> 8) & 0xff) / 255.f, float((h >> 16) & 0xff) / 255.f, 1.f}}; - - unsigned int offset = unsigned(mv.data.size()); - - for (size_t j = 0; j < m.vertex_count; ++j) - { - mv.data.push_back(positions->data[mlv[m.vertex_offset + j]]); - mc.data.push_back(c); - } - - for (size_t j = 0; j < m.triangle_count; ++j) - { - meshlets.indices.push_back(offset + mlt[m.triangle_offset + j * 3 + 0]); - meshlets.indices.push_back(offset + mlt[m.triangle_offset + j * 3 + 1]); - meshlets.indices.push_back(offset + mlt[m.triangle_offset + j * 3 + 2]); - } - } - - meshlets.type = cgltf_primitive_type_triangles; - meshlets.streams.push_back(mv); - meshlets.streams.push_back(mc); - - // generate bounds meshes, using a sphere per meshlet - bounds.nodes = mesh.nodes; - - Stream bv = {cgltf_attribute_type_position}; - Stream bc = {cgltf_attribute_type_color}; - - for (size_t i = 0; i < ml.size(); ++i) - { - const meshopt_Meshlet& m = ml[i]; - - meshopt_Bounds mb = meshopt_computeMeshletBounds(&mlv[m.vertex_offset], &mlt[m.triangle_offset], m.triangle_count, positions->data[0].f, positions->data.size(), sizeof(Attr)); - - unsigned int h = unsigned(i); - h ^= h >> 13; - h *= 0x5bd1e995; - h ^= h >> 15; - - Attr c = {{float(h & 0xff) / 255.f, float((h >> 8) & 0xff) / 255.f, float((h >> 16) & 0xff) / 255.f, 0.1f}}; - - unsigned int offset = unsigned(bv.data.size()); - - const int N = 10; - - for (int y = 0; y <= N; ++y) - { - float u = (y == N) ? 0 : float(y) / N * 2 * 3.1415926f; - float sinu = sinf(u), cosu = cosf(u); - - for (int x = 0; x <= N; ++x) - { - float v = float(x) / N * 3.1415926f; - float sinv = sinf(v), cosv = cosf(v); - - float fx = sinv * cosu; - float fy = sinv * sinu; - float fz = cosv; - - Attr p = {{mb.center[0] + mb.radius * fx, mb.center[1] + mb.radius * fy, mb.center[2] + mb.radius * fz, 1.f}}; - - bv.data.push_back(p); - bc.data.push_back(c); - } - } - - for (int y = 0; y < N; ++y) - for (int x = 0; x < N; ++x) - { - bounds.indices.push_back(offset + (N + 1) * (y + 0) + (x + 0)); - bounds.indices.push_back(offset + (N + 1) * (y + 0) + (x + 1)); - bounds.indices.push_back(offset + (N + 1) * (y + 1) + (x + 0)); - - bounds.indices.push_back(offset + (N + 1) * (y + 1) + (x + 0)); - bounds.indices.push_back(offset + (N + 1) * (y + 0) + (x + 1)); - bounds.indices.push_back(offset + (N + 1) * (y + 1) + (x + 1)); - } - } - - bounds.type = cgltf_primitive_type_triangles; - bounds.streams.push_back(bv); - bounds.streams.push_back(bc); -} -#endif diff --git a/Dependencies/meshoptimizer/gltf/node.cpp b/Dependencies/meshoptimizer/gltf/node.cpp deleted file mode 100644 index 5186e53..0000000 --- a/Dependencies/meshoptimizer/gltf/node.cpp +++ /dev/null @@ -1,206 +0,0 @@ -// This file is part of gltfpack; see gltfpack.h for version/license details -#include "gltfpack.h" - -#include -#include - -void markScenes(cgltf_data* data, std::vector& nodes) -{ - for (size_t i = 0; i < nodes.size(); ++i) - nodes[i].scene = -1; - - for (size_t i = 0; i < data->scenes_count; ++i) - for (size_t j = 0; j < data->scenes[i].nodes_count; ++j) - { - NodeInfo& ni = nodes[data->scenes[i].nodes[j] - data->nodes]; - - if (ni.scene >= 0) - ni.scene = -2; // multiple scenes - else - ni.scene = int(i); - } - - for (size_t i = 0; i < data->nodes_count; ++i) - { - cgltf_node* root = &data->nodes[i]; - while (root->parent) - root = root->parent; - - nodes[i].scene = nodes[root - data->nodes].scene; - } -} - -void markAnimated(cgltf_data* data, std::vector& nodes, const std::vector& animations) -{ - for (size_t i = 0; i < animations.size(); ++i) - { - const Animation& animation = animations[i]; - - for (size_t j = 0; j < animation.tracks.size(); ++j) - { - const Track& track = animation.tracks[j]; - - // mark nodes that have animation tracks that change their base transform as animated - if (!track.dummy) - { - NodeInfo& ni = nodes[track.node - data->nodes]; - - ni.animated_paths |= (1 << track.path); - } - } - } - - for (size_t i = 0; i < data->nodes_count; ++i) - { - NodeInfo& ni = nodes[i]; - - for (cgltf_node* node = &data->nodes[i]; node; node = node->parent) - ni.animated |= nodes[node - data->nodes].animated_paths != 0; - } -} - -void markNeededNodes(cgltf_data* data, std::vector& nodes, const std::vector& meshes, const std::vector& animations, const Settings& settings) -{ - // mark all joints as kept - for (size_t i = 0; i < data->skins_count; ++i) - { - const cgltf_skin& skin = data->skins[i]; - - // for now we keep all joints directly referenced by the skin and the entire ancestry tree; we keep names for joints as well - for (size_t j = 0; j < skin.joints_count; ++j) - { - NodeInfo& ni = nodes[skin.joints[j] - data->nodes]; - - ni.keep = true; - } - } - - // mark all animated nodes as kept - for (size_t i = 0; i < animations.size(); ++i) - { - const Animation& animation = animations[i]; - - for (size_t j = 0; j < animation.tracks.size(); ++j) - { - const Track& track = animation.tracks[j]; - - if (settings.anim_const || !track.dummy) - { - NodeInfo& ni = nodes[track.node - data->nodes]; - - ni.keep = true; - } - } - } - - // mark all mesh nodes as kept - for (size_t i = 0; i < meshes.size(); ++i) - { - const Mesh& mesh = meshes[i]; - - for (size_t j = 0; j < mesh.nodes.size(); ++j) - { - NodeInfo& ni = nodes[mesh.nodes[j] - data->nodes]; - - ni.keep = true; - } - } - - // mark all light/camera nodes as kept - for (size_t i = 0; i < data->nodes_count; ++i) - { - const cgltf_node& node = data->nodes[i]; - - if (node.light || node.camera) - { - nodes[i].keep = true; - } - } - - // mark all named nodes as needed (if -kn is specified) - if (settings.keep_nodes) - { - for (size_t i = 0; i < data->nodes_count; ++i) - { - const cgltf_node& node = data->nodes[i]; - - if (node.name && *node.name) - { - nodes[i].keep = true; - } - } - } -} - -void remapNodes(cgltf_data* data, std::vector& nodes, size_t& node_offset) -{ - // to keep a node, we currently need to keep the entire ancestry chain - for (size_t i = 0; i < data->nodes_count; ++i) - { - if (!nodes[i].keep) - continue; - - for (cgltf_node* node = &data->nodes[i]; node; node = node->parent) - nodes[node - data->nodes].keep = true; - } - - // generate sequential indices for all nodes; they aren't sorted topologically - for (size_t i = 0; i < data->nodes_count; ++i) - { - NodeInfo& ni = nodes[i]; - - if (ni.keep) - { - ni.remap = int(node_offset); - - node_offset++; - } - } -} - -void decomposeTransform(float translation[3], float rotation[4], float scale[3], const float* transform) -{ - float m[4][4] = {}; - memcpy(m, transform, 16 * sizeof(float)); - - // extract translation from last row - translation[0] = m[3][0]; - translation[1] = m[3][1]; - translation[2] = m[3][2]; - - // compute determinant to determine handedness - float det = - m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2]) - - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0]) + - m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]); - - float sign = (det < 0.f) ? -1.f : 1.f; - - // recover scale from axis lengths - scale[0] = sqrtf(m[0][0] * m[0][0] + m[1][0] * m[1][0] + m[2][0] * m[2][0]) * sign; - scale[1] = sqrtf(m[0][1] * m[0][1] + m[1][1] * m[1][1] + m[2][1] * m[2][1]) * sign; - scale[2] = sqrtf(m[0][2] * m[0][2] + m[1][2] * m[1][2] + m[2][2] * m[2][2]) * sign; - - // normalize axes to get a pure rotation matrix - float rsx = (scale[0] == 0.f) ? 0.f : 1.f / scale[0]; - float rsy = (scale[1] == 0.f) ? 0.f : 1.f / scale[1]; - float rsz = (scale[2] == 0.f) ? 0.f : 1.f / scale[2]; - - float r00 = m[0][0] * rsx, r10 = m[1][0] * rsx, r20 = m[2][0] * rsx; - float r01 = m[0][1] * rsy, r11 = m[1][1] * rsy, r21 = m[2][1] * rsy; - float r02 = m[0][2] * rsz, r12 = m[1][2] * rsz, r22 = m[2][2] * rsz; - - // "branchless" version of Mike Day's matrix to quaternion conversion - int qc = r22 < 0 ? (r00 > r11 ? 0 : 1) : (r00 < -r11 ? 2 : 3); - float qs1 = qc & 2 ? -1.f : 1.f; - float qs2 = qc & 1 ? -1.f : 1.f; - float qs3 = (qc - 1) & 2 ? -1.f : 1.f; - - float qt = 1.f - qs3 * r00 - qs2 * r11 - qs1 * r22; - float qs = 0.5f / sqrtf(qt); - - rotation[qc ^ 0] = qs * qt; - rotation[qc ^ 1] = qs * (r01 + qs1 * r10); - rotation[qc ^ 2] = qs * (r20 + qs2 * r02); - rotation[qc ^ 3] = qs * (r12 + qs3 * r21); -} diff --git a/Dependencies/meshoptimizer/gltf/package.json b/Dependencies/meshoptimizer/gltf/package.json deleted file mode 100644 index 62845c0..0000000 --- a/Dependencies/meshoptimizer/gltf/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "gltfpack", - "version": "0.17.0", - "description": "A command-line tool that can optimize glTF files for size and speed", - "author": "Arseny Kapoulkine", - "license": "MIT", - "bugs": "https://github.com/zeux/meshoptimizer/issues", - "homepage": "https://github.com/zeux/meshoptimizer", - "keywords": [ - "gltf" - ], - "repository": { - "type": "git", - "url": "https://github.com/zeux/meshoptimizer" - }, - "bin": "./cli.js", - "main": "./library.js", - "files": [ - "*.js", "*.wasm" - ], - "scripts": { - "prepublishOnly": "node cli.js -v" - } -} diff --git a/Dependencies/meshoptimizer/gltf/parsegltf.cpp b/Dependencies/meshoptimizer/gltf/parsegltf.cpp deleted file mode 100644 index ec7b633..0000000 --- a/Dependencies/meshoptimizer/gltf/parsegltf.cpp +++ /dev/null @@ -1,525 +0,0 @@ -// This file is part of gltfpack; see gltfpack.h for version/license details -#include "gltfpack.h" - -#include -#include -#include - -static const char* getError(cgltf_result result, cgltf_data* data) -{ - switch (result) - { - case cgltf_result_file_not_found: - return data ? "resource not found" : "file not found"; - - case cgltf_result_io_error: - return "I/O error"; - - case cgltf_result_invalid_json: - return "invalid JSON"; - - case cgltf_result_invalid_gltf: - return "invalid GLTF"; - - case cgltf_result_out_of_memory: - return "out of memory"; - - case cgltf_result_legacy_gltf: - return "legacy GLTF"; - - case cgltf_result_data_too_short: - return data ? "buffer too short" : "not a GLTF file"; - - case cgltf_result_unknown_format: - return data ? "unknown resource format" : "not a GLTF file"; - - default: - return "unknown error"; - } -} - -static void readAccessor(std::vector& data, const cgltf_accessor* accessor) -{ - assert(accessor->type == cgltf_type_scalar); - - data.resize(accessor->count); - cgltf_accessor_unpack_floats(accessor, &data[0], data.size()); -} - -static void readAccessor(std::vector& data, const cgltf_accessor* accessor) -{ - size_t components = cgltf_num_components(accessor->type); - - std::vector temp(accessor->count * components); - cgltf_accessor_unpack_floats(accessor, &temp[0], temp.size()); - - data.resize(accessor->count); - - for (size_t i = 0; i < accessor->count; ++i) - { - for (size_t k = 0; k < components && k < 4; ++k) - data[i].f[k] = temp[i * components + k]; - } -} - -static void fixupIndices(std::vector& indices, cgltf_primitive_type& type) -{ - if (type == cgltf_primitive_type_line_loop) - { - std::vector result; - result.reserve(indices.size() * 2 + 2); - - for (size_t i = 1; i <= indices.size(); ++i) - { - result.push_back(indices[i - 1]); - result.push_back(indices[i % indices.size()]); - } - - indices.swap(result); - type = cgltf_primitive_type_lines; - } - else if (type == cgltf_primitive_type_line_strip) - { - std::vector result; - result.reserve(indices.size() * 2); - - for (size_t i = 1; i < indices.size(); ++i) - { - result.push_back(indices[i - 1]); - result.push_back(indices[i]); - } - - indices.swap(result); - type = cgltf_primitive_type_lines; - } - else if (type == cgltf_primitive_type_triangle_strip) - { - std::vector result; - result.reserve(indices.size() * 3); - - for (size_t i = 2; i < indices.size(); ++i) - { - int flip = i & 1; - - result.push_back(indices[i - 2 + flip]); - result.push_back(indices[i - 1 - flip]); - result.push_back(indices[i]); - } - - indices.swap(result); - type = cgltf_primitive_type_triangles; - } - else if (type == cgltf_primitive_type_triangle_fan) - { - std::vector result; - result.reserve(indices.size() * 3); - - for (size_t i = 2; i < indices.size(); ++i) - { - result.push_back(indices[0]); - result.push_back(indices[i - 1]); - result.push_back(indices[i]); - } - - indices.swap(result); - type = cgltf_primitive_type_triangles; - } - else if (type == cgltf_primitive_type_lines) - { - // glTF files don't require that line index count is divisible by 2, but it is obviously critical for scenes to render - indices.resize(indices.size() / 2 * 2); - } - else if (type == cgltf_primitive_type_triangles) - { - // glTF files don't require that triangle index count is divisible by 3, but it is obviously critical for scenes to render - indices.resize(indices.size() / 3 * 3); - } -} - -static void parseMeshesGltf(cgltf_data* data, std::vector& meshes, std::vector >& mesh_remap) -{ - size_t total_primitives = 0; - - for (size_t mi = 0; mi < data->meshes_count; ++mi) - total_primitives += data->meshes[mi].primitives_count; - - meshes.reserve(total_primitives); - mesh_remap.resize(data->meshes_count); - - for (size_t mi = 0; mi < data->meshes_count; ++mi) - { - const cgltf_mesh& mesh = data->meshes[mi]; - - size_t remap_offset = meshes.size(); - - for (size_t pi = 0; pi < mesh.primitives_count; ++pi) - { - const cgltf_primitive& primitive = mesh.primitives[pi]; - - if (primitive.type == cgltf_primitive_type_points && primitive.indices) - { - fprintf(stderr, "Warning: ignoring primitive %d of mesh %d because indexed points are not supported\n", int(pi), int(mi)); - continue; - } - - meshes.push_back(Mesh()); - Mesh& result = meshes.back(); - - result.scene = -1; - - result.material = primitive.material; - - result.type = primitive.type; - - result.streams.reserve(primitive.attributes_count); - - if (primitive.indices) - { - result.indices.resize(primitive.indices->count); - for (size_t i = 0; i < primitive.indices->count; ++i) - result.indices[i] = unsigned(cgltf_accessor_read_index(primitive.indices, i)); - } - else if (primitive.type != cgltf_primitive_type_points) - { - size_t count = primitive.attributes ? primitive.attributes[0].data->count : 0; - - // note, while we could generate a good index buffer, reindexMesh will take care of this - result.indices.resize(count); - for (size_t i = 0; i < count; ++i) - result.indices[i] = unsigned(i); - } - - fixupIndices(result.indices, result.type); - - for (size_t ai = 0; ai < primitive.attributes_count; ++ai) - { - const cgltf_attribute& attr = primitive.attributes[ai]; - - if (attr.type == cgltf_attribute_type_invalid) - { - fprintf(stderr, "Warning: ignoring unknown attribute %s in primitive %d of mesh %d\n", attr.name, int(pi), int(mi)); - continue; - } - - result.streams.push_back(Stream()); - Stream& s = result.streams.back(); - - s.type = attr.type; - s.index = attr.index; - - readAccessor(s.data, attr.data); - - if (attr.type == cgltf_attribute_type_color && attr.data->type == cgltf_type_vec3) - { - for (size_t i = 0; i < s.data.size(); ++i) - s.data[i].f[3] = 1.0f; - } - } - - for (size_t ti = 0; ti < primitive.targets_count; ++ti) - { - const cgltf_morph_target& target = primitive.targets[ti]; - - for (size_t ai = 0; ai < target.attributes_count; ++ai) - { - const cgltf_attribute& attr = target.attributes[ai]; - - if (attr.type == cgltf_attribute_type_invalid) - { - fprintf(stderr, "Warning: ignoring unknown attribute %s in morph target %d of primitive %d of mesh %d\n", attr.name, int(ti), int(pi), int(mi)); - continue; - } - - result.streams.push_back(Stream()); - Stream& s = result.streams.back(); - - s.type = attr.type; - s.index = attr.index; - s.target = int(ti + 1); - - readAccessor(s.data, attr.data); - } - } - - result.targets = primitive.targets_count; - result.target_weights.assign(mesh.weights, mesh.weights + mesh.weights_count); - result.target_names.assign(mesh.target_names, mesh.target_names + mesh.target_names_count); - - result.variants.assign(primitive.mappings, primitive.mappings + primitive.mappings_count); - } - - mesh_remap[mi] = std::make_pair(remap_offset, meshes.size()); - } -} - -static void parseMeshNodesGltf(cgltf_data* data, std::vector& meshes, const std::vector >& mesh_remap) -{ - for (size_t i = 0; i < data->nodes_count; ++i) - { - cgltf_node& node = data->nodes[i]; - if (!node.mesh) - continue; - - std::pair range = mesh_remap[node.mesh - data->meshes]; - - for (size_t mi = range.first; mi < range.second; ++mi) - { - Mesh* mesh = &meshes[mi]; - - if (!mesh->nodes.empty() && mesh->skin != node.skin) - { - // this should be extremely rare - if the same mesh is used with different skins, we need to duplicate it - // in this case we don't spend any effort on keeping the number of duplicates to the minimum, because this - // should really never happen. - meshes.push_back(*mesh); - mesh = &meshes.back(); - } - - mesh->nodes.push_back(&node); - mesh->skin = node.skin; - } - } - - for (size_t i = 0; i < meshes.size(); ++i) - { - Mesh& mesh = meshes[i]; - - // because the rest of gltfpack assumes that empty nodes array = world-space mesh, we need to filter unused meshes - if (mesh.nodes.empty()) - { - mesh.streams.clear(); - mesh.indices.clear(); - } - } -} - -static void parseAnimationsGltf(cgltf_data* data, std::vector& animations) -{ - animations.reserve(data->animations_count); - - for (size_t i = 0; i < data->animations_count; ++i) - { - const cgltf_animation& animation = data->animations[i]; - - animations.push_back(Animation()); - Animation& result = animations.back(); - - result.name = animation.name; - - result.tracks.reserve(animation.channels_count); - - for (size_t j = 0; j < animation.channels_count; ++j) - { - const cgltf_animation_channel& channel = animation.channels[j]; - - if (!channel.target_node) - { - fprintf(stderr, "Warning: ignoring channel %d of animation %d because it has no target node\n", int(j), int(i)); - continue; - } - - result.tracks.push_back(Track()); - Track& track = result.tracks.back(); - - track.node = channel.target_node; - track.path = channel.target_path; - - track.components = (channel.target_path == cgltf_animation_path_type_weights) ? track.node->mesh->primitives[0].targets_count : 1; - - track.interpolation = channel.sampler->interpolation; - - readAccessor(track.time, channel.sampler->input); - readAccessor(track.data, channel.sampler->output); - } - - if (result.tracks.empty()) - { - fprintf(stderr, "Warning: ignoring animation %d because it has no valid tracks\n", int(i)); - animations.pop_back(); - } - } -} - -static bool requiresExtension(cgltf_data* data, const char* name) -{ - for (size_t i = 0; i < data->extensions_required_count; ++i) - if (strcmp(data->extensions_required[i], name) == 0) - return true; - - return false; -} - -static bool needsDummyBuffers(cgltf_data* data) -{ - for (size_t i = 0; i < data->accessors_count; ++i) - { - cgltf_accessor* accessor = &data->accessors[i]; - - if (accessor->buffer_view && accessor->buffer_view->buffer->data == NULL) - return true; - - if (accessor->is_sparse) - { - cgltf_accessor_sparse* sparse = &accessor->sparse; - - if (sparse->indices_buffer_view->buffer->data == NULL) - return true; - if (sparse->values_buffer_view->buffer->data == NULL) - return true; - } - } - - for (size_t i = 0; i < data->images_count; ++i) - { - cgltf_image* image = &data->images[i]; - - if (image->buffer_view && image->buffer_view->buffer->data == NULL) - return true; - } - - return false; -} - -static void evacuateExtras(cgltf_data* data, std::string& extras, cgltf_extras& item) -{ - size_t offset = extras.size(); - - extras.append(data->json + item.start_offset, item.end_offset - item.start_offset); - - item.start_offset = offset; - item.end_offset = extras.size(); -} - -static void evacuateExtras(cgltf_data* data, std::string& extras) -{ - size_t size = 0; - - size += data->asset.extras.end_offset - data->asset.extras.start_offset; - - for (size_t i = 0; i < data->materials_count; ++i) - size += data->materials[i].extras.end_offset - data->materials[i].extras.start_offset; - - for (size_t i = 0; i < data->nodes_count; ++i) - size += data->nodes[i].extras.end_offset - data->nodes[i].extras.start_offset; - - extras.reserve(size); - - evacuateExtras(data, extras, data->asset.extras); - - for (size_t i = 0; i < data->materials_count; ++i) - evacuateExtras(data, extras, data->materials[i].extras); - - for (size_t i = 0; i < data->nodes_count; ++i) - evacuateExtras(data, extras, data->nodes[i].extras); -} - -static void freeFile(cgltf_data* data) -{ - data->json = NULL; - data->bin = NULL; - - free(data->file_data); - data->file_data = NULL; -} - -static bool freeUnusedBuffers(cgltf_data* data) -{ - std::vector used(data->buffers_count); - - for (size_t i = 0; i < data->skins_count; ++i) - { - const cgltf_skin& skin = data->skins[i]; - - if (skin.inverse_bind_matrices && skin.inverse_bind_matrices->buffer_view) - { - assert(skin.inverse_bind_matrices->buffer_view->buffer); - used[skin.inverse_bind_matrices->buffer_view->buffer - data->buffers] = 1; - } - } - - for (size_t i = 0; i < data->images_count; ++i) - { - const cgltf_image& image = data->images[i]; - - if (image.buffer_view) - { - assert(image.buffer_view->buffer); - used[image.buffer_view->buffer - data->buffers] = 1; - } - } - - bool free_bin = false; - - for (size_t i = 0; i < data->buffers_count; ++i) - { - cgltf_buffer& buffer = data->buffers[i]; - - if (!used[i] && buffer.data) - { - if (buffer.data != data->bin) - free(buffer.data); - else - free_bin = true; - - buffer.data = NULL; - } - } - - return free_bin; -} - -cgltf_data* parseGltf(const char* path, std::vector& meshes, std::vector& animations, std::string& extras, const char** error) -{ - cgltf_data* data = 0; - - cgltf_options options = {}; - cgltf_result result = cgltf_parse_file(&options, path, &data); - - if (data) - { - evacuateExtras(data, extras); - - if (!data->bin) - freeFile(data); - } - - result = (result == cgltf_result_success) ? cgltf_load_buffers(&options, data, path) : result; - result = (result == cgltf_result_success) ? cgltf_validate(data) : result; - - *error = NULL; - - if (result != cgltf_result_success) - *error = getError(result, data); - else if (requiresExtension(data, "KHR_draco_mesh_compression")) - *error = "file requires Draco mesh compression support"; - else if (requiresExtension(data, "EXT_meshopt_compression")) - *error = "file has already been compressed using gltfpack"; - else if (requiresExtension(data, "KHR_texture_basisu")) - *error = "file requires BasisU texture support"; - else if (requiresExtension(data, "EXT_mesh_gpu_instancing")) - *error = "file requires mesh instancing support"; - else if (needsDummyBuffers(data)) - *error = "buffer has no data"; - - if (*error) - { - cgltf_free(data); - return 0; - } - - if (requiresExtension(data, "KHR_mesh_quantization")) - fprintf(stderr, "Warning: file uses quantized geometry; repacking may result in increased quantization error\n"); - - std::vector > mesh_remap; - - parseMeshesGltf(data, meshes, mesh_remap); - parseMeshNodesGltf(data, meshes, mesh_remap); - parseAnimationsGltf(data, animations); - - bool free_bin = freeUnusedBuffers(data); - - if (data->bin && free_bin) - freeFile(data); - - return data; -} diff --git a/Dependencies/meshoptimizer/gltf/parseobj.cpp b/Dependencies/meshoptimizer/gltf/parseobj.cpp deleted file mode 100644 index 0637e94..0000000 --- a/Dependencies/meshoptimizer/gltf/parseobj.cpp +++ /dev/null @@ -1,219 +0,0 @@ -// This file is part of gltfpack; see gltfpack.h for version/license details -#include "gltfpack.h" - -#include "../extern/fast_obj.h" - -#include - -#include -#include - -static void defaultFree(void*, void* p) -{ - free(p); -} - -static int textureIndex(const std::vector& textures, const char* name) -{ - for (size_t i = 0; i < textures.size(); ++i) - if (textures[i] == name) - return int(i); - - return -1; -} - -static cgltf_data* parseSceneObj(fastObjMesh* obj) -{ - cgltf_data* data = (cgltf_data*)calloc(1, sizeof(cgltf_data)); - data->memory.free = defaultFree; - - std::vector textures; - - for (unsigned int mi = 0; mi < obj->material_count; ++mi) - { - fastObjMaterial& om = obj->materials[mi]; - - if (om.map_Kd.name && textureIndex(textures, om.map_Kd.name) < 0) - textures.push_back(om.map_Kd.name); - } - - data->images = (cgltf_image*)calloc(textures.size(), sizeof(cgltf_image)); - data->images_count = textures.size(); - - for (size_t i = 0; i < textures.size(); ++i) - { - data->images[i].uri = (char*)malloc(textures[i].size() + 1); - strcpy(data->images[i].uri, textures[i].c_str()); - } - - data->textures = (cgltf_texture*)calloc(textures.size(), sizeof(cgltf_texture)); - data->textures_count = textures.size(); - - for (size_t i = 0; i < textures.size(); ++i) - { - data->textures[i].image = &data->images[i]; - } - - data->materials = (cgltf_material*)calloc(obj->material_count, sizeof(cgltf_material)); - data->materials_count = obj->material_count; - - for (unsigned int mi = 0; mi < obj->material_count; ++mi) - { - cgltf_material& gm = data->materials[mi]; - fastObjMaterial& om = obj->materials[mi]; - - gm.has_pbr_metallic_roughness = true; - gm.pbr_metallic_roughness.base_color_factor[0] = 1.0f; - gm.pbr_metallic_roughness.base_color_factor[1] = 1.0f; - gm.pbr_metallic_roughness.base_color_factor[2] = 1.0f; - gm.pbr_metallic_roughness.base_color_factor[3] = 1.0f; - gm.pbr_metallic_roughness.metallic_factor = 0.0f; - gm.pbr_metallic_roughness.roughness_factor = 1.0f; - - gm.alpha_cutoff = 0.5f; - - if (om.map_Kd.name) - { - gm.pbr_metallic_roughness.base_color_texture.texture = &data->textures[textureIndex(textures, om.map_Kd.name)]; - gm.pbr_metallic_roughness.base_color_texture.scale = 1.0f; - - gm.alpha_mode = (om.illum == 4 || om.illum == 6 || om.illum == 7 || om.illum == 9) ? cgltf_alpha_mode_mask : cgltf_alpha_mode_opaque; - } - - if (om.map_d.name) - { - gm.alpha_mode = cgltf_alpha_mode_blend; - } - } - - data->scenes = (cgltf_scene*)calloc(1, sizeof(cgltf_scene)); - data->scenes_count = 1; - - return data; -} - -static void parseMeshesObj(fastObjMesh* obj, cgltf_data* data, std::vector& meshes) -{ - unsigned int material_count = std::max(obj->material_count, 1u); - - std::vector vertex_count(material_count); - std::vector index_count(material_count); - - for (unsigned int fi = 0; fi < obj->face_count; ++fi) - { - unsigned int mi = obj->face_materials[fi]; - - vertex_count[mi] += obj->face_vertices[fi]; - index_count[mi] += (obj->face_vertices[fi] - 2) * 3; - } - - std::vector mesh_index(material_count); - - for (unsigned int mi = 0; mi < material_count; ++mi) - { - if (index_count[mi] == 0) - continue; - - mesh_index[mi] = meshes.size(); - - meshes.push_back(Mesh()); - Mesh& mesh = meshes.back(); - - if (data->materials_count) - { - assert(mi < data->materials_count); - mesh.material = &data->materials[mi]; - } - - mesh.type = cgltf_primitive_type_triangles; - - mesh.streams.resize(3); - mesh.streams[0].type = cgltf_attribute_type_position; - mesh.streams[0].data.resize(vertex_count[mi]); - mesh.streams[1].type = cgltf_attribute_type_normal; - mesh.streams[1].data.resize(vertex_count[mi]); - mesh.streams[2].type = cgltf_attribute_type_texcoord; - mesh.streams[2].data.resize(vertex_count[mi]); - mesh.indices.resize(index_count[mi]); - mesh.targets = 0; - } - - std::vector mesh_normals(meshes.size()); - std::vector mesh_texcoords(meshes.size()); - - std::vector vertex_offset(material_count); - std::vector index_offset(material_count); - - size_t group_offset = 0; - - for (unsigned int fi = 0; fi < obj->face_count; ++fi) - { - unsigned int mi = obj->face_materials[fi]; - Mesh& mesh = meshes[mesh_index[mi]]; - - size_t vo = vertex_offset[mi]; - size_t io = index_offset[mi]; - - for (unsigned int vi = 0; vi < obj->face_vertices[fi]; ++vi) - { - fastObjIndex ii = obj->indices[group_offset + vi]; - - Attr p = {{obj->positions[ii.p * 3 + 0], obj->positions[ii.p * 3 + 1], obj->positions[ii.p * 3 + 2]}}; - Attr n = {{obj->normals[ii.n * 3 + 0], obj->normals[ii.n * 3 + 1], obj->normals[ii.n * 3 + 2]}}; - Attr t = {{obj->texcoords[ii.t * 2 + 0], 1.f - obj->texcoords[ii.t * 2 + 1]}}; - - mesh.streams[0].data[vo + vi] = p; - mesh.streams[1].data[vo + vi] = n; - mesh.streams[2].data[vo + vi] = t; - - mesh_normals[mesh_index[mi]] |= ii.n > 0; - mesh_texcoords[mesh_index[mi]] |= ii.t > 0; - } - - for (unsigned int vi = 2; vi < obj->face_vertices[fi]; ++vi) - { - size_t to = io + (vi - 2) * 3; - - mesh.indices[to + 0] = unsigned(vo); - mesh.indices[to + 1] = unsigned(vo + vi - 1); - mesh.indices[to + 2] = unsigned(vo + vi); - } - - vertex_offset[mi] += obj->face_vertices[fi]; - index_offset[mi] += (obj->face_vertices[fi] - 2) * 3; - group_offset += obj->face_vertices[fi]; - } - - for (size_t i = 0; i < meshes.size(); ++i) - { - Mesh& mesh = meshes[i]; - - assert(mesh.streams.size() == 3); - assert(mesh.streams[1].type == cgltf_attribute_type_normal); - assert(mesh.streams[2].type == cgltf_attribute_type_texcoord); - - if (!mesh_texcoords[i]) - mesh.streams.erase(mesh.streams.begin() + 2); - - if (!mesh_normals[i]) - mesh.streams.erase(mesh.streams.begin() + 1); - } -} - -cgltf_data* parseObj(const char* path, std::vector& meshes, const char** error) -{ - fastObjMesh* obj = fast_obj_read(path); - - if (!obj) - { - *error = "file not found"; - return 0; - } - - cgltf_data* data = parseSceneObj(obj); - parseMeshesObj(obj, data, meshes); - - fast_obj_destroy(obj); - - return data; -} diff --git a/Dependencies/meshoptimizer/gltf/stream.cpp b/Dependencies/meshoptimizer/gltf/stream.cpp deleted file mode 100644 index ab757ac..0000000 --- a/Dependencies/meshoptimizer/gltf/stream.cpp +++ /dev/null @@ -1,820 +0,0 @@ -// This file is part of gltfpack; see gltfpack.h for version/license details -#include "gltfpack.h" - -#include - -#include -#include -#include -#include - -#include "../src/meshoptimizer.h" - -struct Bounds -{ - Attr min, max; - - Bounds() - { - min.f[0] = min.f[1] = min.f[2] = min.f[3] = +FLT_MAX; - max.f[0] = max.f[1] = max.f[2] = max.f[3] = -FLT_MAX; - } - - bool isValid() const - { - return min.f[0] <= max.f[0] && min.f[1] <= max.f[1] && min.f[2] <= max.f[2] && min.f[3] <= max.f[3]; - } -}; - -static void updateAttributeBounds(const Mesh& mesh, cgltf_attribute_type type, Bounds& b) -{ - Attr pad = {}; - - for (size_t j = 0; j < mesh.streams.size(); ++j) - { - const Stream& s = mesh.streams[j]; - - if (s.type == type) - { - if (s.target == 0) - { - for (size_t k = 0; k < s.data.size(); ++k) - { - const Attr& a = s.data[k]; - - b.min.f[0] = std::min(b.min.f[0], a.f[0]); - b.min.f[1] = std::min(b.min.f[1], a.f[1]); - b.min.f[2] = std::min(b.min.f[2], a.f[2]); - b.min.f[3] = std::min(b.min.f[3], a.f[3]); - - b.max.f[0] = std::max(b.max.f[0], a.f[0]); - b.max.f[1] = std::max(b.max.f[1], a.f[1]); - b.max.f[2] = std::max(b.max.f[2], a.f[2]); - b.max.f[3] = std::max(b.max.f[3], a.f[3]); - } - } - else - { - for (size_t k = 0; k < s.data.size(); ++k) - { - const Attr& a = s.data[k]; - - pad.f[0] = std::max(pad.f[0], fabsf(a.f[0])); - pad.f[1] = std::max(pad.f[1], fabsf(a.f[1])); - pad.f[2] = std::max(pad.f[2], fabsf(a.f[2])); - pad.f[3] = std::max(pad.f[3], fabsf(a.f[3])); - } - } - } - } - - for (int k = 0; k < 4; ++k) - { - b.min.f[k] -= pad.f[k]; - b.max.f[k] += pad.f[k]; - } -} - -QuantizationPosition prepareQuantizationPosition(const std::vector& meshes, const Settings& settings) -{ - QuantizationPosition result = {}; - - result.bits = settings.pos_bits; - - Bounds b; - - for (size_t i = 0; i < meshes.size(); ++i) - { - updateAttributeBounds(meshes[i], cgltf_attribute_type_position, b); - } - - if (b.isValid()) - { - result.offset[0] = b.min.f[0]; - result.offset[1] = b.min.f[1]; - result.offset[2] = b.min.f[2]; - result.scale = std::max(b.max.f[0] - b.min.f[0], std::max(b.max.f[1] - b.min.f[1], b.max.f[2] - b.min.f[2])); - } - - return result; -} - -static size_t follow(std::vector& parents, size_t index) -{ - while (index != parents[index]) - { - size_t parent = parents[index]; - - parents[index] = parents[parent]; - index = parent; - } - - return index; -} - -void prepareQuantizationTexture(cgltf_data* data, std::vector& result, std::vector& indices, const std::vector& meshes, const Settings& settings) -{ - // use union-find to associate each material with a canonical material - // this is necessary because any set of materials that are used on the same mesh must use the same quantization - std::vector parents(result.size()); - - for (size_t i = 0; i < parents.size(); ++i) - parents[i] = i; - - for (size_t i = 0; i < meshes.size(); ++i) - { - const Mesh& mesh = meshes[i]; - - if (!mesh.material && mesh.variants.empty()) - continue; - - size_t root = follow(parents, (mesh.material ? mesh.material : mesh.variants[0].material) - data->materials); - - for (size_t j = 0; j < mesh.variants.size(); ++j) - { - size_t var = follow(parents, mesh.variants[j].material - data->materials); - - parents[var] = root; - } - - indices[i] = root; - } - - // compute canonical material bounds based on meshes that use them - std::vector bounds(result.size()); - - for (size_t i = 0; i < meshes.size(); ++i) - { - const Mesh& mesh = meshes[i]; - - if (!mesh.material && mesh.variants.empty()) - continue; - - indices[i] = follow(parents, indices[i]); - updateAttributeBounds(mesh, cgltf_attribute_type_texcoord, bounds[indices[i]]); - } - - // update all material data using canonical bounds - for (size_t i = 0; i < result.size(); ++i) - { - QuantizationTexture& qt = result[i]; - - qt.bits = settings.tex_bits; - - const Bounds& b = bounds[follow(parents, i)]; - - if (b.isValid()) - { - qt.offset[0] = b.min.f[0]; - qt.offset[1] = b.min.f[1]; - qt.scale[0] = b.max.f[0] - b.min.f[0]; - qt.scale[1] = b.max.f[1] - b.min.f[1]; - } - } -} - -void getPositionBounds(float min[3], float max[3], const Stream& stream, const QuantizationPosition* qp) -{ - assert(stream.type == cgltf_attribute_type_position); - assert(stream.data.size() > 0); - - min[0] = min[1] = min[2] = FLT_MAX; - max[0] = max[1] = max[2] = -FLT_MAX; - - for (size_t i = 0; i < stream.data.size(); ++i) - { - const Attr& a = stream.data[i]; - - for (int k = 0; k < 3; ++k) - { - min[k] = std::min(min[k], a.f[k]); - max[k] = std::max(max[k], a.f[k]); - } - } - - if (qp) - { - float pos_rscale = qp->scale == 0.f ? 0.f : 1.f / qp->scale; - - for (int k = 0; k < 3; ++k) - { - if (stream.target == 0) - { - min[k] = float(meshopt_quantizeUnorm((min[k] - qp->offset[k]) * pos_rscale, qp->bits)); - max[k] = float(meshopt_quantizeUnorm((max[k] - qp->offset[k]) * pos_rscale, qp->bits)); - } - else - { - min[k] = (min[k] >= 0.f ? 1.f : -1.f) * float(meshopt_quantizeUnorm(fabsf(min[k]) * pos_rscale, qp->bits)); - max[k] = (max[k] >= 0.f ? 1.f : -1.f) * float(meshopt_quantizeUnorm(fabsf(max[k]) * pos_rscale, qp->bits)); - } - } - } -} - -static void renormalizeWeights(uint8_t (&w)[4]) -{ - int sum = w[0] + w[1] + w[2] + w[3]; - - if (sum == 255) - return; - - // we assume that the total error is limited to 0.5/component = 2 - // this means that it's acceptable to adjust the max. component to compensate for the error - int max = 0; - - for (int k = 1; k < 4; ++k) - if (w[k] > w[max]) - max = k; - - w[max] += uint8_t(255 - sum); -} - -static void encodeOct(int& fu, int& fv, float nx, float ny, float nz, int bits) -{ - float nl = fabsf(nx) + fabsf(ny) + fabsf(nz); - float ns = nl == 0.f ? 0.f : 1.f / nl; - - nx *= ns; - ny *= ns; - - float u = (nz >= 0.f) ? nx : (1 - fabsf(ny)) * (nx >= 0.f ? 1.f : -1.f); - float v = (nz >= 0.f) ? ny : (1 - fabsf(nx)) * (ny >= 0.f ? 1.f : -1.f); - - fu = meshopt_quantizeSnorm(u, bits); - fv = meshopt_quantizeSnorm(v, bits); -} - -static StreamFormat writeVertexStreamRaw(std::string& bin, const Stream& stream, cgltf_type type, size_t components) -{ - assert(components >= 1 && components <= 4); - - for (size_t i = 0; i < stream.data.size(); ++i) - { - const Attr& a = stream.data[i]; - - bin.append(reinterpret_cast(a.f), sizeof(float) * components); - } - - StreamFormat format = {type, cgltf_component_type_r_32f, false, sizeof(float) * components}; - return format; -} - -static int quantizeColor(float v, int bytebits, int bits) -{ - int result = meshopt_quantizeUnorm(v, bytebits); - - // replicate the top bit into the low significant bits - const int mask = (1 << (bytebits - bits)) - 1; - - return (result & ~mask) | (mask & -(result >> (bytebits - 1))); -} - -StreamFormat writeVertexStream(std::string& bin, const Stream& stream, const QuantizationPosition& qp, const QuantizationTexture& qt, const Settings& settings) -{ - if (stream.type == cgltf_attribute_type_position) - { - if (!settings.quantize) - return writeVertexStreamRaw(bin, stream, cgltf_type_vec3, 3); - - if (stream.target == 0) - { - float pos_rscale = qp.scale == 0.f ? 0.f : 1.f / qp.scale; - - for (size_t i = 0; i < stream.data.size(); ++i) - { - const Attr& a = stream.data[i]; - - uint16_t v[4] = { - uint16_t(meshopt_quantizeUnorm((a.f[0] - qp.offset[0]) * pos_rscale, qp.bits)), - uint16_t(meshopt_quantizeUnorm((a.f[1] - qp.offset[1]) * pos_rscale, qp.bits)), - uint16_t(meshopt_quantizeUnorm((a.f[2] - qp.offset[2]) * pos_rscale, qp.bits)), - 0}; - bin.append(reinterpret_cast(v), sizeof(v)); - } - - StreamFormat format = {cgltf_type_vec3, cgltf_component_type_r_16u, false, 8}; - return format; - } - else - { - float pos_rscale = qp.scale == 0.f ? 0.f : 1.f / qp.scale; - - int maxv = 0; - - for (size_t i = 0; i < stream.data.size(); ++i) - { - const Attr& a = stream.data[i]; - - maxv = std::max(maxv, meshopt_quantizeUnorm(fabsf(a.f[0]) * pos_rscale, qp.bits)); - maxv = std::max(maxv, meshopt_quantizeUnorm(fabsf(a.f[1]) * pos_rscale, qp.bits)); - maxv = std::max(maxv, meshopt_quantizeUnorm(fabsf(a.f[2]) * pos_rscale, qp.bits)); - } - - if (maxv <= 127) - { - for (size_t i = 0; i < stream.data.size(); ++i) - { - const Attr& a = stream.data[i]; - - int8_t v[4] = { - int8_t((a.f[0] >= 0.f ? 1 : -1) * meshopt_quantizeUnorm(fabsf(a.f[0]) * pos_rscale, qp.bits)), - int8_t((a.f[1] >= 0.f ? 1 : -1) * meshopt_quantizeUnorm(fabsf(a.f[1]) * pos_rscale, qp.bits)), - int8_t((a.f[2] >= 0.f ? 1 : -1) * meshopt_quantizeUnorm(fabsf(a.f[2]) * pos_rscale, qp.bits)), - 0}; - bin.append(reinterpret_cast(v), sizeof(v)); - } - - StreamFormat format = {cgltf_type_vec3, cgltf_component_type_r_8, false, 4}; - return format; - } - else - { - for (size_t i = 0; i < stream.data.size(); ++i) - { - const Attr& a = stream.data[i]; - - int16_t v[4] = { - int16_t((a.f[0] >= 0.f ? 1 : -1) * meshopt_quantizeUnorm(fabsf(a.f[0]) * pos_rscale, qp.bits)), - int16_t((a.f[1] >= 0.f ? 1 : -1) * meshopt_quantizeUnorm(fabsf(a.f[1]) * pos_rscale, qp.bits)), - int16_t((a.f[2] >= 0.f ? 1 : -1) * meshopt_quantizeUnorm(fabsf(a.f[2]) * pos_rscale, qp.bits)), - 0}; - bin.append(reinterpret_cast(v), sizeof(v)); - } - - StreamFormat format = {cgltf_type_vec3, cgltf_component_type_r_16, false, 8}; - return format; - } - } - } - else if (stream.type == cgltf_attribute_type_texcoord) - { - if (!settings.quantize) - return writeVertexStreamRaw(bin, stream, cgltf_type_vec2, 2); - - float uv_rscale[2] = { - qt.scale[0] == 0.f ? 0.f : 1.f / qt.scale[0], - qt.scale[1] == 0.f ? 0.f : 1.f / qt.scale[1], - }; - - for (size_t i = 0; i < stream.data.size(); ++i) - { - const Attr& a = stream.data[i]; - - uint16_t v[2] = { - uint16_t(meshopt_quantizeUnorm((a.f[0] - qt.offset[0]) * uv_rscale[0], qt.bits)), - uint16_t(meshopt_quantizeUnorm((a.f[1] - qt.offset[1]) * uv_rscale[1], qt.bits)), - }; - bin.append(reinterpret_cast(v), sizeof(v)); - } - - StreamFormat format = {cgltf_type_vec2, cgltf_component_type_r_16u, false, 4}; - return format; - } - else if (stream.type == cgltf_attribute_type_normal) - { - if (!settings.quantize) - return writeVertexStreamRaw(bin, stream, cgltf_type_vec3, 3); - - bool oct = settings.compressmore && stream.target == 0; - int bits = settings.nrm_bits; - - StreamFormat::Filter filter = oct ? StreamFormat::Filter_Oct : StreamFormat::Filter_None; - - for (size_t i = 0; i < stream.data.size(); ++i) - { - const Attr& a = stream.data[i]; - - float nx = a.f[0], ny = a.f[1], nz = a.f[2]; - - if (bits > 8) - { - int16_t v[4]; - - if (oct) - { - int fu, fv; - encodeOct(fu, fv, nx, ny, nz, bits); - - v[0] = int16_t(fu); - v[1] = int16_t(fv); - v[2] = int16_t(meshopt_quantizeSnorm(1.f, bits)); - v[3] = 0; - } - else - { - v[0] = int16_t(meshopt_quantizeSnorm(nx, bits)); - v[1] = int16_t(meshopt_quantizeSnorm(ny, bits)); - v[2] = int16_t(meshopt_quantizeSnorm(nz, bits)); - v[3] = 0; - } - - bin.append(reinterpret_cast(v), sizeof(v)); - } - else - { - int8_t v[4]; - - if (oct) - { - int fu, fv; - encodeOct(fu, fv, nx, ny, nz, bits); - - v[0] = int8_t(fu); - v[1] = int8_t(fv); - v[2] = int8_t(meshopt_quantizeSnorm(1.f, bits)); - v[3] = 0; - } - else - { - v[0] = int8_t(meshopt_quantizeSnorm(nx, bits)); - v[1] = int8_t(meshopt_quantizeSnorm(ny, bits)); - v[2] = int8_t(meshopt_quantizeSnorm(nz, bits)); - v[3] = 0; - } - - bin.append(reinterpret_cast(v), sizeof(v)); - } - } - - if (bits > 8) - { - StreamFormat format = {cgltf_type_vec3, cgltf_component_type_r_16, true, 8, filter}; - return format; - } - else - { - StreamFormat format = {cgltf_type_vec3, cgltf_component_type_r_8, true, 4, filter}; - return format; - } - } - else if (stream.type == cgltf_attribute_type_tangent) - { - if (!settings.quantize) - return writeVertexStreamRaw(bin, stream, cgltf_type_vec4, 4); - - bool oct = settings.compressmore && stream.target == 0; - int bits = (settings.nrm_bits > 8) ? 8 : settings.nrm_bits; - - StreamFormat::Filter filter = oct ? StreamFormat::Filter_Oct : StreamFormat::Filter_None; - - for (size_t i = 0; i < stream.data.size(); ++i) - { - const Attr& a = stream.data[i]; - - float nx = a.f[0], ny = a.f[1], nz = a.f[2], nw = a.f[3]; - - int8_t v[4]; - - if (oct) - { - int fu, fv; - encodeOct(fu, fv, nx, ny, nz, bits); - - v[0] = int8_t(fu); - v[1] = int8_t(fv); - v[2] = int8_t(meshopt_quantizeSnorm(1.f, bits)); - v[3] = int8_t(meshopt_quantizeSnorm(nw, bits)); - } - else - { - v[0] = int8_t(meshopt_quantizeSnorm(nx, bits)); - v[1] = int8_t(meshopt_quantizeSnorm(ny, bits)); - v[2] = int8_t(meshopt_quantizeSnorm(nz, bits)); - v[3] = int8_t(meshopt_quantizeSnorm(nw, bits)); - } - - bin.append(reinterpret_cast(v), sizeof(v)); - } - - cgltf_type type = (stream.target == 0) ? cgltf_type_vec4 : cgltf_type_vec3; - - StreamFormat format = {type, cgltf_component_type_r_8, true, 4, filter}; - return format; - } - else if (stream.type == cgltf_attribute_type_color) - { - int bits = settings.col_bits; - - for (size_t i = 0; i < stream.data.size(); ++i) - { - const Attr& a = stream.data[i]; - - if (bits > 8) - { - uint16_t v[4] = { - uint16_t(quantizeColor(a.f[0], 16, bits)), - uint16_t(quantizeColor(a.f[1], 16, bits)), - uint16_t(quantizeColor(a.f[2], 16, bits)), - uint16_t(quantizeColor(a.f[3], 16, bits))}; - bin.append(reinterpret_cast(v), sizeof(v)); - } - else - { - uint8_t v[4] = { - uint8_t(quantizeColor(a.f[0], 8, bits)), - uint8_t(quantizeColor(a.f[1], 8, bits)), - uint8_t(quantizeColor(a.f[2], 8, bits)), - uint8_t(quantizeColor(a.f[3], 8, bits))}; - bin.append(reinterpret_cast(v), sizeof(v)); - } - } - - if (bits > 8) - { - StreamFormat format = {cgltf_type_vec4, cgltf_component_type_r_16u, true, 8}; - return format; - } - else - { - StreamFormat format = {cgltf_type_vec4, cgltf_component_type_r_8u, true, 4}; - return format; - } - } - else if (stream.type == cgltf_attribute_type_weights) - { - for (size_t i = 0; i < stream.data.size(); ++i) - { - const Attr& a = stream.data[i]; - - float ws = a.f[0] + a.f[1] + a.f[2] + a.f[3]; - float wsi = (ws == 0.f) ? 0.f : 1.f / ws; - - uint8_t v[4] = { - uint8_t(meshopt_quantizeUnorm(a.f[0] * wsi, 8)), - uint8_t(meshopt_quantizeUnorm(a.f[1] * wsi, 8)), - uint8_t(meshopt_quantizeUnorm(a.f[2] * wsi, 8)), - uint8_t(meshopt_quantizeUnorm(a.f[3] * wsi, 8))}; - - if (wsi != 0.f) - renormalizeWeights(v); - - bin.append(reinterpret_cast(v), sizeof(v)); - } - - StreamFormat format = {cgltf_type_vec4, cgltf_component_type_r_8u, true, 4}; - return format; - } - else if (stream.type == cgltf_attribute_type_joints) - { - unsigned int maxj = 0; - - for (size_t i = 0; i < stream.data.size(); ++i) - maxj = std::max(maxj, unsigned(stream.data[i].f[0])); - - assert(maxj <= 65535); - - if (maxj <= 255) - { - for (size_t i = 0; i < stream.data.size(); ++i) - { - const Attr& a = stream.data[i]; - - uint8_t v[4] = { - uint8_t(a.f[0]), - uint8_t(a.f[1]), - uint8_t(a.f[2]), - uint8_t(a.f[3])}; - bin.append(reinterpret_cast(v), sizeof(v)); - } - - StreamFormat format = {cgltf_type_vec4, cgltf_component_type_r_8u, false, 4}; - return format; - } - else - { - for (size_t i = 0; i < stream.data.size(); ++i) - { - const Attr& a = stream.data[i]; - - uint16_t v[4] = { - uint16_t(a.f[0]), - uint16_t(a.f[1]), - uint16_t(a.f[2]), - uint16_t(a.f[3])}; - bin.append(reinterpret_cast(v), sizeof(v)); - } - - StreamFormat format = {cgltf_type_vec4, cgltf_component_type_r_16u, false, 8}; - return format; - } - } - else - { - return writeVertexStreamRaw(bin, stream, cgltf_type_vec4, 4); - } -} - -StreamFormat writeIndexStream(std::string& bin, const std::vector& stream) -{ - unsigned int maxi = 0; - for (size_t i = 0; i < stream.size(); ++i) - maxi = std::max(maxi, stream[i]); - - // save 16-bit indices if we can; note that we can't use restart index (65535) - if (maxi < 65535) - { - for (size_t i = 0; i < stream.size(); ++i) - { - uint16_t v[1] = {uint16_t(stream[i])}; - bin.append(reinterpret_cast(v), sizeof(v)); - } - - StreamFormat format = {cgltf_type_scalar, cgltf_component_type_r_16u, false, 2}; - return format; - } - else - { - for (size_t i = 0; i < stream.size(); ++i) - { - uint32_t v[1] = {stream[i]}; - bin.append(reinterpret_cast(v), sizeof(v)); - } - - StreamFormat format = {cgltf_type_scalar, cgltf_component_type_r_32u, false, 4}; - return format; - } -} - -StreamFormat writeTimeStream(std::string& bin, const std::vector& data) -{ - for (size_t i = 0; i < data.size(); ++i) - { - float v[1] = {data[i]}; - bin.append(reinterpret_cast(v), sizeof(v)); - } - - StreamFormat format = {cgltf_type_scalar, cgltf_component_type_r_32f, false, 4}; - return format; -} - -static void encodeQuat(int16_t v[4], const Attr& a, int bits) -{ - const float scaler = sqrtf(2.f); - - // establish maximum quaternion component - int qc = 0; - qc = fabsf(a.f[1]) > fabsf(a.f[qc]) ? 1 : qc; - qc = fabsf(a.f[2]) > fabsf(a.f[qc]) ? 2 : qc; - qc = fabsf(a.f[3]) > fabsf(a.f[qc]) ? 3 : qc; - - // we use double-cover properties to discard the sign - float sign = a.f[qc] < 0.f ? -1.f : 1.f; - - // note: we always encode a cyclical swizzle to be able to recover the order via rotation - v[0] = int16_t(meshopt_quantizeSnorm(a.f[(qc + 1) & 3] * scaler * sign, bits)); - v[1] = int16_t(meshopt_quantizeSnorm(a.f[(qc + 2) & 3] * scaler * sign, bits)); - v[2] = int16_t(meshopt_quantizeSnorm(a.f[(qc + 3) & 3] * scaler * sign, bits)); - v[3] = int16_t((meshopt_quantizeSnorm(1.f, bits) & ~3) | qc); -} - -static void encodeExpShared(uint32_t v[3], const Attr& a, int bits) -{ - // get exponents from all components - int ex, ey, ez; - frexp(a.f[0], &ex); - frexp(a.f[1], &ey); - frexp(a.f[2], &ez); - - // use maximum exponent to encode values; this guarantess that mantissa is [-1, 1] - // note that we additionally scale the mantissa to make it a K-bit signed integer (K-1 bits for magnitude) - int exp = std::max(ex, std::max(ey, ez)) - (bits - 1); - - // compute renormalized rounded mantissas for each component - int mx = int(ldexp(a.f[0], -exp) + (a.f[0] >= 0 ? 0.5f : -0.5f)); - int my = int(ldexp(a.f[1], -exp) + (a.f[1] >= 0 ? 0.5f : -0.5f)); - int mz = int(ldexp(a.f[2], -exp) + (a.f[2] >= 0 ? 0.5f : -0.5f)); - - int mmask = (1 << 24) - 1; - - // encode exponent & mantissa into each resulting value - v[0] = (mx & mmask) | (unsigned(exp) << 24); - v[1] = (my & mmask) | (unsigned(exp) << 24); - v[2] = (mz & mmask) | (unsigned(exp) << 24); -} - -StreamFormat writeKeyframeStream(std::string& bin, cgltf_animation_path_type type, const std::vector& data, const Settings& settings) -{ - if (type == cgltf_animation_path_type_rotation) - { - StreamFormat::Filter filter = settings.compressmore ? StreamFormat::Filter_Quat : StreamFormat::Filter_None; - - for (size_t i = 0; i < data.size(); ++i) - { - const Attr& a = data[i]; - - int16_t v[4]; - - if (filter == StreamFormat::Filter_Quat) - { - encodeQuat(v, a, settings.rot_bits); - } - else - { - v[0] = int16_t(meshopt_quantizeSnorm(a.f[0], 16)); - v[1] = int16_t(meshopt_quantizeSnorm(a.f[1], 16)); - v[2] = int16_t(meshopt_quantizeSnorm(a.f[2], 16)); - v[3] = int16_t(meshopt_quantizeSnorm(a.f[3], 16)); - } - - bin.append(reinterpret_cast(v), sizeof(v)); - } - - StreamFormat format = {cgltf_type_vec4, cgltf_component_type_r_16, true, 8, filter}; - return format; - } - else if (type == cgltf_animation_path_type_weights) - { - for (size_t i = 0; i < data.size(); ++i) - { - const Attr& a = data[i]; - - uint8_t v[1] = {uint8_t(meshopt_quantizeUnorm(a.f[0], 8))}; - bin.append(reinterpret_cast(v), sizeof(v)); - } - - StreamFormat format = {cgltf_type_scalar, cgltf_component_type_r_8u, true, 1}; - return format; - } - else if (type == cgltf_animation_path_type_translation || type == cgltf_animation_path_type_scale) - { - StreamFormat::Filter filter = settings.compressmore ? StreamFormat::Filter_Exp : StreamFormat::Filter_None; - int bits = (type == cgltf_animation_path_type_translation) ? settings.trn_bits : settings.scl_bits; - - for (size_t i = 0; i < data.size(); ++i) - { - const Attr& a = data[i]; - - if (filter == StreamFormat::Filter_Exp) - { - uint32_t v[3]; - encodeExpShared(v, a, bits); - bin.append(reinterpret_cast(v), sizeof(v)); - } - else - { - float v[3] = {a.f[0], a.f[1], a.f[2]}; - bin.append(reinterpret_cast(v), sizeof(v)); - } - } - - StreamFormat format = {cgltf_type_vec3, cgltf_component_type_r_32f, false, 12, filter}; - return format; - } - else - { - for (size_t i = 0; i < data.size(); ++i) - { - const Attr& a = data[i]; - - float v[4] = {a.f[0], a.f[1], a.f[2], a.f[3]}; - bin.append(reinterpret_cast(v), sizeof(v)); - } - - StreamFormat format = {cgltf_type_vec4, cgltf_component_type_r_32f, false, 16}; - return format; - } -} - -void compressVertexStream(std::string& bin, const std::string& data, size_t count, size_t stride) -{ - assert(data.size() == count * stride); - - std::vector compressed(meshopt_encodeVertexBufferBound(count, stride)); - size_t size = meshopt_encodeVertexBuffer(&compressed[0], compressed.size(), data.c_str(), count, stride); - - bin.append(reinterpret_cast(&compressed[0]), size); -} - -void compressIndexStream(std::string& bin, const std::string& data, size_t count, size_t stride) -{ - assert(stride == 2 || stride == 4); - assert(data.size() == count * stride); - assert(count % 3 == 0); - - std::vector compressed(meshopt_encodeIndexBufferBound(count, count)); - size_t size = 0; - - if (stride == 2) - size = meshopt_encodeIndexBuffer(&compressed[0], compressed.size(), reinterpret_cast(data.c_str()), count); - else - size = meshopt_encodeIndexBuffer(&compressed[0], compressed.size(), reinterpret_cast(data.c_str()), count); - - bin.append(reinterpret_cast(&compressed[0]), size); -} - -void compressIndexSequence(std::string& bin, const std::string& data, size_t count, size_t stride) -{ - assert(stride == 2 || stride == 4); - assert(data.size() == count * stride); - - std::vector compressed(meshopt_encodeIndexSequenceBound(count, count)); - size_t size = 0; - - if (stride == 2) - size = meshopt_encodeIndexSequence(&compressed[0], compressed.size(), reinterpret_cast(data.c_str()), count); - else - size = meshopt_encodeIndexSequence(&compressed[0], compressed.size(), reinterpret_cast(data.c_str()), count); - - bin.append(reinterpret_cast(&compressed[0]), size); -} diff --git a/Dependencies/meshoptimizer/gltf/wasistubs.cpp b/Dependencies/meshoptimizer/gltf/wasistubs.cpp deleted file mode 100644 index 2573b11..0000000 --- a/Dependencies/meshoptimizer/gltf/wasistubs.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#ifdef __wasi__ -#include -#include - -#include - -extern "C" void __cxa_throw(void* ptr, void* type, void* destructor) -{ - abort(); -} - -extern "C" void* __cxa_allocate_exception(size_t thrown_size) -{ - abort(); -} - -extern "C" int32_t __wasi_path_open32(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, int32_t arg6, int32_t arg7, int32_t arg8) - __attribute__(( - __import_module__("wasi_snapshot_preview1"), - __import_name__("path_open32"), - __warn_unused_result__)); - -extern "C" int32_t __imported_wasi_snapshot_preview1_path_open(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int64_t arg5, int64_t arg6, int32_t arg7, int32_t arg8) -{ - return __wasi_path_open32(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); -} - -extern "C" int32_t __wasi_fd_seek32(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) - __attribute__(( - __import_module__("wasi_snapshot_preview1"), - __import_name__("fd_seek32"), - __warn_unused_result__)); - -extern "C" int32_t __imported_wasi_snapshot_preview1_fd_seek(int32_t arg0, int64_t arg1, int32_t arg2, int32_t arg3) -{ - *(uint64_t*)arg3 = 0; - return __wasi_fd_seek32(arg0, arg1, arg2, arg3); -} - -extern "C" int32_t __imported_wasi_snapshot_preview1_clock_time_get(int32_t arg0, int64_t arg1, int32_t arg2) -{ - return __WASI_ERRNO_NOSYS; -} - -#endif diff --git a/Dependencies/meshoptimizer/gltf/wasistubs.txt b/Dependencies/meshoptimizer/gltf/wasistubs.txt deleted file mode 100644 index dc29d46..0000000 --- a/Dependencies/meshoptimizer/gltf/wasistubs.txt +++ /dev/null @@ -1,2 +0,0 @@ -__wasi_path_open32 -__wasi_fd_seek32 diff --git a/Dependencies/meshoptimizer/gltf/write.cpp b/Dependencies/meshoptimizer/gltf/write.cpp deleted file mode 100644 index fbc3b4c..0000000 --- a/Dependencies/meshoptimizer/gltf/write.cpp +++ /dev/null @@ -1,1604 +0,0 @@ -// This file is part of gltfpack; see gltfpack.h for version/license details -#include "gltfpack.h" - -#include -#include -#include -#include - -static const char* componentType(cgltf_component_type type) -{ - switch (type) - { - case cgltf_component_type_r_8: - return "5120"; - case cgltf_component_type_r_8u: - return "5121"; - case cgltf_component_type_r_16: - return "5122"; - case cgltf_component_type_r_16u: - return "5123"; - case cgltf_component_type_r_32u: - return "5125"; - case cgltf_component_type_r_32f: - return "5126"; - default: - return "0"; - } -} - -static const char* shapeType(cgltf_type type) -{ - switch (type) - { - case cgltf_type_scalar: - return "SCALAR"; - case cgltf_type_vec2: - return "VEC2"; - case cgltf_type_vec3: - return "VEC3"; - case cgltf_type_vec4: - return "VEC4"; - case cgltf_type_mat2: - return "MAT2"; - case cgltf_type_mat3: - return "MAT3"; - case cgltf_type_mat4: - return "MAT4"; - default: - return ""; - } -} - -const char* attributeType(cgltf_attribute_type type) -{ - switch (type) - { - case cgltf_attribute_type_position: - return "POSITION"; - case cgltf_attribute_type_normal: - return "NORMAL"; - case cgltf_attribute_type_tangent: - return "TANGENT"; - case cgltf_attribute_type_texcoord: - return "TEXCOORD"; - case cgltf_attribute_type_color: - return "COLOR"; - case cgltf_attribute_type_joints: - return "JOINTS"; - case cgltf_attribute_type_weights: - return "WEIGHTS"; - default: - return "ATTRIBUTE"; - } -} - -const char* animationPath(cgltf_animation_path_type type) -{ - switch (type) - { - case cgltf_animation_path_type_translation: - return "translation"; - case cgltf_animation_path_type_rotation: - return "rotation"; - case cgltf_animation_path_type_scale: - return "scale"; - case cgltf_animation_path_type_weights: - return "weights"; - default: - return ""; - } -} - -static const char* lightType(cgltf_light_type type) -{ - switch (type) - { - case cgltf_light_type_directional: - return "directional"; - case cgltf_light_type_point: - return "point"; - case cgltf_light_type_spot: - return "spot"; - default: - return ""; - } -} - -static const char* alphaMode(cgltf_alpha_mode mode) -{ - switch (mode) - { - case cgltf_alpha_mode_opaque: - return "OPAQUE"; - - case cgltf_alpha_mode_mask: - return "MASK"; - - case cgltf_alpha_mode_blend: - return "BLEND"; - - default: - return ""; - } -} - -static const char* compressionMode(BufferView::Compression mode) -{ - switch (mode) - { - case BufferView::Compression_Attribute: - return "ATTRIBUTES"; - - case BufferView::Compression_Index: - return "TRIANGLES"; - - case BufferView::Compression_IndexSequence: - return "INDICES"; - - default: - return ""; - } -} - -static const char* compressionFilter(StreamFormat::Filter filter) -{ - switch (filter) - { - case StreamFormat::Filter_None: - return "NONE"; - - case StreamFormat::Filter_Oct: - return "OCTAHEDRAL"; - - case StreamFormat::Filter_Quat: - return "QUATERNION"; - - case StreamFormat::Filter_Exp: - return "EXPONENTIAL"; - - default: - return ""; - } -} - -static void writeTextureInfo(std::string& json, const cgltf_data* data, const cgltf_texture_view& view, const QuantizationTexture* qt, const char* scale = NULL) -{ - assert(view.texture); - - bool has_transform = false; - cgltf_texture_transform transform = {}; - transform.scale[0] = transform.scale[1] = 1.f; - - if (hasValidTransform(view)) - { - transform = view.transform; - has_transform = true; - } - - if (qt) - { - transform.offset[0] += qt->offset[0]; - transform.offset[1] += qt->offset[1]; - transform.scale[0] *= qt->scale[0] / float((1 << qt->bits) - 1); - transform.scale[1] *= qt->scale[1] / float((1 << qt->bits) - 1); - has_transform = true; - } - - append(json, "{\"index\":"); - append(json, size_t(view.texture - data->textures)); - if (view.texcoord != 0) - { - append(json, ",\"texCoord\":"); - append(json, size_t(view.texcoord)); - } - if (scale && view.scale != 1) - { - append(json, ",\""); - append(json, scale); - append(json, "\":"); - append(json, view.scale); - } - if (has_transform) - { - append(json, ",\"extensions\":{\"KHR_texture_transform\":{"); - append(json, "\"offset\":["); - append(json, transform.offset[0]); - append(json, ","); - append(json, transform.offset[1]); - append(json, "],\"scale\":["); - append(json, transform.scale[0]); - append(json, ","); - append(json, transform.scale[1]); - append(json, "]"); - if (transform.rotation != 0.f) - { - append(json, ",\"rotation\":"); - append(json, transform.rotation); - } - append(json, "}}"); - } - append(json, "}"); -} - -static const float white[4] = {1, 1, 1, 1}; -static const float black[4] = {0, 0, 0, 0}; - -static void writeMaterialComponent(std::string& json, const cgltf_data* data, const cgltf_pbr_metallic_roughness& pbr, const QuantizationTexture* qt) -{ - comma(json); - append(json, "\"pbrMetallicRoughness\":{"); - if (memcmp(pbr.base_color_factor, white, 16) != 0) - { - comma(json); - append(json, "\"baseColorFactor\":["); - append(json, pbr.base_color_factor[0]); - append(json, ","); - append(json, pbr.base_color_factor[1]); - append(json, ","); - append(json, pbr.base_color_factor[2]); - append(json, ","); - append(json, pbr.base_color_factor[3]); - append(json, "]"); - } - if (pbr.base_color_texture.texture) - { - comma(json); - append(json, "\"baseColorTexture\":"); - writeTextureInfo(json, data, pbr.base_color_texture, qt); - } - if (pbr.metallic_factor != 1) - { - comma(json); - append(json, "\"metallicFactor\":"); - append(json, pbr.metallic_factor); - } - if (pbr.roughness_factor != 1) - { - comma(json); - append(json, "\"roughnessFactor\":"); - append(json, pbr.roughness_factor); - } - if (pbr.metallic_roughness_texture.texture) - { - comma(json); - append(json, "\"metallicRoughnessTexture\":"); - writeTextureInfo(json, data, pbr.metallic_roughness_texture, qt); - } - append(json, "}"); -} - -static void writeMaterialComponent(std::string& json, const cgltf_data* data, const cgltf_pbr_specular_glossiness& pbr, const QuantizationTexture* qt) -{ - comma(json); - append(json, "\"KHR_materials_pbrSpecularGlossiness\":{"); - if (pbr.diffuse_texture.texture) - { - comma(json); - append(json, "\"diffuseTexture\":"); - writeTextureInfo(json, data, pbr.diffuse_texture, qt); - } - if (pbr.specular_glossiness_texture.texture) - { - comma(json); - append(json, "\"specularGlossinessTexture\":"); - writeTextureInfo(json, data, pbr.specular_glossiness_texture, qt); - } - if (memcmp(pbr.diffuse_factor, white, 16) != 0) - { - comma(json); - append(json, "\"diffuseFactor\":["); - append(json, pbr.diffuse_factor[0]); - append(json, ","); - append(json, pbr.diffuse_factor[1]); - append(json, ","); - append(json, pbr.diffuse_factor[2]); - append(json, ","); - append(json, pbr.diffuse_factor[3]); - append(json, "]"); - } - if (memcmp(pbr.specular_factor, white, 12) != 0) - { - comma(json); - append(json, "\"specularFactor\":["); - append(json, pbr.specular_factor[0]); - append(json, ","); - append(json, pbr.specular_factor[1]); - append(json, ","); - append(json, pbr.specular_factor[2]); - append(json, "]"); - } - if (pbr.glossiness_factor != 1) - { - comma(json); - append(json, "\"glossinessFactor\":"); - append(json, pbr.glossiness_factor); - } - append(json, "}"); -} - -static void writeMaterialComponent(std::string& json, const cgltf_data* data, const cgltf_clearcoat& cc, const QuantizationTexture* qt) -{ - comma(json); - append(json, "\"KHR_materials_clearcoat\":{"); - if (cc.clearcoat_texture.texture) - { - comma(json); - append(json, "\"clearcoatTexture\":"); - writeTextureInfo(json, data, cc.clearcoat_texture, qt); - } - if (cc.clearcoat_roughness_texture.texture) - { - comma(json); - append(json, "\"clearcoatRoughnessTexture\":"); - writeTextureInfo(json, data, cc.clearcoat_roughness_texture, qt); - } - if (cc.clearcoat_normal_texture.texture) - { - comma(json); - append(json, "\"clearcoatNormalTexture\":"); - writeTextureInfo(json, data, cc.clearcoat_normal_texture, qt, "scale"); - } - if (cc.clearcoat_factor != 0) - { - comma(json); - append(json, "\"clearcoatFactor\":"); - append(json, cc.clearcoat_factor); - } - if (cc.clearcoat_factor != 0) - { - comma(json); - append(json, "\"clearcoatRoughnessFactor\":"); - append(json, cc.clearcoat_roughness_factor); - } - append(json, "}"); -} - -static void writeMaterialComponent(std::string& json, const cgltf_data* data, const cgltf_transmission& tm, const QuantizationTexture* qt) -{ - comma(json); - append(json, "\"KHR_materials_transmission\":{"); - if (tm.transmission_texture.texture) - { - comma(json); - append(json, "\"transmissionTexture\":"); - writeTextureInfo(json, data, tm.transmission_texture, qt); - } - if (tm.transmission_factor != 0) - { - comma(json); - append(json, "\"transmissionFactor\":"); - append(json, tm.transmission_factor); - } - append(json, "}"); -} - -static void writeMaterialComponent(std::string& json, const cgltf_data* data, const cgltf_ior& tm, const QuantizationTexture* qt) -{ - (void)data; - (void)qt; - - comma(json); - append(json, "\"KHR_materials_ior\":{"); - append(json, "\"ior\":"); - append(json, tm.ior); - append(json, "}"); -} - -static void writeMaterialComponent(std::string& json, const cgltf_data* data, const cgltf_specular& tm, const QuantizationTexture* qt) -{ - comma(json); - append(json, "\"KHR_materials_specular\":{"); - if (tm.specular_texture.texture) - { - comma(json); - append(json, "\"specularTexture\":"); - writeTextureInfo(json, data, tm.specular_texture, qt); - } - if (tm.specular_color_texture.texture) - { - comma(json); - append(json, "\"specularColorTexture\":"); - writeTextureInfo(json, data, tm.specular_color_texture, qt); - } - if (tm.specular_factor != 1) - { - comma(json); - append(json, "\"specularFactor\":"); - append(json, tm.specular_factor); - } - if (memcmp(tm.specular_color_factor, white, 12) != 0) - { - comma(json); - append(json, "\"specularColorFactor\":["); - append(json, tm.specular_color_factor[0]); - append(json, ","); - append(json, tm.specular_color_factor[1]); - append(json, ","); - append(json, tm.specular_color_factor[2]); - append(json, "]"); - } - append(json, "}"); -} - -static void writeMaterialComponent(std::string& json, const cgltf_data* data, const cgltf_sheen& tm, const QuantizationTexture* qt) -{ - comma(json); - append(json, "\"KHR_materials_sheen\":{"); - if (tm.sheen_color_texture.texture) - { - comma(json); - append(json, "\"sheenColorTexture\":"); - writeTextureInfo(json, data, tm.sheen_color_texture, qt); - } - if (tm.sheen_roughness_texture.texture) - { - comma(json); - append(json, "\"sheenRoughnessTexture\":"); - writeTextureInfo(json, data, tm.sheen_roughness_texture, qt); - } - if (memcmp(tm.sheen_color_factor, black, 12) != 0) - { - comma(json); - append(json, "\"sheenColorFactor\":["); - append(json, tm.sheen_color_factor[0]); - append(json, ","); - append(json, tm.sheen_color_factor[1]); - append(json, ","); - append(json, tm.sheen_color_factor[2]); - append(json, "]"); - } - if (tm.sheen_roughness_factor != 0) - { - comma(json); - append(json, "\"sheenRoughnessFactor\":"); - append(json, tm.sheen_roughness_factor); - } - append(json, "}"); -} - -static void writeMaterialComponent(std::string& json, const cgltf_data* data, const cgltf_volume& tm, const QuantizationPosition* qp, const QuantizationTexture* qt) -{ - comma(json); - append(json, "\"KHR_materials_volume\":{"); - if (tm.thickness_texture.texture) - { - comma(json); - append(json, "\"thicknessTexture\":"); - writeTextureInfo(json, data, tm.thickness_texture, qt); - } - if (tm.thickness_factor != 0) - { - // thickness is in mesh coordinate space which is rescaled by quantization - float node_scale = qp ? qp->scale / float((1 << qp->bits) - 1) : 1.f; - - comma(json); - append(json, "\"thicknessFactor\":"); - append(json, tm.thickness_factor / node_scale); - } - if (memcmp(tm.attenuation_color, white, 12) != 0) - { - comma(json); - append(json, "\"attenuationColor\":["); - append(json, tm.attenuation_color[0]); - append(json, ","); - append(json, tm.attenuation_color[1]); - append(json, ","); - append(json, tm.attenuation_color[2]); - append(json, "]"); - } - if (tm.attenuation_distance != FLT_MAX) - { - comma(json); - append(json, "\"attenuationDistance\":"); - append(json, tm.attenuation_distance); - } - append(json, "}"); -} - -static void writeMaterialComponent(std::string& json, const cgltf_data* data, const cgltf_emissive_strength& tm) -{ - (void)data; - - comma(json); - append(json, "\"KHR_materials_emissive_strength\":{"); - if (tm.emissive_strength != 1) - { - comma(json); - append(json, "\"emissiveStrength\":"); - append(json, tm.emissive_strength); - } - append(json, "}"); -} - -static void writeMaterialComponent(std::string& json, const cgltf_data* data, const cgltf_iridescence& tm, const QuantizationTexture* qt) -{ - comma(json); - append(json, "\"KHR_materials_iridescence\":{"); - if (tm.iridescence_factor != 0) - { - comma(json); - append(json, "\"iridescenceFactor\":"); - append(json, tm.iridescence_factor); - } - if (tm.iridescence_texture.texture) - { - comma(json); - append(json, "\"iridescenceTexture\":"); - writeTextureInfo(json, data, tm.iridescence_texture, qt); - } - if (tm.iridescence_ior != 1.3f) - { - comma(json); - append(json, "\"iridescenceIor\":"); - append(json, tm.iridescence_ior); - } - if (tm.iridescence_thickness_min != 100.f) - { - comma(json); - append(json, "\"iridescenceThicknessMinimum\":"); - append(json, tm.iridescence_thickness_min); - } - if (tm.iridescence_thickness_max != 400.f) - { - comma(json); - append(json, "\"iridescenceThicknessMaximum\":"); - append(json, tm.iridescence_thickness_max); - } - if (tm.iridescence_thickness_texture.texture) - { - comma(json); - append(json, "\"iridescenceThicknessTexture\":"); - writeTextureInfo(json, data, tm.iridescence_thickness_texture, qt); - } - append(json, "}"); -} - -void writeMaterial(std::string& json, const cgltf_data* data, const cgltf_material& material, const QuantizationPosition* qp, const QuantizationTexture* qt) -{ - if (material.name && *material.name) - { - comma(json); - append(json, "\"name\":\""); - append(json, material.name); - append(json, "\""); - } - - if (material.has_pbr_metallic_roughness) - { - writeMaterialComponent(json, data, material.pbr_metallic_roughness, qt); - } - - if (material.normal_texture.texture) - { - comma(json); - append(json, "\"normalTexture\":"); - writeTextureInfo(json, data, material.normal_texture, qt, "scale"); - } - - if (material.occlusion_texture.texture) - { - comma(json); - append(json, "\"occlusionTexture\":"); - writeTextureInfo(json, data, material.occlusion_texture, qt, "strength"); - } - - if (material.emissive_texture.texture) - { - comma(json); - append(json, "\"emissiveTexture\":"); - writeTextureInfo(json, data, material.emissive_texture, qt); - } - - if (memcmp(material.emissive_factor, black, 12) != 0) - { - comma(json); - append(json, "\"emissiveFactor\":["); - append(json, material.emissive_factor[0]); - append(json, ","); - append(json, material.emissive_factor[1]); - append(json, ","); - append(json, material.emissive_factor[2]); - append(json, "]"); - } - - if (material.alpha_mode != cgltf_alpha_mode_opaque) - { - comma(json); - append(json, "\"alphaMode\":\""); - append(json, alphaMode(material.alpha_mode)); - append(json, "\""); - } - - if (material.alpha_cutoff != 0.5f) - { - comma(json); - append(json, "\"alphaCutoff\":"); - append(json, material.alpha_cutoff); - } - - if (material.double_sided) - { - comma(json); - append(json, "\"doubleSided\":true"); - } - - if (material.has_pbr_specular_glossiness || material.has_clearcoat || material.has_transmission || material.has_ior || material.has_specular || material.has_sheen || material.has_volume || material.has_emissive_strength || material.has_iridescence || material.unlit) - { - comma(json); - append(json, "\"extensions\":{"); - - if (material.has_pbr_specular_glossiness) - { - writeMaterialComponent(json, data, material.pbr_specular_glossiness, qt); - } - - if (material.has_clearcoat) - { - writeMaterialComponent(json, data, material.clearcoat, qt); - } - - if (material.has_transmission) - { - writeMaterialComponent(json, data, material.transmission, qt); - } - - if (material.has_ior) - { - writeMaterialComponent(json, data, material.ior, qt); - } - - if (material.has_specular) - { - writeMaterialComponent(json, data, material.specular, qt); - } - - if (material.has_sheen) - { - writeMaterialComponent(json, data, material.sheen, qt); - } - - if (material.has_volume) - { - writeMaterialComponent(json, data, material.volume, qp, qt); - } - - if (material.has_emissive_strength) - { - writeMaterialComponent(json, data, material.emissive_strength); - } - - if (material.has_iridescence) - { - writeMaterialComponent(json, data, material.iridescence, qt); - } - - if (material.unlit) - { - comma(json); - append(json, "\"KHR_materials_unlit\":{}"); - } - - append(json, "}"); - } -} - -size_t getBufferView(std::vector& views, BufferView::Kind kind, StreamFormat::Filter filter, BufferView::Compression compression, size_t stride, int variant) -{ - if (variant >= 0) - { - for (size_t i = 0; i < views.size(); ++i) - { - BufferView& v = views[i]; - - if (v.kind == kind && v.filter == filter && v.compression == compression && v.stride == stride && v.variant == variant) - return i; - } - } - - BufferView view = {kind, filter, compression, stride, variant}; - views.push_back(view); - - return views.size() - 1; -} - -void writeBufferView(std::string& json, BufferView::Kind kind, StreamFormat::Filter filter, size_t count, size_t stride, size_t bin_offset, size_t bin_size, BufferView::Compression compression, size_t compressed_offset, size_t compressed_size) -{ - assert(bin_size == count * stride); - - // when compression is enabled, we store uncompressed data in buffer 1 and compressed data in buffer 0 - // when compression is disabled, we store uncompressed data in buffer 0 - size_t buffer = compression != BufferView::Compression_None ? 1 : 0; - - append(json, "{\"buffer\":"); - append(json, buffer); - append(json, ",\"byteOffset\":"); - append(json, bin_offset); - append(json, ",\"byteLength\":"); - append(json, bin_size); - if (kind == BufferView::Kind_Vertex) - { - append(json, ",\"byteStride\":"); - append(json, stride); - } - if (kind == BufferView::Kind_Vertex || kind == BufferView::Kind_Index) - { - append(json, ",\"target\":"); - append(json, (kind == BufferView::Kind_Vertex) ? "34962" : "34963"); - } - if (compression != BufferView::Compression_None) - { - append(json, ",\"extensions\":{"); - append(json, "\"EXT_meshopt_compression\":{"); - append(json, "\"buffer\":0"); - append(json, ",\"byteOffset\":"); - append(json, size_t(compressed_offset)); - append(json, ",\"byteLength\":"); - append(json, size_t(compressed_size)); - append(json, ",\"byteStride\":"); - append(json, stride); - append(json, ",\"mode\":\""); - append(json, compressionMode(compression)); - append(json, "\""); - if (filter != StreamFormat::Filter_None) - { - append(json, ",\"filter\":\""); - append(json, compressionFilter(filter)); - append(json, "\""); - } - append(json, ",\"count\":"); - append(json, count); - append(json, "}}"); - } - append(json, "}"); -} - -static void writeAccessor(std::string& json, size_t view, size_t offset, cgltf_type type, cgltf_component_type component_type, bool normalized, size_t count, const float* min = 0, const float* max = 0, size_t numminmax = 0) -{ - append(json, "{\"bufferView\":"); - append(json, view); - append(json, ",\"byteOffset\":"); - append(json, offset); - append(json, ",\"componentType\":"); - append(json, componentType(component_type)); - append(json, ",\"count\":"); - append(json, count); - append(json, ",\"type\":\""); - append(json, shapeType(type)); - append(json, "\""); - - if (normalized) - { - append(json, ",\"normalized\":true"); - } - - if (min && max) - { - assert(numminmax); - - append(json, ",\"min\":["); - for (size_t k = 0; k < numminmax; ++k) - { - comma(json); - append(json, min[k]); - } - append(json, "],\"max\":["); - for (size_t k = 0; k < numminmax; ++k) - { - comma(json); - append(json, max[k]); - } - append(json, "]"); - } - - append(json, "}"); -} - -static void writeEmbeddedImage(std::string& json, std::vector& views, const char* data, size_t size, const char* mime_type, TextureKind kind) -{ - size_t view = getBufferView(views, BufferView::Kind_Image, StreamFormat::Filter_None, BufferView::Compression_None, 1, -1 - kind); - - assert(views[view].data.empty()); - views[view].data.assign(data, size); - - append(json, "\"bufferView\":"); - append(json, view); - append(json, ",\"mimeType\":\""); - append(json, mime_type); - append(json, "\""); -} - -static std::string decodeUri(const char* uri) -{ - std::string result = uri; - - if (!result.empty()) - { - cgltf_decode_uri(&result[0]); - result.resize(strlen(result.c_str())); - } - - return result; -} - -void writeSampler(std::string& json, const cgltf_sampler& sampler) -{ - if (sampler.mag_filter != 0) - { - comma(json); - append(json, "\"magFilter\":"); - append(json, size_t(sampler.mag_filter)); - } - if (sampler.min_filter != 0) - { - comma(json); - append(json, "\"minFilter\":"); - append(json, size_t(sampler.min_filter)); - } - if (sampler.wrap_s != 10497) - { - comma(json); - append(json, "\"wrapS\":"); - append(json, size_t(sampler.wrap_s)); - } - if (sampler.wrap_t != 10497) - { - comma(json); - append(json, "\"wrapT\":"); - append(json, size_t(sampler.wrap_t)); - } -} - -void writeImage(std::string& json, std::vector& views, const cgltf_image& image, const ImageInfo& info, size_t index, const char* input_path, const Settings& settings) -{ - bool dataUri = image.uri && strncmp(image.uri, "data:", 5) == 0; - - if (image.uri && !dataUri && !settings.texture_embed && !settings.texture_ktx2) - { - // fast-path: we don't need to read the image to memory - append(json, "\"uri\":\""); - append(json, image.uri); - append(json, "\""); - return; - } - - std::string img_data; - std::string mime_type; - if (!readImage(image, input_path, img_data, mime_type)) - { - fprintf(stderr, "Warning: unable to read image %d (%s), skipping\n", int(index), image.uri ? image.uri : "?"); - return; - } - - writeEmbeddedImage(json, views, img_data.c_str(), img_data.size(), mime_type.c_str(), info.kind); -} - -void writeEncodedImage(std::string& json, std::vector& views, const cgltf_image& image, const std::string& encoded, const ImageInfo& info, const char* output_path, const Settings& settings) -{ - bool dataUri = image.uri && strncmp(image.uri, "data:", 5) == 0; - - if (!settings.texture_embed && image.uri && !dataUri && output_path) - { - std::string ktx_uri = getFileName(image.uri) + ".ktx2"; - std::string ktx_full_path = getFullPath(decodeUri(ktx_uri.c_str()).c_str(), output_path); - - if (writeFile(ktx_full_path.c_str(), encoded)) - { - append(json, "\"uri\":\""); - append(json, ktx_uri); - append(json, "\""); - } - else - { - fprintf(stderr, "Warning: unable to save encoded image %s, skipping\n", image.uri); - } - } - else - { - writeEmbeddedImage(json, views, encoded.c_str(), encoded.size(), "image/ktx2", info.kind); - } -} - -void writeTexture(std::string& json, const cgltf_texture& texture, cgltf_data* data, const Settings& settings) -{ - if (texture.image) - { - if (texture.sampler) - { - append(json, "\"sampler\":"); - append(json, size_t(texture.sampler - data->samplers)); - append(json, ","); - } - - if (settings.texture_ktx2) - { - append(json, "\"extensions\":{\"KHR_texture_basisu\":{\"source\":"); - append(json, size_t(texture.image - data->images)); - append(json, "}}"); - } - else - { - append(json, "\"source\":"); - append(json, size_t(texture.image - data->images)); - } - } -} - -void writeMeshAttributes(std::string& json, std::vector& views, std::string& json_accessors, size_t& accr_offset, const Mesh& mesh, int target, const QuantizationPosition& qp, const QuantizationTexture& qt, const Settings& settings) -{ - std::string scratch; - - for (size_t j = 0; j < mesh.streams.size(); ++j) - { - const Stream& stream = mesh.streams[j]; - - if (stream.target != target) - continue; - - scratch.clear(); - StreamFormat format = writeVertexStream(scratch, stream, qp, qt, settings); - BufferView::Compression compression = settings.compress ? BufferView::Compression_Attribute : BufferView::Compression_None; - - size_t view = getBufferView(views, BufferView::Kind_Vertex, format.filter, compression, format.stride, stream.type); - size_t offset = views[view].data.size(); - views[view].data += scratch; - - comma(json_accessors); - if (stream.type == cgltf_attribute_type_position) - { - float min[3] = {}; - float max[3] = {}; - getPositionBounds(min, max, stream, settings.quantize ? &qp : NULL); - - writeAccessor(json_accessors, view, offset, format.type, format.component_type, format.normalized, stream.data.size(), min, max, 3); - } - else - { - writeAccessor(json_accessors, view, offset, format.type, format.component_type, format.normalized, stream.data.size()); - } - - size_t vertex_accr = accr_offset++; - - comma(json); - append(json, "\""); - append(json, attributeType(stream.type)); - if (stream.type != cgltf_attribute_type_position && stream.type != cgltf_attribute_type_normal && stream.type != cgltf_attribute_type_tangent) - { - append(json, "_"); - append(json, size_t(stream.index)); - } - append(json, "\":"); - append(json, vertex_accr); - } -} - -size_t writeMeshIndices(std::vector& views, std::string& json_accessors, size_t& accr_offset, const Mesh& mesh, const Settings& settings) -{ - std::string scratch; - StreamFormat format = writeIndexStream(scratch, mesh.indices); - BufferView::Compression compression = settings.compress ? (mesh.type == cgltf_primitive_type_triangles ? BufferView::Compression_Index : BufferView::Compression_IndexSequence) : BufferView::Compression_None; - - size_t view = getBufferView(views, BufferView::Kind_Index, StreamFormat::Filter_None, compression, format.stride); - size_t offset = views[view].data.size(); - views[view].data += scratch; - - comma(json_accessors); - writeAccessor(json_accessors, view, offset, format.type, format.component_type, format.normalized, mesh.indices.size()); - - size_t index_accr = accr_offset++; - - return index_accr; -} - -static size_t writeAnimationTime(std::vector& views, std::string& json_accessors, size_t& accr_offset, float mint, int frames, float period, const Settings& settings) -{ - std::vector time(frames); - - for (int j = 0; j < frames; ++j) - time[j] = mint + float(j) * period; - - std::string scratch; - StreamFormat format = writeTimeStream(scratch, time); - BufferView::Compression compression = settings.compress ? BufferView::Compression_Attribute : BufferView::Compression_None; - - size_t view = getBufferView(views, BufferView::Kind_Time, StreamFormat::Filter_None, compression, format.stride); - size_t offset = views[view].data.size(); - views[view].data += scratch; - - comma(json_accessors); - writeAccessor(json_accessors, view, offset, cgltf_type_scalar, format.component_type, format.normalized, frames, &time.front(), &time.back(), 1); - - size_t time_accr = accr_offset++; - - return time_accr; -} - -size_t writeJointBindMatrices(std::vector& views, std::string& json_accessors, size_t& accr_offset, const cgltf_skin& skin, const QuantizationPosition& qp, const Settings& settings) -{ - std::string scratch; - - for (size_t j = 0; j < skin.joints_count; ++j) - { - float transform[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; - - if (skin.inverse_bind_matrices) - { - cgltf_accessor_read_float(skin.inverse_bind_matrices, j, transform, 16); - } - - if (settings.quantize) - { - float node_scale = qp.scale / float((1 << qp.bits) - 1); - - // pos_offset has to be applied first, thus it results in an offset rotated by the bind matrix - transform[12] += qp.offset[0] * transform[0] + qp.offset[1] * transform[4] + qp.offset[2] * transform[8]; - transform[13] += qp.offset[0] * transform[1] + qp.offset[1] * transform[5] + qp.offset[2] * transform[9]; - transform[14] += qp.offset[0] * transform[2] + qp.offset[1] * transform[6] + qp.offset[2] * transform[10]; - - // node_scale will be applied before the rotation/scale from transform - for (int k = 0; k < 12; ++k) - transform[k] *= node_scale; - } - - scratch.append(reinterpret_cast(transform), sizeof(transform)); - } - - BufferView::Compression compression = settings.compress ? BufferView::Compression_Attribute : BufferView::Compression_None; - - size_t view = getBufferView(views, BufferView::Kind_Skin, StreamFormat::Filter_None, compression, 64); - size_t offset = views[view].data.size(); - views[view].data += scratch; - - comma(json_accessors); - writeAccessor(json_accessors, view, offset, cgltf_type_mat4, cgltf_component_type_r_32f, false, skin.joints_count); - - size_t matrix_accr = accr_offset++; - - return matrix_accr; -} - -static void writeInstanceData(std::vector& views, std::string& json_accessors, cgltf_animation_path_type type, const std::vector& data, const Settings& settings) -{ - BufferView::Compression compression = settings.compress ? BufferView::Compression_Attribute : BufferView::Compression_None; - - std::string scratch; - StreamFormat format = writeKeyframeStream(scratch, type, data, settings); - - size_t view = getBufferView(views, BufferView::Kind_Instance, format.filter, compression, format.stride, type); - size_t offset = views[view].data.size(); - views[view].data += scratch; - - comma(json_accessors); - writeAccessor(json_accessors, view, offset, format.type, format.component_type, format.normalized, data.size()); -} - -size_t writeInstances(std::vector& views, std::string& json_accessors, size_t& accr_offset, const std::vector& transforms, const QuantizationPosition& qp, const Settings& settings) -{ - std::vector position, rotation, scale; - position.resize(transforms.size()); - rotation.resize(transforms.size()); - scale.resize(transforms.size()); - - for (size_t i = 0; i < transforms.size(); ++i) - { - decomposeTransform(position[i].f, rotation[i].f, scale[i].f, transforms[i].data); - - if (settings.quantize) - { - const float* transform = transforms[i].data; - - float node_scale = qp.scale / float((1 << qp.bits) - 1); - - // pos_offset has to be applied first, thus it results in an offset rotated by the instance matrix - position[i].f[0] += qp.offset[0] * transform[0] + qp.offset[1] * transform[4] + qp.offset[2] * transform[8]; - position[i].f[1] += qp.offset[0] * transform[1] + qp.offset[1] * transform[5] + qp.offset[2] * transform[9]; - position[i].f[2] += qp.offset[0] * transform[2] + qp.offset[1] * transform[6] + qp.offset[2] * transform[10]; - - // node_scale will be applied before the rotation/scale from transform - scale[i].f[0] *= node_scale; - scale[i].f[1] *= node_scale; - scale[i].f[2] *= node_scale; - } - } - - writeInstanceData(views, json_accessors, cgltf_animation_path_type_translation, position, settings); - writeInstanceData(views, json_accessors, cgltf_animation_path_type_rotation, rotation, settings); - writeInstanceData(views, json_accessors, cgltf_animation_path_type_scale, scale, settings); - - size_t result = accr_offset; - accr_offset += 3; - return result; -} - -void writeMeshNode(std::string& json, size_t mesh_offset, cgltf_node* node, cgltf_skin* skin, cgltf_data* data, const QuantizationPosition* qp) -{ - comma(json); - append(json, "{\"mesh\":"); - append(json, mesh_offset); - if (skin) - { - comma(json); - append(json, "\"skin\":"); - append(json, size_t(skin - data->skins)); - } - if (qp) - { - float node_scale = qp->scale / float((1 << qp->bits) - 1); - - append(json, ",\"translation\":["); - append(json, qp->offset[0]); - append(json, ","); - append(json, qp->offset[1]); - append(json, ","); - append(json, qp->offset[2]); - append(json, "],\"scale\":["); - append(json, node_scale); - append(json, ","); - append(json, node_scale); - append(json, ","); - append(json, node_scale); - append(json, "]"); - } - if (node && node->weights_count) - { - append(json, ",\"weights\":["); - for (size_t j = 0; j < node->weights_count; ++j) - { - comma(json); - append(json, node->weights[j]); - } - append(json, "]"); - } - append(json, "}"); -} - -void writeMeshNodeInstanced(std::string& json, size_t mesh_offset, size_t accr_offset) -{ - comma(json); - append(json, "{\"mesh\":"); - append(json, mesh_offset); - append(json, ",\"extensions\":{\"EXT_mesh_gpu_instancing\":{\"attributes\":{"); - - comma(json); - append(json, "\"TRANSLATION\":"); - append(json, accr_offset + 0); - - comma(json); - append(json, "\"ROTATION\":"); - append(json, accr_offset + 1); - - comma(json); - append(json, "\"SCALE\":"); - append(json, accr_offset + 2); - - append(json, "}}}"); - append(json, "}"); -} - -void writeSkin(std::string& json, const cgltf_skin& skin, size_t matrix_accr, const std::vector& nodes, cgltf_data* data) -{ - comma(json); - append(json, "{"); - append(json, "\"joints\":["); - for (size_t j = 0; j < skin.joints_count; ++j) - { - comma(json); - append(json, size_t(nodes[skin.joints[j] - data->nodes].remap)); - } - append(json, "]"); - append(json, ",\"inverseBindMatrices\":"); - append(json, matrix_accr); - if (skin.skeleton) - { - comma(json); - append(json, "\"skeleton\":"); - append(json, size_t(nodes[skin.skeleton - data->nodes].remap)); - } - append(json, "}"); -} - -void writeNode(std::string& json, const cgltf_node& node, const std::vector& nodes, cgltf_data* data) -{ - const NodeInfo& ni = nodes[&node - data->nodes]; - - if (node.name && *node.name) - { - comma(json); - append(json, "\"name\":\""); - append(json, node.name); - append(json, "\""); - } - if (node.has_translation) - { - comma(json); - append(json, "\"translation\":["); - append(json, node.translation[0]); - append(json, ","); - append(json, node.translation[1]); - append(json, ","); - append(json, node.translation[2]); - append(json, "]"); - } - if (node.has_rotation) - { - comma(json); - append(json, "\"rotation\":["); - append(json, node.rotation[0]); - append(json, ","); - append(json, node.rotation[1]); - append(json, ","); - append(json, node.rotation[2]); - append(json, ","); - append(json, node.rotation[3]); - append(json, "]"); - } - if (node.has_scale) - { - comma(json); - append(json, "\"scale\":["); - append(json, node.scale[0]); - append(json, ","); - append(json, node.scale[1]); - append(json, ","); - append(json, node.scale[2]); - append(json, "]"); - } - if (node.has_matrix) - { - comma(json); - append(json, "\"matrix\":["); - for (int k = 0; k < 16; ++k) - { - comma(json); - append(json, node.matrix[k]); - } - append(json, "]"); - } - - bool has_children = !ni.meshes.empty(); - for (size_t j = 0; j < node.children_count; ++j) - has_children |= nodes[node.children[j] - data->nodes].keep; - - if (has_children) - { - comma(json); - append(json, "\"children\":["); - for (size_t j = 0; j < node.children_count; ++j) - { - const NodeInfo& ci = nodes[node.children[j] - data->nodes]; - - if (ci.keep) - { - comma(json); - append(json, size_t(ci.remap)); - } - } - for (size_t j = 0; j < ni.meshes.size(); ++j) - { - comma(json); - append(json, ni.meshes[j]); - } - append(json, "]"); - } - if (node.camera) - { - comma(json); - append(json, "\"camera\":"); - append(json, size_t(node.camera - data->cameras)); - } - if (node.light) - { - comma(json); - append(json, "\"extensions\":{\"KHR_lights_punctual\":{\"light\":"); - append(json, size_t(node.light - data->lights)); - append(json, "}}"); - } -} - -void writeAnimation(std::string& json, std::vector& views, std::string& json_accessors, size_t& accr_offset, const Animation& animation, size_t i, cgltf_data* data, const std::vector& nodes, const Settings& settings) -{ - std::vector tracks; - - for (size_t j = 0; j < animation.tracks.size(); ++j) - { - const Track& track = animation.tracks[j]; - - const NodeInfo& ni = nodes[track.node - data->nodes]; - - if (!ni.keep) - continue; - - if (!settings.anim_const && (ni.animated_paths & (1 << track.path)) == 0) - continue; - - tracks.push_back(&track); - } - - if (tracks.empty()) - { - char index[16]; - sprintf(index, "%d", int(i)); - - fprintf(stderr, "Warning: ignoring animation %s because it has no tracks with motion; use -ac to override\n", animation.name && *animation.name ? animation.name : index); - return; - } - - bool needs_time = false; - bool needs_pose = false; - - for (size_t j = 0; j < tracks.size(); ++j) - { - const Track& track = *tracks[j]; - - assert(track.time.empty()); - assert(track.data.size() == track.components * (track.constant ? 1 : animation.frames)); - - needs_time = needs_time || !track.constant; - needs_pose = needs_pose || track.constant; - } - - bool needs_range = needs_pose && !needs_time && animation.frames > 1; - - needs_pose = needs_pose && !(needs_range && tracks.size() == 1); - - assert(int(needs_time) + int(needs_pose) + int(needs_range) <= 2); - - float animation_period = 1.f / float(settings.anim_freq); - float animation_length = float(animation.frames - 1) * animation_period; - - size_t time_accr = needs_time ? writeAnimationTime(views, json_accessors, accr_offset, animation.start, animation.frames, animation_period, settings) : 0; - size_t pose_accr = needs_pose ? writeAnimationTime(views, json_accessors, accr_offset, animation.start, 1, 0.f, settings) : 0; - size_t range_accr = needs_range ? writeAnimationTime(views, json_accessors, accr_offset, animation.start, 2, animation_length, settings) : 0; - - std::string json_samplers; - std::string json_channels; - - size_t track_offset = 0; - - for (size_t j = 0; j < tracks.size(); ++j) - { - const Track& track = *tracks[j]; - - bool range = needs_range && j == 0; - int range_size = range ? 2 : 1; - - std::string scratch; - StreamFormat format = writeKeyframeStream(scratch, track.path, track.data, settings); - - if (range) - { - assert(range_size == 2); - scratch += scratch; - } - - BufferView::Compression compression = settings.compress && track.path != cgltf_animation_path_type_weights ? BufferView::Compression_Attribute : BufferView::Compression_None; - - size_t view = getBufferView(views, BufferView::Kind_Keyframe, format.filter, compression, format.stride, track.path); - size_t offset = views[view].data.size(); - views[view].data += scratch; - - comma(json_accessors); - writeAccessor(json_accessors, view, offset, format.type, format.component_type, format.normalized, track.data.size() * range_size); - - size_t data_accr = accr_offset++; - - comma(json_samplers); - append(json_samplers, "{\"input\":"); - append(json_samplers, range ? range_accr : track.constant ? pose_accr : time_accr); - append(json_samplers, ",\"output\":"); - append(json_samplers, data_accr); - if (track.interpolation == cgltf_interpolation_type_step) - append(json_samplers, ",\"interpolation\":\"STEP\""); - append(json_samplers, "}"); - - const NodeInfo& tni = nodes[track.node - data->nodes]; - size_t target_node = size_t(tni.remap); - - if (track.path == cgltf_animation_path_type_weights) - { - assert(tni.meshes.size() == 1); - target_node = tni.meshes[0]; - } - - comma(json_channels); - append(json_channels, "{\"sampler\":"); - append(json_channels, track_offset); - append(json_channels, ",\"target\":{\"node\":"); - append(json_channels, target_node); - append(json_channels, ",\"path\":\""); - append(json_channels, animationPath(track.path)); - append(json_channels, "\"}}"); - - track_offset++; - } - - comma(json); - append(json, "{"); - if (animation.name && *animation.name) - { - append(json, "\"name\":\""); - append(json, animation.name); - append(json, "\","); - } - append(json, "\"samplers\":["); - append(json, json_samplers); - append(json, "],\"channels\":["); - append(json, json_channels); - append(json, "]}"); -} - -void writeCamera(std::string& json, const cgltf_camera& camera) -{ - comma(json); - append(json, "{"); - - switch (camera.type) - { - case cgltf_camera_type_perspective: - append(json, "\"type\":\"perspective\",\"perspective\":{"); - append(json, "\"yfov\":"); - append(json, camera.data.perspective.yfov); - append(json, ",\"znear\":"); - append(json, camera.data.perspective.znear); - if (camera.data.perspective.aspect_ratio != 0.f) - { - append(json, ",\"aspectRatio\":"); - append(json, camera.data.perspective.aspect_ratio); - } - if (camera.data.perspective.zfar != 0.f) - { - append(json, ",\"zfar\":"); - append(json, camera.data.perspective.zfar); - } - append(json, "}"); - break; - - case cgltf_camera_type_orthographic: - append(json, "\"type\":\"orthographic\",\"orthographic\":{"); - append(json, "\"xmag\":"); - append(json, camera.data.orthographic.xmag); - append(json, ",\"ymag\":"); - append(json, camera.data.orthographic.ymag); - append(json, ",\"znear\":"); - append(json, camera.data.orthographic.znear); - append(json, ",\"zfar\":"); - append(json, camera.data.orthographic.zfar); - append(json, "}"); - break; - - default: - fprintf(stderr, "Warning: skipping camera of unknown type\n"); - } - - append(json, "}"); -} - -void writeLight(std::string& json, const cgltf_light& light) -{ - comma(json); - append(json, "{\"type\":\""); - append(json, lightType(light.type)); - append(json, "\""); - if (memcmp(light.color, white, 12) != 0) - { - comma(json); - append(json, "\"color\":["); - append(json, light.color[0]); - append(json, ","); - append(json, light.color[1]); - append(json, ","); - append(json, light.color[2]); - append(json, "]"); - } - if (light.intensity != 1.f) - { - comma(json); - append(json, "\"intensity\":"); - append(json, light.intensity); - } - if (light.range != 0.f) - { - comma(json); - append(json, "\"range\":"); - append(json, light.range); - } - if (light.type == cgltf_light_type_spot) - { - comma(json); - append(json, "\"spot\":{"); - append(json, "\"innerConeAngle\":"); - append(json, light.spot_inner_cone_angle); - append(json, ",\"outerConeAngle\":"); - append(json, light.spot_outer_cone_angle == 0.f ? 0.78539816339f : light.spot_outer_cone_angle); - append(json, "}"); - } - append(json, "}"); -} - -void writeArray(std::string& json, const char* name, const std::string& contents) -{ - if (contents.empty()) - return; - - comma(json); - append(json, "\""); - append(json, name); - append(json, "\":["); - append(json, contents); - append(json, "]"); -} - -void writeExtensions(std::string& json, const ExtensionInfo* extensions, size_t count) -{ - bool used_extensions = false; - bool required_extensions = false; - - for (size_t i = 0; i < count; ++i) - { - used_extensions |= extensions[i].used; - required_extensions |= extensions[i].used && extensions[i].required; - } - - if (used_extensions) - { - comma(json); - append(json, "\"extensionsUsed\":["); - for (size_t i = 0; i < count; ++i) - if (extensions[i].used) - { - comma(json); - append(json, "\""); - append(json, extensions[i].name); - append(json, "\""); - } - append(json, "]"); - } - - if (required_extensions) - { - comma(json); - append(json, "\"extensionsRequired\":["); - for (size_t i = 0; i < count; ++i) - if (extensions[i].used && extensions[i].required) - { - comma(json); - append(json, "\""); - append(json, extensions[i].name); - append(json, "\""); - } - append(json, "]"); - } -} - -void writeExtras(std::string& json, const std::string& data, const cgltf_extras& extras) -{ - if (extras.start_offset == extras.end_offset) - return; - - assert(extras.start_offset < data.size()); - assert(extras.end_offset <= data.size()); - - comma(json); - append(json, "\"extras\":"); - appendJson(json, data.c_str() + extras.start_offset, data.c_str() + extras.end_offset); -} - -void writeScene(std::string& json, const cgltf_scene& scene, const std::string& roots) -{ - comma(json); - append(json, "{"); - if (scene.name && *scene.name) - { - append(json, "\"name\":\""); - append(json, scene.name); - append(json, "\""); - } - if (!roots.empty()) - { - comma(json); - append(json, "\"nodes\":["); - append(json, roots); - append(json, "]"); - } - append(json, "}"); -} diff --git a/Dependencies/meshoptimizer/js/README.md b/Dependencies/meshoptimizer/js/README.md deleted file mode 100644 index 9fab69d..0000000 --- a/Dependencies/meshoptimizer/js/README.md +++ /dev/null @@ -1,110 +0,0 @@ -# meshoptimizer.js - -This folder contains JavaScript/WebAssembly modules that can be used to access parts of functionality of meshoptimizer library. While normally these would be used internally by glTF loaders, processors and other Web optimization tools, they can also be used directly if needed. The modules are available as an [NPM package](https://www.npmjs.com/package/meshoptimizer) but can also be redistributed individually on a file-by-file basis. - -## Structure - -Each component comes in two variants: - -- `meshopt_component.js` uses a UMD-style module declaration and can be used by a wide variety of JavaScript module loaders, including node.js require(), AMD, Common.JS, and can also be loaded into the web page directly via a `