Repository: recp/AssetKit Branch: main Commit: 8cd23a03b970 Files: 429 Total size: 1.8 MB Directory structure: gitextract_y45q9sak/ ├── .github/ │ ├── FUNDING.yml │ └── __disabled_workflows/ │ └── cmake.yml ├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── EXTENSIONS.md ├── EXTRAS.md ├── LICENSE ├── README.md ├── appveyor.yml ├── docs/ │ ├── make.bat │ └── source/ │ ├── api.rst │ ├── build.rst │ ├── conf.py │ ├── getting_started.rst │ ├── index.rst │ ├── opt.rst │ ├── quick_intro.rst │ └── version.rst ├── include/ │ └── ak/ │ ├── animation.h │ ├── assetkit.h │ ├── bbox.h │ ├── cam.h │ ├── common.h │ ├── context.h │ ├── controller.h │ ├── coord-util.h │ ├── coord.h │ ├── core-types.h │ ├── geom.h │ ├── gsplat.h │ ├── image.h │ ├── instance.h │ ├── library.h │ ├── light.h │ ├── map.h │ ├── material.h │ ├── memory.h │ ├── node.h │ ├── options.h │ ├── path.h │ ├── profile.h │ ├── sid.h │ ├── source.h │ ├── string.h │ ├── texture.h │ ├── transform.h │ ├── trash.h │ ├── type.h │ ├── url.h │ ├── util.h │ └── version.h ├── scripts/ │ └── strpool.py ├── src/ │ ├── CMakeLists.txt │ ├── accessor.c │ ├── accessor.h │ ├── anim/ │ │ ├── CMakeLists.txt │ │ ├── bake.c │ │ └── conflict.c │ ├── array.c │ ├── array.h │ ├── asset.c │ ├── assetkit.c │ ├── base64.c │ ├── base64.h │ ├── bbox/ │ │ ├── CMakeLists.txt │ │ ├── bbox.c │ │ ├── bbox.h │ │ ├── geom.c │ │ ├── mesh.c │ │ ├── mesh_prim.c │ │ └── scene.c │ ├── bitwise/ │ │ ├── CMakeLists.txt │ │ └── bitwise.h │ ├── camera/ │ │ ├── CMakeLists.txt │ │ └── cam.c │ ├── common.c │ ├── common.h │ ├── coord/ │ │ ├── CMakeLists.txt │ │ ├── camera.c │ │ ├── common.c │ │ ├── common.h │ │ ├── doc.c │ │ ├── geom.c │ │ ├── mesh.c │ │ ├── node.c │ │ ├── scene.c │ │ ├── transform.c │ │ ├── transforms.c │ │ └── vector.c │ ├── data.c │ ├── data.h │ ├── decoders/ │ │ └── gltf/ │ │ ├── draco/ │ │ │ └── assetkit_draco.cc │ │ ├── ktx2/ │ │ │ └── assetkit_ktx2.cc │ │ ├── meshopt/ │ │ │ └── assetkit_meshoptimizer.cc │ │ └── spz/ │ │ └── assetkit_spz.cc │ ├── default/ │ │ ├── CMakeLists.txt │ │ ├── cam.c │ │ ├── cam.h │ │ ├── cmp.c │ │ ├── coord.c │ │ ├── id.c │ │ ├── light.c │ │ ├── light.h │ │ ├── material.c │ │ ├── material.h │ │ ├── opt.c │ │ ├── opt.h │ │ ├── semantic.c │ │ ├── semantic.h │ │ ├── type.c │ │ └── type.h │ ├── endian.h │ ├── find.c │ ├── geom/ │ │ ├── CMakeLists.txt │ │ └── mesh.c │ ├── id.c │ ├── id.h │ ├── image/ │ │ ├── CMakeLists.txt │ │ └── image.c │ ├── instance/ │ │ ├── CMakeLists.txt │ │ ├── inst.c │ │ └── list.c │ ├── io/ │ │ ├── 3mf/ │ │ │ ├── CMakeLists.txt │ │ │ ├── README.md │ │ │ └── imp/ │ │ │ ├── 3mf.c │ │ │ ├── 3mf.h │ │ │ ├── CMakeLists.txt │ │ │ └── common.h │ │ ├── CMakeLists.txt │ │ ├── common/ │ │ │ ├── CMakeLists.txt │ │ │ ├── postscript.c │ │ │ ├── postscript.h │ │ │ ├── util.c │ │ │ └── util.h │ │ ├── dae/ │ │ │ ├── 1.4/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── dae14.c │ │ │ │ ├── dae14.h │ │ │ │ ├── image.c │ │ │ │ ├── image.h │ │ │ │ ├── surface.c │ │ │ │ └── surface.h │ │ │ ├── CMakeLists.txt │ │ │ ├── README.md │ │ │ ├── brep/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── brep.c │ │ │ │ ├── brep.h │ │ │ │ ├── curve.c │ │ │ │ ├── curve.h │ │ │ │ ├── nurb.c │ │ │ │ ├── nurb.h │ │ │ │ ├── surface.c │ │ │ │ ├── surface.h │ │ │ │ ├── topology.c │ │ │ │ └── topology.h │ │ │ ├── bugfix/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── scenekit.c │ │ │ │ ├── scenekit.h │ │ │ │ ├── transp.c │ │ │ │ ├── transp.h │ │ │ │ └── url.h │ │ │ ├── common.h │ │ │ ├── core/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── anim.c │ │ │ │ ├── anim.h │ │ │ │ ├── asset.c │ │ │ │ ├── asset.h │ │ │ │ ├── cam.c │ │ │ │ ├── cam.h │ │ │ │ ├── color.c │ │ │ │ ├── color.h │ │ │ │ ├── ctlr.c │ │ │ │ ├── ctlr.h │ │ │ │ ├── enum.c │ │ │ │ ├── enum.h │ │ │ │ ├── geom.c │ │ │ │ ├── geom.h │ │ │ │ ├── light.c │ │ │ │ ├── light.h │ │ │ │ ├── line.c │ │ │ │ ├── line.h │ │ │ │ ├── mesh.c │ │ │ │ ├── mesh.h │ │ │ │ ├── morph.c │ │ │ │ ├── morph.h │ │ │ │ ├── node.c │ │ │ │ ├── node.h │ │ │ │ ├── param.c │ │ │ │ ├── param.h │ │ │ │ ├── poly.c │ │ │ │ ├── poly.h │ │ │ │ ├── scene.c │ │ │ │ ├── scene.h │ │ │ │ ├── skin.c │ │ │ │ ├── skin.h │ │ │ │ ├── source.c │ │ │ │ ├── source.h │ │ │ │ ├── spline.c │ │ │ │ ├── spline.h │ │ │ │ ├── techn.c │ │ │ │ ├── techn.h │ │ │ │ ├── triangle.c │ │ │ │ ├── triangle.h │ │ │ │ ├── value.c │ │ │ │ ├── value.h │ │ │ │ ├── vert.c │ │ │ │ └── vert.h │ │ │ ├── ctlr.c │ │ │ ├── dae.c │ │ │ ├── dae.h │ │ │ ├── fixup/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── angle.c │ │ │ │ ├── angle.h │ │ │ │ ├── channel.c │ │ │ │ ├── channel.h │ │ │ │ ├── ctlr.c │ │ │ │ ├── ctlr.h │ │ │ │ ├── geom.c │ │ │ │ ├── geom.h │ │ │ │ ├── mesh.c │ │ │ │ ├── mesh.h │ │ │ │ ├── node.c │ │ │ │ ├── node.h │ │ │ │ ├── tex.c │ │ │ │ └── tex.h │ │ │ ├── fx/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── colortex.c │ │ │ │ ├── colortex.h │ │ │ │ ├── effect.c │ │ │ │ ├── effect.h │ │ │ │ ├── fltprm.c │ │ │ │ ├── fltprm.h │ │ │ │ ├── img.c │ │ │ │ ├── img.h │ │ │ │ ├── mat.c │ │ │ │ ├── mat.h │ │ │ │ ├── profile.c │ │ │ │ ├── profile.h │ │ │ │ ├── samp.c │ │ │ │ ├── samp.h │ │ │ │ ├── techn.c │ │ │ │ └── techn.h │ │ │ ├── postscript.c │ │ │ ├── postscript.h │ │ │ ├── strpool.c │ │ │ ├── strpool.h │ │ │ ├── strpool.json │ │ │ └── strpool.py │ │ ├── gltf/ │ │ │ ├── CMakeLists.txt │ │ │ ├── README.md │ │ │ ├── common.h │ │ │ ├── imp/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── common.h │ │ │ │ ├── core/ │ │ │ │ │ ├── CMakeLists.txt │ │ │ │ │ ├── accessor.c │ │ │ │ │ ├── accessor.h │ │ │ │ │ ├── anim.c │ │ │ │ │ ├── anim.h │ │ │ │ │ ├── asset.c │ │ │ │ │ ├── asset.h │ │ │ │ │ ├── buffer.c │ │ │ │ │ ├── buffer.h │ │ │ │ │ ├── camera.c │ │ │ │ │ ├── camera.h │ │ │ │ │ ├── enum.c │ │ │ │ │ ├── enum.h │ │ │ │ │ ├── ext.c │ │ │ │ │ ├── ext.h │ │ │ │ │ ├── image.c │ │ │ │ │ ├── image.h │ │ │ │ │ ├── material.c │ │ │ │ │ ├── material.h │ │ │ │ │ ├── mesh.c │ │ │ │ │ ├── mesh.h │ │ │ │ │ ├── node.c │ │ │ │ │ ├── node.h │ │ │ │ │ ├── profile.c │ │ │ │ │ ├── profile.h │ │ │ │ │ ├── sampler.c │ │ │ │ │ ├── sampler.h │ │ │ │ │ ├── scene.c │ │ │ │ │ ├── scene.h │ │ │ │ │ ├── skin.c │ │ │ │ │ ├── skin.h │ │ │ │ │ ├── texture.c │ │ │ │ │ └── texture.h │ │ │ │ ├── ext/ │ │ │ │ │ ├── CMakeLists.txt │ │ │ │ │ ├── compression.c │ │ │ │ │ ├── decoder.c │ │ │ │ │ ├── decoder.h │ │ │ │ │ ├── gsplat.c │ │ │ │ │ ├── instancing.c │ │ │ │ │ ├── instancing.h │ │ │ │ │ ├── lights.c │ │ │ │ │ ├── lights.h │ │ │ │ │ ├── variants.c │ │ │ │ │ └── variants.h │ │ │ │ ├── extra.c │ │ │ │ ├── extra.h │ │ │ │ ├── gltf.c │ │ │ │ ├── gltf.h │ │ │ │ ├── mesh_fixup.c │ │ │ │ ├── mesh_fixup.h │ │ │ │ ├── postscript.c │ │ │ │ └── postscript.h │ │ │ ├── strpool.c │ │ │ ├── strpool.h │ │ │ ├── strpool.json │ │ │ └── strpool.py │ │ ├── obj/ │ │ │ ├── CMakeLists.txt │ │ │ ├── README.md │ │ │ ├── common.h │ │ │ ├── group.c │ │ │ ├── group.h │ │ │ ├── mtl.c │ │ │ ├── mtl.h │ │ │ ├── obj.c │ │ │ ├── obj.h │ │ │ ├── util.c │ │ │ └── util.h │ │ ├── ply/ │ │ │ ├── CMakeLists.txt │ │ │ ├── README.md │ │ │ ├── ascii.c │ │ │ ├── bin.c │ │ │ ├── common.h │ │ │ ├── ply.c │ │ │ ├── ply.h │ │ │ └── util.h │ │ └── stl/ │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── common.h │ │ ├── stl.c │ │ └── stl.h │ ├── json.h │ ├── lib/ │ │ ├── CMakeLists.txt │ │ ├── geom.c │ │ └── lib.c │ ├── light/ │ │ ├── CMakeLists.txt │ │ └── light.c │ ├── main.c │ ├── map.c │ ├── mat/ │ │ ├── CMakeLists.txt │ │ └── mat.c │ ├── mem/ │ │ ├── CMakeLists.txt │ │ ├── common.h │ │ ├── ext.c │ │ ├── intr.c │ │ ├── lt.c │ │ ├── lt.h │ │ ├── mem.c │ │ ├── mmap.c │ │ ├── rb.c │ │ └── rb.h │ ├── mesh/ │ │ ├── CMakeLists.txt │ │ ├── duplicator.c │ │ ├── edit.c │ │ ├── edit_buff.c │ │ ├── edit_buff_fixup.c │ │ ├── edit_common.h │ │ ├── edit_index.c │ │ ├── index.c │ │ ├── index.h │ │ ├── input.c │ │ ├── isolate.c │ │ ├── material.c │ │ ├── normal.c │ │ └── triangulate.c │ ├── miniz/ │ │ ├── CMakeLists.txt │ │ ├── LICENSE │ │ ├── miniz.c │ │ └── miniz.h │ ├── morph/ │ │ ├── CMakeLists.txt │ │ └── intr.c │ ├── node/ │ │ ├── CMakeLists.txt │ │ └── node.c │ ├── platform/ │ │ ├── CMakeLists.txt │ │ ├── dylib.c │ │ └── dylib.h │ ├── profile.c │ ├── profile.h │ ├── resc/ │ │ ├── CMakeLists.txt │ │ ├── path.c │ │ ├── resource.c │ │ ├── resource.h │ │ └── url.c │ ├── sid.c │ ├── sid.h │ ├── sid_constr.c │ ├── simd/ │ │ ├── arm.h │ │ ├── base64.h │ │ ├── intrin.h │ │ ├── wasm.h │ │ └── x86.h │ ├── skin/ │ │ ├── CMakeLists.txt │ │ ├── fix.c │ │ ├── fix.h │ │ └── skin.c │ ├── string.c │ ├── strpool.c │ ├── strpool.h │ ├── strpool.json │ ├── strpool.py │ ├── topo/ │ │ ├── CMakeLists.txt │ │ ├── topo.c │ │ └── topo.h │ ├── transform/ │ │ ├── CMakeLists.txt │ │ ├── dup.c │ │ ├── trans.c │ │ └── traverse.c │ ├── trash.c │ ├── trash.h │ ├── tree.c │ ├── tree.h │ ├── type.c │ ├── type.h │ ├── utils.c │ ├── utils.h │ ├── win32/ │ │ ├── CMakeLists.txt │ │ ├── dllmain.c │ │ └── strptime.c │ ├── xml.c │ └── xml.h └── test/ ├── include/ │ └── common.h ├── runner.c ├── src/ │ ├── collada/ │ │ └── test_dae_load.c │ ├── test_common.h │ └── test_memory.c └── tests.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: [recp] patreon: recp open_collective: assetkit ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] ================================================ FILE: .github/__disabled_workflows/cmake.yml ================================================ name: CMake on: push: branches: [ master ] pull_request: branches: [ master ] env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE: Release jobs: build: # The CMake configure and build commands are platform agnostic and should work equally # well on Windows or Mac. You can convert this to a matrix build if you need # cross-platform coverage. # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} - name: Build # Build your program with the given configuration run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} # - name: Test # working-directory: ${{github.workspace}}/build # # Execute tests defined by the CMake configuration. # # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail # run: ctest -C ${{env.BUILD_TYPE}} ================================================ FILE: .gitignore ================================================ *.xcodeproj *.xcworkspace *.sln *.vcxproj *.vcxproj.* *.suo *.sdf *.opensdf ipch/ Debug/ Release/ .DS_Store .vs *.nupkg *.opendb packages.config /aclocal.m4 /ar-lib /autom4te.cache/ /compile /config.guess /config.log /config.status /config.sub /configure /depcomp /install-sh /ltmain.sh /missing /libtool .libs/ .deps/ *.[oa] *.l[oa] Makefile Makefile.in m4/*.m4 .buildstamp .dirstamp packages/ .anjuta/* *.anjuta* config.h.* libxml* xml2* python/setup.py config.h stamp* COPYING .idea/* *.VC.db test-driver /include/include/ *.log test/ak-tests.trs test/ak-tests *.gcov *.gcno *.gcda tests.trs tests *.userprefs *.orig cmake-build-debug win/x64/* win/x86/* build/ /deps/draco/ /deps/meshoptimizer/ /deps/spz/ /deps/ktx/ .cache/ .scannerwork/ .project .cproject .vscode/ proj/ ================================================ FILE: .gitmodules ================================================ [submodule "deps/cglm"] path = deps/cglm url = https://github.com/recp/cglm.git [submodule "deps/json"] path = deps/json url = https://github.com/recp/json.git [submodule "deps/xml"] path = deps/xml url = https://github.com/recp/xml.git [submodule "deps/ds"] path = deps/ds url = https://github.com/recp/ds.git ================================================ FILE: .travis.yml ================================================ language: c os: - linux - osx sudo: required dist: trusty compiler: - clang - gcc matrix: fast_finish: true exclude: # Skip GCC builds on macOS. - os: osx compiler: gcc include: # Additional GCC builds for code coverage. - os: linux compiler: gcc env: CODE_COVERAGE=ON cache: apt: true addons: apt: packages: - lcov branches: only: - master script: - sh ./build-deps.sh - sh ./autogen.sh - if [[ "$CC" == "gcc" && "$CODE_COVERAGE" == "ON" ]]; then ./configure CFLAGS="-ftest-coverage -fprofile-arcs"; else ./configure; fi - make -j8 # - make check after_success: - if [[ "$CC" == "gcc" && "$CODE_COVERAGE" == "ON" ]]; then pip install --user cpp-coveralls && coveralls --build-root . --exclude lib --exclude test --gcov-options '\-lp' --verbose; fi ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.16) project(assetkit VERSION 0.3.2 LANGUAGES C) if(POLICY CMP0076) cmake_policy(SET CMP0076 NEW) endif() if(POLICY CMP0169) cmake_policy(SET CMP0169 OLD) endif() set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED YES) set(DEFAULT_BUILD_TYPE "Release") get_directory_property(AK_HAS_PARENT PARENT_DIRECTORY) set(CMAKE_C_FLAGS_DEBUG "-g") if(MSVC) set(CMAKE_C_FLAGS_RELEASE "/Ox /DNDEBUG") set(CMAKE_CXX_FLAGS_RELEASE "/Ox /DNDEBUG") else() set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") endif() set(AK_BUILD) option(AK_SHARED "Shared build" ON) option(AK_STATIC "Static build" OFF) option(AK_USE_C99 "" OFF) option(AK_USE_TEST "Enable Tests" OFF) option(AK_BUILD_GLTF_DRACO_DECODER "Build optional glTF Draco decoder shim" ON) option(AK_BUILD_GLTF_MESHOPT_DECODER "Build optional glTF meshoptimizer decoder shim" ON) option(AK_BUILD_GLTF_SPZ_DECODER "Build optional glTF Gaussian splatting (SPZ) decoder shim" ON) option(AK_BUILD_GLTF_KTX2_DECODER "Build optional glTF KHR_texture_basisu (KTX2/BasisU) decoder shim" ON) option(AK_FETCH_DEPS "Fetch optional decoder dependencies into AK_DEPS_ROOT when missing" ON) option(AK_ENABLE_LTO "Enable link-time optimization for release builds" OFF) set(AK_DRACO_ROOT "" CACHE PATH "Optional Draco install/source root") set(AK_MESHOPT_ROOT "" CACHE PATH "Optional meshoptimizer source root") set(AK_SPZ_ROOT "" CACHE PATH "Optional SPZ install/source root") set(AK_KTX2_ROOT "" CACHE PATH "Optional KTX-Software install/source root") set(AK_DEPS_ROOT "${PROJECT_SOURCE_DIR}/deps" CACHE PATH "AssetKit dependency checkout root") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") if(NOT AK_STATIC AND AK_SHARED) set(AK_BUILD SHARED) else(AK_STATIC) set(AK_BUILD STATIC) endif() if(AK_USE_C99) set(CMAKE_C_STANDARD 99) endif() if(MSVC) # Ref: https://skia.googlesource.com/third_party/sdl/+/refs/heads/master/CMakeLists.txt#225 # Make sure /RTC1 is disabled, otherwise it will use functions from the CRT foreach(flag_var CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO) string(REGEX REPLACE "/RTC(su|[1su])" "" ${flag_var} "${${flag_var}}") endforeach(flag_var) endif() if(NOT AK_HAS_PARENT AND NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified.") set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() if(AK_ENABLE_LTO) include(CheckIPOSupported) check_ipo_supported(RESULT AK_LTO_OK OUTPUT AK_LTO_MSG) if(AK_LTO_OK) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO ON) else() message(WARNING "LTO was requested but is not supported: ${AK_LTO_MSG}") endif() endif() function(ak_target_common_options target) if(MSVC) target_compile_options(${target} PRIVATE /W3 $<$:/Gy> $<$:/Oi> $<$:/Gy> $<$:/Oi>) else() target_compile_options(${target} PRIVATE -Wall -Wextra -Wstrict-aliasing=2 -Wno-overlength-strings $<$:-Wmissing-declarations>) endif() endfunction() function(ak_target_c_options target) ak_target_common_options(${target}) if(MSVC) target_compile_options(${target} PRIVATE /TC) endif() endfunction() function(ak_target_cxx_options target) ak_target_common_options(${target}) endfunction() include(GNUInstallDirs) set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) if(NOT CPack_CMake_INCLUDED) include(CPack) endif() # Target Start add_library(${PROJECT_NAME} ${AK_BUILD} "") add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) ak_target_c_options(${PROJECT_NAME}) if(APPLE) set(AK_INSTALL_RPATH "@loader_path") elseif(UNIX) set(AK_INSTALL_RPATH "\$ORIGIN") endif() if(AK_SHARED) target_compile_definitions(${PROJECT_NAME} PRIVATE AK_EXPORTS) if(MSVC) target_compile_definitions(${PROJECT_NAME} PRIVATE _WINDOWS _USRDLL _assetkit_dll_DLL) endif() else() target_compile_definitions(${PROJECT_NAME} PUBLIC AK_STATIC) endif() set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR}) if(AK_INSTALL_RPATH) set_target_properties(${PROJECT_NAME} PROPERTIES INSTALL_RPATH "${AK_INSTALL_RPATH}") endif() add_subdirectory(src) # Dependencies && Submodules find_package(Git QUIET) if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") # Update submodules as needed option(GIT_SUBMODULE "Check submodules during build" ON) if(GIT_SUBMODULE) message(STATUS "Submodule update") execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} RESULT_VARIABLE GIT_SUBMOD_RESULT) if(NOT GIT_SUBMOD_RESULT EQUAL "0") message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") endif() endif() endif() if(NOT EXISTS "${PROJECT_SOURCE_DIR}/deps/cglm/CMakeLists.txt") message(FATAL_ERROR "The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.") endif() add_subdirectory(deps/ds) add_dependencies(${PROJECT_NAME} ds) # set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/deps" ${CMAKE_MODULE_PATH}) # find_package(ds REQUIRED) # target_link_libraries(${PROJECT_NAME} ds::ds) # Include Dirs target_include_directories(${PROJECT_NAME} PUBLIC $ $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/deps/cglm/include ${PROJECT_SOURCE_DIR}/deps/ds/include ${PROJECT_SOURCE_DIR}/deps/json/include ${PROJECT_SOURCE_DIR}/deps/xml/include ) target_link_libraries(${PROJECT_NAME} PRIVATE ds ${CMAKE_DL_LIBS}) if(AK_BUILD_GLTF_DRACO_DECODER OR AK_BUILD_GLTF_MESHOPT_DECODER OR AK_BUILD_GLTF_SPZ_DECODER OR AK_BUILD_GLTF_KTX2_DECODER) enable_language(CXX) endif() if(AK_BUILD_GLTF_DRACO_DECODER) find_path(AK_DRACO_INCLUDE_DIR NAMES draco/compression/decode.h HINTS ${AK_DRACO_ROOT} ${AK_DRACO_ROOT}/install ${AK_DRACO_ROOT}/include ${AK_DEPS_ROOT}/draco/install ${AK_DEPS_ROOT}/draco ${AK_DEPS_ROOT}/draco/include PATH_SUFFIXES include NO_DEFAULT_PATH) find_library(AK_DRACO_LIBRARY NAMES draco HINTS ${AK_DRACO_ROOT} ${AK_DRACO_ROOT}/install ${AK_DRACO_ROOT}/lib ${AK_DEPS_ROOT}/draco/install ${AK_DEPS_ROOT}/draco ${AK_DEPS_ROOT}/draco/lib PATH_SUFFIXES lib NO_DEFAULT_PATH) if((NOT AK_DRACO_INCLUDE_DIR OR NOT AK_DRACO_LIBRARY) AND AK_FETCH_DEPS) include(ExternalProject) set(AK_DRACO_INSTALL_DIR "${AK_DEPS_ROOT}/draco/install") set(AK_DRACO_INCLUDE_DIR "${AK_DRACO_INSTALL_DIR}/include") if(WIN32) set(AK_DRACO_LIBRARY "${AK_DRACO_INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}/draco.lib") else() set(AK_DRACO_LIBRARY "${AK_DRACO_INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}/libdraco.a") endif() ExternalProject_Add(assetkit_draco_dep GIT_REPOSITORY https://github.com/google/draco.git GIT_TAG main GIT_SHALLOW TRUE GIT_SUBMODULES "" SOURCE_DIR "${AK_DEPS_ROOT}/draco" BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/assetkit_draco-build" INSTALL_DIR "${AK_DRACO_INSTALL_DIR}" BUILD_BYPRODUCTS "${AK_DRACO_LIBRARY}" CMAKE_ARGS -Wno-dev -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX= -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DDRACO_ANIMATION_ENCODING=OFF -DDRACO_BUILD_EXECUTABLES=OFF -DDRACO_GLTF_BITSTREAM=ON -DDRACO_INSTALL=ON -DDRACO_JS_GLUE=OFF -DDRACO_MAYA_PLUGIN=OFF -DDRACO_TESTS=OFF -DDRACO_TRANSCODER_SUPPORTED=OFF -DDRACO_UNITY_PLUGIN=OFF -DDRACO_WASM=OFF -DBUILD_SHARED_LIBS=OFF) endif() if(NOT AK_DRACO_INCLUDE_DIR OR NOT AK_DRACO_LIBRARY) message(FATAL_ERROR "Draco decoder requested but Draco was not found. " "Set AK_DRACO_ROOT, enable AK_FETCH_DEPS, or disable " "AK_BUILD_GLTF_DRACO_DECODER.") endif() add_library(assetkit_draco SHARED src/decoders/gltf/draco/assetkit_draco.cc) ak_target_cxx_options(assetkit_draco) if(TARGET assetkit_draco_dep) add_dependencies(assetkit_draco assetkit_draco_dep) endif() target_include_directories(assetkit_draco SYSTEM PRIVATE ${AK_DRACO_INCLUDE_DIR}) target_include_directories(assetkit_draco PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/deps/cglm/include ${PROJECT_SOURCE_DIR}/deps/ds/include ${PROJECT_SOURCE_DIR}/deps/json/include ${PROJECT_SOURCE_DIR}/deps/xml/include) target_link_libraries(assetkit_draco PRIVATE ${PROJECT_NAME} ds ${AK_DRACO_LIBRARY}) set_target_properties(assetkit_draco PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED YES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN YES) if(AK_INSTALL_RPATH) set_target_properties(assetkit_draco PROPERTIES INSTALL_RPATH "${AK_INSTALL_RPATH}") endif() if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") target_compile_options(assetkit_draco PRIVATE -Wno-deprecated-copy-with-user-provided-copy -Wno-ignored-qualifiers -Wno-implicit-const-int-float-conversion -Wno-sign-compare) endif() install(TARGETS assetkit_draco LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() if(AK_BUILD_GLTF_MESHOPT_DECODER) find_path(AK_MESHOPT_INCLUDE_DIR NAMES meshoptimizer.h HINTS ${AK_MESHOPT_ROOT} ${AK_MESHOPT_ROOT}/src ${AK_DEPS_ROOT}/meshoptimizer ${AK_DEPS_ROOT}/meshoptimizer/src PATH_SUFFIXES src NO_DEFAULT_PATH) if(NOT AK_MESHOPT_INCLUDE_DIR AND AK_FETCH_DEPS) include(FetchContent) FetchContent_Declare(assetkit_meshoptimizer_dep GIT_REPOSITORY https://github.com/zeux/meshoptimizer.git GIT_TAG master GIT_SHALLOW TRUE SOURCE_DIR "${AK_DEPS_ROOT}/meshoptimizer") FetchContent_GetProperties(assetkit_meshoptimizer_dep) if(NOT assetkit_meshoptimizer_dep_POPULATED) FetchContent_Populate(assetkit_meshoptimizer_dep) endif() set(AK_MESHOPT_INCLUDE_DIR "${AK_DEPS_ROOT}/meshoptimizer/src") endif() set(AK_MESHOPT_SOURCES ${AK_MESHOPT_INCLUDE_DIR}/indexcodec.cpp ${AK_MESHOPT_INCLUDE_DIR}/vertexcodec.cpp ${AK_MESHOPT_INCLUDE_DIR}/vertexfilter.cpp) if(NOT AK_MESHOPT_INCLUDE_DIR OR NOT EXISTS "${AK_MESHOPT_INCLUDE_DIR}/indexcodec.cpp" OR NOT EXISTS "${AK_MESHOPT_INCLUDE_DIR}/vertexcodec.cpp" OR NOT EXISTS "${AK_MESHOPT_INCLUDE_DIR}/vertexfilter.cpp") message(FATAL_ERROR "meshoptimizer decoder requested but source files were not found. " "Set AK_MESHOPT_ROOT, enable AK_FETCH_DEPS, or disable " "AK_BUILD_GLTF_MESHOPT_DECODER.") endif() add_library(assetkit_meshoptimizer SHARED src/decoders/gltf/meshopt/assetkit_meshoptimizer.cc ${AK_MESHOPT_SOURCES}) ak_target_cxx_options(assetkit_meshoptimizer) target_include_directories(assetkit_meshoptimizer PRIVATE ${AK_MESHOPT_INCLUDE_DIR}) set_target_properties(assetkit_meshoptimizer PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED YES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN YES) if(AK_INSTALL_RPATH) set_target_properties(assetkit_meshoptimizer PROPERTIES INSTALL_RPATH "${AK_INSTALL_RPATH}") endif() install(TARGETS assetkit_meshoptimizer LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() # --------------------------------------------------------------------------- # Optional Gaussian splat (SPZ) decoder shim — KHR_gaussian_splatting future # compression sub-extension support. Niantic Spatial's libspz handles the # format; we link statically and expose the AssetKit decoder API. # --------------------------------------------------------------------------- if(AK_BUILD_GLTF_SPZ_DECODER) find_path(AK_SPZ_INCLUDE_DIR NAMES spz/spz.h HINTS ${AK_SPZ_ROOT} ${AK_SPZ_ROOT}/install ${AK_SPZ_ROOT}/include ${AK_DEPS_ROOT}/spz/install ${AK_DEPS_ROOT}/spz ${AK_DEPS_ROOT}/spz/include PATH_SUFFIXES include NO_DEFAULT_PATH) find_library(AK_SPZ_LIBRARY NAMES spz HINTS ${AK_SPZ_ROOT} ${AK_SPZ_ROOT}/install ${AK_SPZ_ROOT}/lib ${AK_DEPS_ROOT}/spz/install ${AK_DEPS_ROOT}/spz ${AK_DEPS_ROOT}/spz/lib PATH_SUFFIXES lib NO_DEFAULT_PATH) if((NOT AK_SPZ_INCLUDE_DIR OR NOT AK_SPZ_LIBRARY) AND AK_FETCH_DEPS) include(ExternalProject) set(AK_SPZ_INSTALL_DIR "${AK_DEPS_ROOT}/spz/install") set(AK_SPZ_INCLUDE_DIR "${AK_SPZ_INSTALL_DIR}/include") if(WIN32) set(AK_SPZ_LIBRARY "${AK_SPZ_INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}/spz.lib") else() set(AK_SPZ_LIBRARY "${AK_SPZ_INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}/libspz.a") endif() ExternalProject_Add(assetkit_spz_dep GIT_REPOSITORY https://github.com/nianticlabs/spz.git GIT_TAG main GIT_SHALLOW TRUE SOURCE_DIR "${AK_DEPS_ROOT}/spz" BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/assetkit_spz-build" INSTALL_DIR "${AK_SPZ_INSTALL_DIR}" BUILD_BYPRODUCTS "${AK_SPZ_LIBRARY}" CMAKE_ARGS -Wno-dev -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX= -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DBUILD_SHARED_LIBS=OFF) endif() if(NOT AK_SPZ_INCLUDE_DIR OR NOT AK_SPZ_LIBRARY) message(WARNING "SPZ Gaussian-splat decoder requested but libspz was not found. " "Set AK_SPZ_ROOT, enable AK_FETCH_DEPS, or disable " "AK_BUILD_GLTF_SPZ_DECODER. Skipping decoder build for now.") else() add_library(assetkit_spz SHARED src/decoders/gltf/spz/assetkit_spz.cc) ak_target_cxx_options(assetkit_spz) if(TARGET assetkit_spz_dep) add_dependencies(assetkit_spz assetkit_spz_dep) endif() target_include_directories(assetkit_spz SYSTEM PRIVATE ${AK_SPZ_INCLUDE_DIR}) target_include_directories(assetkit_spz PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/deps/cglm/include ${PROJECT_SOURCE_DIR}/deps/ds/include ${PROJECT_SOURCE_DIR}/deps/json/include) # SPZ depends on zstd + zlib (for ngsp/zstd-compressed streams + the gzip # codec). On macOS both ship with the SDK; elsewhere we expect system # packages. find_package handles missing libraries gracefully. find_package(ZLIB) find_library(AK_SPZ_ZSTD_LIB NAMES zstd HINTS /opt/homebrew/lib /usr/local/lib /usr/lib) target_link_libraries(assetkit_spz PRIVATE ${PROJECT_NAME} ds ${AK_SPZ_LIBRARY}) if(ZLIB_FOUND) target_link_libraries(assetkit_spz PRIVATE ZLIB::ZLIB) endif() if(AK_SPZ_ZSTD_LIB) target_link_libraries(assetkit_spz PRIVATE ${AK_SPZ_ZSTD_LIB}) else() message(WARNING "libzstd not found — SPZ ngsp/zstd-compressed splat streams " "will fail at runtime. Install via 'brew install zstd' on " "macOS or your distro's package manager.") endif() set_target_properties(assetkit_spz PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN YES) if(AK_INSTALL_RPATH) set_target_properties(assetkit_spz PROPERTIES INSTALL_RPATH "${AK_INSTALL_RPATH}") endif() install(TARGETS assetkit_spz LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() endif() # --------------------------------------------------------------------------- # Optional KTX2 / BasisU texture decoder shim — KHR_texture_basisu support. # Khronos KTX-Software provides libktx with built-in BasisU transcoding # (UASTC / ETC1S → RGBA8 / BC7 / ASTC depending on target). The shim # decodes a KTX2 buffer into an RGBA8 AkImage so the renderer can consume # it like any other Core Graphics-decoded texture. # --------------------------------------------------------------------------- if(AK_BUILD_GLTF_KTX2_DECODER) find_path(AK_KTX2_INCLUDE_DIR NAMES ktx.h HINTS ${AK_KTX2_ROOT} ${AK_KTX2_ROOT}/install ${AK_KTX2_ROOT}/include ${AK_DEPS_ROOT}/ktx/install ${AK_DEPS_ROOT}/ktx ${AK_DEPS_ROOT}/ktx/include PATH_SUFFIXES include NO_DEFAULT_PATH) find_library(AK_KTX2_LIBRARY NAMES ktx ktx_read HINTS ${AK_KTX2_ROOT} ${AK_KTX2_ROOT}/install ${AK_KTX2_ROOT}/lib ${AK_DEPS_ROOT}/ktx/install ${AK_DEPS_ROOT}/ktx ${AK_DEPS_ROOT}/ktx/lib PATH_SUFFIXES lib NO_DEFAULT_PATH) if((NOT AK_KTX2_INCLUDE_DIR OR NOT AK_KTX2_LIBRARY) AND AK_FETCH_DEPS) include(ExternalProject) set(AK_KTX2_INSTALL_DIR "${AK_DEPS_ROOT}/ktx/install") set(AK_KTX2_INCLUDE_DIR "${AK_KTX2_INSTALL_DIR}/include") if(WIN32) set(AK_KTX2_LIBRARY "${AK_KTX2_INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}/ktx.lib") else() set(AK_KTX2_LIBRARY "${AK_KTX2_INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}/libktx.a") endif() ExternalProject_Add(assetkit_ktx2_dep GIT_REPOSITORY https://github.com/KhronosGroup/KTX-Software.git GIT_TAG main GIT_SHALLOW TRUE SOURCE_DIR "${AK_DEPS_ROOT}/ktx" BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/assetkit_ktx2-build" INSTALL_DIR "${AK_KTX2_INSTALL_DIR}" BUILD_BYPRODUCTS "${AK_KTX2_LIBRARY}" CMAKE_ARGS -Wno-dev -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX= -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DKTX_FEATURE_TESTS=OFF -DKTX_FEATURE_TOOLS=OFF -DKTX_FEATURE_DOC=OFF -DKTX_FEATURE_LOADTEST_APPS=OFF -DKTX_FEATURE_STATIC_LIBRARY=ON -DBASISU_SUPPORT_SSE=ON -DBUILD_SHARED_LIBS=OFF) endif() if(NOT AK_KTX2_INCLUDE_DIR OR NOT AK_KTX2_LIBRARY) message(WARNING "KTX2/BasisU decoder requested but libktx was not found. " "Set AK_KTX2_ROOT, enable AK_FETCH_DEPS, or disable " "AK_BUILD_GLTF_KTX2_DECODER. Skipping decoder build for now.") else() add_library(assetkit_ktx2 SHARED src/decoders/gltf/ktx2/assetkit_ktx2.cc) ak_target_cxx_options(assetkit_ktx2) if(TARGET assetkit_ktx2_dep) add_dependencies(assetkit_ktx2 assetkit_ktx2_dep) endif() target_include_directories(assetkit_ktx2 SYSTEM PRIVATE ${AK_KTX2_INCLUDE_DIR}) target_include_directories(assetkit_ktx2 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/deps/cglm/include ${PROJECT_SOURCE_DIR}/deps/ds/include ${PROJECT_SOURCE_DIR}/deps/json/include) target_link_libraries(assetkit_ktx2 PRIVATE ${PROJECT_NAME} ds ${AK_KTX2_LIBRARY}) set_target_properties(assetkit_ktx2 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN YES) if(AK_INSTALL_RPATH) set_target_properties(assetkit_ktx2 PROPERTIES INSTALL_RPATH "${AK_INSTALL_RPATH}") endif() install(TARGETS assetkit_ktx2 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() endif() # Test Configuration if(AK_USE_TEST) include(CTest) enable_testing() add_subdirectory(test) endif() # Install install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(DIRECTORY include/ak DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} PATTERN ".*" EXCLUDE) # Config export(TARGETS ${PROJECT_NAME} NAMESPACE ${PROJECT_NAME}:: FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" ) install(EXPORT ${PROJECT_NAME} FILE "${PROJECT_NAME}Config.cmake" NAMESPACE ${PROJECT_NAME}:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) ================================================ FILE: EXTENSIONS.md ================================================ # Extensions AssetKit handles glTF extensions in three ways: - Typed support for extensions that affect AssetKit's runtime model. - Optional side decoder libraries for compressed payloads. - Preserved JSON payload through `ak_extra()` for app/vendor-specific data. ## Required Extensions `extensionsRequired` is strict. If an extension is required for correct geometry, animation, texture, material, or splat data, AssetKit must implement it or reject the asset. `extensionsUsed` can be more permissive. If an optional extension is not needed for correct loading, AssetKit may preserve the payload in `ak_extra()` so a viewer/tool can inspect it. ## Optional Decoder Libraries Compression and heavy decoders live outside the main C library. AssetKit loads these side libraries only when needed: - `libassetkit_draco` - `libassetkit_meshoptimizer` - `libassetkit_spz` - `libassetkit_ktx2` On Windows they are `assetkit_*.dll`; on macOS `libassetkit_*.dylib`; on Linux `libassetkit_*.so`. AssetKit first searches next to the loaded `libassetkit` binary, then falls back to the platform loader search path. CMake builds the side libraries by default when dependencies are available or can be fetched: ```bash cmake -S . -B build -DCMAKE_BUILD_TYPE=Release cmake --build build ``` Useful options: ```cmake -DAK_FETCH_DEPS=OFF -DAK_BUILD_GLTF_DRACO_DECODER=OFF -DAK_BUILD_GLTF_MESHOPT_DECODER=OFF -DAK_BUILD_GLTF_SPZ_DECODER=OFF -DAK_BUILD_GLTF_KTX2_DECODER=OFF ``` ## Typed Extension Data Some extensions are represented as normal AssetKit fields: - `KHR_materials_variants` - `AkDoc.materialVariants` - `AkMeshPrimitive.variantMappings` - `ak_materialVariantByName()` - `KHR_gaussian_splatting` - `AkMeshPrimitive.gsplat` - splat attributes stay in the primitive input chain - `KHR_animation_pointer` - maps supported JSON pointer targets to AssetKit animation targets SPZ is only a decoder format. Public Gaussian splat data is generic and lives in `AkGaussianSplat`. More detail: [docs/source/extensions.rst](docs/source/extensions.rst) ================================================ FILE: EXTRAS.md ================================================ # Extras AssetKit preserves source-format metadata that it does not need to interpret directly. - COLLADA: `` - glTF: `extras` and preserved `extensions` payloads Use `ak_extra()` to read it: ```c AkTree *extra; extra = ak_extra(object); if (extra) { /* Walk extra->chld, node->next and node->attribs. */ } ``` Use `ak_extra_set()` when custom loader code wants to attach metadata: ```c ak_extra_set(object, extraTree); ``` The returned `AkTree` is owned by the document heap. Do not free it directly. ## COLLADA Older AssetKit code stored COLLADA `` data directly on struct fields such as `node->extra`, `mesh->extra`, `geom->extra` or `material->extra`. Those fields still work. New code should prefer `ak_extra(object)` so the same path works for COLLADA and glTF. ## glTF glTF metadata is stored under a root tree named `extra`: ```text extra extensions KHR_example_extension enabled type=value val=true extras author type=value val=... ``` Each glTF tree node has a `type` attribute: - `object` - `array` - `value` - `null` - `unknown` Array items are repeated child nodes named `item` and preserve source order. Typed AssetKit fields are still preferred for extensions AssetKit understands, for example material variants or Gaussian splat metadata. `ak_extra()` is for metadata, vendor payloads, debug payloads, and extension data that higher-level tools may want to inspect. More detail: [docs/source/extras.rst](docs/source/extras.rst) ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================


C/C++ CI CMake Codacy Badge Coverage Status glTF Badge

Patreon: Become a patron Sponsors on Open Collective Backers on Open Collective


Brand-new modern 3D asset importer, exporter library. This library will include common 3D utils funcs. It is written with C99 but C++ wrappers or other language bindings can be written in the future. This library will try to full support COLLADA specs and glTF specs, plus well-known other 3D formats e.g .obj, .stl, .ply... 📌 There is also an optional renderer library called [Graphics Kernel (Realtime Rendering)](https://github.com/recp/libgk) and [rays (Path/Ray Tracer)](https://github.com/recp/rays) which can render **AssetKit** contents. You can see how to load **AssetKit** to [Graphics Kernel](https://github.com/recp/libgk) in [AssetKit-GL](https://github.com/recp/assetkit-gl) repo. Both renderers and documentation with samples will be updated regularly...

#### 📚 Documentation (In Progress) Almost all functions (inline versions) and parameters will be documented inside the corresponding headers.
Complete documentation: http://assetkit.readthedocs.io Runtime metadata and extension notes: - [Extras and extension data](EXTRAS.md) - [glTF extensions and optional decoders](EXTENSIONS.md) ## 💪 Supported Formats * [ ] Asset Exchange (todo) http://github.com/AssetExchange/spec * [x] COLLADA 1.4 and COLLADA 1.4.1 * [x] COLLADA 1.5 * [x] glTF 2.0 (Embedded or Separated (.gltf), Binary (.glb), Extensions...) * [x] Wavefront Obj (.obj + .mtl) * [x] STL (ASCII, Binary) * [x] PLY (ASCII, Binary) * [ ] 3MF (in progress) * [ ] FBX (License?, probably need to download FBX SDK externally) * [ ] USD and friends (License?) * [ ] Alembic (License?) * [ ] Draco * [ ] X3D * [x] in progress for next... * [ ] Exporter ## 🚀 Features - Single interface for glTF 2.0 (with extensions), COLLADA 1.4/1.4.1/1.5, Wavefront Obj and others... - Very very small and very fast library - Javascript-like API to get URL or ID `obj = ak_getObjectById(doc, objectId)`... - Options to Generate Mesh Normals *(Default: enabled)* - Option to Triangulate Polygons *(Default: enabled)* - Option to change Coordinate System *(Default: enabled)* - Option to calculate Bounding Boxes *(Default: enabled)* - Unique and Flexible Coordinate System - Support multiple coordinate system - Can convert any coordinate system to another with adding transform or with changing transform, vertex data... - Unique and Flexible Memory Management System - Hierarchical unique memory management - When a node is freed then all sub memories will be freed - COLLADA's **sid** and **ID** values are mapped to memory nodes itself to reduce memory size and make it easy to manage things. - Allow attach ID, sid or user data to a memory node - Object-based Asset support; resolve asset element for any element - Bugfix some DAE files - Will be optimized to be fastest, smallest and most flexible, extendible Asset loader. - Uses **mmap** to load files, you can disable this if needed - [ ] Documentation - [x] Cmake support - [ ] Tests ## 🔨 Build AssetKit uses CMake on macOS, Linux and Windows. It can be built as a standalone project, embedded with `add_subdirectory()` or installed and used with `find_package()`. ### Standalone CMake build ```bash $ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release $ cmake --build build $ cmake --install build # [Optional] ``` On multi-config generators such as Visual Studio or Xcode: ```bash $ cmake -S . -B build $ cmake --build build --config Release $ cmake --install build --config Release # [Optional] ``` Release builds use the platform compiler's optimized release mode. AssetKit also has `AK_ENABLE_LTO=ON` for link-time optimization when the compiler and generator support it; it is off by default for predictable cross-platform builds. ##### CMake options with defaults: ```CMake option(AK_SHARED "Shared build" ON) option(AK_STATIC "Static build" OFF) option(AK_USE_TEST "Enable Tests" OFF) option(AK_BUILD_GLTF_DRACO_DECODER "Build optional glTF Draco decoder shim" ON) option(AK_BUILD_GLTF_MESHOPT_DECODER "Build optional glTF meshoptimizer decoder shim" ON) option(AK_FETCH_DEPS "Fetch optional decoder dependencies into AK_DEPS_ROOT when missing" ON) option(AK_ENABLE_LTO "Enable link-time optimization for release builds" OFF) ``` Optional glTF compression decoders are side libraries. They are built next to the main C library by default, but not linked into `libassetkit`. CMake fetches missing decoder dependencies into `deps/` by default, so a normal standalone build produces `libassetkit`, `libassetkit_draco` and `libassetkit_meshoptimizer` when network access is available: ```bash $ cmake -S . -B build $ cmake --build build ``` Use `-DAK_FETCH_DEPS=OFF` with `AK_DRACO_ROOT` / `AK_MESHOPT_ROOT` for offline or packaged builds. Use `-DAK_BUILD_GLTF_DRACO_DECODER=OFF` or `-DAK_BUILD_GLTF_MESHOPT_DECODER=OFF` to skip these side libraries. ### Embedded in another CMake project AssetKit can be used like a submodule: ```cmake cmake_minimum_required(VERSION 3.16) project(my_app LANGUAGES C) add_subdirectory(external/assetkit) add_executable(my_app src/main.c) target_link_libraries(my_app PRIVATE assetkit::assetkit) ``` When embedded, AssetKit does not force a default build type on the parent project. If you do not want CMake to fetch optional decoder dependencies while configuring your parent project, pass: ```cmake set(AK_FETCH_DEPS OFF CACHE BOOL "" FORCE) set(AK_BUILD_GLTF_DRACO_DECODER OFF CACHE BOOL "" FORCE) set(AK_BUILD_GLTF_MESHOPT_DECODER OFF CACHE BOOL "" FORCE) add_subdirectory(external/assetkit) ``` ### Installed package After `cmake --install`, consumers can use: ```cmake find_package(assetkit CONFIG REQUIRED) target_link_libraries(my_app PRIVATE assetkit::assetkit) ``` ### Windows Windows builds are supported through CMake. `git` must be available when `GIT_SUBMODULE=ON` or `AK_FETCH_DEPS=ON`. ```Powershell $ cmake -S . -B build $ cmake --build build --config Release ``` ## Contributors This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. ## Backers Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/assetkit#backer)] ## Sponsors Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/assetkit#sponsor)] ### Trademarks glTF and COLLADA and their logos are trademarks of Khronos Group. ================================================ FILE: appveyor.yml ================================================ image: Visual Studio 2017 build_script: - ps: >- cd win .\build.bat ================================================ FILE: docs/make.bat ================================================ @ECHO OFF pushd %~dp0 REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=python -msphinx ) set SOURCEDIR=source set BUILDDIR=build set SPHINXPROJ=cglm if "%1" == "" goto help %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( echo. echo.The Sphinx module was not found. Make sure you have Sphinx installed, echo.then set the SPHINXBUILD environment variable to point to the full echo.path of the 'sphinx-build' executable. Alternatively you may add the echo.Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% goto end :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% :end popd ================================================ FILE: docs/source/api.rst ================================================ API documentation ================================ .. toctree:: :maxdepth: 1 :caption: API categories: version ================================================ FILE: docs/source/build.rst ================================================ Build AssetKit ================================ | **AssetKit** core uses bundled submodules. Optional glTF decoder side libraries can fetch their decoder dependencies when enabled. The same CMake project can be used standalone, as a subdirectory in another CMake project, or as an installed package. Standalone CMake build: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: bash :linenos: $ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release $ cmake --build build $ cmake --install build # [Optional] With multi-config generators such as Visual Studio or Xcode: .. code-block:: bash :linenos: $ cmake -S . -B build $ cmake --build build --config Release $ cmake --install build --config Release # [Optional] The build folder contains the main AssetKit library and, by default, optional glTF decoder side libraries. **CMake Options:** .. code-block:: CMake :linenos: option(AK_SHARED "Shared build" ON) option(AK_STATIC "Static build" OFF) option(AK_USE_C99 "" OFF) # C11 option(AK_USE_TEST "Enable Tests" OFF) # for make check - make test option(AK_BUILD_GLTF_DRACO_DECODER "Build optional glTF Draco decoder shim" ON) option(AK_BUILD_GLTF_MESHOPT_DECODER "Build optional glTF meshoptimizer decoder shim" ON) option(AK_FETCH_DEPS "Fetch optional decoder dependencies into AK_DEPS_ROOT when missing" ON) option(AK_ENABLE_LTO "Enable link-time optimization for release builds" OFF) ``AK_ENABLE_LTO`` is optional. Release builds already use optimized compiler flags; LTO is off by default because support differs by compiler, generator and platform. Embedded in another CMake project: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: CMake :linenos: cmake_minimum_required(VERSION 3.16) project(my_app LANGUAGES C) add_subdirectory(external/assetkit/) add_executable(my_app src/main.c) target_link_libraries(my_app PRIVATE assetkit::assetkit) When embedded, AssetKit does not force a default build type on the parent project. If dependency fetching is not wanted during parent configure: .. code-block:: CMake :linenos: set(AK_FETCH_DEPS OFF CACHE BOOL "" FORCE) set(AK_BUILD_GLTF_DRACO_DECODER OFF CACHE BOOL "" FORCE) set(AK_BUILD_GLTF_MESHOPT_DECODER OFF CACHE BOOL "" FORCE) add_subdirectory(external/assetkit/) Installed package: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: CMake :linenos: find_package(assetkit CONFIG REQUIRED) target_link_libraries(my_app PRIVATE assetkit::assetkit) Optional glTF compression decoders: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: bash :linenos: $ cmake -S . -B build $ cmake --build build Decoder side libraries are built next to ``libassetkit`` by default, but not linked into the main C library. CMake fetches missing decoder dependencies into ``deps/`` by default. Use ``-DAK_FETCH_DEPS=OFF`` with ``AK_DRACO_ROOT`` / ``AK_MESHOPT_ROOT`` for offline or packaged builds. Use ``-DAK_BUILD_GLTF_DRACO_DECODER=OFF`` or ``-DAK_BUILD_GLTF_MESHOPT_DECODER=OFF`` to skip these side libraries. Windows: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Windows builds use CMake. ``git`` must be available when ``GIT_SUBMODULE=ON`` or ``AK_FETCH_DEPS=ON``. .. code-block:: bash :linenos: $ cmake -S . -B build $ cmake --build build --config Release Currently tests are not available on Windows. Documentation (Sphinx): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **AssetKit** uses sphinx framework for documentation, it allows lot of formats for documentation. To see all options see sphinx build page: https://www.sphinx-doc.org/en/master/man/sphinx-build.html Example build: .. code-block:: bash :linenos: $ cd assetkit/docs $ sphinx-build source build ================================================ FILE: docs/source/conf.py ================================================ # -*- coding: utf-8 -*- # # AssetKit documentation build configuration file, created by # sphinx-quickstart on Tue Jun 6 20:31:05 2017. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # # import os # import sys # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.doctest', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.mathjax', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode', 'sphinx.ext.githubpages' ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = u'AssetKit' copyright = u'2020, Recep Aslantas' author = u'Recep Aslantas' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = u'0.3.2' # The full version, including alpha/beta/rc tags. release = u'0.3.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = [] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} html_theme_options = { # 'github_banner': 'true', # 'github_button': 'true', # 'github_user': 'recp', # 'github_repo': 'AssetKit', # 'travis_button': 'true', # 'show_related': 'true', # 'fixed_sidebar': 'true' } # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. htmlhelp_basename = 'assetkitdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'AssetKit.tex', u'AssetKit Documentation', u'Recep Aslantas', 'manual'), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'AssetKit', u'AssetKit Documentation', [author], 1) ] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'AssetKit', u'AssetKit Documentation', author, 'AssetKit', 'One line description of project.', 'Miscellaneous'), ] # -- Options for Epub output ------------------------------------------------- # Bibliographic Dublin Core info. epub_title = project epub_author = author epub_publisher = author epub_copyright = copyright # The unique identifier of the text. This can be a ISBN number # or the project homepage. # # epub_identifier = '' # A unique identification for the text. # # epub_uid = '' # A list of files that should not be packed into the epub file. epub_exclude_files = ['search.html'] # -- Extension configuration ------------------------------------------------- # -- Options for todo extension ---------------------------------------------- # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True ================================================ FILE: docs/source/getting_started.rst ================================================ Getting Started ================================ There are lot of file formats out there. It is not easy to implement each of them individually in an engine or software. **AssetKit** provides single extensible interface for all file formats that is supported. **AssetKit** tries to full support all major file formats. ================================================ FILE: docs/source/index.rst ================================================ .. cglm documentation master file, created by sphinx-quickstart on Tue Jun 6 20:31:05 2017. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. .. image:: assetkit.png :width: 492px  :height: 297px :align: center | **AssetKit** is brand-new 2D/3D asset importer, exporter and util library that is written in C language. C++ wrappers or other language bindings can be written in the future. This library will include common 3D util funcs. Supported Formats ================================================================================ * [x] COLLADA 1.4 and COLLADA 1.4.1 * [x] COLLADA 1.5 * [x] glTF 2.0 (Embedded or Separated (.gltf), Binary (.glb), Extensions...) * [x] Wavefront Obj (.obj + .mtl) * [x] STL (ASCII, Binary) * [x] PLY (ASCII, Binary) * [ ] USD and friends (License?) * [ ] Alembic (License?) * [ ] Draco * [ ] X3D * [x] in progress for next... Features ================================================================================ * Very very small, very fast and flexible library * Single interface for glTF 2.0 (with extensions), COLLADA 1.4/1.4.1/1.5, Wavefront Obj and others... * Javascript-like API to get URL or ID `obj = ak_getObjectById(doc, objectId)`... * Options to Generate Mesh Normals *(Default: enabled)* * Option to Triangulate Polygons *(Default: enabled)* * Option to change Coordinate System *(Default: enabled)* * Option to calculate Bounding Boxes *(Default: enabled)* * Unique and Flexible Coordinate System * Support multiple coordinate system * Can convert any coordinate system to another with adding transform or with changing transform, vertex data... * Unique and Flexible Memory Management System * Hierarchical unique memory management * When a node is freed then all sub memories will be freed * COLLADA's **sid** and **ID** values are mapped to memory nodes itself to reduce memory size and make it easy to manage things. * Allow attach ID, sid or user data to a memory node * Object-based Asset support; resolve asset element for any element * Bugfix some DAE files * Will be optimized to be fastest, smallest and most flexible, extendible Asset loader. * and others... .. toctree:: :maxdepth: 2 :caption: Getting Started: build getting_started .. toctree:: :maxdepth: 3 :caption: Tutorials: quick_intro .. toctree:: :maxdepth: 2 :caption: API: api .. toctree:: :maxdepth: 2 :caption: Options: opt Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` ================================================ FILE: docs/source/opt.rst ================================================ .. default-domain:: C Options =============================================================================== Currently **AssetKit** provides global options but in th future document based options may be supported. Options / Preferences / Settings Design ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To make things simplier and easy to manage, **AssetKit** provides options as **key** - **value** pair. The key always is the :code:`AkOption` enum type. The value's type is :code:`uintptr_t`, so you can pass integers, enums and pointers (by casting to `uintptr_t` e.g. :code:`(uintptr_t)(void *)myPointer`). Documented global options: .. code-block:: c typedef enum AkOption { AK_OPT_INDICES_DEFAULT = 0, /* false */ AK_OPT_INDICES_SINGLE_INTERLEAVED = 1, /* false */ AK_OPT_INDICES_SINGLE_SEPARATE = 2, /* false */ AK_OPT_INDICES_SINGLE = 3, /* false */ AK_OPT_NOINDEX_INTERLEAVED = 4, /* true */ AK_OPT_NOINDEX_SEPARATE = 5, /* true */ AK_OPT_COORD = 6, /* Y_UP */ AK_OPT_DEFAULT_ID_PREFIX = 7, /* id- */ AK_OPT_COMPUTE_BBOX = 8, /* false */ AK_OPT_TRIANGULATE = 9, /* true */ AK_OPT_GEN_NORMALS_IF_NEEDED = 10, /* true */ AK_OPT_DEFAULT_PROFILE = 11, /* COMMON */ AK_OPT_EFFECT_PROFILE = 12, /* true */ AK_OPT_TECHNIQUE = 13, /* "common" */ AK_OPT_TECHNIQUE_FX = 14, /* "common" */ AK_OPT_ZERO_INDEXED_INPUT = 15, /* false */ AK_OPT_IMAGE_LOAD_FLIP_VERTICALLY = 16, /* true */ AK_OPT_ADD_DEFAULT_CAMERA = 17, /* true */ AK_OPT_ADD_DEFAULT_LIGHT = 18, /* false */ AK_OPT_COORD_CONVERT_TYPE = 19, /* DEFAULT */ AK_OPT_BUGFIXES = 20, /* TRUE */ AK_OPT_GLTF_EXT_SPEC_GLOSS = 21, /* TRUE */ AK_OPT_COMPUTE_EXACT_CENTER = 22 /* FALSE */ } AkOption; The comment at right contains default value for that option. All options will be documented below. (**TODO**) As you can see and imagine, not all options are integer or boolean. For instance to set :code:`Y_UP` for :code:`AK_OPT_COORD` option, you need to cast :code:`Y_UP` opinter to :code:`uintptr_t` type. Sample options: .. code-block:: c /* compute bounding box for mesh and for its primitives */ ak_opt_set(AK_OPT_COMPUTE_BBOX, true); /* triangulate meshes that are not triangles e.g. polygons... */ ak_opt_set(AK_OPT_TRIANGULATE, true); /* change UP axis to Y UP, see coordinate sys documentation. */ ak_opt_set(AK_OPT_TRIANGULATE, (uintptr_t)AK_YUP); Functions: 1. :c:func:`ak_opt_set` #. :c:func:`ak_opt_get` #. :c:func:`ak_opt_set_str` .. c:function:: void ak_opt_set(AkOption option, uintptr_t value) Sets an option by key and value. Pass integers, booleans or pointers as value. Value needs to be casted to **uintptr_t**. Pass only supported values for a key. Parameters: | *[in]* **option** option (see AkOption enum) | *[in]* **value** option value .. c:function:: uintptr_t ak_opt_get(AkOption option) Get value of option as **uintptr_t**. If the option is pointer than you need to cast it to pointer. For instance :code:`(AkCoordSys *)ak_opt_get(AK_OPT_COORD)` or :code:`(void *)ak_opt_get(AK_OPT_COORD)`. If there are no warnings then you don't need to cast result to Boolean or Integers for Boolean/Integer options. For instance :code:`if (ak_opt_get(AK_OPT_TRIANGULATE)) ...` Parameters: | *[in]* **option** option (see AkOption enum) .. c:function:: void ak_opt_set_str(AkOption option, const char *value) Similar to :c:func:`ak_opt_set` but it accepts null terminated string parameter and it uses :c:func:`ak_strdup` to duplicate string to keep it. Then it casts duplicated string to :code:`uintptr_t`, so you can get value anytime with :c:func:`ak_opt_get`. **NOTE:** When you set new value then the old value will be free-ed. If you need to keep old value then you must duplicate it yourself. Otherwise memory leaks would occoured... Parameters: | *[in]* **option** option (see AkOption enum) | *[in]* **value** option value as null-terminated string ================================================ FILE: docs/source/quick_intro.rst ================================================ Quick Implementation =================================== Assuming you already followed Build instructions and Getting Started sections. Also assuming you already linked **AssetKit** to your project and set include paths. 1. Include ---------------- **AssetKit** uses **ak** prefix for all functions and type names. To include **AssetKit** .. code-block:: c :linenos: #include /* other headers */ #include ... 2. Preparing ---------------- You may want to prepare loader before call :c:func:`ak_load` load function. a. Setting Image loader ~~~~~~~~~~~~~~~~~~~~~~~~ There was image loader inside **AssetKit** but it has been dropped to make it more generic. Becasue you may already have an image loader e.g. stb_image ... **AssetKit** can trigger images and cache them for you, if it is already loaded than it will return loaded contents. You need to set loader as below. The example used stb_image but you mat use another image loader... .. code-block:: c :linenos: void* imageLoadFromFile(const char * __restrict path, int * __restrict width, int * __restrict height, int * __restrict components) { return stbi_load(path, width, height, components, 0); } void* imageLoadFromMemory(const char * __restrict data, size_t len, int * __restrict width, int * __restrict height, int * __restrict components) { return stbi_load_from_memory((stbi_uc const*)data, (int)len, width, height, components, 0); } void imageFlipVerticallyOnLoad(bool flip) { stbi_set_flip_vertically_on_load(flip); } /* Call this before loading document e.g. ak_load() or images */ ak_imageInitLoader(imageLoadFromFile, imageLoadFromMemory, imageFlipVerticallyOnLoad); Just ensure that you set image loader before loading images. It is good to set it once before :c:func:`ak_load`. b. Setting Options if Needed ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Make sure that you set UP axis if you want to different one from **Y_UP**. For instance if you want to load contents as **Z_UP** .. code-block:: c ak_opt_set(AK_OPT_COORD, (uintptr_t)AK_ZUP); see options in the documentation. If the default values don't work for you then just change them before :c:func:`ak_load`. 3. Load Document ---------------- Use :c:func:`ak_load()` to load a 3D file. It returns :c:type:`AkDoc` which contains everything you need. You must check the result/return, it must be AK_OK otherwise, document is not loaded or falied at some point. .. code-block:: c :linenos: AkDoc *doc; AkResult ret; if ((ret = ak_load(&doc, "[Path to a file e.g ./sample.gltf]", NULL) != AK_OK) { printf("Document couldn't be loaded"); return; } or .. code-block:: c :linenos: AkDoc *doc; AkResult ret; ret = ak_load(&doc, "[Path to a file e.g ./sample.gltf]", NULL); if (ret != AK_OK) { printf("Document couldn't be loaded"); return; } **doc** is passed as reference, if the result is success than the document will be set that reference parameter. **AssetKit** will try to load referenced textures, images, binary files... so you must only pass original file, not folder. Now you loaded the document you want. See next step. ------ There are two ways to load geometries from loaded document. a. Load scene[s], nodes than load referenced geometries b. Load all geometries in the document The second way may cause to load unused geometries, because a geometry may not be referenced in scenes. It is better to follow scene > node > instance geometry > geometry path. 4. Load Scene[s] ---------------- **AssetKit** can load scenes, nodes, geometries and so on. If the file you loaded doesn't support scenes e.g Wavefront Obj. AssetKit creates a default scene for that file formats and adds reference of geometries to that scene. There are **scene library** and **scene** in AssetKit **document**. The **scene** is the active scene for rendering, it references a scene from library. .. code-block:: c :linenos: AkInstanceBase *instScene; AkVisualScene *scene; if ((instScene = doc->scene.visualScene)) { scene = (AkVisualScene *)ak_instanceObject(doc->scene.visualScene); } `scene.visualScene` is instance reference ( :c:type:`AkInstanceBase` ), any scene may be instanced with this link/object. Another instance objects may have different types e.g. instance geometry (inherited from :c:type:`AkInstanceBase`). We need to get actual scene object from instance object. There are a few helpers for this task. But we will use :c:func:`ak_instanceObject` here. 5. Load Nodes[s] ---------------- After you get a scene, you can iterate through root nodes. There are also nodes in NodeLibrary in document but in this way you only get used nodes. There are a few elements in nodes - Node Transform - One or more instance geometries - One or more instance cameras - One or more instance lights - One or more instance nodes - One or more child nodes - Bounding Box as AABB of Node - ... 5.1 Transforms ~~~~~~~~~~~~~~~~ You must multiply node's transform with its parent to get transform in WORLD space for each node recursively. A node can contain Matrix or individual Transform Elements like Rotation, Translation or Scaling. **AssetKit** also provides a util to combine these individual transforms into matrix with :c:func:`ak_transformCombine`. **AssetKit** does not combines them automatically because they may be referenced to animated individually. 5.2 Instance Geometries ~~~~~~~~~~~~~~~~~~~~~~~~ This is the one of critical sections to understand. Nodes uses :c:type:`AkInstanceGeometry` type to reference a :c:type:`AkGeometry`. A :c:type:`AkInstanceGeometry` object may store these informations: * Instance to geometry * Material to bind * Instance to morpher * Instance to skinner 5.2.1 Instance to geometry | Loading Geometry ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A node may contain multiple geometries, so you must iterate each one and get the geometry with :c:func:`ak_instanceObject` function. After you get the geometry you can load geeometry elements. A :c:type:`AkGeometry` object can contain one of mesh, spline and brep. .. code-block:: c :linenos: AkObject *prim; AkResult ret; /* return if the geometry is already loaded, you can use a RBTree or HasMap... (see https://github.com/recp/ds) */ prim = geom->gdata; switch ((AkGeometryType)prim->type) { case AK_GEOMETRY_MESH: /* load mesh */ ret = loadMesh(...); break; default: ret = AK_ERR; } return ret; Now it is time to load a mesh. 5.2.2 Loading mesh ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This tutorial will only cover loading meshes, extra tutorials may be provided in the future for loading curves, nurbs... A mesh object is packed as :c:type:`AkObject` inside :c:type:`AkGeometry`. In previous section you may see that we have a switch control to check whether we have a mesh inside geometry or not. **AssetKit** provides unique design to store this kind of objects with :c:type:`AkObject`. (Think :c:type:`AkObject` as **Object** class in .NET or **NSObject** in ObjC.) Otherwise we would store additional pointers or inherits Mesh from Geometry and then cast it to mesh. This is another option of course, even **AssetKit** may change to this design in the future if needed. Currently we are not doing this because geometry object is top container. **AssetKit** provides a helper to get object from :c:type:`AkObject` with :c:func:`ak_objGet` macro. We can get :c:type:`AkMesh` object from :c:type:`AkGeometry` as .. code-block:: c :linenos: AkMesh *mesh; mesh = ak_objGet(geom->gdata); Now we have mesh object. Let's inspect a mesh type. A mesh contains one or more primitives (or submeshes) as :c:type:`AkMeshPrimitive`. Each primitive contains AABB, the mesh also contains an AABB which is sum of all. A mesh also contains default weights for morph targets but a Node in Scene object can override that. 5.2.2.1 Loading mesh primitives ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A mesh primitive may be one of :c:type:`AkLines`, :c:type:`AkPolygon` or :c:type:`AkTriangles`. :c:type:`AkMeshPrimitive` is base type for all of them. You can cast them to :c:type:`AkMeshPrimitive` or you can use **.base** member. ================================================ FILE: docs/source/version.rst ================================================ .. default-domain:: C version ================================================================================ Header: ak/version.h **AssetKit** uses semantic versioning (http://semver.org) which is MAJOR.MINOR.PATCH | **AK_VERSION_MAJOR** is major number of the version. | **AK_VERSION_MINOR** is minor number of the version. | **AK_VERSION_PATCH** is patch number of the version. every release increases these numbers. You can check existing version by including `ak/version.h` ================================================ FILE: include/ak/animation.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_animation_h #define assetkit_animation_h #ifdef __cplusplus extern "C" { #endif #include "common.h" #include #include #include #include "type.h" #include "source.h" #include "url.h" typedef enum AkSamplerBehavior { AK_SAMPLER_BEHAVIOR_UNDEFINED = 0, AK_SAMPLER_BEHAVIOR_CONSTANT = 1, AK_SAMPLER_BEHAVIOR_GRADIENT = 2, AK_SAMPLER_BEHAVIOR_CYCLE = 3, AK_SAMPLER_BEHAVIOR_OSCILLATE = 4, AK_SAMPLER_BEHAVIOR_CYCLE_RELATIVE = 5 } AkSamplerBehavior; typedef enum AkTargetPropertyType { AK_TARGET_UNKNOWN = 0, AK_TARGET_X = 1, AK_TARGET_Y = 2, AK_TARGET_Z = 3, AK_TARGET_XY = 4, AK_TARGET_XYZ = 5, AK_TARGET_ANGLE = 6, AK_TARGET_POSITION = 7, AK_TARGET_SCALE = 8, AK_TARGET_ROTATE = 9, AK_TARGET_QUAT = 10, AK_TARGET_WEIGHTS = 11, AK_TARGET_FLOAT = 12, AK_TARGET_VEC2 = 13, AK_TARGET_VEC3 = 14, AK_TARGET_VEC4 = 15, AK_TARGET_COLOR = 16, AK_TARGET_BOOL = 17 } AkTargetPropertyType; typedef enum AkInterpolationType { AK_INTERPOLATION_UNKNOWN = 0, AK_INTERPOLATION_LINEAR = 1, AK_INTERPOLATION_BEZIER = 2, AK_INTERPOLATION_CARDINAL = 3, AK_INTERPOLATION_HERMITE = 4, AK_INTERPOLATION_BSPLINE = 5, AK_INTERPOLATION_STEP = 6, AK_INTERPOLATION_MAXLEN = 255 } AkInterpolationType; typedef struct AkAnimSampler { AkOneWayIterBase base; AkInput *input; AkInput *inputInput; AkInput *outputInput; AkInput *interpInput; AkInput *inTangentInput; AkInput *outTangentInput; AkInterpolationType uniInterpolation; AkSamplerBehavior pre; AkSamplerBehavior post; } AkAnimSampler; typedef struct AkResolvedTarget { void *target; uint32_t off; bool isPartial; } AkResolvedTarget; typedef struct AkChannel { struct AkChannel *next; const char *target; AkResolvedTarget *resolvedTarget; AkURL source; AkTargetPropertyType targetType; } AkChannel; typedef struct AkAnimation { AkOneWayIterBase base; struct AkAnimation *animation; /* subanimation */ AkAnimSampler *sampler; AkChannel *channel; const char *name; AkTree *extra; /* TODO: WILL BE DELETED */ AkSource *source; } AkAnimation; /*! * Per-frame transform sequence produced by ak_nodeBakeAnimation(). * `matrices` is `count × 16` floats, column-major (cglm convention), * each block mapping a node-local point into its parent's space. * `times` is `count` floats, parallel to the matrix array. * * Free with ak_free(out) — the inner buffers were sub-allocated under * the struct and cascade in the AssetKit heap. */ typedef struct AkBakedAnimation { float *matrices; /* count × 16 floats, column-major */ float *times; /* count floats */ uint32_t count; } AkBakedAnimation; AK_INLINE bool ak_channelTargetIsPartial(const AkChannel *ch) { return ch->target && strchr(ch->target, '.') != NULL; } /** * returns NULL if no attribute (whole target animation), * otherwise pointer into ch->target after the '.' * result lifetime tied to ch->target — do NOT free */ AK_INLINE const char * ak_channelTargetAttr(const AkChannel *ch) { const char *dot; if (!ch->target || !(dot = strchr(ch->target, '.'))) return NULL; return dot + 1; } AK_INLINE AkResolvedTarget ak_channelTarget(AkContext * __restrict ctx, AkChannel * __restrict ch) { const char *sidAttrib; AkResolvedTarget resolved = {0}; uint32_t attrOff; /** glTF (and DAE post-fixup) provide a pre-resolved target. Honor it first — the SID string in ch->target is then optional and used only as a debug/lookup hint. glTF example: target => "translation" ch->resolvedTarget = AkResolvedTarget* with target = AkTranslate* and off = 0 (glTF animates the whole transform element). DAE example for morph weights: target => "morph-weights(0)" dae_fixup_channel resolves the source-and-(idx) pattern at fixup time and writes ch->resolvedTarget = AkResolvedTarget* with target = AkInstanceMorph*, off = idx, isPartial = true. */ if (ch->resolvedTarget) return *ch->resolvedTarget; /** DAE SID path: "node1/translate.Y" resolved.target = AkTranslate*, sidAttrib = "Y" */ if (ch->target) { if ((resolved.target = ak_sid_resolve(ctx, ch->target, &sidAttrib)) && (attrOff = ak_sid_attr_offset(sidAttrib)) != UINT32_MAX) { resolved.isPartial = sidAttrib != NULL; resolved.off = attrOff; } else { resolved.target = NULL; } } return resolved; } #define ak_inputBegin(INP, T) (*(T*)INP->data) #define ak_inputEnd(INP, T) (*(T*)((char*)INP->data + INP->len - sizeof(T))) /*! * @brief Test whether two animations would write to any of the same * animatable slot. Two channels conflict iff they resolve (via * ak_channelTarget) to the same target pointer AND either at least * one is a whole-target write, or they share the same partial slot * offset. * * Useful for runtime players that pick which animations may run in * parallel — overlapping writes otherwise produce undefined ordering. * * @param ctx resolution context (used to evaluate SID-targeted channels) * @param a first animation * @param b second animation * @return true iff any pair of channels conflicts */ AK_EXPORT bool ak_animationsConflict(AkContext * __restrict ctx, AkAnimation * __restrict a, AkAnimation * __restrict b); /*! * @brief Build the maximal conflict-free set anchored at `primary`. * * `primary` is always selected (it's the animation the caller wants * to activate). Then each candidate is tested against everything * already selected — added if it doesn't conflict with any of them. * * First-fit greedy: candidate iteration order decides which side * of a conflict wins. Pass candidates in the priority order you * want (typically: doc order with `primary` excluded). * * Use case: a UI like "user clicked Animation 2 — what other * animations can stay enabled in parallel?" * * @param ctx resolution context * @param primary anchor animation (must be in result, may be NULL) * @param candidates array of candidate AkAnimation* pointers * @param candidatesCount length of candidates * @param outCompatible pre-allocated buffer of at least * `candidatesCount + 1` AkAnimation* slots * @return count of selected animations (= written into outCompatible) */ AK_EXPORT size_t ak_animationsCompatibleSet(AkContext * __restrict ctx, AkAnimation * __restrict primary, AkAnimation ** __restrict candidates, size_t candidatesCount, AkAnimation ** __restrict outCompatible); /*! * @brief Total number of animations across every animation library on the * document. Useful for sizing buffers passed to the *FromDoc * compatible-set helper. */ AK_EXPORT size_t ak_animationsCount(struct AkDoc * __restrict doc); /*! * @brief Convenience over `ak_animationsCompatibleSet` that walks the * document's animation libraries itself — so callers don't have to * materialise a `candidates[]` array. * * @param ctx resolution context * @param doc the AssetKit document * @param primary anchor animation (must be in result, may be NULL) * @param outCompatible pre-allocated buffer of at least * `ak_animationsCount(doc) + 1` slots * @return count of selected animations */ AK_EXPORT size_t ak_animationsCompatibleSetFromDoc(AkContext * __restrict ctx, struct AkDoc * __restrict doc, AkAnimation * __restrict primary, AkAnimation ** __restrict outCompatible); /*! * @brief Hint that the node's animation should be baked rather than * driven via per-property channels. * * Returns true when the node's transform chain holds 2+ rotate * elements — the canonical case is a Maya joint with * jointOrient{XYZ} + rotate{XYZ} (six elements in one * chain, only three Euler slots in any decomposed-property * animation API: SCNNode.eulerAngles, three.js Object3D.rotation, * Filament TransformManager rotation). * * Renderers that animate via decomposed properties MUST bake * these nodes — partial rotates clobber each other in the * Euler slot. Renderers that compose joint world matrices on * the CPU per frame (assetkit-opengl/gk pattern) don't need * this and can keep their per-channel walk. */ AK_EXPORT bool ak_nodeNeedsBaking(struct AkNode * __restrict node); /*! * @brief Sample every animation channel that targets any AkObject in * `node->transform` (translate / rotate / scale / matrix / * skew / quat) on a shared time grid and emit a stream of 4×4 * local matrices. * * Time grid is the union of the involved channels' keyframe * times (no resampling — every original keyframe is preserved * exactly; channels that lack a value at some t are linearly * interpolated). STEP interpolation is honored; BEZIER / * HERMITE fall back to LINEAR (the bake is keyframe-aligned, * so engine-side interpolation can refine the curve). * * Static AkObjects in the chain (those with no targeting * channel) keep their authored values — bind pose is preserved * between animated frames. The function snapshot/restores * animated AkObject state so callers can keep using * node->transform for bind-pose composition afterwards. * * Output AkBakedAnimation is heap-allocated; caller frees with * ak_free(out). Returns NULL when the node has no transform or * no channel targets any element of its chain. */ AK_EXPORT AkBakedAnimation* ak_nodeBakeAnimation(struct AkDoc * __restrict doc, struct AkNode * __restrict node); #ifdef __cplusplus } #endif #endif /* assetkit_animation_h */ ================================================ FILE: include/ak/assetkit.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_h #define assetkit_h #include #include #include "common.h" #ifdef __cplusplus extern "C" { #endif struct FList; struct FListItem; struct AkBuffer; struct AkLibrary; /* End Core Value Types */ #include "core-types.h" #include "memory.h" #include "coord.h" #include "url.h" #include "type.h" typedef enum AkFileType { AK_FILE_TYPE_AUTO = 0, AK_FILE_TYPE_COLLADA = 1, AK_FILE_TYPE_GLTF = 2, AK_FILE_TYPE_WAVEFRONT = 3, AK_FILE_TYPE_STL = 4, AK_FILE_TYPE_PLY = 5, AK_FILE_TYPE_3MF = 6 } AkFileType; typedef enum AkAltitudeMode { AK_ALTITUDE_RELATIVETOGROUND = 0, AK_ALTITUDE_ABSOLUTE = 1 } AkAltitudeMode; typedef enum AkFace { AK_FACE_POSITIVE_X = 1, AK_FACE_NEGATIVE_X = 2, AK_FACE_POSITIVE_Y = 3, AK_FACE_NEGATIVE_Y = 4, AK_FACE_POSITIVE_Z = 5, AK_FACE_NEGATIVE_Z = 6 } AkFace; typedef enum AkChannelFormat { AK_CHANNEL_FORMAT_RGB = 1, AK_CHANNEL_FORMAT_RGBA = 2, AK_CHANNEL_FORMAT_RGBE = 3, AK_CHANNEL_FORMAT_L = 4, AK_CHANNEL_FORMAT_LA = 5, AK_CHANNEL_FORMAT_D = 6, AK_CHANNEL_FORMAT_XYZ = 7, AK_CHANNEL_FORMAT_XYZW = 8 } AkChannelFormat; typedef enum AkRangeFormat { AK_RANGE_FORMAT_SNORM = 1, AK_RANGE_FORMAT_UNORM = 2, AK_RANGE_FORMAT_SINT = 3, AK_RANGE_FORMAT_UINT = 4, AK_RANGE_FORMAT_FLOAT = 5 } AkRangeFormat; typedef enum AkPrecisionFormat { AK_PRECISION_FORMAT_DEFAULT = 1, AK_PRECISION_FORMAT_LOW = 2, AK_PRECISION_FORMAT_MID = 3, AK_PRECISION_FORMAT_HIGHT = 4, AK_PRECISION_FORMAT_MAX = 5 } AkPrecisionFormat; typedef enum AkInputSemantic { /* read semanticRaw */ AK_INPUT_OTHER = 0, AK_INPUT_BINORMAL = 1, AK_INPUT_COLOR = 2, AK_INPUT_CONTINUITY = 3, AK_INPUT_IMAGE = 4, AK_INPUT_INPUT = 5, AK_INPUT_IN_TANGENT = 6, AK_INPUT_INTERPOLATION = 7, AK_INPUT_INV_BIND_MATRIX = 8, AK_INPUT_JOINT = 9, AK_INPUT_LINEAR_STEPS = 10, AK_INPUT_MORPH_TARGET = 11, AK_INPUT_MORPH_WEIGHT = 12, AK_INPUT_NORMAL = 13, AK_INPUT_OUTPUT = 14, AK_INPUT_OUT_TANGENT = 15, AK_INPUT_POSITION = 16, AK_INPUT_TANGENT = 17, AK_INPUT_TEXBINORMAL = 18, AK_INPUT_TEXCOORD = 19, AK_INPUT_TEXTANGENT = 20, AK_INPUT_UV = 21, AK_INPUT_WEIGHT = 22 } AkInputSemantic; typedef enum AkCurveElementType { AK_CURVE_LINE = 1, AK_CURVE_CIRCLE = 2, AK_CURVE_ELLIPSE = 3, AK_CURVE_PARABOLA = 4, AK_CURVE_HYPERBOLA = 5, AK_CURVE_NURBS = 6, } AkCurveElementType; typedef enum AkSurfaceElementType { AK_SURFACE_CONE = 1, AK_SURFACE_PLANE = 2, AK_SURFACE_CYLINDER = 3, AK_SURFACE_NURBS_SURFACE = 4, AK_SURFACE_SPHERE = 5, AK_SURFACE_TORUS = 6, AK_SURFACE_SWEPT_SURFACE = 7 } AkSurfaceElementType; typedef enum AkInstanceType { AK_INSTANCE_NODE = 1, AK_INSTANCE_CAMERA = 2, AK_INSTANCE_LIGHT = 3, AK_INSTANCE_GEOMETRY = 4, AK_INSTANCE_IMAGE = 5, AK_INSTANCE_CONTROLLER = 6, AK_INSTANCE_EFFECT = 7 } AkInstanceType; typedef struct AkValue { void *value; AkTypeDesc type; } AkValue; typedef struct AkTreeNodeAttr { const char *name; char *val; struct AkTreeNodeAttr *next; struct AkTreeNodeAttr *prev; } AkTreeNodeAttr; typedef struct AkTreeNode { AkTreeNodeAttr *attribs; const char *name; char *val; struct AkTreeNode *chld; struct AkTreeNode *parent; struct AkTreeNode *next; struct AkTreeNode *prev; unsigned long attrc; unsigned long chldc; } AkTreeNode; typedef struct AkTreeNode AkTree; /*! * @brief Return optional metadata attached to an AssetKit object. * * COLLADA and preserved glTF extras/extensions are exposed as * AkTree. The returned tree is owned by the document heap. */ AK_EXPORT AkTree* ak_extra(void * __restrict obj); /*! * @brief Attach optional metadata to an AssetKit object. */ AK_EXPORT void ak_extra_set(void * __restrict obj, AkTree * __restrict extra); #include "source.h" typedef struct AkUnit { const char * name; double dist; } AkUnit; typedef struct AkColorRGBA { AkFloat R; AkFloat G; AkFloat B; AkFloat A; } AkColorRGBA; typedef union AkColor { AK_ALIGN(16) AkColorRGBA rgba; AK_ALIGN(16) AkFloat4 vec; } AkColor; AK_INLINE bool ak_colorLessThanOne(AkColor color) { return color.rgba.R < 0.999 || color.rgba.G < 0.999 || color.rgba.B < 0.999 || color.rgba.A < 0.999 ; } AK_INLINE float ak_sRGB_linearf(float channel) { if (channel <= 0.04045) { return channel / 12.92; } else { return powf((channel + 0.055) / 1.055, 2.4); } } AK_INLINE float ak_linear_sRGBf(float channel) { if (channel <= 0.0031308f) { return channel * 12.92f; } else { return 1.055f * powf(channel, 1.0f / 2.4f) - 0.055f; } } AK_INLINE void ak_sRGB_linear(AkColor * __restrict color) { color->rgba.R = ak_sRGB_linearf(color->rgba.R); color->rgba.G = ak_sRGB_linearf(color->rgba.G); color->rgba.B = ak_sRGB_linearf(color->rgba.B); } AK_INLINE void ak_linear_sRGB(AkColor * __restrict color) { color->rgba.R = ak_linear_sRGBf(color->rgba.R); color->rgba.G = ak_linear_sRGBf(color->rgba.G); color->rgba.B = ak_linear_sRGBf(color->rgba.B); } typedef struct AkContributor { const char * author; const char * authorEmail; const char * authorWebsite; const char * authoringTool; const char * comments; const char * copyright; const char * sourceData; struct AkContributor * next; } AkContributor; typedef struct AkAltitude { AkAltitudeMode mode; double val; } AkAltitude; typedef struct AkGeoLoc { double lng; double lat; AkAltitude alt; } AkGeoLoc; typedef struct AkCoverage { AkGeoLoc geoLoc; } AkCoverage; typedef struct AkAssetInf { AkCoordSys *coordSys; AkUnit *unit; AkContributor *contributor; AkCoverage *coverage; const char *subject; const char *title; const char *keywords; const char *revision; AkTree *extra; time_t created; time_t modified; } AkAssetInf; typedef struct AkDocInf { AkAssetInf base; const char *dir; const char *name; size_t dirlen; AkFileType ftype; bool flipImage; } AkDocInf; typedef struct AkTechnique { const char * profile; /** * @brief * COLLADA Specs 1.5: * This XML Schema namespace attribute identifies an additional schema * to use for validating the content of this instance document. Optional. */ const char * xmlns; AkTree * chld; struct AkTechnique * next; } AkTechnique; /* FX */ /* Effects */ /* * base type of param */ typedef struct AkParam { const char *ref; struct AkParam *prev; struct AkParam *next; } AkParam; typedef struct AkHexData { const char *format; const char *hexval; /* hex value */ void *data; /* binary value */ } AkHexData; typedef struct AkInitFrom { struct AkInitFrom *next; const char *ref; const char *resolvedFullPath; AkHexData *hex; struct AkBuffer *buff; const char *buffMime; AkFace face; AkUInt mipIndex; AkUInt depth; AkInt arrayIndex; AkBool mipsGenerate; } AkInitFrom; struct AkNode; typedef struct AkInstanceBase { /* const char * sid; */ AkURL url; AkInstanceType type; void *object; const char *name; AkTree *extra; struct AkNode *node; struct AkInstanceBase *prev; struct AkInstanceBase *next; } AkInstanceBase; typedef struct AkEvaluateTarget { AkParam *param; AkInstanceBase *instanceImage; unsigned long index; unsigned long slice; unsigned long mip; AkFace face; } AkEvaluateTarget; #include "profile.h" struct AkNewParam; typedef struct AkEffect { /* const char * id; */ const char *name; struct AkNewParam *newparam; AkProfile *profile; AkTree *extra; struct AkEffect *next; /* effect specific options, override global options */ AkProfileType bestProfile; } AkEffect; typedef struct AkInstanceEffect { AkInstanceBase base; AkTechniqueHint *techniqueHint; } AkInstanceEffect; typedef struct AkMaterial { /* const char * id; */ AkOneWayIterBase base; const char *name; AkInstanceEffect *effect; AkTree *extra; } AkMaterial; struct AkAccessor; typedef struct AkInput { const char *semanticRaw; struct AkInput *next; struct AkAccessor *accessor; uint32_t index; /* TEXCOORD0, TEXCOORD1... */ bool isIndexed; AkInputSemantic semantic; uint32_t offset; uint32_t set; /* TODO: WILL BE DELETED */ // AkURL source; } AkInput; struct AkInstanceMaterial; typedef struct AkBindMaterial { AkParam *param; struct AkInstanceMaterial *tcommon; AkTechnique *technique; AkTree *extra; } AkBindMaterial; typedef struct AkInstanceGeometry { AkInstanceBase base; AkBindMaterial *bindMaterial; struct AkInstanceMorph *morpher; struct AkInstanceSkin *skinner; } AkInstanceGeometry; typedef struct AkInstanceNode { AkInstanceBase base; const char *proxy; } AkInstanceNode; /* * TODO: separate all instances to individual nodes? */ struct AkMatrix; struct AkBoundingBox; struct AkTransform; typedef struct AkBind { const char * semantic; const char * target; struct AkBind * next; } AkBind; typedef struct AkBindVertexInput { struct AkBindVertexInput *next; const char *semantic; const char *inputSemantic; AkUInt inputSet; } AkBindVertexInput; typedef struct AkInstanceMaterial { AkInstanceBase base; const char *symbol; AkTechniqueOverride *techniqueOverride; AkBind *bind; AkBindVertexInput *bindVertexInput; } AkInstanceMaterial; typedef struct AkRender { /* const char * sid; */ const char * name; const char * cameraNode; AkStringArrayL * layer; AkInstanceMaterial * instanceMaterial; AkTree * extra; struct AkRender * next; } AkRender; typedef struct AkEvaluateScene { /* const char * id; */ /* const char * sid; */ const char * name; AkRender * render; AkTree * extra; AkBool enable; struct AkEvaluateScene * next; } AkEvaluateScene; struct AkInstanceList; typedef struct AkVisualScene { /* const char * id; */ AkOneWayIterBase base; const char *name; struct AkNode *node; struct AkNode *firstCamNode; /* first found camera */ struct AkInstanceList *cameras; /* all cameras inside scene */ struct AkInstanceList *lights; /* all lights inside scene */ AkEvaluateScene *evaluateScene; struct AkBoundingBox *bbox; AkTree *extra; } AkVisualScene; typedef struct AkScene { /* TODO: instance_physics_scene instance_kinematics_scene */ AkInstanceBase *visualScene; AkTree * extra; } AkScene; struct AkMorph; struct AkSkin; typedef struct AkLibraries { struct AkLibrary *cameras; struct AkLibrary *lights; struct AkLibrary *effects; struct AkLibrary *libimages; struct AkLibrary *materials; struct AkLibrary *geometries; struct AkLibrary *controllers; struct AkLibrary *visualScenes; struct AkLibrary *nodes; struct AkLibrary *animations; struct FListItem *buffers; struct FListItem *accessors; struct FListItem *textures; struct FListItem *samplers; struct FListItem *images; struct AkMorph *morphs; struct AkSkin *skins; } AkLibraries; typedef const char* (*AkFetchFromURLHandler)(const char * __restrict url); typedef struct AkDoc { AkDocInf *inf; AkCoordSys *coordSys; AkUnit *unit; AkTree *extra; void *reserved; void *userData; float loadMillis; AkLibraries lib; AkScene scene; /* KHR_materials_variants: document-level variant names. */ struct AkMaterialVariant *materialVariants; uint32_t materialVariantCount; } AkDoc; #include "context.h" #include "geom.h" #include "image.h" #include "string.h" #include "coord-util.h" #include "library.h" #include "instance.h" #include "cam.h" #include "transform.h" #include "sid.h" #include "light.h" #include "node.h" #include "texture.h" #include "animation.h" #include "controller.h" #include "gsplat.h" AK_EXPORT AkResult ak_load(AkDoc ** __restrict dest, const char * __restrict url, .../* options */); AK_EXPORT void * ak_getId(void * __restrict objptr); AK_EXPORT AkResult ak_setId(void * __restrict objptr, const char * __restrict objectId); AK_EXPORT AkResult ak_moveId(void * __restrict objptrOld, void * __restrict objptrNew); AK_EXPORT void * ak_getObjectById(AkDoc * __restrict doc, const char * __restrict objectId); AK_EXPORT void * ak_getObjectByUrl(AkURL * __restrict url); const char* ak_getFile(const char *url); char* ak_getFileFrom(AkDoc *doc, const char *url); AK_EXPORT const char * ak_generatId(AkDoc * __restrict doc, void * __restrict parentmem, const char * __restrict prefix); AK_EXPORT void* ak_getAssetInfo(void * __restrict obj, size_t itemOffset); /* same as: ak_getAssetInfo(obj, offsetof(AkAssetInf, coordSys)) */ AK_EXPORT AkCoordSys* ak_getCoordSys(void * __restrict obj); AK_EXPORT bool ak_hasCoordSys(void * __restrict obj); AK_EXPORT void ak_retainURL(void * __restrict obj, AkURL * __restrict url); AK_EXPORT void ak_releaseURL(void * __restrict obj, AkURL * __restrict url); AK_EXPORT void ak_setFetchFromURLHandler(AkFetchFromURLHandler handler); #ifdef __cplusplus } #endif #endif /* assetkit_h */ ================================================ FILE: include/ak/bbox.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_bbox_h #define assetkit_bbox_h #include "common.h" #include struct AkMesh; struct AkMeshPrimitive; struct AkGeometry; struct AkScene; struct AkMeshPrimitive; typedef struct AkBoundingBox { float min[3]; float max[3]; bool isvalid; } AkBoundingBox; /*! * @brief calc bbox for whole scene, this calc scene bbox with transformations * of geom nodes * * @param scene visual scene */ void ak_bbox_scene(struct AkVisualScene * __restrict scene); /*! * @brief calc bbox for whole geometry * this will affect scene bbox * * @param geom geometry */ void ak_bbox_geom(struct AkGeometry * __restrict geom); /*! * @brief calc bbox for whole mesh * this will affect geom bbox * * @param mesh mesh */ void ak_bbox_mesh(struct AkMesh * __restrict mesh); /*! * @brief calc bbox for mesh primitive * this will affect geom bbox and mesh bbox * * @param prim primitive */ void ak_bbox_mesh_prim(struct AkMeshPrimitive * __restrict prim); /*! * @brief get center of bbox * * @param[in] bbox bbox * @param[out] center center of bbox */ void ak_bbox_center(AkBoundingBox * __restrict bbox, float center[3]); /*! * @brief returns radius of sphere which is surround bbox square * * @param bbox bbox * * @return radius (r) of outer sphere */ float ak_bbox_radius(AkBoundingBox * __restrict bbox); #endif /* assetkit_bbox_h */ ================================================ FILE: include/ak/cam.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_cam_h #define assetkit_cam_h #ifdef __cplusplus extern "C" { #endif #include "common.h" struct AkLibraryItemBase; typedef enum AkProjectionType { AK_PROJECTION_PERSPECTIVE = 0, /* default */ AK_PROJECTION_ORTHOGRAPHIC = 1, AK_PROJECTION_OTHER = 2 } AkProjectionType; typedef struct AkProjection { AkProjectionType type; uint32_t tag; } AkProjection; typedef struct AkPerspective { AkProjection base; float xfov; float yfov; float aspectRatio; float znear; float zfar; } AkPerspective; typedef struct AkOrthographic { AkProjection base; float xmag; float ymag; float aspectRatio; float znear; float zfar; } AkOrthographic; typedef struct AkOptics { AkProjection *tcommon; AkTechnique *technique; } AkOptics; typedef struct AkImager { AkTechnique *technique; AkTree *extra; } AkImager; typedef struct AkCamera { /* const char * id; */ AkOneWayIterBase base; const char *name; AkOptics *optics; AkImager *imager; AkTree *extra; } AkCamera; /*! * @brief Top camera in scene if available * * pass NULL which parameter is not wanted and reduce overhead of calc these * params, only pass param[s] which you need * * @warning the returned projection matrix's aspect ratio is same as exported * * @param[in] doc document (required) * @param[out] camera found camera (optional) * @param[out] matrix transform matrix (optional) of camera in world * @param[out] projMatrix proj matrix (optional) * * @return AK_OK if camera found else AK_EFOUND */ AK_EXPORT AkResult ak_firstCamera(AkDoc * __restrict doc, AkCamera ** camera, float * matrix, float * projMatrix); AK_EXPORT const AkCamera* ak_defaultCamera(void * __restrict memparent); /*! * @brief Allocate a fresh perspective camera and register it in the * document's cameras library. * * Wires up AkCamera + AkOptics + AkPerspective in one shot using the * heap that owns @p doc. The returned pointer is owned by that heap; * its lifetime is tied to @p memparent (or the document if memparent * is NULL). After this call the camera is queryable via the cameras * library — callers don't need to do their own lib-list management. * * The camera is *not* attached to any scene node here — use * ak_nodeAttachCamera() for that. * * @param[in] doc document the camera will live in (required) * @param[in] memparent heap parent for ownership (NULL → doc) * @param[in] yfov vertical field of view in radians * @param[in] aspect aspect ratio (width / height); 0 → unset * @param[in] znear near clipping distance * @param[in] zfar far clipping distance * * @return Newly allocated AkCamera, or NULL on allocation failure. */ AK_EXPORT AkCamera * ak_camMakePerspective(AkDoc * __restrict doc, void * __restrict memparent, float yfov, float aspect, float znear, float zfar); /*! * @brief Allocate a fresh orthographic camera and register it in the * document's cameras library. * * Mirrors ak_camMakePerspective(): allocates AkCamera + AkOptics + * AkOrthographic, wires them, and adds the camera to doc->lib.cameras. * * @param[in] doc document the camera will live in (required) * @param[in] memparent heap parent for ownership (NULL → doc) * @param[in] xmag half-width of the view volume * @param[in] ymag half-height of the view volume * @param[in] aspect aspect ratio (width / height); 0 → unset * @param[in] znear near clipping distance * @param[in] zfar far clipping distance * * @return Newly allocated AkCamera, or NULL on allocation failure. */ AK_EXPORT AkCamera * ak_camMakeOrthographic(AkDoc * __restrict doc, void * __restrict memparent, float xmag, float ymag, float aspect, float znear, float zfar); #ifdef __cplusplus } #endif #endif /* assetkit_cam_h */ ================================================ FILE: include/ak/common.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_common_h #define assetkit_common_h /* since C99 or compiler ext */ #include #include #include #include #include #include #ifdef DEBUG # include # include #endif #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__) # define AK_WINAPI #endif #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__) # ifdef AK_STATIC # define AK_EXPORT # elif defined(AK_EXPORTS) # define AK_EXPORT __declspec(dllexport) # else # define AK_EXPORT __declspec(dllimport) # endif # define AK_HIDE #else # define AK_EXPORT __attribute__((visibility("default"))) # define AK_HIDE __attribute__((visibility("hidden"))) #endif #if defined(_MSC_VER) # define AK_INLINE __forceinline # define AK_ALIGN(X) __declspec(align(X)) #else # define AK_ALIGN(X) __attribute((aligned(X))) # define AK_INLINE static inline __attribute((always_inline)) #endif #ifndef __has_builtin # define __has_builtin(x) 0 #endif #define AK_ARRAY_LEN(ARR) sizeof(ARR) / sizeof(ARR[0]) #define AK_APPEND_FLINK(SRC,LAST,ITEM) \ do { \ if (LAST) \ LAST->next = ITEM; \ else \ SRC = ITEM; \ LAST = ITEM; \ } while (0) typedef int32_t AkEnum; typedef enum AkResult { AK_NOOP = 1, /* no operation needed */ AK_OK = 0, AK_ERR = -1, /* UKNOWN ERR */ AK_ETCOMMON = -2, /* no tcommon found */ AK_EFOUND = -1000, AK_ENOMEM = -ENOMEM, AK_EPERM = -EPERM, AK_EBADF = -EBADF, /* docoumnt couldn't parsed / loaded */ AK_EINVAL = -EINVAL } AkResult; typedef struct AkOneWayIterBase { struct AkOneWayIterBase *next; } AkOneWayIterBase; typedef struct AkTwoWayIterBase { struct AkTwoWayIterBase *next; struct AkTwoWayIterBase *prev; } AkTwoWayIterBase; #define AK_PATH_FROM_DOC(DOC, DEST, FILE_NAME) \ do { \ size_t dirLength, newPathLength; \ \ dirLength = strlen(DOC->inf->dir); \ newPathLength = dirLength + strlen(FILE_NAME) + 1; \ \ DEST = alloca(newPathLength + 1); \ strcpy(DEST, DOC->inf->dir); \ if (DOC->inf->dir[dirLength - 1] != '/' && FILE_NAME[0] != '/') { \ strcat(DEST, "/"); \ } \ strcat(DEST, FILE_NAME); \ } while (0) #endif /* assetkit_common_h */ ================================================ FILE: include/ak/context.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_context_h #define assetkit_context_h #ifdef __cplusplus extern "C" { #endif #include "common.h" #include "map.h" #include "util.h" typedef struct AkContext { AkDoc *doc; AkTechniqueHint *techniqueHint; AkInstanceMaterial *instanceMaterial; // AkMap *bindVertexInput; } AkContext; AK_INLINE AkContext AkContextZeroed(void) { return (AkContext){0}; } #ifdef __cplusplus } #endif #endif /* assetkit_context_h */ ================================================ FILE: include/ak/controller.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_controller_h #define assetkit_controller_h #ifdef __cplusplus extern "C" { #endif #include "common.h" #include "geom.h" struct AkNode; struct FListItem; typedef enum AkMorphMethod { /* Weights of blend shapes normalized to 1 (or 100%) */ AK_MORPH_METHOD_NORMALIZED = 1, /* Blend shapes defined as a difference from the base shape */ AK_MORPH_METHOD_RELATIVE = 2, /* Alias for RELATIVE, treat additive as relative in this context */ AK_MORPH_METHOD_ADDITIVE = AK_MORPH_METHOD_RELATIVE, /* Each blend shape applied fully on top of the previous one */ AK_MORPH_METHOD_ABSOLUTE = 3 } AkMorphMethod; typedef struct AkBoneWeight { uint32_t joint; float weight; } AkBoneWeight; typedef struct AkBoneWeights { uint32_t *counts; /* joint count per vertex */ size_t *indexes; /* offset of weight at buffer by index */ AkBoneWeight *weights; size_t nWeights; /* cache: count of weights */ size_t nVertex; /* cache: count of pJointsCount/pWeightsIndex */ } AkBoneWeights; /** * Skin controller — vertex skinning data. * * Per-vertex bone data storage differs by source format: * - DAE: variable-count CSR layout in `weights[]` (filled by * dae_fixup_ctlr in authored primitive order). * - glTF: raw vec4 JOINTS_n / WEIGHTS_n accessors stay in `prim->input` * (no aggregation — the format is already GPU-ready). * * Bridges should call ak_skinFillWeights() rather than touching `weights[]` * or `prim->input` directly. The helper hides this format divergence and * yields fixed-N (typically 4) flat buffers ready for upload. * * Default joints (`joints[]`) are populated for glTF; DAE leaves it NULL * and resolves joints per-instance via AkInstanceSkin.overrideJoints (DAE * lets the same skin bind to different skeletons per instance). * * `skeleton` is the closest common ancestor of joints, used by Apple * SCNSkinner.skeleton (and similar engines) as the coordinate-space * reference for bone lookups. Optional — when NULL, callers fall back * to joints[0] (typically the root joint by convention). glTF: filled * from the optional `skin.skeleton` JSON hint. DAE: filled from * 's first URL when fixing the * instance. */ typedef struct AkSkin { AkOneWayIterBase base; AkFloat4x4 *invBindPoses; struct AkNode **joints; /* default joints (glTF; NULL for DAE) */ AkBoneWeights **weights; /* per primitive (DAE only; NULL for glTF) */ struct AkNode *skeleton; /* common ancestor; NULL if not authored */ void *inspectResult; /* private cache for raw accessor pairs */ size_t nJoints; /* cache: joint count */ uint32_t nPrims; /* cache: primitive count */ uint32_t nMaxJoints; AkFloat4x4 bindShapeMatrix; } AkSkin; typedef enum AkMorphableType { AK_MORPHABLE_GEOMETRY, /* per-target geometry, must be same as base */ AK_MORPHABLE_MORPHABLE /* morph inputs if no geometry object is used */ } AkMorphableType; /** * @brief input/attribute layout in shader orr in interleaved buffer * * currently two layouts are supported: * ------------------------------------------------------------------------ * P1 P2 P3 N1 N2 N3 T01 T02 T03 ... * P1 N1 T01 P2 N2 T02 P3 N3 T03 ... (natural layout) * * IMPORTANT: in natural layout, input orders may not same as baseShape * if you need same order as baseShape, use P1P2N1N2 layout * or create an issue to bring this feature to here which is in TODO. */ typedef enum AkMorphInterleaveLayout { AK_MORPH_UNKNOWN = 0, AK_MORPH_P1P2N1N2 = 1, /* each target's inputs are groupped by input type */ AK_MORPH_NATURAL = 3 /* P1N1 P2N2 but input orders are natural as target */ } AkMorphInterleaveLayout; // typedef struct AkSparseMorphInfo { // uint32_t *affectedVertices; /* indices of vertices that change */ // uint32_t nAffectedVertices; // float sparsityRatio; /* 0.0-1.0, useful for optimization decisions */ // } AkSparseMorphInfo; /* per-target inputs to morph */ typedef struct AkMorphable { struct AkMorphable *next; AkInput *input; uint32_t inputCount; } AkMorphable; typedef struct AkMorphPreset { const char *name; /* "neutral", "smile_max", ... */ AkFloatArray *weights; /* length = morph->targetCount */ } AkMorphPreset; typedef struct AkMorphTarget { struct AkMorphTarget *next; AkObject *target; /* AkGeometry or AkMorphable to morph */ uint32_t primitiveCount; /* number of mesh primitives to morph */ } AkMorphTarget; typedef struct AkMorphInspectInput { struct AkMorphInspectInput *next; AkInput *input; uint32_t intrOffset; union { bool inBaseMesh; bool inTarget; }; } AkMorphInspectInput; typedef struct AkMorphInspectMorphable { struct AkMorphInspectMorphable *next; AkMorphInspectInput *input; AkMorphInspectInput *lastInput; uint32_t inputsCount; float weight; /* Per-primitive slice inside a target view. Multi-primitive meshes can have different vertex counts and strides per primitive. */ uint32_t vertexCount; uint32_t stridePerVertex; size_t bufferOffset; size_t bufferSize; } AkMorphInspectMorphable; typedef struct AkMorphInspectTargetView { struct AkMorphInspectTargetView *next; AkMorphInspectMorphable *morphable; uint32_t nTargets; /* Total byte size of this target slice, summed across all per-primitive morphables. */ size_t interleaveBufferSize; } AkMorphInspectTargetView; /* AkMorphInspectView o | o -> AkMorphInspectTargetView ( like Mesh ) o | o -> AkMorphInspectMorphable 1 ( like Mesh Primitive ) o -> AkMorphInspectInput 1 -> AkMorphInspectInput 2 -> AkMorphInspectInput 3 o -> AkMorphInspectMorphable 2 o -> AkMorphInspectMorphable 3 o -> AkMorphInspectTargetView o | o -> AkMorphInspectMorphable 1 o -> AkMorphInspectMorphable 2 o -> AkMorphInspectMorphable 3 */ typedef struct AkMorphInspectView { /* first one is baseShape if includeBaseShape param is set to 'true' */ AkMorphInspectTargetView *base; AkMorphInspectTargetView *targets; AkFloatArray *initialWeights; uint32_t nTargets; size_t interleaveTotalBufferSize; bool includeBaseShape; bool ignoreUncommonInputs; AkMorphInterleaveLayout layout; } AkMorphInspectView; typedef struct AkMorph { AkOneWayIterBase base; AkMorphTarget *target; AkMorphInspectView *inspectResult; AkFloatArray *defaultWeights; /* this overrides mesh.weights */ const char **targetNames; /* optional, length = targetCount */ AkMorphPreset *presets; /* optional named weight sets */ AkMorphMethod method; uint32_t targetCount; uint32_t presetCount; } AkMorph; typedef bool (*AkMorphProgressFn)(AkMorph * __restrict morph, uint32_t targetIndex, uint32_t targetCount, void * __restrict userdata); // TODO: multi-morph-per-mesh just thought loudly ? // typedef struct AkPrimitiveMorph { // AkOneWayIterBase base; // AkMorphTarget *target; // AkMorphInspectView *inspectResult; // AkFloatArray *weights; /* default weights or NULL to zero */ // AkMorphMethod method; // uint32_t targetCount; // } AkPrimitiveMorph; // // typedef struct AkMeshMorph { // AkOneWayIterBase base; // AkPrimitiveMorph *morph; // float weight; // } AkMeshMorph; typedef struct AkInstanceMorph { AkMorph *morph; AkFloatArray *overrideWeights; /* override morph.weights and mesh.weight or NULL */ } AkInstanceMorph; typedef struct AkInstanceSkin { AkSkin *skin; struct AkNode **overrideJoints; /* override default joints or NULL */ } AkInstanceSkin; /*! * @brief format-agnostic per-vertex bone-data extraction with INTERLEAVED * output, suitable for a single GPU vertex buffer (OpenGL/Metal/ * Vulkan typical layout). * * Per-vertex layout in the output buffer: * * | JointIDs[maxJoint] (uint16) | Weights[maxJoint] (float) | * * Matches a shader vertex input like: * * in uvec4 JOINTS; (backed by uint16 vertex input) * in vec4 WEIGHTS; * * Quality is identical to ak_skinFillWeights() — same format- * agnostic core (DAE CSR + glTF accessors), same top-N selection * when authored joint count exceeds maxJoint, same renormalize so * weights sum to 1. Only the output packing differs (interleaved * single buffer vs. separate idx/wgt arrays). * * For the SceneKit/RealityKit path use ak_skinFillWeights() * instead — Apple's APIs require separate boneIndices and * boneWeights SCNGeometrySources. * * Layout: per vertex `[J0..J(N-1) (uint16) W0..W(N-1) (float)]`. * * @param skin AkSkin (for both DAE-CSR and glTF-accessor inputs) * @param prim mesh primitive being skinned (vertex order source) * @param primIdx primitive index fallback; prim pointer is authoritative * @param maxJoint storage slots per vertex (typically 4) * @param buff destination buffer; if *buff is NULL one is alloc'd * with ak_calloc(NULL, ...) and stored back into *buff * (caller frees with ak_free) * @return total bytes written, or 0 on failure */ AK_EXPORT size_t ak_skinInterleave(AkSkin * __restrict skin, AkMeshPrimitive * __restrict prim, uint32_t primIdx, uint32_t maxJoint, void ** __restrict buff); /*! * @brief format-agnostic per-vertex bone-data extraction for one primitive. * * Fills caller-provided fixed-N flat buffers (joint indices + weights) * regardless of the asset's source format: * * - DAE primitives use the CSR layout in skin->weights[]; * variable joint count per vertex -> top-N selected by weight, * zero-padded if countinput; all sets are merged, top-N is selected, and * UBYTE/USHORT joint indices are written as uint16_t. * * Bridges should call this rather than reading skin->weights[] or * prim->input directly — the dual storage is an implementation * detail of the parsers. * * @param[in] skin skin owning the bone data * @param[in] prim mesh primitive being skinned * @param[in] primIdx primitive index fallback; prim pointer is authoritative * @param[in] maxJoint fixed slot count per vertex (typically 4) * @param[out] outIndices buffer for vertexCount × maxJoint × sizeof(uint16_t) * @param[out] outWeights buffer for vertexCount × maxJoint × sizeof(float) * @return vertex count on success, 0 on error. */ AK_EXPORT size_t ak_skinFillWeights(AkSkin * __restrict skin, AkMeshPrimitive * __restrict prim, uint32_t primIdx, uint32_t maxJoint, uint16_t * __restrict outIndices, float * __restrict outWeights); /*! * @brief collect vertex indices affected by one joint for one primitive. * * Works for both DAE CSR skin weights and glTF JOINTS_n/WEIGHTS_n * accessors. The function returns the total matching vertex count even * when `outVertices` is NULL or `capacity` is smaller than the result, * so callers can first query size and then fill a buffer. * * @param[in] skin skin owning the bone data * @param[in] prim mesh primitive at index `primIdx` * @param[in] primIdx primitive index in mesh * @param[in] jointIdx joint index to scan for * @param[out] outVertices optional vertex-index buffer * @param[in] capacity number of uint32_t slots in outVertices * @return total number of affected vertices. */ AK_EXPORT size_t ak_skinVerticesForJoint(AkSkin * __restrict skin, AkMeshPrimitive * __restrict prim, uint32_t primIdx, uint32_t jointIdx, uint32_t * __restrict outVertices, size_t capacity); /*! * @brief inspect a morph to get bufferSize and bufferStride to alloc memory for * interleaved morph buffer with desired inputs. Also returns a list of * inputs for each target. You can use this list to collect inputs from * morph targets. * * inspected result will be stored in morph->inspectResult. You can use * this result to collect inputs same order as baseShape's inputs' order * from morph targets. Inputs that dont exists in baseShape will be ignored. * If you need them, pass ignoreUncommonInputs = false. * * @param[in] baseMesh base mesh to morph * @param[in] morph AkMorph object * @param[in] desiredInputs desired inputs (other inputs will be ignored) * or NULL to collect all inputs, desiredInputsCount must be 0 in this case * @param[in] desiredInputsCount desired inputs count or 0 to collect all inputs * @param[in] includeBaseShape if true, baseShape will be included in result e.g. bytes stride, buffer size etc. * @param[in] ignoreUncommonInputs if true, all inputs that dont exist in base mesh will be ignored */ AK_EXPORT AkResult ak_morphInspect(AkGeometry * __restrict baseMesh, AkMorph * __restrict morph, AkInputSemantic desiredInputs[], uint8_t desiredInputsCount, bool includeBaseShape, bool ignoreUncommonInputs); /*! * @brief prepare morph inspect result to interleave morph object with desired inputs * this prepares inputs order by specified layout parameter and sets intrOffset, * inBaseMesh etc. properties. * * make sure that you called ak_morphInspect() to get buffSize * and alloc a buffer with that size. * * @param[in] inspectView inspect result * @param[in] layout interleave layout e.g. p1p2n1n2 or p1n1p2n2 */ AK_EXPORT AkResult ak_morphInspectPrepareLayout(AkMorphInspectView * __restrict inspectView, AkMorphInterleaveLayout layout); /*! * @brief interleave morph object with desired inputs with desired input orders. * * Make sure that you called ak_morphInspect() to get buffSize * and alloc a buffer with that size. * * All inputs except desired inputs will be ignored. If morph object don't * contain a desired input than it will be ignored too. * * You can send this buffer to GPU and use directly. * * WARN: all inputs that dont exist in base mesh will be ignored * if you need them, you can use ak_morphInspect() to get all * inputs for your own interleave() implementation. Create an issue * if you need bring this feature to here. * * @param[in] baseMesh base mesh to morph * @param[in] morph AkMorph object * @param[in] layout interleave layout e.g. p1p2n1n2 or p1n1p2n2 * @param[out] destBuff pre-allocated buffer to store interleaved data */ AK_EXPORT AkResult ak_morphInterleave(AkGeometry * __restrict baseMesh, AkMorph * __restrict morph, AkMorphInterleaveLayout layout, void * __restrict destBuff); AK_EXPORT AkResult ak_morphInterleaveWithProgress(AkGeometry * __restrict baseMesh, AkMorph * __restrict morph, AkMorphInterleaveLayout layout, void * __restrict destBuff, AkMorphProgressFn progress, void * __restrict userdata); AK_EXPORT const AkMorphPreset* ak_morphPresetByName(AkMorph * __restrict morph, const char * __restrict name); AK_EXPORT bool ak_morphApplyPreset(AkMorph * __restrict morph, const char * __restrict presetName, float * __restrict outWeights, uint32_t capacity); AK_INLINE bool ak_morphHasOverride(const AkInstanceMorph* inst) { return inst && inst->overrideWeights && inst->overrideWeights->count > 0; } /* TODO: CPU morph evaluator (utility, low priority) * * Computes the final blended vertex data on the CPU using base mesh + targets * + weights. Output is a "final deformed mesh" ready to be uploaded as a * single static draw — no GPU-side blending required. * * Use cases: * - mesh export / bake tools (e.g., bake "smile_max" pose as a static asset) * - software renderers without GPU shader blending * - pre-bake / asset pipeline tooling * * Modern engines (SceneKit, RealityKit, custom Metal/Vulkan) do NOT need this: * they consume ak_morphInterleave output + a weights uniform array and blend * on the GPU. This evaluator is purely for the niche cases above. * * Sketch: * * AK_EXPORT * AkResult * ak_morphEvaluate(AkGeometry * __restrict baseMesh, * AkMorph * __restrict morph, * const AkInstanceMorph * __restrict inst, // NULL → defaults * void * __restrict destBuff);// pre-alloc base layout */ //AkResult //ak_morphEvaluateWeights(const AkMorph * __restrict morph, // const AkInstanceMorph * __restrict inst, // AkFloat ** __restrict out /* len = morph->targetCount */) { // AkFloatArray *ov; // AkFloatArray *def; // AkMesh *mesh; // uint32_t n; // // if (!morph || !out) // return AK_ERR; // // n = morph->targetCount; // ov = (inst) ? inst->overrideWeights : NULL; // // /* 1) Kaynak seçimi ve kopyalama (override > default > zeros) */ // if (ov && ov->count) { // *out = ov->items; // return AK_OK; // } else if ((def = morph->defaultWeights) && def->count) { // *out = def->items; // return AK_OK; // } else if ((ak_objGet(morph->target->target))) { // // // return mesh->weights; // } // // return AK_OK; //} #ifdef __cplusplus } #endif #endif /* assetkit_controller_h */ ================================================ FILE: include/ak/coord-util.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_coord_util_h #define assetkit_coord_util_h #ifdef __cplusplus extern "C" { #endif #include "common.h" struct AkNode; struct AkVisualScene; AK_EXPORT void ak_changeCoordSys(AkDoc * __restrict doc, AkCoordSys * newCoordSys); AK_EXPORT void ak_changeCoordSysGeom(AkGeometry * __restrict geom, AkCoordSys * newCoordSys); AK_EXPORT void ak_changeCoordSysMesh(AkMesh * __restrict mesh, AkCoordSys * newCoordSys); AK_EXPORT void ak_fixNodeCoordSys(struct AkNode * __restrict node); AK_EXPORT void ak_fixSceneCoordSys(struct AkVisualScene * __restrict scene); #ifdef __cplusplus } #endif #endif /* assetkit_coord_util_h */ ================================================ FILE: include/ak/coord.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_coord_h #define assetkit_coord_h #ifdef __cplusplus extern "C" { #endif #include "common.h" typedef enum AkCoordCvtType { AK_COORD_CVT_DISABLED = 0, /* import document as they are, don't apply any coord sys operation */ AK_COORD_CVT_FIX_TRANSFORM = 1, /* only add transforms to fix orientation */ AK_COORD_CVT_ALL = 2, /* change positions, all things... */ AK_COORD_CVT_DEFAULT = AK_COORD_CVT_FIX_TRANSFORM } AkCoordCvtType; typedef struct AkAxisAccessor { int8_t right; int8_t up; int8_t fwd; int8_t s_right; int8_t s_up; int8_t s_fwd; } AkAxisAccessor; typedef enum AkAxis { AK_AXIS_NEGATIVE_X =-1, AK_AXIS_NEGATIVE_Y =-2, AK_AXIS_NEGATIVE_Z =-3, AK_AXIS_POSITIVE_X = 1, AK_AXIS_POSITIVE_Y = 2, AK_AXIS_POSITIVE_Z = 3, } AkAxis; typedef enum AkAxisRotDirection { AK_AXIS_ROT_DIR_LH = -2, AK_AXIS_ROT_DIR_RH = 0 } AkAxisRotDirection; typedef struct AkAxisOrientation { AkAxis right; /* +X */ AkAxis up; /* +Y */ AkAxis fwd; /* -Z */ } AkAxisOrientation; typedef struct AkCoordSys { AkAxisOrientation axis; /* the default value is AK_AXIS_ROT_DIRECTION_RH (Right Handed) */ AkAxisRotDirection rotDirection; /* when creating custom coord sys, this value must be set correctly, there is no default value */ AkAxisOrientation cameraOrientation; } AkCoordSys; /* Right Hand (Default) */ extern AkCoordSys * AK_ZUP; extern AkCoordSys * AK_YUP; extern AkCoordSys * AK_XUP; /* Left Hand */ extern AkCoordSys * AK_ZUP_LH; extern AkCoordSys * AK_YUP_LH; extern AkCoordSys * AK_XUP_LH; struct AkTransform; AK_INLINE void ak_coordAxisToiVec3(AkAxisOrientation axisOri, int32_t vec[3]) { vec[0] = axisOri.right; vec[1] = axisOri.up; vec[2] = axisOri.fwd; } AK_INLINE void ak_coordToiVec3(AkCoordSys * __restrict coordSys, int32_t vec[3]) { vec[0] = coordSys->axis.right; vec[1] = coordSys->axis.up; vec[2] = coordSys->axis.fwd; } AK_INLINE bool ak_coordOrientationIsEq(AkCoordSys *c1, AkCoordSys *c2) { return c1->axis.right == c2->axis.right && c1->axis.up == c2->axis.up && c1->axis.fwd == c2->axis.fwd; } AK_EXPORT void ak_coordCvtVector(AkCoordSys *oldCoordSystem, float *vector, AkCoordSys *newCoordSystem); AK_EXPORT void ak_coordCvtVectorTo(AkCoordSys *oldCoordSystem, float *oldVector, AkCoordSys *newCoordSystem, float *newVector); AK_EXPORT void ak_coordCvtVectors(AkCoordSys *oldCoordSystem, float *vectorArray, size_t len, AkCoordSys *newCoordSystem); AK_EXPORT void ak_coordCvtTransform(AkCoordSys *oldCoordSystem, AkFloat4x4 transform, AkCoordSys *newCoordSystem); /* find transform between two coordinate system */ AK_EXPORT void ak_coordFindTransform(struct AkTransform *transform, AkCoordSys *oldCoordSys, AkCoordSys *newCoordSys); AK_EXPORT void ak_coordFixCamOri(AkCoordSys *oldCoordSys, AkCoordSys *newCoordSys, AkFloat4x4 transform); struct AkDoc; struct AkNode; AK_EXPORT void ak_coordRotNodeForFixedCoord(struct AkDoc *doc, void *memparent, AkObject **destTransform); AK_EXPORT void ak_coordCvtNodeTransforms(struct AkDoc * __restrict doc, struct AkNode * __restrict node); #ifdef __cplusplus } #endif #endif /* assetkit_coord_h */ ================================================ FILE: include/ak/core-types.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_core_types_h #define assetkit_core_types_h typedef const char *AkString; typedef char *AkMutString; typedef bool AkBool; typedef int16_t AkInt16; typedef uint16_t AkUInt16; typedef int32_t AkInt; typedef uint32_t AkUInt; typedef int64_t AkInt64; typedef uint64_t AkUInt64; typedef float AkFloat; typedef double AkDouble; typedef AkBool AkBool4[4]; typedef AkInt AkInt2[2]; typedef AkInt AkInt4[4]; typedef AkFloat AkFloat2[2]; typedef AkDouble AkDouble2[2]; typedef AkFloat AkFloat3[3]; typedef AkDouble AkDouble3[3]; typedef AK_ALIGN(16) AkFloat AkFloat4[4]; typedef AK_ALIGN(16) AkDouble AkDouble4[4]; typedef AK_ALIGN(32) AkDouble AkDouble4x4[4]; typedef AK_ALIGN(32) AkFloat4 AkFloat4x4[4]; #undef AK__DEF_ARRAY #define AK__DEF_ARRAY(TYPE) \ typedef struct TYPE##Array { \ size_t count; \ TYPE items[]; \ } TYPE##Array; \ \ typedef struct TYPE##ArrayL { \ struct TYPE##ArrayL * next; \ size_t count; \ TYPE items[]; \ } TYPE##ArrayL AK__DEF_ARRAY(AkBool); AK__DEF_ARRAY(AkInt); AK__DEF_ARRAY(AkUInt); AK__DEF_ARRAY(AkFloat); AK__DEF_ARRAY(AkDouble); AK__DEF_ARRAY(AkString); #undef AK__DEF_ARRAY #endif /* assetkit_core_types_h */ ================================================ FILE: include/ak/geom.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_geom_h #define assetkit_geom_h #ifdef __cplusplus extern "C" { #endif #include "common.h" #include "map.h" #include "bbox.h" #include "map.h" struct RBTree; struct AkGeometry; struct AkMesh; struct FListItem; struct AkMaterial; typedef enum AkGeometryType { AK_GEOMETRY_MESH = 1, AK_GEOMETRY_SPLINE = 2, AK_GEOMETRY_BREP = 3 } AkGeometryType; typedef enum AkGeometryEditFlags { AK_GEOM_EDIT_FLAG_ARRAYS = 1, AK_GEOM_EDIT_FLAG_INDICES = 2, AK_GEOM_EDIT_FLAG_MUTEX = 3 } AkGeometryEditFlags; typedef enum AkMeshPrimitiveType { AK_PRIMITIVE_LINES = 1, AK_PRIMITIVE_POLYGONS = 2, AK_PRIMITIVE_TRIANGLES = 3, AK_PRIMITIVE_POINTS = 4 } AkMeshPrimitiveType; typedef enum AkTriangleMode { AK_TRIANGLES = 1, AK_TRIANGLE_STRIP = 3, AK_TRIANGLE_FAN = 5 } AkTriangleMode; typedef enum AkLineMode { AK_LINES = 1, AK_LINE_LOOP = 3, AK_LINE_STRIP = 5 } AkLineMode; typedef struct AkVertices { /* const char * id; */ const char *name; AkTree *extra; AkInput *input; uint32_t inputCount; } AkVertices; /* TODO: */ typedef struct AkJointDesc { uint32_t *counts; size_t *indexes; uint32_t allCount; } AkJointDesc; typedef struct AkMeshPrimitive { struct AkMeshPrimitive *next; struct AkMesh *mesh; AkBoundingBox *bbox; /* per-primitive bbox */ const char *name; const char *bindmaterial; struct AkMaterial *material; AkInput *input; AkInput *pos; AkUIntArray *indices; AkTree *extra; void *udata; AkMeshPrimitiveType type; uint32_t nPolygons; uint32_t inputCount; AkFloat3 center; /* TODO: remove */ uint32_t indexStride; uint32_t reserved1; /* private member */ uint32_t reserved2; /* private member */ void *reserved3; /* private member */ /* KHR_materials_variants: optional material overrides. */ struct AkMaterialVariantMapping *variantMappings; uint32_t variantMappingCount; /* KHR_gaussian_splatting: optional splat metadata. Splat inputs stay in the normal primitive input chain. */ struct AkGaussianSplat *gsplat; } AkMeshPrimitive; typedef struct AkLines { AkMeshPrimitive base; AkLineMode mode; } AkLines; typedef struct AkPolygon { AkMeshPrimitive base; AkDoubleArrayL *holes; AkUIntArray *vcount; AkBool haveHoles; } AkPolygon; typedef struct AkTriangles { AkMeshPrimitive base; AkTriangleMode mode; } AkTriangles; typedef struct AkMeshEditHelper { AkGeometryEditFlags flags; struct RBTree *buffers; /* new buffers */ struct RBTree *indices; /* new indices */ AkMap *inputBufferMap; /* input-accessor-buffer map */ void *mutex; void *duplicator; bool skipFixIndices; } AkMeshEditHelper; typedef struct AkMesh { struct AkGeometry *geom; const char *convexHullOf; AkMeshPrimitive *primitive; AkBoundingBox *bbox; AkTree *extra; AkMeshEditHelper *edith; struct FListItem *skins; const char *name; AkFloatArray *weights; uint32_t primitiveCount; AkFloat3 center; } AkMesh; typedef struct AkSpline { struct AkGeometry *geom; AkSource *source; AkVertices *cverts; AkTree *extra; AkBool closed; } AkSpline; typedef struct AkLine { AkFloat3 origin; AkFloat3 direction; AkTree *extra; } AkLine; typedef struct AkCircle { AkFloat radius; AkTree *extra; } AkCircle; typedef struct AkEllipse { AkFloat2 radius; AkTree *extra; } AkEllipse; typedef struct AkParabola { AkFloat focal; AkTree *extra; } AkParabola; typedef struct AkHyperbola { AkFloat2 radius; AkTree *extra; } AkHyperbola; typedef struct AkNurbs { AkSource *source; AkVertices *cverts; AkTree *extra; AkUInt degree; AkBool closed; } AkNurbs; typedef struct AkCurve { AkFloatArrayL *orient; AkFloat3 origin; AkObject *curve; struct AkCurve *next; } AkCurve; typedef struct AkCurves { AkCurve *curve; AkTree *extra; } AkCurves; typedef struct AkCone { AkFloat radius; AkFloat angle; AkTree *extra; } AkCone; typedef struct AkPlane { AkFloat4 equation; AkTree *extra; } AkPlane; typedef struct AkCylinder { AkFloat2 radius; AkTree *extra; } AkCylinder; typedef struct AkNurbsSurface { AkSource *source; AkVertices *cverts; AkTree *extra; AkUInt degree_u; AkUInt degree_v; AkBool closed_u; AkBool closed_v; } AkNurbsSurface; typedef struct AkSphere { AkFloat radius; AkTree *extra; } AkSphere; typedef struct AkTorus { AkFloat2 radius; AkTree * extra; } AkTorus; typedef struct AkSweptSurface { AkCurve *curve; AkFloat3 direction; AkFloat3 origin; AkFloat3 axis; AkTree *extra; } AkSweptSurface; typedef struct AkSurface { /* const char * sid; */ const char *name; AkObject *surface; AkFloatArrayL *orient; AkFloat3 origin; struct AkSurface *next; } AkSurface; typedef struct AkSurfaces { AkSurface *surface; AkTree *extra; } AkSurfaces; typedef struct AkEdges { /* const char * id; */ const char *name; AkInput *input; AkUIntArray *primitives; AkTree *extra; AkUInt count; uint32_t inputCount; } AkEdges; typedef struct AkWires { /* const char * id; */ const char *name; AkInput *input; AkUIntArray *vcount; AkUIntArray *primitives; AkTree *extra; AkUInt count; uint32_t inputCount; } AkWires; typedef struct AkFaces { /* const char * id; */ const char *name; AkInput *input; AkUIntArray *vcount; AkUIntArray *primitives; AkTree *extra; AkUInt count; uint32_t inputCount; } AkFaces; typedef struct AkPCurves { /* const char * id; */ const char *name; AkInput *input; AkUIntArray *vcount; AkUIntArray *primitives; AkTree *extra; AkUInt count; uint32_t inputCount; } AkPCurves; typedef struct AkShells { /* const char * id; */ const char *name; AkInput *input; AkUIntArray *vcount; AkUIntArray *primitives; AkTree *extra; AkUInt count; uint32_t inputCount; } AkShells; typedef struct AkSolids { /* const char * id; */ const char *name; AkInput *input; AkUIntArray *vcount; AkUIntArray *primitives; AkTree *extra; AkUInt count; uint32_t inputCount; } AkSolids; /* TODO: */ typedef struct AkBoundryRep { struct AkGeometry *geom; AkCurves *curves; AkCurves *surfaceCurves; AkSurfaces *surfaces; AkSource *source; AkVertices *vertices; AkEdges *edges; AkWires *wires; AkFaces *faces; AkPCurves *pcurves; AkShells *shells; AkSolids *solids; AkTree *extra; } AkBoundryRep; typedef struct AkGeometry { /* const char * id; */ AkOneWayIterBase base; const char *name; AkObject *gdata; AkTree *extra; AkMap *materialMap; AkBoundingBox *bbox; } AkGeometry; typedef enum AkMeshIsolateType { AK_MESH_ISOLATE_NONE = 0 << 0, AK_MESH_ISOLATE_BUFFERS = 1 << 1, AK_MESH_ISOLATE_ACCESSORS = 2 << 2 } AkMeshIsolateType; /*! * @brief Total input count except VERTEX input * * returns primitive.inputCount - 1 + primitive.vertices.inputCount * * @return total input count */ AK_EXPORT uint32_t ak_meshInputCount(AkMesh * __restrict mesh); /*! * @brief set material (symbol) for primitive * actual material will set with bind_material/instance material * * @param prim mesh primitive * @param material material * * @return result */ AK_EXPORT AkResult ak_meshSetMaterial(AkMeshPrimitive *prim, const char *material); /*! * @brief triangulate all mesh primitives * * @param mesh mesh * * @return new triangles/faces count */ AK_EXPORT uint32_t ak_meshTriangulate(AkMesh * __restrict mesh); /*! * @brief triangulate polygon * * @param poly polygon primitive * * @return new triangles/faces count */ AK_EXPORT uint32_t ak_meshTriangulatePoly(AkPolygon * __restrict poly); /*! * @brief returns true if least one primitive doesn't have normals * * @param mesh mesh * * @return boolean */ AK_EXPORT bool ak_meshNeedsNormals(AkMesh * __restrict mesh); /*! * @brief returns true if primitive doesn't have normals * * @param prim primitive * * @return boolean */ AK_EXPORT bool ak_meshPrimNeedsNormals(AkMeshPrimitive * __restrict prim); /*! * @brief generate normals for all pritimives of mesh * * @param mesh mesh */ AK_EXPORT void ak_meshGenNormals(AkMesh * __restrict mesh); /*! * @brief prepare mesh for edit, or enable edit mode with default attribs * * @param mesh mesh */ AK_EXPORT void ak_meshBeginEdit(AkMesh * __restrict mesh); /*! * @brief prepare mesh for edit, or enable edit mode * * @param mesh meshs * @param flags flags needed while edit mode */ AK_EXPORT void ak_meshBeginEditA(AkMesh * __restrict mesh, AkGeometryEditFlags flags); /*! * @brief finish edit, disable edit mode, relese allocated memory for editing * * @param mesh mesh */ AK_EXPORT void ak_meshEndEdit(AkMesh * __restrict mesh); AK_EXPORT AkUIntArray* ak_meshIndicesArrayFor(AkMesh * __restrict mesh, AkMeshPrimitive * __restrict prim, bool readonly); AK_EXPORT AkSourceBuffState* ak_meshReserveBuffer(AkMesh * __restrict mesh, void * __restrict buffid, size_t itemSize, uint32_t stride, size_t acc_count); AK_EXPORT void ak_meshReserveBufferForInput(AkMesh * __restrict mesh, AkInput * __restrict input, size_t count); AK_EXPORT void ak_meshReserveBuffers(AkMesh * __restrict mesh, AkMeshPrimitive * __restrict prim, size_t count); AK_EXPORT AkResult ak_meshFillBuffers(AkMesh * __restrict mesh); AK_EXPORT void ak_moveIndices(AkMesh * __restrict mesh); AK_EXPORT void ak_meshMoveBuffers(AkMesh * __restrict mesh); AK_EXPORT AkSourceEditHelper* ak_meshSourceEditHelper(AkMesh * __restrict mesh, AkInput * __restrict input); AK_EXPORT AkDuplicator* ak_meshDuplicatorForIndices(AkMesh * __restrict mesh, AkMeshPrimitive * __restrict prim); AK_EXPORT void ak_meshFixIndexBuffer(AkMesh * __restrict mesh, AkMeshPrimitive * __restrict prim, AkDuplicator * __restrict duplicator); void ak_meshReIndexInputs(AkMesh * __restrict mesh); void ak_inputNameIndexed(AkInput * __restrict input, char * __restrict buf); AK_EXPORT void ak_inputNameBySet(AkInput * __restrict input, char * __restrict buf); AK_EXPORT AkInput* ak_meshInputGet(AkMeshPrimitive *prim, const char *inputSemantic, uint32_t set); AK_EXPORT bool ak_meshIsIsolated(AkMesh *mesh); /*! * @brief current isolation rule * * 1. Separated buffer per accessor * 2. Separated accessor per input * * @return true if the rules are match */ AK_EXPORT bool ak_meshIsPrimIsolated(AkMeshPrimitive *prim); AK_EXPORT void ak_meshIsolate(AkMesh *mesh); AK_EXPORT void ak_meshIsolatePrim(AkMeshPrimitive *prim); #ifdef __cplusplus } #endif #endif /* assetkit_geom_h */ ================================================ FILE: include/ak/gsplat.h ================================================ /* * Copyright (C) 2026 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_gsplat_h #define assetkit_gsplat_h #ifdef __cplusplus extern "C" { #endif #include "common.h" /*! * @brief KHR_gaussian_splatting metadata. * * The base extension stores splats as POINT primitives. Per-splat data * stays in AkMeshPrimitive.input (POSITION, ROTATION, SCALE, OPACITY, * COLOR_0..COLOR_15). This struct stores only the extension-level * rendering hints and optional decoder output for compressed payloads. */ typedef enum AkGaussianSplatKernel { AK_GSPLAT_KERNEL_UNKNOWN = 0, AK_GSPLAT_KERNEL_ELLIPSE = 1 /* 2D ellipse projection of an ellipsoid */ } AkGaussianSplatKernel; typedef enum AkGaussianSplatColorSpace { AK_GSPLAT_COLOR_UNKNOWN = 0, AK_GSPLAT_COLOR_SRGB_REC709_DISPLAY = 1, /* "srgb_rec709_display" */ AK_GSPLAT_COLOR_LIN_REC709_DISPLAY = 2 /* "lin_rec709_display" */ } AkGaussianSplatColorSpace; typedef enum AkGaussianSplatProjection { AK_GSPLAT_PROJECTION_PERSPECTIVE = 0, /* default */ AK_GSPLAT_PROJECTION_ORTHOGRAPHIC = 1 } AkGaussianSplatProjection; typedef enum AkGaussianSplatSortingMethod { AK_GSPLAT_SORTING_CAMERA_DISTANCE = 0, /* default */ AK_GSPLAT_SORTING_NONE = 1 } AkGaussianSplatSortingMethod; typedef struct AkGaussianSplat { AkGaussianSplatKernel kernel; AkGaussianSplatColorSpace colorSpace; AkGaussianSplatProjection projection; AkGaussianSplatSortingMethod sortingMethod; /* Filled by an optional decoder when a compression extension is present. */ void *decodedData; /* opaque, decoder-owned */ uint32_t decodedCount; /* decoded splat count */ uint32_t reserved; } AkGaussianSplat; /*---------------------------------------------------------------------*/ /* External decoder interface. */ /* */ /* Apps provide a side library exporting assetkit_gsplat_create, the */ /* same pattern used by Draco / meshoptimizer / KTX2 shims. AssetKit */ /* dlopens it from AK_OPT_GLTF_GSPLAT_DECODER_PATH or, when autoload */ /* is enabled, from the standard side-library name. */ /* */ /* The uncompressed base KHR_gaussian_splatting extension does not */ /* need this decoder; renderers read primitive accessors directly. */ /*---------------------------------------------------------------------*/ struct AkHeap; struct AkGLTFState; struct AkMeshPrimitive; struct json_t; /* Decoders may implement either entrypoint. decodeBytes is preferred when the compression extension references a bufferView directly; decodePrimitive is kept for formats that need broader glTF state. */ typedef int (*AkGaussianSplatDecodeBytesFn)(struct AkHeap * heap, struct AkMeshPrimitive * prim, const uint8_t * data, size_t size); typedef int (*AkGaussianSplatDecodePrimitiveFn)(struct AkGLTFState * gst, struct AkMeshPrimitive * prim, const struct json_t * jprim, const struct json_t * jcompression); typedef struct AkGaussianSplatDecoder { void *userdata; AkGaussianSplatDecodeBytesFn decodeBytes; AkGaussianSplatDecodePrimitiveFn decodePrimitive; void (*close)(void *ud); } AkGaussianSplatDecoder; /*! * @brief Decoder-library entrypoint. Returns 0 on success. */ typedef int (*AkGaussianSplatDecoderCreateFn)(AkGaussianSplatDecoder * out); #ifdef __cplusplus } #endif #endif /* assetkit_gsplat_h */ ================================================ FILE: include/ak/image.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_image_h #define assetkit_image_h #include "common.h" struct AkInitFrom; typedef enum AkImageType { AK_IMAGE_TYPE_1D = 0, AK_IMAGE_TYPE_2D = 1, AK_IMAGE_TYPE_3D = 2, AK_IMAGE_TYPE_CUBE = 3 } AkImageType; typedef struct AkImageData { void *data; uint32_t width; uint32_t height; AkEnum comp; } AkImageData; typedef struct AkSizeExact { uint32_t width; uint32_t height; } AkSizeExact; typedef struct AkSizeRatio { float width; float height; } AkSizeRatio; typedef struct AkMips { uint32_t levels; bool autoGenerate; } AkMips; typedef struct AkImageFormat { const char *space; const char *exact; AkChannelFormat channel; AkRangeFormat range; AkPrecisionFormat precision; } AkImageFormat; typedef struct AkImageSize { AkUInt width; AkUInt height; AkUInt depth; } AkImageSize; typedef struct AkImageBase { AkImageFormat *format; struct AkInitFrom *initFrom; long arrayLen; AkImageType type; } AkImageBase; typedef struct AkImage2d { AkImageBase base; AkSizeExact *sizeExact; AkSizeRatio *sizeRatio; AkMips *mips; const char *unnormalized; } AkImage2d; typedef struct AkImage3d { AkImageBase base; AkImageSize size; AkMips mips; } AkImage3d; typedef struct AkImageCube { AkImageBase base; uint32_t width; AkMips mips; } AkImageCube; typedef struct AkImage { /* const char * id; */ /* const char * sid; */ const char *name; struct AkInitFrom *initFrom; AkImageBase *image; AkImageData *data; AkTree *extra; struct AkImage *next; AkBool renderable; AkBool renderableShare; bool flipOnLoad; } AkImage; AK_EXPORT void ak_imageLoad(AkImage * __restrict image); /* Loader Configurator */ typedef AkImageData* (*AkImageLoadFromFileFn)(AkHeap * __restrict heap, AkImage * __restrict image, const char * __restrict path, bool flipVertically); typedef AkImageData* (*AkImageLoadFromMemoryFn)(AkHeap * __restrict heap, AkImage * __restrict image, AkBuffer * __restrict buff, bool flipVertically); typedef void (*AkImageFlipVerticallyOnLoad)(bool flip); AK_EXPORT void ak_imageInitLoader(AkImageLoadFromFileFn fromFile, AkImageLoadFromMemoryFn fromMemory); #endif /* assetkit_image_h */ ================================================ FILE: include/ak/instance.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_instance_h #define assetkit_instance_h #ifdef __cplusplus extern "C" { #endif #include "common.h" #include "node.h" typedef struct AkInstanceListItem { struct AkInstanceListItem *prev; struct AkInstanceListItem *next; AkInstanceBase *instance; size_t index; } AkInstanceListItem; typedef struct AkInstanceList { AkInstanceListItem *first; AkInstanceListItem *last; size_t count; size_t lastindex; } AkInstanceList; AK_EXPORT AkInstanceBase* ak_instanceMake(AkHeap * __restrict heap, void * __restrict memparent, void * __restrict object); AK_EXPORT AkInstanceGeometry* ak_instanceMakeGeom(AkHeap * __restrict heap, void * __restrict memparent, AkGeometry * __restrict object); AK_EXPORT void ak_instanceListAdd(AkInstanceList *list, AkInstanceBase *inst); AK_EXPORT void ak_instanceListDel(AkInstanceList *list, AkInstanceListItem *item); AK_EXPORT void ak_instanceListEmpty(AkInstanceList *list); char* ak_instanceName(AkInstanceListItem *item); AK_EXPORT void * ak_instanceObject(AkInstanceBase *instance); AK_EXPORT AkNode * ak_instanceObjectNode(AkNode * node); AK_EXPORT AkGeometry * ak_instanceObjectGeom(AkNode * node); AK_EXPORT AkGeometry * ak_instanceObjectGeomId(AkDoc * __restrict doc, const char * id); AK_EXPORT AkNode* ak_instanceMoveToSubNode(AkNode * __restrict node, AkInstanceBase *inst); AK_EXPORT AkNode* ak_instanceMoveToSubNodeIfNeeded(AkNode * __restrict node, AkInstanceBase *inst); #ifdef __cplusplus } #endif #endif /* assetkit_instance_h */ ================================================ FILE: include/ak/library.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_lib_h #define assetkit_lib_h #ifdef __cplusplus extern "C" { #endif #include "common.h" #include "cam.h" #include "light.h" typedef struct AkLibrary { /* const char * id; */ struct AkLibrary *next; const char *name; AkTree *extra; AkOneWayIterBase *chld; uint64_t count; } AkLibrary; AK_EXPORT AkGeometry * ak_libFirstGeom(AkDoc * __restrict doc); AK_EXPORT AkResult ak_libAddCamera(AkDoc * __restrict doc, AkCamera * __restrict cam); AK_EXPORT AkResult ak_libAddLight(AkDoc * __restrict doc, AkLight * __restrict light); AK_EXPORT void ak_libInsertInto(AkLibrary *lib, void *item, int32_t prevoff, int32_t nextoff); AK_EXPORT AkLibrary* ak_libFirstOrCreat(AkDoc * __restrict doc, uint32_t itemOffset); AK_EXPORT AkLibrary* ak_libImageFirstOrCreat(AkDoc * __restrict doc); #ifdef __cplusplus } #endif #endif /* assetkit_lib_h */ ================================================ FILE: include/ak/light.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_light_h #define assetkit_light_h #ifdef __cplusplus extern "C" { #endif #include "common.h" typedef enum AkLightType { AK_LIGHT_TYPE_AMBIENT = 1, AK_LIGHT_TYPE_DIRECTIONAL = 2, AK_LIGHT_TYPE_POINT = 3, AK_LIGHT_TYPE_SPOT = 4, AK_LIGHT_TYPE_CUSTOM = 5 } AkLightType; typedef struct AkLightBase { AkLightType type; uint32_t ctype; /* custom type, because type always is custom */ AkColor color; AkFloat3 direction; float intensity; float range; } AkLightBase; typedef AkLightBase AkAmbientLight; typedef AkLightBase AkDirectionalLight; typedef struct AkPointLight { AkLightBase base; float constAttn; float linearAttn; float quadAttn; } AkPointLight; typedef struct AkSpotLight { AkLightBase base; float constAttn; float linearAttn; float quadAttn; float innerConeAngle; float outerConeAngle; float falloffAngle; float falloffExp; } AkSpotLight; typedef struct AkLight { /* const char * id; */ const char *name; AkLightBase *tcommon; AkTechnique *technique; AkTree *extra; struct AkLight *next; } AkLight; AK_EXPORT AkLight* ak_defaultLight(void * __restrict memparent); /*! * @brief Allocate a light of the given type with sensible defaults * (white color, downward direction for directional/spot, * constant attenuation for point/spot, 30° spot falloff) * and register it in the document's lights library. * * Wires up AkLight + the matching tcommon variant (AkLightBase for * ambient/directional, AkPointLight, or AkSpotLight) in one call. * Pair with ak_nodeAttachLight() to expose the light in the scene * tree. * * @param[in] doc document the light will live in (required) * @param[in] memparent heap parent for ownership (NULL → doc) * @param[in] type AK_LIGHT_TYPE_AMBIENT/DIRECTIONAL/POINT/SPOT * * @return Newly allocated AkLight, or NULL on failure (unsupported * type, allocation failure, etc.) */ AK_EXPORT AkLight * ak_lightMake(AkDoc * __restrict doc, void * __restrict memparent, AkLightType type); #ifdef __cplusplus } #endif #endif /* assetkit_light_h */ ================================================ FILE: include/ak/map.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* TODO: use separate rbtree instead of heap's rbtree (optional) * this map impl have many TODOs */ #ifndef assetkit_map_h #define assetkit_map_h #ifdef __cplusplus extern "C" { #endif #include "common.h" #include "memory.h" #include typedef struct AkMapItem { struct AkMapItem *prev; struct AkMapItem *next; void *data; bool isMapItem; } AkMapItem; typedef struct AkMap { AkHeap *heap; AkMapItem *root; } AkMap; typedef AkHeapSrchCmpFn AkMapCmp; AK_EXPORT void ak_map_addptr(AkMap *map, void *ptr); AK_EXPORT void* ak_map_find(AkMap *map, void *id); AK_EXPORT AkMapItem* ak_map_findm(AkMap *map, void *id); AK_EXPORT void ak_map_add(AkMap *map, void *value, void *id); AK_EXPORT void ak_multimap_add(AkMap *map, void *value, void *id); AK_EXPORT AkMap * ak_map_new(AkMapCmp cmp); AK_EXPORT void ak_map_destroy(AkMap *map); #ifdef __cplusplus } #endif #endif /* assetkit_map_h */ ================================================ FILE: include/ak/material.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_material_h #define assetkit_material_h #ifdef __cplusplus extern "C" { #endif #include "common.h" #include "texture.h" struct AkBindMaterial; struct AkMeshPrimitive; struct AkEffect; struct AkInstanceMaterial; struct AkMaterial; struct AkDoc; struct AkTreeNode; /*! * @brief Named material variant from KHR_materials_variants. */ typedef struct AkMaterialVariant { struct AkMaterialVariant *next; const char *name; struct AkTreeNode *extras; } AkMaterialVariant; /*! * @brief Primitive material override for a document variant index. */ typedef struct AkMaterialVariantMapping { struct AkMaterialVariantMapping *next; struct AkMaterial *material; uint32_t variantIndex; } AkMaterialVariantMapping; AK_EXPORT AkMaterialVariant* ak_materialVariantByName(struct AkDoc * __restrict doc, const char * __restrict name); typedef enum AkOpaque { AK_OPAQUE_OPAQUE = 0, /* fully opaque */ AK_OPAQUE_A_ONE = 1, AK_OPAQUE_A_ZERO = 2, AK_OPAQUE_RGB_ONE = 3, AK_OPAQUE_RGB_ZERO = 4, AK_OPAQUE_BLEND = 5, /* Blend only */ AK_OPAQUE_MASK = 6, /* Activate alpha cutoff */ AK_OPAQUE_DEFAULT = AK_OPAQUE_OPAQUE } AkOpaque; typedef enum AkMaterialType { AK_MATERIAL_PHONG = 1, AK_MATERIAL_BLINN = 2, AK_MATERIAL_LAMBERT = 3, AK_MATERIAL_CONSTANT = 4, AK_MATERIAL_METALLIC_ROUGHNESS = 5, /* PBR Material */ AK_MATERIAL_SPECULAR_GLOSSINES = 6, /* PBR Material */ AK_MATERIAL_PBR = 7, /* PBR Material */ AK_MATERIAL_UNLIT = AK_MATERIAL_CONSTANT } AkMaterialType; /* typedef struct AkFloatOrParam { float *val; AkParam *param; } AkFloatOrParam; */ typedef struct AkColorDesc { AkColor *color; AkParam *param; AkTextureRef *texture; } AkColorDesc; typedef struct AkTransparent { AkColorDesc *color; float amount; AkOpaque opaque; float cutoff; } AkTransparent; typedef struct AkReflective { AkColorDesc *color; float amount; } AkReflective; typedef struct AkOcclusion { AkTextureRef *tex; float strength; AkTextureChannels textureChannels; } AkOcclusion; typedef struct AkNormalMap { AkTextureRef *tex; float scale; } AkNormalMap; typedef struct AkMaterialMetallicProp { AkTextureRef *tex; float intensity; AkTextureChannels textureChannels; } AkMaterialMetallicProp; typedef struct AkMaterialSpecularProp { AkTextureRef *specularTex; AkColorDesc *color; union { float strength; float shininess; }; AkTextureChannels textureChannels; } AkMaterialSpecularProp; typedef struct AkMaterialClearcoat { AkTextureRef *roughnessTexture; AkTextureRef *normalTexture; AkTextureRef *texture; float intensity; float roughness; float normalScale; AkTextureChannels textureChannels; AkTextureChannels roughnessTextureChannels; } AkMaterialClearcoat; typedef struct AkMaterialEmissionProp { AkColorDesc color; float strength; } AkMaterialEmissionProp; /* KHR_materials_transmission */ typedef struct AkMaterialTransmissionProp { AkTextureRef *texture; float factor; AkTextureChannels textureChannels; } AkMaterialTransmissionProp; typedef struct AkMaterialSheen { AkColorDesc *color; AkTextureRef *roughnessTexture; float roughness; AkTextureChannels roughnessTextureChannels; } AkMaterialSheen; typedef struct AkMaterialIridescence { AkTextureRef *texture; AkTextureRef *thicknessTexture; float factor; float ior; float thicknessMinimum; float thicknessMaximum; AkTextureChannels textureChannels; AkTextureChannels thicknessTextureChannels; } AkMaterialIridescence; typedef struct AkMaterialVolume { AkTextureRef *thicknessTexture; AkColor attenuationColor; float thicknessFactor; float attenuationDistance; AkTextureChannels thicknessTextureChannels; } AkMaterialVolume; typedef struct AkMaterialAnisotropy { AkTextureRef *texture; float strength; float rotation; } AkMaterialAnisotropy; typedef struct AkMaterialDispersion { float dispersion; } AkMaterialDispersion; typedef struct AkMaterialDiffuseTransmission { AkTextureRef *texture; AkColorDesc *color; float factor; AkTextureChannels textureChannels; } AkMaterialDiffuseTransmission; typedef struct AkTechniqueFxCommon { AkColorDesc *ambient; AkMaterialEmissionProp *emission; union { AkColorDesc *diffuse; AkColorDesc *albedo; }; AkOcclusion *occlusion; AkNormalMap *normal; AkMaterialClearcoat *clearcoat; /* metallic properties */ AkMaterialMetallicProp *metalness; AkMaterialMetallicProp *roughness; /* specular */ AkMaterialSpecularProp *specular; /* reflectivity */ AkReflective *reflective; float ior; /* TODO: can we merge transparent and transmission */ /* transparency */ AkTransparent *transparent; AkMaterialTransmissionProp *transmission; AkMaterialSheen *sheen; AkMaterialIridescence *iridescence; AkMaterialVolume *volume; AkMaterialAnisotropy *anisotropy; AkMaterialDispersion *dispersion; AkMaterialDiffuseTransmission *diffuseTransmission; /* common */ AkMaterialType type; bool doubleSided; } AkTechniqueFxCommon; /*! * @brief a helper that returns effect for given mesh prim for a bindMaterial * * @param bindMat bind material object in AkNode * @param meshPrim mesh primitive * @param foundInstMat instance material * * @return effect that points by a AkMaterial */ AK_EXPORT struct AkEffect* ak_effectForBindMaterial(struct AkBindMaterial * __restrict bindMat, struct AkMeshPrimitive * __restrict meshPrim, struct AkInstanceMaterial ** __restrict foundInstMat); #ifdef __cplusplus } #endif #endif /* assetkit_material_h */ ================================================ FILE: include/ak/memory.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_memory_h #define assetkit_memory_h #include #include #include "common.h" #ifdef __cplusplus extern "C" { #endif typedef struct AkObject { struct AkObject * next; size_t size; AkEnum type; void * pData; char data[]; } AkObject; /* Inline payload access — pData is the start of the struct copy living inside the AkObject's tail buffer. Used when ak_objAlloc was invoked with sizeof(SomeStruct). */ #define ak_objGetOrNull(OBJ) (OBJ ? (OBJ)->pData : NULL) #define ak_objGet(OBJ) ((OBJ)->pData) /* Pointer-storage access — when ak_objAlloc was invoked with sizeof(SomePtr) the inline payload is one pointer-sized slot holding a reference to an external object (the referenced object owns its own lifetime). ak_objGetTarget dereferences once to recover that pointer. */ #define ak_objGetTarget(OBJ) (*(void **)((OBJ)->pData)) #define ak_allocator ak_mem_allocator() typedef struct AkHeapAllocator { void *(*malloc)(size_t); void *(*calloc)(size_t, size_t); void *(*realloc)(void *, size_t); int (*memalign)(void **, size_t, size_t); char *(*strdup)(const char *); void (*free)(void *); size_t (*size)(const void *); } AkHeapAllocator; typedef struct AkHeapSrchCtx AkHeapSrchCtx; typedef struct AkHeapNode AkHeapNode; typedef struct AkHeap AkHeap; struct AkURL; typedef int (*AkHeapSrchCmpFn)(void * __restrict key1, void * __restrict key2); typedef void (*AkHeapSrchPrintFn)(void * __restrict key); typedef enum AkHeapFlags { AK_HEAP_FLAGS_NONE = 0, AK_HEAP_FLAGS_INITIALIZED = 1 << 0, AK_HEAP_FLAGS_DYNAMIC = 1 << 1, } AkHeapFlags; typedef enum AkHeapNodeFlags { AK_HEAP_NODE_FLAGS_NONE = 0, /* plain node */ AK_HEAP_NODE_FLAGS_HEAP_CHLD = 1 << 0, /* node has attached heaps */ AK_HEAP_NODE_FLAGS_EXT = 1 << 1, /* node has one of: */ AK_HEAP_NODE_FLAGS_SID_CHLD = 1 << 2, /* least one of children has sid */ AK_HEAP_NODE_FLAGS_RED = 1 << 3, /* RBtree color bit */ AK_HEAP_NODE_FLAGS_SRCH = 1 << 4, /* node has an id */ AK_HEAP_NODE_FLAGS_SID = 1 << 5, /* memory node or its attr has sid */ AK_HEAP_NODE_FLAGS_REFC = 1 << 6, /* node is reference counted */ AK_HEAP_NODE_FLAGS_EXTRA = 1 << 7, /* node has element */ AK_HEAP_NODE_FLAGS_INF = 1 << 8, /* node has element */ AK_HEAP_NODE_FLAGS_URL = 1 << 9, /* node has retained mem via url */ AK_HEAP_NODE_FLAGS_USR = 1 << 10, /* user data */ AK_HEAP_NODE_FLAGS_USRF = 1 << 11, /* user data must be freed */ AK_HEAP_NODE_FLAGS_MMAP = 1 << 12, /* attached mmap-ed memory list */ AK_HEAP_NODE_FLAGS_SID_NODE = AK_HEAP_NODE_FLAGS_SID, AK_HEAP_NODE_FLAGS_EXT_ALL = AK_HEAP_NODE_FLAGS_EXT | AK_HEAP_NODE_FLAGS_SRCH | AK_HEAP_NODE_FLAGS_SID | AK_HEAP_NODE_FLAGS_REFC | AK_HEAP_NODE_FLAGS_EXTRA | AK_HEAP_NODE_FLAGS_INF | AK_HEAP_NODE_FLAGS_URL | AK_HEAP_NODE_FLAGS_USR | AK_HEAP_NODE_FLAGS_USRF | AK_HEAP_NODE_FLAGS_MMAP, AK_HEAP_NODE_FLAGS_EXT_FRST = AK_HEAP_NODE_FLAGS_SRCH } AkHeapNodeFlags; AK_EXPORT AkHeapAllocator * ak_heap_allocator(AkHeap * __restrict heap); AK_EXPORT AkHeap * ak_heap_getheap(void * __restrict memptr); AK_EXPORT AkHeap * ak_heap_default(void); AK_EXPORT AkHeap * ak_heap_new(AkHeapAllocator *allocator, AkHeapSrchCmpFn cmp, AkHeapSrchPrintFn print); AK_EXPORT void ak_heap_attach(AkHeap * __restrict parent, AkHeap * __restrict chld); AK_EXPORT void ak_heap_dettach(AkHeap * __restrict parent, AkHeap * __restrict chld); AK_EXPORT void ak_heap_setdata(AkHeap * __restrict heap, void * __restrict memptr); AK_EXPORT void* ak_heap_data(AkHeap * __restrict heap); AK_EXPORT void ak_heap_init(AkHeap * __restrict heap, AkHeapAllocator * __restrict allocator, AkHeapSrchCmpFn cmp, AkHeapSrchPrintFn print); AK_EXPORT void ak_heap_destroy(AkHeap * __restrict heap); AK_EXPORT char* ak_heap_strdup(AkHeap * __restrict heap, void * __restrict parent, const char * str); AK_EXPORT char* ak_heap_strndup(AkHeap * __restrict heap, void * __restrict parent, const char * str, size_t size); AK_EXPORT void* ak_heap_alloc(AkHeap * __restrict heap, void * __restrict parent, size_t size); AK_EXPORT void* ak_heap_calloc(AkHeap * __restrict heap, void * __restrict parent, size_t size); AK_EXPORT void* ak_heap_realloc(AkHeap * __restrict heap, void * __restrict parent, void * __restrict memptr, size_t newsize); AK_EXPORT void * ak_heap_chld(AkHeapNode *heapNode); AK_EXPORT void ak_heap_chld_set(AkHeapNode * __restrict heapNode, AkHeapNode * __restrict chldNode); AK_EXPORT AkHeapNode * ak_heap_parent(AkHeapNode *heapNode); AK_EXPORT void ak_heap_setp(AkHeapNode * __restrict heapNode, AkHeapNode * __restrict newParent); AK_EXPORT void ak_heap_moveh(AkHeapNode * __restrict heapNode, AkHeap * __restrict newheap); AK_EXPORT void ak_heap_setpm(void * __restrict memptr, void * __restrict newparent); AK_EXPORT void ak_heap_free(AkHeap * __restrict heap, AkHeapNode * __restrict heapNode); AK_EXPORT void ak_heap_cleanup(AkHeap * __restrict heap); AK_EXPORT void * ak_heap_getId(AkHeap * __restrict heap, AkHeapNode * __restrict heapNode); AK_EXPORT void ak_heap_setId(AkHeap * __restrict heap, AkHeapNode * __restrict heapNode, void * __restrict memId); AK_EXPORT AkResult ak_heap_getNodeById(AkHeap * __restrict heap, void * __restrict memId, AkHeapNode ** __restrict dest); AK_EXPORT AkResult ak_heap_getNodeByURL(AkHeap * __restrict heap, struct AkURL * __restrict url, AkHeapNode ** __restrict dest); AK_EXPORT AkResult ak_heap_getMemById(AkHeap * __restrict heap, void * __restrict memId, void ** __restrict dest); AK_EXPORT void* ak_heap_setUserData(AkHeap * __restrict heap, void * __restrict mem, void * __restrict userData); AK_EXPORT int ak_heap_refc(AkHeapNode * __restrict heapNode); AK_EXPORT int ak_heap_retain(AkHeapNode * __restrict heapNode); AK_EXPORT void ak_heap_release(AkHeapNode * __restrict heapNode); AK_EXPORT void ak_heap_printKeys(AkHeap * __restrict heap); /* default heap helpers */ AK_EXPORT AkHeap* ak_attachedHeap(void * __restrict memptr); AK_EXPORT void ak_setAttachedHeap(void * __restrict memptr, AkHeap * __restrict heap); AK_EXPORT AkHeapAllocator * ak_mem_allocator(void); AK_EXPORT void ak_mem_printKeys(void); AK_EXPORT void* ak_malloc(void * __restrict parent, size_t size); AK_EXPORT void* ak_calloc(void * __restrict parent, size_t size); AK_EXPORT char* ak_strdup(void * __restrict parent, const char * __restrict str); AK_EXPORT void* ak_realloc(void * __restrict parent, void * __restrict memptr, size_t newsize); AK_EXPORT void ak_mem_setp(void * __restrict memptr, void * __restrict newparent); AK_EXPORT void * ak_mem_parent(void *mem); AK_EXPORT void ak_free(void * __restrict memptr); AK_EXPORT void * ak_mem_getId(void * __restrict memptr); AK_EXPORT void ak_mem_setId(void * __restrict memptr, void * __restrict memId); AK_EXPORT AkResult ak_mem_getMemById(void * __restrict ctx, void * __restrict memId, void ** __restrict dest); AK_EXPORT int ak_refc(void * __restrict mem); AK_EXPORT int ak_retain(void * __restrict mem); AK_EXPORT void ak_release(void * __restrict mem); /* mem wrapper helpers */ AK_EXPORT AkObject* ak_objAlloc(AkHeap * __restrict heap, void * __restrict memParent, size_t typeSize, AkEnum typeEnum, bool zeroed); AK_EXPORT void* ak_userData(void * __restrict mem); AK_EXPORT void* ak_setUserData(void * __restrict mem, void * __restrict userData); AK_EXPORT AkObject* ak_objFrom(void * __restrict memptr); AK_EXPORT void* ak_mmap_rdonly(int fd, size_t size); AK_EXPORT void ak_unmap(void *file, size_t size); AK_EXPORT void ak_mmap_attach(void * __restrict obj, void * __restrict mapped, size_t sized); AK_EXPORT void ak_unmmap_attached(void * __restrict obj); #ifdef __cplusplus } #endif #endif /* assetkit_memory_h */ ================================================ FILE: include/ak/node.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_node_h #define assetkit_node_h #ifdef __cplusplus extern "C" { #endif #include "common.h" struct AkInstanceMorph; struct AkAccessor; typedef enum AkNodeFlags { AK_NODEF_FIXED_COORD = 1 } AkNodeFlags; /*! * @brief Per-instance TRS accessors for EXT_mesh_gpu_instancing. */ typedef struct AkInstanceAttribs { struct AkAccessor *translation; /* optional, vec3 x count */ struct AkAccessor *rotation; /* optional, vec4 x count (quaternion) */ struct AkAccessor *scale; /* optional, vec3 x count */ uint32_t count; /* number of instances */ } AkInstanceAttribs; typedef enum AkNodeType { AK_NODE_TYPE_NODE = 1, AK_NODE_TYPE_CAMERA_NODE = 2, AK_NODE_TYPE_JOINT = 3 } AkNodeType; typedef struct AkListIter { void *prev; void *next; } AkListIter; typedef struct AkTreeWithParentIter { void *prev; void *next; void *parent; void *chld; } AkTreeWithParentIter; typedef struct AkTreeIter { void *prev; void *next; void *chld; } AkTreeIter; typedef struct AkNode { /* const char * id; */ /* const char * sid; */ const char *name; AkNodeFlags flags; AkNodeType nodeType; AkStringArray *layer; struct AkTransform *transform; bool visible; /* only avilable if library is forced to calculate them check these two matrix to avoid extra or same calculation */ struct AkMatrix *matrix; struct AkMatrix *matrixWorld; struct AkBoundingBox *bbox; AkInstanceGeometry *geometry; AkInstanceBase *camera; AkInstanceBase *light; AkInstanceNode *node; /* EXT_mesh_gpu_instancing, NULL if not authored. */ AkInstanceAttribs *instancing; AkTree *extra; struct AkNode *prev; struct AkNode *next; struct AkNode *chld; struct AkNode *parent; } AkNode; AK_EXPORT void ak_addSubNode(AkNode * __restrict parent, AkNode * __restrict subnode, bool fixCoordSys); /*! * @brief Allocate a fresh AkNode in the document's heap. * * Optionally attaches the new node as a child of @p parent (via * ak_addSubNode without coord-sys fix-up — the caller is in control * of orientation). The node's name is duplicated into the heap if * @p name is non-NULL. * * @param[in] doc document the node will live in (required) * @param[in] parent parent node to attach as child of, or NULL * to leave the new node unparented * @param[in] name optional node name (deep-copied) * * @return Newly allocated AkNode, or NULL on allocation failure. */ AK_EXPORT AkNode * ak_nodeMake(AkDoc * __restrict doc, AkNode * __restrict parent, const char * name); /*! * @brief Find a direct child of @p parent whose name matches @p name. * * Linear scan over the immediate children chain. NULL parent or NULL * name returns NULL. Returns the first match — names aren't unique * by spec, so callers that care should walk the result chain * themselves. */ AK_EXPORT AkNode * ak_nodeFindChildByName(AkNode * __restrict parent, const char * name); /*! * @brief Find a child by name, creating it under @p parent if missing. * * Convenience wrapper for the common "ensure a designated container * node exists" pattern (e.g. a "User Cameras" group). Equivalent to * ak_nodeFindChildByName followed by ak_nodeMake when nothing matches. */ AK_EXPORT AkNode * ak_nodeFindOrMakeChild(AkDoc * __restrict doc, AkNode * __restrict parent, const char * name); /*! * @brief Attach a camera to a node by creating a camera instance. * * Allocates an AkInstanceBase, sets type = AK_INSTANCE_CAMERA, and * chains it onto node->camera (preserving any existing camera * instance — multi-camera nodes are uncommon but legal). * * Does NOT register the camera in the cameras library — that's the * job of ak_camMakePerspective / ak_camMakeOrthographic. Pair them. * * @return The freshly allocated camera instance. */ AK_EXPORT AkInstanceBase * ak_nodeAttachCamera(AkNode * __restrict node, AkCamera * __restrict cam); /*! * @brief Attach a light to a node by creating a light instance. * * Mirrors ak_nodeAttachCamera: allocates an AkInstanceBase with * type = AK_INSTANCE_LIGHT and chains it onto node->light. Pair * with ak_lightMake() which handles the lights library entry. * * @return The freshly allocated light instance. */ AK_EXPORT AkInstanceBase * ak_nodeAttachLight(AkNode * __restrict node, AkLight * __restrict light); /*! * @brief Replace the node's transform with a single column-major 4×4 * matrix (AKT_MATRIX item). * * Allocates an `AkTransform` for the node if it didn't already have * one, then drops a fresh AKT_MATRIX-typed AkObject in its `item` * slot containing the supplied matrix. Any prior transform chain * (translate / rotate / scale items) is replaced — convenient for * runtime UI edits where the user works with a decomposed pose and * we serialize the composed result. * * @param[in] node target node (required) * @param[in] matrix 16 floats in column-major order * (matches cglm / OpenGL / SCNMatrix4 layout) */ AK_EXPORT void ak_nodeSetTransformMatrix(AkNode * __restrict node, const float matrix[16]); /*! * @brief Find a root-level node in a visual scene by name. * * Visual scenes hold their roots as a sibling chain reachable via * `scene->node->next`. This walks that chain looking for a name * match. NULL inputs return NULL. */ AK_EXPORT AkNode * ak_sceneFindRoot(struct AkVisualScene * __restrict scene, const char * name); /*! * @brief Find a root-level node by name, or create one in @p scene. * * Convenience for "ensure a top-level container exists" — e.g. a * "User Cameras" group placed alongside the asset's authored roots. * Created nodes are appended to the end of the existing root chain * (memparent = scene), preserving the original asset ordering. */ AK_EXPORT AkNode * ak_sceneFindOrMakeRoot(AkDoc * __restrict doc, struct AkVisualScene * __restrict scene, const char * name); #ifdef __cplusplus } #endif #endif /* assetkit_node_h */ ================================================ FILE: include/ak/options.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_options_h #define assetkit_options_h #ifdef __cplusplus extern "C" { #endif #include "common.h" /* TODO: */ typedef enum AkOption { AK_OPT_INDICES_DEFAULT = 0, /* false */ AK_OPT_INDICES_SINGLE_INTERLEAVED = 1, /* false */ AK_OPT_INDICES_SINGLE_SEPARATE = 2, /* false */ AK_OPT_INDICES_SINGLE = 3, /* false */ AK_OPT_NOINDEX_INTERLEAVED = 4, /* true */ AK_OPT_NOINDEX_SEPARATE = 5, /* true */ AK_OPT_COORD = 6, /* Y_UP */ AK_OPT_DEFAULT_ID_PREFIX = 7, /* id- */ AK_OPT_COMPUTE_BBOX = 8, /* false */ AK_OPT_TRIANGULATE = 9, /* true */ AK_OPT_GEN_NORMALS_IF_NEEDED = 10, /* true */ AK_OPT_DEFAULT_PROFILE = 11, /* COMMON */ AK_OPT_EFFECT_PROFILE = 12, /* true */ AK_OPT_TECHNIQUE = 13, /* "common" */ AK_OPT_TECHNIQUE_FX = 14, /* "common" */ AK_OPT_ZERO_INDEXED_INPUT = 15, /* false */ AK_OPT_IMAGE_LOAD_FLIP_VERTICALLY = 16, /* true */ AK_OPT_ADD_DEFAULT_CAMERA = 17, /* true */ AK_OPT_ADD_DEFAULT_LIGHT = 18, /* false */ AK_OPT_COORD_CONVERT_TYPE = 19, /* DEFAULT */ AK_OPT_BUGFIXES = 20, /* TRUE */ AK_OPT_COMPUTE_EXACT_CENTER = 21, /* FALSE */ AK_OPT_USE_MMAP = 22, /* TRUE */ /* TODO: not implemented yet, https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#mikktspace https://github.com/mmikk/MikkTSpace */ AK_OPT_GEN_TANGENTS_IF_NEEDED = 23, /* true */ /* if your engine doesnt support triangle strip, triangle fan, let AssetKit convert them to TRIANGLES */ AK_OPT_CVT_TRIANGLESTRIP = 24, /* false */ AK_OPT_CVT_TRIANGLEFAN = 25, /* false */ /* if your engine doesnt support line loop, line strip, let AssetKit convert them to LINES */ AK_OPT_CVT_LINELOOP = 26, /* false */ AK_OPT_CVT_LINESTRIP = 27, /* false */ /* Keep KHR_mesh_quantization accessors in their authored integer form. */ AK_OPT_PRESERVE_QUANTIZED_ATTRS = 28, /* false */ /* Optional glTF extension decoder settings. */ AK_OPT_GLTF_EXT_DECODER_AUTOLOAD = 29, /* true */ AK_OPT_GLTF_MESHOPT_DECODER_PATH = 30, /* NULL */ AK_OPT_GLTF_DRACO_DECODER_PATH = 31, /* NULL */ AK_OPT_GLTF_GSPLAT_DECODER_PATH = 32, /* NULL */ AK_OPT_GLTF_KTX2_DECODER_PATH = 33, /* NULL */ } AkOption; AK_EXPORT void ak_opt_set(AkOption option, uintptr_t value); AK_EXPORT uintptr_t ak_opt_get(AkOption option); AK_EXPORT void ak_opt_set_str(AkOption option, const char *value); #ifdef __cplusplus } #endif #endif /* assetkit_options_h */ ================================================ FILE: include/ak/path.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_path_h #define assetkit_path_h #ifdef __cplusplus extern "C" { #endif #include "common.h" #include struct AkHeap; struct AkDoc; AK_EXPORT const char * ak_path_fragment(const char *path); AK_EXPORT size_t ak_path_trim(const char *path, char *trimmed); AK_EXPORT int ak_path_join(char *fragments[], char *buf, size_t *size); AK_EXPORT int ak_path_isfile(const char *path); AK_EXPORT char* ak_path_dir(struct AkHeap * __restrict heap, void * __restrict memparent, const char * __restrict path); AK_EXPORT const char* ak_fullpath(struct AkDoc * __restrict doc, const char * __restrict ref, char * __restrict buf); #ifdef __cplusplus } #endif #endif /* assetkit_path_h */ ================================================ FILE: include/ak/profile.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_profile_h #define assetkit_profile_h #ifdef __cplusplus extern "C" { #endif #include "common.h" #include "material.h" struct AkEffect; typedef enum AkProfileType { AK_PROFILE_TYPE_UNKOWN =-1, AK_PROFILE_TYPE_COMMON = 0, AK_PROFILE_TYPE_GLTF = 6 } AkProfileType; typedef struct AkTechniqueFx { /* const char * id; */ /* const char * sid; */ AkTechniqueFxCommon *common; AkTree *extra; struct AkTechniqueFx *next; } AkTechniqueFx; typedef struct AkTechniqueOverride { const char * ref; const char * pass; } AkTechniqueOverride; typedef struct AkTechniqueHint { struct AkTechniqueHint *next; const char *platform; const char *ref; const char *profile; AkProfileType profileType; } AkTechniqueHint; struct AkNewParam; typedef struct AkProfile { /* const char * id; */ AkProfileType type; struct AkNewParam *newparam; AkTechniqueFx *technique; AkTree *extra; struct AkProfile *next; } AkProfile; typedef AkProfile AkProfileCommon; typedef AkProfile AkProfileGLTF; AkProfile* ak_profile(struct AkEffect * __restrict effect, AkProfile * __restrict after); AkProfileType ak_profileType(struct AkEffect * __restrict effect); uint32_t ak_supportedProfiles(AkProfileType ** profileTypes); void ak_setSupportedProfiles(AkProfileType profileTypes[], uint32_t count); const char* ak_platform(void); void ak_setPlatform(const char platform[64]); AK_EXPORT AkProfileCommon* ak_getProfileCommon(struct AkEffect * __restrict effect); AK_EXPORT AkTechniqueFxCommon* ak_getProfileTechniqueCommon(struct AkEffect * __restrict effect); #ifdef __cplusplus } #endif #endif /* assetkit_profile_h */ ================================================ FILE: include/ak/sid.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * TODO: maybe there is better way to implement sid addressing? */ #ifndef assetkit_sid_h #define assetkit_sid_h #ifdef __cplusplus extern "C" { #endif #include "common.h" #include "context.h" AK_EXPORT const char * ak_sid_get(void *memnode); AK_EXPORT const char * ak_sid_geta(void *memnode, void *memptr); AK_EXPORT void ak_sid_dup(void *newMemnode, void *oldMemnode); AK_EXPORT void ak_sid_set(void *memnode, const char * __restrict sid); AK_EXPORT void ak_sid_seta(void *memnode, void *memptr, const char * __restrict sid); AK_EXPORT void * ak_sid_resolve(AkContext * __restrict ctx, const char * __restrict target, const char ** __restrict attribString); AK_EXPORT void * ak_sid_resolve_from(AkContext * __restrict ctx, const char * __restrict id, const char * __restrict target, const char ** __restrict attribString); AK_EXPORT void * ak_sid_resolve_val(AkContext * __restrict ctx, const char * __restrict target); AK_EXPORT uint32_t ak_sid_attr_offset(const char *attr); #ifdef __cplusplus } #endif #endif /* assetkit_sid_h */ ================================================ FILE: include/ak/source.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_source_h #define assetkit_source_h #ifdef __cplusplus extern "C" { #endif #include "common.h" #include #include #include "type.h" #include "url.h" #include "core-types.h" /* Input -> Source -> TechniqueCommon (Accessor) -> Buffer Input -> Accessor -> Buffer */ struct AkTechnique; struct AkBuffer; /* for vectors: item count, for matrics: item count | matrix size */ typedef enum AkComponentSize { AK_COMPONENT_SIZE_UNKNOWN = 0, AK_COMPONENT_SIZE_SCALAR = 1, AK_COMPONENT_SIZE_VEC2 = 2, AK_COMPONENT_SIZE_VEC3 = 3, AK_COMPONENT_SIZE_VEC4 = 4, AK_COMPONENT_SIZE_MAT2 = (4 << 3) | 2, AK_COMPONENT_SIZE_MAT3 = (9 << 3) | 3, AK_COMPONENT_SIZE_MAT4 = (16 << 3) | 4 } AkComponentSize; typedef struct AkDataParam { /* const char * sid; */ struct AkDataParam *next; const char *name; const char *semantic; AkTypeDesc type; } AkDataParam; typedef struct AkBuffer { const char *name; void *data; size_t length; } AkBuffer; typedef struct AkAccessor { struct AkBuffer *buffer; const char *name; void *min; void *max; size_t byteOffset; /* byte offset on the buffer */ size_t byteStride; /* stride in bytes */ size_t byteLength; /* total bytes for this accessor */ uint32_t count; /* count to access buffer */ uint32_t bytesPerComponent; /* component stride in bytes */ AkComponentSize componentSize; /* vec1 | vec2 | vec3 | vec4 ... */ AkTypeId componentType; /* single component type */ uint32_t componentCount; size_t fillByteSize; /* filled size for single access */ int32_t gpuTarget; /* GPU buffer target to bound */ bool normalized; /* Source-side metadata preserved across dequantize. When AssetKit widens a normalized integer / KHR_mesh_quantization integer attribute to float, componentType becomes AKT_FLOAT and normalized is cleared — but the original encoding is kept here so callers can reason about the source format (and reconstruct the quantized mapping if needed). When AK_OPT_PRESERVE_QUANTIZED_ATTRS is set and the buffer is left as integers, originalComponentType == componentType and originallyNormalized == normalized. */ AkTypeId originalComponentType; bool originallyNormalized; } AkAccessor; typedef struct AkSource { /* const char * id; */ const char *name; AkBuffer *buffer; AkAccessor *tcommon; struct AkTechnique *technique; struct AkSource *next; int32_t target; } AkSource; typedef struct AkDuplicatorRange { struct AkDuplicatorRange *next; AkUIntArray *dupc; AkUIntArray *dupcsum; size_t startIndex; size_t endIndex; } AkDuplicatorRange; typedef struct AkDuplicator { AkDuplicatorRange *range; void *buffstate; void *vertices; size_t dupCount; size_t bufCount; } AkDuplicator; typedef struct AkSourceBuffState { AkDuplicator *duplicator; void *buff; char *url; size_t count; uint32_t stride; } AkSourceBuffState; typedef struct AkSourceEditHelper { struct AkSourceEditHelper *next; AkAccessor *oldsource; AkAccessor *source; } AkSourceEditHelper; AK_EXPORT AkBuffer* ak_sourceDetachArray(AkAccessor * __restrict acc); /* Dequantize an accessor's source data into a caller-supplied float buffer. Always writes (count * componentCount) floats; outCapacity must be at least that many. Uses originalComponentType / originallyNormalized to drive integer-to-float conversion (normalized integers divide by the type max, non-normalized integers cast to float). Accessors that already store floats are copied through unchanged. This is the on-demand path callers reach for when AssetKit was asked to keep the raw quantized buffer (AK_OPT_PRESERVE_QUANTIZED_ATTRS) but a particular consumer wants floats for one accessor. Returns the number of floats written (0 on error or zero count). */ AK_EXPORT size_t ak_accessorAsFloat(AkAccessor * __restrict acc, float * __restrict out, size_t outCapacity); /* In-place dequantize: replaces the accessor's buffer with a tightly-packed float buffer, updates componentType / byteStride / fillByteSize / normalized, and registers the new buffer on the owning doc. Idempotent — accessors that are already AKT_FLOAT are left untouched. originalComponentType / originallyNormalized are preserved so callers can still recover the source-side encoding after the in-place widen. */ AK_EXPORT void ak_accessorMakeFloat(AkAccessor * __restrict acc); #ifdef __cplusplus } #endif #endif /* assetkit_source_h */ ================================================ FILE: include/ak/string.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_string_h #define assetkit_string_h #include "common.h" #include "core-types.h" AK_EXPORT const char* ak_strltrim_fast(const char * __restrict str); /*! * @brief util for count tokens before call strtok to save realloc calls * * @param[in] buff string buffer * @param[in] sep separators use comma to use multiple e.g. " \t\r" * @param[out] len non-separator char count * * @return returns word count */ AK_EXPORT int ak_strtok_count(char * __restrict buff, char * __restrict sep, size_t *len); AK_EXPORT int ak_strtok_count_fast(char * __restrict buff, size_t srclen, size_t *len); AK_EXPORT unsigned long ak_strtof(char * __restrict src, size_t srclen, unsigned long n, AkFloat * __restrict dest); AK_EXPORT unsigned long ak_strtof_line(char * __restrict src, size_t srclen, unsigned long n, AkFloat * __restrict dest); AK_EXPORT unsigned long ak_strtod(char * __restrict src, size_t srclen, unsigned long n, AkDouble * __restrict dest); AK_EXPORT unsigned long ak_strtoui(char * __restrict src, size_t srclen, unsigned long n, AkUInt * __restrict dest); AK_EXPORT unsigned long ak_strtoi(char * __restrict src, size_t srclen, unsigned long n, AkInt * __restrict dest); AK_EXPORT unsigned long ak_strtoi_line(char * __restrict src, size_t srclen, unsigned long n, AkInt * __restrict dest); AK_EXPORT unsigned long ak_strtob(char * __restrict src, size_t srclen, unsigned long n, AkBool * __restrict dest); AK_EXPORT char* ak_tolower(char *str); AK_EXPORT char* ak_toupper(char *str); #endif /* assetkit_string_h */ ================================================ FILE: include/ak/texture.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_texture_h #define assetkit_texture_h #include "common.h" #include "image.h" typedef enum AkWrapMode { AK_WRAP_MODE_WRAP = 1, AK_WRAP_MODE_MIRROR = 2, AK_WRAP_MODE_CLAMP = 3, AK_WRAP_MODE_BORDER = 4, AK_WRAP_MODE_MIRROR_ONCE = 5 } AkWrapMode; typedef enum AkMinFilter { AK_MINFILTER_LINEAR = 0, AK_MINFILTER_NEAREST = 1, AK_MINFILTER_ANISOTROPIC = 2, AK_LINEAR_MIPMAP_NEAREST = 2, AK_LINEAR_MIPMAP_LINEAR = 3, AK_NEAREST_MIPMAP_NEAREST = 4, AK_NEAREST_MIPMAP_LINEAR = 5 } AkMinFilter; typedef enum AkMagFilter { AK_MAGFILTER_LINEAR = 0, AK_MAGFILTER_NEAREST = 1 } AkMagFilter; typedef enum AkMipFilter { AK_MIPFILTER_LINEAR = 0, AK_MIPFILTER_NONE = 1, AK_MIPFILTER_NEAREST = 2 } AkMipFilter; typedef enum AkTextureColorSpace { AK_TEXTURE_COLORSPACE_UNSPECIFIED = 0, AK_TEXTURE_COLORSPACE_LINEAR = 1, AK_TEXTURE_COLORSPACE_SRGB = 2 } AkTextureColorSpace; typedef enum AkTextureChannels { AK_TEXTURE_CHANNEL_NONE = 0, AK_TEXTURE_CHANNEL_R = 1 << 0, AK_TEXTURE_CHANNEL_G = 1 << 1, AK_TEXTURE_CHANNEL_B = 1 << 2, AK_TEXTURE_CHANNEL_A = 1 << 3, AK_TEXTURE_CHANNEL_RGB = AK_TEXTURE_CHANNEL_R | AK_TEXTURE_CHANNEL_G | AK_TEXTURE_CHANNEL_B, AK_TEXTURE_CHANNEL_RGBA = AK_TEXTURE_CHANNEL_RGB | AK_TEXTURE_CHANNEL_A, AK_TEXTURE_CHANNEL_GB = AK_TEXTURE_CHANNEL_G | AK_TEXTURE_CHANNEL_B } AkTextureChannels; typedef struct AkSampler { const char *uniformName; const char *coordInputName; AkColor *borderColor; // AkInstanceBase *instanceImage; AkTree *extra; const char *name; AkWrapMode wrapS; AkWrapMode wrapT; AkWrapMode wrapP; AkMinFilter minfilter; AkMagFilter magfilter; AkMipFilter mipfilter; uint32_t maxAnisotropy; uint32_t mipMaxLevel; uint32_t mipMinLevel; float mipBias; } AkSampler; typedef struct AkTexture { struct AkTexture *next; AkImage *image; AkSampler *sampler; const char *name; AkTypeId type; } AkTexture; typedef struct AkTextureTransform { AkFloat2 offset; float rotation; AkFloat2 scale; int slot; const char *coordInputName; } AkTextureTransform; typedef struct AkTextureRef { struct AkTexture *texture; /* to bind texture to input coord dynamically, e.g. used by bindMaterial in node like COLLADA */ const char *texcoord; /* glTF like texture bind */ const char *coordInputName; int slot; AkTextureColorSpace colorSpace; AkTextureChannels channels; /* Texture Transform */ AkTextureTransform *transform; } AkTextureRef; AK_INLINE AkTextureRef* ak_texref_usage(AkTextureRef *texref, AkTextureColorSpace colorSpace, AkTextureChannels channels) { if (texref) { texref->colorSpace = colorSpace; texref->channels = channels; } return texref; } #ifdef __cglm__ AK_INLINE mat4s ak_textrans_mat4(AkTextureTransform *transform) { return glms_mat4_(textrans)(transform->scale[0], transform->scale[1], transform->rotation, transform->offset[0], transform->offset[1]); } #endif #endif /* assetkit_texture_h */ ================================================ FILE: include/ak/transform.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_transform_h #define assetkit_transform_h #ifdef __cplusplus extern "C" { #endif #include "common.h" typedef struct AkTransform { AkObject *base; /* fixup transform */ AkObject *item; } AkTransform; typedef struct AkLookAt { /* const char * sid; */ AkFloat3 val[3]; } AkLookAt; typedef struct AkMatrix { /* const char * sid; */ AK_ALIGN(16) AkFloat val[4][4]; } AkMatrix; typedef struct AkRotate { /* const char * sid; */ AK_ALIGN(16) AkFloat val[4]; } AkRotate; typedef struct AkScale { /* const char * sid; */ AkFloat val[3]; } AkScale; typedef struct AkSkew { /* const char * sid; */ AkFloat angle; AkFloat3 rotateAxis; AkFloat3 aroundAxis; } AkSkew; typedef struct AkTranslate { /* const char * sid; */ AkFloat val[3]; } AkTranslate; typedef struct AkQuaternion { AK_ALIGN(16) AkFloat val[4]; } AkQuaternion; /*! * @brief build skew matrix from AkSkew * * @param skew skew element * @param matrix skew matrix (must be aligned 16) */ AK_EXPORT void ak_transformSkewMatrix(AkSkew * __restrict skew, float * matrix); /*! * @brief combines all node's transform elements * * if there is no transform then this returns identity matrix * * @param transform transform * @param matrix combined transform (must be aligned 16) */ AK_EXPORT void ak_transformCombine(AkTransform * __restrict transform, float * __restrict matrix); /*! * @brief combines all node's transform elements in **world coord sys** * * this func walks/traverses to top node to get world coord * this is not cheap op, obviously. * * @param node node * @param matrix combined transform (must be aligned 16) */ AK_EXPORT void ak_transformCombineWorld(AkNode * __restrict node, float * matrix); /*! * @brief duplicate all transforms of node1 to node2 * * @warning duplicated transform will alloc extra memory */ AK_EXPORT void ak_transformDup(AkNode * __restrict srcNode, AkNode * __restrict destNode); AK_EXPORT void ak_transformFreeBase(AkTransform * __restrict transform); AK_EXPORT AkObject* ak_getTransformTRS(AkNode *node, AkTypeId transformType); #ifdef __cplusplus } #endif #endif /* assetkit_transform_h */ ================================================ FILE: include/ak/trash.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_trash_h #define assetkit_trash_h #ifdef __cplusplus extern "C" { #endif #include "common.h" AK_EXPORT void ak_trash_add(void *mem); AK_EXPORT void ak_trash_empty(void); #ifdef __cplusplus } #endif #endif /* assetkit_trash_h */ ================================================ FILE: include/ak/type.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_types_h #define assetkit_types_h #include "common.h" #include typedef enum AkTypeId { AKT_UNKNOWN =-1, AKT_NONE = 0, AKT_CUSTOM = 0, AKT_OBJECT = 1, AKT_BOOL = 2, AKT_BOOL2 = 3, AKT_BOOL3 = 4, AKT_BOOL4 = 5, AKT_INT = 6, AKT_INT2 = 7, AKT_INT3 = 8, AKT_INT4 = 9, AKT_FLOAT = 10, AKT_FLOAT2 = 11, AKT_FLOAT3 = 12, AKT_FLOAT4 = 13, AKT_FLOAT2x2 = 14, AKT_FLOAT3x3 = 15, AKT_FLOAT4x4 = 16, AKT_STRING = 17, AKT_SAMPLER1D = 18, AKT_SAMPLER2D = 19, AKT_SAMPLER3D = 20, AKT_SAMPLER_CUBE = 21, AKT_SAMPLER_RECT = 22, AKT_SAMPLER_DEPTH = 23, AKT_IDREF = 24, AKT_NAME = 25, AKT_SIDREF = 26, AKT_TOKEN = 27, AKT_UINT = 28, AKT_BYTE = 29, AKT_UBYTE = 30, AKT_SHORT = 31, AKT_USHORT = 32, AKT_DOUBLE = 33, AKT_INT64 = 34, AKT_UINT64 = 35, AKT_EFFECT, AKT_PROFILE, AKT_PARAM, AKT_NEWPARAM, AKT_SETPARAM, AKT_TECHNIQUE_FX, AKT_TECHNIQUE, AKT_SAMPLER, AKT_TEXTURE, AKT_TEXTURE_REF, AKT_TEXTURE_NAME, AKT_TEXCOORD, AKT_NODE, AKT_SCENE, AKT_SOURCE, AKT_ACCESSOR, AKT_BUFFER, AKT_GEOMETRY, AKT_MESH, AKT_CONTROLLER, AKT_SKIN, AKT_MORPH, AKT_MORPH_INST, AKT_RESOLVED_TARGET, AKT_LOOKAT, AKT_TRANSLATE, AKT_ROTATE, AKT_SCALE, AKT_SKEW, AKT_MATRIX, AKT_QUATERNION } AkTypeId; typedef struct AkTypeDesc { const char *typeName; AkTypeId typeId; int size; int userData; } AkTypeDesc; AK_EXPORT AkTypeId ak_typeid(void * __restrict mem); AK_EXPORT AkTypeId ak_typeidh(AkHeapNode * __restrict hnode); AK_EXPORT void ak_setypeid(void * __restrict mem, AkTypeId tid); AK_EXPORT bool ak_isKindOf(void * __restrict mem, void * __restrict other); AK_EXPORT AkTypeDesc* ak_typeDesc(AkTypeId typeId); AK_EXPORT AkTypeDesc* ak_typeDescByName(const char * __restrict name); AK_EXPORT void ak_registerType(AkTypeId typeId, AkTypeDesc *desc); #endif /* assetkit_types_h */ ================================================ FILE: include/ak/url.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_url_h #define assetkit_url_h #ifdef __cplusplus extern "C" { #endif #include "common.h" typedef struct AkURL { const char *url; /* only fragment */ struct AkDoc *doc; /* document */ void *ptr; /* direct link */ void *reserved; /* private */ } AkURL; void ak_url_init(void *parent, char *urlstring, AkURL *dest); void ak_url_dup(AkURL *src, void *parent, AkURL *dest); void ak_url_init_with_id(AkHeapAllocator *alc, void *parent, char *idstirng, AkURL *dest); char * ak_url_string(AkHeapAllocator *alc, char *id); void ak_url_ref(AkURL *url); void ak_url_unref(AkURL *url); #ifdef __cplusplus } #endif #endif /* assetkit_url_h */ ================================================ FILE: include/ak/util.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_util_h #define assetkit_util_h #include "common.h" /* pre-defined compare funcs */ AK_EXPORT int ak_cmp_str(void *key1, void *key2); AK_EXPORT int ak_cmp_ptr(void *key1, void *key2); AK_EXPORT int ak_digitsize(size_t number); #endif /* assetkit_util_h */ ================================================ FILE: include/ak/version.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef assetkit_version_h #define assetkit_version_h #define AK_VERSION_MAJOR 0 #define AK_VERSION_MINOR 3 #define AK_VERSION_PATCH 2 #endif /* assetkit_version_h */ ================================================ FILE: scripts/strpool.py ================================================ #!/usr/bin/python3 # # Copyright (C) 2020 Recep Aslantas # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # from os.path import realpath from os.path import dirname basedir = dirname(realpath(__file__)) + '/../' strpools = [ 'src/strpool.py', 'src/io/dae/strpool.py', 'src/io/gltf/strpool.py' ] for sp in strpools: __file__ = basedir + sp exec(open(__file__).read()) ================================================ FILE: src/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) add_subdirectory(anim) add_subdirectory(bbox) add_subdirectory(bitwise) add_subdirectory(camera) add_subdirectory(coord) add_subdirectory(default) add_subdirectory(geom) add_subdirectory(image) add_subdirectory(instance) add_subdirectory(io) add_subdirectory(lib) add_subdirectory(light) add_subdirectory(mat) add_subdirectory(mem) add_subdirectory(mesh) add_subdirectory(morph) add_subdirectory(node) add_subdirectory(platform) add_subdirectory(resc) add_subdirectory(skin) add_subdirectory(topo) add_subdirectory(transform) add_subdirectory(miniz) if(MSVC OR MSYS OR MINGW) add_subdirectory(win32) endif() ================================================ FILE: src/accessor.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "accessor.h" #include "default/semantic.h" #include #include AkAccessor* ak_accessor_dup(AkAccessor *oldacc) { AkHeap *heap; AkAccessor *acc; heap = ak_heap_getheap(oldacc); acc = ak_heap_alloc(heap, ak_mem_parent(oldacc), sizeof(*acc)); memcpy(acc, oldacc, sizeof(*oldacc)); ak_setypeid(acc, AKT_ACCESSOR); return acc; } /* Read one component from a quantized source pointer as float. The componentType describes the bytes actually under `src`, and `normalized` selects between divide-by-typemax (true) and plain cast (false). Used by both ak_accessorAsFloat and ak_accessorMakeFloat. */ AK_INLINE float ak_accessor_componentToFloat(const void * __restrict src, AkTypeId componentType, bool normalized) { switch (componentType) { case AKT_FLOAT: return *(const float *)src; case AKT_BYTE: { float f = (float)(*(const int8_t *)src); if (normalized) { f /= 127.0f; if (f < -1.0f) f = -1.0f; } return f; } case AKT_UBYTE: { float f = (float)(*(const uint8_t *)src); if (normalized) f /= 255.0f; return f; } case AKT_SHORT: { float f = (float)(*(const int16_t *)src); if (normalized) { f /= 32767.0f; if (f < -1.0f) f = -1.0f; } return f; } case AKT_USHORT: { float f = (float)(*(const uint16_t *)src); if (normalized) f /= 65535.0f; return f; } case AKT_INT: return (float)(*(const int32_t *)src); case AKT_UINT: return (float)(*(const uint32_t *)src); default: return 0.0f; } } AK_EXPORT size_t ak_accessorAsFloat(AkAccessor * __restrict acc, float * __restrict out, size_t outCapacity) { size_t needed, perItemBytes; uint32_t comps, perComponentBytes, v, c; char *src; AkTypeId srcType; bool srcNorm; if (!acc || !out || !acc->buffer || !acc->buffer->data || acc->count == 0) return 0; comps = acc->componentCount; needed = (size_t)comps * acc->count; if (outCapacity < needed) return 0; /* Read what's actually in the buffer. When AssetKit dequantized at parse time, componentType is AKT_FLOAT and we just copy through; when AK_OPT_PRESERVE_QUANTIZED_ATTRS kept the integers, componentType still matches the source encoding. originalComponentType is for callers who want to know the source format regardless. */ srcType = acc->componentType; srcNorm = acc->normalized; perComponentBytes = acc->bytesPerComponent; perItemBytes = acc->byteStride ? acc->byteStride : (size_t)comps * perComponentBytes; src = (char *)acc->buffer->data + acc->byteOffset; for (v = 0; v < acc->count; v++) { char *vsrc = src + (size_t)v * perItemBytes; for (c = 0; c < comps; c++) { const void *cp = vsrc + (size_t)c * perComponentBytes; out[(size_t)v * comps + c] = ak_accessor_componentToFloat(cp, srcType, srcNorm); } } return needed; } AK_EXPORT void ak_accessorMakeFloat(AkAccessor * __restrict acc) { AkHeap *heap; AkBuffer *fbuf; size_t floatBufSize; uint32_t comps; if (!acc || acc->componentType == AKT_FLOAT || !acc->buffer || !acc->buffer->data || acc->count == 0 || acc->fillByteSize == 0) return; heap = ak_heap_getheap(acc); comps = acc->componentCount; floatBufSize = (size_t)comps * acc->count * sizeof(float); /* Parent the new buffer on the accessor itself so its lifetime tracks the accessor. The original quantized buffer keeps its existing parent linkage, so callers that retained a reference (e.g. an external GPU upload using the raw bytes) are unaffected. */ fbuf = ak_heap_calloc(heap, acc, sizeof(*fbuf)); fbuf->data = ak_heap_alloc(heap, fbuf, floatBufSize); fbuf->length = floatBufSize; if (!ak_accessorAsFloat(acc, fbuf->data, (size_t)comps * acc->count)) return; acc->buffer = fbuf; acc->byteOffset = 0; acc->bytesPerComponent = sizeof(float); acc->fillByteSize = (size_t)comps * sizeof(float); acc->byteStride = acc->fillByteSize; acc->componentType = AKT_FLOAT; acc->normalized = false; } ================================================ FILE: src/accessor.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_accessor_h #define ak_accessor_h #include "common.h" AkAccessor* ak_accessor_dup(AkAccessor *oldacc); AK_INLINE bool ak_areSimilarLayouts(AkAccessor *acc1, AkAccessor *acc2) { return (acc1 == acc2) || ( acc1->componentType == acc2->componentType && acc1->count == acc2->count && acc1->componentCount == acc2->componentCount && acc1->componentSize == acc2->componentSize ); } #endif /* ak_accessor_h */ ================================================ FILE: src/anim/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/anim/bake.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Animation baking — per-node transform sampling * ============================================== * * Maya's joint convention emits each joint with up to six independent * elements (jointOrient{XYZ} + rotate{XYZ}). The full local * matrix is only correct after all six are composed in document order * (handled by ak_transformCombine). Decomposed-property animation APIs * (Apple SCNNode.eulerAngles, three.js Object3D.rotation, Filament * TransformManager) cannot represent that — they offer three Euler * slots, period. * * Solution: bake. Sample every channel that targets any AkObject in * the node's transform chain on a shared time grid, run * ak_transformCombine per sample, emit a stream of 4×4 local matrices. * Bridges then attach a single transform-keyed animation per node. * * Renderers that compose bone matrices CPU-side every frame * (assetkit-opengl/gk pattern) can skip the bake — they already iterate * channels per frame. The helper is opt-in; ak_nodeNeedsBaking() is the * heuristic. */ #include "../common.h" #include #include #include #include #include /* Cap mostly to keep the per-node fixed-size scratch arrays small. A pathological node would have to authored ~64 transform elements or ~256 channels — neither shows up in real assets. */ #define BAKE_MAX_XFORM_OBJECTS 64 #define BAKE_MAX_CHANNELS 256 #define BAKE_MAX_ANIM_STACK 256 /* Maximum time gap between adjacent samples in the baked output. Reason: consumers (Apple SCNAnimation, three.js KeyframeTrack, ...) linearly interpolate the 4×4 matrix elements between adjacent samples. Linear lerp between rotation matrices is NOT slerp — for large angular deltas (e.g. a propeller spinning 348° in 1 second with only two authored keyframes) the lerp goes through the *interior* of the rotation manifold, producing a near-identity intermediate matrix and a visibly tiny wobble instead of a full spin. Inserting subdivisions every BAKE_MAX_STEP seconds bounds the per-pair angular delta so the lerp approximates a slerp closely enough for typical playback. 30 Hz target (~33 ms). Fine balance: dense enough to handle 360°/s rotations without artifacts (max ~12° between samples), sparse enough to keep memory + decode cost low for long animations. */ #define BAKE_MAX_STEP 0.0333f /* Per-channel binding: where to write the sampled value, and where to read it from. */ typedef struct ChannelBind { AkObject *target; /* AkObject wrapping AkRotate/AkTranslate/... */ uint32_t off; /* logical slot in target (0..3 typical) */ bool isPartial; /* single component vs whole vector */ const float *keyTimes; size_t keyCount; const float *keyValues; /* contiguous; valueStride floats per frame */ size_t valueStride; AkInterpolationType interp; float saved[16]; /* snapshot of target->pData at function entry */ size_t savedLen; /* number of floats valid in saved[] */ } ChannelBind; static int bake_floatcmp(const void *a, const void *b) { float fa = *(const float *)a; float fb = *(const float *)b; return (fa > fb) - (fa < fb); } /* Snapshot length per AkObject type — how many floats live in pData. */ static size_t bake_payloadFloatCount(AkObject *obj) { switch ((AkTypeId)obj->type) { case AKT_TRANSLATE: return 3; /* AkTranslate.val[3] */ case AKT_SCALE: return 3; /* AkScale.val[3] */ case AKT_ROTATE: return 4; /* AkRotate.val[4] */ case AKT_QUATERNION:return 4; /* AkQuaternion.val[4] */ case AKT_MATRIX: return 16; /* AkMatrix.val[4][4] */ case AKT_SKEW: return 7; /* angle + 2 vec3 */ default: return 0; } } /* Linear interpolation between adjacent keyframes. STEP honored. For BEZIER/HERMITE we fall back to LINEAR — the resulting bake is already keyframe-aligned so the caller's interpolation in the engine layer can handle the curve refinement (LINEAR between keyframes is indistinguishable from the original curve at sample times that equal a keyframe). */ static float bake_sampleScalar(const ChannelBind *b, size_t componentIdx, float t) { size_t ki, valStride; float t0, t1, v0, v1, alpha; if (b->keyCount == 0) return 0.0f; /* find first key with time >= t */ for (ki = 0; ki < b->keyCount && b->keyTimes[ki] < t; ki++) { } valStride = b->valueStride; if (ki == 0) { return b->keyValues[componentIdx]; } if (ki >= b->keyCount) { return b->keyValues[(b->keyCount - 1) * valStride + componentIdx]; } v0 = b->keyValues[(ki - 1) * valStride + componentIdx]; v1 = b->keyValues[ki * valStride + componentIdx]; if (b->interp == AK_INTERPOLATION_STEP) return v0; t0 = b->keyTimes[ki - 1]; t1 = b->keyTimes[ki]; alpha = (t1 > t0) ? (t - t0) / (t1 - t0) : 0.0f; return v0 * (1.0f - alpha) + v1 * alpha; } static void bake_collectChannels(AkAnimation * __restrict anim, AkContext * __restrict actx, AkObject ** __restrict xformObjects, int nXform, ChannelBind * __restrict binds, int * __restrict nBinds) { AkAnimation *stack[BAKE_MAX_ANIM_STACK], *next; AkChannel *ch; AkAnimSampler *samp; AkInput *inp, *inputInput, *outputInput; AkResolvedTarget rt; int j, top; bool isOurs; top = 0; while (anim && *nBinds < BAKE_MAX_CHANNELS) { for (ch = anim->channel; ch && *nBinds < BAKE_MAX_CHANNELS; ch = ch->next) { rt = ak_channelTarget(actx, ch); if (!rt.target) continue; isOurs = false; for (j = 0; j < nXform; j++) { if (rt.target == xformObjects[j]) { isOurs = true; break; } } if (!isOurs) continue; samp = ak_getObjectByUrl(&ch->source); if (!samp) continue; inputInput = outputInput = NULL; for (inp = samp->input; inp; inp = inp->next) { if (inp->semantic == AK_INPUT_INPUT && !inputInput) inputInput = inp; else if (inp->semantic == AK_INPUT_OUTPUT && !outputInput) outputInput = inp; } if (!inputInput || !inputInput->accessor || !inputInput->accessor->buffer || !inputInput->accessor->buffer->data || !outputInput || !outputInput->accessor || !outputInput->accessor->buffer || !outputInput->accessor->buffer->data) continue; binds[*nBinds].target = rt.target; binds[*nBinds].off = rt.off; binds[*nBinds].isPartial = rt.isPartial; binds[*nBinds].keyTimes = (const float *) ((const char *)inputInput->accessor->buffer->data + inputInput->accessor->byteOffset); binds[*nBinds].keyCount = inputInput->accessor->count; binds[*nBinds].keyValues = (const float *) ((const char *)outputInput->accessor->buffer->data + outputInput->accessor->byteOffset); binds[*nBinds].valueStride = outputInput->accessor->componentCount; binds[*nBinds].interp = samp->uniInterpolation; (*nBinds)++; } if (anim->animation) { next = (AkAnimation *)anim->base.next; if (next && top < BAKE_MAX_ANIM_STACK) stack[top++] = next; anim = anim->animation; } else if (anim->base.next) { anim = (AkAnimation *)anim->base.next; } else if (top > 0) { anim = stack[--top]; } else { anim = NULL; } } } AK_EXPORT bool ak_nodeNeedsBaking(AkNode * __restrict node) { AkObject *it; uint32_t rotateCount; if (!node || !node->transform) return false; rotateCount = 0; for (it = node->transform->base; it; it = it->next) { if ((AkTypeId)it->type == AKT_ROTATE && ++rotateCount > 1) return true; } for (it = node->transform->item; it; it = it->next) { if ((AkTypeId)it->type == AKT_ROTATE && ++rotateCount > 1) return true; } return false; } AK_EXPORT AkBakedAnimation * ak_nodeBakeAnimation(AkDoc * __restrict doc, AkNode * __restrict node) { AkLibrary *animLib; AkOneWayIterBase *animIt; AkObject *xformObjects[BAKE_MAX_XFORM_OBJECTS]; AkObject *it; AkBakedAnimation *out; float *allTimes, *dense; ChannelBind binds[BAKE_MAX_CHANNELS]; AkContext actx; size_t totalTimes, uniqueCount, denseCount, subdivs, d, s; size_t i, k, t_idx; float t0, t1, gap; int nXform, nBinds, j; if (!doc || !node || !node->transform) return NULL; /* 1. Snapshot the AkObjects that make up the node's transform chain. Order in the array doesn't matter for matching, only ak_transformCombine cares about chain order — and that's read directly from node->transform each sample. */ nXform = 0; for (it = node->transform->base; it && nXform < BAKE_MAX_XFORM_OBJECTS; it = it->next) { xformObjects[nXform++] = it; } for (it = node->transform->item; it && nXform < BAKE_MAX_XFORM_OBJECTS; it = it->next) { xformObjects[nXform++] = it; } if (nXform == 0) return NULL; /* 2. Walk every animation channel; bind those whose resolved target is one of our transform AkObjects. */ memset(&actx, 0, sizeof(actx)); actx.doc = doc; nBinds = 0; for (animLib = doc->lib.animations; animLib; animLib = animLib->next) { for (animIt = animLib->chld; animIt; animIt = animIt->next) { bake_collectChannels((AkAnimation *)animIt, &actx, xformObjects, nXform, binds, &nBinds); if (nBinds >= BAKE_MAX_CHANNELS) break; } } if (nBinds == 0) return NULL; /* 3. Snapshot animated AkObject payloads so we can restore them on exit. This lets the caller continue using node->transform for bind-pose composition without observing the mutations we make per sample. */ for (j = 0; j < nBinds; j++) { binds[j].savedLen = bake_payloadFloatCount(binds[j].target); if (binds[j].savedLen > 0) memcpy(binds[j].saved, binds[j].target->pData, binds[j].savedLen * sizeof(float)); } /* 4. Build the union time grid: concat all channels' keyTimes, sort, dedupe in place. Allocates total then shrinks via uniqueCount. */ totalTimes = 0; for (j = 0; j < nBinds; j++) totalTimes += binds[j].keyCount; if (totalTimes == 0) { /* restore + bail */ for (j = 0; j < nBinds; j++) { if (binds[j].savedLen > 0) memcpy(binds[j].target->pData, binds[j].saved, binds[j].savedLen * sizeof(float)); } return NULL; } allTimes = ak_calloc(NULL, sizeof(float) * totalTimes); k = 0; for (j = 0; j < nBinds; j++) { memcpy(allTimes + k, binds[j].keyTimes, binds[j].keyCount * sizeof(float)); k += binds[j].keyCount; } qsort(allTimes, totalTimes, sizeof(float), bake_floatcmp); uniqueCount = 0; for (i = 0; i < totalTimes; i++) { if (uniqueCount == 0 || allTimes[i] > allTimes[uniqueCount - 1]) { allTimes[uniqueCount++] = allTimes[i]; } } /* 4b. Densify: insert subdivisions between adjacent samples whose gap exceeds BAKE_MAX_STEP. Without this, sparse keyframes (e.g. 2 frames spanning a 348° rotation) would force the consumer to linearly interpolate matrix elements across the gap — that misses the rotation manifold and renders as a near-identity wobble. Densifying bounds the per-pair angular delta. Use ceil rather than floor: a 50 ms gap with 33.3 ms step truncates to 1 subdivision (still 50 ms wide) under floor division, leaving the per-pair gap above the stated maximum. ceilf bounds it. */ denseCount = 1; /* first sample always emitted */ for (i = 1; i < uniqueCount; i++) { gap = allTimes[i] - allTimes[i - 1]; if (gap > BAKE_MAX_STEP) { denseCount += (size_t)ceilf(gap / BAKE_MAX_STEP); } else { denseCount += 1; } } if (denseCount > uniqueCount) { dense = ak_calloc(NULL, sizeof(float) * denseCount); d = 0; dense[d++] = allTimes[0]; for (i = 1; i < uniqueCount; i++) { t0 = allTimes[i - 1]; t1 = allTimes[i]; gap = t1 - t0; subdivs = (gap > BAKE_MAX_STEP) ? (size_t)ceilf(gap / BAKE_MAX_STEP) : 1; for (s = 1; s <= subdivs; s++) { dense[d++] = t0 + (gap * (float)s / (float)subdivs); } } ak_free(allTimes); allTimes = dense; uniqueCount = denseCount; } /* 5. Allocate the result. The matrices/times buffers parent off `out`, so a single ak_free(out) cleans up everything. */ out = ak_calloc(NULL, sizeof(*out)); out->count = (uint32_t)uniqueCount; out->matrices = ak_calloc(out, sizeof(float) * 16 * uniqueCount); out->times = ak_calloc(out, sizeof(float) * uniqueCount); /* 6. For each unique time, sample channels, mutate AkObjects in place, then run the standard combine over node->transform. The combiner sees current AkObject state, so the per-sample matrix reflects all rotates / translates / scales of the chain. */ for (t_idx = 0; t_idx < uniqueCount; t_idx++) { float t = allTimes[t_idx]; float matrix[16]; for (j = 0; j < nBinds; j++) { ChannelBind *b = &binds[j]; float *dst = (float *)b->target->pData; size_t n = b->savedLen; if (b->isPartial) { /* Single-component animation. The sampler's OUTPUT is scalar per keyframe (componentCount==1), so the component index into keyValues is 0. We write that into target->pData[off]. */ if (b->off < n) dst[b->off] = bake_sampleScalar(b, 0, t); } else { /* Whole-target animation: OUTPUT has valueStride components per keyframe; one-to-one with target->pData. */ size_t lim = b->valueStride < n ? b->valueStride : n; for (k = 0; k < lim; k++) dst[k] = bake_sampleScalar(b, k, t); } } matrix[0] = 1; matrix[1] = 0; matrix[2] = 0; matrix[3] = 0; matrix[4] = 0; matrix[5] = 1; matrix[6] = 0; matrix[7] = 0; matrix[8] = 0; matrix[9] = 0; matrix[10] = 1; matrix[11] = 0; matrix[12] = 0; matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; ak_transformCombine(node->transform, matrix); memcpy(out->matrices + t_idx * 16, matrix, 16 * sizeof(float)); out->times[t_idx] = t; } /* 7. Restore static AkObject payloads. Bind pose is now untouched. */ for (j = 0; j < nBinds; j++) { if (binds[j].savedLen > 0) memcpy(binds[j].target->pData, binds[j].saved, binds[j].savedLen * sizeof(float)); } ak_free(allTimes); return out; } ================================================ FILE: src/anim/conflict.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Animation conflict detection * ============================ * * Two animations conflict iff any pair of their channels resolves to the * SAME animatable slot — i.e. they would write to the same memory at * playback time. Runtimes that play multiple animations in parallel use * this to decide which clips can co-exist; overlapping writes otherwise * produce undefined ordering. * * Resolution semantics (matches `ak_channelTarget`): * * target : the struct being animated (AkTransform component, AkInstanceMorph, ...) * off : LOGICAL slot/component index within target * isPartial: whether the channel writes a single component (true) or the * whole target value (false) * * Conflict rules between two resolved channels (rta, rtb): * * 1. rta.target != rtb.target → no conflict (disjoint memory) * 2. !rta.isPartial OR !rtb.isPartial → CONFLICT * (a whole-target write covers every slot, so any companion write * on the same target is overlapped) * 3. both partial AND rta.off == rtb.off → CONFLICT (same slot) * 4. both partial AND different off → no conflict * * This is exhaustive at the AssetKit semantic layer. Bridges may layer * further keyPath-level checks on top when their target mapping fans * multiple AkObjects to the same engine-side property (e.g. AKT_MATRIX * and AKT_TRANSLATE both affect a single node transform). */ #include "../common.h" AK_EXPORT bool ak_animationsConflict(AkContext * __restrict ctx, AkAnimation * __restrict a, AkAnimation * __restrict b) { AkChannel *cha, *chb; AkResolvedTarget rta, rtb; if (!a || !b || a == b) return false; for (cha = a->channel; cha; cha = cha->next) { rta = ak_channelTarget(ctx, cha); if (!rta.target) continue; for (chb = b->channel; chb; chb = chb->next) { rtb = ak_channelTarget(ctx, chb); if (!rtb.target || rtb.target != rta.target) continue; /* at least one side writes the whole target → overlap */ if (!rta.isPartial || !rtb.isPartial) return true; /* both partial — overlap iff same slot */ if (rta.off == rtb.off) return true; } } return false; } AK_EXPORT size_t ak_animationsCompatibleSet(AkContext * __restrict ctx, AkAnimation * __restrict primary, AkAnimation ** __restrict candidates, size_t candidatesCount, AkAnimation ** __restrict outCompatible) { size_t selected, i, j; bool conflicts; if (!outCompatible) return 0; selected = 0; if (primary) { outCompatible[selected++] = primary; } if (!candidates || candidatesCount == 0) return selected; for (i = 0; i < candidatesCount; i++) { if (!candidates[i] || candidates[i] == primary) continue; conflicts = false; for (j = 0; j < selected; j++) { if (ak_animationsConflict(ctx, candidates[i], outCompatible[j])) { conflicts = true; break; } } if (!conflicts) { outCompatible[selected++] = candidates[i]; } } return selected; } AK_EXPORT size_t ak_animationsCount(AkDoc * __restrict doc) { AkLibrary *lib; AkOneWayIterBase *iter; size_t count; if (!doc) return 0; count = 0; for (lib = doc->lib.animations; lib; lib = lib->next) { for (iter = lib->chld; iter; iter = iter->next) { count++; } } return count; } AK_EXPORT size_t ak_animationsCompatibleSetFromDoc(AkContext * __restrict ctx, AkDoc * __restrict doc, AkAnimation * __restrict primary, AkAnimation ** __restrict outCompatible) { AkLibrary *lib; AkOneWayIterBase *iter; AkAnimation **candidates; size_t count, i; if (!doc || !outCompatible) { /* still honor primary even with no candidates */ if (primary && outCompatible) { outCompatible[0] = primary; return 1; } return 0; } count = ak_animationsCount(doc); if (count == 0) { if (primary) { outCompatible[0] = primary; return 1; } return 0; } candidates = alloca(sizeof(*candidates) * count); i = 0; for (lib = doc->lib.animations; lib; lib = lib->next) { for (iter = lib->chld; iter; iter = iter->next) { candidates[i++] = (AkAnimation *)iter; } } return ak_animationsCompatibleSet(ctx, primary, candidates, count, outCompatible); } ================================================ FILE: src/array.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "array.h" #include static bool ak_strarray_is_sep(char c, char separator) { return c == separator || c == '\n' || c == '\r' || c == '\t'; } static size_t ak_strarray_count(const char *content, char separator) { size_t count; bool inTok; count = 0; inTok = false; if (!content) return 0; while (*content) { if (ak_strarray_is_sep(*content, separator)) { inTok = false; } else if (!inTok) { inTok = true; count++; } content++; } return count; } AK_HIDE AkResult ak_strtostr_array(AkHeap * __restrict heap, void * __restrict memParent, char *content, char separator, AkStringArray ** __restrict array) { AkStringArray *stringArray; char *pData; char *tok; char separatorStr[5]; size_t arrayIndex; size_t itemCount; size_t arraySize; size_t arrayDataSize; arrayIndex = 0; if (!content || !array) return AK_EINVAL; itemCount = ak_strarray_count(content, separator); separatorStr[0] = separator; separatorStr[1] = '\n'; separatorStr[2] = '\r'; separatorStr[3] = '\t'; separatorStr[4] = '\0'; /* |pSTR1|pSTR2|pSTR3|STR1\0STR2\0STR3| the last one is pointer to all data */ arraySize = sizeof(char *) * (itemCount + 1); arrayDataSize = strlen(content) + itemCount + 1 /* NULL */; stringArray = ak_heap_alloc(heap, memParent, sizeof(*stringArray) + arraySize); if (!stringArray) return AK_ENOMEM; pData = ak_heap_alloc(heap, stringArray, arrayDataSize); if (!pData) return AK_ENOMEM; stringArray->count = itemCount; stringArray->items[itemCount] = pData; pData[0] = '\0'; tok = strtok(content, separatorStr); while (tok) { strcpy(pData, tok); stringArray->items[arrayIndex++] = pData; pData += strlen(tok); *pData++ = '\0'; tok = strtok(NULL, separatorStr); } *array = stringArray; return AK_OK; } AK_HIDE AkResult ak_strtostr_arrayL(AkHeap * __restrict heap, void * __restrict memParent, char * stringRep, char separator, AkStringArrayL ** __restrict array) { AkStringArrayL *stringArray; char *pData; char *tok; char separatorStr[5]; size_t arrayIndex; size_t itemCount; size_t arraySize; size_t arrayDataSize; arrayIndex = 0; if (!stringRep || !array) return AK_EINVAL; itemCount = ak_strarray_count(stringRep, separator); separatorStr[0] = separator; separatorStr[1] = '\n'; separatorStr[2] = '\r'; separatorStr[3] = '\t'; separatorStr[4] = '\0'; /* |pSTR1|pSTR2|pSTR3|STR1\0STR2\0STR3| the last one is pointer to all data */ arraySize = sizeof(char *) * (itemCount + 1); arrayDataSize = strlen(stringRep) + itemCount + 1 /* NULL */; stringArray = ak_heap_alloc(heap, memParent, sizeof(*stringArray) + arraySize); if (!stringArray) return AK_ENOMEM; pData = ak_heap_alloc(heap, stringArray, arrayDataSize); if (!pData) return AK_ENOMEM; stringArray->count = itemCount; stringArray->items[itemCount] = pData; pData[0] = '\0'; tok = strtok(stringRep, separatorStr); while (tok) { strcpy(pData, tok); stringArray->items[arrayIndex++] = pData; pData += strlen(tok); *pData++ = '\0'; tok = strtok(NULL, separatorStr); } *array = stringArray; return AK_OK; } ================================================ FILE: src/array.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_array_h #define ak_array_h #include "../include/ak/assetkit.h" AK_HIDE AkResult ak_strtostr_array(AkHeap * __restrict heap, void * __restrict memParent, char *content, char separator, AkStringArray ** __restrict array); AK_HIDE AkResult ak_strtostr_arrayL(AkHeap * __restrict heap, void * __restrict memParent, char *content, char separator, AkStringArrayL ** __restrict array); #endif /* ak_array_h */ ================================================ FILE: src/asset.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "common.h" #include #include #include static AkTree** ak_extraField(void * __restrict obj) { AkHeap *heap; if (!obj) return NULL; heap = ak_heap_getheap(obj); if (heap && ak_heap_data(heap) == obj) return &((AkDoc *)obj)->extra; switch (ak_typeid(obj)) { case AKT_NODE: return &((AkNode *)obj)->extra; case AKT_SCENE: return &((AkVisualScene *)obj)->extra; case AKT_GEOMETRY: return &((AkGeometry *)obj)->extra; case AKT_MESH: return &((AkMesh *)obj)->extra; case AKT_EFFECT: return &((AkEffect *)obj)->extra; case AKT_PROFILE: return &((AkProfile *)obj)->extra; case AKT_TECHNIQUE_FX: return &((AkTechniqueFx *)obj)->extra; case AKT_SAMPLER: case AKT_SAMPLER2D: return &((AkSampler *)obj)->extra; default: break; } return NULL; } AK_EXPORT AkTree* ak_extra(void * __restrict obj) { AkHeapNode *hnode; AkTree **extra; if (!obj) return NULL; hnode = ak__alignof(obj); if ((extra = ak_heap_ext_get(hnode, AK_HEAP_NODE_FLAGS_EXTRA)) && *extra) return *extra; if ((extra = ak_extraField(obj))) return *extra; return NULL; } AK_EXPORT void ak_extra_set(void * __restrict obj, AkTree * __restrict extra) { AkHeap *heap; AkHeapNode *hnode; AkTree **slot; AkTree **field; if (!obj) return; heap = ak_heap_getheap(obj); hnode = ak__alignof(obj); slot = ak_heap_ext_add(heap, hnode, AK_HEAP_NODE_FLAGS_EXTRA); *slot = extra; if ((field = ak_extraField(obj))) *field = extra; } AK_EXPORT void* ak_getAssetInfo(void * __restrict obj, size_t itemOffset) { AkHeapNode *hnode; char **ai; void **found; assert(obj && itemOffset < sizeof(AkAssetInf)); hnode = ak__alignof(obj); do { if ((ai = ak_heap_ext_get(hnode, AK_HEAP_NODE_FLAGS_INF)) && (found = (void **)(*ai + itemOffset))) return *found; hnode = ak_heap_parent(hnode); if (!hnode) return NULL; } while (true); } AK_EXPORT AkCoordSys* ak_getCoordSys(void * __restrict obj) { /* TODO: return default coord sys if null */ return ak_getAssetInfo(obj, offsetof(AkAssetInf, coordSys)); } AK_EXPORT bool ak_hasCoordSys(void * __restrict obj) { AkHeapNode *hnode; char **ai; void **found; hnode = ak__alignof(obj); ai = ak_heap_ext_get(hnode, AK_HEAP_NODE_FLAGS_INF); if (!ai) return false; found = (void **)(*ai + offsetof(AkAssetInf, coordSys)); return found != NULL; } ================================================ FILE: src/assetkit.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../include/ak/assetkit.h" #include "utils.h" #include "io/dae/dae.h" #include "io/gltf/imp/gltf.h" #include "io/obj/obj.h" #include "io/stl/stl.h" #include "io/ply/ply.h" #include "io/3mf/imp/3mf.h" #include #include #include typedef struct { const char * fext; AkResult (*floader_fn)(AkDoc ** __restrict, const char * __restrict); } floader_t; AK_EXPORT AkResult ak_load(AkDoc ** __restrict dest, const char * __restrict url, ...) { floader_t *floader; const char *localurl; int file_type; int _err_no; va_list pref_args; va_start(pref_args, url); file_type = va_arg(pref_args, int); va_end(pref_args); localurl = ak_getFile(url); if (!localurl) return AK_EBADF; floader_t floaders[] = { {"dae", dae_doc}, {"gltf", gltf_gltf}, {"glb", gltf_glb}, {"obj", wobj_obj}, {"stl", stl_stl}, {"ply", ply_ply}, {"3mf", imp_3mf}, }; floader = NULL; if (file_type == AK_FILE_TYPE_AUTO) { char * file_ext; file_ext = strrchr(localurl, '.'); if (file_ext) { int floader_len; int i; ++file_ext; floader_len = AK_ARRAY_LEN(floaders); for (i = 0; i < floader_len; i++) { if (strcmp(file_ext, floaders[i].fext) == 0) { floader = &floaders[i]; break; } } } else { /* TODO */ } } else { switch (file_type) { case AK_FILE_TYPE_COLLADA: { floader = &floaders[0]; break; } case AK_FILE_TYPE_GLTF: { floader = &floaders[1]; break; } case AK_FILE_TYPE_WAVEFRONT: floader = &floaders[3]; break; case AK_FILE_TYPE_STL: floader = &floaders[4]; break; case AK_FILE_TYPE_PLY: floader = &floaders[5]; break; case AK_FILE_TYPE_3MF: floader = &floaders[6]; break; default: *dest = NULL; break; } } if (floader) _err_no = floader->floader_fn(dest, localurl); else goto err; return _err_no; err: *dest = NULL; return AK_ERR; } ================================================ FILE: src/base64.c ================================================ /* * Base64 encoding/decoding (RFC1341) * Copyright (c) 2005-2011, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * * Modified by Recep Aslantas (github @recp) */ #include "base64.h" #include "simd/base64.h" #include static const unsigned char base64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const unsigned char base64_dtable[256] = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x3e, 0x80, 0x80, 0x80, 0x3f, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }; /** * base64_encode - Base64 encode * @src: Data to be encoded * @len: Length of the data to be encoded * @out_len: Pointer to output length variable, or %NULL if not used * Returns: Allocated buffer of out_len bytes of encoded data, * or %NULL on failure * * Caller is responsible for freeing the returned buffer. Returned buffer is * nul terminated to make it easier to use as a C string. The nul terminator is * not included in out_len. */ AK_HIDE unsigned char* base64_encode(AkHeap * __restrict heap, void * __restrict memparent, const unsigned char * __restrict src, size_t len, size_t * __restrict out_len) { unsigned char *out, *pos; const unsigned char *end, *in; size_t olen; int line_len; olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */ olen += olen / 72; /* line feeds */ olen++; /* nul termination */ if (olen < len) return NULL; /* integer overflow */ out = ak_heap_alloc(heap, memparent, olen); if (out == NULL) return NULL; end = src + len; in = src; pos = out; line_len = 0; while (end - in >= 3) { *pos++ = base64_table[in[0] >> 2]; *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; *pos++ = base64_table[in[2] & 0x3f]; in += 3; line_len += 4; if (line_len >= 72) { *pos++ = '\n'; line_len = 0; } } if (end - in) { *pos++ = base64_table[in[0] >> 2]; if (end - in == 1) { *pos++ = base64_table[(in[0] & 0x03) << 4]; *pos++ = '='; } else { *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; *pos++ = base64_table[(in[1] & 0x0f) << 2]; } *pos++ = '='; line_len += 4; } if (line_len) *pos++ = '\n'; *pos = '\0'; if (out_len) *out_len = pos - out; return out; } /** * base64_decode - Base64 decode * @src: Data to be decoded * @len: Length of the data to be decoded * @out_len: Pointer to output length variable * Returns: Allocated buffer of out_len bytes of decoded data, * or %NULL on failure * * Caller is responsible for freeing the returned buffer. */ AK_HIDE unsigned char* base64_decode_slow(AkHeap * __restrict heap, void * __restrict memparent, const unsigned char * __restrict src, size_t len, size_t * __restrict out_len) { unsigned char dtable[256], *out, *pos, block[4], tmp; size_t i, count, olen; int pad = 0; memset(dtable, 0x80, 256); for (i = 0; i < sizeof(base64_table) - 1; i++) dtable[base64_table[i]] = (unsigned char) i; dtable['='] = 0; count = 0; for (i = 0; i < len; i++) { if (dtable[src[i]] != 0x80) count++; } if (count == 0 || count % 4) return NULL; olen = count / 4 * 3; pos = out = ak_heap_alloc(heap, memparent, olen); if (out == NULL) return NULL; count = 0; for (i = 0; i < len; i++) { tmp = dtable[src[i]]; if (tmp == 0x80) continue; if (src[i] == '=') pad++; block[count] = tmp; count++; if (count == 4) { *pos++ = (block[0] << 2) | (block[1] >> 4); *pos++ = (block[1] << 4) | (block[2] >> 2); *pos++ = (block[2] << 6) | block[3]; count = 0; if (pad) { if (pad == 1) pos--; else if (pad == 2) pos -= 2; else { /* Invalid padding */ ak_free(out); return NULL; } break; } } } *out_len = pos - out; return out; } /** * Fast path for compact data URIs. glTF embedded buffers/images are normally * emitted without whitespace, so avoid the validation/counting pass. */ AK_HIDE unsigned char* base64_decode(AkHeap * __restrict heap, void * __restrict memparent, const unsigned char * __restrict src, size_t len, size_t * __restrict out_len) { unsigned char *out, *pos; size_t i, olen; size_t simd_consumed, simd_written; int pad; if (len == 0 || (len & 3) != 0) return base64_decode_slow(heap, memparent, src, len, out_len); pad = 0; if (src[len - 1] == '=') { pad++; if (src[len - 2] == '=') pad++; } olen = len / 4 * 3 - (size_t)pad; pos = out = ak_heap_alloc(heap, memparent, olen ? olen : 1); if (out == NULL) return NULL; if (!ak_simd_base64_decode(src, len, out, &simd_consumed, &simd_written)) { ak_free(out); return base64_decode_slow(heap, memparent, src, len, out_len); } i = simd_consumed; pos = out + simd_written; for (; i < len; i += 4) { unsigned char a, b, c, d; int final; final = i + 4 == len; if (src[i] == '=' || src[i + 1] == '=' || ((src[i + 2] == '=' || src[i + 3] == '=') && !final) || (src[i + 2] == '=' && src[i + 3] != '=')) { ak_free(out); return base64_decode_slow(heap, memparent, src, len, out_len); } a = base64_dtable[src[i]]; b = base64_dtable[src[i + 1]]; c = src[i + 2] == '=' ? 0 : base64_dtable[src[i + 2]]; d = src[i + 3] == '=' ? 0 : base64_dtable[src[i + 3]]; if ((a | b | c | d) & 0x80) { ak_free(out); return base64_decode_slow(heap, memparent, src, len, out_len); } *pos++ = (unsigned char)((a << 2) | (b >> 4)); if (src[i + 2] != '=') { *pos++ = (unsigned char)((b << 4) | (c >> 2)); if (src[i + 3] != '=') *pos++ = (unsigned char)((c << 6) | d); } } *out_len = (size_t)(pos - out); return out; } AK_HIDE void base64_buff(const char * __restrict b64, size_t len, AkBuffer * __restrict buff) { const char *b64Data; const char *marker; size_t markerLen; markerLen = sizeof(";base64,") - 1; marker = memchr(b64, ';', len); if (!marker || (size_t)(len - (uintptr_t)(marker - b64)) <= markerLen) return; if (memcmp(marker, ";base64,", markerLen) != 0) return; b64Data = marker + markerLen; buff->data = base64_decode(ak_heap_getheap(buff), buff, (const unsigned char *)b64Data, len - (uintptr_t)(b64Data - b64), &buff->length); } ================================================ FILE: src/base64.h ================================================ /* * Base64 encoding/decoding (RFC1341) * Copyright (c) 2005, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * * Modified by Recep Aslantas (github @recp) */ #ifndef BASE64_H #define BASE64_H #include #include #include "../include/ak/memory.h" #include "../include/ak/source.h" AK_HIDE unsigned char* base64_encode(AkHeap * __restrict heap, void * __restrict memparent, const unsigned char * __restrict src, size_t len, size_t * __restrict out_len); AK_HIDE unsigned char* base64_decode(AkHeap * __restrict heap, void * __restrict memparent, const unsigned char * __restrict src, size_t len, size_t * __restrict out_len); AK_HIDE void base64_buff(const char * __restrict b64, size_t len, AkBuffer * __restrict buff); #endif /* BASE64_H */ ================================================ FILE: src/bbox/CMakeLists.txt ================================================ target_sources(${PROJECT_NAME} PRIVATE bbox.h bbox.c geom.c mesh_prim.c mesh.c scene.c ) ================================================ FILE: src/bbox/bbox.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "bbox.h" #include void ak_bbox_pick(vec3 min, vec3 max, vec3 vec) { glm_vec3_minv(min, vec, min); glm_vec3_maxv(max, vec, max); } void ak_bbox_pick_pbox(AkBoundingBox *parent, AkBoundingBox *chld) { glm_vec3_minv(parent->min, chld->min, parent->min); glm_vec3_maxv(parent->max, chld->max, parent->max); } void ak_bbox_pick_pbox2(AkBoundingBox *parent, float vec1[3], float vec2[3]) { glm_vec3_minv(parent->min, vec1, parent->min); glm_vec3_minv(parent->min, vec2, parent->min); glm_vec3_maxv(parent->max, vec1, parent->max); glm_vec3_maxv(parent->max, vec2, parent->max); } void ak_bbox_center(AkBoundingBox * __restrict bbox, float center[3]) { glm_vec3_center(bbox->min, bbox->max, center); } float ak_bbox_radius(AkBoundingBox * __restrict bbox) { return glm_vec3_distance(bbox->max, bbox->min) * 0.5f; } ================================================ FILE: src/bbox/bbox.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_src_bbox_h #define ak_src_bbox_h #include "../common.h" #include "../../include/ak/bbox.h" AK_INLINE void ak_bbox_invalidate(AkBoundingBox * __restrict bbox) { glm_vec3_broadcast(FLT_MAX, bbox->min); glm_vec3_broadcast(-FLT_MAX, bbox->max); } void ak_bbox_pick(float min[3], float max[3], float vec[3]); void ak_bbox_pick_pbox(AkBoundingBox *parent, AkBoundingBox *chld); void ak_bbox_pick_pbox2(AkBoundingBox *parent, float vec1[3], float vec2[3]); #endif /* ak_src_bbox_h */ ================================================ FILE: src/bbox/geom.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "bbox.h" void ak_bbox_geom(struct AkGeometry * __restrict geom) { AkObject *primitive; primitive = geom->gdata; switch ((AkGeometryType)primitive->type) { case AK_GEOMETRY_MESH: ak_bbox_mesh(ak_objGet(primitive)); break; case AK_GEOMETRY_SPLINE: /* TODO: */ case AK_GEOMETRY_BREP: /* TODO: */ break; } } ================================================ FILE: src/bbox/mesh.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "bbox.h" void ak_bbox_mesh(struct AkMesh * __restrict mesh) { AkMeshPrimitive *prim; vec3 center; int32_t primcount; primcount = 0; prim = mesh->primitive; if (!mesh->bbox) mesh->bbox = ak_heap_calloc(ak_heap_getheap(prim), ak_objFrom(mesh), sizeof(*mesh->bbox)); ak_bbox_invalidate(mesh->bbox); while (prim) { ak_bbox_mesh_prim(prim); primcount++; prim = prim->next; } /* compute centroid */ if (!ak_opt_get(AK_OPT_COMPUTE_EXACT_CENTER)) { ak_bbox_center(mesh->bbox, mesh->center); } else { glm_vec3_zero(center); /* calculate exact center of primitive */ if (primcount > 0) { while (prim) { ak_bbox_mesh_prim(prim); glm_vec3_add(prim->center, center, center); primcount++; prim = prim->next; } glm_vec3_divs(center, (float)primcount, mesh->center); } } } ================================================ FILE: src/bbox/mesh_prim.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "bbox.h" #include #include void ak_bbox_mesh_prim(struct AkMeshPrimitive * __restrict prim) { AkHeap *heap; AkGeometry *geom; AkMesh *mesh; AkBuffer *posbuff; char *data; AkAccessor *acc; float *vec; vec3 center, min, max; size_t i, count, byteStride; bool exactCenter; mesh = prim->mesh; geom = mesh->geom; posbuff = NULL; acc = NULL; if (!prim->pos || !(acc = prim->pos->accessor) || !(posbuff = acc->buffer)) return; data = ((char *)posbuff->data + acc->byteOffset); glm_vec3_broadcast(FLT_MAX, min); glm_vec3_broadcast(-FLT_MAX, max); glm_vec3_broadcast(0.0f, center); exactCenter = ak_opt_get(AK_OPT_COMPUTE_EXACT_CENTER); byteStride = acc->byteStride; if (byteStride == 0) byteStride = acc->fillByteSize; /* we must walk through indices if exists because source may contain unrelated data and this will cause get wrong box */ if (prim->indices) { AkUInt *ind; size_t icount; uint32_t st, vo; icount = prim->indices->count; vo = prim->pos->offset; st = prim->indexStride; ind = prim->indices->items; count = icount; if (!exactCenter) { for (i = 0; i < icount; i += st) { vec = (float *)(data + ind[i + vo] * byteStride); glm_vec3_add(vec, center, center); ak_bbox_pick(min, max, vec); } } else { for (i = 0; i < icount; i += st) { vec = (float *)(data + ind[i + vo] * byteStride); ak_bbox_pick(min, max, vec); } } } else { count = acc->count; if (!exactCenter) { for (i = 0; i < count; i++) { vec = (float *)(data + byteStride * i); ak_bbox_pick(min, max, vec); } } else { for (i = 0; i < count; i++) { vec = (float *)(data + byteStride * i); glm_vec3_add(vec, center, center); ak_bbox_pick(min, max, vec); } } } heap = ak_heap_getheap(prim); if (!prim->bbox) { prim->bbox = ak_heap_calloc(heap, prim, sizeof(*prim->bbox)); ak_bbox_invalidate(prim->bbox); } if (!mesh->bbox) { mesh->bbox = ak_heap_calloc(heap, prim, sizeof(*prim->bbox)); ak_bbox_invalidate(mesh->bbox); } if (!geom->bbox) { geom->bbox = ak_heap_calloc(heap, prim, sizeof(*prim->bbox)); ak_bbox_invalidate(geom->bbox); } glm_vec3_copy(min, prim->bbox->min); glm_vec3_copy(max, prim->bbox->max); ak_bbox_pick_pbox(mesh->bbox, prim->bbox); ak_bbox_pick_pbox(geom->bbox, mesh->bbox); /* compute centroid */ if (!ak_opt_get(AK_OPT_COMPUTE_EXACT_CENTER)) { glm_vec3_center(prim->bbox->min, prim->bbox->max, prim->center); } else if (count > 0) { /* calculate exact center of primitive */ glm_vec3_divs(center, (float)count, center); } else { glm_vec3_zero(prim->center); } } ================================================ FILE: src/bbox/scene.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "bbox.h" #include #include static void ak_bbox_node(AkHeap * __restrict heap, AkVisualScene * __restrict scene, AkNode * __restrict node, mat4 parentTrans); static void ak_bbox_node(AkHeap * __restrict heap, AkVisualScene * __restrict scene, AkNode * __restrict node, mat4 parentTrans) { AkFloat (*matrixWorld)[4]; if (!node->matrix) node->matrix = ak_heap_alloc(heap, node, sizeof(*node->matrix)); if (!node->matrixWorld) node->matrixWorld = ak_heap_alloc(heap, node, sizeof(*node->matrixWorld)); matrixWorld = node->matrixWorld->val; ak_transformCombine(node->transform, node->matrix->val[0]); glm_mat4_mul(parentTrans, node->matrix->val, matrixWorld); if (node->geometry) { AkInstanceBase *geomInst; AkGeometry *geom; AkBoundingBox bbox; vec4 min, max; glm_vec3_broadcast(FLT_MAX, bbox.min); glm_vec3_broadcast(-FLT_MAX, bbox.max); min[3] = max[3] = 1.0f; /* find bbox for node to avoid extra calc */ geomInst = &node->geometry->base; while (geomInst) { geom = ak_instanceObject(geomInst); if (geom && geom->bbox) ak_bbox_pick_pbox(&bbox, geom->bbox); geomInst = geomInst->next; } glm_vec3_copy(bbox.min, min); glm_vec3_copy(bbox.max, max); glm_mat4_mulv(matrixWorld, min, min); glm_mat4_mulv(matrixWorld, max, max); if (scene->bbox->isvalid) { ak_bbox_pick_pbox2(scene->bbox, min, max); } else { glm_vec3_copy(min, scene->bbox->min); glm_vec3_copy(max, scene->bbox->max); scene->bbox->isvalid = true; } } if (node->node) { AkNode *nodei; if ((nodei = ak_instanceObjectNode(node))) { do { ak_bbox_node(heap, scene, nodei, matrixWorld); nodei = nodei->next; } while (nodei); } } if (node->chld) { AkNode *nodei; nodei = node->chld; do { ak_bbox_node(heap, scene, nodei, matrixWorld); nodei = nodei->next; } while (nodei); } } void ak_bbox_scene(struct AkVisualScene * __restrict scene) { mat4 trans = GLM_MAT4_IDENTITY_INIT; AkHeap *heap; AkNode *node; node = scene->node; heap = ak_heap_getheap(scene); if (!scene->bbox) { scene->bbox = ak_heap_calloc(heap, scene, sizeof(*scene->bbox)); ak_bbox_invalidate(scene->bbox); } while (node) { ak_bbox_node(heap, scene, node, trans); node = node->next; } } ================================================ FILE: src/bitwise/CMakeLists.txt ================================================ target_sources(${PROJECT_NAME} PRIVATE bitwise.h ) ================================================ FILE: src/bitwise/bitwise.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_bitwise_h #define ak_bitwise_h #include "../common.h" #include AK_INLINE uint32_t ak_bitw_ctz(uint32_t x) { #if __has_builtin(__builtin_ctz) return __builtin_ctz(x); #else uint32_t i; i = 0; while ((x >>= 1) > 0) i++; return i; #endif } AK_INLINE uint32_t ak_bitw_ffs(uint32_t x) { #if __has_builtin(__builtin_ffs) return __builtin_ffs(x); #else return ak_bitw_ctz(x) + 1; #endif } AK_INLINE uint32_t ak_bitw_clz(uint32_t x) { #if __has_builtin(__builtin_clz) return __builtin_clz(x); #else return sizeof(uint32_t) * CHAR_BIT - ak_bitw_ffs(x); #endif } #endif /* ak_bitwise_h */ ================================================ FILE: src/camera/CMakeLists.txt ================================================ target_sources(${PROJECT_NAME} PRIVATE cam.c ) ================================================ FILE: src/camera/cam.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "../default/cam.h" #include AK_EXPORT AkResult ak_firstCamera(AkDoc * __restrict doc, AkCamera ** camera, float * matrix, float * projMatrix) { AkHeap *heap; AkVisualScene *scene; AkNode *camNode; AkCamera *cam; if (!doc->scene.visualScene) goto efound; scene = ak_instanceObject(doc->scene.visualScene); if (!scene->firstCamNode) goto efound; heap = ak_heap_getheap(doc); camNode = scene->firstCamNode; /* view matrix */ if (matrix) ak_transformCombineWorld(camNode, matrix); if (camera || projMatrix) { cam = ak_instanceObject(camNode->camera); if (!cam) { if (ak_opt_get(AK_OPT_ADD_DEFAULT_CAMERA)) { AkInstanceBase *cameraInst; cam = (AkCamera *)ak_defaultCamera(camNode); cameraInst = ak_instanceMake(heap, camNode, cam); ak_instanceListEmpty(scene->cameras); ak_instanceListAdd(scene->cameras, cameraInst); cameraInst->next = camNode->camera; camNode->camera = cameraInst; ak_libAddCamera(doc, cam); } else { goto efound; } } if (camera) *camera = cam; if (projMatrix) { switch ((int)cam->optics->tcommon->type) { case AK_PROJECTION_PERSPECTIVE: { AkPerspective *perspective; perspective = (AkPerspective *)cam->optics->tcommon; glm_perspective(perspective->yfov, perspective->aspectRatio, perspective->znear, perspective->zfar, (vec4 *)projMatrix); break; } case AK_PROJECTION_ORTHOGRAPHIC: { AkOrthographic *ortho; ortho = (AkOrthographic *)cam->optics->tcommon; glm_ortho(-ortho->xmag, ortho->xmag, -ortho->ymag, ortho->ymag, ortho->znear, ortho->zfar, (vec4 *)projMatrix); break; } } } } return AK_OK; efound: if (camera) *camera = NULL; return AK_EFOUND; } AK_EXPORT const AkCamera* ak_defaultCamera(void * __restrict memparent) { AkHeap *heap; AkCamera *cam; const AkCamera *defcam; defcam = ak_def_camera(); if (memparent) heap = ak_heap_getheap(memparent); else heap = ak_heap_default(); cam = ak_heap_calloc(heap, memparent, sizeof(*cam)); memcpy(cam, defcam, sizeof(*defcam)); cam->optics = ak_heap_calloc(heap, cam, sizeof(*cam->optics)); cam->optics->tcommon = ak_heap_calloc(heap, cam, sizeof(AkPerspective)); memcpy(cam->optics->tcommon, defcam->optics->tcommon, sizeof(AkPerspective)); return cam; } AK_EXPORT AkCamera * ak_camMakePerspective(AkDoc * __restrict doc, void * __restrict memparent, float yfov, float aspect, float znear, float zfar) { AkHeap *heap; AkCamera *cam; AkPerspective *persp; if (!doc) return NULL; heap = ak_heap_getheap(doc); cam = ak_heap_calloc(heap, memparent ? memparent : (void *)doc, sizeof(*cam)); cam->optics = ak_heap_calloc(heap, cam, sizeof(*cam->optics)); /* Concrete projection lives in the camera's heap region so it's freed with the camera. */ persp = ak_heap_calloc(heap, cam, sizeof(*persp)); persp->base.type = AK_PROJECTION_PERSPECTIVE; persp->yfov = yfov; persp->aspectRatio = aspect; persp->znear = znear; persp->zfar = zfar; cam->optics->tcommon = (AkProjection *)persp; ak_libAddCamera(doc, cam); return cam; } AK_EXPORT AkCamera * ak_camMakeOrthographic(AkDoc * __restrict doc, void * __restrict memparent, float xmag, float ymag, float aspect, float znear, float zfar) { AkHeap *heap; AkCamera *cam; AkOrthographic *ortho; if (!doc) return NULL; heap = ak_heap_getheap(doc); cam = ak_heap_calloc(heap, memparent ? memparent : (void *)doc, sizeof(*cam)); cam->optics = ak_heap_calloc(heap, cam, sizeof(*cam->optics)); ortho = ak_heap_calloc(heap, cam, sizeof(*ortho)); ortho->base.type = AK_PROJECTION_ORTHOGRAPHIC; ortho->xmag = xmag; ortho->ymag = ymag; ortho->aspectRatio = aspect; ortho->znear = znear; ortho->zfar = zfar; cam->optics->tcommon = (AkProjection *)ortho; ak_libAddCamera(doc, cam); return cam; } ================================================ FILE: src/common.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "common.h" #include AK_HIDE int ak_enumpair_cmp(const void * a, const void * b) { return strcmp(((const ak_enumpair *)a)->key, ((const ak_enumpair *)b)->key); } AK_HIDE int ak_enumpair_cmp2(const void * a, const void * b) { return strcmp(a, ((const ak_enumpair *)b)->key); } AK_HIDE int ak_enumpair_json_val_cmp(const void * a, const void * b) { const char *s; if (!(s = json_string(a))) return -1; return strncmp(s, ((const ak_enumpair *)b)->key, ((json_t *)a)->valsize); } ================================================ FILE: src/common.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_src_common_h #define ak_src_common_h #define AK_INTERN #include "../include/ak/assetkit.h" #include "../include/ak/options.h" #include "../include/ak/map.h" #include "../include/ak/type.h" #include "../include/ak/url.h" #include "../include/ak/transform.h" #include "mem/common.h" #ifndef CGLM_ALL_UNALIGNED # define CGLM_ALL_UNALIGNED #endif #include #include #include #include #include #include #include #include #include #include #ifdef _MSC_VER # define strncasecmp _strnicmp # define strcasecmp _stricmp # define strtok_r strtok_s # define mktemp _mktemp # define ASM __asm # else # define ASM __asm__ #endif #ifdef __GNUC__ # define AK_DESTRUCTOR __attribute__((destructor)) # define AK_CONSTRUCTOR __attribute__((constructor)) #else # define AK_DESTRUCTOR # define AK_CONSTRUCTOR #endif #define AK__UNUSED(X) (void)X #define I2P (void *)(intptr_t) /*! * @brief get sign of 32 bit integer as +1 or -1 * * @param X integer value */ #define AK_GET_SIGN(X) ((X >> 31) - (-X >> 31)) #define AK_ARRAY_SEP_CHECK (c == ' ' || c == '\n' || c == '\t' \ || c == '\r' || c == '\f' || c == '\v') #define AK_ARRAY_SEPLINE_CHECK (c == ' ' || c == '\t' || c == '\f' || c == '\v') #define AK_ARRAY_SPACE_CHECK (c == ' ' || c == '\t' || c == '\f' || c == '\v') #define AK_ARRAY_NLINE_CHECK (c == '\n' || c == '\r') typedef struct ak_enumpair { const char *key; AkEnum val; } ak_enumpair; typedef struct { const char * name; AkEnum val; } dae_enum; AK_HIDE int ak_enumpair_cmp(const void * a, const void * b); AK_HIDE int ak_enumpair_cmp2(const void * a, const void * b); AK_HIDE int ak_enumpair_json_val_cmp(const void * a, const void * b); AK_EXPORT int ak_cmp_str(void * key1, void *key2); AK_EXPORT int ak_cmp_ptr(void *key1, void *key2); AK_EXPORT int ak_cmp_i32(void *key1, void *key2); AK_EXPORT int ak_cmp_vec3(void * key1, void *key2); AK_EXPORT int ak_cmp_ivec3(void *key1, void *key2); AK_EXPORT int ak_cmp_vec4(void *key1, void *key2); typedef int (*AkCmpFn)(void * key1, void *key2); #endif /* ak_src_common_h */ ================================================ FILE: src/coord/CMakeLists.txt ================================================ target_sources(${PROJECT_NAME} PRIVATE camera.c common.h common.c doc.c geom.c mesh.c node.c scene.c transform.c transforms.c vector.c ) ================================================ FILE: src/coord/camera.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "common.h" #include AK_HIDE void ak_coordRotForFixedCoord(AkCoordSys *oldCoordSys, AkCoordSys *newCoordSys, AkAxisOrientation *oldAxes, AkAxisOrientation *newAxes, vec4 fwdAxis, vec4 upAxis); AK_HIDE void ak_coordRotForFixedCoord(AkCoordSys *oldCoordSys, AkCoordSys *newCoordSys, AkAxisOrientation *oldAxes, AkAxisOrientation *newAxes, vec4 fwdAxis, vec4 upAxis) { ivec3 camOriOld, camOriNew; vec3 v1, v2, v3, tmp; AkAxisAccessor a0, a1; ak_coordAxisCamAccessors(newCoordSys, &a0, &a1); ak_coordAxisOri(oldCoordSys, *oldAxes, camOriOld); ak_coordAxisOri(newCoordSys, *newAxes, camOriNew); glm_vec3_broadcast(0.0f, v1); glm_vec3_broadcast(0.0f, v2); /* we want to rotate from new to old!!! */ v1[abs(camOriNew[2]) - 1] = (float)glm_sign(camOriNew[2]); v2[abs(camOriOld[2]) - 1] = (float)glm_sign(camOriOld[2]); glm_vec3_cross(v1, v2, v3); /* angle for forward axis */ fwdAxis[3] = glm_vec3_angle(v1, v2); if (fwdAxis[3] != 0.0f) { /* forward axis */ glm_vec3_copy(v3, fwdAxis); /* convert to current coord sys */ AK_CVT_VEC(fwdAxis) } /* up axis */ glm_vec3_broadcast(0.0f, v1); glm_vec3_broadcast(0.0f, v2); v1[abs(camOriNew[1]) - 1] = (float)glm_sign(camOriNew[1]); v2[abs(camOriOld[1]) - 1] = (float)glm_sign(camOriOld[1]); /* rotate with fwd to find new up (rotated) */ glm_vec3_rotate(v1, fwdAxis[3], v3); glm_vec3_cross(v1, v2, v3); /* angle for up axis */ upAxis[3] = glm_vec3_angle(v1, v2); /* up direction */ if (upAxis[3] != 0.0f) { /* up axis */ glm_vec3_copy(v3, upAxis); /* convert to current coord sys */ AK_CVT_VEC(upAxis) /* rotate found axis with fwd */ if (fwdAxis[3] != 0.0f) glm_vec3_rotate(upAxis, -fwdAxis[3], fwdAxis); } } AK_EXPORT void ak_coordFixCamOri(AkCoordSys *oldCoordSys, AkCoordSys *newCoordSys, AkFloat4x4 transform) { vec4 fwdAxis, upAxis; if (ak_coordOrientationIsEq(oldCoordSys, newCoordSys)) return; memset(fwdAxis, 0, sizeof(fwdAxis)); memset(upAxis, 0, sizeof(upAxis)); ak_coordRotForFixedCoord(oldCoordSys, newCoordSys, &oldCoordSys->cameraOrientation, &newCoordSys->cameraOrientation, fwdAxis, upAxis); /* apply rotation for forward direction */ if (fwdAxis[3] != 0.0f) glm_rotate(transform, fwdAxis[3], fwdAxis); /* apply rotation for up direction */ if (upAxis[3] != 0.0f) glm_rotate(transform, upAxis[3], upAxis); } AK_EXPORT void ak_coordRotNodeForFixedCoord(AkDoc *doc, void *memparent, AkObject **destTransform) { AkHeap *heap; AkCoordSys *oldCoordSys, *newCoordSys; AkObject *transformFwd, *transformUp; AkRotate *rotate; vec4 fwdAxis, upAxis; oldCoordSys = doc->coordSys; newCoordSys = (void *)ak_opt_get(AK_OPT_COORD); *destTransform = NULL; if (ak_coordOrientationIsEq(oldCoordSys, newCoordSys)) return; memset(fwdAxis, 0, sizeof(fwdAxis)); memset(upAxis, 0, sizeof(upAxis)); ak_coordRotForFixedCoord(oldCoordSys, newCoordSys, &oldCoordSys->cameraOrientation, &newCoordSys->cameraOrientation, fwdAxis, upAxis); heap = ak_heap_getheap(doc); /* we assume that we only need two rotation for fwd and up (not right) */ /* rotation for forward direction */ if (fwdAxis[3] != 0.0f) { transformFwd = ak_objAlloc(heap, memparent, sizeof(*rotate), AKT_ROTATE, true); rotate = ak_objGet(transformFwd); glm_vec4_copy(fwdAxis, rotate->val); *destTransform = transformFwd; } /* rotation for up direction */ if (upAxis[3] != 0.0f) { transformUp = ak_objAlloc(heap, memparent, sizeof(*rotate), AKT_ROTATE, true); rotate = ak_objGet(transformUp); glm_vec4_copy(upAxis, rotate->val); if (*destTransform) (*destTransform)->next = transformUp; else *destTransform = transformUp; } } ================================================ FILE: src/coord/common.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "common.h" #include void ak_coordAxisOri(AkCoordSys * __restrict coordSys, AkAxisOrientation axis, int ori[3]) { int axisOri[3]; int coord[3]; int i, j; ak_coordAxisToiVec3(axis, axisOri); ak_coordToiVec3(coordSys, coord); for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) if (axisOri[i] == coord[j]) ori[i] = (j + 1) * glm_sign(axisOri[j]); else if (abs(axisOri[i]) == abs(coord[j])) ori[i] = -(j + 1) * glm_sign(axisOri[j]); } } void ak_coordAxisOriAbs(AkCoordSys * __restrict coordSys, AkAxisOrientation axis, int newAxisOri[3]) { ak_coordAxisOri(coordSys, axis, newAxisOri); newAxisOri[0] = abs(newAxisOri[0]) - 1; newAxisOri[1] = abs(newAxisOri[1]) - 1; newAxisOri[2] = abs(newAxisOri[2]) - 1; } ================================================ FILE: src/coord/common.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_coord_common_h #define ak_coord_common_h #include "../common.h" #define AK__Z_RH {AK_AXIS_POSITIVE_X, AK_AXIS_POSITIVE_Z, AK_AXIS_POSITIVE_Y} #define AK__Y_RH {AK_AXIS_POSITIVE_X, AK_AXIS_POSITIVE_Y, AK_AXIS_NEGATIVE_Z} #define AK__X_RH {AK_AXIS_NEGATIVE_Y, AK_AXIS_POSITIVE_X, AK_AXIS_NEGATIVE_Z} #define AK__Z_LH {AK_AXIS_POSITIVE_X, AK_AXIS_POSITIVE_Z, AK_AXIS_NEGATIVE_Y} #define AK__Y_LH {AK_AXIS_POSITIVE_X, AK_AXIS_POSITIVE_Y, AK_AXIS_POSITIVE_Z} #define AK__X_LH {AK_AXIS_NEGATIVE_Y, AK_AXIS_POSITIVE_X, AK_AXIS_POSITIVE_Z} #define AK_COORD(P0, P1, P2) &(AkCoordSys){P0, P1, P2} #define AK_CVT_VEC_TO(X0, X1) \ X1[a1.up] = X0[a0.up] * a0.s_up * a1.s_up; \ X1[a1.right] = X0[a0.right] * a0.s_right * a1.s_right; \ X1[a1.fwd] = X0[a0.fwd] * a0.s_fwd * a1.s_fwd; #define AK_CVT_VEC_NOSIGN_TO(X0, X1) \ X1[a1.up] = X0[a0.up]; \ X1[a1.right] = X0[a0.right]; \ X1[a1.fwd] = X0[a0.fwd]; #define AK_CVT_VEC(X) \ AK_CVT_VEC_TO(X, tmp) \ X[0] = tmp[0]; \ X[1] = tmp[1]; \ X[2] = tmp[2]; #define AK_CVT_VEC_NOSIGN(X) \ AK_CVT_VEC_NOSIGN_TO(X, tmp) \ X[0] = tmp[0]; \ X[1] = tmp[1]; \ X[2] = tmp[2]; AK_INLINE void ak_coordAxisAccessors(AkCoordSys * __restrict oldCoordSys, AkCoordSys * __restrict newCoordSys, AkAxisAccessor * __restrict a0, AkAxisAccessor * __restrict a1) { AkAxisOrientation oldAxis, newAxis; oldAxis = oldCoordSys->axis; newAxis = newCoordSys->axis; a0->s_up = AK_GET_SIGN(oldAxis.up); a0->s_right = AK_GET_SIGN(oldAxis.right); a0->s_fwd = AK_GET_SIGN(oldAxis.fwd); a0->up = abs(oldAxis.up) - 1; a0->right = abs(oldAxis.right) - 1; a0->fwd = abs(oldAxis.fwd) - 1; a1->s_up = AK_GET_SIGN(newAxis.up); a1->s_right = AK_GET_SIGN(newAxis.right); a1->s_fwd = AK_GET_SIGN(newAxis.fwd); a1->up = abs(newAxis.up) - 1; a1->right = abs(newAxis.right) - 1; a1->fwd = abs(newAxis.fwd) - 1; } AK_INLINE void ak_coordAxisCamAccessors(AkCoordSys * __restrict newCoordSys, AkAxisAccessor * __restrict a0, AkAxisAccessor * __restrict a1) { AkAxisOrientation oldAxis, newAxis; oldAxis = newCoordSys->cameraOrientation; newAxis = newCoordSys->axis; a0->s_up = AK_GET_SIGN(oldAxis.up); a0->s_right = AK_GET_SIGN(oldAxis.right); a0->s_fwd = AK_GET_SIGN(oldAxis.fwd); a0->up = abs(oldAxis.up) - 1; a0->right = abs(oldAxis.right) - 1; a0->fwd = abs(oldAxis.fwd) - 1; a1->s_up = AK_GET_SIGN(newAxis.up); a1->s_right = AK_GET_SIGN(newAxis.right); a1->s_fwd = AK_GET_SIGN(newAxis.fwd); a1->up = abs(newAxis.up) - 1; a1->right = abs(newAxis.right) - 1; a1->fwd = abs(newAxis.fwd) - 1; } void ak_coordAxisOri(AkCoordSys * __restrict coordSys, AkAxisOrientation axis, int newAxisOri[3]); void ak_coordAxisOriLocate(int coord1[3], int coord2[3], int ori[3]); void ak_coordAxisOriAbs(AkCoordSys * __restrict coordSys, AkAxisOrientation axis, int newAxisOri[3]); #endif /* ak_coord_common_h */ ================================================ FILE: src/coord/doc.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" AK_EXPORT void ak_changeCoordSys(AkDoc * __restrict doc, AkCoordSys * newCoordSys) { AkLibrary *libGeom; AkGeometry *geom; libGeom = doc->lib.geometries; while (libGeom) { geom = (void *)libGeom->chld; while (geom) { ak_changeCoordSysGeom(geom, newCoordSys); geom = (void *)geom->base.next; } libGeom = libGeom->next; } doc->coordSys = newCoordSys; } ================================================ FILE: src/coord/geom.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" AK_EXPORT void ak_changeCoordSysGeom(AkGeometry * __restrict geom, AkCoordSys * newCoordSys) { AkObject *primitive; primitive = geom->gdata; switch ((AkGeometryType)primitive->type) { case AK_GEOMETRY_MESH: ak_changeCoordSysMesh(ak_objGet(primitive), newCoordSys); break; case AK_GEOMETRY_SPLINE: /* TODO: */ case AK_GEOMETRY_BREP: /* TODO: */ break; } } ================================================ FILE: src/coord/mesh.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" /* TODO: use mutex */ AK_EXPORT void ak_changeCoordSysMesh(AkMesh * __restrict mesh, AkCoordSys * newCoordSys) { AkMeshPrimitive *primi; AkDoc *doc; AkHeap *heap; AkInput *input; AkMap *map; AkMapItem *mapi; heap = ak_heap_getheap(mesh->geom); doc = heap->data; map = ak_map_new(NULL); /* find sources to update */ if (!(primi = mesh->primitive)) return; while (primi) { input = primi->input; while (input) { /* TODO: other semantics which are depend on coord sys */ if (input->semantic == AK_INPUT_POSITION || input->semantic == AK_INPUT_NORMAL) { if (!input->accessor) { input = input->next; continue; } ak_map_addptr(map, input->accessor); } input = input->next; } primi = primi->next; } mapi = map->root; while (mapi) { AkAccessor *acci; AkBuffer *buffi; acci = ak_getId(mapi); buffi = acci->buffer; if (!buffi) { mapi = mapi->next; continue; } /* TODO: INT, DOUBLE.. */ if (acci->componentType == AKT_FLOAT) { ak_coordCvtVectors(doc->coordSys, buffi->data, buffi->length / acci->bytesPerComponent, newCoordSys); } mapi = mapi->next; } ak_map_destroy(map); } ================================================ FILE: src/coord/node.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "../../include/ak/coord-util.h" #include "common.h" #include AK_HIDE bool ak_fixInstanceTransform(AkNode *node, AkInstanceBase *inst, AkCoordSys *instCoordSys, AkCoordSys *coordSys); AK_HIDE bool ak_fixInstanceTransform(AkNode *node, AkInstanceBase *inst, AkCoordSys *instCoordSys, AkCoordSys *coordSys) { AkHeap *heap; AkNode *subNode; heap = ak_heap_getheap(inst); subNode = ak_instanceMoveToSubNodeIfNeeded(node, inst); if (!subNode) return false; if (!subNode->transform) subNode->transform = ak_heap_calloc(heap, subNode, sizeof(*subNode->transform)); /* fixup transform[s] */ ak_coordFindTransform(subNode->transform, instCoordSys, coordSys); return true; } AK_EXPORT void ak_fixNodeCoordSys(AkNode * __restrict node) { AkHeap *heap; void *parentObject; AkCoordSys *coordSys, *parentCoordSys, *instCoordSys, *coordSysToFix; AkInstanceBase *inst; AkInstanceBase *instArray[] = {(AkInstanceBase *)node->geometry, (AkInstanceBase *)node->node, node->camera, node->light}; int i, instArrayLen, instTransCount; bool hasCoordSys; hasCoordSys = ak_hasCoordSys(node); coordSys = ak_getCoordSys(node); instArrayLen = AK_ARRAY_LEN(instArray); parentCoordSys = NULL; coordSysToFix = NULL; instTransCount = 0; /* fix instance transforms */ for (i = 0; i < instArrayLen; i++) { inst = instArray[i]; while (inst) { void *instObj; instObj = ak_instanceObject(inst); if (instObj && ak_hasCoordSys(instObj)) { instCoordSys = ak_getCoordSys(instObj); if (instCoordSys != coordSys) { bool movedToSub; movedToSub = ak_fixInstanceTransform(node, inst, instCoordSys, coordSys); if (!movedToSub) { coordSysToFix = instCoordSys; instTransCount++; } } } inst = inst->next; } } heap = ak_heap_getheap(node); switch (ak_opt_get(AK_OPT_COORD_CONVERT_TYPE)) { case AK_COORD_CVT_FIX_TRANSFORM: { if (instTransCount == 1 && coordSysToFix) coordSys = coordSysToFix; parentObject = node->parent; if (!parentObject) parentObject = ak_mem_parent(node); if (parentObject) parentCoordSys = ak_getCoordSys(parentObject); else parentCoordSys = AK_YUP; if (parentCoordSys == coordSys) return; if (hasCoordSys /* don't change nodes in library_nodes */ || (!node->parent && ak_typeid(parentObject) == AKT_SCENE)) { if (!node->transform) node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform)); /* fixup transform[s] */ ak_coordFindTransform(node->transform, coordSys, parentCoordSys); } break; } case AK_COORD_CVT_ALL: if (node->transform) ak_coordCvtNodeTransforms(ak_heap_data(heap), node); break; default: break; } } ================================================ FILE: src/coord/scene.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "../../include/ak/coord-util.h" #include "common.h" #include AK_EXPORT void ak_fixSceneCoordSys(struct AkVisualScene * __restrict scene) { AkHeap *heap; AkCoordSys *newCoordSys; AkNode *node; heap = ak_heap_getheap(scene); newCoordSys = (void *)ak_opt_get(AK_OPT_COORD); node = scene->node; while (node) { if (!node->transform) node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform)); /* fixup transform[s] */ ak_coordFindTransform(node->transform, ak_getCoordSys(node), newCoordSys); node = node->next; } } ================================================ FILE: src/coord/transform.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "common.h" #include #include AK_EXPORT void ak_coordCvtTransform(AkCoordSys *oldCoordSystem, AkFloat4x4 transform, AkCoordSys *newCoordSystem) { mat4 rot, scale; vec4 pos; vec3 scalev, angles, tmp; AkAxisAccessor a0, a1; ivec3 eulerNew; AkAxisRotDirection rotDirection; rotDirection = (oldCoordSystem->rotDirection + 1) * (newCoordSystem->rotDirection + 1); ak_coordAxisAccessors(oldCoordSystem, newCoordSystem, &a0, &a1); /* decompose rotation and scaling factors */ glm_decompose_rs(transform, rot, scalev); /* extract euler angles XYZ */ glm_euler_angles(rot, angles); AK_CVT_VEC(angles); /* find new euler sequence */ ak_coordAxisOriAbs(newCoordSystem, oldCoordSystem->axis, eulerNew); /* apply new rotation direction */ glm_vec3_scale(angles, rotDirection, angles); /* apply new rotation */ glm_euler_by_order(angles, glm_euler_order(eulerNew), rot); /* find new scaling factors */ AK_CVT_VEC_NOSIGN(scalev); /* apply new scaling factors */ glm_mat4_copy(GLM_MAT4_IDENTITY, scale); scale[0][0] = scalev[0]; scale[1][1] = scalev[1]; scale[2][2] = scalev[2]; glm_vec4_copy(transform[3], pos); glm_mul(rot, scale, transform); /* apply new translation */ AK_CVT_VEC(pos) glm_vec4_copy(pos, transform[3]); } AK_EXPORT void ak_coordFindTransform(AkTransform *transform, AkCoordSys *oldCoordSys, AkCoordSys *newCoordSys) { AkHeap *heap; AkObject *firstTrans, *lastTrans; AkAxisAccessor a0, a1; ivec3 oriOld, oriNew; vec3 x1, y1, z1, oth, axis; float angle; if (oldCoordSys == newCoordSys || ak_coordOrientationIsEq(oldCoordSys, newCoordSys)) return; firstTrans = lastTrans = NULL; heap = ak_heap_getheap(transform); if (!transform->base) ak_transformFreeBase(transform); ak_coordAxisAccessors(oldCoordSys, newCoordSys, &a0, &a1); ak_coordToiVec3(oldCoordSys, oriOld); ak_coordToiVec3(newCoordSys, oriNew); glm_vec3_broadcast(0.0f, x1); glm_vec3_broadcast(0.0f, y1); glm_vec3_broadcast(0.0f, z1); x1[abs(oriOld[0]) - 1] = (float)glm_sign(oriOld[0]); y1[abs(oriOld[1]) - 1] = (float)glm_sign(oriOld[1]); z1[abs(oriOld[2]) - 1] = (float)glm_sign(oriOld[2]); /* step-1: X axis; a rotation will be enough */ /* find rotation axis */ glm_vec3_broadcast(0.0f, oth); oth[abs(oriNew[0]) - 1] = (float)glm_sign(oriNew[0]); glm_vec3_cross(x1, oth, axis); /* angle for x axis */ angle = glm_vec3_angle(x1, oth); /* append transform */ if (angle != 0.0f) { AkObject *rotObj; AkRotate *rot; rotObj = ak_objAlloc(heap, transform, sizeof(*rotObj), AKT_ROTATE, true); rot = ak_objGet(rotObj); glm_vec3_copy(axis, rot->val); rot->val[3] = angle; /* rotate y and z */ glm_vec3_rotate(y1, angle, axis); glm_vec3_rotate(z1, angle, axis); firstTrans = lastTrans = rotObj; } /* step-2: Y axis; an extra rotation around X will be enough */ /* find rotation axis */ glm_vec3_broadcast(0.0f, oth); oth[abs(oriNew[1]) - 1] = (float)glm_sign(oriNew[1]); glm_vec3_cross(y1, oth, axis); /* angle for y axis */ angle = glm_vec3_angle(y1, oth); /* append transform */ if (angle != 0.0f) { AkObject *rotObj; AkRotate *rot; rotObj = ak_objAlloc(heap, transform, sizeof(*rotObj), AKT_ROTATE, true); rot = ak_objGet(rotObj); glm_vec3_copy(axis, rot->val); rot->val[3] = angle; /* rotate z, we know that x will keep it orientation */ glm_vec3_rotate(z1, angle, axis); if (lastTrans) lastTrans->next = rotObj; else firstTrans = rotObj; lastTrans = rotObj; } /* step-3: Z axis; we can't add rotation, we need negative scale */ /* check old and new Z axes are same */ glm_vec3_broadcast(0.0f, oth); oth[abs(oriNew[2]) - 1] = (float)glm_sign(oriNew[2]); if (!glm_vec3_eqv_eps(z1, oth)) { /* fix Z by negative scale */ AkObject *scaleObj; AkScale *scale; scaleObj = ak_objAlloc(heap, transform, sizeof(*scaleObj), AKT_SCALE, true); scale = ak_objGet(scaleObj); scale->val[0] = 1.0f; scale->val[1] = 1.0f; scale->val[2] = -1.0f; if (lastTrans) lastTrans->next = scaleObj; else firstTrans = scaleObj; } transform->base = firstTrans; } ================================================ FILE: src/coord/transforms.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "common.h" #include AK_EXPORT void ak_coordCvtNodeTransforms(AkDoc * __restrict doc, AkNode * __restrict node) { AkCoordSys *oldCoordSys, *newCoordsys; AkObject *transform, *lastTransform; vec3 tmp; AkAxisAccessor a0, a1; if (!node->transform) return; oldCoordSys = doc->coordSys; newCoordsys = (void *)ak_opt_get(AK_OPT_COORD); ak_coordAxisAccessors(oldCoordSys, newCoordsys, &a0, &a1); transform = lastTransform = node->transform->item; while (transform) { switch (transform->type) { case AKT_MATRIX: { AkMatrix *matrix; matrix = ak_objGet(transform); ak_coordCvtTransform(oldCoordSys, matrix->val, newCoordsys); break; } case AKT_LOOKAT: { AkLookAt *lookAt; lookAt = ak_objGet(transform); /* convert eye vector */ AK_CVT_VEC(lookAt->val[0]); /* convert center vector */ AK_CVT_VEC(lookAt->val[1]); /* convert up vector */ AK_CVT_VEC(lookAt->val[2]); break; } case AKT_ROTATE: { AkRotate *rotate; rotate = ak_objGet(transform); AK_CVT_VEC(rotate->val); break; } case AKT_QUATERNION: { AkQuaternion *quat; float *val; quat = ak_objGet(transform); val = quat->val; AK_CVT_VEC(val); break; } case AKT_SCALE: { AkScale *scale; scale = ak_objGet(transform); AK_CVT_VEC_NOSIGN(scale->val); break; } case AKT_TRANSLATE: { AkTranslate *translate; translate = ak_objGet(transform); AK_CVT_VEC(translate->val); break; } case AKT_SKEW: { AkSkew *skew; skew = ak_objGet(transform); /* TODO: */ break; } } lastTransform = transform; transform = transform->next; } /* extra rotation for camera orientation */ if (node->flags & AK_NODEF_FIXED_COORD) { AkObject *extraTransformItem; ak_coordRotNodeForFixedCoord(doc, node, &extraTransformItem); if (extraTransformItem) { if (lastTransform) lastTransform->next = extraTransformItem; else { node->transform = ak_heap_calloc(ak_heap_getheap(extraTransformItem), node, sizeof(*node->transform)); node->transform->item = extraTransformItem; } } } } ================================================ FILE: src/coord/vector.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "common.h" AK_EXPORT void ak_coordCvtVectorTo(AkCoordSys *oldCoordSystem, float *oldVector, AkCoordSys *newCoordSystem, float *newVector) { AkAxisAccessor a0, a1; ak_coordAxisAccessors(oldCoordSystem, newCoordSystem, &a0, &a1); AK_CVT_VEC_TO(oldVector, newVector) } AK_EXPORT void ak_coordCvtVector(AkCoordSys *oldCoordSystem, float *vector, AkCoordSys *newCoordSystem) { float tmp[3]; ak_coordCvtVectorTo(oldCoordSystem, vector, newCoordSystem, tmp); vector[0] = tmp[0]; vector[1] = tmp[1]; vector[2] = tmp[2]; } AK_EXPORT void ak_coordCvtVectors(AkCoordSys *oldCoordSystem, float *vectorArray, size_t len, AkCoordSys *newCoordSystem) { AkAxisAccessor a0, a1; size_t i; float tmp[3]; ak_coordAxisAccessors(oldCoordSystem, newCoordSystem, &a0, &a1); for (i = 0; i < len; i += 3) { AK_CVT_VEC_TO((vectorArray + i), tmp) vectorArray[i] = tmp[0]; vectorArray[i + 1] = tmp[1]; vectorArray[i + 2] = tmp[2]; } } ================================================ FILE: src/data.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "data.h" #include AkDataContext* ak_data_new(void *memparent, size_t nodeitems, size_t itemsize, AkCmpFn cmp) { AkHeap *heap; AkDataContext *ctx; heap = ak_heap_getheap(memparent); ctx = ak_heap_calloc(heap, memparent, sizeof(*ctx)); ctx->nodesize = nodeitems * itemsize; ctx->itemsize = itemsize; ctx->heap = heap; ctx->cmp = cmp; return ctx; } int ak_data_append(AkDataContext *dctx, void *item) { AkDataChunk *chunk; void *mem; size_t size; size = dctx->itemsize; assert(dctx->nodesize > size); if (dctx->usedsize + size > dctx->size) { chunk = ak_heap_alloc(dctx->heap, dctx, sizeof(*chunk) + dctx->nodesize); chunk->usedsize = 0; chunk->next = NULL; if (dctx->last) dctx->last->next = chunk; dctx->last = chunk; dctx->size += dctx->nodesize; if (!dctx->data) dctx->data = chunk; } else { chunk = dctx->last; } mem = chunk->data + chunk->usedsize; memcpy(mem, item, size); chunk->usedsize += size; dctx->usedsize += size; dctx->itemcount++; return (int)dctx->itemcount - 1; } int ak_data_append_unq(AkDataContext *dctx, void *item) { int idx; idx = ak_data_exists(dctx, item); if (idx != -1) return idx; return ak_data_append(dctx, item); } void ak_data_walk(AkDataContext *dctx) { AkDataChunk *chunk; void *data; size_t isz, csz, i, index; if (!dctx->data) return; isz = dctx->itemsize; chunk = dctx->data; index = 0; while (chunk) { csz = dctx->nodesize - chunk->usedsize; for (i = isz; i < csz; i += isz) { data = chunk->data; dctx->walkFn(dctx, data, index++); } chunk = chunk->next; } } int ak_data_exists(AkDataContext *dctx, void *item) { AkDataChunk *chunk; char *pmem; size_t isz, csz, i; int idx; bool found; if (!dctx->data) return -1; found = false; idx = 0; isz = dctx->itemsize; chunk = dctx->data; while (chunk) { csz = chunk->usedsize; pmem = chunk->data; for (i = 0; i < csz; i += isz) { if (dctx->cmp(pmem, item) == 0) return idx; pmem += isz; idx++; } chunk = chunk->next; } if (!found) idx = -1; return idx; } size_t ak_data_join(AkDataContext *dctx, void *buff, size_t bytesoff, size_t stride) { AkDataChunk *chunk; char *pmem, *data; size_t isz, csz, i, count; count = 0; if (!dctx->data) return count; isz = dctx->itemsize; chunk = dctx->data; pmem = buff; if (bytesoff > 0) pmem += bytesoff; if (stride == 0) stride = isz; while (chunk) { csz = chunk->usedsize; data = chunk->data; for (i = 0; i < csz; i += isz) { memcpy(pmem, data, isz); pmem += stride; data += isz; count++; } chunk = chunk->next; } return count; } ================================================ FILE: src/data.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_data_h #define ak_data_h #include "common.h" struct AkDataContext; typedef void (*AkDataContextWalkFn)(struct AkDataContext *dctx, void *item, size_t index); typedef struct AkDataChunk { struct AkDataChunk *next; size_t usedsize; char data[]; } AkDataChunk; typedef struct AkDataContext { void *memparent; AkHeap *heap; AkDataChunk *data; AkDataChunk *last; AkCmpFn cmp; AkDataContextWalkFn walkFn; size_t nodesize; size_t size; size_t usedsize; size_t itemsize; size_t itemcount; } AkDataContext; AkDataContext* ak_data_new(void *memparent, size_t nodeitems, size_t itemsize, AkCmpFn cmp); /*! * @brief join batch data into continued array * * @param dctx data context * @param buff buffer * @param stride bytes stride * @param buff bytesgap gap between item stride * * @return array count */ size_t ak_data_join(AkDataContext *dctx, void *buff, size_t stride, size_t bytesgap); /*! * @brief append item, this will copy data by size of itemsize into context * * @param dctx data context * @param item item * * @return index */ int ak_data_append(AkDataContext *dctx, void *item); /*! * @brief append if not exists and return appended item index * * @param dctx data context * @param item item * * @return index */ int ak_data_append_unq(AkDataContext *dctx, void *item); /*! * @brief walk through by walkFn * * @param dctx data context */ void ak_data_walk(AkDataContext *dctx); /*! * @brief check if item is exists * * @param dctx data context * @param item item * * @return returns item index */ int ak_data_exists(AkDataContext *dctx, void *item); #endif /* ak_data_h */ ================================================ FILE: src/decoders/gltf/draco/assetkit_draco.cc ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #if defined(_WIN32) # define AK_DRACO_EXPORT __declspec(dllexport) #else # define AK_DRACO_EXPORT __attribute__((visibility("default"))) #endif typedef struct AkHeap AkHeap; typedef struct AkMeshPrimitive AkMeshPrimitive; typedef struct FListItem FListItem; typedef struct RBTree RBTree; typedef enum AkTypeId { AKT_NONE = 0, AKT_INT = 6, AKT_FLOAT = 10, AKT_BYTE = 29, AKT_UBYTE = 30, AKT_SHORT = 31, AKT_USHORT = 32, AKT_UINT = 28 } AkTypeId; typedef enum json_type { JSON_OBJECT = 1, JSON_ARRAY = 2, JSON_STRING = 3 } json_type_t; typedef struct json_t { struct json_t *parent; struct json_t *next; const char *key; void *value; int32_t valsize; int32_t keysize; json_type_t type; } json_t; typedef struct AkBuffer { const char *name; void *data; size_t length; } AkBuffer; typedef struct AkAccessor { AkBuffer *buffer; const char *name; void *min; void *max; size_t byteOffset; size_t byteStride; size_t byteLength; uint32_t count; uint32_t bytesPerComponent; int32_t componentSize; AkTypeId componentType; uint32_t componentCount; size_t fillByteSize; int32_t gpuTarget; bool normalized; AkTypeId originalComponentType; bool originallyNormalized; } AkAccessor; typedef struct AkLibrary { struct AkLibrary *next; const char *name; void *extra; void *chld; uint64_t count; } AkLibrary; typedef struct AkLibraries { AkLibrary *cameras; AkLibrary *lights; AkLibrary *effects; AkLibrary *libimages; AkLibrary *materials; AkLibrary *geometries; AkLibrary *controllers; AkLibrary *visualScenes; AkLibrary *nodes; AkLibrary *animations; FListItem *buffers; FListItem *accessors; FListItem *textures; FListItem *samplers; FListItem *images; void *morphs; void *skins; } AkLibraries; typedef struct AkDoc { void *inf; void *coordSys; void *unit; void *extra; void *reserved; void *userData; float loadMillis; AkLibraries lib; } AkDoc; typedef struct AkBufferView { AkBuffer *buffer; const char *name; size_t byteOffset; size_t byteLength; size_t byteStride; } AkBufferView; typedef struct AkGLTFState { AkHeap *heap; AkDoc *doc; json_t *root; void *tmpParent; FListItem *buffers; RBTree *bufferMap; FListItem *bufferViews; RBTree *skinBound; RBTree *meshTargets; void *bindata; void *defaultMaterial; void *meshopt; void *draco; size_t bindataLen; bool stop; bool isbinary; } AkGLTFState; extern "C" { void *ak_heap_alloc(AkHeap *heap, void *parent, size_t size); void *ak_heap_calloc(AkHeap *heap, void *parent, size_t size); void flist_sp_insert(FListItem **first, void *value); void *flist_sp_at(FListItem **first, int32_t index); } extern "C" { #include "io/gltf/strpool.h" } static int32_t ak_draco_json_int32(const json_t * __restrict obj, int32_t def) { char *end; long val; if (!obj || obj->type != JSON_STRING || !obj->value) return def; errno = 0; val = strtol((const char *)obj->value, &end, 10); if (errno != 0 || end == (const char *)obj->value) return def; return (int32_t)val; } static json_t* ak_draco_json_get(const json_t * __restrict object, const char * __restrict key) { json_t *it; size_t keysize; if (!object || object->type != JSON_OBJECT || !key) return NULL; keysize = strlen(key); it = (json_t *)object->value; while (it && ((size_t)it->keysize != keysize || strncmp(it->key, key, keysize) != 0)) it = it->next; return it; } #define json_get ak_draco_json_get #define json_int32 ak_draco_json_int32 static json_t* ak_draco_json_getn(const json_t * __restrict object, const char * __restrict key, size_t keysize) { json_t *it; if (!object || object->type != JSON_OBJECT || !key) return NULL; it = (json_t *)object->value; while (it && ((size_t)it->keysize != keysize || strncmp(it->key, key, keysize) != 0)) it = it->next; return it; } static size_t ak_draco_component_size(AkTypeId type) { switch (type) { case AKT_BYTE: case AKT_UBYTE: return 1; case AKT_SHORT: case AKT_USHORT: return 2; case AKT_INT: case AKT_UINT: case AKT_FLOAT: return 4; default: break; } return 0; } static bool ak_draco_store_value(const draco::PointAttribute * __restrict att, draco::PointIndex pidx, AkAccessor * __restrict acc, char * __restrict dst) { draco::AttributeValueIndex avi; int8_t comps; if (!att || !acc || !dst) return false; avi = att->mapped_index(pidx); comps = (int8_t)acc->componentCount; switch (acc->componentType) { case AKT_BYTE: return att->ConvertValue(avi, comps, (int8_t *)dst); case AKT_UBYTE: return att->ConvertValue(avi, comps, (uint8_t *)dst); case AKT_SHORT: return att->ConvertValue(avi, comps, (int16_t *)dst); case AKT_USHORT: return att->ConvertValue(avi, comps, (uint16_t *)dst); case AKT_INT: return att->ConvertValue(avi, comps, (int32_t *)dst); case AKT_UINT: return att->ConvertValue(avi, comps, (uint32_t *)dst); case AKT_FLOAT: return att->ConvertValue(avi, comps, (float *)dst); default: break; } return false; } static bool ak_draco_fill_attribute(AkGLTFState * __restrict gst, const draco::Mesh * __restrict mesh, AkAccessor * __restrict acc, const draco::PointAttribute * __restrict att) { AkBuffer *buff; char *dst; size_t compSize; size_t stride; size_t len; uint32_t i; if (!gst || !mesh || !acc || !att) return false; if (acc->count != (uint32_t)mesh->num_points()) return false; compSize = ak_draco_component_size(acc->componentType); if (compSize == 0 || acc->componentCount == 0) return false; stride = compSize * acc->componentCount; len = stride * acc->count; buff = (AkBuffer *)ak_heap_calloc(gst->heap, gst->doc, sizeof(*buff)); buff->data = ak_heap_alloc(gst->heap, buff, len); buff->length = len; dst = (char *)buff->data; for (i = 0; i < acc->count; i++) { if (!ak_draco_store_value(att, draco::PointIndex(i), acc, dst + (size_t)i * stride)) return false; } acc->buffer = buff; acc->byteOffset = 0; acc->bytesPerComponent = (uint32_t)compSize; acc->fillByteSize = stride; acc->byteStride = stride; acc->byteLength = len; flist_sp_insert(&gst->doc->lib.buffers, buff); return true; } static bool ak_draco_write_index(char * __restrict dst, AkTypeId type, uint32_t val) { switch (type) { case AKT_UBYTE: if (val > UINT8_MAX) return false; *(uint8_t *)dst = (uint8_t)val; return true; case AKT_USHORT: if (val > UINT16_MAX) return false; *(uint16_t *)dst = (uint16_t)val; return true; case AKT_UINT: *(uint32_t *)dst = val; return true; default: break; } return false; } static bool ak_draco_fill_indices(AkGLTFState * __restrict gst, const draco::Mesh * __restrict mesh, AkAccessor * __restrict acc) { AkBuffer *buff; char *dst; size_t compSize; size_t len; uint32_t faceCount; uint32_t idxCount; uint32_t f; uint32_t c; uint32_t outIdx; const draco::Mesh::Face *face; if (!gst || !mesh || !acc) return false; faceCount = (uint32_t)mesh->num_faces(); idxCount = faceCount * 3; if (acc->count != idxCount) return false; compSize = ak_draco_component_size(acc->componentType); if (compSize == 0) return false; len = compSize * idxCount; buff = (AkBuffer *)ak_heap_calloc(gst->heap, gst->doc, sizeof(*buff)); buff->data = ak_heap_alloc(gst->heap, buff, len); buff->length = len; dst = (char *)buff->data; outIdx = 0; face = NULL; for (f = 0; f < faceCount; f++) { face = &mesh->face(draco::FaceIndex(f)); for (c = 0; c < 3; c++, outIdx++) { if (!ak_draco_write_index(dst + (size_t)outIdx * compSize, acc->componentType, (uint32_t)(*face)[c].value())) return false; } } acc->buffer = buff; acc->byteOffset = 0; acc->bytesPerComponent = (uint32_t)compSize; acc->componentCount = 1; acc->fillByteSize = compSize; acc->byteStride = compSize; acc->byteLength = len; flist_sp_insert(&gst->doc->lib.buffers, buff); return true; } static bool ak_draco_fill_primitive(AkGLTFState * __restrict gst, AkMeshPrimitive * __restrict prim, const json_t * __restrict jprim, const json_t * __restrict jdraco, const draco::Mesh * __restrict mesh) { const json_t *jattrs; const json_t *jdattrs; const json_t *jattr; const json_t *jdattr; const json_t *jidx; AkAccessor *acc; int32_t accIdx; int32_t attId; if (!gst || !prim || !jprim || !jdraco || !mesh) return false; jidx = json_get(jprim, _s_gltf_indices); if (jidx) { accIdx = json_int32(jidx, -1); acc = (AkAccessor *)flist_sp_at(&gst->doc->lib.accessors, accIdx); if (!acc || !ak_draco_fill_indices(gst, mesh, acc)) return false; } jattrs = json_get(jprim, _s_gltf_attributes); jdattrs = json_get(jdraco, _s_gltf_attributes); if (!jattrs || !jdattrs) return false; jattr = (json_t *)jattrs->value; while (jattr) { jdattr = ak_draco_json_getn(jdattrs, jattr->key, (size_t)jattr->keysize); if (!jdattr) return false; accIdx = json_int32(jattr, -1); attId = json_int32(jdattr, -1); acc = (AkAccessor *)flist_sp_at(&gst->doc->lib.accessors, accIdx); if (!acc) return false; if (!ak_draco_fill_attribute(gst, mesh, acc, mesh->GetAttributeByUniqueId((uint32_t)attId))) return false; jattr = jattr->next; } return true; } extern "C" AK_DRACO_EXPORT int ak_draco_decode_gltf_primitive(AkGLTFState *gst, AkMeshPrimitive *prim, const json_t *jprim, const json_t *jdraco) { AkBufferView *bv; AkBuffer *buff; const json_t *it; const char *src; size_t off; int32_t bvIdx; draco::Decoder dec; draco::DecoderBuffer dbuf; std::unique_ptr mesh; if (!gst || !prim || !jprim || !jdraco) return -1; it = json_get(jdraco, _s_gltf_bufferView); bvIdx = it ? json_int32(it, -1) : -1; if (bvIdx < 0) return -1; bv = (AkBufferView *)flist_sp_at(&gst->bufferViews, bvIdx); if (!bv || !(buff = bv->buffer) || !buff->data) return -1; if (bv->byteOffset > buff->length || bv->byteLength == 0 || bv->byteLength > buff->length - bv->byteOffset) return -1; off = bv->byteOffset; src = (const char *)buff->data + off; dbuf.Init(src, bv->byteLength); { draco::StatusOr > meshRes = dec.DecodeMeshFromBuffer(&dbuf); if (!meshRes.ok()) return -1; mesh = std::move(meshRes).value(); } if (!mesh) return -1; return ak_draco_fill_primitive(gst, prim, jprim, jdraco, mesh.get()) ? 0 : -1; } ================================================ FILE: src/decoders/gltf/ktx2/assetkit_ktx2.cc ================================================ /* * Copyright (C) 2026 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * AssetKit KTX2 / BasisU image decoder shim — KHR_texture_basisu support. * * Compile: links against `libktx.a` (KhronosGroup/KTX-Software) which * provides KTX2 container parsing + BasisU transcoding (UASTC / ETC1S → * RGBA8 by default; the host can request a different target). Build flag: * AK_BUILD_GLTF_KTX2_DECODER. CMake FetchContent pulls KTX-Software when * not pre-installed. * * Runtime entry: AssetKit dlopens this dylib via * AK_OPT_GLTF_KTX2_DECODER_PATH (or autoload) and calls * `assetkit_ktx2_decode` with raw KTX2 bytes (read from a bufferView or * resolved URI). The shim transcodes to RGBA8 and returns the decoded * pixel buffer + width/height/channels so the caller can wire it into an * AkImage / AkBuffer pair. * * Why a separate shim and not in-tree libktx: KTX-Software is large * (>15 MB built) and pulls in BasisU's substantial codec (~5 MB). Most * AssetKit consumers don't ship KTX2-encoded textures; making it * runtime-loadable keeps the core build slim. Apps that need it drop * the dylib next to their binary. */ #include #include #include #include #include #if defined(_WIN32) # define AK_KTX2_EXPORT __declspec(dllexport) #else # define AK_KTX2_EXPORT __attribute__((visibility("default"))) #endif /*--------------------------------------------------------------------*/ /* Decoded image descriptor returned to AssetKit. The shim allocates */ /* `data` with malloc; the caller must `free` it after copying into */ /* an AkBuffer (or hand ownership over). Mirrors the simple pattern */ /* of WebP/HEIF decoder shims commonly found in 3D asset toolchains. */ /*--------------------------------------------------------------------*/ extern "C" { /* Per-mip descriptor inside the decoded image's flat buffer. */ typedef struct AkKTX2MipLevel { uint32_t width; uint32_t height; uint32_t byteOffset; /* offset into AkKTX2DecodedImage.data */ uint32_t byteLength; /* width × height × channels */ } AkKTX2MipLevel; typedef struct AkKTX2DecodedImage { uint8_t *data; /* RGBA8 pixel storage, all mips concatenated */ size_t dataLength; /* total bytes across all mips */ uint32_t width; /* base mip width */ uint32_t height; /* base mip height */ uint32_t channels; /* 4 for RGBA8 */ uint32_t mipCount; /* number of mip levels stored */ AkKTX2MipLevel *mips; /* array of length `mipCount`; allocated with malloc; caller frees with `free(mips)` after consuming */ /* Reserved: KTX2 carries cubemap face count, layer count, srgb flag. For the assetlook bridge we only care about 2D mip pyramids. */ uint32_t reserved[2]; } AkKTX2DecodedImage; typedef struct AkKTX2Decoder { void *userdata; /* Decode a KTX2 byte buffer to RGBA8. Returns 0 on success. The caller frees `out->data` via `free()` once consumed. */ int (*decode)(const uint8_t *data, size_t size, AkKTX2DecodedImage *out); void (*close)(void *ud); } AkKTX2Decoder; typedef int (*AkKTX2DecoderCreateFn)(AkKTX2Decoder *out); } /* extern "C" */ /*--------------------------------------------------------------------*/ /* libktx integration. */ /*--------------------------------------------------------------------*/ namespace { /* Return the requested transcode format for our target. RGBA8 is the universal-fallback that's CPU-decodable on any platform; on macOS we hand the resulting bytes straight to CGBitmapContextCreate / SCNMaterialProperty. Future: pick BC7 / ASTC for GPU-direct upload when the renderer hints support — out of scope here. */ constexpr ktx_transcode_fmt_e kTargetFormat = KTX_TTF_RGBA32; int ktx2_decode_to_rgba8(const uint8_t *data, size_t size, AkKTX2DecodedImage *out) { if (!data || size == 0 || !out) return -1; std::memset(out, 0, sizeof(*out)); ktxTexture2 *tex = nullptr; KTX_error_code rc = ktxTexture2_CreateFromMemory(data, size, KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, &tex); if (rc != KTX_SUCCESS || !tex) return -2; /* Transcode if the texture is BasisU-compressed (UASTC or ETC1S). */ if (ktxTexture2_NeedsTranscoding(tex)) { rc = ktxTexture2_TranscodeBasis(tex, kTargetFormat, 0); if (rc != KTX_SUCCESS) { ktxTexture_Destroy(ktxTexture(tex)); return -3; } } const uint32_t baseW = tex->baseWidth; const uint32_t baseH = tex->baseHeight; const uint32_t mipCount = tex->numLevels > 0 ? tex->numLevels : 1; /* Two-pass: (1) compute total bytes + per-mip dimensions, (2) allocate a single contiguous buffer + copy each mip in. Concatenated layout keeps the caller's free() simple (one buffer, one free). */ AkKTX2MipLevel *mips = (AkKTX2MipLevel *)std::malloc( sizeof(AkKTX2MipLevel) * mipCount); if (!mips) { ktxTexture_Destroy(ktxTexture(tex)); return -5; } size_t totalBytes = 0; for (uint32_t mip = 0; mip < mipCount; ++mip) { const uint32_t w = std::max(1u, baseW >> mip); const uint32_t h = std::max(1u, baseH >> mip); const size_t sz = (size_t)w * h * 4; mips[mip].width = w; mips[mip].height = h; mips[mip].byteOffset = (uint32_t)totalBytes; mips[mip].byteLength = (uint32_t)sz; totalBytes += sz; } uint8_t *pixels = (uint8_t *)std::malloc(totalBytes); if (!pixels) { std::free(mips); ktxTexture_Destroy(ktxTexture(tex)); return -6; } for (uint32_t mip = 0; mip < mipCount; ++mip) { ktx_size_t mipOffset = 0; rc = ktxTexture_GetImageOffset(ktxTexture(tex), mip, 0, 0, &mipOffset); if (rc != KTX_SUCCESS) { std::free(pixels); std::free(mips); ktxTexture_Destroy(ktxTexture(tex)); return -4; } std::memcpy(pixels + mips[mip].byteOffset, ktxTexture_GetData(ktxTexture(tex)) + mipOffset, mips[mip].byteLength); } out->data = pixels; out->dataLength = totalBytes; out->width = baseW; out->height = baseH; out->channels = 4; out->mipCount = mipCount; out->mips = mips; ktxTexture_Destroy(ktxTexture(tex)); return 0; } } /* anonymous namespace */ extern "C" AK_KTX2_EXPORT int assetkit_ktx2_decode(const uint8_t *data, size_t size, AkKTX2DecodedImage *out) { return ktx2_decode_to_rgba8(data, size, out); } extern "C" AK_KTX2_EXPORT int assetkit_ktx2_create(AkKTX2Decoder *out) { if (!out) return -1; out->userdata = nullptr; out->decode = ktx2_decode_to_rgba8; out->close = nullptr; return 0; } ================================================ FILE: src/decoders/gltf/meshopt/assetkit_meshoptimizer.cc ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "meshoptimizer.h" #if defined(_WIN32) # define AK_MESHOPT_EXPORT __declspec(dllexport) #else # define AK_MESHOPT_EXPORT __attribute__((visibility("default"))) #endif typedef enum AkMeshoptMode { AK_MESHOPT_MODE_UNKNOWN = 0, AK_MESHOPT_MODE_ATTRIBUTES = 1, AK_MESHOPT_MODE_TRIANGLES = 2, AK_MESHOPT_MODE_INDICES = 3 } AkMeshoptMode; typedef enum AkMeshoptFilter { AK_MESHOPT_FILTER_NONE = 0, AK_MESHOPT_FILTER_OCTAHEDRAL = 1, AK_MESHOPT_FILTER_QUATERNION = 2, AK_MESHOPT_FILTER_EXPONENTIAL = 3 } AkMeshoptFilter; extern "C" AK_MESHOPT_EXPORT int ak_meshopt_decode_gltf_buffer(void *destination, size_t destination_size, const unsigned char *buffer, size_t buffer_size, size_t count, size_t stride, int mode, int filter) { int res; if (!destination || !buffer || stride == 0 || count > ((size_t)-1) / stride || destination_size < count * stride) return -1; res = -1; switch ((AkMeshoptMode)mode) { case AK_MESHOPT_MODE_ATTRIBUTES: res = meshopt_decodeVertexBuffer(destination, count, stride, buffer, buffer_size); break; case AK_MESHOPT_MODE_TRIANGLES: res = meshopt_decodeIndexBuffer(destination, count, stride, buffer, buffer_size); break; case AK_MESHOPT_MODE_INDICES: res = meshopt_decodeIndexSequence(destination, count, stride, buffer, buffer_size); break; default: return -1; } if (res != 0) return res; switch ((AkMeshoptFilter)filter) { case AK_MESHOPT_FILTER_OCTAHEDRAL: meshopt_decodeFilterOct(destination, count, stride); break; case AK_MESHOPT_FILTER_QUATERNION: meshopt_decodeFilterQuat(destination, count, stride); break; case AK_MESHOPT_FILTER_EXPONENTIAL: meshopt_decodeFilterExp(destination, count, stride); break; default: break; } return 0; } ================================================ FILE: src/decoders/gltf/spz/assetkit_spz.cc ================================================ /* * Copyright (C) 2026 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * AssetKit Gaussian Splat decoder shim — SPZ format (Niantic Spatial). * * Compile: links against `libspz.a` (https://github.com/nianticlabs/spz). * Build flag: AK_BUILD_GLTF_SPZ_DECODER. CMake FetchContent pulls SPZ if not * pre-installed (mirroring Draco / meshopt patterns). * * Usage at runtime: AssetKit dlopens this dylib (`AK_OPT_GLTF_GSPLAT_DECODER_PATH` * or autoload). When a glTF primitive carries the future * `EXT_gsplat_compression_spz` (or any compression sub-extension that names * "spz" as the format), the dispatcher calls `decodePrimitive` here. We * read the SPZ payload from the referenced bufferView, decode via * `spz::loadSpzFromMemory`, then allocate fresh AssetKit accessors and * populate the primitive's input chain with standard * KHR_gaussian_splatting attributes (POSITION / ROTATION / SCALE / OPACITY / * COLOR_0 + SH coefficients in COLOR_1..N if the file ships them). * * Out-of-band format wrapper note: glTF doesn't yet ratify a per-format * compression sub-extension for SPZ. Until it does, apps targeting this * decoder should agree on a vendor-prefixed extension name (e.g. * `EXT_gsplat_compression_spz`) and pass the bufferView index in * `compression.bufferView`. The shim below treats whichever sub-extension * the dispatcher hands it as opaque — it just needs the bufferView index. */ #include #include #include #include #include #include #include #include #if defined(_WIN32) # define AK_GSPLAT_EXPORT __declspec(dllexport) #else # define AK_GSPLAT_EXPORT __attribute__((visibility("default"))) #endif typedef struct json_t json_t; /*--------------------------------------------------------------------*/ /* AssetKit accessor + buffer helpers, scoped to this shim. We mirror */ /* the Draco shim's storage strategy: each decoded attribute lives in */ /* its own AkBuffer + AkAccessor pair, attached to the primitive's */ /* input chain. Lifetime is the primitive's heap parent. */ /*--------------------------------------------------------------------*/ namespace { constexpr size_t kFloatSize = sizeof(float); struct GLTFStateLite { AkHeap *heap; void *doc; /* unused remainder; we only touch `heap` and the document's buffer/accessor flists which we don't access directly here */ }; /* Allocate an AkBuffer of given byte length under the primitive heap; copy the source vector into it. */ AkBuffer* make_buffer_from_vector(AkHeap *heap, void *parent, const std::vector &src) { const size_t bytes = src.size() * kFloatSize; AkBuffer *buf = (AkBuffer *)ak_heap_calloc(heap, parent, sizeof(AkBuffer)); if (!buf) return nullptr; buf->length = bytes; buf->data = ak_heap_alloc(heap, buf, bytes); if (!buf->data) return nullptr; std::memcpy(buf->data, src.data(), bytes); return buf; } /* Single-component-type accessor over a buffer of `count` × `comps` floats. */ AkAccessor* make_float_accessor(AkHeap *heap, void *parent, AkBuffer *buf, uint32_t count, uint32_t comps) { AkComponentSize componentSize = AK_COMPONENT_SIZE_SCALAR; if (comps == 2) componentSize = AK_COMPONENT_SIZE_VEC2; else if (comps == 3) componentSize = AK_COMPONENT_SIZE_VEC3; else if (comps == 4) componentSize = AK_COMPONENT_SIZE_VEC4; AkAccessor *acc = (AkAccessor *)ak_heap_calloc(heap, parent, sizeof(AkAccessor)); if (!acc) return nullptr; acc->buffer = buf; acc->byteOffset = 0; acc->count = count; acc->bytesPerComponent = (uint32_t)kFloatSize; acc->componentSize = componentSize; acc->componentType = AKT_FLOAT; acc->componentCount = comps; acc->fillByteSize = (size_t)comps * kFloatSize; acc->byteStride = acc->fillByteSize; acc->byteLength = (size_t)count * acc->fillByteSize; acc->originalComponentType = AKT_FLOAT; return acc; } /* Append an input to the primitive's input chain. Caller holds the head; we do simple prepend to match how the rest of the loader builds it. */ AkInput* prepend_input(AkHeap *heap, AkMeshPrimitive *prim, AkAccessor *acc, AkInputSemantic sem, const char *semanticRaw, uint32_t set) { AkInput *inp = (AkInput *)ak_heap_calloc(heap, prim, sizeof(AkInput)); if (!inp) return nullptr; inp->accessor = acc; inp->semantic = sem; inp->semanticRaw = ak_heap_strdup(heap, inp, semanticRaw); inp->set = set; inp->next = prim->input; prim->input = inp; prim->inputCount++; return inp; } } /* anonymous namespace */ /*--------------------------------------------------------------------*/ /* Decoder entrypoint — invoked by AssetKit when a primitive's */ /* gaussian-splat compression sub-extension references SPZ data. */ /* */ /* Args: */ /* gst — AkGLTFState* (opaque to us; we only need heap) */ /* prim — primitive to populate */ /* jprim — primitive JSON (unused, kept for parity) */ /* jcompression — the sub-extension JSON (carries `bufferView`) */ /*--------------------------------------------------------------------*/ /* Forward declarations so `assetkit_gsplat_create` can wire both function pointers in the decoder struct regardless of definition order below. */ extern "C" AK_GSPLAT_EXPORT int ak_spz_decodeBytes(AkHeap *heap, AkMeshPrimitive *prim, const uint8_t *data, size_t size); extern "C" AK_GSPLAT_EXPORT int ak_spz_decodePrimitive(struct AkGLTFState *gst_opaque, AkMeshPrimitive *prim, const json_t * /*jprim*/, const json_t *jcompression) { if (!gst_opaque || !prim || !jcompression) return -1; /* Pull `heap` out of AkGLTFState. The state's first field is a heap pointer (matches the layout the Draco shim relies on). */ AkHeap *heap = *reinterpret_cast(gst_opaque); /* Locate the bufferView the compression sub-extension points at, resolve to its raw bytes. The dispatcher should have surfaced the bufferView pointer already; we assume the sub-extension provides a "bufferView": field. AssetKit's flist of bufferViews is reachable but indexing requires the full state — defer that to the dispatcher in ext.c, which calls us with `jcompression` already containing pre-resolved buffer pointer + size in fields the spec locks down once approved. For now we expect a `data` ptr + `size` callback wired by the dispatcher. */ /* TODO: once a per-format compression sub-extension is ratified, the dispatcher in src/io/gltf/imp/core/ext.c will resolve the bufferView and pass `data`+`size` here directly. The skeleton above leaves that wiring open. */ (void)heap; (void)prim; return -1; /* not yet wired — bufferView resolution path pending spec */ } extern "C" AK_GSPLAT_EXPORT int assetkit_gsplat_create(AkGaussianSplatDecoder *out) { if (!out) return -1; out->userdata = nullptr; out->decodeBytes = ak_spz_decodeBytes; /* preferred path */ out->decodePrimitive = ak_spz_decodePrimitive; /* legacy fallback */ out->close = nullptr; return 0; } /*--------------------------------------------------------------------*/ /* SPZ → AssetKit conversion (helper used by the decoder once the */ /* dispatcher delivers raw bytes). Public so a host integration test */ /* can exercise the path with a known SPZ blob. */ /*--------------------------------------------------------------------*/ extern "C" AK_GSPLAT_EXPORT int ak_spz_decodeBytes(AkHeap *heap, AkMeshPrimitive *prim, const uint8_t *data, size_t size) { if (!heap || !prim || !data || size == 0) return -1; spz::GaussianCloud cloud; try { spz::UnpackOptions opts; cloud = spz::loadSpz(data, size, opts); } catch (...) { return -2; } if (cloud.numPoints <= 0) return -3; const uint32_t n = static_cast(cloud.numPoints); /* libspz returns *encoded* fields per the SPZ format convention: scales : log-scale → caller must exp(x) alphas : pre-sigmoid → caller must sigmoid(x) = 1/(1+exp(-x)) colors : SH DC component → caller must 0.5 + 0.282095·x (clamp 0..1) We decode in-shim so AssetKit consumers see standard renderer-ready values: scale (linear units), opacity (0..1), color (0..1 RGB). Higher-order SH coefficients pass through unchanged — renderers that care evaluate them direction-dependent. NaN/Inf guards: corrupted SPZ payloads or extreme outliers in legit data can produce NaN after exp() or sigmoid() when raw values are beyond representable range. We sanitize each field as it's converted so a single bad splat doesn't blow out the whole renderer. */ auto sanitize = [](float v, float fallback) { return std::isfinite(v) ? v : fallback; }; /* POSITION (vec3) — sanitize NaN/Inf to origin (visually clusters bad splats at the model origin where they're easy to spot). */ { std::vector safePos(cloud.positions.size()); for (size_t i = 0; i < cloud.positions.size(); ++i) safePos[i] = sanitize(cloud.positions[i], 0.0f); if (auto *buf = make_buffer_from_vector(heap, prim, safePos)) { auto *acc = make_float_accessor(heap, prim, buf, n, 3); prepend_input(heap, prim, acc, AK_INPUT_POSITION, "POSITION", 0); } } /* ROTATION (vec4 quat xyzw) — sanitize NaN to identity quat (0,0,0,1). A degenerate quat can collapse to a non-rotation, but identity means the splat aligns with object axes which is renderable. */ { std::vector safeRot(cloud.rotations.size()); for (size_t i = 0; i < cloud.rotations.size(); i += 4) { float x = sanitize(cloud.rotations[i + 0], 0.0f); float y = sanitize(cloud.rotations[i + 1], 0.0f); float z = sanitize(cloud.rotations[i + 2], 0.0f); float w = sanitize(cloud.rotations[i + 3], 1.0f); // If the quat collapsed (all zeros after sanitize), force identity. if (x == 0.0f && y == 0.0f && z == 0.0f && w == 0.0f) { w = 1.0f; } safeRot[i + 0] = x; safeRot[i + 1] = y; safeRot[i + 2] = z; safeRot[i + 3] = w; } if (auto *buf = make_buffer_from_vector(heap, prim, safeRot)) { auto *acc = make_float_accessor(heap, prim, buf, n, 4); prepend_input(heap, prim, acc, AK_INPUT_OTHER, "ROTATION", 0); } } /* SCALE (vec3) — decode log → linear via exp(x). Clamp the log range before exp() so an extreme value doesn't produce inf scale. Real splat scales are sub-meter; even log(100) = ~4.6 is generous. */ { std::vector linearScales(cloud.scales.size()); for (size_t i = 0; i < cloud.scales.size(); ++i) { float logS = sanitize(cloud.scales[i], 0.0f); // 0 in log → 1 linear // Clamp log to ±15 (linear range ≈ 3·10⁻⁷ … 3·10⁶). Beyond this // is virtually-certainly bad data. if (logS < -15.0f) logS = -15.0f; if (logS > 15.0f) logS = 15.0f; linearScales[i] = std::exp(logS); } if (auto *buf = make_buffer_from_vector(heap, prim, linearScales)) { auto *acc = make_float_accessor(heap, prim, buf, n, 3); prepend_input(heap, prim, acc, AK_INPUT_OTHER, "SCALE", 0); } } /* OPACITY (float) — decode logit → 0..1 via sigmoid. NaN logit defaults to 0 (sigmoid(0) = 0.5 ≈ "moderately visible"). */ { std::vector opacities(cloud.alphas.size()); for (size_t i = 0; i < cloud.alphas.size(); ++i) { const float x = sanitize(cloud.alphas[i], 0.0f); // sigmoid(x) on extreme x is fine: sigmoid(±∞) → 0/1. We sanitized // for NaN; ±Inf path produces clean 0 or 1. opacities[i] = 1.0f / (1.0f + std::exp(-x)); } if (auto *buf = make_buffer_from_vector(heap, prim, opacities)) { auto *acc = make_float_accessor(heap, prim, buf, n, 1); prepend_input(heap, prim, acc, AK_INPUT_OTHER, "OPACITY", 0); } } /* COLOR_0 (vec3) — decode SH DC component → 0..1 RGB. Formula `0.5 + 0.282095·x` is the SH band-0 inverse normalization constant (1 / (2·sqrt(π)) ≈ 0.282095). Clamp to [0,1] to guard against extrapolation on splats with extreme DC values; sanitize NaN→0 first so the clamp produces 0.5 (mid-gray fallback). */ { std::vector rgb(cloud.colors.size()); constexpr float kSH0 = 0.282094791773878f; /* 1 / (2*sqrt(pi)) */ for (size_t i = 0; i < cloud.colors.size(); ++i) { float dc = sanitize(cloud.colors[i], 0.0f); float v = 0.5f + kSH0 * dc; if (v < 0.0f) v = 0.0f; if (v > 1.0f) v = 1.0f; rgb[i] = v; } if (auto *buf = make_buffer_from_vector(heap, prim, rgb)) { auto *acc = make_float_accessor(heap, prim, buf, n, 3); prepend_input(heap, prim, acc, AK_INPUT_COLOR, "COLOR", 0); } } /* Higher-order SH coefficients (COLOR_1..COLOR_N) — vec3 each, packed as `numPoints × shDim × 3`. shDim depends on the spherical-harmonic degree (degree 1 → 3 dirs, degree 2 → 8 dirs, degree 3 → 15 dirs). We split into per-dir accessors so renderers that read COLOR_n can fetch any subset. NaN sanitize → 0 so a bad coefficient gives no view-dependent contribution rather than blowing out colors. */ if (cloud.shDegree > 0 && !cloud.sh.empty()) { const int shDim = cloud.shDegree * (cloud.shDegree + 2); for (int d = 0; d < shDim; ++d) { std::vector band(static_cast(n) * 3); for (uint32_t i = 0; i < n; ++i) { const size_t srcBase = (size_t)i * (size_t)shDim * 3 + (size_t)d * 3; const size_t dstBase = (size_t)i * 3; band[dstBase + 0] = sanitize(cloud.sh[srcBase + 0], 0.0f); band[dstBase + 1] = sanitize(cloud.sh[srcBase + 1], 0.0f); band[dstBase + 2] = sanitize(cloud.sh[srcBase + 2], 0.0f); } auto *buf = make_buffer_from_vector(heap, prim, band); auto *acc = make_float_accessor(heap, prim, buf, n, 3); prepend_input(heap, prim, acc, AK_INPUT_COLOR, "COLOR", static_cast(d + 1)); } } return 0; } ================================================ FILE: src/default/CMakeLists.txt ================================================ target_sources(${PROJECT_NAME} PRIVATE semantic.h type.c type.h opt.c opt.h semantic.c material.c material.h id.c light.c light.h coord.c cmp.c cam.h cam.c ) ================================================ FILE: src/default/cam.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "cam.h" #include AkPerspective ak_def_cam_tcommon = { .base = { .type = AK_PROJECTION_PERSPECTIVE, .tag = 0 }, .yfov = GLM_PI_4f, .xfov = GLM_PI_2f, .aspectRatio = 0.5f, .znear = 0.01f, .zfar = 100.0f }; AkOptics ak_def_cam_optics = { .tcommon = &ak_def_cam_tcommon.base, .technique = NULL }; const AkCamera ak_def_cam = { .name = "default", .optics = &ak_def_cam_optics }; const AkCamera* ak_def_camera(void) { return &ak_def_cam; } ================================================ FILE: src/default/cam.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_def_cam_h #define ak_def_cam_h #include "../common.h" const AkCamera* ak_def_camera(void); #endif /* ak_def_cam_h */ ================================================ FILE: src/default/cmp.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include AK_EXPORT int ak_cmp_str(void * key1, void *key2) { return strcmp((char *)key1, (char *)key2); } AK_EXPORT int ak_cmp_ptr(void *key1, void *key2) { if (key1 > key2) return 1; else if (key1 < key2) return -1; return 0; } AK_EXPORT int ak_cmp_i32(void *key1, void *key2) { int32_t a, b; a = *(int32_t *)key1; b = *(int32_t *)key2; return a - b; } AK_EXPORT int ak_cmp_vec3(void *key1, void *key2) { float *v1, *v2; v1 = key1; v2 = key2; if (v1[0] > v2[0]) return 1; else if (v1[0] < v2[0]) return -1; if (v1[1] > v2[1]) return 1; else if (v1[1] < v2[1]) return -1; if (v1[2] > v2[2]) return 1; else if (v1[2] < v2[2]) return -1; return 0; } AK_EXPORT int ak_cmp_ivec3(void *key1, void *key2) { int32_t *v1, *v2; v1 = key1; v2 = key2; if (v1[0] > v2[0]) return 1; else if (v1[0] < v2[0]) return -1; if (v1[1] > v2[1]) return 1; else if (v1[1] < v2[1]) return -1; if (v1[2] > v2[2]) return 1; else if (v1[2] < v2[2]) return -1; return 0; } AK_EXPORT int ak_cmp_vec4(void *key1, void *key2) { float *v1, *v2; v1 = key1; v2 = key2; if (v1[0] > v2[0]) return 1; else if (v1[0] < v2[0]) return -1; if (v1[1] > v2[1]) return 1; else if (v1[1] < v2[1]) return -1; if (v1[2] > v2[2]) return 1; else if (v1[2] < v2[2]) return -1; if (v1[3] > v2[3]) return 1; else if (v1[3] < v2[3]) return -1; return 0; } ================================================ FILE: src/default/coord.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "../coord/common.h" /* Right Hand (Default) */ AkCoordSys AK__Y_RH_VAL = {AK__Y_RH, AK_AXIS_ROT_DIR_RH, AK__Y_RH}; AkCoordSys *AK_YUP = &AK__Y_RH_VAL; AkCoordSys *AK_ZUP = AK_COORD(AK__Z_RH, AK_AXIS_ROT_DIR_RH, AK__Y_RH); AkCoordSys *AK_XUP = AK_COORD(AK__X_RH, AK_AXIS_ROT_DIR_RH, AK__Y_RH); /* Left Hand */ AkCoordSys *AK_ZUP_LH = AK_COORD(AK__Z_LH, AK_AXIS_ROT_DIR_LH, AK__Y_LH); AkCoordSys *AK_YUP_LH = AK_COORD(AK__Y_LH, AK_AXIS_ROT_DIR_LH, AK__Y_LH); AkCoordSys *AK_XUP_LH = AK_COORD(AK__X_LH, AK_AXIS_ROT_DIR_LH, AK__Y_LH); AkCoordSys *AK_DEFAULT_COORD = &AK__Y_RH_VAL; ================================================ FILE: src/default/id.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" const char * AK_DEF_ID_PRFX = "id-"; ================================================ FILE: src/default/light.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "light.h" AkDirectionalLight akdef_light_tcommon = { .direction = {0.0f, 0.0f, -1.0f}, .type = AK_LIGHT_TYPE_DIRECTIONAL, .color = { {1.0, 1.0, 1.0, 1.0} }, .ctype = 0 }; AkLight akdef_light = { .name = "default", .tcommon = &akdef_light_tcommon, .technique = NULL, .extra = NULL, .next = NULL }; const AkLight* ak_def_light(void) { return &akdef_light; } ================================================ FILE: src/default/light.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_def_light_h #define ak_def_light_h #include "../common.h" const AkLight* ak_def_light(void); #endif /* ak_def_light_h */ ================================================ FILE: src/default/material.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "material.h" #include "../common.h" //float ak__def_transpval = 1.0f; //AkFloatOrParam ak__def_transparency = { // .val = &ak__def_transpval, // .param = NULL //}; // //AkFloatOrParam* //ak_def_transparency(void) { // return &ak__def_transparency; //} ================================================ FILE: src/default/material.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_def_material_h #define ak_def_material_h #include "../common.h" //AkFloatOrParam* //ak_def_transparency(void); #endif /* ak_def_material_h */ ================================================ FILE: src/default/opt.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "opt.h" #include #include extern AkCoordSys AK__Y_RH_VAL; extern const char * AK_DEF_ID_PRFX; const char *ak__def_techniques[] = {"common", NULL}; uintptr_t AK_OPTIONS[] = { false, /* 0: _INDICES_NONE */ false, /* 1: _INDICES_SINGLE_INTERLEAVED */ false, /* 2: _INDICES_SINGLE_SEPARATE */ false, /* 3: _INDICES_SINGLE */ true, /* 4: _NO_INDICES_INTERLEAVED */ false, /* 5: _NO_INDICES_SEPARATE */ (uintptr_t)&AK__Y_RH_VAL, /* 6: _COORD */ (uintptr_t)&AK_DEF_ID_PRFX, /* 7: _DEFAULT_ID_PREFIX */ false, /* 8: _COMPUTE_BBOX */ true, /* 9: _TRIANGULATE */ true, /* 10: _GEN_NORMALS_IF_NEEDED */ AK_PROFILE_TYPE_COMMON, /* 11: _DEFAULT_PROFILE */ true, /* 12: _EFFECT_PROFILE */ (uintptr_t)ak__def_techniques, /* 13: _TECHNIQUE */ (uintptr_t)ak__def_techniques, /* 14: _TECHNIQUE_FX */ false, /* 15: _ZERO_INDEXED_INPUT */ true, /* 16: _IMAGE_LOAD_FLIP_VERTICALLY */ true, /* 17: _ADD_DEFAULT_CAMERA */ false, /* 18: _ADD_DEFAULT_LIGHT */ AK_COORD_CVT_DEFAULT, /* 19: _COORD_CONVERT_TYPE */ true, /* 20: _BUGFIXES */ false, /* 21: _COMPUTE_EXACT_CENTER */ #ifndef _MSC_VER true, /* 22: _USE_MMAP */ #else false, #endif true, /* 23: _GEN_TANGENTS_IF_NEEDED */ false, /* 24: _CVT_TRIANGLESTRIP */ false, /* 25: _CVT_TRIANGLEFAN */ false, /* 26: _CVT_LINELOOP */ false, /* 27: _CVT_LINESTRIP */ false, /* 28: _PRESERVE_QUANTIZED_ATTRS */ true, /* 29: _GLTF_EXT_DECODER_AUTOLOAD */ (uintptr_t)NULL, /* 30: _GLTF_MESHOPT_DECODER_PATH */ (uintptr_t)NULL, /* 31: _GLTF_DRACO_DECODER_PATH */ (uintptr_t)NULL, /* 32: _GLTF_GSPLAT_DECODER_PATH */ (uintptr_t)NULL /* 33: _GLTF_KTX2_DECODER_PATH */ }; AK_EXPORT void ak_opt_set(AkOption option, uintptr_t value) { assert((uint32_t)option < AK_ARRAY_LEN(AK_OPTIONS)); AK_OPTIONS[option] = value; } AK_EXPORT uintptr_t ak_opt_get(AkOption option) { assert((uint32_t)option < AK_ARRAY_LEN(AK_OPTIONS)); return AK_OPTIONS[option]; } AK_EXPORT void ak_opt_set_str(AkOption option, const char *value) { assert((uint32_t)option < AK_ARRAY_LEN(AK_OPTIONS)); AK_OPTIONS[option] = (uintptr_t)ak_strdup(NULL, value); } ================================================ FILE: src/default/opt.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_def_opt_h #define ak_def_opt_h #include "../common.h" typedef struct AkOptionItem { AkEnum name; uintptr_t value; } AkOptionItem; #endif /* ak_def_opt_h */ ================================================ FILE: src/default/semantic.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "semantic.h" /* Prefix desc: P represents param, T represents type */ #define AK_PUSER 0 /* undefined by lib */ #define AK_PXYZ 1 #define AK_TFLOAT 0 AkTypeDesc ak_def_dtypes[] = { {"float", AKT_FLOAT, sizeof(float), 0} }; AkInputSemanticPair ak_def_params[] = { {0, NULL, NULL}, {3, &ak_def_dtypes[AK_TFLOAT], (char*[]){"X", "Y", "Z"}} }; const AkInputSemanticPair* ak_def_sm_pairs[] = { /* _OTHER */ &ak_def_params[AK_PUSER], /* _BINORMAL */ &ak_def_params[AK_PUSER], /* _COLOR */ &ak_def_params[AK_PUSER], /* _CONTINUITY */ &ak_def_params[AK_PUSER], /* _IMAGE */ &ak_def_params[AK_PUSER], /* _INPUT */ &ak_def_params[AK_PUSER], /* _IN_TANGENT */ &ak_def_params[AK_PUSER], /* _INTERPOLATION */ &ak_def_params[AK_PUSER], /* _INV_BIND_MATRIX */ &ak_def_params[AK_PUSER], /* _JOINT */ &ak_def_params[AK_PUSER], /* _LINEAR_STEPS */ &ak_def_params[AK_PUSER], /* _MORPH_TARGET */ &ak_def_params[AK_PUSER], /* _MORPH_WEIGHT */ &ak_def_params[AK_PUSER], /* _NORMAL */ &ak_def_params[AK_PXYZ], /* _OUTPUT */ &ak_def_params[AK_PUSER], /* _OUT_TANGENT */ &ak_def_params[AK_PUSER], /* _POSITION */ &ak_def_params[AK_PXYZ], /* _TANGENT */ &ak_def_params[AK_PUSER], /* _TEXBINORMAL */ &ak_def_params[AK_PUSER], /* _TEXCOORD */ &ak_def_params[AK_PUSER], /* _TEXTANGENT */ &ak_def_params[AK_PUSER], /* _UV */ &ak_def_params[AK_PUSER], /* _VERTEX */ &ak_def_params[AK_PUSER], /* _WEIGHT */ &ak_def_params[AK_PUSER] }; const AkInputSemanticPair** ak_def_semantic(void) { return ak_def_sm_pairs; } uint32_t ak_def_semanticc(void) { return AK_ARRAY_LEN(ak_def_sm_pairs); } ================================================ FILE: src/default/semantic.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_def_semantic_h #define ak_def_semantic_h #include "../common.h" typedef struct AkInputSemanticPair { uint32_t count; AkTypeDesc *type; char **params; } AkInputSemanticPair; const AkInputSemanticPair** ak_def_semantic(void); uint32_t ak_def_semanticc(void); #endif /* ak_def_semantic_h */ ================================================ FILE: src/default/type.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "type.h" #include "../common.h" AkTypeDesc ak_def_type_descs[] = { {"float", AKT_FLOAT, sizeof(AkFloat), 0}, {"float2", AKT_FLOAT2, sizeof(AkFloat2), 0}, {"float3", AKT_FLOAT3, sizeof(AkFloat3), 0}, {"float4", AKT_FLOAT4, sizeof(AkFloat4), 0}, {"float2x2", AKT_FLOAT2x2, sizeof(AkFloat2), 0}, {"float3x3", AKT_FLOAT3x3, sizeof(AkFloat2[2]), 0}, {"float4x4", AKT_FLOAT4x4, sizeof(AkFloat4[4]), 0}, {"int", AKT_INT, sizeof(AkInt), 0}, {"int2", AKT_INT2, sizeof(AkInt[2]), 0}, {"int3", AKT_INT3, sizeof(AkInt[3]), 0}, {"int4", AKT_INT4, sizeof(AkInt[4]), 0}, {"bool", AKT_BOOL, sizeof(AkBool), 0}, {"bool2", AKT_BOOL2, sizeof(AkBool[2]), 0}, {"bool3", AKT_BOOL3, sizeof(AkBool[3]), 0}, {"bool4", AKT_BOOL4, sizeof(AkBool[4]), 0}, {"string", AKT_STRING, sizeof(AkString), 0}, /* for glTF */ {"uint", AKT_UINT, sizeof(AkUInt), 0}, {"byte", AKT_BYTE, sizeof(char), 0}, {"ubyte", AKT_UBYTE, sizeof(unsigned char), 0}, {"short", AKT_SHORT, sizeof(short), 0}, {"ushort", AKT_USHORT, sizeof(unsigned short), 0}, {"sampler2D", AKT_SAMPLER2D, sizeof(AkSampler), 0}, /* for PLY */ {"char", AKT_BYTE, sizeof(char), 0}, {"uchar", AKT_UBYTE, sizeof(char), 0}, {"double", AKT_DOUBLE, sizeof(AkDouble), 0}, {"int8", AKT_INT, sizeof(char), 0}, {"uint8", AKT_UINT, sizeof(char), 0}, {"int16", AKT_SHORT, sizeof(AkInt16), 0}, {"uint16", AKT_USHORT, sizeof(AkUInt16), 0}, {"int32", AKT_UINT, sizeof(AkInt), 0}, {"uint32", AKT_UINT, sizeof(AkUInt), 0}, {"float32", AKT_FLOAT, sizeof(AkFloat), 0}, {"float64", AKT_DOUBLE, sizeof(AkDouble), 0}, {NULL, 0, 0, 0} }; const AkTypeDesc* ak_def_typedesc(void) { return ak_def_type_descs; } ================================================ FILE: src/default/type.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_def_type_h #define ak_def_type_h #include "../common.h" const AkTypeDesc* ak_def_typedesc(void); #endif /* ak_def_type_h */ ================================================ FILE: src/endian.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef endian_h #define endian_h #ifdef __cplusplus extern "C" { #endif #include "../include/ak/common.h" #include #if _MSC_VER # if (defined(_M_IX86) || defined(_M_X64)) # define ARCH_X86 # endif # if (defined(_M_AMD64) || defined(_M_X64)) # define ARCH_X86_64 # endif #else # ifdef __i386__ # define ARCH_X86 # endif # ifdef __x86_64__ # define ARCH_X86_64 # endif #endif AK_INLINE uint16_t bswapu16(uint16_t val) { #if !defined(_MSC_VER) return (uint16_t)((val << 8) | (val >> 8)); #else return _byteswap_ushort(val); #endif } AK_INLINE uint32_t bswapu32(uint32_t val) { #if defined(__llvm__) return __builtin_bswap32(val); #elif defined(ARCH_X86) # if !defined(_MSC_VER) __asm__ ("bswap %0" : "+r" (val)); return val; # else return _byteswap_ulong(val); # endif #else val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF); return (val << 16) | (val >> 16); #endif } AK_INLINE uint64_t bswapu64(uint64_t val) { #if defined(__llvm__) return __builtin_bswap64(val); #elif defined(ARCH_X86_64) # if !defined(_MSC_VER) __asm__ ("bswap %0" : "+r" (val)); return val; # else return _byteswap_uint64(val); # endif #elif defined(ARCH_X86) # if !defined(_MSC_VER) __asm__ ("bswap %%eax\n\t" "bswap %%edx\n\t" "xchgl %%eax, %%edx" : "+A" (val)); return val; # else return _byteswap_uint64(val); # endif #else /* return ((((val) & 0xff00000000000000ull) >> 56) | (((val) & 0x00ff000000000000ull) >> 40) | (((val) & 0x0000ff0000000000ull) >> 24) | (((val) & 0x000000ff00000000ull) >> 8) | (((val) & 0x00000000ff000000ull) << 8) | (((val) & 0x0000000000ff0000ull) << 24) | (((val) & 0x000000000000ff00ull) << 40) | (((val) & 0x00000000000000ffull) << 56)); */ val = ((val << 8) & 0xFF00FF00FF00FF00ULL) | ((val >> 8) & 0x00FF00FF00FF00FFULL); val = ((val << 16) & 0xFFFF0000FFFF0000ULL) | ((val >> 16) & 0x0000FFFF0000FFFFULL); return (val << 32) | (val >> 32); #endif } /* helper for little endian */ /* 16-bit */ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ # define le_16(X, DATA) \ do { \ memcpy(&X, DATA, 2); \ DATA = (char *)DATA + 2; \ } while (0) #else # define le_16(X, DATA) \ do { \ memcpy(&X, DATA, 2); \ X = bswapu16((uint16_t)X); \ DATA = (char *)DATA + 2; \ } while (0) #endif /* 32-bit */ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ # define le_32(X, DATA) \ do { \ memcpy(&X, DATA, 4); \ DATA = (char *)DATA + 4; \ } while (0) #else # define le_32(X, DATA) \ do { \ memcpy(&X, DATA, 4); \ X = bswapu32((uint32_t)X); \ DATA = (char *)DATA + 4; \ } while (0) #endif /* 64-bit */ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ # define le_64(X, DATA) \ do { \ memcpy(&X, DATA, 8); \ DATA = (char *)DATA + 8; \ } while (0) #else # define le_64(X, DATA) \ do { \ memcpy(&X, DATA, 8); \ X = bswapu64((uint64_t)X); \ DATA = (char *)DATA + 8; \ } while (0) #endif /* helper for big endian */ /* 16-bit */ #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ # define be_16(X, DATA) \ do { \ memcpy(&X, DATA, 2); \ DATA = (char *)DATA + 2; \ } while (0) #else # define be_16(X, DATA) \ do { \ memcpy(&X, DATA, 2); \ X = bswapu16((uint16_t)X); \ DATA = (char *)DATA + 2; \ } while (0) #endif /* 32-bit */ #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ # define be_32(X, DATA) \ do { \ memcpy(&X, DATA, 4); \ DATA = (char *)DATA + 4; \ } while (0) #else # define be_32(X, DATA) \ do { \ memcpy(&X, DATA, 4); \ X = bswapu32((uint32_t)X); \ DATA = (char *)DATA + 4; \ } while (0) #endif /* 64-bit */ #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ # define be_64(X, DATA) \ do { \ memcpy(&X, DATA, 8); \ DATA = (char *)DATA + 8; \ } while (0) #else # define be_64(X, DATA) \ do { \ memcpy(&X, DATA, 8); \ X = bswapu64((uint64_t)X); \ DATA = (char *)DATA + 8; \ } while (0) #endif #define memcpy_endian64(isLittleEndian, X, DATA) \ do { \ if (isLittleEndian) { \ le_64(X, DATA); \ } else { \ be_64(X, DATA); \ } \ } while (0) #define memcpy_endian32(isLittleEndian, X, DATA) \ do { \ if (isLittleEndian) { \ le_32(X, DATA); \ } else { \ be_32(X, DATA); \ } \ } while (0) #define memcpy_endian16(isLittleEndian, X, DATA) \ do { \ if (isLittleEndian) { \ le_16(X, DATA); \ } else { \ be_16(X, DATA); \ } \ } while (0) #ifdef __cplusplus } #endif #endif /* endian_h */ ================================================ FILE: src/find.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "common.h" AK_EXPORT void * ak_getId(void * __restrict objptr) { return ak_mem_getId((void *)objptr); } AK_EXPORT AkResult ak_setId(void * __restrict objptr, const char * __restrict objectId) { ak_mem_setId(objptr, (void *)objectId); return AK_OK; } AK_EXPORT AkResult ak_moveId(void * __restrict objptrOld, void * __restrict objptrNew) { char *objectId; objectId = ak_getId(objptrOld); if (objectId) { ak_heap_setpm(objectId, objptrNew); ak_setId(objptrOld, NULL); ak_setId(objptrNew, objectId); } return AK_OK; } AK_EXPORT void * ak_getObjectById(AkDoc * __restrict doc, const char * __restrict objectId) { void *foundObject; ak_mem_getMemById(doc, (void *)objectId, &foundObject); return foundObject; } ================================================ FILE: src/geom/CMakeLists.txt ================================================ target_sources(${PROJECT_NAME} PRIVATE mesh.c ) ================================================ FILE: src/geom/mesh.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" AK_EXPORT uint32_t ak_meshInputCount(AkMesh * __restrict mesh) { AkMeshPrimitive *prim; uint32_t count; count = 0; prim = mesh->primitive; while (prim) { count += prim->inputCount; prim = prim->next; } return count; } ================================================ FILE: src/id.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "common.h" #include "id.h" #include #include #include AK_HIDE void ak_id_newheap(AkHeap * __restrict heap) { size_t *idp; void *idpstr; heap->idheap = ak_heap_new(NULL, NULL, NULL); /* default prefix */ idpstr = *(void **)ak_opt_get(AK_OPT_DEFAULT_ID_PREFIX); idp = ak_heap_alloc(heap->idheap, NULL, sizeof(size_t)); *idp = 1; ak_heap_setId(heap->idheap, ak__alignof(idp), idpstr); ak_heap_attach(heap, heap->idheap); } AK_EXPORT const char * ak_generatId(AkDoc * __restrict doc, void * __restrict parentmem, const char * __restrict prefix) { return ak_id_gen(ak_heap_getheap(doc), parentmem, prefix); } AK_HIDE const char * ak_id_gen(AkHeap * __restrict heap, void * __restrict parentmem, const char * __restrict prefix) { AkHeap *idheap; size_t *idp; void *idpf; char *id; const char *idpstr; size_t size, tknsize; AkResult ret; if (!prefix) idpstr = *(const char **)ak_opt_get(AK_OPT_DEFAULT_ID_PREFIX); else idpstr = prefix; idheap = heap->idheap; if (!idheap) { ak_id_newheap(heap); idheap = heap->idheap; } ret = ak_heap_getMemById(idheap, (void *)idpstr, &idpf); if (ret != AK_OK) { idp = ak_heap_alloc(idheap, NULL, sizeof(size_t)); *idp = 1; ak_heap_setId(idheap, ak__alignof(idp), (void *)idpstr); } else { idp = idpf; } size = strlen(idpstr); do { AkHeapNode *node; AkResult ret; /* we ensure that token > 0 when in ctor */ tknsize = (size_t)log10((double)*idp) + 1; id = ak_heap_alloc(heap, parentmem, size + tknsize + 1); id[size + tknsize] = '\0'; strcpy(id, idpstr); sprintf(id + size, "%zu", *idp); ret = ak_heap_getNodeById(heap, (void *)id, &node); ++(*idp); /* ok no dups */ if (ret == AK_EFOUND) break; ak_free(id); } while (true); return id; } ================================================ FILE: src/id.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_id_h #define ak_id_h #include "common.h" AK_HIDE void ak_id_newheap(AkHeap * __restrict heap); AK_HIDE const char * ak_id_gen(AkHeap * __restrict heap, void * __restrict parentmem, const char * __restrict prefix); #endif /* ak_id_h */ ================================================ FILE: src/image/CMakeLists.txt ================================================ target_sources(${PROJECT_NAME} PRIVATE image.c ) ================================================ FILE: src/image/image.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "../../include/ak/bbox.h" #include "../../include/ak/path.h" #include #ifdef _MSC_VER # ifndef PATH_MAX # define PATH_MAX 260 # endif #endif typedef struct AkImageConf { AkImageLoadFromFileFn loadFromFile; AkImageLoadFromMemoryFn loadFromMemory; } AkImageConf; static AkImageConf ak__img_conf = {0}; AK_EXPORT void ak_imageInitLoader(AkImageLoadFromFileFn fromFile, AkImageLoadFromMemoryFn fromMemory) { ak__img_conf.loadFromFile = fromFile; ak__img_conf.loadFromMemory = fromMemory; } AK_EXPORT void ak_imageLoad(AkImage * __restrict image) { AkHeap *heap; AkDoc *doc; AkImageData *idata; unsigned char *data; bool flipImage; if (image->data) return; idata = NULL; data = NULL; heap = ak_heap_getheap(image); doc = ak_heap_data(heap); flipImage = false; /* glTF uses top-left as origin */ if (doc->inf->flipImage) { flipImage = ak_opt_get(AK_OPT_IMAGE_LOAD_FLIP_VERTICALLY); } if (image->initFrom) { AkInitFrom *initFrom; int x, y, ch; initFrom = image->initFrom; if (initFrom->ref) { char pathbuf[PATH_MAX]; const char *path; if (!ak__img_conf.loadFromFile) return; path = ak_fullpath(doc, initFrom->ref, pathbuf); initFrom->resolvedFullPath = ak_strdup(initFrom, pathbuf); image->data = ak__img_conf.loadFromFile(heap, image, path, flipImage); } else if (initFrom->buff && initFrom->buff->data) { if (!ak__img_conf.loadFromMemory) return; image->data = ak__img_conf.loadFromMemory(heap, image, initFrom->buff, flipImage); } else if (initFrom->hex) { /* TODO: */ } } } ================================================ FILE: src/instance/CMakeLists.txt ================================================ target_sources(${PROJECT_NAME} PRIVATE inst.c list.c ) ================================================ FILE: src/instance/inst.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "../id.h" AK_EXPORT AkInstanceGeometry* ak_instanceMakeGeom(AkHeap * __restrict heap, void * __restrict memparent, AkGeometry * __restrict object) { AkInstanceGeometry *instGeom; instGeom = ak_heap_calloc(heap, memparent, sizeof(*instGeom)); instGeom->base.url.ptr = object; instGeom->base.object = object; instGeom->base.type = AK_INSTANCE_GEOMETRY; return instGeom; } AK_EXPORT AkInstanceBase* ak_instanceMake(AkHeap * __restrict heap, void * __restrict memparent, void * __restrict object) { AkInstanceBase *instance; const char *id; if (!object || !memparent || !heap) return NULL; instance = ak_heap_calloc(heap, memparent, sizeof(*instance)); /* we already have the object */ instance->object = object; /* target must have id or we will generate an id for it */ id = ak_getId(object); if (!id) id = ak_id_gen(heap, object, NULL); ak_url_init_with_id(heap->allocator, instance, (char *)id, &instance->url); return instance; } AK_EXPORT void * ak_instanceObject(AkInstanceBase *instanceBase) { if (!instanceBase) return NULL; if (!instanceBase->object) instanceBase->object = ak_getObjectByUrl(&instanceBase->url); return instanceBase->object; } AK_EXPORT AkNode * ak_instanceObjectNode(AkNode * node) { AkInstanceBase *instanceBase; instanceBase = &node->node->base; if (!instanceBase->object) instanceBase->object = ak_getObjectByUrl(&instanceBase->url); return instanceBase->object; } AK_EXPORT AkGeometry * ak_instanceObjectGeom(AkNode * node) { AkInstanceBase *instanceBase; instanceBase = &node->geometry->base; if (!instanceBase->object) instanceBase->object = ak_getObjectByUrl(&instanceBase->url); return instanceBase->object; } AK_EXPORT AkGeometry * ak_instanceObjectGeomId(AkDoc * __restrict doc, const char * id) { AkNode *node; AkInstanceBase *instanceBase; node = ak_getObjectById(doc, id); if (!node) return NULL; instanceBase = &node->geometry->base; if (!instanceBase->object) instanceBase->object = ak_getObjectByUrl(&instanceBase->url); return instanceBase->object; } AK_EXPORT AkNode* ak_instanceMoveToSubNode(AkNode * __restrict node, AkInstanceBase *inst) { AkHeap *heap; AkNode *subNode; size_t off; heap = ak_heap_getheap(node); subNode = ak_heap_calloc(heap, node, sizeof(*node)); subNode->visible = true; switch (inst->type) { case AK_INSTANCE_GEOMETRY: off = offsetof(AkNode, geometry); break; case AK_INSTANCE_LIGHT: off = offsetof(AkNode, light); break; case AK_INSTANCE_CAMERA: off = offsetof(AkNode, camera); break; case AK_INSTANCE_NODE: off = offsetof(AkNode, node); break; default: ak_free(subNode); return NULL; break; } if (inst->prev) inst->prev->next = inst->next; if (inst->next) inst->next->prev = inst->prev; if (*(void **)((char *)node + off) == inst) *(void **)((char *)node + off) = NULL; *(void **)((char *)subNode + off) = inst; inst->node = subNode; ak_heap_setpm(inst, subNode); ak_addSubNode(node, subNode, false); return subNode; } AK_EXPORT AkNode* ak_instanceMoveToSubNodeIfNeeded(AkNode * __restrict node, AkInstanceBase *inst) { void *instObj, *parentObject; AkCoordSys *coordSys, *instCoordSys; AkInstanceBase *insti; AkInstanceBase *instArray[] = {(AkInstanceBase *)node->geometry, (AkInstanceBase *)node->node, node->camera, node->light}; int i, instArrayLen; instObj = ak_instanceObject(inst); if (!instObj) goto ret; /* check if node coordsys is equal to instance */ coordSys = ak_getCoordSys(node); instCoordSys = ak_getCoordSys(instObj); if (!ak_hasCoordSys(instObj)) goto ret; parentObject = node->parent; if (!parentObject) parentObject = ak_mem_parent(node); if ((ak_hasCoordSys(node) && coordSys != instCoordSys) || (!node->parent && ak_typeid(parentObject) == AKT_SCENE)) goto move; /* check all instances coord sys in this node */ instArrayLen = AK_ARRAY_LEN(instArray); for (i = 0; i < instArrayLen; i++) { insti = instArray[i]; while (insti) { if (insti != inst) { void *instObji; instObji = ak_instanceObject(insti); if (!ak_hasCoordSys(instObji) || ak_getCoordSys(instObji) != instCoordSys) { goto move; } } insti = insti->next; } } ret: return NULL; move: return ak_instanceMoveToSubNode(node, inst); } ================================================ FILE: src/instance/list.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "../../include/ak/util.h" #include #include AK_EXPORT void ak_instanceListAdd(AkInstanceList *list, AkInstanceBase *inst) { AkHeap *heap; AkInstanceListItem *ili; heap = ak_heap_getheap(list); ili = ak_heap_alloc(heap, list, sizeof(*ili)); ili->instance = inst; ili->index = ++list->lastindex; list->count++; ili->prev = list->last; ili->next = NULL; if (!list->first) list->first = ili; if (list->last) list->last->next = ili; list->last = ili; } AK_EXPORT void ak_instanceListDel(AkInstanceList *list, AkInstanceListItem *item) { if (list->first == item) list->first = item->next; if (list->last == item) list->last = item->prev; if (item->prev) item->prev->next = item->next; if (item->next) item->next->prev = item->prev; list->count--; ak_free(item); } AK_EXPORT void ak_instanceListEmpty(AkInstanceList *list) { AkInstanceListItem *item, *tofree; item = list->first; while (item) { tofree = item; item = item->next; ak_free(tofree); } list->first = NULL; list->last = NULL; list->count = 0; list->lastindex = 0; } char* ak_instanceName(AkInstanceListItem *item) { AkHeap *heap; AkInstanceBase *inst; char *name, *objId; void *obj; uint32_t indexDigit; size_t idlen; inst = item->instance; assert(inst); heap = ak_heap_getheap(item); /* instance name */ if (inst->name) return ak_heap_strdup(heap, item, inst->name); /* we can use node.name or node.id for instance name */ if (inst->node) { char *nodeid; if (inst->node->name) return ak_heap_strdup(heap, item, inst->node->name); nodeid = ak_getId(inst->node); if (nodeid) return ak_heap_strdup(heap, item, nodeid); } obj = ak_instanceObject(inst); if (!obj) return NULL; objId = ak_getId(obj); idlen = strlen(objId); indexDigit = ak_digitsize(item->index); name = ak_heap_alloc(heap, item, idlen + indexDigit + 2); memcpy(name, objId, idlen); sprintf(name + idlen, "-%zu", item->index); name[idlen + indexDigit + 1] = '\0'; return name; } ================================================ FILE: src/io/3mf/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) add_subdirectory(imp) ================================================ FILE: src/io/3mf/README.md ================================================ # AssetKit: 3mf Status - [ ] 3mf XML ================================================ FILE: src/io/3mf/imp/3mf.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "3mf.h" #include "../../../miniz/miniz.h" AK_HIDE AkResult imp_3mf(AkDoc ** __restrict dest, const char * __restrict filepath) { // AkHeap *heap; // AkDoc *doc; // const xml_doc_t *xdoc; // xml_t *xml, *assetEl; // AkAssetInf *inf; // xml_attr_t *versionAttr; // void *xmlString, *zipped; // AkLibraries *libs; // FListItem *freeUsrData; // _3MFState dstVal, *dst; // size_t xmlSize, zipSize; // AkResult ret; // // if ((ret = ak_readfile(filepath, NULL, &zipped, &zipSize)) != AK_OK) // return ret; // // /* if we know uncompressed fixed size, otherwise use chunked uncompress and merge it */ // xmlSize = zipSize * 4; // xmlString = malloc(xmlSize); /* TODO: enough size? */ // // /* TODO: optimize memory */ // /* unzip */ // if (mz_uncompress(xmlString, &xmlSize, zipped, zipSize) != MZ_OK) { // goto err; // } // // if (mz_zip_reader_validate_mem(xmlString, xmlSize)) { // mz_zip_archive zip; // mz_zip_zero_struct(&zip); // if (!mz_zip_reader_init_mem(&zip, xmlString, xmlSize, 0)) { // free(xmlString); // return AK_ERR_IO; // } // // if (mz_zip_reader_is_file_a_directory(&zip, "3D/3dmodel.model", 0)) { // mz_zip_reader_end(&zip); // free(xmlString); // return AK_ERR_IO; // } // // xmlSize = mz_zip_reader_get_file_stat(&zip, "3D/3dmodel.model")->m_uncomp_size; // xmlString = realloc(xmlString, xmlSize); // if (!mz_zip_reader_extract_file_to_mem(&zip, // "3D/3dmodel.model", // xmlString, // xmlSize, // 0)) { // mz_zip_reader_end(&zip); // free(xmlString); // return AK_ERR_IO; // } // // mz_zip_reader_end(&zip); // } // // xdoc = xml_parse(xmlString, XML_PREFIXES | XML_READONLY); // if (!xdoc || !(xml = xdoc->root)) { // if (xdoc) { free((void *)xdoc); } // ak_releasefile(xmlString, xmlSize); // return AK_ERR; // } // // heap = ak_heap_new(NULL, NULL, NULL); // doc = ak_heap_calloc(heap, NULL, sizeof(*doc)); // // doc->inf = ak_heap_calloc(heap, doc, sizeof(*doc->inf)); // doc->inf->name = filepath; // doc->inf->dir = ak_path_dir(heap, doc, filepath); // doc->inf->flipImage = true; // doc->inf->ftype = AK_FILE_TYPE_COLLADA; // doc->coordSys = AK_YUP; /* Default */ // // /* for fixing skin and morph vertices */ // doc->reserved = rb_newtree_ptr(); // ((RBTree *)doc->reserved)->onFreeNode = ak_daeFreeDupl; // // if (doc->inf->dir) // doc->inf->dirlen = strlen(doc->inf->dir); // // ak_heap_setdata(heap, doc); // ak_id_newheap(heap); // // memset(&dstVal, 0, sizeof(dstVal)); // // dstVal.doc = doc; // dstVal.heap = heap; // dstVal.tempmem = ak_heap_alloc(heap, doc, sizeof(void*)); // dstVal.meshInfo = rb_newtree_ptr(); // dstVal.inputmap = rb_newtree_ptr(); // dstVal.texmap = rb_newtree_ptr(); // dstVal.instanceMap = rb_newtree_ptr(); // // dstVal.ctlrSkinMap = rb_newtree_ptr(); // dstVal.ctlrMorphMap = rb_newtree_ptr(); // // dst = &dstVal; // // dstVal.texmap->userData = dst; // // /* get version info */ // /* because it is current and most used version */ // dst->version = AK_COLLADA_VERSION_141; // if ((versionAttr = xmla(xml, _s_dae_version))) { // ak_enumpair *v; // // for (v = daeVersions; v->key; v++) { // if (!strncmp(v->key, versionAttr->val, versionAttr->valsize)) { // dst->version = v->val; // break; // } // } // } // // libs = &doc->lib; // assetEl = NULL; // xml = xml->val; // // /* with default Asset Parameters */ // assetEl = xml_elem(xml->parent, _s_dae_asset); // if ((inf = dae_asset(dst, assetEl, doc, &doc->inf->base))) { // doc->coordSys = inf->coordSys; // doc->unit = inf->unit; // } // // while (xml) { // if (xml_tag_eq(xml, _s_dae_lib_cameras)) { // dae_lib(dst, xml, _s_dae_camera, dae_cam, &libs->cameras); // } else if (xml_tag_eq(xml, _s_dae_lib_lights)) { // dae_lib(dst, xml, _s_dae_light, dae_light, &libs->lights); // } else if (xml_tag_eq(xml, _s_dae_lib_geometries)) { // dae_lib(dst, xml, _s_dae_geometry, dae_geom, &libs->geometries); // } else if (xml_tag_eq(xml, _s_dae_lib_effects)) { // dae_lib(dst, xml, _s_dae_effect, dae_effect, &libs->effects); // } else if (xml_tag_eq(xml, _s_dae_lib_images)) { // dae_lib(dst, xml, _s_dae_image, dae_image, &libs->libimages); // } else if (xml_tag_eq(xml, _s_dae_lib_materials)) { // dae_lib(dst, xml, _s_dae_material, dae_material, &libs->materials); // } else if (xml_tag_eq(xml, _s_dae_lib_controllers)) { // dae_lib(dst, xml, _s_dae_controller, dae_ctlr, &libs->controllers); // } else if (xml_tag_eq(xml, _s_dae_lib_visual_scenes)) { // dae_lib(dst, xml, _s_dae_visual_scene, dae_vscene, &libs->visualScenes); // } else if (xml_tag_eq(xml, _s_dae_lib_nodes)) { // dae_lib(dst, xml, _s_dae_node, dae_node2, &libs->nodes); // } else if (xml_tag_eq(xml, _s_dae_lib_animations)) { // dae_lib(dst, xml, _s_dae_animation, dae_anim, &libs->animations); // } else if (xml_tag_eq(xml, _s_dae_scene)) { // dae_scene(dst, xml); // } // xml = xml->next; // } // // *dest = doc; // // /* post-parse operations */ // dae_postscript(dst); // // /* cleanup up details */ // freeUsrData = dst->linkedUserData; // while (freeUsrData) { // void *tofree; // // if ((tofree = ak_userData(freeUsrData->data))) // ak_free(tofree); // // ak_heap_ext_rm(heap, ak__alignof(freeUsrData->data), AK_HEAP_NODE_FLAGS_USR); // freeUsrData = freeUsrData->next; // } // // ak_free(dstVal.tempmem); // // flist_sp_destroy(&dst->linkedUserData); // // rb_destroy(dstVal.meshInfo); // rb_destroy(dstVal.inputmap); // rb_destroy(dstVal.texmap); // rb_destroy(dstVal.instanceMap); // // flist_sp_destroy(&dstVal.vertMap); // // rb_destroy(dstVal.ctlrSkinMap); // rb_destroy(dstVal.ctlrMorphMap); // // if (xdoc) // free((void *)xdoc); // // if (xmlString) // ak_releasefile(xmlString, xmlSize); // // /* TODO: memory leak, free this RBTree*/ // /* rb_destroy(doc->reserved); */ // // return AK_OK; // //err: return AK_EBADF; } ================================================ FILE: src/io/3mf/imp/3mf.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _3mf_h #define _3mf_h #include "common.h" AK_HIDE AkResult imp_3mf(AkDoc ** __restrict dest, const char * __restrict filepath); #endif /* _3mf_h */ ================================================ FILE: src/io/3mf/imp/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/io/3mf/imp/common.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _3mf_common_h #define _3mf_common_h #include "../../../../include/ak/assetkit.h" #include "../../../../include/ak/url.h" #include "../../../common.h" #include "../../../utils.h" #include "../../../xml.h" #include #include #include #include typedef AK_ALIGN(16) struct _3MFState { AkHeap *heap; void *tempmem; AkDoc *doc; bool stop; } _3MFState; #endif /* _3mf_common_h */ ================================================ FILE: src/io/CMakeLists.txt ================================================ add_subdirectory(common) add_subdirectory(dae) add_subdirectory(gltf) add_subdirectory(obj) add_subdirectory(stl) add_subdirectory(ply) add_subdirectory(3mf) ================================================ FILE: src/io/common/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/io/common/postscript.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "postscript.h" #include "../../mesh/index.h" AK_HIDE void io_postscript(AkDoc * __restrict doc) { AkLibrary *geomLib; AkGeometry *geom; geomLib = doc->lib.geometries; while (geomLib) { geom = (void *)geomLib->chld; while (geom) { AkObject *primitive; primitive = geom->gdata; switch ((AkGeometryType)primitive->type) { case AK_GEOMETRY_MESH: { AkMesh *mesh; AkMeshEditHelper *edith; mesh = ak_objGet(primitive); ak_meshBeginEdit(mesh); edith = mesh->edith; edith->skipFixIndices = true; /* to do it once per mesh */ if (ak_opt_get(AK_OPT_TRIANGULATE)) ak_meshTriangulate(mesh); if (ak_opt_get(AK_OPT_GEN_NORMALS_IF_NEEDED)) if (ak_meshNeedsNormals(mesh)) ak_meshGenNormals(mesh); edith->skipFixIndices = false; ak_meshFixIndices(mesh); ak_meshEndEdit(mesh); if (ak_opt_get(AK_OPT_COMPUTE_BBOX)) ak_bbox_mesh(mesh); } default: break; } geom = (void *)geom->base.next; } geomLib = geomLib->next; } } ================================================ FILE: src/io/common/postscript.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ply_postscript_h #define ply_postscript_h #include "../../../include/ak/assetkit.h" AK_HIDE void io_postscript(AkDoc * __restrict doc); #endif /* ply_postscript_h */ ================================================ FILE: src/io/common/util.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "util.h" AK_HIDE AkMesh* ak_allocMesh(AkHeap * __restrict heap, AkLibrary * __restrict memp, AkGeometry ** __restrict geomLink) { AkGeometry *geom; AkObject *meshObj; AkMesh *mesh; /* create geometries */ geom = ak_heap_calloc(heap, memp, sizeof(*geom)); geom->materialMap = ak_map_new(ak_cmp_str); /* destroy heap with this object */ ak_setAttachedHeap(geom, geom->materialMap->heap); meshObj = ak_objAlloc(heap, geom, sizeof(AkMesh), AK_GEOMETRY_MESH, true); geom->gdata = meshObj; mesh = ak_objGet(meshObj); mesh->geom = geom; if (geomLink) *geomLink = geom; return mesh; } AK_HIDE AkInput* io_addInput(AkHeap * __restrict heap, AkDataContext * __restrict dctx, AkMeshPrimitive * __restrict prim, AkInputSemantic sem, const char * __restrict semRaw, AkComponentSize compSize, AkTypeId type, uint32_t offset) { AkDoc *doc; AkBuffer *buff; AkAccessor *acc; AkInput *inp; AkTypeDesc *typeDesc; int nComponents; doc = ak_heap_data(heap); typeDesc = ak_typeDesc(type); nComponents = (int)compSize; buff = ak_heap_calloc(heap, doc, sizeof(*buff)); buff->data = ak_heap_alloc(heap, buff, dctx->usedsize); buff->length = dctx->usedsize; ak_data_join(dctx, buff->data, 0, 0); flist_sp_insert(&doc->lib.buffers, buff); acc = ak_heap_calloc(heap, doc, sizeof(*acc)); acc->buffer = buff; acc->byteLength = buff->length; acc->byteStride = typeDesc->size * nComponents; acc->componentSize = compSize; acc->componentType = type; acc->bytesPerComponent = typeDesc->size; acc->componentCount = nComponents; acc->fillByteSize = typeDesc->size * nComponents; acc->count = (uint32_t)dctx->itemcount; inp = ak_heap_calloc(heap, prim, sizeof(*inp)); inp->accessor = acc; inp->semantic = sem; inp->semanticRaw = ak_heap_strdup(heap, inp, semRaw); inp->offset = offset; ak_retain(acc); inp->next = prim->input; prim->input = inp; prim->inputCount++; return inp; } AK_HIDE AkAccessor* io_acc(AkHeap * __restrict heap, AkDoc * __restrict doc, AkComponentSize compSize, AkTypeId type, uint32_t count, AkBuffer * __restrict buff) { AkAccessor *acc; AkTypeDesc *typeDesc; int nComponents; typeDesc = ak_typeDesc(type); nComponents = (int)compSize; acc = ak_heap_calloc(heap, doc, sizeof(*acc)); acc->buffer = buff; acc->byteLength = buff->length; acc->byteStride = typeDesc->size * nComponents; acc->componentSize = compSize; acc->componentType = type; acc->bytesPerComponent = typeDesc->size; acc->componentCount = nComponents; acc->fillByteSize = typeDesc->size * nComponents; acc->count = count; return acc; } AK_HIDE AkInput* io_input(AkHeap * __restrict heap, AkMeshPrimitive * __restrict prim, AkAccessor * __restrict acc, AkInputSemantic sem, const char * __restrict semRaw, uint32_t offset) { AkInput *inp; inp = ak_heap_calloc(heap, prim, sizeof(*inp)); inp->accessor = acc; inp->semantic = sem; inp->semanticRaw = ak_heap_strdup(heap, inp, semRaw); inp->offset = offset; inp->next = prim->input; prim->input = inp; prim->inputCount++; return inp; } ================================================ FILE: src/io/common/util.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef io_common_util_h #define io_common_util_h #include "../../../include/ak/assetkit.h" #include "../../common.h" #include "../../utils.h" #include "../../tree.h" #include "../../json.h" #include "../../data.h" #include #include AK_HIDE AkMesh* ak_allocMesh(AkHeap * __restrict heap, AkLibrary * __restrict memp, AkGeometry ** __restrict geomLink); AK_HIDE AkInput* io_addInput(AkHeap * __restrict heap, AkDataContext * __restrict dctx, AkMeshPrimitive * __restrict prim, AkInputSemantic sem, const char * __restrict semRaw, AkComponentSize compSize, AkTypeId type, uint32_t offset); AK_HIDE AkAccessor* io_acc(AkHeap * __restrict heap, AkDoc * __restrict doc, AkComponentSize compSize, AkTypeId type, uint32_t count, AkBuffer * __restrict buff); AK_HIDE AkInput* io_input(AkHeap * __restrict heap, AkMeshPrimitive * __restrict prim, AkAccessor * __restrict acc, AkInputSemantic sem, const char * __restrict semRaw, uint32_t offset); #endif /* io_common_util_h */ ================================================ FILE: src/io/dae/1.4/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/io/dae/1.4/dae14.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../../../../include/ak/assetkit.h" #include "../common.h" #include "dae14.h" AK_HIDE void dae14_loadjobs_add(DAEState * __restrict dst, void * __restrict parent, void * __restrict value, AkDae14LoadJobType type) { AkDae14LoadJob *job, *last; if (!dst) return; job = ak_heap_calloc(dst->heap, NULL, sizeof(*job)); job->parent = parent; job->type = type; job->value = value; last = dst->jobs14; if (last) last->prev = job; job->next = dst->jobs14; dst->jobs14 = job; } AK_HIDE void dae14_loadjobs_finish(DAEState * __restrict dst) { AkDae14LoadJob *job; job = dst->jobs14; while (job) { switch (job->type) { case AK_DAE14_LOADJOB_SURFACE: { AkNewParam *surfaceParam; AkDae14Surface *surface; AkContext ctx; memset(&ctx, 0, sizeof(ctx)); ctx.doc = dst->doc; surfaceParam = ak_sid_resolve(&ctx, job->value, NULL); if (surfaceParam) { AkSampler *sampler; AkInstanceBase *instanceImage; surface = surfaceParam->val->value; /* surface may already migrated to 1.5+ */ if (!surface->instanceImage) { AkImage *image; sampler = job->parent; /* convert initFrom to instance_image */ instanceImage = ak_heap_calloc(dst->heap, sampler, sizeof(*instanceImage)); if (surface->extra) { instanceImage->extra = surface->extra; surface->extra = NULL; ak_mem_setp(instanceImage->extra, instanceImage); } rb_insert(dst->instanceMap, sampler, instanceImage); ak_url_init_with_id(dst->heap->allocator, instanceImage, (char *)surface->initFrom->image, &instanceImage->url); /* TODO: */ // sampler->instanceImage = instanceImage; surface->instanceImage = instanceImage; /* convert other params to update/new image */ image = ak_instanceObject(instanceImage); if (image) { if (surface->initFrom) { image->initFrom->face = surface->initFrom->face; image->initFrom->mipIndex = surface->initFrom->mip; image->initFrom->arrayIndex = surface->initFrom->slice; } image->renderable = surface->initAsTarget; } } } break; } } job = job->next; } /* cleanup */ job = dst->jobs14; while (job) { AkDae14LoadJob *tofree; tofree = job; switch (job->type) { case AK_DAE14_LOADJOB_SURFACE: { AkNewParam *surfaceParam; AkDae14Surface *surface; AkContext ctx; memset(&ctx, 0, sizeof(ctx)); ctx.doc = dst->doc; surfaceParam = ak_sid_resolve(&ctx, job->value, NULL); if (surfaceParam) { void *parentOfParam; parentOfParam = ak_mem_parent(surfaceParam); surface = surfaceParam->val->value; ak_free(surface); if (surfaceParam->prev) surfaceParam->prev->next = surfaceParam->next; if (surfaceParam->next) surfaceParam->next->prev = surfaceParam->prev; switch (ak_typeid(parentOfParam)) { case AKT_EFFECT: { AkEffect *effect; effect = parentOfParam; if (effect->newparam == surfaceParam) effect->newparam = surfaceParam->next; break; } case AKT_PROFILE: { AkProfile *profile; profile = parentOfParam; if (profile->newparam == surfaceParam) profile->newparam = surfaceParam->next; break; } default: break; } ak_free(surfaceParam); } break; } } job = job->next; ak_free(tofree->value); ak_free(tofree); } dst->jobs14 = NULL; } ================================================ FILE: src/io/dae/1.4/dae14.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae14_h #define dae14_h #include "../common.h" typedef enum AkDae14LoadJobType { AK_DAE14_LOADJOB_SURFACE = 1 } AkDae14LoadJobType; typedef struct AkDae14LoadJob { struct AkDae14LoadJob *prev; struct AkDae14LoadJob *next; void *parent; void *value; AkDae14LoadJobType type; } AkDae14LoadJob; typedef struct AkDae14SurfaceFrom { const char *image; AkUInt mip; AkUInt slice; AkFace face; } AkDae14SurfaceFrom; typedef struct AkDae14Surface { AkInstanceBase *instanceImage; AkTree *extra; const char *format; AkImageFormat formatHint; AkImageSize size; float viewportRatio[2]; int mipLevels; bool mipmapGenerate; /* initializers */ bool initAsNull; bool initAsTarget; AkDae14SurfaceFrom *initFrom; } AkDae14Surface; AK_HIDE void dae14_loadjobs_add(DAEState * __restrict dst, void * __restrict parent, void * __restrict value, AkDae14LoadJobType type); AK_HIDE void dae14_loadjobs_finish(DAEState * __restrict dst); #endif /* dae14_h */ ================================================ FILE: src/io/dae/1.4/image.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "image.h" #include "../core/asset.h" #include "../core/enum.h" AK_HIDE void dae14_fxMigrateImg(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkDoc *doc; AkImage *img; AkInitFrom *initFrom; const char *format; AkLibrary *lib; heap = dst->heap; doc = dst->doc; if (memp) lib = memp; else lib = ak_libImageFirstOrCreat(doc); img = ak_heap_calloc(heap, lib, sizeof(*img)); initFrom = ak_heap_calloc(heap, img, sizeof(*img->initFrom)); img->initFrom = initFrom; xmla_setid(xml, heap, img); img->name = xmla_strdup_by(xml, heap, _s_dae_name, img); format = xmla_strdup_by(xml, heap, _s_dae_format, img->initFrom); initFrom->mipsGenerate = false; /* 1.4's default, 1.5's is true */ initFrom->depth = xmla_u32(xmla(xml, _s_dae_depth), 0); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_asset)) { (void)dae_asset(dst, xml, img, NULL); } else if (xml_tag_eq(xml, _s_dae_data)) { AkHexData *hex; hex = ak_heap_calloc(heap, initFrom, sizeof(*hex)); hex->format = format; ak_heap_setpm((void *)format, hex); if (hex->format) { hex->hexval = xml_strdup(xml, heap, hex); img->initFrom->hex = hex; } } else if (xml_tag_eq(xml, _s_dae_init_from)) { img->initFrom->ref = xml_strdup(xml, heap, initFrom); } else if (xml_tag_eq(xml, _s_dae_extra)) { img->extra = tree_fromxml(heap, img, xml); } xml = xml->next; } if (!memp) ak_libInsertInto(lib, img, -1, offsetof(AkImage, next)); } ================================================ FILE: src/io/dae/1.4/image.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_14_image_h #define ak_14_image_h #include "../common.h" AK_HIDE void dae14_fxMigrateImg(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); #endif /* ak_14_image_h */ ================================================ FILE: src/io/dae/1.4/surface.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "surface.h" #include "../core/asset.h" #include "../core/enum.h" AK_HIDE AkDae14Surface* dae14_surface(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkDae14Surface *surf; xml_attr_t *att; const xml_t *sval; heap = dst->heap; surf = ak_heap_calloc(heap, memp, sizeof(*surf)); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_init_from)) { AkDae14SurfaceFrom *initFrom; initFrom = ak_heap_calloc(heap, surf, sizeof(*heap)); initFrom->mip = xmla_u32(xmla(xml, _s_dae_mip), 0); initFrom->slice = xmla_u32(xmla(xml, _s_dae_slice), 0); if ((att = xmla(xml, _s_dae_face))) { initFrom->face = dae_face(att); } else { initFrom->face = AK_FACE_POSITIVE_Y; } initFrom->image = xml_strdup(xml, heap, initFrom); surf->initFrom = initFrom; } else if (xml_tag_eq(xml, _s_dae_init_as_target)) { surf->initAsTarget = true; /* becuse the element exists */ } else if (xml_tag_eq(xml, _s_dae_format)) { surf->format = xml_strdup(xml, heap, surf); } else if (xml_tag_eq(xml, _s_dae_format_hint)) { AkImageFormat *format; xml_t *xfmt; format = ak_heap_calloc(heap, memp, sizeof(*format)); xfmt = xml->val; while (xfmt) { if (xml_tag_eq(xfmt, _s_dae_channels) && (sval = xmls(xfmt))) { format->channel = dae_enumChannel(sval->val, sval->valsize); } else if (xml_tag_eq(xfmt, _s_dae_range) && (sval = xmls(xfmt))) { format->range = dae_range(sval->val, sval->valsize); } else if (xml_tag_eq(xfmt, _s_dae_precision) && (sval = xmls(xfmt))) { format->precision = dae_precision(sval->val, sval->valsize); } else if (xml_tag_eq(xfmt, _s_dae_option)) { format->space = xml_strdup(xml, heap, format); } else if (xml_tag_eq(xfmt, _s_dae_exact)) { format->exact = xml_strdup(xml, heap, format); } xfmt = xfmt->next; } } else if (xml_tag_eq(xml, _s_dae_size) && (sval = xmls(xml))) { AkUInt size[3]; xml_strtoui_fast(sval, size, 3); surf->size.width = size[0]; surf->size.height = size[1]; surf->size.depth = size[2]; } else if (xml_tag_eq(xml, _s_dae_viewport_ratio) && (sval = xmls(xml))) { xml_strtof_fast(sval, surf->viewportRatio, 2); } else if (xml_tag_eq(xml, _s_dae_mip_levels) && (sval = xmls(xml))) { surf->mipLevels = (int)strtol(sval->val, NULL, 10); } else if (xml_tag_eq(xml, _s_dae_mipmap_generate) && (sval = xmls(xml))) { surf->mipmapGenerate = (bool)strtol(sval->val, NULL, 10); } else if (xml_tag_eq(xml, _s_dae_extra)) { surf->extra = tree_fromxml(heap, surf, xml); } xml = xml->next; } return surf; } ================================================ FILE: src/io/dae/1.4/surface.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_surface_h #define ak_surface_h #include "../common.h" #include "dae14.h" AK_HIDE AkDae14Surface* dae14_surface(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); #endif /* ak_surface_h */ ================================================ FILE: src/io/dae/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) add_subdirectory(core) add_subdirectory(fx) add_subdirectory(fixup) add_subdirectory(bugfix) add_subdirectory(1.4) add_subdirectory(brep) ================================================ FILE: src/io/dae/README.md ================================================ # AssetKit: COLLADA Status - [x] Single Interface for glTF and COLLADA - [x] COLLADA 1.4 / 1.4.1 - [x] COLLADA 1.5 - [x] Object-based Asset support - Resolving Asset support for an element - [x] Fix / Convert UP axis to any other - [x] ID resolving - [x] SID resolving - [x] Bugfix some DAE files - [x] Convert angles to Radians - [x] Convert Multi-Index indices to Single-Index indices by keeping primitive indices - [x] Options to Generate Mesh Normals - [x] Option to Triangulate Polygons - [x] Libraries support - [x] Geometries - [x] Meshes (Triangles, Polygons, Lines) - [x] Brep - Curves, Nurbs, Solids... - [x] Nodes - [x] Instances (instance Geometry, Light, Camera...) - [x] Simplified Controller mechanism into **node->morpher** and **node->skinner** - [x] Fix camera node transform - [x] **bind_material** and bind vertex input support to bind material dynamically - [x] Scenes - Active Scene - Visual Scenes - [x] Cameras - [x] Lights - [x] Materials - [x] Images - [x] Samplers - [x] Textures - [x] Common Profile - [x] Phong, Blinn, Lambert and Constant - ⚠️ Other profiles were supported, but currently they are removed. But multi profile is still supported. We can support other profiles in the future. - [x] Transparency (A_ONE, RGB_ONE, A_ZERO, RGB_ZERO) - [x] Skin - [x] Morph - [x] Animations - [x] Extra - [x] Load external DAE files and cache them - [ ] Parse MathML formulas - [ ] Physics - [ ] Kinematics - [ ] ZAE - [ ] More to work on... ### Trademarks glTF and COLLADA and their logos are trademarks of Khronos Group. ================================================ FILE: src/io/dae/brep/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/io/dae/brep/brep.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "brep.h" #include "../core/source.h" #include "../core/vert.h" #include "curve.h" #include "surface.h" #include "topology.h" AK_HIDE AkObject* dae_brep(DAEState * __restrict dst, xml_t * __restrict xml, AkGeometry * __restrict geom) { AkObject *obj; AkBoundryRep *brep; AkHeap *heap; AkSource *source; heap = dst->heap; xml = xml->val; obj = ak_objAlloc(heap, geom, sizeof(*brep), AK_GEOMETRY_BREP, true); brep = ak_objGet(obj); brep->geom = geom; while (xml) { if (xml_tag_eq(xml, _s_dae_curves)) { brep->curves = dae_curves(dst, xml, obj); } else if (xml_tag_eq(xml, _s_dae_surface_curves)) { brep->surfaceCurves = dae_curves(dst, xml, obj); } else if (xml_tag_eq(xml, _s_dae_surfaces)) { brep->surfaces = dae_surfaces(dst, xml, obj); } else if (xml_tag_eq(xml, _s_dae_source)) { if ((source = dae_source(dst, xml, NULL, 0))) { source->next = brep->source; brep->source = source; } } else if (xml_tag_eq(xml, _s_dae_vertices)) { brep->vertices = dae_vert(dst, xml, obj); } else if (xml_tag_eq(xml, _s_dae_edges)) { brep->edges = dae_edges(dst, xml, obj); } else if (xml_tag_eq(xml, _s_dae_wires)) { brep->wires = dae_wires(dst, xml, obj); } else if (xml_tag_eq(xml, _s_dae_faces)) { brep->faces = dae_faces(dst, xml, obj); } else if (xml_tag_eq(xml, _s_dae_pcurves)) { brep->pcurves = dae_pcurves(dst, xml, obj); } else if (xml_tag_eq(xml, _s_dae_shells)) { brep->shells = dae_shells(dst, xml, obj); } else if (xml_tag_eq(xml, _s_dae_solids)) { brep->solids = dae_solids(dst, xml, obj); } else if (xml_tag_eq(xml, _s_dae_extra)) { brep->extra = tree_fromxml(heap, brep, xml); } xml = xml->next; } return obj; } ================================================ FILE: src/io/dae/brep/brep.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_brep_h #define dae_brep_h #include "../common.h" AK_HIDE AkObject* dae_brep(DAEState * __restrict dst, xml_t * __restrict xml, AkGeometry * __restrict geom); #endif /* dae_brep_h */ ================================================ FILE: src/io/dae/brep/curve.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "curve.h" #include "nurb.h" #include "../../../array.h" AK_HIDE AkCurve* dae_curve(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkObject *obj; AkCurve *curve; const xml_t *sval; heap = dst->heap; curve = ak_heap_calloc(heap, memp, sizeof(*curve)); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_line)) { AkLine *line; xml_t *xline; xline = xml->val; obj = ak_objAlloc(heap, curve, sizeof(*line), AK_CURVE_LINE, true); line = ak_objGet(obj); while (xline) { if (xml_tag_eq(xline, _s_dae_origin) && (sval = xmls(xline))) { xml_strtof_fast(sval, line->origin, 3); } else if (xml_tag_eq(xline, _s_dae_direction) && (sval = xmls(xline))) { xml_strtof_fast(sval, line->direction, 3); } else if (xml_tag_eq(xline, _s_dae_extra)) { line->extra = tree_fromxml(heap, obj, xml); } xline = xline->next; } curve->curve = obj; } else if (xml_tag_eq(xml, _s_dae_circle)) { AkCircle *circ; xml_t *xcirc; xcirc = xml->val; obj = ak_objAlloc(heap, curve, sizeof(*circ), AK_CURVE_CIRCLE, true); circ = ak_objGet(obj); while (xcirc) { if (xml_tag_eq(xcirc, _s_dae_radius) && xcirc->val) { circ->radius = xml_float(xcirc->val, 0.0f); } else if (xml_tag_eq(xcirc, _s_dae_extra)) { circ->extra = tree_fromxml(heap, obj, xml); } xcirc = xcirc->next; } curve->curve = obj; } else if (xml_tag_eq(xml, _s_dae_ellipse)) { AkEllipse *ell; xml_t *xell; xell = xml->val; obj = ak_objAlloc(heap, curve, sizeof(*ell), AK_CURVE_ELLIPSE, true); ell = ak_objGet(obj); while (xell) { if (xml_tag_eq(xell, _s_dae_radius) && (sval = xmls(xell))) { xml_strtof_fast(sval, (AkFloat *)&ell->radius, 2); } else if (xml_tag_eq(xell, _s_dae_extra)) { ell->extra = tree_fromxml(heap, obj, xml); } xell = xell->next; } curve->curve = obj; } else if (xml_tag_eq(xml, _s_dae_parabola)) { AkParabola *par; xml_t *xpar; xpar = xml->val; obj = ak_objAlloc(heap, curve, sizeof(*par), AK_CURVE_PARABOLA, true); par = ak_objGet(obj); while (xpar) { if (xml_tag_eq(xpar, _s_dae_focal) && xpar->val) { par->focal = xml_float(xpar->val, 0.0f); } else if (xml_tag_eq(xpar, _s_dae_extra)) { par->extra = tree_fromxml(heap, obj, xml); } xpar = xpar->next; } curve->curve = obj; } else if (xml_tag_eq(xml, _s_dae_hyperbola)) { AkHyperbola *hpar; xml_t *xhpar; xhpar = xml->val; obj = ak_objAlloc(heap, curve, sizeof(*hpar), AK_CURVE_HYPERBOLA, true); hpar = ak_objGet(obj); while (xhpar) { if (xml_tag_eq(xhpar, _s_dae_radius) && (sval = xmls(xhpar))) { xml_strtof_fast(sval, (AkFloat *)&hpar->radius, 2); } else if (xml_tag_eq(xhpar, _s_dae_extra)) { hpar->extra = tree_fromxml(heap, obj, xml); } xhpar = xhpar->next; } curve->curve = obj; } else if (xml_tag_eq(xml, _s_dae_nurbs)) { curve->curve = dae_nurbs(dst, xml, curve); } else if (xml_tag_eq(xml, _s_dae_orient) && (sval = xmls(xml))) { AkFloatArrayL *orient; AkResult ret; ret = xml_strtof_arrayL(heap, curve, sval, &orient); if (ret == AK_OK) { orient->next = curve->orient; curve->orient = orient; } } else if (xml_tag_eq(xml, _s_dae_origin) && (sval = xmls(xml))) { xml_strtof_fast(sval, curve->origin, 3); } xml = xml->next; } return curve; } AK_HIDE AkCurves* dae_curves(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkCurves *curves; AkCurve *curve; heap = dst->heap; curves = ak_heap_calloc(heap, memp, sizeof(*curves)); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_curve)) { if ((curve = dae_curve(dst, xml, curves))) { curve->next = curves->curve; curves->curve = curve; } } else if (xml_tag_eq(xml, _s_dae_extra)) { curves->extra = tree_fromxml(heap, curves, xml); } xml = xml->next; } return curves; } ================================================ FILE: src/io/dae/brep/curve.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_brep_curve_h #define dae_brep_curve_h #include "../common.h" AK_HIDE AkCurve* dae_curve(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); AK_HIDE AkCurves* dae_curves(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); #endif /* dae_brep_curve_h */ ================================================ FILE: src/io/dae/brep/nurb.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "nurb.h" #include "../core/vert.h" #include "../core/source.h" #include "../core/enum.h" AK_HIDE AkObject* dae_nurbs(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkObject *obj; AkNurbs *nurbs; AkSource *source; heap = dst->heap; obj = ak_objAlloc(heap, memp, sizeof(*nurbs), 0, true); nurbs = ak_objGet(obj); nurbs->degree = xmla_u32(xmla(xml, _s_dae_degree), 0); nurbs->closed = xmla_u32(xmla(xml, _s_dae_closed), 0); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_source)) { if ((source = dae_source(dst, xml, NULL, 0))) { source->next = nurbs->source; nurbs->source = source; } } else if (xml_tag_eq(xml, _s_dae_control_vertices)) { nurbs->cverts = dae_vert(dst, xml, obj); } else if (xml_tag_eq(xml, _s_dae_extra)) { nurbs->extra = tree_fromxml(heap, nurbs, xml); } xml = xml->next; } return obj; } AK_HIDE AkObject* dae_nurbs_surface(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkObject *obj; AkNurbsSurface *nurbsSurface; AkSource *source; heap = dst->heap; obj = ak_objAlloc(heap, memp, sizeof(*nurbsSurface), 0, true); nurbsSurface = ak_objGet(obj); nurbsSurface->degree_u = xmla_u32(xmla(xml, _s_dae_degree_u), 0); nurbsSurface->degree_v = xmla_u32(xmla(xml, _s_dae_degree_v), 0); nurbsSurface->closed_u = xmla_u32(xmla(xml, _s_dae_closed_u), 0); nurbsSurface->closed_v = xmla_u32(xmla(xml, _s_dae_closed_v), 0); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_source)) { if ((source = dae_source(dst, xml, NULL, 0))) { source->next = nurbsSurface->source; nurbsSurface->source = source; } } else if (xml_tag_eq(xml, _s_dae_control_vertices)) { nurbsSurface->cverts = dae_vert(dst, xml, obj); } else if (xml_tag_eq(xml, _s_dae_extra)) { nurbsSurface->extra = tree_fromxml(heap, nurbsSurface, xml); } xml = xml->next; } return obj; } ================================================ FILE: src/io/dae/brep/nurb.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_brep_nurbs_h #define dae_brep_nurbs_h #include "../common.h" AK_HIDE AkObject* dae_nurbs(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); AK_HIDE AkObject* dae_nurbs_surface(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); #endif /* dae_brep_nurbs_h */ ================================================ FILE: src/io/dae/brep/surface.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "surface.h" #include "nurb.h" #include "curve.h" #include "../../../array.h" AK_HIDE AkSurface* dae_surface(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkSurface *surf; AkObject *obj; const xml_t *sval; heap = dst->heap; surf = ak_heap_calloc(heap, memp, sizeof(*surf)); sid_set(xml, heap, surf); surf->name = xmla_strdup_by(xml, heap, _s_dae_name, surf); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_cone)) { AkCone *cone; xml_t *xcone; obj = ak_objAlloc(heap, surf, sizeof(*cone), AK_SURFACE_CONE, true); cone = ak_objGet(obj); xcone = xml->val; while (xcone) { if (xml_tag_eq(xcone, _s_dae_radius) && xcone->val) { cone->radius = xml_float(xcone->val, 0.0f); } else if (xml_tag_eq(xcone, _s_dae_angle) && xcone->val) { cone->angle = xml_float(xcone->val, 0.0f); } else if (xml_tag_eq(xcone, _s_dae_extra)) { cone->extra = tree_fromxml(heap, obj, xcone); } xcone = xcone->next; } surf->surface = obj; } else if (xml_tag_eq(xml, _s_dae_plane)) { AkPlane *plane; xml_t *xplane; obj = ak_objAlloc(heap, surf, sizeof(*plane), AK_SURFACE_PLANE, true); plane = ak_objGet(obj); xplane = xml->val; while (xplane) { if (xml_tag_eq(xplane, _s_dae_equation) && (sval = xmls(xplane))) { xml_strtof_fast(sval, (AkFloat *)&plane->equation, 4); } else if (xml_tag_eq(xplane, _s_dae_extra)) { plane->extra = tree_fromxml(heap, obj, xplane); } xplane = xplane->next; } surf->surface = obj; } else if (xml_tag_eq(xml, _s_dae_cylinder)) { AkCylinder *clyn; xml_t *xclyn; obj = ak_objAlloc(heap, surf, sizeof(*clyn), AK_SURFACE_CYLINDER, true); clyn = ak_objGet(obj); xclyn = xml->val; while (xclyn) { if (xml_tag_eq(xclyn, _s_dae_radius) && (sval = xmls(xclyn))) { xml_strtof_fast(sval, (AkFloat *)&clyn->radius, 2); } else if (xml_tag_eq(xclyn, _s_dae_extra)) { clyn->extra = tree_fromxml(heap, obj, xclyn); } xclyn = xclyn->next; } surf->surface = obj; } else if (xml_tag_eq(xml, _s_dae_nurbs_surface)) { surf->surface = dae_nurbs_surface(dst, xml, surf); } else if (xml_tag_eq(xml, _s_dae_sphere)) { AkSphere *sphere; xml_t *xsphere; obj = ak_objAlloc(heap, surf, sizeof(*sphere), AK_SURFACE_SPHERE, true); sphere = ak_objGet(obj); xsphere = xml->val; while (xsphere) { if (xml_tag_eq(xsphere, _s_dae_radius) && xsphere->val) { sphere->radius = xml_float(xsphere->val, 0.0f); } else if (xml_tag_eq(xsphere, _s_dae_extra)) { sphere->extra = tree_fromxml(heap, obj, xsphere); } xsphere = xsphere->next; } surf->surface = obj; } else if (xml_tag_eq(xml, _s_dae_torus)) { AkTorus *torus; xml_t *xtorus; obj = ak_objAlloc(heap, surf, sizeof(*xtorus), AK_SURFACE_TORUS, true); torus = ak_objGet(obj); xtorus = xml->val; while (xtorus) { if (xml_tag_eq(xtorus, _s_dae_radius) && (sval = xmls(xtorus))) { xml_strtof_fast(sval, (AkFloat *)&torus->radius, 2); } else if (xml_tag_eq(xtorus, _s_dae_extra)) { torus->extra = tree_fromxml(heap, obj, xtorus); } xtorus = xtorus->next; } surf->surface = obj; } else if (xml_tag_eq(xml, _s_dae_swept_surface)) { AkObject *obj; AkSweptSurface *sweptSurface; xml_t *xswept; obj = ak_objAlloc(heap, surf, sizeof(*sweptSurface), AK_SURFACE_SWEPT_SURFACE, true); sweptSurface = ak_objGet(obj); xswept = xml->val; while (xswept) { if (xml_tag_eq(xswept, _s_dae_curve)) { sweptSurface->curve = dae_curve(dst, xswept, obj); } else if (xml_tag_eq(xswept, _s_dae_direction) && (sval = xmls(xml))) { xml_strtof_fast(sval, (AkFloat *)&sweptSurface->direction, 3); } else if (xml_tag_eq(xswept, _s_dae_origin) && (sval = xmls(xml))) { xml_strtof_fast(sval, (AkFloat *)&sweptSurface->origin, 3); } else if (xml_tag_eq(xswept, _s_dae_axis) && (sval = xmls(xml))) { xml_strtof_fast(sval, (AkFloat *)&sweptSurface->axis, 3); } else if (xml_tag_eq(xswept, _s_dae_extra)) { sweptSurface->extra = tree_fromxml(heap, obj, xswept); } xswept = xswept->next; } } else if (xml_tag_eq(xml, _s_dae_orient) && (sval =xmls(xml))) { AkFloatArrayL *orient; AkResult ret; ret = xml_strtof_arrayL(heap, surf, sval, &orient); if (ret == AK_OK) { orient->next = surf->orient; surf->orient = orient; } } else if (xml_tag_eq(xml, _s_dae_origin) && (sval = xmls(xml))) { xml_strtof_fast(sval, surf->origin, 3); } xml = xml->next; } return surf; } AK_HIDE AkSurfaces* dae_surfaces(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkSurfaces *surfaces; AkSurface *surface; heap = dst->heap; surfaces = ak_heap_calloc(heap, memp, sizeof(*surfaces)); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_surface)) { if ((surface = dae_surface(dst, xml, memp))) { surface->next = surfaces->surface; surfaces->surface = surface; } } else if (xml_tag_eq(xml, _s_dae_extra)) { surfaces->extra = tree_fromxml(heap, surfaces, xml); } xml = xml->next; } return surfaces; } ================================================ FILE: src/io/dae/brep/surface.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_brep_surface_h #define dae_brep_surface_h #include "../common.h" AK_HIDE AkSurface* dae_surface(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); AK_HIDE AkSurfaces* dae_surfaces(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); #endif /* dae_brep_surface_h */ ================================================ FILE: src/io/dae/brep/topology.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "topology.h" #include "../core/enum.h" #include "../../../array.h" AK_HIDE AkEdges* dae_edges(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkEdges *edges; const xml_t *sval; heap = dst->heap; edges = ak_heap_calloc(heap, memp, sizeof(*edges)); xmla_setid(xml, heap, edges); edges->name = xmla_strdup_by(xml, heap, _s_dae_name, edges); edges->count = xmla_u32(xmla(xml, _s_dae_count), 0); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_input)) { AkInput *inp; inp = ak_heap_calloc(heap, edges, sizeof(*inp)); inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp); if (!inp->semanticRaw) { ak_free(inp); } else { AkEnum inputSemantic; inputSemantic = dae_semantic(inp->semanticRaw); if (inputSemantic < 0) inputSemantic = AK_INPUT_OTHER; inp->semantic = inputSemantic; inp->offset = xmla_u32(xmla(xml, _s_dae_offset), 0); inp->set = xmla_u32(xmla(xml, _s_dae_set), 0); if ((uint32_t)inp->semantic != AK_INPUT_SEMANTIC_VERTEX) { AkURL *url; inp->semantic = dae_semantic(inp->semanticRaw); inp->next = edges->input; edges->input = inp; edges->inputCount++; url = url_from(xml, _s_dae_source, edges); rb_insert(dst->inputmap, inp, url); } else { ak_free(inp); } } } else if (xml_tag_eq(xml, _s_dae_p) && (sval = xmls(xml))) { AkUIntArray *prims; AkResult ret; ret = xml_strtoui_array(heap, edges, sval, &prims); if (ret == AK_OK) edges->primitives = prims; } else if (xml_tag_eq(xml, _s_dae_extra)) { edges->extra = tree_fromxml(heap, edges, xml); } xml = xml->next; } return edges; } AK_HIDE AkWires* dae_wires(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkWires *wires; const xml_t *sval; heap = dst->heap; wires = ak_heap_calloc(heap, memp, sizeof(*wires)); xmla_setid(xml, heap, wires); wires->name = xmla_strdup_by(xml, heap, _s_dae_name, wires); wires->count = xmla_u32(xmla(xml, _s_dae_count), 0); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_input)) { AkInput *inp; inp = ak_heap_calloc(heap, wires, sizeof(*inp)); inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp); if (!inp->semanticRaw) { ak_free(inp); } else { AkEnum inputSemantic; inputSemantic = dae_semantic(inp->semanticRaw); if (inputSemantic < 0) inputSemantic = AK_INPUT_OTHER; inp->semantic = inputSemantic; inp->offset = xmla_u32(xmla(xml, _s_dae_offset), 0); inp->set = xmla_u32(xmla(xml, _s_dae_set), 0); if ((uint32_t)inp->semantic != AK_INPUT_SEMANTIC_VERTEX) { AkURL *url; inp->semantic = dae_semantic(inp->semanticRaw); inp->next = wires->input; wires->input = inp; wires->inputCount++; url = url_from(xml, _s_dae_source, wires); rb_insert(dst->inputmap, inp, url); } else { ak_free(inp); } } } else if (xml_tag_eq(xml, _s_dae_vcount) && (sval = xmls(xml))) { AkUIntArray *vcount; AkResult ret; ret = xml_strtoui_array(heap, wires, sval, &vcount); if (ret == AK_OK) wires->vcount = vcount; } else if (xml_tag_eq(xml, _s_dae_p) && (sval = xmls(xml))) { AkUIntArray *prims; AkResult ret; ret = xml_strtoui_array(heap, wires, sval, &prims); if (ret == AK_OK) wires->primitives = prims; } else if (xml_tag_eq(xml, _s_dae_extra)) { wires->extra = tree_fromxml(heap, wires, xml); } xml = xml->next; } return wires; } AK_HIDE AkFaces* dae_faces(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkFaces *faces; const xml_t *sval; heap = dst->heap; faces = ak_heap_calloc(heap, memp, sizeof(*faces)); xmla_setid(xml, heap, faces); faces->name = xmla_strdup_by(xml, heap, _s_dae_name, faces); faces->count = xmla_u32(xmla(xml, _s_dae_count), 0); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_input)) { AkInput *inp; inp = ak_heap_calloc(heap, faces, sizeof(*inp)); inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp); if (!inp->semanticRaw) { ak_free(inp); } else { AkEnum inputSemantic; inputSemantic = dae_semantic(inp->semanticRaw); if (inputSemantic < 0) inputSemantic = AK_INPUT_OTHER; inp->semantic = inputSemantic; inp->offset = xmla_u32(xmla(xml, _s_dae_offset), 0); inp->set = xmla_u32(xmla(xml, _s_dae_set), 0); if ((uint32_t)inp->semantic != AK_INPUT_SEMANTIC_VERTEX) { AkURL *url; inp->semantic = dae_semantic(inp->semanticRaw); inp->next = faces->input; faces->input = inp; faces->inputCount++; url = url_from(xml, _s_dae_source, faces); rb_insert(dst->inputmap, inp, url); } else { ak_free(inp); } } } else if (xml_tag_eq(xml, _s_dae_vcount) && (sval = xmls(xml))) { AkUIntArray *vcount; AkResult ret; ret = xml_strtoui_array(heap, faces, sval, &vcount); if (ret == AK_OK) faces->vcount = vcount; } else if (xml_tag_eq(xml, _s_dae_p) && (sval = xmls(xml))) { AkUIntArray *prims; AkResult ret; ret = xml_strtoui_array(heap, faces, sval, &prims); if (ret == AK_OK) faces->primitives = prims; } else if (xml_tag_eq(xml, _s_dae_extra)) { faces->extra = tree_fromxml(heap, faces, xml); } xml = xml->next; } return faces; } AK_HIDE AkPCurves* dae_pcurves(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkPCurves *pcurves; const xml_t *sval; heap = dst->heap; pcurves = ak_heap_calloc(heap, memp, sizeof(*pcurves)); xmla_setid(xml, heap, pcurves); pcurves->name = xmla_strdup_by(xml, heap, _s_dae_name, pcurves); pcurves->count = xmla_u32(xmla(xml, _s_dae_count), 0); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_input)) { AkInput *inp; inp = ak_heap_calloc(heap, pcurves, sizeof(*inp)); inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp); if (!inp->semanticRaw) { ak_free(inp); } else { AkEnum inputSemantic; inputSemantic = dae_semantic(inp->semanticRaw); if (inputSemantic < 0) inputSemantic = AK_INPUT_OTHER; inp->semantic = inputSemantic; inp->offset = xmla_u32(xmla(xml, _s_dae_offset), 0); inp->set = xmla_u32(xmla(xml, _s_dae_set), 0); if ((uint32_t)inp->semantic != AK_INPUT_SEMANTIC_VERTEX) { AkURL *url; inp->semantic = dae_semantic(inp->semanticRaw); inp->next = pcurves->input; pcurves->input = inp; pcurves->inputCount++; url = url_from(xml, _s_dae_source, pcurves); rb_insert(dst->inputmap, inp, url); } else { ak_free(inp); } } } else if (xml_tag_eq(xml, _s_dae_vcount) && (sval = xmls(xml))) { AkUIntArray *vcount; AkResult ret; ret = xml_strtoui_array(heap, pcurves, sval, &vcount); if (ret == AK_OK) pcurves->vcount = vcount; } else if (xml_tag_eq(xml, _s_dae_p) && (sval = xmls(xml))) { AkUIntArray *prims; AkResult ret; ret = xml_strtoui_array(heap, pcurves, sval, &prims); if (ret == AK_OK) pcurves->primitives = prims; } else if (xml_tag_eq(xml, _s_dae_extra)) { pcurves->extra = tree_fromxml(heap, pcurves, xml); } xml = xml->next; } return pcurves; } AK_HIDE AkShells* dae_shells(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkShells *shells; const xml_t *sval; heap = dst->heap; shells = ak_heap_calloc(heap, memp, sizeof(*shells)); xmla_setid(xml, heap, shells); shells->name = xmla_strdup_by(xml, heap, _s_dae_name, shells); shells->count = xmla_u32(xmla(xml, _s_dae_count), 0); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_input)) { AkInput *inp; inp = ak_heap_calloc(heap, shells, sizeof(*inp)); inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp); if (!inp->semanticRaw) { ak_free(inp); } else { AkEnum inputSemantic; inputSemantic = dae_semantic(inp->semanticRaw); if (inputSemantic < 0) inputSemantic = AK_INPUT_OTHER; inp->semantic = inputSemantic; inp->offset = xmla_u32(xmla(xml, _s_dae_offset), 0); inp->set = xmla_u32(xmla(xml, _s_dae_set), 0); if ((uint32_t)inp->semantic != AK_INPUT_SEMANTIC_VERTEX) { AkURL *url; inp->semantic = dae_semantic(inp->semanticRaw); inp->next = shells->input; shells->input = inp; shells->inputCount++; url = url_from(xml, _s_dae_source, shells); rb_insert(dst->inputmap, inp, url); } else { ak_free(inp); } } } else if (xml_tag_eq(xml, _s_dae_vcount) && (sval = xmls(xml))) { AkUIntArray *vcount; AkResult ret; ret = xml_strtoui_array(heap, shells, sval, &vcount); if (ret == AK_OK) shells->vcount = vcount; } else if (xml_tag_eq(xml, _s_dae_p) && (sval = xmls(xml))) { AkUIntArray *prims; AkResult ret; ret = xml_strtoui_array(heap, shells, sval, &prims); if (ret == AK_OK) shells->primitives = prims; } else if (xml_tag_eq(xml, _s_dae_extra)) { shells->extra = tree_fromxml(heap, shells, xml); } xml = xml->next; } return shells; } AK_HIDE AkSolids* dae_solids(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp){ AkHeap *heap; AkSolids *solids; const xml_t *sval; heap = dst->heap; solids = ak_heap_calloc(heap, memp, sizeof(*solids)); xmla_setid(xml, heap, solids); solids->name = xmla_strdup_by(xml, heap, _s_dae_name, solids); solids->count = xmla_u32(xmla(xml, _s_dae_count), 0); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_input)) { AkInput *inp; inp = ak_heap_calloc(heap, solids, sizeof(*inp)); inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp); if (!inp->semanticRaw) { ak_free(inp); } else { AkEnum inputSemantic; inputSemantic = dae_semantic(inp->semanticRaw); if (inputSemantic < 0) inputSemantic = AK_INPUT_OTHER; inp->semantic = inputSemantic; inp->offset = xmla_u32(xmla(xml, _s_dae_offset), 0); inp->set = xmla_u32(xmla(xml, _s_dae_set), 0); if ((uint32_t)inp->semantic != AK_INPUT_SEMANTIC_VERTEX) { AkURL *url; inp->semantic = dae_semantic(inp->semanticRaw); inp->next = solids->input; solids->input = inp; solids->inputCount++; url = url_from(xml, _s_dae_source, solids); rb_insert(dst->inputmap, inp, url); } else { ak_free(inp); } } } else if (xml_tag_eq(xml, _s_dae_vcount) && (sval = xmls(xml))) { AkUIntArray *vcount; AkResult ret; ret = xml_strtoui_array(heap, solids, sval, &vcount); if (ret == AK_OK) solids->vcount = vcount; } else if (xml_tag_eq(xml, _s_dae_p) && (sval = xmls(xml))) { AkUIntArray *prims; AkResult ret; ret = xml_strtoui_array(heap, solids, sval, &prims); if (ret == AK_OK) solids->primitives = prims; } else if (xml_tag_eq(xml, _s_dae_extra)) { solids->extra = tree_fromxml(heap, solids, xml); } xml = xml->next; } return solids; } ================================================ FILE: src/io/dae/brep/topology.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_brep_topology_h #define dae_brep_topology_h #include "../common.h" AK_HIDE AkEdges* dae_edges(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); AK_HIDE AkWires* dae_wires(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); AK_HIDE AkFaces* dae_faces(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); AK_HIDE AkPCurves* dae_pcurves(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); AK_HIDE AkShells* dae_shells(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); AK_HIDE AkSolids* dae_solids(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); #endif /* dae_brep_topology_h */ ================================================ FILE: src/io/dae/bugfix/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/io/dae/bugfix/scenekit.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "scenekit.h" #include #include static bool dae_strcase_contains(const char * __restrict str, const char * __restrict needle) { const char *s, *n, *match; if (!str || !needle || !needle[0]) return false; for (; *str; str++) { s = str; n = needle; match = str; while (*s && *n && tolower((unsigned char)*s) == tolower((unsigned char)*n)) { s++; n++; } if (!*n) return true; str = match; } return false; } static bool dae_scenekit_authored(AkDoc * __restrict doc) { AkContributor *contr; if (!doc || !doc->inf) return false; for (contr = doc->inf->base.contributor; contr; contr = contr->next) { if (dae_strcase_contains(contr->authoringTool, "scenekit")) return true; } return false; } static bool dae_colordesc_has_texture(DAEState * __restrict dst, AkColorDesc * __restrict color) { if (!color) return false; return color->texture || (dst->texmap && rb_find(dst->texmap, color)); } static bool dae_scenekit_is_red_fill(AkMaterial * __restrict material, AkTechniqueFxCommon * __restrict common) { AkColor *color; if (!material || !material->name || strcmp(material->name, _s_dae_material) != 0 || !common || !common->diffuse || !common->diffuse->color) return false; color = common->diffuse->color; return color->rgba.R > 0.45f && color->rgba.R < 0.90f && color->rgba.G < 0.40f && color->rgba.B < 0.50f && color->rgba.R > color->rgba.G + 0.20f && color->rgba.R > color->rgba.B + 0.15f && color->rgba.A > 0.95f; } static AkTechniqueFxCommon* dae_scenekit_primitive_common(DAEState * __restrict dst, AkInstanceGeometry * __restrict instGeom, AkMeshPrimitive * __restrict prim, AkInstanceMaterial ** __restrict instMat) { AkEffect *effect; AK__UNUSED(dst); *instMat = NULL; if (!instGeom->bindMaterial) return NULL; effect = ak_effectForBindMaterial(instGeom->bindMaterial, prim, instMat); if (!effect) return NULL; return ak_getProfileTechniqueCommon(effect); } static bool dae_scenekit_primitive_has_texture(DAEState * __restrict dst, AkInstanceGeometry * __restrict instGeom, AkMeshPrimitive * __restrict prim) { AkTechniqueFxCommon *common; AkInstanceMaterial *instMat; common = dae_scenekit_primitive_common(dst, instGeom, prim, &instMat); return common && dae_colordesc_has_texture(dst, common->diffuse); } static bool dae_scenekit_is_textured_twin(DAEState * __restrict dst, AkInstanceGeometry * __restrict instGeom, AkMeshPrimitive * __restrict prim, AkMeshPrimitive * __restrict other) { return other && other != prim && other->type == AK_PRIMITIVE_TRIANGLES && other->nPolygons == prim->nPolygons && dae_scenekit_primitive_has_texture(dst, instGeom, other); } static bool dae_scenekit_should_drop_primitive(DAEState * __restrict dst, AkInstanceGeometry * __restrict instGeom, AkMeshPrimitive * __restrict prim, AkMeshPrimitive * __restrict prev, AkMeshPrimitive * __restrict next) { AkTechniqueFxCommon *common; AkInstanceMaterial *instMat; AkMaterial *material; if (prim->type != AK_PRIMITIVE_TRIANGLES || prim->nPolygons == 0) return false; common = dae_scenekit_primitive_common(dst, instGeom, prim, &instMat); if (!common || dae_colordesc_has_texture(dst, common->diffuse)) return false; material = instMat ? ak_instanceObject(&instMat->base) : NULL; return dae_scenekit_is_red_fill(material, common) && (dae_scenekit_is_textured_twin(dst, instGeom, prim, prev) || dae_scenekit_is_textured_twin(dst, instGeom, prim, next)); } static void dae_scenekit_unmap_material(AkGeometry * __restrict geom, AkMeshPrimitive * __restrict prim) { AkMapItem *head, *item; if (!geom || !geom->materialMap || !prim->bindmaterial) return; head = ak_map_findm(geom->materialMap, (void *)prim->bindmaterial); if (!head) return; item = head->data; while (item) { if (item->data == prim) { if (item->prev) item->prev->next = item->next; else head->data = item->next; if (item->next) item->next->prev = item->prev; return; } item = item->next; } } static void dae_scenekit_fix_mesh(DAEState * __restrict dst, AkInstanceGeometry * __restrict instGeom, AkGeometry * __restrict geom, AkMesh * __restrict mesh) { AkMeshPrimitive *prim, *prev, *next; prev = NULL; prim = mesh->primitive; while (prim) { next = prim->next; if (dae_scenekit_should_drop_primitive(dst, instGeom, prim, prev, next)) { if (prev) prev->next = next; else mesh->primitive = next; dae_scenekit_unmap_material(geom, prim); prim->next = NULL; if (mesh->primitiveCount > 0) mesh->primitiveCount--; } else { prev = prim; } prim = next; } } static void dae_scenekit_fix_node(DAEState * __restrict dst, AkNode * __restrict node) { AkInstanceGeometry *instGeom; AkGeometry *geom; AkObject *geomData; for (; node; node = node->next) { for (instGeom = node->geometry; instGeom; instGeom = (AkInstanceGeometry *)instGeom->base.next) { geom = ak_instanceObject(&instGeom->base); if (!geom || ak_typeid(geom) != AKT_GEOMETRY) continue; geomData = geom->gdata; if (!geomData || (AkGeometryType)geomData->type != AK_GEOMETRY_MESH) continue; dae_scenekit_fix_mesh(dst, instGeom, geom, ak_objGet(geomData)); } if (node->chld) dae_scenekit_fix_node(dst, node->chld); } } AK_HIDE void dae_bugfix_scenekit_backfaces(DAEState * __restrict dst) { AkVisualScene *vscn; if (!dst || !dst->doc || !ak_opt_get(AK_OPT_BUGFIXES) || !dae_scenekit_authored(dst->doc) || !dst->doc->lib.visualScenes) return; for (vscn = (void *)dst->doc->lib.visualScenes->chld; vscn; vscn = (void *)vscn->base.next) { dae_scenekit_fix_node(dst, vscn->node); } } ================================================ FILE: src/io/dae/bugfix/scenekit.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_bugfix_scenekit_h #define dae_bugfix_scenekit_h #include "../common.h" AK_HIDE void dae_bugfix_scenekit_backfaces(DAEState * __restrict dst); #endif /* dae_bugfix_scenekit_h */ ================================================ FILE: src/io/dae/bugfix/transp.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "transp.h" #include #include AK_HIDE void dae_bugfix_transp(AkTransparent * __restrict transp) { AkContributor *contr; char *tool; if (!(contr = ak_getAssetInfo(transp, offsetof(AkAssetInf, contributor))) || !(tool = (char *)contr->authoringTool)) return; tool = ak_tolower(strdup(tool)); /* fix old SketchUp transparency bug */ if (strstr(tool, _s_dae_sketchup)) { int major, minor, patch; if(sscanf(tool, "%*[^0123456789]%d%*[. ]%d%*[. ]%d", &major, &minor, &patch)) { /* don't flip >= 7.1.1 */ if (major <= 7 && minor < 2 && patch < 1) { transp->amount = 1.0f - transp->amount; } } } /* _s_dae_sketchup */ free(tool); } ================================================ FILE: src/io/dae/bugfix/transp.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_bugfix_transp_h #define dae_bugfix_transp_h #include "../common.h" AK_HIDE void dae_bugfix_transp(AkTransparent * __restrict transp); #endif /* dae_bugfix_transp_h */ ================================================ FILE: src/io/dae/bugfix/url.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_bugfix_url_h #define dae_bugfix_url_h #include #ifdef _MSC_VER # include # define dae_alloca _alloca #else # include # define dae_alloca alloca #endif /* * Industry bug: many DAE exporters (Blender, Maya, some glTF→DAE * converters) emit `<... source="some_id"/>` instead of the spec-correct * `<... source="#some_id"/>` for internal references. Without the * fragment marker AssetKit's url machinery routes these strings through * the external-resource fetch path, which never resolves and (worse) * NULL-derefs in ak_path_fragment. * * Policy: AssetKit is lenient toward asset-side mistakes that have an * unambiguous fix. When the URL has no '#' AND no path/URL separator * (`/`, `\`, scheme `://`), treat it as a bare internal id and prepend * the missing '#'. * * Implementation: macro instead of an inline function so alloca() lives * in the caller's stack frame — the normalized buffer must outlive * ak_url_init, which strdups the input internally (its '#' branch). No * extra heap allocation for the common malformed case; well-formed * input is a no-op. */ #define DAE_URL_INIT_FIXED(memp_, urlstring_, url_) \ do { \ char *_dbu_str = (urlstring_); \ if (_dbu_str && _dbu_str[0] != '#' \ && !strchr(_dbu_str, '/') \ && !strchr(_dbu_str, '\\') \ && !strstr(_dbu_str, "://")) { \ size_t _dbu_len = strlen(_dbu_str); \ char *_dbu_norm = dae_alloca(_dbu_len + 2); \ _dbu_norm[0] = '#'; \ memcpy(_dbu_norm + 1, _dbu_str, _dbu_len + 1); \ ak_free(_dbu_str); \ _dbu_str = _dbu_norm; \ } \ ak_url_init((memp_), _dbu_str, (url_)); \ } while (0) #endif /* dae_bugfix_url_h */ ================================================ FILE: src/io/dae/common.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_common_h #define dae_common_h #include "../../../include/ak/assetkit.h" #include "../../../include/ak/url.h" #include "../../common.h" #include "../../utils.h" #include "../../tree.h" #include "../../xml.h" #include "strpool.h" #include #include #include #include #include "bugfix/url.h" #ifndef AK_INPUT_SEMANTIC_VERTEX # define AK_INPUT_SEMANTIC_VERTEX 100001 #endif typedef enum AkCOLLADAVersion { AK_COLLADA_VERSION_150 = 150, AK_COLLADA_VERSION_141 = 141, AK_COLLADA_VERSION_140 = 140 } AkCOLLADAVersion; typedef enum AkControllerType { AK_CONTROLLER_MORPH = 1, AK_CONTROLLER_SKIN = 2 } AkControllerType; typedef struct AkURLQueue { AkURL *url; struct AkURLQueue *next; } AkURLQueue; typedef struct AkDAEVerticesMapItem { AkInput *inp; AkMeshPrimitive *prim; } AkDAEVerticesMapItem; typedef AK_ALIGN(16) struct DAEState { AkHeap *heap; void *tempmem; void *jobs14; AkDoc *doc; AkURLQueue *urlQueue; FListItem *accessors; FListItem *instCtlrs; FListItem *inputs; FListItem *toRadiansSampelers; FListItem *linkedUserData; RBTree *meshInfo; RBTree *inputmap; RBTree *texmap; RBTree *instanceMap; FListItem *vertMap; /* maps base AkGeometry* → AkMorph*. Populated by dae_fixup_ctlr's MORPH case so that the postscript orphan-attach pass can wrap uses of the base mesh in an AkInstanceMorph (DAE exporters — especially glTF→DAE — frequently emit a morph controller without ever wrapping the geometry in , leaving the morph dangling otherwise). */ RBTree *meshTargets; AkSource *sources; AkCOLLADAVersion version; bool stop; } DAEState; typedef struct AkDaeMeshInfo { AkInput *pos; size_t nVertex; } AkDaeMeshInfo; typedef struct AkDAETextureRef { const char *texture; const char *texcoord; AkTextureColorSpace colorSpace; AkTextureChannels channels; } AkDAETextureRef; typedef struct AkNewParam { /* const char * sid; */ struct AkNewParam *prev; struct AkNewParam *next; const char *semantic; AkValue *val; } AkNewParam; typedef struct AkController { /* const char * id; */ AkOneWayIterBase base; const char *name; void *data; AkTree *extra; AkControllerType type; } AkController; typedef struct AkInstanceController { AkInstanceBase base; AkURL geometry; struct AkNode **joints; AkBindMaterial *bindMaterial; struct FListItem *reserved; } AkInstanceController; typedef struct AkAccessorDAE { struct AkDataParam *param; AkURL source; size_t offset; uint32_t stride; uint32_t bound; } AkAccessorDAE; typedef struct AkSkinJointsDAE { AkInput *joints; AkInput *invBindMatrix; } AkSkinJointsDAE; typedef struct AkSkinWeightsDAE { AkInput *joints; AkInput *weights; AkUIntArray *v; } AkSkinWeightsDAE; typedef struct AkSkinDAE { AkURL baseGeom; AkSource *source; AkTree *extra; AkSkinJointsDAE joints; AkSkinWeightsDAE weights; uint32_t inputCount; } AkSkinDAE; typedef struct AkMorphDAE { AkURL baseGeom; AkSource *source; AkTree *extra; AkInput *input; } AkMorphDAE; AK_INLINE void sid_seta(xml_t * __restrict xml, AkHeap * __restrict heap, void * __restrict memnode, void * __restrict memptr) { const char *sid; if (!(sid = xmla_strdup_by(xml, heap, _s_dae_sid, memnode))) return; ak_sid_seta(memnode, memptr, sid); } AK_INLINE void sid_set(xml_t * __restrict xml, AkHeap * __restrict heap, void * __restrict memnode) { const char *sid; if (!(sid = xmla_strdup_by(xml, heap, _s_dae_sid, memnode))) return; ak_sid_set(memnode, sid); } AK_INLINE void url_set(DAEState * __restrict dst, xml_t * __restrict xml, const char * __restrict name, void * __restrict memp, AkURL * __restrict url) { AkURLQueue *urlQueue; xml_attr_t *att; if (!(att = xmla(xml, name)) || !att->val) { url->reserved = NULL; url->url = NULL; return; } /* DAE_URL_INIT_FIXED normalizes bare ids ("foo" → "#foo") emitted by non-conforming exporters via stack alloca; well-formed input is a no-op. See bugfix/url.h. */ DAE_URL_INIT_FIXED(memp, xmla_strdup(att, dst->heap, memp), url); urlQueue = dst->heap->allocator->malloc(sizeof(*urlQueue)); urlQueue->next = dst->urlQueue; urlQueue->url = url; dst->urlQueue = urlQueue; } AK_INLINE void dae_vertmap_add(DAEState * __restrict dst, AkInput * __restrict inp, AkMeshPrimitive * __restrict prim) { AkDAEVerticesMapItem *item; if (!inp || !prim) { return; } item = ak_heap_calloc(dst->heap, dst->tempmem, sizeof(*item)); item->inp = inp; item->prim = prim; flist_sp_insert(&dst->vertMap, item); } AK_INLINE AkURL* url_from(xml_t * __restrict xml, const char * __restrict name, void * __restrict memp) { AkHeap *heap; AkURL *url; xml_attr_t *att; if (!(att = xmla(xml, name)) || ! att->val) return NULL; heap = ak_heap_getheap(memp); url = ak_heap_calloc(heap, memp, sizeof(*url)); /* see url_set: normalize bare-id industry bug before init. */ DAE_URL_INIT_FIXED(memp, xmla_strdup(att, heap, memp), url); return url; } AK_EXPORT AkGeometry* ak_baseGeometry(AkURL * __restrict baseurl); #endif /* dae_common_h */ ================================================ FILE: src/io/dae/core/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/io/dae/core/anim.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "anim.h" #include "asset.h" #include "source.h" #include "enum.h" AK_HIDE void* dae_anim(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkAnimation *anim; heap = dst->heap; anim = ak_heap_calloc(heap, memp, sizeof(*anim)); xmla_setid(xml, heap, anim); anim->name = xmla_strdup_by(xml, heap, _s_dae_name, anim); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_asset)) { (void)dae_asset(dst, xml, anim, NULL); } else if (xml_tag_eq(xml, _s_dae_source)) { AkSource *source; /* store interpolation in char */ if ((source = dae_source(dst, xml, dae_animInterp, AKT_UBYTE))) { source->next = anim->source; anim->source = source; } } else if (xml_tag_eq(xml, _s_dae_sampler)) { AkAnimSampler *samp; if ((samp = dae_animSampler(dst, xml, anim))) { samp->base.next = (void *)anim->sampler; anim->sampler = samp; } } else if (xml_tag_eq(xml, _s_dae_channel)) { AkChannel *ch; if ((ch = dae_channel(dst, xml, anim))) { ch->next = anim->channel; anim->channel = ch; } } else if (xml_tag_eq(xml, _s_dae_animation)) { AkAnimation *subAnim; if ((subAnim = dae_anim(dst, xml, anim))) { subAnim->base.next = (AkOneWayIterBase *)anim->animation; anim->animation = subAnim; } } else if (xml_tag_eq(xml, _s_dae_extra)) { anim->extra = tree_fromxml(heap, anim, xml); } xml = xml->next; } return anim; } AK_HIDE AkAnimSampler* dae_animSampler(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkAnimSampler *samp; AkInput *inp; xml_attr_t *att; heap = dst->heap; samp = ak_heap_calloc(heap, memp, sizeof(*samp)); xmla_setid(xml, heap, samp); if ((att = xmla(xml, _s_dae_pre_behavior))) samp->pre = dae_animBehavior(att); if ((att = xmla(xml, _s_dae_post_behavior))) samp->post = dae_animBehavior(att); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_input)) { inp = ak_heap_calloc(heap, samp, sizeof(*inp)); inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp); if (!inp->semanticRaw) { ak_free(inp); } else { AkURL *url; AkEnum inputSemantic; inputSemantic = dae_semantic(inp->semanticRaw); if (inputSemantic < 0) inputSemantic = AK_INPUT_OTHER; inp->semantic = inputSemantic; inp->offset = xmla_u32(xmla(xml, _s_dae_offset), 0); inp->semantic = dae_semantic(inp->semanticRaw); url = url_from(xml, _s_dae_source, memp); rb_insert(dst->inputmap, inp, url); /* check if there are angles, because they are in degress, will be converted to radians, we will wait to load whole dae file because all sources may not be loaded at this time */ if (inp->semantic == AK_INPUT_OUTPUT) flist_sp_insert(&dst->toRadiansSampelers, samp); switch (inp->semantic) { case AK_INPUT_INPUT: samp->inputInput = inp; break; case AK_INPUT_OUTPUT: samp->outputInput = inp; break; case AK_INPUT_IN_TANGENT: samp->inTangentInput = inp; break; case AK_INPUT_OUT_TANGENT: samp->outTangentInput = inp; break; case AK_INPUT_INTERPOLATION: samp->interpInput = inp; break; default: break; } inp->next = samp->input; samp->input = inp; } } xml = xml->next; } return samp; } AK_HIDE AkChannel* dae_channel(DAEState * __restrict dst, void * __restrict xml, void * __restrict memp) { AkChannel *ch; ch = ak_heap_calloc(dst->heap, memp, sizeof(*ch)); url_set(dst, xml, _s_dae_source, ch, &ch->source); ch->target = xmla_strdup_by(xml, dst->heap, _s_dae_target, ch); return ch; } ================================================ FILE: src/io/dae/core/anim.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_anim_h #define dae_anim_h #include "../common.h" AK_HIDE void* dae_anim(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); AK_HIDE AkAnimSampler* dae_animSampler(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); AK_HIDE AkChannel* dae_channel(DAEState * __restrict dst, void * __restrict xml, void * __restrict memp); #endif /* dae_anim_h */ ================================================ FILE: src/io/dae/core/asset.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "asset.h" AK_HIDE AkAssetInf* dae_asset(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp, AkAssetInf * __restrict inf) { AkHeap *heap; xml_attr_t *attr; AkContributor *cont; xml_t *xcont; char *val; heap = dst->heap; if (!inf) inf = ak_heap_alloc(heap, memp, sizeof(*inf)); /* DAE default definitions */ /* CoordSys is Y_UP */ inf->coordSys = AK_YUP; /* Unit is 1 meter */ inf->unit = ak_heap_calloc(heap, inf, sizeof(*inf->unit)); inf->unit->dist = 1.0; inf->unit->name = ak_heap_strdup(heap, inf->unit, _s_dae_meter); if (xml) { xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_contributor)) { cont = ak_heap_calloc(heap, inf, sizeof(*cont)); xcont = xml->val; while (xcont) { if (xml_tag_eq(xcont, _s_dae_author)) cont->author = xml_strdup(xcont, heap, inf); else if (xml_tag_eq(xcont, _s_dae_author_email)) cont->authorEmail = xml_strdup(xcont, heap, inf); else if (xml_tag_eq(xcont, _s_dae_author_website)) cont->authorWebsite = xml_strdup(xcont, heap, inf); else if (xml_tag_eq(xcont, _s_dae_authoring_tool)) cont->authoringTool = xml_strdup(xcont, heap, inf); else if (xml_tag_eq(xcont, _s_dae_comments)) cont->comments = xml_strdup(xcont, heap, inf); else if (xml_tag_eq(xcont, _s_dae_copyright)) cont->copyright = xml_strdup(xcont, heap, inf); else if (xml_tag_eq(xcont, _s_dae_source_data)) cont->sourceData = xml_strdup(xcont, heap, inf); xcont = xcont->next; } inf->contributor = cont; } else if (xml_tag_eq(xml, _s_dae_created)) { if ((val = xml_strdup(xml, heap, inf))) { memset(&xml[xml->valsize], '\0', xml->valsize); inf->created = ak_parse_date(val, NULL); ak_free(val); } } else if (xml_tag_eq(xml, _s_dae_modified)) { if ((val = xml_strdup(xml, heap, inf))) { memset(&xml[xml->valsize], '\0', xml->valsize); inf->modified = ak_parse_date(val, NULL); ak_free(val); } } else if (xml_tag_eq(xml, _s_dae_keywords)) { inf->keywords = xml_strdup(xml, heap, inf); } else if (xml_tag_eq(xml, _s_dae_revision)) { inf->revision = xml_strdup(xml, heap, inf); } else if (xml_tag_eq(xml, _s_dae_subject)) { inf->subject = xml_strdup(xml, heap, inf); } else if (xml_tag_eq(xml, _s_dae_title)) { inf->title = xml_strdup(xml, heap, inf); } else if (xml_tag_eq(xml, _s_dae_unit)) { if ((attr = xmla(xml, _s_dae_name))) inf->unit->name = xmla_strdup(attr, heap, inf->unit); if ((attr = xmla(xml, _s_dae_meter))) { /* memset((char *)attr->val +attr->valsize, '\0', 1); */ inf->unit->dist = xmla_double(attr, 0.0); } } else if (xml_tag_eq(xml, _s_dae_up_axis)) { if ((val = xml_strdup(xml, heap, inf))) { if (strcasecmp(val, _s_dae_z_up) == 0) inf->coordSys = AK_ZUP; else if (strcasecmp(val, _s_dae_x_up) == 0) inf->coordSys = AK_XUP; else inf->coordSys = AK_YUP; ak_free(val); } } else if (xml_tag_eq(xml, _s_dae_extra)) { inf->extra = tree_fromxml(heap, inf, xml); } xml = xml->next; } } /* else -> default */ *(AkAssetInf **)ak_heap_ext_add(heap, ak__alignof(memp), AK_HEAP_NODE_FLAGS_INF) = inf; return inf; } ================================================ FILE: src/io/dae/core/asset.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_asset_h #define dae_asset_h #include "../common.h" AK_HIDE AkAssetInf* dae_asset(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp, AkAssetInf * __restrict pinf); #endif /* dae_asset_h */ ================================================ FILE: src/io/dae/core/cam.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "cam.h" #include "asset.h" #include "techn.h" AK_HIDE void* dae_cam(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkCamera *cam; AkTechnique *tq; heap = dst->heap; cam = ak_heap_calloc(heap, memp, sizeof(*cam)); cam->name = xmla_strdup_by(xml, heap, _s_dae_name, cam); xmla_setid(xml, heap, cam); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_asset)) { (void)dae_asset(dst, xml, cam, NULL); } else if (xml_tag_eq(xml, _s_dae_optics)) { AkOptics *optics; xml_t *xoptics; optics = ak_heap_calloc(heap, cam, sizeof(*optics)); xoptics = xml->val; while (xoptics) { if (xml_tag_eq(xoptics, _s_dae_techniquec)) { xml_t *xtech, *xtechv; xtech = xoptics->val; while (xtech) { if (xml_tag_eq(xtech, _s_dae_perspective)) { AkPerspective *persp; persp = ak_heap_calloc(heap, optics, sizeof(*persp)); xtechv = xtech->val; while (xtechv) { if (xml_tag_eq(xtechv, _s_dae_xfov)) { sid_seta(xtechv, heap, persp, &persp->xfov); persp->xfov = glm_rad(xml_float(xtechv, 0.0f)); } else if (xml_tag_eq(xtechv, _s_dae_yfov)) { sid_seta(xtechv, heap, persp, &persp->yfov); persp->yfov = glm_rad(xml_float(xtechv, 0.0f)); } else if (xml_tag_eq(xtechv, _s_dae_aspect_ratio)) { sid_seta(xtechv, heap, persp, &persp->aspectRatio); persp->aspectRatio = xml_float(xtechv, 0.0f); } else if (xml_tag_eq(xtechv, _s_dae_znear)) { sid_seta(xtechv, heap, persp, &persp->znear); persp->znear = xml_float(xtechv, 0.0f); } else if (xml_tag_eq(xtechv, _s_dae_zfar)) { sid_seta(xtechv, heap, persp, &persp->zfar); persp->zfar = xml_float(xtechv, 0.0f); } xtechv = xtechv->next; } persp->base.type = AK_PROJECTION_PERSPECTIVE; if (!persp->aspectRatio && persp->yfov && persp->xfov) { persp->aspectRatio = persp->xfov / persp->yfov; } else if (!persp->yfov && persp->aspectRatio && persp->xfov) { persp->yfov = persp->xfov / persp->aspectRatio; } else if (!persp->xfov && persp->aspectRatio && persp->yfov) { persp->xfov = persp->yfov * persp->aspectRatio; } optics->tcommon = &persp->base; } else if (xml_tag_eq(xtech, _s_dae_orthographic)) { AkOrthographic *ortho; ortho = ak_heap_calloc(heap, optics, sizeof(*ortho)); xtechv = xtech->val; while (xtechv) { if (xml_tag_eq(xtechv, _s_dae_xmag)) { sid_seta(xtechv, heap, ortho, &ortho->xmag); ortho->xmag = xml_float(xtechv, 0.0f); } else if (xml_tag_eq(xtechv, _s_dae_ymag)) { sid_seta(xtechv, heap, ortho, &ortho->ymag); ortho->ymag = xml_float(xtechv, 0.0f); } else if (xml_tag_eq(xtechv, _s_dae_aspect_ratio)) { sid_seta(xtechv, heap, ortho, &ortho->aspectRatio); ortho->aspectRatio = xml_float(xtechv, 0.0f); } else if (xml_tag_eq(xtechv, _s_dae_znear)) { sid_seta(xtechv, heap, ortho, &ortho->znear); ortho->znear = xml_float(xtechv, 0.0f); } else if (xml_tag_eq(xtechv, _s_dae_zfar)) { sid_seta(xtechv, heap, ortho, &ortho->zfar); ortho->zfar = xml_float(xtechv, 0.0f); } xtechv = xtechv->next; } ortho->base.type = AK_PROJECTION_ORTHOGRAPHIC; if (!ortho->aspectRatio && ortho->ymag && ortho->xmag) { ortho->aspectRatio = ortho->xmag / ortho->ymag; } else if (!ortho->ymag && ortho->aspectRatio && ortho->xmag) { ortho->ymag = ortho->xmag / ortho->aspectRatio; } else if (!ortho->xmag && ortho->aspectRatio && ortho->ymag) { ortho->xmag = ortho->ymag * ortho->aspectRatio; } optics->tcommon = &ortho->base; } xtech = xtech->next; } } else if (xml_tag_eq(xoptics, _s_dae_technique)) { tq = dae_techn(xoptics, heap, optics); tq->next = optics->technique; optics->technique = tq; } xoptics = xoptics->next; } cam->optics = optics; } else if (xml_tag_eq(xml, _s_dae_imager)) { AkImager *imager; xml_t *ximager; imager = ak_heap_calloc(heap, cam, sizeof(*imager)); ximager = xml->val; while (ximager) { if (xml_tag_eq(ximager, _s_dae_technique)) { tq = dae_techn(ximager, heap, imager); tq->next = imager->technique; imager->technique = tq; } else if (xml_tag_eq(ximager, _s_dae_extra)) { imager->extra = tree_fromxml(heap, imager, xml); } ximager = ximager->next; } cam->imager = imager; } else if (xml_tag_eq(xml, _s_dae_extra)) { cam->extra = tree_fromxml(heap, cam, xml); } xml = xml->next; } return cam; } ================================================ FILE: src/io/dae/core/cam.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_camera_h #define dae_camera_h #include "../common.h" AK_HIDE void* dae_cam(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); #endif /* dae_camera_h */ ================================================ FILE: src/io/dae/core/color.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "color.h" AK_HIDE void dae_color(xml_t * __restrict xml, void * memparent, bool read_sid, bool stack, AkColor * dest) { AkHeap *heap; void *memp; unsigned long c; heap = ak_heap_getheap(memparent); memp = stack ? memparent : dest; if (read_sid) sid_set(xml, heap, memp); c = xml_strtof_fast(xml->val, dest->vec, 4); if (c > 4) c = 4; if (c > 0) { do { dest->vec[4 - c--] = 1.0f; } while (c > 0); } glm_vec4_clamp(dest->vec, 0.0f, 1.0f); /* BUGIFX: assume alpha=0 is an export BUG */ if (dest->vec[3] < 0.125) { dest->vec[3] = 1.0f - dest->vec[3]; } ak_sRGB_linear(dest); } ================================================ FILE: src/io/dae/core/color.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_color_h #define dae_color_h #include "../common.h" AK_HIDE void dae_color(xml_t * __restrict xml, void * memparent, bool read_sid, bool stack, AkColor * dest); #endif /* dae_color_h */ ================================================ FILE: src/io/dae/core/ctlr.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ctlr.h" #include "skin.h" #include "morph.h" #include "../core/asset.h" AK_HIDE void* dae_ctlr(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkController *ctlr; heap = dst->heap; ctlr = ak_heap_calloc(heap, memp, sizeof(*ctlr)); ctlr->name = xmla_strdup_by(xml, heap, _s_dae_name, ctlr); xmla_setid(xml, heap, ctlr); ak_setypeid(ctlr, AKT_CONTROLLER); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_asset)) { (void)dae_asset(dst, xml, ctlr, NULL); } else if (xml_tag_eq(xml, _s_dae_skin)) { ctlr->data = dae_skin(dst, xml, ctlr); ctlr->type = AK_CONTROLLER_SKIN; } else if (xml_tag_eq(xml, _s_dae_morph)) { ctlr->data = dae_morph(dst, xml, ctlr); ctlr->type = AK_CONTROLLER_MORPH; } else if (xml_tag_eq(xml, _s_dae_extra)) { ctlr->extra = tree_fromxml(heap, ctlr, xml); } xml = xml->next; } return ctlr; } ================================================ FILE: src/io/dae/core/ctlr.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_controller_h #define dae_controller_h #include "../common.h" AK_HIDE void* dae_ctlr(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); #endif /* dae_controller_h */ ================================================ FILE: src/io/dae/core/enum.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "enum.h" #include "../../../common.h" #include "../common.h" #include AK_HIDE AkEnum dae_semantic(const char * name) { AkEnum val; long glenums_len, i; if (!name) return AK_INPUT_OTHER; dae_enum glenums[] = { {"BINORMAL", AK_INPUT_BINORMAL}, {"COLOR", AK_INPUT_COLOR}, {"CONTINUITY", AK_INPUT_CONTINUITY}, {"IMAGE", AK_INPUT_IMAGE}, {"INPUT", AK_INPUT_INPUT}, {"IN_TANGENT", AK_INPUT_IN_TANGENT}, {"INTERPOLATION", AK_INPUT_INTERPOLATION}, {"INV_BIND_MATRIX", AK_INPUT_INV_BIND_MATRIX}, {"JOINT", AK_INPUT_JOINT}, {"LINEAR_STEPS", AK_INPUT_LINEAR_STEPS}, {"MORPH_TARGET", AK_INPUT_MORPH_TARGET}, {"MORPH_WEIGHT", AK_INPUT_MORPH_WEIGHT}, {"NORMAL", AK_INPUT_NORMAL}, {"OUTPUT", AK_INPUT_OUTPUT}, {"OUT_TANGENT", AK_INPUT_OUT_TANGENT}, {"POSITION", AK_INPUT_POSITION}, {"TANGENT", AK_INPUT_TANGENT}, {"TEXBINORMAL", AK_INPUT_TEXBINORMAL}, {"TEXCOORD", AK_INPUT_TEXCOORD}, {"TEXTANGENT", AK_INPUT_TEXTANGENT}, {"UV", AK_INPUT_UV}, {"VERTEX", AK_INPUT_SEMANTIC_VERTEX}, {"WEIGHT", AK_INPUT_WEIGHT}, }; /* COLLADA 1.5: ALWAYS is the default */ val = AK_INPUT_OTHER; glenums_len = AK_ARRAY_LEN(glenums); for (i = 0; i < glenums_len; i++) { if (strcasecmp(name, glenums[i].name) == 0) { val = glenums[i].val; break; } } return val; } AK_HIDE AkEnum dae_morphMethod(const xml_attr_t * __restrict xatt) { AkEnum val; long glenums_len, i; if (!xatt) return AK_MORPH_METHOD_NORMALIZED; dae_enum glenums[] = { {"NORMALIZED", AK_MORPH_METHOD_NORMALIZED}, {"RELATIVE", AK_MORPH_METHOD_RELATIVE}, }; val = AK_MORPH_METHOD_NORMALIZED; glenums_len = AK_ARRAY_LEN(glenums); for (i = 0; i < glenums_len; i++) { if (strncasecmp(xatt->val, glenums[i].name, xatt->valsize) == 0) { val = glenums[i].val; break; } } return val; } AK_HIDE AkEnum dae_nodeType(const xml_attr_t * __restrict xatt) { AkEnum val; long glenums_len, i; if (!xatt) return AK_NODE_TYPE_NODE; dae_enum glenums[] = { {"NODE", AK_NODE_TYPE_NODE}, {"JOINT", AK_NODE_TYPE_JOINT}, }; val = AK_NODE_TYPE_NODE; glenums_len = AK_ARRAY_LEN(glenums); for (i = 0; i < glenums_len; i++) { if (strncasecmp(xatt->val, glenums[i].name, xatt->valsize) == 0) { val = glenums[i].val; break; } } return val; } AK_HIDE AkEnum dae_animBehavior(const xml_attr_t * __restrict xatt) { AkEnum val; long glenums_len, i; if (!xatt) return AK_SAMPLER_BEHAVIOR_UNDEFINED; dae_enum glenums[] = { {"UNDEFINED", AK_SAMPLER_BEHAVIOR_UNDEFINED}, {"CONSTANT", AK_SAMPLER_BEHAVIOR_CONSTANT}, {"GRADIENT", AK_SAMPLER_BEHAVIOR_GRADIENT}, {"CYCLE", AK_SAMPLER_BEHAVIOR_CYCLE}, {"OSCILLATE", AK_SAMPLER_BEHAVIOR_OSCILLATE}, {"CYCLE_RELATIVE", AK_SAMPLER_BEHAVIOR_CYCLE_RELATIVE} }; val = AK_SAMPLER_BEHAVIOR_UNDEFINED; glenums_len = AK_ARRAY_LEN(glenums); for (i = 0; i < glenums_len; i++) { if (strncasecmp(xatt->val, glenums[i].name, xatt->valsize) == 0) { val = glenums[i].val; break; } } return val; } AK_HIDE AkEnum dae_animInterp(const char *name, size_t len) { AkEnum val; long glenums_len, i; if (!name) return AK_INTERPOLATION_LINEAR; dae_enum glenums[] = { {"LINEAR", AK_INTERPOLATION_LINEAR}, {"BEZIER", AK_INTERPOLATION_BEZIER}, {"CARDINAL", AK_INTERPOLATION_CARDINAL}, {"HERMITE", AK_INTERPOLATION_HERMITE}, {"BSPLINE", AK_INTERPOLATION_BSPLINE}, {"STEP", AK_INTERPOLATION_STEP} }; val = AK_INTERPOLATION_LINEAR; glenums_len = AK_ARRAY_LEN(glenums); for (i = 0; i < glenums_len; i++) { if (strncasecmp(name, glenums[i].name, len) == 0) { val = glenums[i].val; break; } } return val; } AK_HIDE AkEnum dae_wrap(const xml_t * __restrict xml) { AkEnum val; long glenums_len, i; if (!xml) return 0; dae_enum glenums[] = { {"WRAP", AK_WRAP_MODE_WRAP}, {"CLAMP", AK_WRAP_MODE_CLAMP}, {"BORDER", AK_WRAP_MODE_BORDER}, {"MIRROR", AK_WRAP_MODE_MIRROR}, {"MIRROR_ONCE", AK_WRAP_MODE_MIRROR_ONCE} }; val = 0; glenums_len = AK_ARRAY_LEN(glenums); for (i = 0; i < glenums_len; i++) { if (xml_val_eq(xml, glenums[i].name)) { val = glenums[i].val; break; } } return val; } AK_HIDE AkEnum dae_minfilter(const xml_t * __restrict xml) { AkEnum val; long glenums_len, i; if (!xml) return 0; dae_enum glenums[] = { {"NEAREST", AK_MINFILTER_NEAREST}, {"LINEAR", AK_MINFILTER_LINEAR}, {"ANISOTROPIC", AK_MINFILTER_ANISOTROPIC} }; val = 0; glenums_len = AK_ARRAY_LEN(glenums); for (i = 0; i < glenums_len; i++) { if (xml_val_eq(xml, glenums[i].name)) { val = glenums[i].val; break; } } return val; } AK_HIDE AkEnum dae_mipfilter(const xml_t * __restrict xml) { AkEnum val; long glenums_len, i; if (!xml) return 0; dae_enum glenums[] = { {"NONE", AK_MIPFILTER_NONE}, {"NEAREST", AK_MIPFILTER_NEAREST}, {"LINEAR", AK_MIPFILTER_LINEAR} }; val = 0; glenums_len = AK_ARRAY_LEN(glenums); for (i = 0; i < glenums_len; i++) { if (xml_val_eq(xml, glenums[i].name)) { val = glenums[i].val; break; } } return val; } AK_HIDE AkEnum dae_magfilter(const xml_t * __restrict xml) { AkEnum val; long glenums_len, i; if (!xml) return 0; dae_enum glenums[] = { {"NEAREST", AK_MAGFILTER_NEAREST}, {"LINEAR", AK_MAGFILTER_LINEAR} }; val = 0; glenums_len = AK_ARRAY_LEN(glenums); for (i = 0; i < glenums_len; i++) { if (xml_val_eq(xml, glenums[i].name)) { val = glenums[i].val; break; } } return val; } AK_HIDE AkEnum dae_face(const xml_attr_t * __restrict xatt) { AkEnum val; long glenums_len, i; if (!xatt) return 0; dae_enum glenums[] = { {"POSITIVE_X", AK_FACE_POSITIVE_X}, {"NEGATIVE_X", AK_FACE_NEGATIVE_X}, {"POSITIVE_Y", AK_FACE_POSITIVE_Y}, {"NEGATIVE_Y", AK_FACE_NEGATIVE_Y}, {"POSITIVE_Z", AK_FACE_POSITIVE_Z}, {"NEGATIVE_Z", AK_FACE_NEGATIVE_Z} }; val = 0; glenums_len = AK_ARRAY_LEN(glenums); for (i = 0; i < glenums_len; i++) { if (strncasecmp(xatt->val, glenums[i].name, xatt->valsize) == 0) { val = glenums[i].val; break; } } return val; } AK_HIDE AkEnum dae_opaque(const xml_attr_t * __restrict xatt) { AkEnum val; long glenums_len, i; if (!xatt) return AK_OPAQUE_A_ONE; dae_enum glenums[] = { {"A_ONE", AK_OPAQUE_A_ONE}, {"RGB_ZERO", AK_OPAQUE_RGB_ZERO}, {"A_ZERO", AK_OPAQUE_A_ZERO}, {"RGB_ONE", AK_OPAQUE_RGB_ONE} }; val = AK_OPAQUE_A_ONE; glenums_len = AK_ARRAY_LEN(glenums); for (i = 0; i < glenums_len; i++) { if (strncasecmp(xatt->val, glenums[i].name, xatt->valsize) == 0) { val = glenums[i].val; break; } } return val; } AK_HIDE AkEnum dae_enumChannel(const char *name, size_t len) { AkEnum val; long glenums_len, i; if (!name) return 0; dae_enum glenums[] = { {"RGB", AK_CHANNEL_FORMAT_RGB}, {"RGBA", AK_CHANNEL_FORMAT_RGBA}, {"RGBE", AK_CHANNEL_FORMAT_RGBE}, {"L", AK_CHANNEL_FORMAT_L}, {"LA", AK_CHANNEL_FORMAT_LA}, {"D", AK_CHANNEL_FORMAT_D}, /* 1.4 */ {"XYZ", AK_CHANNEL_FORMAT_XYZ}, {"XYZW", AK_CHANNEL_FORMAT_XYZW} }; val = 0; glenums_len = AK_ARRAY_LEN(glenums); for (i = 0; i < glenums_len; i++) { if (strncasecmp(name, glenums[i].name, len) == 0) { val = glenums[i].val; break; } } return val; } AK_HIDE AkEnum dae_range(const char *name, size_t len) { AkEnum val; long glenums_len, i; if (!name) return 0; dae_enum glenums[] = { {"SNORM", AK_RANGE_FORMAT_SNORM}, {"UNORM", AK_RANGE_FORMAT_UNORM}, {"SINT", AK_RANGE_FORMAT_SINT}, {"UINT", AK_RANGE_FORMAT_UINT}, {"FLOAT", AK_RANGE_FORMAT_FLOAT} }; val = 0; glenums_len = AK_ARRAY_LEN(glenums); for (i = 0; i < glenums_len; i++) { if (strncasecmp(name, glenums[i].name, len) == 0) { val = glenums[i].val; break; } } return val; } AK_HIDE AkEnum dae_precision(const char *name, size_t len) { AkEnum val; long glenums_len, i; if (!name) return 0; dae_enum glenums[] = { {"DEFAULT", AK_PRECISION_FORMAT_DEFAULT}, {"LOW", AK_PRECISION_FORMAT_LOW}, {"MID", AK_PRECISION_FORMAT_MID}, {"HIGH", AK_PRECISION_FORMAT_HIGHT}, {"MAX", AK_PRECISION_FORMAT_MAX} }; val = 0; glenums_len = AK_ARRAY_LEN(glenums); for (i = 0; i < glenums_len; i++) { if (strncasecmp(name, glenums[i].name, len) == 0) { val = glenums[i].val; break; } } return val; } ================================================ FILE: src/io/dae/core/enum.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_enums_h #define dae_enums_h #include "../../../xml.h" AK_HIDE AkEnum dae_semantic(const char * name); AK_HIDE AkEnum dae_morphMethod(const xml_attr_t * __restrict xatt); AK_HIDE AkEnum dae_nodeType(const xml_attr_t * __restrict xatt); AK_HIDE AkEnum dae_animBehavior(const xml_attr_t * __restrict xatt); AK_HIDE AkEnum dae_animInterp(const char * name, size_t len); AK_HIDE AkEnum dae_wrap(const xml_t * __restrict xml); AK_HIDE AkEnum dae_minfilter(const xml_t * __restrict xml); AK_HIDE AkEnum dae_mipfilter(const xml_t * __restrict xml); AK_HIDE AkEnum dae_magfilter(const xml_t * __restrict xml); AK_HIDE AkEnum dae_face(const xml_attr_t * __restrict xatt); AK_HIDE AkEnum dae_opaque(const xml_attr_t * __restrict xatt); AK_HIDE AkEnum dae_enumChannel(const char *name, size_t len); AK_HIDE AkEnum dae_range(const char *name, size_t len); AK_HIDE AkEnum dae_precision(const char *name, size_t len); #endif /* dae_enums_h */ ================================================ FILE: src/io/dae/core/geom.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "geom.h" #include "mesh.h" #include "spline.h" #include "../core/asset.h" #include "../brep/brep.h" AK_HIDE void* dae_geom(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkGeometry *geom; AkHeap *heap; heap = dst->heap; geom = ak_heap_calloc(heap, memp, sizeof(*geom)); geom->name = xmla_strdup_by(xml, heap, _s_dae_name, geom); geom->materialMap = ak_map_new(ak_cmp_str); xmla_setid(xml, heap, geom); /* destroy heap with this object */ ak_setAttachedHeap(geom, geom->materialMap->heap); ak_setypeid(geom, AKT_GEOMETRY); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_asset)) { (void)dae_asset(dst, xml, geom, NULL); } else if (xml_tag_eq(xml, _s_dae_mesh) || xml_tag_eq(xml, _s_dae_convex_mesh)) { geom->gdata = dae_mesh(dst, xml, geom); } else if (xml_tag_eq(xml, _s_dae_spline)) { geom->gdata = dae_spline(dst, xml, geom); } else if (xml_tag_eq(xml, _s_dae_brep)) { geom->gdata = dae_brep(dst, xml, geom); } else if (xml_tag_eq(xml, _s_dae_extra)) { geom->extra = tree_fromxml(heap, geom, xml); } xml = xml->next; } return geom; } ================================================ FILE: src/io/dae/core/geom.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_geometry_h #define dae_geometry_h #include "../common.h" AK_HIDE void* dae_geom(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); #endif /* dae_geometry_h */ ================================================ FILE: src/io/dae/core/light.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "light.h" #include "asset.h" #include "techn.h" #include "color.h" #define AK_DEFAULT_LIGHT_DIR {0.0f, 0.0f, -1.0f} AK_HIDE void* dae_light(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkLight *light; AkHeap *heap; AkTechnique *tq; heap = dst->heap; light = ak_heap_calloc(heap, memp, sizeof(*light)); light->name = xmla_strdup_by(xml, heap, _s_dae_name, light); xmla_setid(xml, heap, light); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_asset)) { (void)dae_asset(dst, xml, light, NULL); } else if (xml_tag_eq(xml, _s_dae_techniquec)) { xml_t *xtech, *xtechv, *xcolor; AkAmbientLight *lightb; AkCoordSys *optCoordSys; xtech = xml->val; if (xml_tag_eq(xtech, _s_dae_ambient)) { lightb = ak_heap_calloc(heap, light, sizeof(AkAmbientLight)); lightb->type = AK_LIGHT_TYPE_AMBIENT; } else if (xml_tag_eq(xtech, _s_dae_directional)) { lightb = ak_heap_calloc(heap, light, sizeof(AkDirectionalLight)); lightb->type = AK_LIGHT_TYPE_DIRECTIONAL; } else if (xml_tag_eq(xtech, _s_dae_point)) { AkPointLight *point; point = ak_heap_calloc(heap, light, sizeof(*point)); point->base.type = AK_LIGHT_TYPE_POINT; lightb = &point->base; /* default values */ point->constAttn = 1.0f; xtechv = xtech->val; while (xtechv) { if (xml_tag_eq(xtechv, _s_dae_color)) { sid_seta(xtechv, heap, point, &point->base.color); } else if (xml_tag_eq(xtechv, _s_dae_const_attn)) { sid_seta(xtechv, heap, point, &point->constAttn); point->constAttn = xml_float(xtechv, 1.0f); } else if (xml_tag_eq(xtechv, _s_dae_linear_attn)) { sid_seta(xtechv, heap, point, &point->linearAttn); point->linearAttn = xml_float(xtechv, 0.0f); } else if (xml_tag_eq(xtechv, _s_dae_quad_attn)) { sid_seta(xtechv, heap, point, &point->quadAttn); point->quadAttn = xml_float(xtechv, 0.0f); } xtechv = xtechv->next; } } else if (xml_tag_eq(xtech, _s_dae_spot)) { AkSpotLight *spot; spot = ak_heap_calloc(heap, light, sizeof(*spot)); spot->base.type = AK_LIGHT_TYPE_SPOT; lightb = &spot->base; /* default values */ spot->constAttn = 1.0f; spot->falloffAngle = glm_rad(180.0f); xtechv = xtech->val; while (xtechv) { if (xml_tag_eq(xtechv, _s_dae_color)) { sid_seta(xtechv, heap, spot, &spot->base.color); } else if (xml_tag_eq(xtechv, _s_dae_const_attn)) { sid_seta(xtechv, heap, spot, &spot->constAttn); spot->constAttn = xml_float(xtechv, 0.0f); } else if (xml_tag_eq(xtechv, _s_dae_linear_attn)) { sid_seta(xtechv, heap, spot, &spot->linearAttn); spot->linearAttn = xml_float(xtechv, 0.0f); } else if (xml_tag_eq(xtechv, _s_dae_quad_attn)) { sid_seta(xtechv, heap, spot, &spot->quadAttn); spot->quadAttn = xml_float(xtechv, 0.0f); } else if (xml_tag_eq(xtechv, _s_dae_falloff_angle)) { sid_seta(xtechv, heap, spot, &spot->falloffAngle); spot->falloffAngle = xml_float(xtechv, 0.0f); glm_make_rad(&spot->falloffAngle); } else if (xml_tag_eq(xtechv, _s_dae_falloff_exp)) { sid_seta(xtechv, heap, spot, &spot->falloffExp); spot->falloffExp = xml_float(xtechv, 0.0f); } xtechv = xtechv->next; } } else { goto nxt; } if ((xcolor = xml_elem(xtech, _s_dae_color))) { dae_color(xcolor, lightb, true, true, &lightb->color); } else { glm_vec4_one(lightb->color.vec); } if ((light->tcommon = lightb)) { /* fix coord sys */ optCoordSys = (void *)ak_opt_get(AK_OPT_COORD); if (ak_opt_get(AK_OPT_COORD_CONVERT_TYPE) == AK_COORD_CVT_ALL && optCoordSys != dst->doc->coordSys) { /* convert default cone direction to new coord sys */ ak_coordCvtVectorTo(dst->doc->coordSys, (vec3)AK_DEFAULT_LIGHT_DIR, optCoordSys, lightb->direction); } else { glm_vec3_copy((vec3)AK_DEFAULT_LIGHT_DIR, lightb->direction); } } } else if (xml_tag_eq(xml, _s_dae_technique)) { tq = dae_techn(xml, heap, light); tq->next = light->technique; light->technique = tq; } else if (xml_tag_eq(xml, _s_dae_extra)) { light->extra = tree_fromxml(heap, light, xml); } nxt: xml = xml->next; } return light; } ================================================ FILE: src/io/dae/core/light.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_light_h #define dae_light_h #include "../common.h" AK_HIDE void* dae_light(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); #endif /* dae_light_h */ ================================================ FILE: src/io/dae/core/line.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "line.h" #include "enum.h" #include "../../../array.h" AK_HIDE AkLines* dae_lines(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp, AkLineMode mode) { AkLines *lines; AkHeap *heap; uint32_t indexoff; heap = dst->heap; lines = ak_heap_calloc(heap, memp, sizeof(*lines)); lines->mode = mode; lines->base.type = AK_PRIMITIVE_LINES; lines->base.name = xmla_strdup_by(xml, heap, _s_dae_name, lines); lines->base.bindmaterial = xmla_strdup_by(xml, heap, _s_dae_material, lines); lines->base.nPolygons = xmla_u32(xmla(xml, _s_dae_count), 0); indexoff = 0; xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_input)) { AkInput *inp; inp = ak_heap_calloc(heap, lines, sizeof(*inp)); inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp); if (!inp->semanticRaw) { ak_free(inp); } else { AkURL *url; AkEnum inputSemantic; inputSemantic = dae_semantic(inp->semanticRaw); if (inputSemantic < 0) inputSemantic = AK_INPUT_OTHER; inp->semantic = inputSemantic; inp->offset = xmla_u32(xmla(xml, _s_dae_offset), 0); inp->set = xmla_u32(xmla(xml, _s_dae_set), 0); url = url_from(xml, _s_dae_source, memp); rb_insert(dst->inputmap, inp, url); if ((uint32_t)inp->semantic != AK_INPUT_SEMANTIC_VERTEX) { inp->semantic = dae_semantic(inp->semanticRaw); inp->next = lines->base.input; lines->base.input = inp; lines->base.inputCount++; if (inp->offset > indexoff) indexoff = inp->offset; } else { dae_vertmap_add(dst, inp, &lines->base); /* don't store VERTEX because it will be duplicated to all prims */ // lines->base.reserved1 = inp->offset; // lines->base.reserved2 = inp->set; // ak_free(inp); } } } else if (xml_tag_eq(xml, _s_dae_p) && xml->val) { AkUIntArray *uintArray; AkResult ret; ret = xml_strtoui_array(heap, lines, xml->val, &uintArray); if (ret == AK_OK) lines->base.indices = uintArray; } else if (xml_tag_eq(xml, _s_dae_extra)) { lines->base.extra = tree_fromxml(heap, lines, xml); } xml = xml->next; } lines->base.indexStride = indexoff + 1; return lines; } ================================================ FILE: src/io/dae/core/line.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_lines_h #define dae_lines_h #include "../common.h" AK_HIDE AkLines* dae_lines(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp, AkLineMode mode); #endif /* dae_lines_h */ ================================================ FILE: src/io/dae/core/mesh.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "mesh.h" #include "source.h" #include "vert.h" #include "triangle.h" #include "poly.h" #include "line.h" AK_INLINE void dae_mesh_appendPrim(AkMesh * __restrict mesh, AkMeshPrimitive ** __restrict lastPrim, AkMeshPrimitive * __restrict prim) { prim->next = NULL; prim->mesh = mesh; if (*lastPrim) (*lastPrim)->next = prim; else mesh->primitive = prim; *lastPrim = prim; mesh->primitiveCount++; } AK_HIDE AkObject* dae_mesh(DAEState * __restrict dst, xml_t * __restrict xml, AkGeometry * __restrict geom) { AkObject *obj; AkMesh *mesh; AkMeshPrimitive *lastPrim; AkHeap *heap; uint32_t m; heap = dst->heap; xml = xml->val; lastPrim = NULL; obj = ak_objAlloc(heap, geom, sizeof(*mesh), AK_GEOMETRY_MESH, true); mesh = ak_objGet(obj); mesh->geom = geom; mesh->convexHullOf = xmla_strdup_by(xml, heap, _s_dae_convex_hull_of, obj); while (xml) { if (xml_tag_eq(xml, _s_dae_source)) { (void)dae_source(dst, xml, NULL, 0); } else if (xml_tag_eq(xml, _s_dae_vertices)) { (void)dae_vert(dst, xml, dst->tempmem); } else if ((xml_tag_eq(xml, _s_dae_triangles) & (m = AK_TRIANGLES)) || (xml_tag_eq(xml, _s_dae_trifans) & (m = AK_TRIANGLE_FAN)) || (xml_tag_eq(xml, _s_dae_tristrips) & (m = AK_TRIANGLE_STRIP))) { AkTriangles *tri; if ((tri = dae_triangles(dst, xml, obj, m))) { dae_mesh_appendPrim(mesh, &lastPrim, &tri->base); if (tri->base.bindmaterial) ak_meshSetMaterial(&tri->base, tri->base.bindmaterial); } } else if ((xml_tag_eq(xml, _s_dae_polygons) & (m = AK_POLY_POLYGONS)) || (xml_tag_eq(xml, _s_dae_polylist) & (m = AK_POLY_POLYLIST))) { AkPolygon *poly; if ((poly = dae_poly(dst, xml, obj, m))) { dae_mesh_appendPrim(mesh, &lastPrim, &poly->base); if (poly->base.bindmaterial) ak_meshSetMaterial(&poly->base, poly->base.bindmaterial); } } else if (xml_tag_eq(xml, _s_dae_lines) & (m = AK_LINES) || (xml_tag_eq(xml, _s_dae_linestrips) & (m = AK_LINE_STRIP))) { AkLines *lines; if ((lines = dae_lines(dst, xml, obj, m))) { dae_mesh_appendPrim(mesh, &lastPrim, &lines->base); if (lines->base.bindmaterial) ak_meshSetMaterial(&lines->base, lines->base.bindmaterial); } } else if (xml_tag_eq(xml, _s_dae_extra)) { mesh->extra = tree_fromxml(heap, obj, xml); } xml = xml->next; } return obj; } ================================================ FILE: src/io/dae/core/mesh.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_mesh_h #define dae_mesh_h #include "../common.h" AK_HIDE AkObject* dae_mesh(DAEState * __restrict dst, xml_t * __restrict xml, AkGeometry * __restrict geom); #endif /* dae_mesh_h */ ================================================ FILE: src/io/dae/core/morph.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "morph.h" #include "source.h" #include "enum.h" #include "../../../array.h" AK_HIDE AkMorph* dae_morph(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkMorph *morph; AkMorphDAE *morphdae; xml_attr_t *att; heap = dst->heap; morph = ak_heap_calloc(heap, memp, sizeof(*morph)); morphdae = ak_heap_calloc(heap, morph, sizeof(*morphdae)); ak_heap_setUserData(heap, morph, morphdae); flist_sp_insert(&dst->linkedUserData, morph); url_set(dst, xml, _s_dae_source, memp, &morphdae->baseGeom); if ((att = xmla(xml, _s_dae_method))) morph->method = dae_morphMethod(att); else morph->method = AK_MORPH_METHOD_NORMALIZED; xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_source)) { AkSource *source; if ((source = dae_source(dst, xml, NULL, 0))) { source->next = morphdae->source; morphdae->source = source; } } else if (xml_tag_eq(xml, _s_dae_targets)) { AkInput *inp; xml_t *xtarg; xtarg = xml->val; while (xtarg) { if (xml_tag_eq(xtarg, _s_dae_input)) { inp = ak_heap_calloc(heap, morphdae, sizeof(*inp)); inp->semanticRaw = xmla_strdup_by(xtarg, heap, _s_dae_semantic, inp); if (!inp->semanticRaw) { ak_free(inp); } else { AkURL *url; AkEnum inputSemantic; inputSemantic = dae_semantic(inp->semanticRaw); if (inputSemantic < 0) inputSemantic = AK_INPUT_OTHER; inp->semantic = inputSemantic; inp->offset = xmla_u32(xmla(xtarg, _s_dae_offset), 0); inp->semantic = dae_semantic(inp->semanticRaw); url = url_from(xtarg, _s_dae_source, memp); rb_insert(dst->inputmap, inp, url); inp->next = morphdae->input; morphdae->input = inp; } } xtarg = xtarg->next; } } else if (xml_tag_eq(xml, _s_dae_extra)) { morphdae->extra = tree_fromxml(heap, morphdae, xml); } xml = xml->next; } return morph; } ================================================ FILE: src/io/dae/core/morph.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_morph_h #define dae_morph_h #include "../common.h" AK_HIDE AkMorph* dae_morph(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); #endif /* dae_morph_h */ ================================================ FILE: src/io/dae/core/node.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "node.h" #include "enum.h" #include "../../../array.h" #include "../core/asset.h" #include "../fx/mat.h" #include "../fixup/node.h" #include AK_HIDE void* dae_node2(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { return dae_node(dst, xml, memp, NULL); } AK_HIDE AkNode* dae_node(DAEState * __restrict dst, xml_t * __restrict xml, void * memp, AkVisualScene * scene) { AkHeap *heap; AkNode *node; const xml_t *sval; xml_attr_t *att; AkObject *last_trans; heap = dst->heap; node = ak_heap_calloc(heap, memp, sizeof(*node)); ak_setypeid(node, AKT_NODE); node->visible = true; xmla_setid(xml, heap, node); sid_set(xml, heap, node); node->name = xmla_strdup_by(xml, heap, _s_dae_name, node); node->nodeType = dae_nodeType(xmla(xml, _s_dae_type)); if (node->nodeType < 1) node->nodeType = AK_NODE_TYPE_NODE; if ((att = xmla(xml, _s_dae_layer))) { AkStringArray *strArray; AkResult ret; char *layer; layer = xmla_strdup(att, heap, node); ret = layer ? ak_strtostr_array(heap, node, layer, ' ', &strArray) : AK_ERR; if (ret == AK_OK) node->layer = strArray; } last_trans = NULL; xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_asset)) { (void)dae_asset(dst, xml, node, NULL); } else if (xml_tag_eq(xml, _s_dae_lookat) && (sval = xmls(xml))) { AkObject *obj; AkLookAt *looakAt; obj = ak_objAlloc(heap, node, sizeof(*looakAt), AKT_LOOKAT, true); looakAt = ak_objGet(obj); sid_set(xml, heap, obj); xml_strtof_fast(sval, (float *)looakAt->val, 9); if (!node->transform) node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform)); AK_APPEND_FLINK(node->transform->item, last_trans, obj); } else if (xml_tag_eq(xml, _s_dae_matrix) && (sval = xmls(xml))) { mat4 transform; AkObject *obj; AkMatrix *matrix; obj = ak_objAlloc(heap, node, sizeof(*matrix), AKT_MATRIX, true); matrix = ak_objGet(obj); sid_set(xml, heap, obj); xml_strtof_fast(sval, transform[0], 16); glm_mat4_transpose_to(transform, matrix->val); if (!node->transform) node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform)); AK_APPEND_FLINK(node->transform->item, last_trans, obj); } else if (xml_tag_eq(xml, _s_dae_rotate) && (sval = xmls(xml))) { AkObject *obj; AkRotate *rotate; obj = ak_objAlloc(heap, node, sizeof(*rotate), AKT_ROTATE, true); rotate = ak_objGet(obj); sid_set(xml, heap, obj); xml_strtof_fast(sval, (AkFloat *)rotate->val, 4); glm_make_rad(&rotate->val[3]); if (!node->transform) node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform)); AK_APPEND_FLINK(node->transform->item, last_trans, obj); } else if (xml_tag_eq(xml, _s_dae_scale) && (sval = xmls(xml))) { AkObject *obj; AkScale *scale; obj = ak_objAlloc(heap, node, sizeof(*scale), AKT_SCALE, true); scale = ak_objGet(obj); sid_set(xml, heap, obj); xml_strtof_fast(sval, (AkFloat *)scale->val, 3); if (!node->transform) node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform)); AK_APPEND_FLINK(node->transform->item, last_trans, obj); } else if (xml_tag_eq(xml, _s_dae_skew) && (sval = xmls(xml))) { AkObject *obj; AkSkew *skew; AkFloat tmp[7]; obj = ak_objAlloc(heap, node, sizeof(*skew), AKT_SKEW, true); skew = ak_objGet(obj); sid_set(xml, heap, obj); xml_strtof_fast(sval, (AkFloat *)tmp, 4); /* COLLADA uses degree here, convert it to radians */ skew->angle = glm_rad(tmp[0]); glm_vec3_copy(&tmp[1], skew->rotateAxis); glm_vec3_copy(&tmp[4], skew->aroundAxis); if (!node->transform) node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform)); AK_APPEND_FLINK(node->transform->item, last_trans, obj); } else if (xml_tag_eq(xml, _s_dae_translate) && (sval = xmls(xml))) { AkObject *obj; AkTranslate *transl; obj = ak_objAlloc(heap, node, sizeof(*transl), AKT_TRANSLATE, true); transl = ak_objGet(obj); sid_set(xml, heap, obj); xml_strtof_fast(sval, (AkFloat *)transl->val, 4); if (!node->transform) node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform)); AK_APPEND_FLINK(node->transform->item, last_trans, obj); } else if (xml_tag_eq(xml, _s_dae_instance_camera)) { AkInstanceBase *instcam; instcam = ak_heap_calloc(heap, node, sizeof(*instcam)); instcam->type = AK_INSTANCE_CAMERA; instcam->name = xmla_strdup_by(xml, heap, _s_dae_name, instcam); url_set(dst, xml, _s_dae_url, instcam, &instcam->url); instcam->node = node; instcam->next = node->camera; node->camera = instcam; instcam->prev = node->camera; if (node->camera) node->camera->prev = instcam; if (scene) { if (!scene->firstCamNode) scene->firstCamNode = node; if (instcam) ak_instanceListAdd(scene->cameras, instcam); } } else if (xml_tag_eq(xml, _s_dae_instance_controller)) { AkInstanceController *instctl; xml_t *xinstctl; instctl = ak_heap_calloc(heap, node, sizeof(*instctl)); instctl->base.type = AK_INSTANCE_CONTROLLER; instctl->base.name = xmla_strdup_by(xml, heap, _s_dae_name, instctl); url_set(dst, xml, _s_dae_url, instctl, &instctl->base.url); xinstctl = xml->val; while (xinstctl) { if (xml_tag_eq(xinstctl, _s_dae_skeleton)) { char *skel; if ((skel = xml_strdup(xinstctl, heap, instctl))) flist_sp_insert(&instctl->reserved, skel); } else if (xml_tag_eq(xinstctl, _s_dae_bind_material)) { instctl->bindMaterial = dae_bindMaterial(dst, xinstctl, instctl); } else if (xml_tag_eq(xinstctl, _s_dae_extra)) { instctl->base.extra = tree_fromxml(heap, instctl, xinstctl); } xinstctl = xinstctl->next; } instctl->base.node = node; flist_sp_insert(&dst->instCtlrs, instctl);; } else if (xml_tag_eq(xml, _s_dae_instance_geometry)) { AkInstanceGeometry *instgeo; xml_t *xinstgeo; instgeo = ak_heap_calloc(heap, node, sizeof(*instgeo)); instgeo->base.type = AK_INSTANCE_GEOMETRY; instgeo->base.name = xmla_strdup_by(xml, heap, _s_dae_name, instgeo); url_set(dst, xml, _s_dae_url, instgeo, &instgeo->base.url); xinstgeo = xml->val; while (xinstgeo) { if (xml_tag_eq(xinstgeo, _s_dae_bind_material)) { instgeo->bindMaterial = dae_bindMaterial(dst, xinstgeo, instgeo); } else if (xml_tag_eq(xinstgeo, _s_dae_extra)) { instgeo->base.extra = tree_fromxml(heap, instgeo, xinstgeo); } xinstgeo = xinstgeo->next; } instgeo->base.node = node; instgeo->base.next = (void *)node->geometry; node->geometry = instgeo; } else if (xml_tag_eq(xml, _s_dae_instance_light)) { AkInstanceBase *instlight; instlight = ak_heap_calloc(heap, node, sizeof(*instlight)); instlight->type = AK_INSTANCE_LIGHT; instlight->name = xmla_strdup_by(xml, heap, _s_dae_name, instlight); url_set(dst, xml, _s_dae_url, instlight, &instlight->url); instlight->node = node; instlight->next = node->light; node->light = instlight; instlight->prev = node->light; if (node->light) node->light->prev = instlight; if (scene && instlight) { AkLight *lightObject; lightObject = ak_instanceObject(instlight); if (lightObject) ak_instanceListAdd(scene->lights, instlight); } } else if (xml_tag_eq(xml, _s_dae_instance_node)) { AkInstanceNode *instnode; instnode = ak_heap_calloc(heap, node, sizeof(*instnode)); instnode->base.type = AK_INSTANCE_NODE; instnode->base.name = xmla_strdup_by(xml, heap, _s_dae_name, instnode); instnode->proxy = xmla_strdup_by(xml, heap, _s_dae_proxy, instnode); url_set(dst, xml, _s_dae_url, instnode, &instnode->base.url); if (node->node) instnode->base.next = &node->node->base; instnode->base.node = node; node->node = instnode; if (node->node) { instnode->base.prev = &node->node->base; node->node->base.prev = &instnode->base; } } else if (xml_tag_eq(xml, _s_dae_node)) { AkNode *subNode; if ((subNode = dae_node(dst, xml, node, scene))) { if (node->chld) { node->chld->prev = subNode; } subNode->next = node->chld; node->chld = subNode; } } else if (xml_tag_eq(xml, _s_dae_extra)) { node->extra = tree_fromxml(heap, node, xml); } xml = xml->next; } if (ak_isKindOf(memp, node)) node->parent = memp; dae_nodeFixup(heap, node); return node; } ================================================ FILE: src/io/dae/core/node.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_node_h #define dae_node_h #include "../common.h" AK_HIDE void* dae_node2(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); AK_HIDE AkNode* dae_node(DAEState * __restrict dst, xml_t * __restrict xml, void * memp, AkVisualScene * scene); #endif /* dae_node_h */ ================================================ FILE: src/io/dae/core/param.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "param.h" #include "value.h" AK_HIDE AkNewParam* dae_newparam(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkNewParam *newparam; newparam = ak_heap_calloc(dst->heap, memp, sizeof(*newparam)); ak_setypeid(newparam, AKT_NEWPARAM); sid_set(xml, dst->heap, newparam); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_semantic)) { } else { /* load once */ if (!newparam->val) { newparam->val = dae_value(dst, xml, newparam); } } xml = xml->next; } return newparam; } AK_HIDE AkParam* dae_param(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkParam *param; param = ak_heap_calloc(dst->heap, memp, sizeof(*param)); ak_setypeid(param, AKT_PARAM); param->ref = xmla_strdup_by(xml, dst->heap, _s_dae_ref, param); return param; } ================================================ FILE: src/io/dae/core/param.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_param_h #define dae_param_h #include "../common.h" AK_HIDE AkNewParam* dae_newparam(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); AK_HIDE AkParam* dae_param(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); #endif /* dae_param_h */ ================================================ FILE: src/io/dae/core/poly.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "poly.h" #include "enum.h" #include "../../../array.h" #include "../../../data.h" AK_HIDE AkPolygon* dae_poly(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp, AkPolygonMode mode) { AkPolygon *poly; FListItem *polyi; AkHeap *heap; uint32_t indexoff, polygonsCount, st; size_t indicesCount; heap = dst->heap; poly = ak_heap_calloc(heap, memp, sizeof(*poly)); poly->haveHoles = false; poly->base.type = AK_PRIMITIVE_POLYGONS; poly->base.name = xmla_strdup_by(xml, heap, _s_dae_name, poly); poly->base.bindmaterial = xmla_strdup_by(xml, heap, _s_dae_material, poly); poly->base.nPolygons = xmla_u32(xmla(xml, _s_dae_count), 0); polyi = NULL; indexoff = 0; polygonsCount = 0; indicesCount = 0; xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_input)) { AkInput *inp; inp = ak_heap_calloc(heap, poly, sizeof(*inp)); inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp); if (!inp->semanticRaw) { ak_free(inp); } else { AkURL *url; AkEnum inputSemantic; inputSemantic = dae_semantic(inp->semanticRaw); if (inputSemantic < 0) inputSemantic = AK_INPUT_OTHER; inp->semantic = inputSemantic; inp->offset = xmla_u32(xmla(xml, _s_dae_offset), 0); inp->set = xmla_u32(xmla(xml, _s_dae_set), 0); url = url_from(xml, _s_dae_source, memp); rb_insert(dst->inputmap, inp, url); if ((uint32_t)inp->semantic != AK_INPUT_SEMANTIC_VERTEX) { inp->semantic = dae_semantic(inp->semanticRaw); inp->next = poly->base.input; poly->base.input = inp; poly->base.inputCount++; if (inp->offset > indexoff) indexoff = inp->offset; } else { dae_vertmap_add(dst, inp, &poly->base); /* don't store VERTEX because it will be duplicated to all prims */ // poly->base.reserved1 = inp->offset; // poly->base.reserved2 = inp->set; // ak_free(inp); } } } else if (xml_tag_eq(xml, _s_dae_p) && xml->val) { AkUIntArray *intArray; if ((xml_strtoui_array(heap, poly, xml->val, &intArray) == AK_OK)) { if (mode == AK_POLY_POLYLIST) { poly->base.indices = intArray; } else if (mode == AK_POLY_POLYGONS) { /* TODO: do this for POLYLIST if vcount not exists */ flist_sp_insert(&polyi, intArray); polygonsCount++; indicesCount += intArray->count; } } } else if (xml_tag_eq(xml, _s_dae_vcount) && xml->val) { AkUIntArray *intArray; if ((xml_strtoui_array(heap, poly, xml->val, &intArray) == AK_OK)) { poly->vcount = intArray; } } else if (xml_tag_eq(xml, _s_dae_extra)) { poly->base.extra = tree_fromxml(heap, poly, xml); } xml = xml->next; } poly->base.indexStride = st = indexoff + 1; if (mode == AK_POLY_POLYGONS) { FListItem *p; AkUIntArray *indices, *vcount; AkUInt *pIndices, *pVcount; /* alloc indices array */ indices = ak_heap_alloc(heap, poly, sizeof(*indices) + sizeof(AkUInt) * indicesCount); indices->count = indicesCount; /* alloc vcount */ vcount = ak_heap_alloc(heap, poly, sizeof(*vcount) + sizeof(AkUInt) * polygonsCount); vcount->count = polygonsCount; pIndices = indices->items; pVcount = vcount->items; p = polyi; while (p) { AkUIntArray *intArray; intArray = p->data; memcpy(pIndices, intArray->items, sizeof(AkUInt) * intArray->count); *pVcount++ = (AkUInt)intArray->count / st; pIndices += intArray->count; ak_free(intArray); p = p->next; } poly->base.indices = indices; poly->vcount = vcount; flist_sp_destroy(&polyi); } return poly; } ================================================ FILE: src/io/dae/core/poly.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_polygons_h #define dae_polygons_h #include "../common.h" typedef enum AkPolygonMode { AK_POLY_POLYLIST = 1, AK_POLY_POLYGONS = 3 } AkPolygonMode; AK_HIDE AkPolygon* dae_poly(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp, AkPolygonMode mode); #endif /* dae_polygons_h */ ================================================ FILE: src/io/dae/core/scene.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "scene.h" #include "node.h" #include "../core/asset.h" #include "../core/asset.h" #include "../fx/mat.h" #include "../../../array.h" #include "../../../../include/ak/light.h" static AkEvaluateScene* dae_evalScene(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); AK_HIDE void* dae_vscene(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkVisualScene *vscn; heap = dst->heap; vscn = ak_heap_calloc(heap, memp, sizeof(*vscn)); ak_setypeid(vscn, AKT_SCENE); xmla_setid(xml, heap, vscn); vscn->name = xmla_strdup_by(xml, heap, _s_dae_name, vscn); vscn->cameras = ak_heap_calloc(heap, vscn, sizeof(*vscn->cameras)); vscn->lights = ak_heap_calloc(heap, vscn, sizeof(*vscn->lights)); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_asset)) { (void)dae_asset(dst, xml, vscn, NULL); } else if (xml_tag_eq(xml, _s_dae_node)) { AkNode *node; if ((node = dae_node(dst, xml, vscn, vscn))) { node->next = vscn->node; vscn->node = node; if (vscn->node) { vscn->node->prev = node; } } } else if (xml_tag_eq(xml, _s_dae_evaluate_scene)) { AkEvaluateScene *evalScene; if ((evalScene = dae_evalScene(dst, xml, vscn))) { vscn->evaluateScene = evalScene; evalScene->next = vscn->evaluateScene; } } else if (xml_tag_eq(xml, _s_dae_extra)) { vscn->extra = tree_fromxml(heap, vscn, xml); } xml = xml->next; } if (vscn->lights->count < 1 && ak_opt_get(AK_OPT_ADD_DEFAULT_LIGHT)) { AkLight *light; AkNode *rootNode; rootNode = vscn->node; if (rootNode) { AkHeap *heap; AkDoc *doc; AkInstanceBase *lightInst; heap = ak_heap_getheap(rootNode); doc = ak_heap_data(heap); light = ak_defaultLight(rootNode); lightInst = ak_instanceMake(heap, rootNode, light); ak_instanceListEmpty(vscn->lights); ak_instanceListAdd(vscn->lights, lightInst); lightInst->next = rootNode->light; rootNode->light = lightInst; ak_libAddLight(doc, light); } } return vscn; } AK_HIDE AkInstanceBase* dae_instVisualScene(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkInstanceBase *visualScene; heap = dst->heap; visualScene = ak_heap_calloc(heap, memp, sizeof(*visualScene)); sid_set(xml, heap, visualScene); visualScene->name = xmla_strdup_by(xml, heap, _s_dae_name, visualScene); url_set(dst, xml, _s_dae_url, visualScene, &visualScene->url); return visualScene; } static AkEvaluateScene* dae_evalScene(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memParent) { AkEvaluateScene *evalScene; AkHeap *heap; heap = dst->heap; xml = xml->val; evalScene = ak_heap_calloc(heap, memParent, sizeof(*evalScene)); xmla_setid(xml, heap, evalScene); sid_set(xml, heap, evalScene); evalScene->name = xmla_strdup_by(xml, heap, _s_dae_name, evalScene); evalScene->enable = xmla_bool(xmla(xml, _s_dae_enable), 0); while (xml) { if (xml_tag_eq(xml, _s_dae_asset)) { (void)dae_asset(dst, xml, evalScene, NULL); } else if (xml_tag_eq(xml, _s_dae_render)) { AkRender *ren; xml_t *xren; ren = ak_heap_calloc(heap, evalScene, sizeof(*ren)); sid_set(xml, heap, ren); ren->name = xmla_strdup_by(xml, heap, _s_dae_name, ren); ren->cameraNode = xmla_strdup_by(xml, heap, _s_dae_camera_node, ren); xren = xml->val; while (xren) { if (xml_tag_eq(xren, _s_dae_layer) && xren->val) { AkStringArrayL *layer; char *contents; AkResult ret; contents = xren->val; contents[xren->valsize] = '\0'; /* TODO: */ ret = ak_strtostr_arrayL(heap, ren, contents, ' ', &layer); if (ret == AK_OK) { layer->next = ren->layer; ren->layer = layer; } } else if (xml_tag_eq(xren, _s_dae_instance_material)) { AkInstanceMaterial *instmat; if ((instmat = dae_instMaterial(dst, xml, ren))) { if (ren->instanceMaterial) { ren->instanceMaterial->base.prev = &instmat->base; instmat->base.next = &ren->instanceMaterial->base; } ren->instanceMaterial = instmat; } } else if (xml_tag_eq(xren, _s_dae_extra)) { ren->extra = tree_fromxml(heap, ren, xml); } xren = xren->next; } } else if (xml_tag_eq(xml, _s_dae_extra)) { evalScene->extra = tree_fromxml(heap, evalScene, xml); } xml = xml->next; } return evalScene; } AK_HIDE void dae_scene(DAEState * __restrict dst, xml_t * __restrict xml) { AkDoc *doc; AkHeap *heap; heap = dst->heap; doc = dst->doc; xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_instance_visual_scene)) { doc->scene.visualScene = dae_instVisualScene(dst, xml, doc); } else if (xml_tag_eq(xml, _s_dae_extra)) { doc->scene.extra = tree_fromxml(heap, doc, xml); } xml = xml->next; } } ================================================ FILE: src/io/dae/core/scene.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_scene_h #define dae_scene_h #include "../common.h" AK_HIDE void* dae_vscene(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); AK_HIDE AkInstanceBase* dae_instVisualScene(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); AK_HIDE void dae_scene(DAEState * __restrict dst, xml_t * __restrict xml); #endif /* dae_scene_h */ ================================================ FILE: src/io/dae/core/skin.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "skin.h" #include "../../../array.h" #include "source.h" #include "enum.h" AK_HIDE AkSkin* dae_skin(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkSkin *skin; AkSkinDAE *skindae; const xml_t *sval; bool foundBindShape; heap = dst->heap; skin = ak_heap_calloc(heap, memp, sizeof(*skin)); skindae = ak_heap_calloc(heap, skin, sizeof(*skindae)); foundBindShape = false; ak_heap_setUserData(heap, skin, skindae); flist_sp_insert(&dst->linkedUserData, skin); url_set(dst, xml, _s_dae_source, memp, &skindae->baseGeom); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_bind_shape_matrix) && (sval = xmls(xml))) { xml_strtof_fast(sval, skin->bindShapeMatrix[0], 16); glm_mat4_transpose(skin->bindShapeMatrix); foundBindShape = true; } else if (xml_tag_eq(xml, _s_dae_source)) { AkSource *source; if ((source = dae_source(dst, xml, NULL, 0))) { source->next = skindae->source; skindae->source = source; } } else if (xml_tag_eq(xml, _s_dae_joints)) { AkInput *inp; xml_t *xjoints; xjoints = xml->val; while (xjoints) { if (xml_tag_eq(xjoints, _s_dae_input)) { inp = ak_heap_calloc(heap, skin, sizeof(*inp)); inp->semanticRaw = xmla_strdup_by(xjoints, heap, _s_dae_semantic, inp); if (!inp->semanticRaw) { ak_free(inp); } else { AkURL *url; AkEnum inputSemantic; inputSemantic = dae_semantic(inp->semanticRaw); if (inputSemantic < 0) inputSemantic = AK_INPUT_OTHER; inp->semantic = inputSemantic; inp->offset = xmla_u32(xmla(xjoints, _s_dae_offset), 0); inp->semantic = dae_semantic(inp->semanticRaw); url = url_from(xjoints, _s_dae_source, memp); if (inputSemantic == AK_INPUT_JOINT) { skindae->joints.joints = inp; rb_insert(dst->inputmap, inp, url); } else if (inputSemantic == AK_INPUT_INV_BIND_MATRIX) { skindae->joints.invBindMatrix = inp; rb_insert(dst->inputmap, inp, url); } else { /* do not support other inputs until needed, probably will not. */ ak_free(inp); } } } xjoints = xjoints->next; } } else if (xml_tag_eq(xml, _s_dae_vertex_weights)) { AkBoneWeights *wei; AkInput *inp; xml_t *xwei; wei = ak_heap_calloc(heap, skin, sizeof(*wei)); xwei = xml->val; while (xwei) { if (xml_tag_eq(xwei, _s_dae_input)) { inp = ak_heap_calloc(heap, skin, sizeof(*inp)); inp->semanticRaw = xmla_strdup_by(xwei, heap, _s_dae_semantic, inp); if (!inp->semanticRaw) { ak_free(inp); } else { AkURL *url; AkEnum inputSemantic; inputSemantic = dae_semantic(inp->semanticRaw); if (inputSemantic < 0) inputSemantic = AK_INPUT_OTHER; inp->semantic = inputSemantic; inp->offset = xmla_u32(xmla(xwei, _s_dae_offset), 0); inp->semantic = dae_semantic(inp->semanticRaw); url = url_from(xwei, _s_dae_source, memp); if (inputSemantic == AK_INPUT_JOINT) { skindae->weights.joints = inp; rb_insert(dst->inputmap, inp, url); } else if (inputSemantic == AK_INPUT_WEIGHT) { skindae->weights.weights = inp; rb_insert(dst->inputmap, inp, url); } else { /* do not support other inputs until needed, probably will not. */ ak_free(inp); } skindae->inputCount++; } } else if (xml_tag_eq(xwei, _s_dae_vcount) && (sval = xmls(xwei))) { size_t count, sz, i; uint32_t *pSum, *pCount, next; if ((count = xml_strtok_count_fast(sval, NULL)) > 0) { sz = count * sizeof(uint32_t); /* we use same temp array to store sum of counts to avoid to create new pointer. Array layout: | pCount | pCountSum | */ wei->counts = pCount = ak_heap_alloc(heap, wei, 2 * sz); /* must equal to position count, we may fix this in postscript */ wei->nVertex = count; xml_strtoui_fast(sval, pCount, (unsigned long)count); /* calculate sum */ pSum = wei->counts + count; for (i = next = 0; i < count; i++) { pSum[i] = next; next = pCount[i] + next; } } } else if (xml_tag_eq(xwei, _s_dae_v) && (sval = xmls(xwei))) { AkUIntArray *intArray; AkResult ret; ret = xml_strtoui_array(heap, wei, sval, &intArray); if (ret == AK_OK) skindae->weights.v = intArray; } /* else if (xml_tag_eq(xwei, _s_dae_extra)) { wei->extra = tree_fromxml(heap, wei, xwei); } */ xwei = xwei->next; } skin->weights = (void *)wei; } else if (xml_tag_eq(xml, _s_dae_extra)) { skindae->extra = tree_fromxml(heap, skindae, xml); } xml = xml->next; } if (!foundBindShape) { glm_mat4_identity(skin->bindShapeMatrix); } return skin; } ================================================ FILE: src/io/dae/core/skin.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_skin_h #define dae_skin_h #include "../common.h" AK_HIDE AkSkin* dae_skin(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); #endif /* dae_skin_h */ ================================================ FILE: src/io/dae/core/source.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "source.h" #include "../core/asset.h" #include "../core/techn.h" #include "../core/enum.h" #include "../core/value.h" AK_HIDE AkSource* dae_source(DAEState * __restrict dst, xml_t * __restrict xml, AkEnum (*asEnum)(const char *name, size_t nameLen), AkTypeId enumType) { AkHeap *heap; AkSource *source; AkBuffer *buffer; AkTechnique *tq; AkAccessor *acc; AkAccessorDAE *accdae; const xml_t *sval; void *rootmemp, *tempmem; uint32_t count; AkTypeId t; bool isName; heap = dst->heap; rootmemp = ak_heap_data(heap->data); tempmem = dst->tempmem; isName = false; buffer = NULL; source = ak_heap_calloc(heap, tempmem, sizeof(*source)); ak_setypeid(source, AKT_SOURCE); xmla_setid(xml, heap, source); source->name = xmla_strdup_by(xml, heap, _s_dae_name, source); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_asset)) { (void)dae_asset(dst, xml, source, NULL); } else if (xml_tag_eq(xml, _s_dae_techniquec)) { xml_t *xacc; AkDataParam *dp_last; if ((xacc = xml_elem(xml, _s_dae_accessor))) { acc = ak_heap_calloc(heap, rootmemp, sizeof(*acc)); accdae = ak_heap_calloc(heap, tempmem, sizeof(*accdae)); ak_heap_setUserData(heap, acc, accdae); acc->count = xmla_u32(xmla(xacc, _s_dae_count), 0); accdae->offset = xmla_u32(xmla(xacc, _s_dae_offset), 0); accdae->stride = xmla_u32(xmla(xacc, _s_dae_stride), 1); ak_setypeid(acc, AKT_ACCESSOR); url_set(dst, xacc, _s_dae_source, accdae, &accdae->source); xacc = xacc->val; dp_last = NULL; while (xacc) { AkDataParam *dp; dp = ak_heap_calloc(heap, accdae, sizeof(*dp)); sid_set(xacc, heap, dp); dp->name = xmla_strdup_by(xacc, heap, _s_dae_name, dp); dae_dtype(xmla_strdup_by(xacc, heap, _s_dae_type, dp), &dp->type); AK_APPEND_FLINK(accdae->param, dp_last, dp); xacc = xacc->next; } source->tcommon = acc; /* append accessor to global list */ /* this will be prepared in postprocess */ flist_sp_insert(&dst->accessors, acc); } } else if (xml_tag_eq(xml, _s_dae_technique)) { tq = dae_techn(xml, heap, source); tq->next = source->technique; source->technique = tq; } else if (xml_valtype(xml) == XML_STRING && (sval = xmls(xml))) { count = xmla_u32(xmla(xml, _s_dae_count), 0); buffer = ak_heap_alloc(heap, rootmemp, sizeof(*buffer)); buffer->name = xmla_strdup_by(xml, heap, _s_dae_name, buffer); source->buffer = buffer; xmla_setid(xml, heap, buffer); if (xml_tag_eq(xml, _s_dae_float_array)) { buffer->length = sizeof(float) * count; buffer->data = ak_heap_alloc(heap, buffer, buffer->length); xml_strtof_fast(sval, buffer->data, count); ak_setUserData(buffer, (void *)(uintptr_t)AKT_FLOAT); } else if (xml_tag_eq(xml, _s_dae_int_array)) { buffer->length = sizeof(uint32_t) * count; buffer->data = ak_heap_alloc(heap, buffer, buffer->length); xml_strtoi_fast(sval, buffer->data, count); ak_setUserData(buffer, (void *)(uintptr_t)AKT_INT); } else if (xml_tag_eq(xml, _s_dae_bool_array)) { buffer->length = sizeof(bool) * count; buffer->data = ak_heap_alloc(heap, buffer, buffer->length); xml_strtob_fast(sval, buffer->data, count); ak_setUserData(buffer, (void *)(uintptr_t)AKT_BOOL); } else if ((xml_tag_eq(xml, _s_dae_Name_array) & (1|(t = AKT_NAME))) || (xml_tag_eq(xml, _s_dae_IDREF_array) & (1|(t = AKT_IDREF))) || (xml_tag_eq(xml, _s_dae_SIDREF_array) & (1|(t = AKT_SIDREF))) || (xml_tag_eq(xml, _s_dae_token_array) & (1|(t = AKT_TOKEN)))) { char *pData, **iter, *tok, *tok_begin, *end, c; const xml_t *v; size_t srclen, toklen, enumLen; uint32_t idx; AkEnum enumValue; /* |pSTR1|pSTR2|pSTR3|STR1\0STR2\0STR3| the last one is pointer to all data */ isName = true; idx = 0; if (asEnum) { ak_setUserData(buffer, (void *)(uintptr_t)enumType); enumLen = ak_typeDesc(enumType)->size; buffer->length = enumLen * count; buffer->data = ak_heap_alloc(heap, buffer, buffer->length); pData = buffer->data; if ((v = sval) && (tok = v->val)) { do { if (idx >= count) break; srclen = v->valsize; end = tok + srclen; do { while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; tok_begin = tok; while (tok < end && !((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; toklen = tok - tok_begin; enumValue = asEnum(tok_begin, toklen); memcpy(pData + idx * enumLen, &enumValue, enumLen); idx++; } while (idx < count && tok < end); } while ((v = xmls_next(v)) && (tok = v->val)); } } else { ak_setUserData(buffer, (void *)(uintptr_t)t); buffer->length = sizeof(char *) * count * 2 + xmls_sumlen(sval) + 1 /* NULL */; iter = buffer->data = ak_heap_alloc(heap, buffer, buffer->length); pData = (char *)buffer->data + sizeof(char *) * (count + 1); iter[count] = pData; if ((v = sval) && (tok = v->val)) { do { if (idx >= count) break; srclen = v->valsize; end = tok + srclen; do { while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; tok_begin = tok; while (tok < end && !((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; toklen = tok - tok_begin; memcpy(pData, tok_begin, toklen); iter[idx++] = pData; pData += toklen; *pData++ = '\0'; } while (idx < count && tok < end); } while ((v = xmls_next(v)) && (tok = v->val)); } } /* if asEnum */ } } xml = xml->next; } if (source->tcommon && isName && asEnum && (accdae = ak_userData(source->tcommon))) { accdae->bound = 1; accdae->stride = 1; } return source; } ================================================ FILE: src/io/dae/core/source.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_source_h #define dae_source_h #include "../common.h" AK_HIDE AkSource* dae_source(DAEState * __restrict dst, xml_t * __restrict xml, AkEnum (*asEnum)(const char *name, size_t nameLen), AkTypeId enumType); #endif /* dae_source_h */ ================================================ FILE: src/io/dae/core/spline.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "spline.h" #include "source.h" #include "enum.h" #include "vert.h" AK_HIDE AkObject* dae_spline(DAEState * __restrict dst, xml_t * __restrict xml, AkGeometry * __restrict geom) { AkHeap *heap; AkObject *obj; AkSpline *spline; AkSource *source; heap = dst->heap; xml = xml->val; obj = ak_objAlloc(heap, geom, sizeof(*spline), AK_GEOMETRY_SPLINE, true); spline = ak_objGet(obj); spline->geom = geom; spline->closed = xmla_u32(xmla(xml, _s_dae_closed), 0); while (xml) { if (xml_tag_eq(xml, _s_dae_source)) { if ((source = dae_source(dst, xml, NULL, 0))) { source->next = spline->source; spline->source = source; } } else if (xml_tag_eq(xml, _s_dae_control_vertices)) { spline->cverts = dae_vert(dst, xml, obj); } else if (xml_tag_eq(xml, _s_dae_extra)) { spline->extra = tree_fromxml(heap, obj, xml); } xml = xml->next; } return obj; } ================================================ FILE: src/io/dae/core/spline.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_spline_h #define dae_spline_h #include "../common.h" AK_HIDE AkObject* dae_spline(DAEState * __restrict dst, xml_t * __restrict xml, AkGeometry * __restrict geom); #endif /* dae_spline_h */ ================================================ FILE: src/io/dae/core/techn.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "techn.h" #include "../common.h" AkTechnique* dae_techn(xml_t * __restrict xml, AkHeap * __restrict heap, void * __restrict memp) { AkTechnique *techn; techn = ak_heap_calloc(heap, memp, sizeof(*techn)); techn->profile = xmla_strdup_by(xml, heap, _s_dae_profile, techn); techn->xmlns = xmla_strdup_by(xml, heap, _s_dae_xmlns, techn); techn->chld = tree_fromxml(heap, techn, xml); return techn; } ================================================ FILE: src/io/dae/core/techn.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_technique_h #define dae_technique_h #include "../common.h" AK_HIDE AkTechnique* dae_techn(xml_t * __restrict xml, AkHeap * __restrict heap, void * __restrict memp); #endif /* dae_technique_h */ ================================================ FILE: src/io/dae/core/triangle.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "triangle.h" #include "enum.h" #include "../../../array.h" AK_HIDE AkTriangles* dae_triangles(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp, AkTriangleMode mode) { AkTriangles *tri; AkHeap *heap; uint32_t indexoff; heap = dst->heap; tri = ak_heap_calloc(heap, memp, sizeof(*tri)); tri->mode = mode; tri->base.type = AK_PRIMITIVE_TRIANGLES; tri->base.name = xmla_strdup_by(xml, heap, _s_dae_name, tri); tri->base.bindmaterial = xmla_strdup_by(xml, heap, _s_dae_material, tri); tri->base.nPolygons = xmla_u32(xmla(xml, _s_dae_count), 0); indexoff = 0; xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_input)) { AkInput *inp; inp = ak_heap_calloc(heap, tri, sizeof(*inp)); inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp); if (!inp->semanticRaw) { ak_free(inp); } else { AkURL *url; AkEnum inputSemantic; inputSemantic = dae_semantic(inp->semanticRaw); if (inputSemantic < 0) inputSemantic = AK_INPUT_OTHER; inp->semantic = inputSemantic; inp->offset = xmla_u32(xmla(xml, _s_dae_offset), 0); inp->set = xmla_u32(xmla(xml, _s_dae_set), 0); url = url_from(xml, _s_dae_source, memp); rb_insert(dst->inputmap, inp, url); if ((uint32_t)inp->semantic != AK_INPUT_SEMANTIC_VERTEX) { inp->semantic = dae_semantic(inp->semanticRaw); inp->next = tri->base.input; tri->base.input = inp; tri->base.inputCount++; if (inp->offset > indexoff) indexoff = inp->offset; } else { dae_vertmap_add(dst, inp, &tri->base); /* don't store VERTEX because it will be duplicated to all prims */ // tri->base.reserved1 = inp->offset; // tri->base.reserved2 = inp->set; // ak_free(inp); } } } else if (xml_tag_eq(xml, _s_dae_p) && xml->val) { AkUIntArray *uintArray; AkResult ret; ret = xml_strtoui_array(heap, tri, xml->val, &uintArray); if (ret == AK_OK) tri->base.indices = uintArray; } else if (xml_tag_eq(xml, _s_dae_extra)) { tri->base.extra = tree_fromxml(heap, tri, xml); } xml = xml->next; } tri->base.indexStride = indexoff + 1; return tri; } ================================================ FILE: src/io/dae/core/triangle.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_triangles_h #define dae_triangles_h #include "../common.h" AK_HIDE AkTriangles* dae_triangles(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp, AkTriangleMode mode); #endif /* dae_triangles_h */ ================================================ FILE: src/io/dae/core/value.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "value.h" #include "../fx/samp.h" #include "../1.4/surface.h" #define AK_CUSTOM_TYPE_SURFACE 1 typedef struct valpair { const char *key; AkTypeId val; int m; /* this is userData for _CUSTOM */ int n; int size; } valpair; static int valpair_cmp1(const void *, const void *); static int valpair_cmp2(const void *, const void *); static int valpair_cmpxt(const void *, const void *); static valpair valmap[] = { {_s_dae_string, AKT_STRING, 1, 1, sizeof(char *)}, {_s_dae_bool, AKT_BOOL, 1, 1, sizeof(bool)}, {_s_dae_bool2, AKT_BOOL2, 1, 2, sizeof(bool[2])}, {_s_dae_bool3, AKT_BOOL3, 1, 3, sizeof(bool[3])}, {_s_dae_bool4, AKT_BOOL4, 1, 4, sizeof(bool[4])}, {_s_dae_int, AKT_INT, 1, 1, sizeof(int)}, {_s_dae_int2, AKT_INT2, 1, 2, sizeof(int[2])}, {_s_dae_int3, AKT_INT3, 1, 3, sizeof(int[3])}, {_s_dae_int4, AKT_INT4, 1, 4, sizeof(int[4])}, {_s_dae_float, AKT_FLOAT, 1, 1, sizeof(float)}, {_s_dae_float2, AKT_FLOAT2, 1, 2, sizeof(float[2])}, {_s_dae_float3, AKT_FLOAT3, 1, 3, sizeof(float[3])}, {_s_dae_float4, AKT_FLOAT4, 1, 4, sizeof(float[4])}, {_s_dae_float2x2, AKT_FLOAT2x2, 2, 2, sizeof(float[2][2])}, {_s_dae_float3x3, AKT_FLOAT3x3, 3, 3, sizeof(float[3][3])}, {_s_dae_float4x4, AKT_FLOAT4x4, 4, 4, sizeof(float[4][4])}, /* samplers */ {_s_dae_sampler1d, AKT_SAMPLER1D, 1, 1, sizeof(AkSampler)}, {_s_dae_sampler2d, AKT_SAMPLER2D, 1, 1, sizeof(AkSampler)}, {_s_dae_sampler3d, AKT_SAMPLER3D, 1, 1, sizeof(AkSampler)}, {_s_dae_sampler_cube, AKT_SAMPLER_CUBE, 1, 1, sizeof(AkSampler)}, {_s_dae_sampler_rect, AKT_SAMPLER_RECT, 1, 1, sizeof(AkSampler)}, {_s_dae_sampler_depth, AKT_SAMPLER_DEPTH, 1, 1, sizeof(AkSampler)}, /* COLLADA 1.4 */ { _s_dae_surface, AKT_CUSTOM, AK_CUSTOM_TYPE_SURFACE, 1, sizeof(AkDae14Surface) }, }; static size_t valmapLen = 0; AK_HIDE void dae_dtype(const char *typeName, AkTypeDesc *type) { valpair *found; if (valmapLen == 0) { valmapLen = AK_ARRAY_LEN(valmap); qsort(valmap, valmapLen, sizeof(valmap[0]), valpair_cmp1); } found = bsearch(typeName, valmap, valmapLen, sizeof(valmap[0]), valpair_cmp2); if (!found) { type->typeId = AKT_UNKNOWN; type->typeName = NULL; type->size = 0; return; } type->size = found->size; type->typeId = found->val; type->typeName = found->key; } AK_HIDE AkValue* dae_value(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkValue *val; valpair *found; const xml_t *sval; heap = dst->heap; sval = xmls(xml); if (valmapLen == 0) { valmapLen = AK_ARRAY_LEN(valmap); qsort(valmap, valmapLen, sizeof(valmap[0]), valpair_cmp1); } found = bsearch(xml, valmap, valmapLen, sizeof(valmap[0]), valpair_cmpxt); if (!found) return NULL; val = ak_heap_calloc(heap, memp, sizeof(*val)); val->type.size = found->size; val->type.typeId = found->val; val->type.typeName = found->key; switch (found->val) { case AKT_STRING: val->value = xml_strdup(xml, heap, val); break; case AKT_BOOL: case AKT_BOOL2: case AKT_BOOL3: case AKT_BOOL4:{ AkBool *boolVal; boolVal = ak_heap_calloc(heap, val, sizeof(*boolVal) * found->m * found->n); xml_strtob_fast(sval, boolVal, found->m * found->n); val->value = boolVal; break; } case AKT_INT: case AKT_INT2: case AKT_INT3: case AKT_INT4:{ AkInt *intVal; intVal = ak_heap_calloc(heap, memp, sizeof(*intVal) * found->m * found->n); xml_strtoi_fast(sval, intVal, found->m * found->n); val->value = intVal; break; } case AKT_FLOAT: case AKT_FLOAT2: case AKT_FLOAT3: case AKT_FLOAT4: case AKT_FLOAT2x2: case AKT_FLOAT3x3: case AKT_FLOAT4x4:{ AkFloat *floatVal; floatVal = ak_heap_calloc(heap, memp, sizeof(*floatVal) * found->m * found->n); xml_strtof_fast(sval, floatVal, found->m * found->n); val->value = floatVal; break; } case AKT_SAMPLER1D: case AKT_SAMPLER2D: case AKT_SAMPLER3D: case AKT_SAMPLER_CUBE: case AKT_SAMPLER_RECT: case AKT_SAMPLER_DEPTH: { AkSampler *sampler; if ((sampler = dae_sampler(dst, xml, val))) { AkTexture *tex; tex = ak_heap_calloc(heap, val, sizeof(*tex)); ak_setypeid(tex, AKT_TEXTURE); tex->sampler = sampler; tex->type = found->val; val->value = tex; } break; } case AKT_CUSTOM: { switch (found->m) { case AK_CUSTOM_TYPE_SURFACE: { val->value = dae14_surface(dst, xml, val); break; } } break; } default: break; } return val; } static int valpair_cmp1(const void * a, const void * b) { return strcmp(((const valpair *)a)->key, ((const valpair *)b)->key); } static int valpair_cmp2(const void * a, const void * b) { return strcmp(a, ((const valpair *)b)->key); } static int valpair_cmpxt(const void *a, const void *b) { return xml_tag_cmp(a, ((const valpair *)b)->key); } ================================================ FILE: src/io/dae/core/value.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_value_h #define dae_value_h #include "../common.h" AK_HIDE AkValue* dae_value(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); AK_HIDE void dae_dtype(const char *typeName, AkTypeDesc *type); #endif /* dae_value_h */ ================================================ FILE: src/io/dae/core/vert.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "vert.h" #include "enum.h" AK_HIDE AkVertices* dae_vert(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkVertices *vert; heap = dst->heap; vert = ak_heap_calloc(heap, memp, sizeof(*vert)); xmla_setid(xml, heap, vert); vert->name = xmla_strdup_by(xml, heap, _s_dae_name, vert); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_input)) { AkInput *inp; inp = ak_heap_calloc(heap, vert, sizeof(*inp)); inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp); if (!inp->semanticRaw) { ak_free(inp); } else { AkURL *url; inp->semantic = dae_semantic(inp->semanticRaw); url = url_from(xml, _s_dae_source, memp); rb_insert(dst->inputmap, inp, url); inp->next = vert->input; vert->input = inp; vert->inputCount++; } } else if (xml_tag_eq(xml, _s_dae_extra)) { vert->extra = tree_fromxml(heap, vert, xml); } xml = xml->next; } return vert; } ================================================ FILE: src/io/dae/core/vert.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_vertices_h #define dae_vertices_h #include "../common.h" AK_HIDE AkVertices* dae_vert(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); #endif /* dae_vertices_h */ ================================================ FILE: src/io/dae/ctlr.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "common.h" AK_EXPORT AkGeometry* ak_baseGeometry(AkURL * __restrict baseurl) { void *found; AkController *ctlr; AkSkinDAE *skindae; AkMorphDAE *morphdae; AkTypeId foundType; if ((found = ak_getObjectByUrl(baseurl))) { foundType = ak_typeid(found); switch (foundType) { case AKT_GEOMETRY: return found; case AKT_CONTROLLER: { /* `ctlr->data` is the cross-format AkSkin/AkMorph (no baseGeom). DAE-specific URL lives on the AkSkinDAE/AkMorphDAE attached as userData by the parser. */ ctlr = found; if (ctlr->type == AK_CONTROLLER_SKIN && (skindae = ak_userData(ctlr->data))) { return ak_baseGeometry(&skindae->baseGeom); } else if (ctlr->type == AK_CONTROLLER_MORPH && (morphdae = ak_userData(ctlr->data))) { return ak_baseGeometry(&morphdae->baseGeom); } } default: break; } } return NULL; } ================================================ FILE: src/io/dae/dae.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "dae.h" #include "common.h" #include "core/asset.h" #include "core/cam.h" #include "core/light.h" #include "core/geom.h" #include "core/ctlr.h" #include "core/node.h" #include "core/scene.h" #include "core/anim.h" #include "fx/effect.h" #include "fx/img.h" #include "fx/mat.h" #include "postscript.h" #include "../../id.h" #include "../../../include/ak/path.h" static ak_enumpair daeVersions[] = { {"1.5.0", AK_COLLADA_VERSION_150}, {"1.5", AK_COLLADA_VERSION_150}, {"1.4.1", AK_COLLADA_VERSION_141}, {"1.4.0", AK_COLLADA_VERSION_140}, {"1.4", AK_COLLADA_VERSION_140}, {NULL, 0} }; typedef void*(*AkLoadLibraryItemFn)(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); static void ak_daeFreeDupl(RBTree *, RBNode *); static void dae_lib(DAEState * __restrict dst, xml_t * __restrict xml, const char * __restrict name, AkLoadLibraryItemFn loadfn, AkLibrary ** __restrict dest); AK_HIDE AkResult dae_doc(AkDoc ** __restrict dest, const char * __restrict filepath) { AkHeap *heap; AkDoc *doc; const xml_doc_t *xdoc; xml_t *xml, *assetEl; AkAssetInf *inf; xml_attr_t *versionAttr; void *xmlString; AkLibraries *libs; FListItem *freeUsrData; DAEState dstVal, *dst; size_t xmlSize; AkResult ret; if ((ret = ak_readfile(filepath, NULL, &xmlString, &xmlSize)) != AK_OK) return ret; xdoc = xml_parse(xmlString, XML_PREFIXES | XML_READONLY); if (!xdoc || !(xml = xdoc->root)) { if (xdoc) free((void *)xdoc); ak_releasefile(xmlString, xmlSize); return AK_ERR; } heap = ak_heap_new(NULL, NULL, NULL); doc = ak_heap_calloc(heap, NULL, sizeof(*doc)); doc->inf = ak_heap_calloc(heap, doc, sizeof(*doc->inf)); doc->inf->name = filepath; doc->inf->dir = ak_path_dir(heap, doc, filepath); doc->inf->flipImage = true; doc->inf->ftype = AK_FILE_TYPE_COLLADA; doc->coordSys = AK_YUP; /* Default */ /* for fixing skin and morph vertices */ doc->reserved = rb_newtree_ptr(); ((RBTree *)doc->reserved)->onFreeNode = ak_daeFreeDupl; if (doc->inf->dir) doc->inf->dirlen = strlen(doc->inf->dir); ak_heap_setdata(heap, doc); ak_id_newheap(heap); memset(&dstVal, 0, sizeof(dstVal)); dstVal.doc = doc; dstVal.heap = heap; dstVal.tempmem = ak_heap_alloc(heap, doc, sizeof(void*)); dstVal.meshInfo = rb_newtree_ptr(); dstVal.inputmap = rb_newtree_ptr(); dstVal.texmap = rb_newtree_ptr(); dstVal.instanceMap = rb_newtree_ptr(); dstVal.meshTargets = rb_newtree_ptr(); dst = &dstVal; dstVal.texmap->userData = dst; /* get version info */ /* because it is current and most used version */ dst->version = AK_COLLADA_VERSION_141; if ((versionAttr = xmla(xml, _s_dae_version))) { ak_enumpair *v; for (v = daeVersions; v->key; v++) { if (!strncmp(v->key, versionAttr->val, versionAttr->valsize)) { dst->version = v->val; break; } } } libs = &doc->lib; assetEl = NULL; xml = xml->val; /* with default Asset Parameters */ assetEl = xml_elem(xml->parent, _s_dae_asset); if ((inf = dae_asset(dst, assetEl, doc, &doc->inf->base))) { doc->coordSys = inf->coordSys; doc->unit = inf->unit; } while (xml) { if (xml_tag_eq(xml, _s_dae_lib_cameras)) { dae_lib(dst, xml, _s_dae_camera, dae_cam, &libs->cameras); } else if (xml_tag_eq(xml, _s_dae_lib_lights)) { dae_lib(dst, xml, _s_dae_light, dae_light, &libs->lights); } else if (xml_tag_eq(xml, _s_dae_lib_geometries)) { dae_lib(dst, xml, _s_dae_geometry, dae_geom, &libs->geometries); } else if (xml_tag_eq(xml, _s_dae_lib_effects)) { dae_lib(dst, xml, _s_dae_effect, dae_effect, &libs->effects); } else if (xml_tag_eq(xml, _s_dae_lib_images)) { dae_lib(dst, xml, _s_dae_image, dae_image, &libs->libimages); } else if (xml_tag_eq(xml, _s_dae_lib_materials)) { dae_lib(dst, xml, _s_dae_material, dae_material, &libs->materials); } else if (xml_tag_eq(xml, _s_dae_lib_controllers)) { dae_lib(dst, xml, _s_dae_controller, dae_ctlr, &libs->controllers); } else if (xml_tag_eq(xml, _s_dae_lib_visual_scenes)) { dae_lib(dst, xml, _s_dae_visual_scene, dae_vscene, &libs->visualScenes); } else if (xml_tag_eq(xml, _s_dae_lib_nodes)) { dae_lib(dst, xml, _s_dae_node, dae_node2, &libs->nodes); } else if (xml_tag_eq(xml, _s_dae_lib_animations)) { dae_lib(dst, xml, _s_dae_animation, dae_anim, &libs->animations); } else if (xml_tag_eq(xml, _s_dae_scene)) { dae_scene(dst, xml); } xml = xml->next; } *dest = doc; /* post-parse operations */ dae_postscript(dst); /* cleanup up details */ freeUsrData = dst->linkedUserData; while (freeUsrData) { void *tofree; if ((tofree = ak_userData(freeUsrData->data))) ak_free(tofree); ak_heap_ext_rm(heap, ak__alignof(freeUsrData->data), AK_HEAP_NODE_FLAGS_USR); freeUsrData = freeUsrData->next; } ak_free(dstVal.tempmem); flist_sp_destroy(&dst->linkedUserData); rb_destroy(dstVal.meshInfo); rb_destroy(dstVal.inputmap); rb_destroy(dstVal.texmap); rb_destroy(dstVal.instanceMap); flist_sp_destroy(&dstVal.vertMap); rb_destroy(dstVal.meshTargets); if (xdoc) free((void *)xdoc); if (xmlString) ak_releasefile(xmlString, xmlSize); /* TODO: memory leak, free this RBTree*/ /* rb_destroy(doc->reserved); */ return AK_OK; } static void ak_daeFreeDupl(RBTree *tree, RBNode *node) { if (node == tree->nullNode) return; ak_free(node->val); } static void dae_lib(DAEState * __restrict dst, xml_t * __restrict xml, const char * __restrict name, AkLoadLibraryItemFn loadfn, AkLibrary ** __restrict dest) { AkHeap *heap; AkDoc *doc; AkLibrary *lib; AkOneWayIterBase *it; heap = dst->heap; doc = dst->doc; lib = ak_heap_calloc(heap, doc, sizeof(*lib)); lib->name = xmla_strdup_by(xml, heap, _s_dae_name, lib); xml = xml->val; while (xml) { if (xml_tag_eq(xml, name)) { if ((it = loadfn(dst, xml, lib))) { it->next = lib->chld; lib->chld = it; lib->count++; } } else if (xml_tag_eq(xml, _s_dae_extra)) { lib->extra = tree_fromxml(heap, lib, xml); } xml = xml->next; } lib->next = *dest; *dest = lib; } ================================================ FILE: src/io/dae/dae.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_h #define dae_h #include "../../../include/ak/assetkit.h" #include AK_HIDE AkResult dae_doc(AkDoc ** __restrict dest, const char * __restrict filepath); #endif /* dae_h */ ================================================ FILE: src/io/dae/fixup/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/io/dae/fixup/angle.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "angle.h" /* COLLADA uses degress for all angles, convert desgress to radians. It may exists in these places as far as I know: 1. Rotate element - fixed in place 2. Perspective (xfov, yfov) - fixed in place 3. Light (fallofAngle) - fixed in place 4. Skew element - fixed in place 5. Animation data (output, tangents...)!!!! - NEEDS TO BE FIXED? */ static void dae_cvtAnglesAt(AkAccessor * __restrict acc, AkBuffer * __restrict buff, uint32_t paramIndex) { AkAccessorDAE *accdae; float *pbuff; size_t i, count, st, off; if (!acc || !buff || !buff->data || !(accdae = ak_userData(acc))) return; acc->componentType = (AkTypeId)(uintptr_t)ak_userData(buff); if (acc->componentType != AKT_FLOAT) return; st = accdae->stride ? accdae->stride : 1; if (paramIndex >= st) return; off = accdae->offset + paramIndex; count = acc->count; pbuff = buff->data; for (i = 0; i < count; i++) glm_make_rad(pbuff + off + i * st); } AK_HIDE void dae_cvtAngles(AkAccessor * __restrict acc, AkBuffer * __restrict buff, const char * __restrict paramName) { AkAccessorDAE *accdae; AkDataParam *param; uint32_t index; if (!(accdae = ak_userData(acc))) return; index = 0; param = accdae->param; while (param) { if (param->name && strcasecmp(param->name, paramName) == 0) dae_cvtAnglesAt(acc, buff, index); index++; param = param->next; } } static void dae_fixAngleTangent(AkInput * __restrict inp, uint32_t outputAngleIndex, uint32_t outputStride) { AkAccessor *acc; AkAccessorDAE *accdae; AkBuffer *buff; uint32_t st; uint32_t idx; if (!inp || !(acc = inp->accessor) || !(accdae = ak_userData(acc)) || !(buff = ak_getObjectByUrl(&accdae->source))) return; st = accdae->stride ? accdae->stride : 1; if (st == outputStride) { idx = outputAngleIndex; } else if (st >= outputStride * 2 && outputAngleIndex * 2 + 1 < st) { /* Bezier-style tangents are usually (time, value) pairs. Only the value component is angular; the time component stays seconds. */ idx = outputAngleIndex * 2 + 1; } else if (st == 1) { idx = 0; } else if (outputAngleIndex < st) { idx = outputAngleIndex; } else { return; } dae_cvtAnglesAt(acc, buff, idx); } /* TODO: This works for BERZIER but HERMITE?? */ AK_HIDE void dae_fixAngles(DAEState * __restrict dst) { /* TODO: */ FListItem *item; AkAnimSampler *sampler; AkDataParam *param; AkAccessor *acc; AkBuffer *buff; AkAccessorDAE *accdae; uint32_t index, outStride; item = dst->toRadiansSampelers; while (item) { sampler = item->data; acc = NULL; buff = NULL; if ((acc = sampler->outputInput->accessor) && (accdae = ak_userData(acc)) && (buff = ak_getObjectByUrl(&accdae->source))) { bool foundAngle; foundAngle = false; index = 0; outStride = accdae->stride ? accdae->stride : 1; if ((param = accdae->param)) { do { if (param->name && strcasecmp(param->name, _s_dae_angle) == 0) { foundAngle = true; break; } index++; } while ((param = param->next)); } if (!foundAngle) goto nxt_sampler; dae_cvtAngles(acc, buff, _s_dae_angle); dae_fixAngleTangent(sampler->inTangentInput, index, outStride); dae_fixAngleTangent(sampler->outTangentInput, index, outStride); } nxt_sampler: item = item->next; } flist_sp_destroy(&dst->toRadiansSampelers); } ================================================ FILE: src/io/dae/fixup/angle.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_fixangle_h #define dae_fixangle_h #include "../common.h" AK_HIDE void dae_fixAngles(DAEState * __restrict dst); AK_HIDE void dae_cvtAngles(AkAccessor * __restrict acc, AkBuffer * __restrict buff, const char * __restrict paramName); #endif /* dae_fixangle_h */ ================================================ FILE: src/io/dae/fixup/channel.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "channel.h" #include #include typedef struct DAEMatrixAnimFix { struct DAEMatrixAnimFix *next; AkAccessor *acc; } DAEMatrixAnimFix; /*---------------------------------------------------------------------------- * Channel target parsing. * * Accepted forms (DAE COLLADA 1.4 §5.3.2 + real-world exporter quirks): * "id(N)" — bare source id + index * "prefix/.../id(N)" — SID-style path, last segment is "id(N)" * * `outIdLen`/`outId` point into the input buffer (no allocation); caller * uses ak_heap_strndup or ak_getObjectById_n if it needs to keep it. *--------------------------------------------------------------------------*/ static bool dae_parseChannelTargetIndexed(const char *target, const char **outIdStart, size_t *outIdLen, uint32_t *outIdx) { const char *open, *close, *idStart, *p; long idx; char *end; if (!target) return false; /* Trailing "(N)" must be present and well-formed. */ if (!(open = strrchr(target, '(')) || !(close = strchr(open + 1, ')')) || close == open + 1) return false; idx = strtol(open + 1, &end, 10); if (end != close || idx < 0) return false; /* Id portion: everything from the last '/' (or start) up to '('. */ if ((idStart = strrchr(target, '/')) && idStart < open) idStart++; else idStart = target; if (idStart >= open) return false; /* "(N)" with empty id */ /* Matrix element targets use forms like "matrix(0)(0)"; those are not morph weight arrays and must stay on the normal SID resolver path. */ for (p = idStart; p < open; p++) { if (*p == '(' || *p == ')') return false; } *outIdStart = idStart; *outIdLen = (size_t)(open - idStart); *outIdx = (uint32_t)idx; return true; } /*---------------------------------------------------------------------------- * Topology lookups. *--------------------------------------------------------------------------*/ /* Walk visual scene tree until we find an AkInstanceMorph that wraps the given AkMorph. Returns first match (multi-instance disambiguation is a spec gray area; first-found is what most authoring tools imply). */ static AkInstanceMorph * dae_findInstanceMorph_node(AkNode * __restrict node, AkMorph *morph) { AkInstanceGeometry *instGeom; AkInstanceMorph *found; AkNode *child; for (; node; node = (AkNode *)node->next) { for (instGeom = node->geometry; instGeom; instGeom = (AkInstanceGeometry *)instGeom->base.next) { if (instGeom->morpher && instGeom->morpher->morph == morph) return instGeom->morpher; } if ((child = node->chld) && (found = dae_findInstanceMorph_node(child, morph))) return found; } return NULL; } static AkInstanceMorph * dae_findInstanceMorph(AkDoc * __restrict doc, AkMorph *morph) { AkVisualScene *vscn; AkInstanceMorph *found; if (!morph || !doc->lib.visualScenes) return NULL; for (vscn = (void *)doc->lib.visualScenes->chld; vscn; vscn = (void *)vscn->base.next) { if ((found = dae_findInstanceMorph_node(vscn->node, morph))) return found; } return NULL; } /* Find the morph controller whose morphdae->source chain contains src. Linear scan over doc->lib.controllers — fine for typical asset sizes; if multi-controller perf becomes a real workload we'd build a source→controller map up front. */ static AkController * dae_findControllerForSource(AkDoc * __restrict doc, AkSource *src) { AkController *ctlr; AkMorph *morph; AkMorphDAE *morphdae; AkSource *s; if (!src || !doc->lib.controllers) return NULL; for (ctlr = (AkController *)doc->lib.controllers->chld; ctlr; ctlr = (AkController *)ctlr->base.next) { if (ctlr->type != AK_CONTROLLER_MORPH) continue; if (!(morph = ctlr->data)) continue; if (!(morphdae = ak_userData(morph))) continue; for (s = morphdae->source; s; s = s->next) { if (s == src) return ctlr; } } return NULL; } static AkInstanceMorph * dae_resolveMorpher(AkDoc * __restrict doc, const char *idStart, size_t idLen) { void *element; AkController *ctlr; char idbuf[256]; /* Bounded inline copy to NUL-terminate the id slice. Source ids in DAE are typically short (<64 chars); the buffer is generous. Rather than allocate, we just bail if it's longer than expected. */ if (idLen == 0 || idLen >= sizeof(idbuf)) return NULL; memcpy(idbuf, idStart, idLen); idbuf[idLen] = '\0'; /* Doc-wide id lookup (hash table — O(1)). The "id" segment of a DAE channel target is typically the SID inside a controller scope; many real-world exporters set the source's id to the same string as the SID, so the doc id table catches the common case. */ if (!(element = ak_getObjectById(doc, idbuf))) return NULL; if (ak_typeid(element) != AKT_SOURCE) return NULL; if (!(ctlr = dae_findControllerForSource(doc, (AkSource *)element))) return NULL; return dae_findInstanceMorph(doc, (AkMorph *)ctlr->data); } static bool dae_resolveMatrixElement(AkContext * __restrict ctx, const char * __restrict target, AkResolvedTarget * __restrict rt) { const char *seg, *open1, *close1, *open2, *close2, *attr; AkObject *obj; long a, b; char *end; char base[512]; size_t n; if (!ctx || !target || !rt) return false; if ((seg = strrchr(target, '/'))) seg++; else seg = target; if (!(open1 = strchr(seg, '(')) || !(close1 = strchr(open1 + 1, ')')) || close1 == open1 + 1) return false; a = strtol(open1 + 1, &end, 10); if (end != close1 || a < 0) return false; open2 = close2 = NULL; b = -1; if (*(close1 + 1) == '(') { open2 = close1 + 1; if (!(close2 = strchr(open2 + 1, ')')) || close2 == open2 + 1) return false; b = strtol(open2 + 1, &end, 10); if (end != close2 || b < 0 || close2[1] != '\0') return false; } else if (close1[1] != '\0') { return false; } n = (size_t)(open1 - target); if (n == 0 || n >= sizeof(base)) return false; memcpy(base, target, n); base[n] = '\0'; attr = NULL; obj = ak_sid_resolve(ctx, base, &attr); if (!obj || attr || ak_typeid(obj) != AKT_OBJECT || (AkTypeId)obj->type != AKT_MATRIX) return false; if (b >= 0) { if (a >= 4 || b >= 4) return false; rt->off = (uint32_t)(b * 4 + a); } else { if (a >= 16) return false; rt->off = (uint32_t)((a % 4) * 4 + (a / 4)); } rt->target = obj; rt->isPartial = true; return true; } static AkInput * dae_animSamplerInput(AkAnimSampler * __restrict samp, AkInputSemantic sem) { AkInput *inp; if (!samp) return NULL; switch (sem) { case AK_INPUT_INPUT: if (samp->inputInput) return samp->inputInput; break; case AK_INPUT_OUTPUT: if (samp->outputInput) return samp->outputInput; break; case AK_INPUT_IN_TANGENT: if (samp->inTangentInput) return samp->inTangentInput; break; case AK_INPUT_OUT_TANGENT: if (samp->outTangentInput) return samp->outTangentInput; break; case AK_INPUT_INTERPOLATION: if (samp->interpInput) return samp->interpInput; break; default: break; } for (inp = samp->input; inp; inp = inp->next) { if (inp->semantic == sem) return inp; } return NULL; } static bool dae_matrixAnimFixed(DAEMatrixAnimFix * __restrict it, AkAccessor * __restrict acc) { for (; it; it = it->next) { if (it->acc == acc) return true; } return false; } static void dae_matrixAnimMark(DAEState * __restrict dst, DAEMatrixAnimFix ** __restrict done, AkAccessor * __restrict acc) { DAEMatrixAnimFix *it; it = ak_heap_calloc(dst->heap, dst->doc, sizeof(*it)); it->acc = acc; it->next = *done; *done = it; } static bool dae_transposeMat4Output(AkAccessor * __restrict acc) { char *base; float *m; size_t st; uint32_t i; if (!acc || !acc->buffer || !acc->buffer->data || acc->componentType != AKT_FLOAT || acc->componentCount != 16) return false; st = acc->byteStride; if (st == 0) st = sizeof(float) * 16; base = (char *)acc->buffer->data + acc->byteOffset; for (i = 0; i < acc->count; i++) { m = (float *)(base + st * i); glm_mat4_transpose((vec4 *)m); } return true; } static void dae_fixupMatrixAccessor(DAEState * __restrict dst, AkInput * __restrict inp, DAEMatrixAnimFix ** __restrict done) { AkAccessor *acc; if (!inp || !(acc = inp->accessor)) return; if (dae_matrixAnimFixed(*done, acc)) return; if (dae_transposeMat4Output(acc)) dae_matrixAnimMark(dst, done, acc); } static void dae_fixupMatrixChannel(DAEState * __restrict dst, AkContext * __restrict ctx, AkChannel * __restrict ch, DAEMatrixAnimFix ** __restrict done) { AkResolvedTarget rt; AkAnimSampler *samp; AkObject *obj; rt = ak_channelTarget(ctx, ch); if (!rt.target || ak_typeid(rt.target) != AKT_OBJECT) return; obj = rt.target; if ((AkTypeId)obj->type != AKT_MATRIX) return; samp = ak_getObjectByUrl(&ch->source); /* DAE values are authored row-major. Static node matrices are normalized to cglm/AssetKit column-major during node parse; animation OUTPUT matrices need the same one-time normalization. Tangents are not evaluated today, but if an exporter authors 16-float matrix tangents, keep them in the same convention for future Bezier/Hermite support. */ dae_fixupMatrixAccessor(dst, dae_animSamplerInput(samp, AK_INPUT_OUTPUT), done); dae_fixupMatrixAccessor(dst, dae_animSamplerInput(samp, AK_INPUT_IN_TANGENT), done); dae_fixupMatrixAccessor(dst, dae_animSamplerInput(samp, AK_INPUT_OUT_TANGENT), done); } static void dae_fixup_channel_walk(DAEState * __restrict dst, AkAnimation * __restrict anim, AkContext * __restrict ctx, DAEMatrixAnimFix ** __restrict done) { AkAnimation *sub; AkChannel *ch; AkResolvedTarget *rt; AkResolvedTarget mrt; AkInstanceMorph *morpher; const char *idStart; size_t idLen; uint32_t idx; for (; anim; anim = (AkAnimation *)anim->base.next) { for (ch = anim->channel; ch; ch = ch->next) { if (!ch->resolvedTarget) { memset(&mrt, 0, sizeof(mrt)); if (dae_resolveMatrixElement(ctx, ch->target, &mrt)) { rt = ak_heap_calloc(dst->heap, ch, sizeof(*rt)); *rt = mrt; ch->resolvedTarget = rt; ch->targetType = AK_TARGET_FLOAT; } else if (dae_parseChannelTargetIndexed(ch->target, &idStart, &idLen, &idx) && (morpher = dae_resolveMorpher(dst->doc, idStart, idLen))) { rt = ak_heap_calloc(dst->heap, ch, sizeof(*rt)); rt->target = morpher; rt->off = idx; rt->isPartial = true; ch->resolvedTarget = rt; ch->targetType = AK_TARGET_WEIGHTS; } } dae_fixupMatrixChannel(dst, ctx, ch, done); } if ((sub = anim->animation)) dae_fixup_channel_walk(dst, sub, ctx, done); } } AK_HIDE void dae_fixup_channel(DAEState * __restrict dst) { AkAnimation *anim; DAEMatrixAnimFix *done; AkContext ctx; if (!dst->doc->lib.animations) return; anim = (AkAnimation *)dst->doc->lib.animations->chld; done = NULL; memset(&ctx, 0, sizeof(ctx)); ctx.doc = dst->doc; dae_fixup_channel_walk(dst, anim, &ctx, &done); } ================================================ FILE: src/io/dae/fixup/channel.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_fixup_channel_h #define dae_fixup_channel_h #include "../common.h" /* * Resolve/fix DAE animation channels that need loader-side help: * - parenthesized index syntax used for morph weights, e.g. * * - matrix component targets, e.g. * where DAE row/column indices need AssetKit column-major offsets * - row-major MAT4 OUTPUT arrays targeting transforms; static * DAE matrices are normalized to AssetKit column-major during node * parse, so animation matrices are normalized here to match. * * For matched morph patterns this builds an AkResolvedTarget pointing at * the AkInstanceMorph (target), the index inside the parentheses (off), * and isPartial=true; ak_channelTarget then returns it directly. Channels * with conventional "node/transform.attr" SID syntax stay on the normal * resolver path. * * Must run AFTER dae_fixup_instctlr so AkInstanceMorph instances exist. */ AK_HIDE void dae_fixup_channel(DAEState * __restrict dst); #endif /* dae_fixup_channel_h */ ================================================ FILE: src/io/dae/fixup/ctlr.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ctlr.h" static AkResult ak_fixBoneWeights(AkHeap *heap, size_t nMeshVertex, AkSkin *skin, AkDuplicator *duplicator, AkBoneWeights *intrWeights, AkBoneWeights *weights, AkAccessor *weightsAcc, uint32_t jointOffset, uint32_t weightsOffset); AK_INLINE uint32_t ak_daeSafeWeightCount(AkBoneWeights * __restrict intrWeights, AkUIntArray * __restrict v, uint32_t viStride, size_t oldIdx) { uint32_t *pCount; uint32_t *pSum; size_t off; size_t avail; uint32_t count; if (!intrWeights || !intrWeights->counts || !v || viStride == 0) return 0; if (oldIdx >= intrWeights->nVertex) return 0; pCount = intrWeights->counts; pSum = intrWeights->counts + intrWeights->nVertex; off = pSum[oldIdx]; count = pCount[oldIdx]; if (off > v->count / viStride) return 0; avail = v->count / viStride - off; if (count > avail) count = (uint32_t)avail; return count; } AK_INLINE float ak_daeReadSkinWeight(AkAccessor * __restrict acc, uint32_t idx) { AkBuffer *buff; char *base; size_t stride; float val; if (!acc || idx >= acc->count || !(buff = acc->buffer) || !buff->data) return 0.0f; stride = acc->byteStride ? acc->byteStride : sizeof(float); base = (char *)buff->data + acc->byteOffset; memcpy(&val, base + (size_t)idx * stride, sizeof(val)); return val; } AK_HIDE void dae_fixup_ctlr(DAEState * __restrict dst) { AkDoc *doc; AkController *ctlr; doc = dst->doc; ctlr = (void *)dst->doc->lib.controllers->chld; while (ctlr) { switch (ctlr->type) { case AK_CONTROLLER_SKIN: { AkSkin *skin; AkSkinDAE *skindae; AkGeometry *geom; skin = ctlr->data; skindae = ak_userData(skin); if (!(geom = ak_baseGeometry(&skindae->baseGeom))) goto nxt_ctlr; switch (geom->gdata->type) { case AK_GEOMETRY_MESH: { AkMesh *mesh; AkDaeMeshInfo *meshInfo; AkMeshPrimitive *prim; AkBoneWeights *intrWeights; /* interleaved */ AkInput *jointswInp, *weightsInp; AkAccessor *weightsAcc; size_t nMeshVertex; uint32_t primIndex; mesh = ak_objGet(geom->gdata); prim = mesh->primitive; intrWeights = (void *)skin->weights; if (!intrWeights || !intrWeights->counts) goto nxt_ctlr; primIndex = 0; meshInfo = rb_find(dst->meshInfo, mesh); jointswInp = skindae->weights.joints; weightsInp = skindae->weights.weights; if (!jointswInp || !weightsInp || !(weightsAcc = weightsInp->accessor)) goto nxt_ctlr; skin->weights = ak_heap_calloc(dst->heap, ctlr->data, sizeof(void *) * mesh->primitiveCount); nMeshVertex = meshInfo ? meshInfo->nVertex : intrWeights->nVertex; flist_sp_insert(&mesh->skins, skin); while (prim) { AkAccessor *posAcc; AkBoneWeights *weights; /* per-primitive weights */ AkDuplicator *dupl; size_t count; if (!prim->pos || !(posAcc = prim->pos->accessor)) { primIndex++; prim = prim->next; continue; } dupl = rb_find(doc->reserved, prim); count = 0; if (dupl && dupl->range) count = dupl->bufCount + dupl->dupCount; if (count == 0) count = posAcc->count; weights = ak_heap_calloc(dst->heap, ctlr->data, sizeof(*weights)); weights->counts = ak_heap_calloc(dst->heap, ctlr->data, count * sizeof(uint32_t)); weights->indexes = ak_heap_calloc(dst->heap, ctlr->data, count * sizeof(size_t)); weights->nVertex = count; ak_fixBoneWeights(dst->heap, nMeshVertex, skin, dupl, intrWeights, weights, weightsAcc, jointswInp->offset, weightsInp->offset); skin->weights[primIndex] = weights; primIndex++; prim = prim->next; } skin->nPrims = primIndex; ak_free(intrWeights); break; } default: break; } break; } case AK_CONTROLLER_MORPH: { AkMorph *morph; AkMorphDAE *morphdae; AkGeometry *baseGeom; AkInput *input, *targetInput, *weightInput; AkAccessor *targetAcc, *weightAcc; AkBuffer *targetBuff, *weightBuff; AkMorphTarget *prevTarget; uint32_t basePrimCount; size_t count, i; AkContext sidCtx = { .doc = doc }; morph = ctlr->data; morphdae = ak_userData(morph); if (!(baseGeom = ak_baseGeometry(&morphdae->baseGeom))) goto nxt_ctlr; /* DAE morph has exactly one MORPH_TARGET input (a NAME/IDREF/SIDREF array of per-target geometries) and one MORPH_WEIGHT input (a float array of default weights). Split the chain so we can walk targets while still having the weights handy for defaultWeights. */ targetInput = weightInput = NULL; for (input = morphdae->input; input; input = input->next) { if (input->semantic == AK_INPUT_MORPH_TARGET) targetInput = input; else if (input->semantic == AK_INPUT_MORPH_WEIGHT) weightInput = input; } if (!targetInput || !(targetAcc = targetInput->accessor) || !(targetBuff = targetAcc->buffer) || !targetBuff->data || targetAcc->count == 0) goto nxt_ctlr; /* Per AkMorphTarget, primitiveCount mirrors the base mesh — DAE assumes target geometries share the base topology (same prim layout, same vertex count). Compute once. */ basePrimCount = (baseGeom->gdata && baseGeom->gdata->type == AK_GEOMETRY_MESH) ? ((AkMesh *)ak_objGet(baseGeom->gdata))->primitiveCount : 1; /* Build AkMorphTarget chain in source order (tail-insert). DAE animation channels reference targets via position — preserving parse order keeps weight indices aligned with the source array. */ count = targetAcc->count; prevTarget = NULL; for (i = 0; i < count; i++) { const char *id; void *resolved; AkObject *wrap; AkMorphTarget *target; const char **idArr; /* Source array entries are typed by the accessor's componentType. COLLADA 1.4/1.5 spec lists IDREF and Name as the typical morph target source types; SIDREF is technically allowed via the generic mechanism. */ idArr = targetBuff->data; if (!(id = idArr[i])) continue; switch (targetAcc->componentType) { case AKT_IDREF: case AKT_NAME: resolved = ak_getObjectById(doc, id); break; case AKT_SIDREF: /* SIDREF source-array entries store the absolute scoped path (e.g. "geom_id/sid_path"). Resolve directly via ak_sid_resolve — `ak_sid_resolve_from` is for already-split (id, sid) pairs and would mis-prefix the path here. */ resolved = ak_sid_resolve(&sidCtx, id, NULL); break; default: resolved = NULL; break; } if (!resolved || ak_typeid(resolved) != AKT_GEOMETRY) continue; /* AkObject wrap carries the type tag used by intr.c switch dispatch. Payload is one pointer-sized slot storing the AkGeometry* — read back via ak_objGetTarget on C/Swift sides. The geometry itself stays in doc->lib.geometries with its own lifetime; this wrap just references it. */ wrap = ak_objAlloc(dst->heap, morph, sizeof(AkGeometry *), AK_MORPHABLE_GEOMETRY, true); ak_objGetTarget(wrap) = (AkGeometry *)resolved; target = ak_heap_calloc(dst->heap, morph, sizeof(*target)); target->target = wrap; target->primitiveCount = basePrimCount; if (prevTarget) prevTarget->next = target; else morph->target = target; prevTarget = target; morph->targetCount++; } if (morph->targetCount == 0) goto nxt_ctlr; /* MORPH_WEIGHT → defaultWeights. Spec requires the weight array's length to match the target count; if it mismatches we still take min(count, targetCount) so partial assets don't silently drop on the floor. */ if (weightInput && (weightAcc = weightInput->accessor) && (weightBuff = weightAcc->buffer) && weightBuff->data && weightAcc->count > 0) { AkFloatArray *defaults; float *src; size_t nWeights; nWeights = weightAcc->count < morph->targetCount ? weightAcc->count : morph->targetCount; defaults = ak_heap_alloc(dst->heap, morph, sizeof(*defaults) + sizeof(float) * nWeights); defaults->count = nWeights; /* Weights are stored densely in the source's accessor; respect stride for safety (DAE float source from is typically tightly packed but technically allowed to be interleaved within its ). */ src = weightBuff->data; if (weightAcc->byteStride && weightAcc->byteStride != sizeof(float)) { char *base = (char *)src + weightAcc->byteOffset; for (i = 0; i < nWeights; i++) { defaults->items[i] = *(float *)(base + i * weightAcc->byteStride); } } else { src = (float *)((char *)src + weightAcc->byteOffset); for (i = 0; i < nWeights; i++) defaults->items[i] = src[i]; } morph->defaultWeights = defaults; } /* Register geom→morph for instance hookup later (DAE node walker reads meshTargets to attach AkInstanceMorph when it sees an referring to a morph controller). */ rb_insert(dst->meshTargets, baseGeom, morph); break; } default: break; } nxt_ctlr: ctlr = (AkController *)ctlr->base.next; } } AK_HIDE void dae_fixup_instctlr(DAEState * __restrict dst) { AkSkinDAE *skindae; FListItem *item; AkInstanceController *instCtlr; AkController *ctlr; AkNode *node; AkInstanceGeometry *instGeom; AkContext ctx = { .doc = dst->doc }; item = dst->instCtlrs; while (item) { AkMorphDAE *morphdae; instCtlr = item->data; ctlr = ak_instanceObject(&instCtlr->base); node = instCtlr->base.node; instGeom = ak_heap_calloc(dst->heap, node, sizeof(*instGeom)); switch (ctlr->type) { case AK_CONTROLLER_SKIN: { AkInstanceSkin *instSkin; AkSkin *skin; AkNode **joints; AkInput *jointsInp, *matrixInp; AkAccessor *jointsAcc, *matrixAcc; AkBuffer *jointsBuff, *matrixBuff; FListItem *skel; const char *sid, **it; AkFloat *mit; AkFloat4x4 *invm; size_t count, i; skin = ctlr->data; skindae = ak_userData(skin); instSkin = ak_heap_calloc(dst->heap, node, sizeof(*instSkin)); skin = ctlr->data; jointsInp = skindae->joints.joints; matrixInp = skindae->joints.invBindMatrix; invm = NULL; joints = NULL; if ((jointsAcc = jointsInp->accessor)) { matrixAcc = matrixInp->accessor; jointsBuff = jointsAcc->buffer; matrixBuff = matrixAcc->buffer; it = jointsBuff->data; mit = matrixBuff->data; count = jointsAcc->count; joints = ak_heap_alloc(dst->heap, instCtlr, sizeof(void **) * count); invm = ak_heap_alloc(dst->heap, ctlr->data, sizeof(mat4) * count); for (i = 0; i < count; i++) { if (!(sid = it[i])) continue; switch (jointsAcc->componentType) { case AKT_IDREF: joints[i] = ak_getObjectById(dst->doc, sid); break; case AKT_SIDREF: case AKT_NAME: if ((skel = instCtlr->reserved)) { do { if ((joints[i] = ak_sid_resolve_from(&ctx, skel->data, sid, NULL))) break; } while ((skel = skel->next)); } break; default: break; } /* move invBindMatrix to new location */ memcpy(invm[i], mit + 16 * i, sizeof(AkFloat) * 16); glm_mat4_transpose(invm[i]); } skin->nJoints = count; skin->invBindPoses = invm; /* DAE persists skeleton root as URL on each ; the same skin can be re-used with different skeletons per instance, but for a single-instance setup the first URL is a faithful AkSkin.skeleton hint. Fall back silently when missing — callers default to joints[0]. */ if (!skin->skeleton && instCtlr->reserved) { const char *skelUrl = instCtlr->reserved->data; void *resolved; if (skelUrl && (resolved = ak_getObjectById(dst->doc, skelUrl + 1)) && ak_typeid(resolved) == AKT_NODE) { skin->skeleton = resolved; } } instSkin->skin = skin; instSkin->overrideJoints = joints; /* COLLADA permits chained controllers — skin's source can be another controller (typically a morph from Maya): Single-level lookahead is enough: AkInstanceGeometry has one morpher + one skinner slot, and no GPU renderer (SceneKit / Three.js / Filament / typical Metal/Vulkan pipelines) consumes a deeper controller stack — the spec's "arbitrary recursion" was never adopted in practice. ak_baseGeometry() collapses the rest of the chain when resolving `base.object`. */ { void *src = ak_getObjectByUrl(&skindae->baseGeom); if (src && ak_typeid(src) == AKT_CONTROLLER) { AkController *intermediate = src; if (intermediate->type == AK_CONTROLLER_MORPH) { AkMorphDAE *morphdae2; AkInstanceMorph *instMorph; morphdae2 = ak_userData(intermediate->data); instMorph = ak_heap_calloc(dst->heap, node, sizeof(*instMorph)); instMorph->morph = intermediate->data; instMorph->overrideWeights = NULL; instGeom->morpher = instMorph; (void)morphdae2; } /* skin→skin nesting is exotic and the data model can't represent two skinners — silently use the outer one. */ } instGeom->base.object = ak_baseGeometry(&skindae->baseGeom); } /* create instance geometry for skin */ instGeom->skinner = instSkin; instGeom->bindMaterial = instCtlr->bindMaterial; ak_heap_setpm(instCtlr->bindMaterial, instGeom); instGeom->base.next = (AkInstanceBase *)node->geometry; if (node->geometry) node->geometry->base.prev = (AkInstanceBase *)instGeom; node->geometry = instGeom; } break; } case AK_CONTROLLER_MORPH: { AkInstanceMorph *instMorph; AkMorph *morph; morph = ctlr->data; morphdae = ak_userData(morph); instMorph = ak_heap_calloc(dst->heap, node, sizeof(*instMorph)); instMorph->morph = morph; instMorph->overrideWeights = NULL; /* DAE has no per-instance weights; animation drives morph.targets */ instGeom->morpher = instMorph; instGeom->bindMaterial = instCtlr->bindMaterial; /* Symmetric to SKIN case: morph→skin→geom is rare but spec-allowed. Single-level lookahead; deeper chains collapse via ak_baseGeometry. */ { void *src = ak_getObjectByUrl(&morphdae->baseGeom); if (src && ak_typeid(src) == AKT_CONTROLLER) { AkController *intermediate = src; if (intermediate->type == AK_CONTROLLER_SKIN) { AkInstanceSkin *instSkin = ak_heap_calloc(dst->heap, node, sizeof(*instSkin)); instSkin->skin = intermediate->data; instSkin->overrideJoints = NULL; /* picked up from default joints */ instGeom->skinner = instSkin; } } } instGeom->base.object = ak_baseGeometry(&morphdae->baseGeom); ak_heap_setpm(instCtlr->bindMaterial, instGeom); instGeom->base.next = (AkInstanceBase *)node->geometry; if (node->geometry) node->geometry->base.prev = (AkInstanceBase *)instGeom; node->geometry = instGeom; break; } default: break; } item = item->next; } } static AkResult ak_fixBoneWeights(AkHeap *heap, size_t nMeshVertex, AkSkin *skin, AkDuplicator *duplicator, AkBoneWeights *intrWeights, AkBoneWeights *weights, AkAccessor *weightsAcc, uint32_t jointOffset, uint32_t weightsOffset) { AkSkinDAE *skindae; AkBoneWeight *w, *iw; AkBuffer *weightsBuff; AkUIntArray *dupc, *dupcsum, *v; uint32_t *pv, *pOldCountSum, *old; size_t *wi, vc, d, s, pno, poo, nwsum, newidx, next, tmp; uint32_t *nj, i, j, k, vcount, viStride, widx; bool useDupl; if (!skin || !intrWeights || !weights || !weightsAcc) return AK_ERR; skindae = ak_userData(skin); dupc = NULL; dupcsum = NULL; useDupl = false; nj = weights->counts; wi = weights->indexes; nwsum = 0; if (duplicator && duplicator->range) { dupc = duplicator->range->dupc; dupcsum = duplicator->range->dupcsum; useDupl = dupc && dupcsum; } if (!nj || !wi || !intrWeights->counts || !(weightsBuff = weightsAcc->buffer) || !weightsBuff->data || !(v = skindae->weights.v) || !(pv = v->items)) return AK_ERR; pOldCountSum = intrWeights->counts + intrWeights->nVertex; viStride = skindae->inputCount; /* input count in element */ if (viStride == 0 || jointOffset >= viStride || weightsOffset >= viStride) return AK_ERR; vc = nMeshVertex; if (intrWeights->nVertex < vc) vc = intrWeights->nVertex; if (useDupl && dupc->count < vc) vc = dupc->count; if (!useDupl && weights->nVertex < vc) vc = weights->nVertex; /* copy to new location and duplicate if needed */ if (useDupl) { for (i = 0; i < vc; i++) { if ((poo = dupc->items[3 * i + 2]) == 0) continue; pno = dupc->items[3 * i]; d = dupc->items[3 * i + 1]; if (pno >= dupcsum->count) continue; s = dupcsum->items[pno]; vcount = ak_daeSafeWeightCount(intrWeights, v, viStride, poo - 1); for (j = 0; j <= d; j++) { newidx = pno + j + s; if (newidx >= weights->nVertex) continue; wi[newidx] = vcount; nj[newidx] = vcount; nwsum += vcount; } } } else { for (i = 0; i < vc; i++) { vcount = ak_daeSafeWeightCount(intrWeights, v, viStride, i); wi[i] = vcount; nj[i] = vcount; nwsum += vcount; } } /* prepare weight index */ for (next = j = 0; j < weights->nVertex; j++) { tmp = wi[j]; wi[j] = next; next = tmp + next; } /* now we know the size of arrays: weights, pJointsCount, npWeightsIndex */ w = nwsum > 0 ? ak_heap_alloc(heap, weights, sizeof(*w) * nwsum) : NULL; nwsum = 0; if (useDupl) { for (i = 0; i < vc; i++) { if ((poo = dupc->items[3 * i + 2]) == 0) continue; pno = dupc->items[3 * i]; d = dupc->items[3 * i + 1]; if (pno >= dupcsum->count) continue; s = dupcsum->items[pno]; vcount = ak_daeSafeWeightCount(intrWeights, v, viStride, poo - 1); old = &pv[pOldCountSum[poo - 1] * viStride]; for (j = 0; j <= d; j++) { tmp = pno + j + s; if (tmp >= weights->nVertex) continue; newidx = wi[tmp]; for (k = 0; k < vcount; k++) { widx = old[k * viStride + weightsOffset]; iw = &w[newidx + k]; iw->joint = old[k * viStride + jointOffset]; iw->weight = ak_daeReadSkinWeight(weightsAcc, widx); } nwsum += vcount; } } } else { for (i = 0; i < vc; i++) { vcount = ak_daeSafeWeightCount(intrWeights, v, viStride, i); old = &pv[pOldCountSum[i] * viStride]; newidx = wi[i]; for (k = 0; k < vcount; k++) { widx = old[k * viStride + weightsOffset]; iw = &w[newidx + k]; iw->joint = old[k * viStride + jointOffset]; iw->weight = ak_daeReadSkinWeight(weightsAcc, widx); } nwsum += vcount; } } weights->weights = w; weights->nWeights = nwsum; return AK_OK; } ================================================ FILE: src/io/dae/fixup/ctlr.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_ctlr_h #define dae_ctlr_h #include "../common.h" AK_HIDE void dae_fixup_ctlr(DAEState * __restrict dst); AK_HIDE void dae_fixup_instctlr(DAEState * __restrict dst); #endif /* dae_ctlr_h */ ================================================ FILE: src/io/dae/fixup/geom.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "geom.h" #include "mesh.h" AK_HIDE AkResult dae_geom_fixup_all(AkDoc * doc) { AkLibrary *geomLib; AkGeometry *geom; geomLib = doc->lib.geometries; while (geomLib) { geom = (void *)geomLib->chld; while (geom) { dae_geom_fixup(geom); geom = (AkGeometry *)geom->base.next; } geomLib = geomLib->next; } return AK_OK; } AK_HIDE AkResult dae_geom_fixup(AkGeometry * geom) { AkObject *primitive; primitive = geom->gdata; switch ((AkGeometryType)primitive->type) { case AK_GEOMETRY_MESH: dae_mesh_fixup(ak_objGet(primitive)); default: break; } return AK_OK; } ================================================ FILE: src/io/dae/fixup/geom.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_geom_fixup_h #define dae_geom_fixup_h #include "../common.h" AK_HIDE AkResult dae_geom_fixup(AkGeometry * geom); AK_HIDE AkResult dae_geom_fixup_all(AkDoc * doc); #endif /* dae_geom_fixup */ ================================================ FILE: src/io/dae/fixup/mesh.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "mesh.h" #include "../../../mesh/index.h" #include "../../../topo/topo.h" AK_HIDE AkResult dae_mesh_fixup(AkMesh * mesh) { AkMeshEditHelper *edith; AkHeap *heap; AkDoc *doc; heap = ak_heap_getheap(mesh->geom); doc = ak_heap_data(heap); topofix(mesh); /* first fixup coord system because verts will be duplicated, reduce extra process */ if (ak_opt_get(AK_OPT_COORD_CONVERT_TYPE) == AK_COORD_CVT_ALL && (void *)ak_opt_get(AK_OPT_COORD) != doc->coordSys) ak_changeCoordSysMesh(mesh, (void *)ak_opt_get(AK_OPT_COORD)); if (!mesh->primitive) return AK_OK; ak_meshBeginEdit(mesh); edith = mesh->edith; edith->skipFixIndices = true; /* to do it once per mesh */ if (ak_opt_get(AK_OPT_TRIANGULATE)) ak_meshTriangulate(mesh); if (ak_opt_get(AK_OPT_GEN_NORMALS_IF_NEEDED)) if (ak_meshNeedsNormals(mesh)) ak_meshGenNormals(mesh); edith->skipFixIndices = false; ak_meshFixIndices(mesh); ak_meshEndEdit(mesh); if (ak_opt_get(AK_OPT_COMPUTE_BBOX)) ak_bbox_mesh(mesh); return AK_OK; } ================================================ FILE: src/io/dae/fixup/mesh.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_mesh_fixup_h #define dae_mesh_fixup_h #include "../common.h" AK_HIDE AkResult dae_mesh_fixup(AkMesh * mesh); #endif /* dae_mesh_fixup_h */ ================================================ FILE: src/io/dae/fixup/node.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "node.h" #include /*! * @brief fix camera, light * * @todo new node lost its id, sid and name, FIX THIS! */ AK_HIDE void dae_nodeFixupFixedCoord(AkHeap * __restrict heap, AkNode * __restrict node) { AkNode *newNode; if (!node->camera && !node->light) return; if (!node->geometry && !node->chld && !node->node) { node->flags |= AK_NODEF_FIXED_COORD; return; } /* move to new node, if we move to new child node we would not neeed to, duplicate transform but the node may be used by sid path, so we couldn't move to child, instead we have to duplicate transfroms :( */ newNode = ak_heap_calloc(heap, node, sizeof(*newNode)); newNode->nodeType = AK_NODE_TYPE_NODE; if (node->camera) { AkInstanceBase *inst; inst = node->camera; while (inst) { ak_heap_setpm(inst, newNode); inst = inst->next; } newNode->camera = node->camera; node->camera = NULL; } if (node->light) { AkInstanceBase *inst; inst = node->light; while (inst) { ak_heap_setpm(inst, newNode); inst = inst->next; } newNode->light = node->light; node->light = NULL; } /* duplicate all transforms before apply rotations */ ak_transformDup(node, newNode); } AK_HIDE void dae_nodeFixup(AkHeap * __restrict heap, AkNode * __restrict node) { if (node->camera || node->light) dae_nodeFixupFixedCoord(heap, node); ak_fixNodeCoordSys(node); } ================================================ FILE: src/io/dae/fixup/node.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_node_fixup_h #define dae_node_fixup_h #include "../common.h" AK_HIDE void dae_nodeFixupFixedCoord(AkHeap * __restrict heap, AkNode * __restrict node); AK_HIDE void dae_nodeFixup(AkHeap * __restrict heap, AkNode* __restrict node); #endif /* dae_node_fixup_h */ ================================================ FILE: src/io/dae/fixup/tex.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tex.h" AK_HIDE void dae_tex_walk(RBTree *tree, RBNode *rbnode); AK_HIDE void dae_fix_textures(DAEState * __restrict dst) { rb_walk(dst->texmap, dae_tex_walk); } AK_HIDE void dae_tex_walk(RBTree *tree, RBNode *rbnode) { AkHeap *heap; AkNewParam *newparam; AkColorDesc *cd; AkDAETextureRef *dtex; AkTextureRef *texref; AkTexture *tex; AkImage *image; DAEState *dst; AkInstanceBase *instanceImage; AkContext actx = {0}; cd = rbnode->key; dtex = rbnode->val; heap = ak_heap_getheap(cd); actx.doc = ak_heap_data(heap); texref = ak_heap_calloc(heap, cd, sizeof(*texref)); newparam = ak_sid_resolve(&actx, dtex->texture, NULL); if (!newparam || !(tex = newparam->val->value)) { ak_free(texref); return; } dst = tree->userData; instanceImage = rb_find(dst->instanceMap, tex->sampler); image = ak_instanceObject(instanceImage); texref->texture = tex; ak_texref_usage(texref, dtex->colorSpace, dtex->channels); /* this is the default */ /* use bind_material to set texcoord */ texref->coordInputName = ak_heap_strdup(heap, texref, "TEXCOORD"); tex->image = image; cd->texture = texref; if (dtex->texcoord) texref->texcoord = ak_heap_strdup(heap, texref, dtex->texcoord); } ================================================ FILE: src/io/dae/fixup/tex.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_tex_fixup_h #define dae_tex_fixup_h #include "../common.h" AK_HIDE void dae_fix_textures(DAEState * __restrict dst); #endif /* dae_tex_fixup_h */ ================================================ FILE: src/io/dae/fx/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/io/dae/fx/colortex.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "colortex.h" #include "../core/param.h" #include "../core/color.h" #include "../core/enum.h" AK_HIDE void dae_colorOrTexSet(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp, AkColorDesc * __restrict clr) { AkHeap *heap; heap = dst->heap; xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_color)) { clr->color = ak_heap_calloc(heap, memp, sizeof(*clr->color)); dae_color(xml, clr->color, true, false, clr->color); } else if (xml_tag_eq(xml, _s_dae_texture)) { AkDAETextureRef *tex; tex = ak_heap_calloc(heap, memp, sizeof(*tex)); ak_setypeid(tex, AKT_TEXTURE); tex->texture = xmla_strdup(xmla(xml, _s_dae_texture), heap, tex); tex->texcoord = xmla_strdup(xmla(xml, _s_dae_texcoord), heap, tex); if (tex->texture) ak_setypeid((void *)tex->texture, AKT_TEXTURE_NAME); if (tex->texcoord) ak_setypeid((void *)tex->texcoord, AKT_TEXCOORD); rb_insert(dst->texmap, clr, tex); } else if (xml_tag_eq(xml, _s_dae_param)) { AkParam *param; if ((param = dae_param(dst, xml, clr))) { if (clr->param) clr->param->prev = param; param->next = clr->param; clr->param = param; } } xml = xml->next; } } ================================================ FILE: src/io/dae/fx/colortex.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_colortex_h #define dae_colortex_h #include "../common.h" AK_HIDE void dae_colorOrTexSet(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp, AkColorDesc * __restrict clr); AK_INLINE AkColorDesc* dae_colorOrTex(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkColorDesc *clr; heap = dst->heap; clr = ak_heap_calloc(heap, memp, sizeof(*clr)); dae_colorOrTexSet(dst, xml, clr, clr); return clr; } #endif /* dae_colortex_h */ ================================================ FILE: src/io/dae/fx/effect.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "effect.h" #include "profile.h" #include "../core/asset.h" #include "../core/techn.h" #include "../core/param.h" #include "../1.4/image.h" AK_HIDE void* dae_effect(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkEffect *effect; heap = dst->heap; effect = ak_heap_calloc(heap, memp, sizeof(*effect)); ak_setypeid(effect, AKT_EFFECT); xmla_setid(xml, heap, effect); effect->name = xmla_strdup_by(xml, heap, _s_dae_name, effect); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_asset)) { (void)dae_asset(dst, xml, effect, NULL); } else if (xml_tag_eq(xml, _s_dae_newparam)) { AkNewParam *newparam; if ((newparam = dae_newparam(dst, xml, effect))) { if (effect->newparam) effect->newparam->prev = newparam; newparam->next = effect->newparam; effect->newparam = newparam; } } else if (xml_tag_eq(xml, _s_dae_prfl_common)) { AkProfile *profile; if ((profile = dae_profile(dst, xml, effect))) { profile->next = effect->profile; effect->profile = profile; } } else if (dst->version < AK_COLLADA_VERSION_150 && xml_tag_eq(xml, _s_dae_image)) { /* migration from 1.4 */ dae14_fxMigrateImg(dst, xml, NULL); } else if (xml_tag_eq(xml, _s_dae_extra)) { effect->extra = tree_fromxml(heap, effect, xml); } xml = xml->next; } return effect; } AK_HIDE AkInstanceEffect* dae_instEffect(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkInstanceEffect *instEffect; xml_attr_t *att; heap = dst->heap; instEffect = ak_heap_calloc(heap, memp, sizeof(*instEffect)); xmla_setid(xml, heap, instEffect); instEffect->base.type = AK_INSTANCE_EFFECT; instEffect->base.name = xmla_strdup_by(xml, heap, _s_dae_name, instEffect); url_set(dst, xml, _s_dae_url, instEffect, &instEffect->base.url); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_technique_hint)) { AkTechniqueHint *techHint; techHint = ak_heap_calloc(heap, instEffect, sizeof(*techHint)); if ((att = xmla(xml, _s_dae_ref))) techHint->ref = xmla_strdup(att, heap, techHint); if ((att = xmla(xml, _s_dae_profile))) techHint->profile = xmla_strdup(att, heap, techHint); if ((att = xmla(xml, _s_dae_platform))) techHint->platform = xmla_strdup(att, heap, techHint); techHint->next = instEffect->techniqueHint; instEffect->techniqueHint = techHint; } else if (xml_tag_eq(xml, _s_dae_extra)) { instEffect->base.extra = tree_fromxml(heap, instEffect, xml); } xml = xml->next; } return instEffect; } ================================================ FILE: src/io/dae/fx/effect.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_fx_effect_h #define dae_fx_effect_h #include "../common.h" AK_HIDE void* dae_effect(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); AK_HIDE AkInstanceEffect* dae_instEffect(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); #endif /* dae_fx_effect_h */ ================================================ FILE: src/io/dae/fx/fltprm.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fltprm.h" #include "../core/param.h" AK_HIDE float dae_float(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp, size_t off, float defaultVal) { AkHeap *heap; const xml_t *sval; float flt; flt = defaultVal; heap = dst->heap; xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_float) && (sval = xmls(xml))) { sid_seta(xml, heap, memp, memp + off); flt = xml_float(xml, defaultVal); } /* else if (xml_tag_eq(xml, _s_dae_param)) { AkParam *param; if ((param = dae_param(dst, xml, flt))) { if (flt->param) flt->param->prev = param; param->next = flt->param; flt->param = param; } } */ xml = xml->next; } return flt; } // //AK_HIDE AkFloatOrParam* //dae_floatOrParam(DAEState * __restrict dst, // xml_t * __restrict xml, // void * __restrict memp) { // AkHeap *heap; // AkFloatOrParam *flt; // const xml_t *sval; // // heap = dst->heap; // flt = ak_heap_calloc(heap, memp, sizeof(*flt)); // // xml = xml->val; // while (xml) { // if (xml_tag_eq(xml, _s_dae_float) && (sval = xmls(xml))) { // float *valuef; // // valuef = ak_heap_calloc(heap, flt, sizeof(*valuef)); // xml_strtof_fast(sval, valuef, 1); // // sid_set(xml, heap, valuef); // // flt->val = valuef; // } else if (xml_tag_eq(xml, _s_dae_param)) { // AkParam *param; // // if ((param = dae_param(dst, xml, flt))) { // if (flt->param) // flt->param->prev = param; // // param->next = flt->param; // flt->param = param; // } // } // xml = xml->next; // } // // return flt; //} ================================================ FILE: src/io/dae/fx/fltprm.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_fltprm_h #define dae_fltprm_h #include "../common.h" //AK_HIDE AkFloatOrParam* //dae_floatOrParam(DAEState * __restrict dst, // xml_t * __restrict xml, // void * __restrict memp); AK_HIDE float dae_float(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp, size_t off, float defaultVal); #endif /* dae_fltprm_h */ ================================================ FILE: src/io/dae/fx/img.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "img.h" #include "../core/asset.h" #include "../1.4/image.h" #include "../core/enum.h" static AkInitFrom* dae_initFrom(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); static AkImageFormat* dae_imageFormat(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); static AkImage2d* dae_create2d(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); static AkImage3d* dae_create3d(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); static AkImageCube* dae_createCube(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); AK_HIDE void* dae_image(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkImage *img; xml_attr_t *att; if (dst->version < AK_COLLADA_VERSION_150) { dae14_fxMigrateImg(dst, xml, memp); return NULL; } heap = dst->heap; img = ak_heap_calloc(heap, memp, sizeof(*img)); xmla_setid(xml, heap, img); sid_set(xml, heap, img); img->name = xmla_strdup_by(xml, heap, _s_dae_name, img); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_asset)) { (void)dae_asset(dst, xml, img, NULL); } else if (xml_tag_eq(xml, _s_dae_renderable)) { img->renderable = (att = xmla(xml, _s_dae_share)) && att->val && strcasecmp(att->val, _s_dae_true) == 0; } else if (xml_tag_eq(xml, _s_dae_init_from)) { img->initFrom = dae_initFrom(dst, xml, img); } else if (xml_tag_eq(xml, _s_dae_create_2d)) { AkImage2d *image2d; if ((image2d = dae_create2d(dst, xml, img))) img->image = &image2d->base; } else if (xml_tag_eq(xml, _s_dae_create_3d)) { AkImage3d *image3d; if ((image3d = dae_create3d(dst, xml, img))) img->image = &image3d->base; } else if (xml_tag_eq(xml, _s_dae_create_cube)) { AkImageCube *imageCube; if ((imageCube = dae_createCube(dst, xml, img))) img->image = &imageCube->base; } else if (xml_tag_eq(xml, _s_dae_extra)) { img->extra = tree_fromxml(heap, img, xml); } xml = xml->next; } return img; } AK_HIDE AkInstanceBase* dae_instImage(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkInstanceBase *instImg; heap = dst->heap; instImg = ak_heap_calloc(heap, memp, sizeof(*instImg)); instImg->name = xmla_strdup_by(xml, heap, _s_dae_name, instImg); sid_set(xml, heap, instImg); url_set(dst, xml, _s_dae_url, instImg, &instImg->url); return instImg; } static AkInitFrom* dae_initFrom(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkInitFrom *initFrom; AkHexData *hex; xml_attr_t *att; heap = dst->heap; initFrom = ak_heap_calloc(heap, memp, sizeof(*initFrom)); initFrom->mipsGenerate = xmla_u32(xmla(xml, _s_dae_mips_generate), 0); initFrom->arrayIndex = xmla_u32(xmla(xml, _s_dae_array_index), 0); initFrom->mipIndex = xmla_u32(xmla(xml, _s_dae_mip_index), 0); initFrom->depth = xmla_u32(xmla(xml, _s_dae_depth), 0); if ((att = xmla(xml, _s_dae_face)) && att->val) initFrom->face = dae_face(att); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_ref)) { initFrom->ref = xml_strdup(xml, heap, initFrom); } else if (xml_tag_eq(xml, _s_dae_hex)) { hex = ak_heap_calloc(heap, initFrom, sizeof(*hex)); hex->format = xmla_strdup(xmla(xml, _s_dae_format), heap, hex); if (hex->format) { hex->hexval = xml_strdup(xml, heap, initFrom); initFrom->hex = hex; } else { ak_free(hex); } } xml = xml->next; } return initFrom; } static AkImageFormat* dae_imageFormat(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkImageFormat *format; xml_attr_t *att; heap = dst->heap; format = ak_heap_calloc(heap, memp, sizeof(*format)); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_hint)) { if ((att = xmla(xml, _s_dae_channels)) && att->val) format->channel = dae_enumChannel(att->val, att->valsize); if ((att = xmla(xml, _s_dae_range)) && att->val) format->range = dae_range(att->val, att->valsize); if ((att = xmla(xml, _s_dae_precision)) && att->val) format->precision = dae_precision(att->val, att->valsize); if ((att = xmla(xml, _s_dae_space)) && att->val) format->space = xmla_strdup(att, heap, format); } else if (xml_tag_eq(xml, _s_dae_exact)) { format->exact = xml_strdup(xml, heap, format); } xml = xml->next; } return format; } static AkImage2d* dae_create2d(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkImage2d *img; heap = dst->heap; img = ak_heap_calloc(heap, memp, sizeof(*img)); img->base.type = AK_IMAGE_TYPE_2D; xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_size_exact)) { AkSizeExact *sizeExact; sizeExact = ak_heap_calloc(heap, img, sizeof(*sizeExact)); sizeExact->width = xmla_u32(xmla(xml, _s_dae_width), 0); sizeExact->height = xmla_u32(xmla(xml, _s_dae_height), 0); img->sizeExact = sizeExact; } else if (xml_tag_eq(xml, _s_dae_size_ratio)) { AkSizeRatio *sizeRatio; sizeRatio = ak_heap_calloc(heap, img, sizeof(*sizeRatio)); sizeRatio->width = xmla_float(xmla(xml, _s_dae_width), 0); sizeRatio->height = xmla_float(xmla(xml, _s_dae_height), 0); img->sizeRatio = sizeRatio; } else if (xml_tag_eq(xml, _s_dae_mips)) { AkMips *mips; mips = ak_heap_calloc(heap, img, sizeof(*mips)); mips->levels = xmla_u32(xmla(xml, _s_dae_levels), 0); mips->autoGenerate = xmla_u32(xmla(xml, _s_dae_auto_generate), 0); img->mips = mips; } else if (xml_tag_eq(xml, _s_dae_unnormalized)) { img->unnormalized = xml_strdup(xml, heap, img); } else if (xml_tag_eq(xml, _s_dae_array)) { img->base.arrayLen = xmla_u32(xmla(xml, _s_dae_length), 0); } else if (xml_tag_eq(xml, _s_dae_format)) { img->base.format = dae_imageFormat(dst, xml, img); } else if (xml_tag_eq(xml, _s_dae_size_exact)) { img->base.initFrom = dae_initFrom(dst, xml, img); } xml = xml->next; } return img; } static AkImage3d* dae_create3d(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkImage3d *img; heap = dst->heap; img = ak_heap_calloc(heap, memp, sizeof(*img)); img->base.type = AK_IMAGE_TYPE_3D; xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_size)) { img->size.width = xmla_u32(xmla(xml, _s_dae_width), 0); img->size.height = xmla_u32(xmla(xml, _s_dae_height), 0); img->size.depth = xmla_u32(xmla(xml, _s_dae_depth), 0); } else if (xml_tag_eq(xml, _s_dae_mips)) { img->mips.levels = xmla_u32(xmla(xml, _s_dae_levels), 0); img->mips.autoGenerate = xmla_u32(xmla(xml, _s_dae_auto_generate), 0); } else if (xml_tag_eq(xml, _s_dae_array)) { img->base.arrayLen = xmla_u32(xmla(xml, _s_dae_length), 0); } else if (xml_tag_eq(xml, _s_dae_format)) { img->base.format = dae_imageFormat(dst, xml, img); } else if (xml_tag_eq(xml, _s_dae_size_exact)) { img->base.initFrom = dae_initFrom(dst, xml, img); } xml = xml->next; } return img; } static AkImageCube* dae_createCube(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkImageCube *img; heap = dst->heap; img = ak_heap_calloc(heap, memp, sizeof(*img)); img->base.type = AK_IMAGE_TYPE_CUBE; xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_size)) { img->width = xmla_u32(xmla(xml, _s_dae_width), 0); } else if (xml_tag_eq(xml, _s_dae_mips)) { img->mips.levels = xmla_u32(xmla(xml, _s_dae_levels), 0); img->mips.autoGenerate = xmla_u32(xmla(xml, _s_dae_auto_generate), 0); } else if (xml_tag_eq(xml, _s_dae_array)) { img->base.arrayLen = xmla_u32(xmla(xml, _s_dae_length), 0); } else if (xml_tag_eq(xml, _s_dae_format)) { img->base.format = dae_imageFormat(dst, xml, img); } else if (xml_tag_eq(xml, _s_dae_size_exact)) { img->base.initFrom = dae_initFrom(dst, xml, img); } xml = xml->next; } return img; } ================================================ FILE: src/io/dae/fx/img.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_fx_img_h #define dae_fx_img_h #include "../common.h" AK_HIDE void* dae_image(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); AK_HIDE AkInstanceBase* dae_instImage(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); #endif /* dae_fx_img_h */ ================================================ FILE: src/io/dae/fx/mat.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "mat.h" #include "effect.h" #include "../core/asset.h" #include "../core/param.h" #include "../core/techn.h" AK_HIDE void* dae_material(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkMaterial *mat; heap = dst->heap; mat = ak_heap_calloc(heap, memp, sizeof(*mat)); xmla_setid(xml, heap, mat); mat->name = xmla_strdup_by(xml, heap, _s_dae_name, mat); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_asset)) { (void)dae_asset(dst, xml, mat, NULL); } else if (xml_tag_eq(xml, _s_dae_inst_effect)) { AkInstanceEffect *instEffect; if ((instEffect = dae_instEffect(dst, xml, mat))) { if (mat->effect) { mat->effect->base.prev = &instEffect->base; instEffect->base.next = &mat->effect->base; } mat->effect = instEffect; } } else if (xml_tag_eq(xml, _s_dae_extra)) { mat->extra = tree_fromxml(heap, mat, xml); } xml = xml->next; } return mat; } AK_HIDE AkBindMaterial* dae_bindMaterial(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkBindMaterial *bindmat; heap = dst->heap; bindmat = ak_heap_calloc(heap, memp, sizeof(*bindmat)); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_param)) { AkParam *param; if ((param = dae_param(dst, xml, bindmat))) { if (bindmat->param) { bindmat->param->prev = param; param->next = bindmat->param; } bindmat->param = param; } } else if (xml_tag_eq(xml, _s_dae_techniquec)) { AkInstanceMaterial *imat; xml_t *ximat; ximat = xml->val; while (ximat) { if (xml_tag_eq(ximat, _s_dae_instance_material)) { if ((imat = dae_instMaterial(dst, ximat, bindmat))) { if (bindmat->tcommon) { bindmat->tcommon->base.prev = &imat->base; imat->base.next = &bindmat->tcommon->base; } bindmat->tcommon = imat; } } ximat = ximat->next; } } else if (xml_tag_eq(xml, _s_dae_technique)) { AkTechnique *tq; if ((tq = dae_techn(xml, heap, bindmat))) { tq->next = bindmat->technique; bindmat->technique = tq; } } else if (xml_tag_eq(xml, _s_dae_extra)) { bindmat->extra = tree_fromxml(heap, bindmat, xml); } xml = xml->next; } return bindmat; } AK_HIDE AkInstanceMaterial* dae_instMaterial(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkInstanceMaterial *mat; xml_attr_t *att; heap = dst->heap; mat = ak_heap_calloc(heap, memp, sizeof(*mat)); sid_set(xml, heap, mat); mat->base.name = xmla_strdup_by(xml, heap, _s_dae_name, mat); mat->symbol = xmla_strdup_by(xml, heap, _s_dae_symbol, mat); url_set(dst, xml, _s_dae_target, mat, &mat->base.url); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_bind)) { AkBind *bind; bind = ak_heap_calloc(heap, mat, sizeof(*bind)); bind->semantic = xmla_strdup_by(xml, heap, _s_dae_semantic, mat); bind->target = xmla_strdup_by(xml, heap, _s_dae_target, mat); bind->next = mat->bind; mat->bind = bind; } else if (xml_tag_eq(xml, _s_dae_bind_vertex_input)) { AkBindVertexInput *bvi; bvi = ak_heap_calloc(heap, mat, sizeof(*bvi)); bvi->semantic = xmla_strdup_by(xml, heap, _s_dae_semantic, mat); bvi->inputSemantic = xmla_strdup_by(xml, heap, _s_dae_input_semantic, mat); if ((att = xmla(xml, _s_dae_input_set))) bvi->inputSet = xmla_u32(att, 0); bvi->next = mat->bindVertexInput; mat->bindVertexInput = bvi; } else if (xml_tag_eq(xml, _s_dae_technique_override)) { AkTechniqueOverride *technOv; technOv = ak_heap_calloc(heap, mat, sizeof(*technOv)); technOv->pass = xmla_strdup_by(xml, heap, _s_dae_pass, technOv); technOv->ref = xmla_strdup_by(xml, heap, _s_dae_ref, technOv); mat->techniqueOverride = technOv; } else if (xml_tag_eq(xml, _s_dae_extra)) { mat->base.extra = tree_fromxml(heap, mat, xml); } xml = xml->next; } return mat; } ================================================ FILE: src/io/dae/fx/mat.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_fx_material_h #define dae_fx_material_h #include "../common.h" AK_HIDE void* dae_material(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); AK_HIDE AkBindMaterial* dae_bindMaterial(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); AK_HIDE AkInstanceMaterial* dae_instMaterial(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); #endif /* dae_fx_material_h */ ================================================ FILE: src/io/dae/fx/profile.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "profile.h" #include "techn.h" #include "../core/param.h" #include "../core/asset.h" #include "../1.4/image.h" AK_HIDE AkProfile* dae_profile(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkProfile *profile; heap = dst->heap; if (!xml_tag_eq(xml, _s_dae_prfl_common)) return NULL; profile = ak_heap_calloc(heap, memp, sizeof(AkProfileCommon)); profile->type = AK_PROFILE_TYPE_COMMON; ak_setypeid(profile, AKT_PROFILE); xmla_setid(xml, heap, profile); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_asset)) { (void)dae_asset(dst, xml, profile, NULL); } else if (xml_tag_eq(xml, _s_dae_newparam)) { AkNewParam *newparam; if ((newparam = dae_newparam(dst, xml, profile))) { if (profile->newparam) profile->newparam->prev = newparam; newparam->next = profile->newparam; profile->newparam = newparam; } } else if (xml_tag_eq(xml, _s_dae_technique)) { AkTechniqueFx *techn; if ((techn = dae_techniqueFx(dst, xml, profile))) { techn->next = profile->technique; profile->technique = techn; } } else if (dst->version < AK_COLLADA_VERSION_150 && xml_tag_eq(xml, _s_dae_image)) { /* migration from 1.4 */ dae14_fxMigrateImg(dst, xml, NULL); } else if (xml_tag_eq(xml, _s_dae_extra)) { profile->extra = tree_fromxml(heap, profile, xml); } xml = xml->next; } return profile; } ================================================ FILE: src/io/dae/fx/profile.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_fx_profile_h #define dae_fx_profile_h #include "../common.h" AK_HIDE AkProfile* dae_profile(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); #endif /* dae_fx_profile_h */ ================================================ FILE: src/io/dae/fx/samp.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "samp.h" #include "img.h" #include "../core/color.h" #include "../core/enum.h" #include "../1.4/dae14.h" #include "../1.4/surface.h" AK_HIDE AkSampler* dae_sampler(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkSampler *samp; heap = dst->heap; samp = ak_heap_calloc(heap, memp, sizeof(*samp)); ak_setypeid(samp, AKT_SAMPLER); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_source)) { /* COLLADA 1.4 uses source -> for texturing */ if (dst->version < AK_COLLADA_VERSION_150) { dae14_loadjobs_add(dst, samp, xml_strdup(xml, heap, samp), AK_DAE14_LOADJOB_SURFACE); } } else if (xml_tag_eq(xml, _s_dae_instance_image)) { AkInstanceBase *instImage; if ((instImage = dae_instImage(dst, xml, samp))) rb_insert(dst->instanceMap, samp, instImage); } else if (xml_tag_eq(xml, _s_dae_wrap_s)) { samp->wrapS = dae_wrap(xml); } else if (xml_tag_eq(xml, _s_dae_wrap_t)) { samp->wrapT = dae_wrap(xml); } else if (xml_tag_eq(xml, _s_dae_wrap_p)) { samp->wrapP = dae_wrap(xml); } else if (xml_tag_eq(xml, _s_dae_minfilter)) { samp->minfilter = dae_minfilter(xml); } else if (xml_tag_eq(xml, _s_dae_magfilter)) { samp->magfilter = dae_magfilter(xml); } else if (xml_tag_eq(xml, _s_dae_mipfilter)) { samp->mipfilter = dae_mipfilter(xml); } else if (xml_tag_eq(xml, _s_dae_border_color)) { AkColor *color; color = ak_heap_calloc(heap, samp, sizeof(*color)); dae_color(xml, samp, true, false, color); samp->borderColor = color; } else if (xml_tag_eq(xml, _s_dae_mip_max_level)) { samp->mipMaxLevel = xml_u32(xml, 0); } else if (xml_tag_eq(xml, _s_dae_mip_min_level)) { samp->mipMinLevel = xml_u32(xml, 0); } else if (xml_tag_eq(xml, _s_dae_mip_bias)) { samp->mipBias = xml_float(xml, 0); } else if (xml_tag_eq(xml, _s_dae_max_anisotropy)) { samp->maxAnisotropy = xml_u32(xml, 1l); } else if (xml_tag_eq(xml, _s_dae_extra)) { samp->extra = tree_fromxml(heap, samp, xml); } xml = xml->next; } return samp; } ================================================ FILE: src/io/dae/fx/samp.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_fx_sampler_h #define dae_fx_sampler_h #include "../common.h" AK_HIDE AkSampler* dae_sampler(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); #endif /* dae_fx_sampler_h */ ================================================ FILE: src/io/dae/fx/techn.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "techn.h" #include "colortex.h" #include "fltprm.h" #include "../core/asset.h" #include "../core/enum.h" #include "../1.4/image.h" #include "../bugfix/transp.h" #include "../../../default/material.h" static AkTechniqueFxCommon* dae_techniqueFxCmn(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp, AkMaterialType mattype); AK_HIDE AkTechniqueFx* dae_techniqueFx(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp) { AkHeap *heap; AkTechniqueFx *techn; AkMaterialType m; heap = dst->heap; techn = ak_heap_calloc(heap, memp, sizeof(*techn)); ak_setypeid(techn, AKT_TECHNIQUE_FX); xmla_setid(xml, heap, techn); sid_set(xml, heap, techn); xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_asset)) { (void)dae_asset(dst, xml, techn, NULL); } else if ((xml_tag_eq(xml, _s_dae_phong) && (m = AK_MATERIAL_PHONG)) || (xml_tag_eq(xml, _s_dae_blinn) && (m = AK_MATERIAL_BLINN)) || (xml_tag_eq(xml, _s_dae_lambert) && (m = AK_MATERIAL_LAMBERT)) || (xml_tag_eq(xml, _s_dae_constant) && (m = AK_MATERIAL_CONSTANT))) { techn->common = dae_techniqueFxCmn(dst, xml, techn, m); } else if (dst->version < AK_COLLADA_VERSION_150 && xml_tag_eq(xml, _s_dae_image)) { /* migration from 1.4 */ dae14_fxMigrateImg(dst, xml, NULL); } else if (xml_tag_eq(xml, _s_dae_extra)) { techn->extra = tree_fromxml(heap, techn, xml); } xml = xml->next; } return techn; } static void dae_colorDescTextureUsage(DAEState * __restrict dst, AkColorDesc * __restrict clr, AkTextureColorSpace colorSpace, AkTextureChannels channels) { AkDAETextureRef *tex; if (!clr || !dst->texmap) return; if ((tex = rb_find(dst->texmap, clr))) { tex->colorSpace = colorSpace; tex->channels = channels; } } static AkTextureChannels dae_transparentTextureChannels(AkOpaque opaque) { switch (opaque) { case AK_OPAQUE_A_ONE: case AK_OPAQUE_A_ZERO: case AK_OPAQUE_MASK: return AK_TEXTURE_CHANNEL_A; case AK_OPAQUE_RGB_ONE: case AK_OPAQUE_RGB_ZERO: return AK_TEXTURE_CHANNEL_RGB; default: return AK_TEXTURE_CHANNEL_RGBA; } } static AkTechniqueFxCommon* dae_techniqueFxCmn(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp, AkMaterialType mattype) { AkHeap *heap; AkTechniqueFxCommon *techn; xml_attr_t *att; AkTransparent *transp; AkOpaque opaque; heap = dst->heap; techn = ak_heap_calloc(heap, memp, sizeof(*techn)); techn->type = mattype; xml = xml->val; while (xml) { if (xml_tag_eq(xml, _s_dae_emission)) { AkMaterialEmissionProp *emission; if (!(emission = techn->emission)) { emission = ak_heap_calloc(heap, techn, sizeof(*emission)); techn->emission = emission; emission->strength = 1.0f; } dae_colorOrTexSet(dst, xml, techn, &techn->emission->color); dae_colorDescTextureUsage(dst, &techn->emission->color, AK_TEXTURE_COLORSPACE_SRGB, AK_TEXTURE_CHANNEL_RGB); } else if (xml_tag_eq(xml, _s_dae_ambient)) { techn->ambient = dae_colorOrTex(dst, xml, techn); dae_colorDescTextureUsage(dst, techn->ambient, AK_TEXTURE_COLORSPACE_SRGB, AK_TEXTURE_CHANNEL_RGB); } else if (xml_tag_eq(xml, _s_dae_diffuse)) { techn->diffuse = dae_colorOrTex(dst, xml, techn); dae_colorDescTextureUsage(dst, techn->diffuse, AK_TEXTURE_COLORSPACE_SRGB, AK_TEXTURE_CHANNEL_RGBA); } else if (xml_tag_eq(xml, _s_dae_specular)) { AkMaterialSpecularProp *specularProp; if (!(specularProp = techn->specular)) { specularProp = ak_heap_calloc(heap, techn, sizeof(*specularProp)); techn->specular = specularProp; } specularProp->color = dae_colorOrTex(dst, xml, specularProp); dae_colorDescTextureUsage(dst, specularProp->color, AK_TEXTURE_COLORSPACE_SRGB, AK_TEXTURE_CHANNEL_RGB); } else if (xml_tag_eq(xml, _s_dae_reflective)) { if (!techn->reflective) techn->reflective = ak_heap_calloc(heap, techn, sizeof(*techn->reflective)); techn->reflective->color = dae_colorOrTex(dst, xml, techn); dae_colorDescTextureUsage(dst, techn->reflective->color, AK_TEXTURE_COLORSPACE_SRGB, AK_TEXTURE_CHANNEL_RGB); } else if (xml_tag_eq(xml, _s_dae_transparent)) { if (!techn->transparent) { transp = ak_heap_calloc(heap, techn, sizeof(*transp)); transp->amount = 1.0f; techn->transparent = transp; } if ((att = xmla(xml, _s_dae_opaque))) opaque = dae_opaque(att); else opaque = AK_OPAQUE_A_ONE; techn->transparent->color = dae_colorOrTex(dst, xml, techn); techn->transparent->opaque = opaque; dae_colorDescTextureUsage(dst, techn->transparent->color, AK_TEXTURE_COLORSPACE_SRGB, dae_transparentTextureChannels(opaque)); } else if (xml_tag_eq(xml, _s_dae_shininess)) { AkMaterialSpecularProp *specularProp; if (!(specularProp = techn->specular)) { specularProp = ak_heap_calloc(heap, techn, sizeof(*specularProp)); techn->specular = specularProp; } specularProp->strength = dae_float(dst, xml, specularProp, offsetof(AkMaterialSpecularProp, shininess), 1.0f); } else if (xml_tag_eq(xml, _s_dae_reflectivity)) { if (!techn->reflective) techn->reflective = ak_heap_calloc(heap, techn, sizeof(*techn->reflective)); techn->reflective->amount = dae_float(dst, xml, techn->reflective, offsetof(AkReflective, amount), 0.0f); } else if (xml_tag_eq(xml, _s_dae_transparency)) { if (!techn->transparent) { transp = ak_heap_calloc(heap, techn, sizeof(*transp)); transp->amount = 1.0f; techn->transparent = transp; } techn->transparent->amount = dae_float(dst, xml, techn->transparent, offsetof(AkTransparent, amount), 1.0f); /* some old version of tools e.g. SketchUp exports incorrect */ if (ak_opt_get(AK_OPT_BUGFIXES)) dae_bugfix_transp(techn->transparent); } else if (xml_tag_eq(xml, _s_dae_index_of_refraction)) { /* TODO: assumed 0.0 for COLLADA */ techn->ior = dae_float(dst, xml, techn, offsetof(AkTechniqueFxCommon, ior), 0.0f); } xml = xml->next; } return techn; } ================================================ FILE: src/io/dae/fx/techn.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_technique_fx_h #define dae_technique_fx_h #include "../common.h" AK_HIDE AkTechniqueFx* dae_techniqueFx(DAEState * __restrict dst, xml_t * __restrict xml, void * __restrict memp); #endif /* dae_technique_fx_h */ ================================================ FILE: src/io/dae/postscript.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "postscript.h" #include "../../xml.h" #include "1.4/dae14.h" #include "fixup/geom.h" #include "fixup/angle.h" #include "fixup/tex.h" #include "fixup/ctlr.h" #include "fixup/channel.h" #include "bugfix/scenekit.h" AK_HIDE void dae_retain_refs(DAEState * __restrict dst); AK_HIDE void dae_fixup_accessors(DAEState * __restrict dst); AK_HIDE void dae_pre_mesh(DAEState * __restrict dst); AK_HIDE void dae_pre_walk(RBTree *tree, RBNode *rbnode); AK_HIDE void dae_input_walk(RBTree *tree, RBNode *rbnode); AK_HIDE void dae_attach_orphan_morphs(DAEState * __restrict dst); AK_HIDE void dae_spread_vert(DAEState * __restrict dst) { AkHeap *heap; AkVertices *vert; AkMeshPrimitive *prim; AkDAEVerticesMapItem *item; FListItem *fitem; AkInput *inp; AkInput *inpv; AkURL *url; if (!(heap = dst->heap) || !(fitem = dst->vertMap)) { return; } /* copy to all primitives */ do { item = fitem->data; prim = item->prim; inp = item->inp; url = rb_find(dst->inputmap, inp); if (!(vert = ak_getObjectByUrl(url))) continue; inpv = vert->input; while (inpv) { inp = ak_heap_calloc(heap, prim, sizeof(*inp)); inp->semantic = inpv->semantic; if (inpv->semanticRaw) inp->semanticRaw = ak_heap_strdup(heap, inp, inpv->semanticRaw); inp->offset = prim->reserved1; inp->set = prim->reserved2; inp->next = prim->input; prim->input = inp; if (inp->semantic == AK_INPUT_POSITION) { prim->pos = inp; if (!rb_find(dst->meshInfo, prim->mesh)) { AkDaeMeshInfo *mi; mi = ak_heap_calloc(heap, NULL, sizeof(*mi)); mi->pos = inp; rb_insert(dst->meshInfo, prim->mesh, mi); } } if ((url = rb_find(dst->inputmap, inpv))) { ak_url_dup(url, inp, url); rb_insert(dst->inputmap, inp, url); } prim->inputCount++; inpv = inpv->next; } /* cleanup will be automatically, because same vertices may be used in multiple places */ } while ((fitem = fitem->next)); } AK_HIDE void dae_postscript(DAEState * __restrict dst) { AkCoordCvtType coordCvtType; AkCoordSys *sourceCoordSys, *targetCoordSys; bool fixTransform; coordCvtType = (AkCoordCvtType)ak_opt_get(AK_OPT_COORD_CONVERT_TYPE); sourceCoordSys = dst->doc ? dst->doc->coordSys : NULL; targetCoordSys = (void *)ak_opt_get(AK_OPT_COORD); fixTransform = coordCvtType == AK_COORD_CVT_FIX_TRANSFORM && sourceCoordSys && targetCoordSys && sourceCoordSys != targetCoordSys && !ak_coordOrientationIsEq(sourceCoordSys, targetCoordSys); dae_spread_vert(dst); /* first migrate 1.4 to 1.5 */ if (dst->version < AK_COLLADA_VERSION_150) dae14_loadjobs_finish(dst); dae_retain_refs(dst); rb_walk(dst->inputmap, dae_input_walk); dae_fixAngles(dst); dae_fixup_accessors(dst); dae_pre_mesh(dst); dae_bugfix_scenekit_backfaces(dst); /* fixup when finished, because we need to collect about source/array usages also we can run fixups as parallels here */ if (!ak_opt_get(AK_OPT_INDICES_DEFAULT)) dae_geom_fixup_all(dst->doc); /* fixup morph and skin because order of vertices may be changed */ if (dst->doc->lib.controllers) { dae_fixup_ctlr(dst); dae_fixup_instctlr(dst); /* Soft attach: many DAE assets — particularly glTF→DAE converted ones — reference the morph base mesh via and forget to wrap it in . The morph controller is in the library but never instanced, so dae_fixup_instctlr never sees it. Walk the scene graph and attach any morph controller that targets this geometry. */ dae_attach_orphan_morphs(dst); } /* Resolve animation channel targets that need controller/instance topology — currently the indexed-array form used for morph weights, e.g. . Must run after morph instances exist (including the orphan-attach pass above). */ if (dst->doc->lib.animations) dae_fixup_channel(dst); /* now set used coordSys */ if (coordCvtType != AK_COORD_CVT_DISABLED) dst->doc->coordSys = targetCoordSys; dae_fix_textures(dst); if (dst->doc && dst->doc->lib.visualScenes) { for (AkVisualScene *vscn = (void *)dst->doc->lib.visualScenes->chld; vscn; vscn = (void *)vscn->base.next) { if (fixTransform) ak_fixSceneCoordSys(vscn); } } } AK_HIDE void dae_retain_refs(DAEState * __restrict dst) { AkHeapAllocator *alc; AkURLQueue *it, *tofree; AkURL *url; AkHeapNode *hnode; int *refc; AkResult ret; alc = dst->heap->allocator; it = dst->urlQueue; while (it) { url = it->url; tofree = it; /* currently only retain objects in this doc */ if (it->url->doc == dst->doc) { hnode = NULL; ret = ak_heap_getNodeByURL(dst->heap, url, &hnode); if (ret == AK_OK && hnode) { /* retain and source arrays ... */ refc = ak_heap_ext_add(dst->heap, hnode, AK_HEAP_NODE_FLAGS_REFC); it->url->ptr = ak__alignas(hnode); (*refc)++; } } it = it->next; alc->free(tofree); } } /* * For every in the scene graph, check whether the * geometry it references has a registered morph controller in * meshTargets. If so, synthesize an AkInstanceMorph and attach it. This * compensates for DAE exporters (typically glTF→DAE converters) that * emit a morph controller but reference the base geometry directly via * rather than wrapping it in . * * Idempotent: skips instGeoms that already have a morpher (real * path already attached one in dae_fixup_instctlr). */ static void dae_attach_orphan_morphs_node(DAEState * __restrict dst, AkNode *node) { AkInstanceGeometry *instGeom; AkInstanceMorph *instMorph; AkMorph *morph; AkGeometry *geom; for (; node; node = (AkNode *)node->next) { for (instGeom = node->geometry; instGeom; instGeom = (AkInstanceGeometry *)instGeom->base.next) { if (instGeom->morpher) continue; if (!(geom = instGeom->base.url.ptr)) continue; if (ak_typeid(geom) != AKT_GEOMETRY) continue; if (!(morph = rb_find(dst->meshTargets, geom))) continue; instMorph = ak_heap_calloc(dst->heap, node, sizeof(*instMorph)); instMorph->morph = morph; instMorph->overrideWeights = NULL; instGeom->morpher = instMorph; } if (node->chld) dae_attach_orphan_morphs_node(dst, node->chld); } } AK_HIDE void dae_attach_orphan_morphs(DAEState * __restrict dst) { AkVisualScene *vscn; if (!dst->meshTargets || !dst->doc->lib.visualScenes) return; for (vscn = (void *)dst->doc->lib.visualScenes->chld; vscn; vscn = (void *)vscn->base.next) { dae_attach_orphan_morphs_node(dst, vscn->node); } } AK_HIDE void dae_input_walk(RBTree *tree, RBNode *rbnode) { AkAccessor *acc; AkSource *src; AkInput *inp; AkURL *url; AK__UNUSED(tree); inp = rbnode->key; if (inp->semantic == AK_INPUT_SEMANTIC_VERTEX) { return; } url = rbnode->val; if (!(src = ak_getObjectByUrl(url))) return; acc = src->tcommon; inp->accessor = acc; /* TODO: handle error if null?? */ if (acc) { ak_retain(acc); } /* TODO: */ // ak_free(src); // ak_free(url); // // rb_destroy(tree); } AK_HIDE void dae_fixup_accessors(DAEState * __restrict dst) { AkHeap *heap; AkDoc *doc; FListItem *item; AkAccessor *acc; AkAccessorDAE *accdae; AkBuffer *buff; AkTypeDesc *type; item = dst->accessors; heap = dst->heap; doc = dst->doc; while (item) { acc = item->data; accdae = ak_userData(acc); buff = ak_getObjectByUrl(&accdae->source); acc->buffer = buff; if ((buff = ak_getObjectByUrl(&accdae->source))) { AkBuffer *newbuff; AkDataParam *dp; char *olditms, *newitms; uint32_t i, j, count, dpoff, bytesPerComponent; size_t oldByteStride, newByteStride; acc->componentType = (AkTypeId)(uintptr_t)ak_userData(buff); if ((type = ak_typeDesc(acc->componentType))) bytesPerComponent = type->size; else goto cont; count = acc->count; acc->byteStride = accdae->stride * bytesPerComponent; acc->byteLength = count * accdae->stride * bytesPerComponent; acc->byteOffset = accdae->offset * bytesPerComponent; accdae->bound = accdae->stride; acc->fillByteSize = accdae->bound * bytesPerComponent; acc->componentCount = accdae->bound; acc->bytesPerComponent = bytesPerComponent; /*--------------------------------------------------------------------* eliminate / remove Data Params e.g. X, Y, Z to make Accessor more small and cleaner *--------------------------------------------------------------------*/ /* the buffer is used more than one place, so duplicate data */ /* TODO: check param that has empty name */ if (acc->buffer && ak_refc(buff) > 1) { oldByteStride = acc->byteStride; newByteStride = accdae->bound * bytesPerComponent; newbuff = ak_heap_calloc(heap, doc, sizeof(*newbuff)); newbuff->length = count * newByteStride; newbuff->data = ak_heap_calloc(heap, newbuff, newbuff->length); newitms = (char *)newbuff->data; olditms = (char *)buff->data + acc->byteOffset; for (i = 0; i < count; i++) { j = 0; dpoff = 0; dp = accdae->param; while (dp) { if (dp->name) { memcpy(newitms + newByteStride * i + bytesPerComponent * j++, olditms + oldByteStride * i + dpoff, dp->type.size); } dpoff += dp->type.size; dp = dp->next; } } ak_release(acc->buffer); ak_retain(newbuff); acc->buffer = newbuff; } ak_heap_ext_rm(heap, ak__alignof(buff), AK_HEAP_NODE_FLAGS_USR); } ak_heap_ext_rm(heap, ak__alignof(accdae), AK_HEAP_NODE_FLAGS_USR); ak_free(accdae); cont: item = item->next; } flist_sp_destroy(&dst->accessors); } AK_HIDE void dae_pre_walk(RBTree *tree, RBNode *rbnode) { AkDaeMeshInfo *mi; AkAccessor *posAcc; AK__UNUSED(tree); mi = rbnode->val; posAcc = NULL; if (!(posAcc = mi->pos->accessor)) return; mi->nVertex = posAcc->count; } AK_HIDE void dae_pre_mesh(DAEState * __restrict dst) { rb_walk(dst->meshInfo, dae_pre_walk); } ================================================ FILE: src/io/dae/postscript.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_postscript_h #define dae_postscript_h #include "common.h" AK_HIDE void dae_postscript(DAEState * __restrict dst); #endif /* dae_postscript_h */ ================================================ FILE: src/io/dae/strpool.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _DAE_STRPOOL_ # define _DAE_STRPOOL_ #endif #include "strpool.h" #include const char _s_dae_pool_0[] = " \0" "COLLADA\0" "asset\0" "contributor\0" "author\0" "author_email\0" "author_website\0" "authoring_tool\0" "comments\0" "copyright\0" "source_data\0" "created\0" "modified\0" "keywords\0" "revision\0" "subject\0" "title\0" "unit\0" "name\0" "meter\0" "up_axis\0" "X_UP\0" "Z_UP\0" "Y_UP\0" "extra\0" "library_cameras\0" "id\0" "camera\0" "optics\0" "technique\0" "technique_common\0" "profile\0" "xmlns\0" "perspective\0" "xfov\0" "yfov\0" "sid\0" "aspect_ratio\0" "znear\0" "zfar\0" "orthographic\0" "xmag\0" "ymag\0" "ambient\0" "color\0" "directional\0" "point\0" "constant_attenuation\0" "linear_attenuation\0" "quadratic_attenuation\0" "spot\0" "falloff_angle\0" "falloff_exponent\0" "imager\0" "light\0" "library_lights\0" "library_effects\0" "effect\0" "newparam\0" "profile_COMMON\0" "string\0" "bool\0" "bool2\0" "bool3\0" "bool4\0" "int\0" "int2\0" "int3\0" "int4\0" "float\0" "float2\0" "float3\0" "float4\0" "float2x2\0" "float3x3\0" "float4x4\0" "semantic\0" "ref\0" "type\0" "platform\0" "url\0" "pass\0" "blinn\0" "constant\0" "lambert\0" "phong\0" "emission\0" "diffuse\0" "specular\0" "shininess\0" "reflective\0" "reflectivity\0" "transparent\0" "transparency\0" "index_of_refraction\0" "opaque\0" "texture\0" "texcoord\0" "param\0" "func\0" "value\0" "src\0" "dest\0" "src_rgb\0" "dest_rgb\0" "src_alpha\0" "dest_alpha\0" "rgb\0" "alpha\0" "face\0" "mode\0" "mask\0" "fail\0" "zfail\0" "zpass\0" "front\0" "back\0" "index\0" "instance_image\0" "renderable\0" "share\0" "true\0" "false\0" "init_from\0" "create_2d\0" "create_3d\0" "create_cube\0" "image\0" "mips_generate\0" "array_index\0" "mip_index\0" "depth\0" "hex\0" "format\0" "size_exact\0" "width\0" "height\0" "size_ratio\0" "mips\0" "levels\0" "auto_generate\0" "unnormalized\0" "array\0" "length\0" "hint\0" "channels\0" "range\0" "precision\0" "space\0" "exact\0" "size\0" "wrap_s\0" "wrap_t\0" "wrap_p\0" "minfilter\0" "magfilter\0" "mipfilter\0" "border_color\0" "mip_max_level\0" "mip_min_level\0" "mip_bias\0" "max_anisotropy\0" "target\0" "symbol\0" "slice\0" "mip\0" "library_images\0" "library_materials\0" "instance_effect\0" "technique_hint\0" "material\0" "library_geometries\0" "geometry\0" "mesh\0" "source\0" "count\0" "accessor\0" "stride\0" "X\0" "Y\0" "Z\0" "vertices\0" "input\0" "offset\0" "polylist\0" "vcount\0" "p\0" "convex_mesh\0" "spline\0" "brep\0" "lines\0" "linestrips\0" "polygons\0" "triangles\0" "trifans\0" "tristrips\0" "float_array\0" "bool_array\0" "IDREF_array\0" "int_array\0" "Name_array\0" "SIDREF_array\0" "token_array\0" "ph\0" "h\0" "convex_hull_of\0" "control_vertices\0" "closed\0" "set\0" "line\0" "circle\0" "ellipse\0" "parabola\0" "hyperbola\0" "nurbs\0" "orient\0" "origin\0" "curve\0" "direction\0" "radius\0" "degree\0" "degree_u\0" "closed_u\0" "degree_v\0" "closed_v\0" "cone\0" "plane\0" "cylinder\0" "nurbs_surface\0" "sphere\0" "torus\0" "swept_surface\0" "surface\0" "angle\0" "axis\0" "curves\0" "surface_curves\0" "surfaces\0" "edges\0" "wires\0" "faces\0" "pcurves\0" ; const char _s_dae_pool_1[] = "shells\0" "solids\0" "library_controllers\0" "controller\0" "skin\0" "morph\0" "bind_shape_matrix\0" "joints\0" "vertex_weights\0" "targets\0" "v\0" "method\0" "library_visual_scenes\0" "visual_scene\0" "node\0" "evaluate_scene\0" "layer\0" "lookat\0" "matrix\0" "rotate\0" "scale\0" "skew\0" "translate\0" "instance_camera\0" "instance_controller\0" "skeleton\0" "bind_material\0" "instance_material\0" "bind\0" "bind_vertex_input\0" "input_semantic\0" "input_set\0" "instance_geometry\0" "instance_light\0" "instance_node\0" "proxy\0" "render\0" "camera_node\0" "technique_override\0" "enable\0" "library_nodes\0" "instance_visual_scene\0" "scene\0" "sampler1D\0" "sampler2D\0" "sampler3D\0" "samplerRECT\0" "samplerCUBE\0" "samplerDEPTH\0" "data\0" "version\0" "format_hint\0" "viewport_ratio\0" "mip_levels\0" "mipmap_generate\0" "option\0" "init_as_target\0" "init_cube\0" "all\0" "primary\0" "order\0" "sketchup\0" "library_animations\0" "animation\0" "sampler\0" "channel\0" "pre_behavior\0" "post_behavior\0" "focal\0" "equation\0" ; #undef _DAE_STRPOOL_ ================================================ FILE: src/io/dae/strpool.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef dae_strpool_h # define dae_strpool_h #ifndef _DAE_STRPOOL_ # define _AK_EXTERN extern #else # define _AK_EXTERN #endif _AK_EXTERN const char _s_dae_pool_0[]; _AK_EXTERN const char _s_dae_pool_1[]; #define _s_dae_0(x) (_s_dae_pool_0 + x) #define _s_dae_1(x) (_s_dae_pool_1 + x) /* _s_dae_pool_0 */ #define _s_dae_space _s_dae_0(0) #define _s_dae_collada _s_dae_0(2) #define _s_dae_asset _s_dae_0(10) #define _s_dae_contributor _s_dae_0(16) #define _s_dae_author _s_dae_0(28) #define _s_dae_author_email _s_dae_0(35) #define _s_dae_author_website _s_dae_0(48) #define _s_dae_authoring_tool _s_dae_0(63) #define _s_dae_comments _s_dae_0(78) #define _s_dae_copyright _s_dae_0(87) #define _s_dae_source_data _s_dae_0(97) #define _s_dae_created _s_dae_0(109) #define _s_dae_modified _s_dae_0(117) #define _s_dae_keywords _s_dae_0(126) #define _s_dae_revision _s_dae_0(135) #define _s_dae_subject _s_dae_0(144) #define _s_dae_title _s_dae_0(152) #define _s_dae_unit _s_dae_0(158) #define _s_dae_name _s_dae_0(163) #define _s_dae_meter _s_dae_0(168) #define _s_dae_up_axis _s_dae_0(174) #define _s_dae_x_up _s_dae_0(182) #define _s_dae_z_up _s_dae_0(187) #define _s_dae_y_up _s_dae_0(192) #define _s_dae_extra _s_dae_0(197) #define _s_dae_lib_cameras _s_dae_0(203) #define _s_dae_id _s_dae_0(219) #define _s_dae_camera _s_dae_0(222) #define _s_dae_optics _s_dae_0(229) #define _s_dae_technique _s_dae_0(236) #define _s_dae_techniquec _s_dae_0(246) #define _s_dae_profile _s_dae_0(263) #define _s_dae_xmlns _s_dae_0(271) #define _s_dae_perspective _s_dae_0(277) #define _s_dae_xfov _s_dae_0(289) #define _s_dae_yfov _s_dae_0(294) #define _s_dae_sid _s_dae_0(299) #define _s_dae_aspect_ratio _s_dae_0(303) #define _s_dae_znear _s_dae_0(316) #define _s_dae_zfar _s_dae_0(322) #define _s_dae_orthographic _s_dae_0(327) #define _s_dae_xmag _s_dae_0(340) #define _s_dae_ymag _s_dae_0(345) #define _s_dae_ambient _s_dae_0(350) #define _s_dae_color _s_dae_0(358) #define _s_dae_directional _s_dae_0(364) #define _s_dae_point _s_dae_0(376) #define _s_dae_const_attn _s_dae_0(382) #define _s_dae_linear_attn _s_dae_0(403) #define _s_dae_quad_attn _s_dae_0(422) #define _s_dae_spot _s_dae_0(444) #define _s_dae_falloff_angle _s_dae_0(449) #define _s_dae_falloff_exp _s_dae_0(463) #define _s_dae_imager _s_dae_0(480) #define _s_dae_light _s_dae_0(487) #define _s_dae_lib_lights _s_dae_0(493) #define _s_dae_lib_effects _s_dae_0(508) #define _s_dae_effect _s_dae_0(524) #define _s_dae_newparam _s_dae_0(531) #define _s_dae_prfl_common _s_dae_0(540) #define _s_dae_string _s_dae_0(555) #define _s_dae_bool _s_dae_0(562) #define _s_dae_bool2 _s_dae_0(567) #define _s_dae_bool3 _s_dae_0(573) #define _s_dae_bool4 _s_dae_0(579) #define _s_dae_int _s_dae_0(585) #define _s_dae_int2 _s_dae_0(589) #define _s_dae_int3 _s_dae_0(594) #define _s_dae_int4 _s_dae_0(599) #define _s_dae_float _s_dae_0(604) #define _s_dae_float2 _s_dae_0(610) #define _s_dae_float3 _s_dae_0(617) #define _s_dae_float4 _s_dae_0(624) #define _s_dae_float2x2 _s_dae_0(631) #define _s_dae_float3x3 _s_dae_0(640) #define _s_dae_float4x4 _s_dae_0(649) #define _s_dae_semantic _s_dae_0(658) #define _s_dae_ref _s_dae_0(667) #define _s_dae_type _s_dae_0(671) #define _s_dae_platform _s_dae_0(676) #define _s_dae_url _s_dae_0(685) #define _s_dae_pass _s_dae_0(689) #define _s_dae_blinn _s_dae_0(694) #define _s_dae_constant _s_dae_0(700) #define _s_dae_lambert _s_dae_0(709) #define _s_dae_phong _s_dae_0(717) #define _s_dae_emission _s_dae_0(723) #define _s_dae_diffuse _s_dae_0(732) #define _s_dae_specular _s_dae_0(740) #define _s_dae_shininess _s_dae_0(749) #define _s_dae_reflective _s_dae_0(759) #define _s_dae_reflectivity _s_dae_0(770) #define _s_dae_transparent _s_dae_0(783) #define _s_dae_transparency _s_dae_0(795) #define _s_dae_index_of_refraction _s_dae_0(808) #define _s_dae_opaque _s_dae_0(828) #define _s_dae_texture _s_dae_0(835) #define _s_dae_texcoord _s_dae_0(843) #define _s_dae_param _s_dae_0(852) #define _s_dae_func _s_dae_0(858) #define _s_dae_value _s_dae_0(863) #define _s_dae_src _s_dae_0(869) #define _s_dae_dest _s_dae_0(873) #define _s_dae_src_rgb _s_dae_0(878) #define _s_dae_dest_rgb _s_dae_0(886) #define _s_dae_src_alpha _s_dae_0(895) #define _s_dae_dest_alpha _s_dae_0(905) #define _s_dae_rgb _s_dae_0(916) #define _s_dae_alpha _s_dae_0(920) #define _s_dae_face _s_dae_0(926) #define _s_dae_mode _s_dae_0(931) #define _s_dae_mask _s_dae_0(936) #define _s_dae_fail _s_dae_0(941) #define _s_dae_zfail _s_dae_0(946) #define _s_dae_zpass _s_dae_0(952) #define _s_dae_front _s_dae_0(958) #define _s_dae_back _s_dae_0(964) #define _s_dae_index _s_dae_0(969) #define _s_dae_instance_image _s_dae_0(975) #define _s_dae_renderable _s_dae_0(990) #define _s_dae_share _s_dae_0(1001) #define _s_dae_true _s_dae_0(1007) #define _s_dae_false _s_dae_0(1012) #define _s_dae_init_from _s_dae_0(1018) #define _s_dae_create_2d _s_dae_0(1028) #define _s_dae_create_3d _s_dae_0(1038) #define _s_dae_create_cube _s_dae_0(1048) #define _s_dae_image _s_dae_0(1060) #define _s_dae_mips_generate _s_dae_0(1066) #define _s_dae_array_index _s_dae_0(1080) #define _s_dae_mip_index _s_dae_0(1092) #define _s_dae_depth _s_dae_0(1102) #define _s_dae_hex _s_dae_0(1108) #define _s_dae_format _s_dae_0(1112) #define _s_dae_size_exact _s_dae_0(1119) #define _s_dae_width _s_dae_0(1130) #define _s_dae_height _s_dae_0(1136) #define _s_dae_size_ratio _s_dae_0(1143) #define _s_dae_mips _s_dae_0(1154) #define _s_dae_levels _s_dae_0(1159) #define _s_dae_auto_generate _s_dae_0(1166) #define _s_dae_unnormalized _s_dae_0(1180) #define _s_dae_array _s_dae_0(1193) #define _s_dae_length _s_dae_0(1199) #define _s_dae_hint _s_dae_0(1206) #define _s_dae_channels _s_dae_0(1211) #define _s_dae_range _s_dae_0(1220) #define _s_dae_precision _s_dae_0(1226) #define _s_dae_spaceText _s_dae_0(1236) #define _s_dae_exact _s_dae_0(1242) #define _s_dae_size _s_dae_0(1248) #define _s_dae_wrap_s _s_dae_0(1253) #define _s_dae_wrap_t _s_dae_0(1260) #define _s_dae_wrap_p _s_dae_0(1267) #define _s_dae_minfilter _s_dae_0(1274) #define _s_dae_magfilter _s_dae_0(1284) #define _s_dae_mipfilter _s_dae_0(1294) #define _s_dae_border_color _s_dae_0(1304) #define _s_dae_mip_max_level _s_dae_0(1317) #define _s_dae_mip_min_level _s_dae_0(1331) #define _s_dae_mip_bias _s_dae_0(1345) #define _s_dae_max_anisotropy _s_dae_0(1354) #define _s_dae_target _s_dae_0(1369) #define _s_dae_symbol _s_dae_0(1376) #define _s_dae_slice _s_dae_0(1383) #define _s_dae_mip _s_dae_0(1389) #define _s_dae_lib_images _s_dae_0(1393) #define _s_dae_lib_materials _s_dae_0(1408) #define _s_dae_inst_effect _s_dae_0(1426) #define _s_dae_technique_hint _s_dae_0(1442) #define _s_dae_material _s_dae_0(1457) #define _s_dae_lib_geometries _s_dae_0(1466) #define _s_dae_geometry _s_dae_0(1485) #define _s_dae_mesh _s_dae_0(1494) #define _s_dae_source _s_dae_0(1499) #define _s_dae_count _s_dae_0(1506) #define _s_dae_accessor _s_dae_0(1512) #define _s_dae_stride _s_dae_0(1521) #define _s_dae_X _s_dae_0(1528) #define _s_dae_Y _s_dae_0(1530) #define _s_dae_Z _s_dae_0(1532) #define _s_dae_vertices _s_dae_0(1534) #define _s_dae_input _s_dae_0(1543) #define _s_dae_offset _s_dae_0(1549) #define _s_dae_polylist _s_dae_0(1556) #define _s_dae_vcount _s_dae_0(1565) #define _s_dae_p _s_dae_0(1572) #define _s_dae_convex_mesh _s_dae_0(1574) #define _s_dae_spline _s_dae_0(1586) #define _s_dae_brep _s_dae_0(1593) #define _s_dae_lines _s_dae_0(1598) #define _s_dae_linestrips _s_dae_0(1604) #define _s_dae_polygons _s_dae_0(1615) #define _s_dae_triangles _s_dae_0(1624) #define _s_dae_trifans _s_dae_0(1634) #define _s_dae_tristrips _s_dae_0(1642) #define _s_dae_float_array _s_dae_0(1652) #define _s_dae_bool_array _s_dae_0(1664) #define _s_dae_IDREF_array _s_dae_0(1675) #define _s_dae_int_array _s_dae_0(1687) #define _s_dae_Name_array _s_dae_0(1697) #define _s_dae_SIDREF_array _s_dae_0(1708) #define _s_dae_token_array _s_dae_0(1721) #define _s_dae_ph _s_dae_0(1733) #define _s_dae_h _s_dae_0(1736) #define _s_dae_convex_hull_of _s_dae_0(1738) #define _s_dae_control_vertices _s_dae_0(1753) #define _s_dae_closed _s_dae_0(1770) #define _s_dae_set _s_dae_0(1777) #define _s_dae_line _s_dae_0(1781) #define _s_dae_circle _s_dae_0(1786) #define _s_dae_ellipse _s_dae_0(1793) #define _s_dae_parabola _s_dae_0(1801) #define _s_dae_hyperbola _s_dae_0(1810) #define _s_dae_nurbs _s_dae_0(1820) #define _s_dae_orient _s_dae_0(1826) #define _s_dae_origin _s_dae_0(1833) #define _s_dae_curve _s_dae_0(1840) #define _s_dae_direction _s_dae_0(1846) #define _s_dae_radius _s_dae_0(1856) #define _s_dae_degree _s_dae_0(1863) #define _s_dae_degree_u _s_dae_0(1870) #define _s_dae_closed_u _s_dae_0(1879) #define _s_dae_degree_v _s_dae_0(1888) #define _s_dae_closed_v _s_dae_0(1897) #define _s_dae_cone _s_dae_0(1906) #define _s_dae_plane _s_dae_0(1911) #define _s_dae_cylinder _s_dae_0(1917) #define _s_dae_nurbs_surface _s_dae_0(1926) #define _s_dae_sphere _s_dae_0(1940) #define _s_dae_torus _s_dae_0(1947) #define _s_dae_swept_surface _s_dae_0(1953) #define _s_dae_surface _s_dae_0(1967) #define _s_dae_angle _s_dae_0(1975) #define _s_dae_axis _s_dae_0(1981) #define _s_dae_curves _s_dae_0(1986) #define _s_dae_surface_curves _s_dae_0(1993) #define _s_dae_surfaces _s_dae_0(2008) #define _s_dae_edges _s_dae_0(2017) #define _s_dae_wires _s_dae_0(2023) #define _s_dae_faces _s_dae_0(2029) #define _s_dae_pcurves _s_dae_0(2035) /* _s_dae_pool_1 */ #define _s_dae_shells _s_dae_1(0) #define _s_dae_solids _s_dae_1(7) #define _s_dae_lib_controllers _s_dae_1(14) #define _s_dae_controller _s_dae_1(34) #define _s_dae_skin _s_dae_1(45) #define _s_dae_morph _s_dae_1(50) #define _s_dae_bind_shape_matrix _s_dae_1(56) #define _s_dae_joints _s_dae_1(74) #define _s_dae_vertex_weights _s_dae_1(81) #define _s_dae_targets _s_dae_1(96) #define _s_dae_v _s_dae_1(104) #define _s_dae_method _s_dae_1(106) #define _s_dae_lib_visual_scenes _s_dae_1(113) #define _s_dae_visual_scene _s_dae_1(135) #define _s_dae_node _s_dae_1(148) #define _s_dae_evaluate_scene _s_dae_1(153) #define _s_dae_layer _s_dae_1(168) #define _s_dae_lookat _s_dae_1(174) #define _s_dae_matrix _s_dae_1(181) #define _s_dae_rotate _s_dae_1(188) #define _s_dae_scale _s_dae_1(195) #define _s_dae_skew _s_dae_1(201) #define _s_dae_translate _s_dae_1(206) #define _s_dae_instance_camera _s_dae_1(216) #define _s_dae_instance_controller _s_dae_1(232) #define _s_dae_skeleton _s_dae_1(252) #define _s_dae_bind_material _s_dae_1(261) #define _s_dae_instance_material _s_dae_1(275) #define _s_dae_bind _s_dae_1(293) #define _s_dae_bind_vertex_input _s_dae_1(298) #define _s_dae_input_semantic _s_dae_1(316) #define _s_dae_input_set _s_dae_1(331) #define _s_dae_instance_geometry _s_dae_1(341) #define _s_dae_instance_light _s_dae_1(359) #define _s_dae_instance_node _s_dae_1(374) #define _s_dae_proxy _s_dae_1(388) #define _s_dae_render _s_dae_1(394) #define _s_dae_camera_node _s_dae_1(401) #define _s_dae_technique_override _s_dae_1(413) #define _s_dae_enable _s_dae_1(432) #define _s_dae_lib_nodes _s_dae_1(439) #define _s_dae_instance_visual_scene _s_dae_1(453) #define _s_dae_scene _s_dae_1(475) #define _s_dae_sampler1d _s_dae_1(481) #define _s_dae_sampler2d _s_dae_1(491) #define _s_dae_sampler3d _s_dae_1(501) #define _s_dae_sampler_rect _s_dae_1(511) #define _s_dae_sampler_cube _s_dae_1(523) #define _s_dae_sampler_depth _s_dae_1(535) #define _s_dae_data _s_dae_1(548) #define _s_dae_version _s_dae_1(553) #define _s_dae_format_hint _s_dae_1(561) #define _s_dae_viewport_ratio _s_dae_1(573) #define _s_dae_mip_levels _s_dae_1(588) #define _s_dae_mipmap_generate _s_dae_1(599) #define _s_dae_option _s_dae_1(615) #define _s_dae_init_as_target _s_dae_1(622) #define _s_dae_init_cube _s_dae_1(637) #define _s_dae_all _s_dae_1(647) #define _s_dae_primary _s_dae_1(651) #define _s_dae_order _s_dae_1(659) #define _s_dae_sketchup _s_dae_1(665) #define _s_dae_lib_animations _s_dae_1(674) #define _s_dae_animation _s_dae_1(693) #define _s_dae_sampler _s_dae_1(703) #define _s_dae_channel _s_dae_1(711) #define _s_dae_pre_behavior _s_dae_1(719) #define _s_dae_post_behavior _s_dae_1(732) #define _s_dae_focal _s_dae_1(746) #define _s_dae_equation _s_dae_1(752) #endif /* dae_strpool_h */ ================================================ FILE: src/io/dae/strpool.json ================================================ { "space": " ", "collada": "COLLADA", "asset": "asset", "contributor": "contributor", "author": "author", "author_email": "author_email", "author_website":"author_website", "authoring_tool":"authoring_tool", "comments": "comments", "copyright": "copyright", "source_data": "source_data", "created": "created", "modified": "modified", "keywords": "keywords", "revision": "revision", "subject": "subject", "title": "title", "unit": "unit", "name": "name", "meter": "meter", "up_axis": "up_axis", "x_up": "X_UP", "z_up": "Z_UP", "y_up": "Y_UP", "extra": "extra", "lib_cameras": "library_cameras", "id": "id", "camera": "camera", "optics": "optics", "technique": "technique", "techniquec": "technique_common", "profile": "profile", "xmlns": "xmlns", "perspective": "perspective", "xfov": "xfov", "yfov": "yfov", "sid": "sid", "aspect_ratio": "aspect_ratio", "znear": "znear", "zfar": "zfar", "orthographic": "orthographic", "xmag": "xmag", "ymag": "ymag", "ambient": "ambient", "color": "color", "directional": "directional", "point": "point", "const_attn": "constant_attenuation", "linear_attn": "linear_attenuation", "quad_attn": "quadratic_attenuation", "spot": "spot", "falloff_angle": "falloff_angle", "falloff_exp": "falloff_exponent", "imager": "imager", "light": "light", "lib_lights": "library_lights", "lib_effects": "library_effects", "effect": "effect", "newparam": "newparam", "prfl_common": "profile_COMMON", "string": "string", "bool": "bool", "bool2": "bool2", "bool3": "bool3", "bool4": "bool4", "int": "int", "int2": "int2", "int3": "int3", "int4": "int4", "float": "float", "float2": "float2", "float3": "float3", "float4": "float4", "float2x2": "float2x2", "float3x3": "float3x3", "float4x4": "float4x4", "semantic": "semantic", "ref": "ref", "type": "type", "platform": "platform", "url": "url", "pass": "pass", "blinn": "blinn", "constant": "constant", "lambert": "lambert", "phong": "phong", "emission": "emission", "diffuse": "diffuse", "specular": "specular", "shininess": "shininess", "reflective": "reflective", "reflectivity": "reflectivity", "transparent": "transparent", "transparency": "transparency", "index_of_refraction": "index_of_refraction", "opaque": "opaque", "texture": "texture", "texcoord": "texcoord", "param": "param", "func": "func", "value": "value", "src": "src", "dest": "dest", "src_rgb": "src_rgb", "dest_rgb": "dest_rgb", "src_alpha": "src_alpha", "dest_alpha": "dest_alpha", "rgb": "rgb", "alpha": "alpha", "face": "face", "mode": "mode", "mask": "mask", "fail": "fail", "zfail": "zfail", "zpass": "zpass", "front": "front", "back": "back", "index": "index", "instance_image": "instance_image", "renderable": "renderable", "share": "share", "true": "true", "false": "false", "init_from": "init_from", "create_2d": "create_2d", "create_3d": "create_3d", "create_cube": "create_cube", "image": "image", "mips_generate": "mips_generate", "array_index": "array_index", "mip_index": "mip_index", "depth": "depth", "hex": "hex", "format": "format", "size_exact": "size_exact", "width": "width", "height": "height", "size_ratio": "size_ratio", "mips": "mips", "levels": "levels", "auto_generate": "auto_generate", "unnormalized": "unnormalized", "array": "array", "length": "length", "hint": "hint", "channels": "channels", "range": "range", "precision": "precision", "spaceText": "space", "exact": "exact", "size": "size", "wrap_s": "wrap_s", "wrap_t": "wrap_t", "wrap_p": "wrap_p", "minfilter": "minfilter", "magfilter": "magfilter", "mipfilter": "mipfilter", "border_color": "border_color", "mip_max_level": "mip_max_level", "mip_min_level": "mip_min_level", "mip_bias": "mip_bias", "max_anisotropy": "max_anisotropy", "target": "target", "symbol": "symbol", "slice": "slice", "mip": "mip", "lib_images": "library_images", "lib_materials": "library_materials", "inst_effect": "instance_effect", "technique_hint": "technique_hint", "material": "material", "lib_geometries": "library_geometries", "geometry": "geometry", "mesh": "mesh", "source": "source", "count": "count", "accessor": "accessor", "stride": "stride", "X": "X", "Y": "Y", "Z": "Z", "vertices": "vertices", "input": "input", "offset": "offset", "polylist": "polylist", "vcount": "vcount", "p": "p", "convex_mesh": "convex_mesh", "spline": "spline", "brep": "brep", "lines": "lines", "linestrips": "linestrips", "polygons": "polygons", "triangles": "triangles", "trifans": "trifans", "tristrips": "tristrips", "float_array": "float_array", "bool_array": "bool_array", "IDREF_array": "IDREF_array", "int_array": "int_array", "Name_array": "Name_array", "SIDREF_array": "SIDREF_array", "token_array": "token_array", "ph": "ph", "h": "h", "convex_hull_of": "convex_hull_of", "control_vertices": "control_vertices", "closed": "closed", "set": "set", "line": "line", "circle": "circle", "ellipse": "ellipse", "parabola": "parabola", "hyperbola": "hyperbola", "nurbs": "nurbs", "orient": "orient", "origin": "origin", "curve": "curve", "direction": "direction", "radius": "radius", "degree": "degree", "degree_u": "degree_u", "closed_u": "closed_u", "degree_v": "degree_v", "closed_v": "closed_v", "cone": "cone", "plane": "plane", "cylinder": "cylinder", "nurbs_surface": "nurbs_surface", "sphere": "sphere", "torus": "torus", "swept_surface": "swept_surface", "surface": "surface", "angle": "angle", "axis": "axis", "curves": "curves", "surface_curves": "surface_curves", "surfaces": "surfaces", "edges": "edges", "wires": "wires", "faces": "faces", "pcurves": "pcurves", "shells": "shells", "solids": "solids", "lib_controllers": "library_controllers", "controller": "controller", "skin": "skin", "morph": "morph", "bind_shape_matrix": "bind_shape_matrix", "joints": "joints", "vertex_weights": "vertex_weights", "targets": "targets", "v": "v", "method": "method", "lib_visual_scenes": "library_visual_scenes", "visual_scene": "visual_scene", "node": "node", "evaluate_scene": "evaluate_scene", "layer": "layer", "lookat": "lookat", "matrix": "matrix", "rotate": "rotate", "scale": "scale", "skew": "skew", "translate": "translate", "instance_camera": "instance_camera", "instance_controller": "instance_controller", "skeleton": "skeleton", "bind_material": "bind_material", "instance_material": "instance_material", "bind": "bind", "bind_vertex_input": "bind_vertex_input", "input_semantic": "input_semantic", "input_set": "input_set", "instance_geometry": "instance_geometry", "instance_light": "instance_light", "instance_node": "instance_node", "proxy": "proxy", "render": "render", "camera_node": "camera_node", "technique_override": "technique_override", "enable": "enable", "lib_nodes": "library_nodes", "instance_visual_scene": "instance_visual_scene", "scene": "scene", "sampler1d": "sampler1D", "sampler2d": "sampler2D", "sampler3d": "sampler3D", "sampler_rect": "samplerRECT", "sampler_cube": "samplerCUBE", "sampler_depth": "samplerDEPTH", "data": "data", "version": "version", "format_hint": "format_hint", "viewport_ratio": "viewport_ratio", "mip_levels": "mip_levels", "mipmap_generate": "mipmap_generate", "option": "option", "init_as_target": "init_as_target", "init_cube": "init_cube", "all": "all", "primary": "primary", "order": "order", "sketchup": "sketchup", "lib_animations": "library_animations", "animation": "animation", "sampler": "sampler", "channel": "channel", "pre_behavior": "pre_behavior", "post_behavior": "post_behavior", "focal": "focal", "equation": "equation" } ================================================ FILE: src/io/dae/strpool.py ================================================ #!/usr/bin/python3 # # Copyright (C) 2020 Recep Aslantas # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # import json from collections import OrderedDict from os.path import realpath from os.path import dirname headerContents = [ ] destdir = dirname(realpath(__file__)) spidx = 0 pos = 0 fspoolJson = open(destdir + "/strpool.json") spool = json.loads(fspoolJson.read(), object_pairs_hook=OrderedDict) fspoolJson.close() fspool_h = open(destdir + "/strpool.h", "wb"); fspool_c = open(destdir + "/strpool.c", "wb"); copyright_str = """\ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ """ fspool_h.write(copyright_str.encode()) fspool_c.write(copyright_str.encode()) fspool_h.write(""" #ifndef dae_strpool_h # define dae_strpool_h #ifndef _DAE_STRPOOL_ # define _AK_EXTERN extern #else # define _AK_EXTERN #endif """.encode()) fspool_c.write(""" #ifndef _DAE_STRPOOL_ # define _DAE_STRPOOL_ #endif #include "strpool.h" #include const char _s_dae_pool_0[] = """.encode()) headerContents.append("\n/* _s_dae_pool_0 */\n") for name, val in spool.items(): valLen = len(val) + 1 # string literal size: 2048 if pos + valLen > 2048: pos = 0 spidx += 1 fspool_c.write(";\n\nconst char _s_dae_pool_{0}[] =\n" .format(str(spidx)).encode()) headerContents.append("\n/* _s_dae_pool_{0} */\n" .format(spidx)) fspool_c.write("\"{0}\\0\"\n".format(val).encode()) headerContents.append("#define _s_dae_{0} _s_dae_{1}({2})\n" .format(name, str(spidx), str(pos))) pos += valLen # source file, then close it fspool_c.write(";\n\n#undef _DAE_STRPOOL_\n".encode()) fspool_c.close() # header file for idx in range(spidx + 1): fspool_h.write("\n_AK_EXTERN const char _s_dae_pool_{0}[];" .format(str(idx)).encode()) fspool_h.write("\n\n".encode()) for idx in range(spidx + 1): fspool_h.write("#define _s_dae_{0}(x) (_s_dae_pool_{0} + x)\n" .format(str(idx)).encode()) # write header contents, then close it fspool_h.writelines(map(lambda x: x.encode(), headerContents)) fspool_h.write("\n#endif /* dae_strpool_h */\n".encode()) fspool_h.close() # try free array del headerContents[:] ================================================ FILE: src/io/gltf/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) add_subdirectory(imp) ================================================ FILE: src/io/gltf/README.md ================================================ # AssetKit: glTF Status - [x] Single Interface for glTF and COLLADA - [x] glTF 2.0 - [x] Options to Generate Mesh Normals - [x] Acessors, Buffers / BufferViews - [x] Geometries (Triangles, Polygons, Lines) - [x] Nodes - [x] Scenes - [x] Cameras - [x] Materials - [x] Images - [x] Samplers - [x] Textures - [x] PBR Materials - [x] Metallic Roughness - [x] Specular Glossiness Extension - [x] Other textures - [x] Occlusion map - [x] Emissive map - [x] Normal Map - [ ] other textures? - [x] alphaMode - [x] alphaCutoff - [x] doubleSided - [x] Skin - [x] Morph - [x] Animations - Extensions - [x] KHR_materials_pbrSpecularGlossiness - [x] KHR_texture_specular - [x] KHR_texture_transform - [x] KHR_materials_clearcoat - [x] KHR_materials_unlit - [x] KHR_materials_emissive_strength - [x] KHR_materials_ior - [x] KHR_materials_transmission - [ ] Lights (TODO) - [ ] Common materials (TODO) - [x] glTF-Separate - [x] glTF-Binary - [x] glTF-Embedded - [ ] Load glTF-Draco (TODO) ### Trademarks glTF and COLLADA and their logos are trademarks of Khronos Group. ================================================ FILE: src/io/gltf/common.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_commoh_h #define gltf_commoh_h #include "../../../include/ak/assetkit.h" #include "../../common.h" #include "../../utils.h" #include "../../tree.h" #include "../../json.h" #include "strpool.h" #include #include /* JSON parser */ #include typedef struct AkBufferView { AkBuffer *buffer; const char *name; size_t byteOffset; size_t byteLength; size_t byteStride; } AkBufferView; typedef struct AkGLTFMeshoptLib AkGLTFMeshoptLib; typedef struct AkGLTFDracoLib AkGLTFDracoLib; typedef struct AkGLTFSPZLib AkGLTFSPZLib; typedef struct AkGLTFKTX2Lib AkGLTFKTX2Lib; typedef struct AkGLTFState { AkHeap *heap; AkDoc *doc; json_t *root; void *tmpParent; FListItem *buffers; RBTree *bufferMap; FListItem *bufferViews; RBTree *skinBound; RBTree *meshTargets; void *bindata; void *defaultMaterial; AkGLTFMeshoptLib *meshopt; AkGLTFDracoLib *draco; AkGLTFSPZLib *spz; /* Gaussian splat (SPZ) decoder, optional */ AkGLTFKTX2Lib *ktx2; /* KTX2/BasisU decoder, optional */ size_t bindataLen; bool stop; bool isbinary; bool animPointerRequired; bool borrowBufferViews; } AkGLTFState; #define GETCHILD(INITIAL, ITEM, INDEX) \ do { \ int i; \ if (INITIAL && (i = INDEX) >= 0) { \ ITEM = (void *)INITIAL; \ while (i > 0) { \ if (!(ITEM = (void *)ITEM->base.next)) { \ i = -1; \ ITEM = NULL; \ break; /* not foud */ \ } \ i--; \ } \ } else { \ ITEM = NULL; \ } \ } while (0) #endif /* gltf_commoh_h */ ================================================ FILE: src/io/gltf/imp/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) add_subdirectory(core) add_subdirectory(ext) ================================================ FILE: src/io/gltf/imp/common.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_imp_commoh_h #define gltf_imp_commoh_h #include "../common.h" #endif /* gltf_imp_commoh_h */ ================================================ FILE: src/io/gltf/imp/core/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/io/gltf/imp/core/accessor.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "accessor.h" #include "enum.h" #include "buffer.h" #include "../../../../accessor.h" #define k_gltf_bufferView 0 #define k_gltf_byteOffset 1 #define k_gltf_componentType 2 #define k_gltf_normalized 3 #define k_gltf_count 4 #define k_gltf_type 5 #define k_gltf_max 6 #define k_gltf_min 7 #define k_gltf_sparse 8 #define k_gltf_name 9 AK_HIDE void gltf_accessors(json_t * __restrict json, void * __restrict userdata) { AkGLTFState *gst; AkDoc *doc; AkHeap *heap; const json_array_t *jaccessors, *jarr; const json_t *jitem, *it; AkAccessor *acc; int componentLen, count, bound; if (!(jaccessors = json_array(json))) return; gst = userdata; doc = gst->doc; heap = gst->heap; json = jaccessors->base.value; componentLen = 1; while (json) { acc = ak_heap_calloc(heap, doc, sizeof(*acc)); ak_setypeid(acc, AKT_ACCESSOR); json_objmap_t accMap[] = { JSON_OBJMAP_OBJ(_s_gltf_bufferView, I2P k_gltf_bufferView), JSON_OBJMAP_OBJ(_s_gltf_byteOffset, I2P k_gltf_byteOffset), JSON_OBJMAP_OBJ(_s_gltf_componentType, I2P k_gltf_componentType), JSON_OBJMAP_OBJ(_s_gltf_normalized, I2P k_gltf_normalized), JSON_OBJMAP_OBJ(_s_gltf_count, I2P k_gltf_count), JSON_OBJMAP_OBJ(_s_gltf_type, I2P k_gltf_type), JSON_OBJMAP_OBJ(_s_gltf_max, I2P k_gltf_max), JSON_OBJMAP_OBJ(_s_gltf_min, I2P k_gltf_min), JSON_OBJMAP_OBJ(_s_gltf_sparse, I2P k_gltf_sparse), JSON_OBJMAP_OBJ(_s_gltf_name, I2P k_gltf_name) }; json_objmap(json, accMap, JSON_ARR_LEN(accMap)); if ((it = accMap[k_gltf_name].object)) { acc->name = json_strdup(it, heap, acc); } /* merge bufferView with acessor and buffer */ if ((it = accMap[k_gltf_bufferView].object)) { AkBuffer *buff, *tmpbuff; AkBufferView *buffView; int32_t buffViewIndex; if ((buffViewIndex = json_int32(it, -1)) > -1 && (buffView = flist_sp_at(&gst->bufferViews, buffViewIndex)) && (tmpbuff = buffView->buffer) /* tmpbuff->data is NULL when the source buffer is supplied via an unsupported extension (EXT_meshopt_compression, KHR_draco_mesh_compression, ...). Skip — accessor will be buffer-less rather than reading from a NULL pointer. */ && tmpbuff->data) { if (!(buff = rb_find(gst->bufferMap, buffView))) { buff = ak_heap_calloc(heap, doc, sizeof(*buff)); buff->data = ak_heap_alloc(heap, buff, buffView->byteLength); buff->length = buffView->byteLength; memcpy(buff->data, (char *)tmpbuff->data + buffView->byteOffset, buffView->byteLength); rb_insert(gst->bufferMap, buffView, buff); } acc->byteStride = buffView->byteStride; acc->buffer = buff; flist_sp_insert(&doc->lib.buffers, buff); } } if ((it = accMap[k_gltf_byteOffset].object)) { acc->byteOffset = json_uint64(it, 0); } if ((it = accMap[k_gltf_componentType].object)) { int componentType; componentType = json_int32(it, -1); acc->componentType = gltf_componentType(componentType); componentLen = gltf_componentLen(componentType); } if ((it = accMap[k_gltf_normalized].object)) { acc->normalized = json_bool(it, false); } if ((it = accMap[k_gltf_count].object)) { acc->count = json_uint32(it, 0); } if ((it = accMap[k_gltf_type].object)) { acc->componentSize = gltf_type(it); } /* prepare for min and max */ if (acc->componentSize < 5) { bound = acc->componentSize; acc->bytesPerComponent = componentLen; acc->componentCount = bound; } else { bound = acc->componentSize >> 3; acc->bytesPerComponent = componentLen; acc->componentCount = bound; } acc->byteLength = acc->bytesPerComponent * bound * acc->count; acc->fillByteSize = acc->bytesPerComponent * bound; /* glTF spec 3.6.2.4: bufferView.byteStride undefined → tightly packed. Normalize once, up front, so all downstream code (sparse densify, dequantize, consumer reads) can use acc->byteStride uniformly without a special case for "0 means packed". */ if (acc->byteStride == 0) { acc->byteStride = acc->fillByteSize; } /* Snapshot the source-side encoding before any in-place dequantize below mutates componentType/normalized. These fields stay valid for the lifetime of the accessor so callers can reason about how the buffer was originally laid out, even after a widen-to-float pass. With AK_OPT_PRESERVE_QUANTIZED_ATTRS the buffer is left as integers and these mirror componentType/normalized. */ acc->originalComponentType = acc->componentType; acc->originallyNormalized = acc->normalized; if (acc->componentSize != AK_COMPONENT_SIZE_UNKNOWN && acc->fillByteSize > 0) { if ((it = accMap[k_gltf_min].object) && it->value) { acc->min = ak_heap_alloc(heap, acc, acc->fillByteSize); if ((jarr = json_array(it))) { jitem = jarr->base.value; count = jarr->count; while (jitem) { json_array_set(acc->min, acc->componentType, --count, it); jitem = jitem->next; } } else { json_array_set(acc->min, acc->componentType, 0, it); } } if ((it = accMap[k_gltf_max].object) && it->value) { acc->max = ak_heap_alloc(heap, acc, acc->fillByteSize); if ((jarr = json_array(it))) { jitem = jarr->base.value; count = jarr->count; while (jitem) { json_array_set(acc->max, acc->componentType, --count, it); jitem = jitem->next; } } else { json_array_set(acc->max, acc->componentType, 0, it); } } } /* Sparse accessor — densify here so consumers always see a flat, tightly-packed buffer. glTF spec 3.6.2.3: when accessor.bufferView is undefined the dense buffer is initialized to zeros; otherwise it starts as a copy of the referenced bufferView slice. Then sparse.values overwrite the entries at sparse.indices. */ if ((it = accMap[k_gltf_sparse].object)) { json_t *jsCount, *jsIndices, *jsValues, *node; AkBufferView *idxBV, *valBV; AkBuffer *denseBuff; char *denseData, *idxPtr, *valPtr; size_t totalSize, idxByteOffset, valByteOffset; uint32_t sparseCount, sparseIdx, i; int32_t idxBVIdx, valBVIdx; AkTypeId idxComponentType; jsCount = json_get(it, _s_gltf_count); jsIndices = json_get(it, _s_gltf_indices); jsValues = json_get(it, _s_gltf_values); if (jsCount && jsIndices && jsValues && (sparseCount = json_uint32(jsCount, 0)) > 0 && acc->fillByteSize > 0 && acc->count > 0) { /* indices descriptor */ idxBVIdx = (node = json_get(jsIndices, _s_gltf_bufferView)) ? json_int32(node, -1) : -1; idxByteOffset = (node = json_get(jsIndices, _s_gltf_byteOffset)) ? json_uint64(node, 0) : 0; idxComponentType = (node = json_get(jsIndices, _s_gltf_componentType)) ? gltf_componentType(json_int32(node, -1)) : AKT_USHORT; /* values descriptor */ valBVIdx = (node = json_get(jsValues, _s_gltf_bufferView)) ? json_int32(node, -1) : -1; valByteOffset = (node = json_get(jsValues, _s_gltf_byteOffset)) ? json_uint64(node, 0) : 0; idxBV = (idxBVIdx >= 0) ? flist_sp_at(&gst->bufferViews, idxBVIdx) : NULL; valBV = (valBVIdx >= 0) ? flist_sp_at(&gst->bufferViews, valBVIdx) : NULL; if (idxBV && valBV && idxBV->buffer && valBV->buffer) { totalSize = (size_t)acc->fillByteSize * acc->count; denseBuff = ak_heap_calloc(heap, doc, sizeof(*denseBuff)); denseBuff->data = ak_heap_alloc(heap, denseBuff, totalSize); denseBuff->length = totalSize; denseData = denseBuff->data; /* initialize from main bufferView slice (if any) or zeros. Source may be interleaved (byteStride > fillByteSize), so copy element by element when needed instead of one big memcpy. */ if (acc->buffer && acc->buffer->data) { if (acc->byteStride == 0 || acc->byteStride == acc->fillByteSize) { memcpy(denseData, (char *)acc->buffer->data + acc->byteOffset, totalSize); } else { uint32_t v; char *baseSrc = (char *)acc->buffer->data + acc->byteOffset; for (v = 0; v < acc->count; v++) { memcpy(denseData + (size_t)v * acc->fillByteSize, baseSrc + (size_t)v * acc->byteStride, acc->fillByteSize); } } } else { memset(denseData, 0, totalSize); } /* overlay sparse values at the given indices */ idxPtr = (char *)idxBV->buffer->data + idxBV->byteOffset + idxByteOffset; valPtr = (char *)valBV->buffer->data + valBV->byteOffset + valByteOffset; for (i = 0; i < sparseCount; i++) { switch (idxComponentType) { case AKT_UBYTE: sparseIdx = ((uint8_t *)idxPtr)[i]; break; case AKT_USHORT: sparseIdx = ((uint16_t *)idxPtr)[i]; break; case AKT_UINT: sparseIdx = ((uint32_t *)idxPtr)[i]; break; default: continue; } if (sparseIdx >= acc->count) continue; memcpy(denseData + (size_t)sparseIdx * acc->fillByteSize, valPtr + (size_t)i * acc->fillByteSize, acc->fillByteSize); } /* swap: accessor now points at the dense buffer, tightly packed */ acc->buffer = denseBuff; acc->byteOffset = 0; acc->byteStride = acc->fillByteSize; flist_sp_insert(&doc->lib.buffers, denseBuff); } } } /* Dequantize normalized integer attributes to packed float buffer. Triggered when accessor.normalized == true on byte/short data — the common case for COLOR_n (UBYTE), TEXCOORD_n (UBYTE/USHORT), and quantized POSITION/NORMAL/TANGENT under KHR_mesh_quantization. Non-normalized integers (e.g. JOINTS_n) are left intact so vertex shaders can index by them. After conversion the accessor owns a tightly-packed float buffer. glTF spec 3.6.1.1: integer-to-float scale factors. Opt-out: AK_OPT_PRESERVE_QUANTIZED_ATTRS keeps the integer buffer intact for renderers that decode on the GPU. originalComponentType and originallyNormalized still describe the source encoding, and ak_accessorAsFloat / ak_accessorMakeFloat let consumers dequantize on demand. */ if (acc->normalized && acc->buffer && acc->buffer->data && acc->componentType != AKT_FLOAT && acc->fillByteSize > 0 && acc->count > 0 && !ak_opt_get(AK_OPT_PRESERVE_QUANTIZED_ATTRS)) { AkBuffer *fbuf; char *src; float *dst; size_t floatBufSize, stride, perComp; uint32_t v, c, comps; comps = acc->componentCount; stride = acc->byteStride; /* normalized up front */ perComp = acc->bytesPerComponent; floatBufSize = (size_t)comps * acc->count * sizeof(float); fbuf = ak_heap_calloc(heap, doc, sizeof(*fbuf)); fbuf->data = ak_heap_alloc(heap, fbuf, floatBufSize); fbuf->length = floatBufSize; dst = fbuf->data; src = (char *)acc->buffer->data + acc->byteOffset; for (v = 0; v < acc->count; v++) { char *vsrc = src + (size_t)v * stride; for (c = 0; c < comps; c++) { void *cp = vsrc + (size_t)c * perComp; float f; switch (acc->componentType) { case AKT_BYTE: f = (float)(*(int8_t *)cp) / 127.0f; if (f < -1.0f) f = -1.0f; break; case AKT_UBYTE: f = (float)(*(uint8_t *)cp) / 255.0f; break; case AKT_SHORT: f = (float)(*(int16_t *)cp) / 32767.0f; if (f < -1.0f) f = -1.0f; break; case AKT_USHORT: f = (float)(*(uint16_t*)cp) / 65535.0f; break; default: f = 0.0f; break; } dst[(size_t)v * comps + c] = f; } } acc->buffer = fbuf; acc->byteOffset = 0; acc->bytesPerComponent = sizeof(float); acc->fillByteSize = (size_t)comps * sizeof(float); acc->byteStride = acc->fillByteSize; acc->componentType = AKT_FLOAT; acc->normalized = false; flist_sp_insert(&doc->lib.buffers, fbuf); } /* KHR_mesh_quantization: non-normalized integer POSITION (vec3) or TEXCOORD (vec2). Spec allows raw integer values that map linearly to model space via a node-level transform — but most engines expect float vertex attributes, so we widen the integers to float in place. The asset's node transform already encodes the correct scale/offset from quantized space, so the result reaches the GPU at the intended position. Heuristic: only widen vec2/vec3 attributes — JOINTS_n is non-normalized integer too but is vec4 (and must stay int for skinning), so it's skipped. TANGENT is vec4 in the quantization spec but is required to be normalized, so it goes through the previous branch instead. Opt-out: AK_OPT_PRESERVE_QUANTIZED_ATTRS keeps the integer buffer intact (see notes on the previous block). */ if (!acc->normalized && acc->buffer && acc->buffer->data && acc->componentType != AKT_FLOAT && acc->componentType != AKT_UINT && (acc->componentCount == 2 || acc->componentCount == 3) && acc->fillByteSize > 0 && acc->count > 0 && !ak_opt_get(AK_OPT_PRESERVE_QUANTIZED_ATTRS)) { AkBuffer *fbuf; char *src; float *dst; size_t floatBufSize, stride, perComp; uint32_t v, c, comps; comps = acc->componentCount; stride = acc->byteStride; /* normalized up front */ perComp = acc->bytesPerComponent; floatBufSize = (size_t)comps * acc->count * sizeof(float); fbuf = ak_heap_calloc(heap, doc, sizeof(*fbuf)); fbuf->data = ak_heap_alloc(heap, fbuf, floatBufSize); fbuf->length = floatBufSize; dst = fbuf->data; src = (char *)acc->buffer->data + acc->byteOffset; for (v = 0; v < acc->count; v++) { char *vsrc = src + (size_t)v * stride; for (c = 0; c < comps; c++) { void *cp = vsrc + (size_t)c * perComp; float f; switch (acc->componentType) { case AKT_BYTE: f = (float)(*(int8_t *)cp); break; case AKT_UBYTE: f = (float)(*(uint8_t *)cp); break; case AKT_SHORT: f = (float)(*(int16_t *)cp); break; case AKT_USHORT: f = (float)(*(uint16_t*)cp); break; default: f = 0.0f; break; } dst[(size_t)v * comps + c] = f; } } acc->buffer = fbuf; acc->byteOffset = 0; acc->bytesPerComponent = sizeof(float); acc->fillByteSize = (size_t)comps * sizeof(float); acc->byteStride = acc->fillByteSize; acc->componentType = AKT_FLOAT; flist_sp_insert(&doc->lib.buffers, fbuf); } /* (byteStride normalization done up front, before sparse + dequantize) */ flist_sp_insert(&gst->doc->lib.accessors, acc); json = json->next; } } ================================================ FILE: src/io/gltf/imp/core/accessor.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_imp_core_accesor_h #define gltf_imp_core_accesor_h #include "../common.h" AK_HIDE void gltf_accessors(json_t * __restrict jbuffView, void * __restrict userdata); #endif /* gltf_imp_core_accesor_h */ ================================================ FILE: src/io/gltf/imp/core/anim.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "anim.h" #include "accessor.h" #include "enum.h" #define k_path 0 #define k_node 1 #define k_ext 2 #define k_anim_samplers 0 #define k_anim_channels 1 #define k_anim_name 2 static bool gltf_animSegEq(const char * __restrict seg, size_t segLen, const char * __restrict name) { return strlen(name) == segLen && strncasecmp(seg, name, segLen) == 0; } static bool gltf_animPtrSeg(const char ** __restrict p, const char * __restrict end, const char * __restrict name) { size_t len; if (*p >= end || **p != '/') return false; (*p)++; len = strlen(name); if ((size_t)(end - *p) < len || strncmp(*p, name, len) != 0) return false; *p += len; return true; } static bool gltf_animPtrIndex(const char ** __restrict p, const char * __restrict end, uint32_t * __restrict index) { uint32_t val; bool found; val = 0; found = false; while (*p < end && **p >= '0' && **p <= '9') { found = true; val = val * 10 + (uint32_t)(**p - '0'); (*p)++; } *index = val; return found; } static bool gltf_animPtrReadSeg(const char ** __restrict p, const char * __restrict end, const char ** __restrict seg, size_t * __restrict segLen) { if (*p >= end || **p != '/') return false; (*p)++; *seg = *p; while (*p < end && **p != '/') (*p)++; *segLen = (size_t)(*p - *seg); return *segLen > 0; } static void gltf_animSetTarget(AkGLTFState * __restrict gst, AkChannel * __restrict ch, void * __restrict target, uint32_t off, bool isPartial, AkTargetPropertyType targetType) { AkResolvedTarget *rt; rt = ak_heap_calloc(gst->heap, ch, sizeof(*rt)); ak_setypeid(rt, AKT_RESOLVED_TARGET); rt->target = target; rt->off = off; rt->isPartial = isPartial; ch->targetType = targetType; ch->resolvedTarget = rt; } static bool gltf_animSetFloatArrayTarget(AkGLTFState * __restrict gst, AkChannel * __restrict ch, float * __restrict target, uint32_t len, uint32_t index, bool hasIndex, AkTargetPropertyType targetType) { if (!target) return false; if (hasIndex && index >= len) return false; gltf_animSetTarget(gst, ch, target, index, hasIndex, targetType); return true; } static bool gltf_animSetFloatTarget(AkGLTFState * __restrict gst, AkChannel * __restrict ch, float * __restrict target) { if (!target) return false; gltf_animSetTarget(gst, ch, target, 0, false, AK_TARGET_FLOAT); return true; } static AkTextureTransform* gltf_animEnsureTexTransform(AkGLTFState * __restrict gst, AkTextureRef * __restrict texref) { AkTextureTransform *texTransf; if (!texref) return NULL; if (!(texTransf = texref->transform)) { texTransf = ak_heap_calloc(gst->heap, texref, sizeof(*texTransf)); texTransf->slot = -1; texTransf->scale[0] = 1.0f; texTransf->scale[1] = 1.0f; texref->transform = texTransf; } return texTransf; } static AkTextureRef* gltf_animEnsureTexRef(AkGLTFState * __restrict gst, AkTextureRef ** __restrict texref, void * __restrict parent, AkTextureColorSpace colorSpace, AkTextureChannels channels) { if (!*texref) { *texref = ak_heap_calloc(gst->heap, parent, sizeof(**texref)); (*texref)->slot = -1; } ak_texref_usage(*texref, colorSpace, channels); return *texref; } static bool gltf_animResolveTexTransform(AkGLTFState * __restrict gst, AkChannel * __restrict ch, AkTextureRef * __restrict texref, const char *p, const char *end) { AkTextureTransform *texTransf; const char *seg; size_t segLen; uint32_t idx; bool hasIdx; if (!gltf_animPtrSeg(&p, end, _s_gltf_extensions) || !gltf_animPtrSeg(&p, end, _s_gltf_KHR_texture_transform) || !gltf_animPtrReadSeg(&p, end, &seg, &segLen)) return false; hasIdx = false; idx = 0; if (p < end) { p++; if (!gltf_animPtrIndex(&p, end, &idx) || p != end) return false; hasIdx = true; } if (!(texTransf = gltf_animEnsureTexTransform(gst, texref))) return false; if (gltf_animSegEq(seg, segLen, _s_gltf_offset)) return gltf_animSetFloatArrayTarget(gst, ch, texTransf->offset, 2, idx, hasIdx, AK_TARGET_VEC2); if (gltf_animSegEq(seg, segLen, _s_gltf_rotation)) return gltf_animSetFloatTarget(gst, ch, &texTransf->rotation); if (gltf_animSegEq(seg, segLen, _s_gltf_scale)) return gltf_animSetFloatArrayTarget(gst, ch, texTransf->scale, 2, idx, hasIdx, AK_TARGET_VEC2); return false; } static bool gltf_animResolveNodePointer(AkGLTFState * __restrict gst, AkChannel * __restrict ch, const char * __restrict ptr, size_t ptrLen) { const char *p; const char *end; const char *prop; AkNode *node; AkObject *xform; AkInstanceGeometry *instGeom; AkInstanceMorph *morpher; size_t propLen; uint32_t nodeIndex; uint32_t itemIndex; bool hasItemIndex; char nodeid[16]; if (!ptr || ptrLen == 0) return false; p = ptr; end = ptr + ptrLen; if (!gltf_animPtrSeg(&p, end, _s_gltf_nodes) || p >= end || *p != '/') return false; p++; if (!gltf_animPtrIndex(&p, end, &nodeIndex)) return false; if (p >= end || *p != '/') return false; p++; prop = p; while (p < end && *p != '/') p++; propLen = (size_t)(p - prop); hasItemIndex = false; itemIndex = 0; sprintf(nodeid, "%s%d", _s_gltf_node, nodeIndex); if (!(node = ak_getObjectById(gst->doc, nodeid))) return false; if (gltf_animSegEq(prop, propLen, _s_gltf_extensions)) { if (!gltf_animPtrSeg(&p, end, _s_gltf_KHR_node_visibility) || !gltf_animPtrSeg(&p, end, _s_gltf_visible) || p != end) return false; gltf_animSetTarget(gst, ch, &node->visible, 0, false, AK_TARGET_BOOL); return true; } if (p < end) { p++; if (!gltf_animPtrIndex(&p, end, &itemIndex) || p != end) return false; hasItemIndex = true; } xform = NULL; if (gltf_animSegEq(prop, propLen, _s_gltf_rotation)) { if (hasItemIndex && itemIndex > 3) return false; xform = ak_getTransformTRS(node, AKT_QUATERNION); if (xform) gltf_animSetTarget(gst, ch, xform, itemIndex, hasItemIndex, AK_TARGET_QUAT); return xform != NULL; } if (gltf_animSegEq(prop, propLen, _s_gltf_translation)) { if (hasItemIndex && itemIndex > 2) return false; xform = ak_getTransformTRS(node, AKT_TRANSLATE); if (xform) gltf_animSetTarget(gst, ch, xform, itemIndex, hasItemIndex, AK_TARGET_POSITION); return xform != NULL; } if (gltf_animSegEq(prop, propLen, _s_gltf_scale)) { if (hasItemIndex && itemIndex > 2) return false; xform = ak_getTransformTRS(node, AKT_SCALE); if (xform) gltf_animSetTarget(gst, ch, xform, itemIndex, hasItemIndex, AK_TARGET_SCALE); return xform != NULL; } if (gltf_animSegEq(prop, propLen, _s_gltf_weights)) { if (!(instGeom = node->geometry) || !(morpher = instGeom->morpher)) return false; if (hasItemIndex && morpher->morph && itemIndex >= morpher->morph->targetCount) return false; gltf_animSetTarget(gst, ch, morpher, itemIndex, hasItemIndex, AK_TARGET_WEIGHTS); return true; } return false; } static AkTechniqueFxCommon* gltf_animMaterialCommon(AkMaterial * __restrict mat) { AkEffect *effect; if (!mat || !mat->effect) return NULL; effect = mat->effect->base.url.ptr; if (!effect) return NULL; return ak_getProfileTechniqueCommon(effect); } static void gltf_animSetColorDefault(AkColor * __restrict color, float r, float g, float b, float a) { color->vec[0] = r; color->vec[1] = g; color->vec[2] = b; color->vec[3] = a; } static AkColor* gltf_animEnsureColor(AkGLTFState * __restrict gst, AkColorDesc * __restrict desc, void * __restrict parent, float r, float g, float b, float a) { AkColor *color; if (!desc) return NULL; if (!(color = desc->color)) { color = ak_heap_calloc(gst->heap, parent, sizeof(*color)); gltf_animSetColorDefault(color, r, g, b, a); desc->color = color; } return color; } static AkMaterialMetallicProp* gltf_animEnsureMetalProp(AkGLTFState * __restrict gst, AkTechniqueFxCommon * __restrict cmn, AkMaterialMetallicProp ** __restrict prop) { if (!*prop) { *prop = ak_heap_calloc(gst->heap, cmn, sizeof(**prop)); (*prop)->intensity = 1.0f; } return *prop; } static AkMaterialSheen* gltf_animEnsureSheen(AkGLTFState * __restrict gst, AkTechniqueFxCommon * __restrict cmn) { AkMaterialSheen *sheen; if (!(sheen = cmn->sheen)) { sheen = ak_heap_calloc(gst->heap, cmn, sizeof(*sheen)); sheen->color = ak_heap_calloc(gst->heap, sheen, sizeof(*sheen->color)); gltf_animEnsureColor(gst, sheen->color, sheen->color, 0.0f, 0.0f, 0.0f, 1.0f); cmn->sheen = sheen; } return sheen; } static AkMaterialIridescence* gltf_animEnsureIridescence(AkGLTFState * __restrict gst, AkTechniqueFxCommon * __restrict cmn) { AkMaterialIridescence *iri; if (!(iri = cmn->iridescence)) { iri = ak_heap_calloc(gst->heap, cmn, sizeof(*iri)); iri->ior = 1.3f; iri->thicknessMinimum = 100.0f; iri->thicknessMaximum = 400.0f; cmn->iridescence = iri; } return iri; } static AkMaterialVolume* gltf_animEnsureVolume(AkGLTFState * __restrict gst, AkTechniqueFxCommon * __restrict cmn) { AkMaterialVolume *vol; if (!(vol = cmn->volume)) { vol = ak_heap_calloc(gst->heap, cmn, sizeof(*vol)); vol->attenuationColor.vec[0] = 1.0f; vol->attenuationColor.vec[1] = 1.0f; vol->attenuationColor.vec[2] = 1.0f; vol->attenuationColor.vec[3] = 1.0f; vol->attenuationDistance = INFINITY; cmn->volume = vol; } return vol; } static AkMaterialAnisotropy* gltf_animEnsureAnisotropy(AkGLTFState * __restrict gst, AkTechniqueFxCommon * __restrict cmn) { AkMaterialAnisotropy *aniso; if (!(aniso = cmn->anisotropy)) { aniso = ak_heap_calloc(gst->heap, cmn, sizeof(*aniso)); cmn->anisotropy = aniso; } return aniso; } static AkMaterialDispersion* gltf_animEnsureDispersion(AkGLTFState * __restrict gst, AkTechniqueFxCommon * __restrict cmn) { AkMaterialDispersion *disp; if (!(disp = cmn->dispersion)) { disp = ak_heap_calloc(gst->heap, cmn, sizeof(*disp)); cmn->dispersion = disp; } return disp; } static AkMaterialDiffuseTransmission* gltf_animEnsureDiffuseTransmission(AkGLTFState * __restrict gst, AkTechniqueFxCommon * __restrict cmn) { AkMaterialDiffuseTransmission *dt; if (!(dt = cmn->diffuseTransmission)) { dt = ak_heap_calloc(gst->heap, cmn, sizeof(*dt)); dt->color = ak_heap_calloc(gst->heap, dt, sizeof(*dt->color)); gltf_animEnsureColor(gst, dt->color, dt->color, 1.0f, 1.0f, 1.0f, 1.0f); cmn->diffuseTransmission = dt; } return dt; } static bool gltf_animResolveMaterialPBR(AkGLTFState * __restrict gst, AkChannel * __restrict ch, AkTechniqueFxCommon * __restrict cmn, const char *p, const char *end) { const char *seg; size_t segLen; uint32_t idx; bool hasIdx; AkColor *color; AkMaterialMetallicProp *prop, *rough; if (!gltf_animPtrReadSeg(&p, end, &seg, &segLen)) return false; if (gltf_animSegEq(seg, segLen, _s_gltf_baseColorTex)) { if (!cmn->albedo) cmn->albedo = ak_heap_calloc(gst->heap, cmn, sizeof(*cmn->albedo)); return gltf_animResolveTexTransform( gst, ch, gltf_animEnsureTexRef(gst, &cmn->albedo->texture, cmn->albedo, AK_TEXTURE_COLORSPACE_SRGB, AK_TEXTURE_CHANNEL_RGBA), p, end); } if (gltf_animSegEq(seg, segLen, _s_gltf_metalRoughTex)) { prop = gltf_animEnsureMetalProp(gst, cmn, &cmn->metalness); rough = gltf_animEnsureMetalProp(gst, cmn, &cmn->roughness); prop->textureChannels = AK_TEXTURE_CHANNEL_B; rough->textureChannels = AK_TEXTURE_CHANNEL_G; if (!prop->tex) prop->tex = gltf_animEnsureTexRef(gst, &prop->tex, prop, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_GB); if (!rough->tex) rough->tex = prop->tex; return gltf_animResolveTexTransform(gst, ch, prop->tex, p, end); } hasIdx = false; idx = 0; if (p < end) { p++; if (!gltf_animPtrIndex(&p, end, &idx) || p != end) return false; hasIdx = true; } if (gltf_animSegEq(seg, segLen, _s_gltf_baseColor)) { if (!cmn->albedo) cmn->albedo = ak_heap_calloc(gst->heap, cmn, sizeof(*cmn->albedo)); color = gltf_animEnsureColor(gst, cmn->albedo, cmn->albedo, 1.0f, 1.0f, 1.0f, 1.0f); return gltf_animSetFloatArrayTarget(gst, ch, color->vec, 4, idx, hasIdx, AK_TARGET_COLOR); } if (gltf_animSegEq(seg, segLen, _s_gltf_metalFac)) { prop = gltf_animEnsureMetalProp(gst, cmn, &cmn->metalness); return gltf_animSetFloatTarget(gst, ch, &prop->intensity); } if (gltf_animSegEq(seg, segLen, _s_gltf_roughFac)) { prop = gltf_animEnsureMetalProp(gst, cmn, &cmn->roughness); return gltf_animSetFloatTarget(gst, ch, &prop->intensity); } return false; } static bool gltf_animResolveMaterialExt(AkGLTFState * __restrict gst, AkChannel * __restrict ch, AkTechniqueFxCommon * __restrict cmn, const char *p, const char *end) { const char *ext; const char *seg; size_t extLen; size_t segLen; uint32_t idx; bool hasIdx; AkColor *color; AkMaterialSheen *sheen; AkMaterialIridescence *iri; AkMaterialVolume *vol; AkMaterialAnisotropy *aniso; AkMaterialDispersion *disp; AkMaterialDiffuseTransmission *dt; if (!gltf_animPtrReadSeg(&p, end, &ext, &extLen) || !gltf_animPtrReadSeg(&p, end, &seg, &segLen)) return false; if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_clearcoat)) { if (!cmn->clearcoat) { cmn->clearcoat = ak_heap_calloc(gst->heap, cmn, sizeof(*cmn->clearcoat)); cmn->clearcoat->normalScale = 1.0f; } if (gltf_animSegEq(seg, segLen, _s_gltf_clearcoatTexture)) cmn->clearcoat->textureChannels = AK_TEXTURE_CHANNEL_R; if (gltf_animSegEq(seg, segLen, _s_gltf_clearcoatTexture)) return gltf_animResolveTexTransform( gst, ch, gltf_animEnsureTexRef(gst, &cmn->clearcoat->texture, cmn->clearcoat, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_R), p, end); if (gltf_animSegEq(seg, segLen, _s_gltf_clearcoatRoughnessTexture)) cmn->clearcoat->roughnessTextureChannels = AK_TEXTURE_CHANNEL_G; if (gltf_animSegEq(seg, segLen, _s_gltf_clearcoatRoughnessTexture)) return gltf_animResolveTexTransform( gst, ch, gltf_animEnsureTexRef(gst, &cmn->clearcoat->roughnessTexture, cmn->clearcoat, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_G), p, end); if (gltf_animSegEq(seg, segLen, _s_gltf_clearcoatNormalTexture)) return gltf_animResolveTexTransform( gst, ch, gltf_animEnsureTexRef(gst, &cmn->clearcoat->normalTexture, cmn->clearcoat, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_RGB), p, end) || (gltf_animPtrSeg(&p, end, _s_gltf_scale) && p == end && gltf_animSetFloatTarget(gst, ch, &cmn->clearcoat->normalScale)); } if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_specular) && cmn->specular) { if (gltf_animSegEq(seg, segLen, _s_gltf_specularTexture)) cmn->specular->textureChannels = AK_TEXTURE_CHANNEL_A; if (gltf_animSegEq(seg, segLen, _s_gltf_specularTexture)) return gltf_animResolveTexTransform( gst, ch, gltf_animEnsureTexRef(gst, &cmn->specular->specularTex, cmn->specular, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_A), p, end); if (gltf_animSegEq(seg, segLen, _s_gltf_specularColorTexture)) return cmn->specular->color && gltf_animResolveTexTransform( gst, ch, gltf_animEnsureTexRef(gst, &cmn->specular->color->texture, cmn->specular->color, AK_TEXTURE_COLORSPACE_SRGB, AK_TEXTURE_CHANNEL_RGB), p, end); } if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_transmission) && cmn->transmission && gltf_animSegEq(seg, segLen, _s_gltf_transmissionTexture)) cmn->transmission->textureChannels = AK_TEXTURE_CHANNEL_R; if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_transmission) && cmn->transmission && gltf_animSegEq(seg, segLen, _s_gltf_transmissionTexture)) return gltf_animResolveTexTransform( gst, ch, gltf_animEnsureTexRef(gst, &cmn->transmission->texture, cmn->transmission, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_R), p, end); if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_sheen)) { sheen = gltf_animEnsureSheen(gst, cmn); if (gltf_animSegEq(seg, segLen, _s_gltf_sheenColorTexture)) return gltf_animResolveTexTransform( gst, ch, gltf_animEnsureTexRef(gst, &sheen->color->texture, sheen->color, AK_TEXTURE_COLORSPACE_SRGB, AK_TEXTURE_CHANNEL_RGB), p, end); if (gltf_animSegEq(seg, segLen, _s_gltf_sheenRoughnessTexture)) sheen->roughnessTextureChannels = AK_TEXTURE_CHANNEL_A; if (gltf_animSegEq(seg, segLen, _s_gltf_sheenRoughnessTexture)) return gltf_animResolveTexTransform( gst, ch, gltf_animEnsureTexRef(gst, &sheen->roughnessTexture, sheen, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_A), p, end); } if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_iridescence)) { iri = gltf_animEnsureIridescence(gst, cmn); if (gltf_animSegEq(seg, segLen, _s_gltf_iridescenceTexture)) iri->textureChannels = AK_TEXTURE_CHANNEL_R; if (gltf_animSegEq(seg, segLen, _s_gltf_iridescenceTexture)) return gltf_animResolveTexTransform( gst, ch, gltf_animEnsureTexRef(gst, &iri->texture, iri, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_R), p, end); if (gltf_animSegEq(seg, segLen, _s_gltf_iridescenceThicknessTexture)) iri->thicknessTextureChannels = AK_TEXTURE_CHANNEL_G; if (gltf_animSegEq(seg, segLen, _s_gltf_iridescenceThicknessTexture)) return gltf_animResolveTexTransform( gst, ch, gltf_animEnsureTexRef(gst, &iri->thicknessTexture, iri, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_G), p, end); } if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_volume)) { vol = gltf_animEnsureVolume(gst, cmn); if (gltf_animSegEq(seg, segLen, _s_gltf_thicknessTexture)) vol->thicknessTextureChannels = AK_TEXTURE_CHANNEL_G; if (gltf_animSegEq(seg, segLen, _s_gltf_thicknessTexture)) return gltf_animResolveTexTransform( gst, ch, gltf_animEnsureTexRef(gst, &vol->thicknessTexture, vol, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_G), p, end); } if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_anisotropy)) { aniso = gltf_animEnsureAnisotropy(gst, cmn); if (gltf_animSegEq(seg, segLen, _s_gltf_anisotropyTexture)) return gltf_animResolveTexTransform( gst, ch, gltf_animEnsureTexRef(gst, &aniso->texture, aniso, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_RGB), p, end); } if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_diffuse_transmission)) { dt = gltf_animEnsureDiffuseTransmission(gst, cmn); if (gltf_animSegEq(seg, segLen, _s_gltf_diffuseTransmissionTexture)) dt->textureChannels = AK_TEXTURE_CHANNEL_A; if (gltf_animSegEq(seg, segLen, _s_gltf_diffuseTransmissionTexture)) return gltf_animResolveTexTransform( gst, ch, gltf_animEnsureTexRef(gst, &dt->texture, dt, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_A), p, end); if (gltf_animSegEq(seg, segLen, _s_gltf_diffuseTransmissionColorTexture)) return gltf_animResolveTexTransform( gst, ch, gltf_animEnsureTexRef(gst, &dt->color->texture, dt->color, AK_TEXTURE_COLORSPACE_SRGB, AK_TEXTURE_CHANNEL_RGB), p, end); } hasIdx = false; idx = 0; if (p < end) { p++; if (!gltf_animPtrIndex(&p, end, &idx) || p != end) return false; hasIdx = true; } if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_emissive_strength) && gltf_animSegEq(seg, segLen, _s_gltf_emissiveStrength)) { if (!cmn->emission) { cmn->emission = ak_heap_calloc(gst->heap, cmn, sizeof(*cmn->emission)); cmn->emission->strength = 1.0f; } return gltf_animSetFloatTarget(gst, ch, &cmn->emission->strength); } if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_ior) && gltf_animSegEq(seg, segLen, _s_gltf_ior)) return gltf_animSetFloatTarget(gst, ch, &cmn->ior); if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_transmission) && gltf_animSegEq(seg, segLen, _s_gltf_transmissionFactor)) { if (!cmn->transmission) cmn->transmission = ak_heap_calloc(gst->heap, cmn, sizeof(*cmn->transmission)); return gltf_animSetFloatTarget(gst, ch, &cmn->transmission->factor); } if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_clearcoat)) { if (!cmn->clearcoat) { cmn->clearcoat = ak_heap_calloc(gst->heap, cmn, sizeof(*cmn->clearcoat)); cmn->clearcoat->normalScale = 1.0f; } if (gltf_animSegEq(seg, segLen, _s_gltf_clearcoatFactor)) return gltf_animSetFloatTarget(gst, ch, &cmn->clearcoat->intensity); if (gltf_animSegEq(seg, segLen, _s_gltf_clearcoatRoughnessFactor)) return gltf_animSetFloatTarget(gst, ch, &cmn->clearcoat->roughness); } if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_specular)) { if (!cmn->specular) cmn->specular = ak_heap_calloc(gst->heap, cmn, sizeof(*cmn->specular)); if (gltf_animSegEq(seg, segLen, _s_gltf_specularFactor)) return gltf_animSetFloatTarget(gst, ch, &cmn->specular->strength); if (gltf_animSegEq(seg, segLen, _s_gltf_specularColorFactor)) { if (!cmn->specular->color) cmn->specular->color = ak_heap_calloc(gst->heap, cmn->specular, sizeof(*cmn->specular->color)); color = gltf_animEnsureColor(gst, cmn->specular->color, cmn->specular->color, 1.0f, 1.0f, 1.0f, 1.0f); return gltf_animSetFloatArrayTarget(gst, ch, color->vec, 3, idx, hasIdx, AK_TARGET_COLOR); } } if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_sheen)) { sheen = gltf_animEnsureSheen(gst, cmn); if (gltf_animSegEq(seg, segLen, _s_gltf_sheenColorFactor)) { color = gltf_animEnsureColor(gst, sheen->color, sheen->color, 0.0f, 0.0f, 0.0f, 1.0f); return gltf_animSetFloatArrayTarget(gst, ch, color->vec, 3, idx, hasIdx, AK_TARGET_COLOR); } if (gltf_animSegEq(seg, segLen, _s_gltf_sheenRoughnessFactor)) return gltf_animSetFloatTarget(gst, ch, &sheen->roughness); } if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_iridescence)) { iri = gltf_animEnsureIridescence(gst, cmn); if (gltf_animSegEq(seg, segLen, _s_gltf_iridescenceFactor)) return gltf_animSetFloatTarget(gst, ch, &iri->factor); if (gltf_animSegEq(seg, segLen, _s_gltf_iridescenceIor)) return gltf_animSetFloatTarget(gst, ch, &iri->ior); if (gltf_animSegEq(seg, segLen, _s_gltf_iridescenceThicknessMinimum)) return gltf_animSetFloatTarget(gst, ch, &iri->thicknessMinimum); if (gltf_animSegEq(seg, segLen, _s_gltf_iridescenceThicknessMaximum)) return gltf_animSetFloatTarget(gst, ch, &iri->thicknessMaximum); } if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_volume)) { vol = gltf_animEnsureVolume(gst, cmn); if (gltf_animSegEq(seg, segLen, _s_gltf_thicknessFactor)) return gltf_animSetFloatTarget(gst, ch, &vol->thicknessFactor); if (gltf_animSegEq(seg, segLen, _s_gltf_attenuationDistance)) return gltf_animSetFloatTarget(gst, ch, &vol->attenuationDistance); if (gltf_animSegEq(seg, segLen, _s_gltf_attenuationColor)) return gltf_animSetFloatArrayTarget(gst, ch, vol->attenuationColor.vec, 3, idx, hasIdx, AK_TARGET_COLOR); } if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_anisotropy)) { aniso = gltf_animEnsureAnisotropy(gst, cmn); if (gltf_animSegEq(seg, segLen, _s_gltf_anisotropyStrength)) return gltf_animSetFloatTarget(gst, ch, &aniso->strength); if (gltf_animSegEq(seg, segLen, _s_gltf_anisotropyRotation)) return gltf_animSetFloatTarget(gst, ch, &aniso->rotation); } if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_dispersion)) { disp = gltf_animEnsureDispersion(gst, cmn); if (gltf_animSegEq(seg, segLen, _s_gltf_dispersion)) return gltf_animSetFloatTarget(gst, ch, &disp->dispersion); } if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_diffuse_transmission)) { dt = gltf_animEnsureDiffuseTransmission(gst, cmn); if (gltf_animSegEq(seg, segLen, _s_gltf_diffuseTransmissionFactor)) return gltf_animSetFloatTarget(gst, ch, &dt->factor); if (gltf_animSegEq(seg, segLen, _s_gltf_diffuseTransmissionColorFactor)) { color = gltf_animEnsureColor(gst, dt->color, dt->color, 1.0f, 1.0f, 1.0f, 1.0f); return gltf_animSetFloatArrayTarget(gst, ch, color->vec, 3, idx, hasIdx, AK_TARGET_COLOR); } } return false; } static bool gltf_animResolveMaterialPointer(AkGLTFState * __restrict gst, AkChannel * __restrict ch, const char * __restrict ptr, size_t ptrLen) { const char *p; const char *end; const char *seg; const char *q; AkMaterial *mat; AkTechniqueFxCommon *cmn; AkColor *color; size_t segLen; uint32_t matIndex; uint32_t idx; bool hasIdx; p = ptr; end = ptr + ptrLen; if (!gltf_animPtrSeg(&p, end, _s_gltf_materials) || p >= end || *p != '/') return false; p++; if (!gltf_animPtrIndex(&p, end, &matIndex) || p >= end || *p != '/') return false; GETCHILD(gst->doc->lib.materials->chld, mat, matIndex); if (!(cmn = gltf_animMaterialCommon(mat))) return false; if (!gltf_animPtrReadSeg(&p, end, &seg, &segLen)) return false; if (gltf_animSegEq(seg, segLen, _s_gltf_pbrMetalRough)) return gltf_animResolveMaterialPBR(gst, ch, cmn, p, end); if (gltf_animSegEq(seg, segLen, _s_gltf_extensions)) return gltf_animResolveMaterialExt(gst, ch, cmn, p, end); if (gltf_animSegEq(seg, segLen, _s_gltf_normalTex)) { if (!cmn->normal) { cmn->normal = ak_heap_calloc(gst->heap, cmn, sizeof(*cmn->normal)); cmn->normal->scale = 1.0f; } q = p; if (gltf_animPtrSeg(&q, end, _s_gltf_scale) && q == end) return gltf_animSetFloatTarget(gst, ch, &cmn->normal->scale); return gltf_animResolveTexTransform( gst, ch, gltf_animEnsureTexRef(gst, &cmn->normal->tex, cmn->normal, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_RGB), p, end); } if (gltf_animSegEq(seg, segLen, _s_gltf_occlusionTex)) { if (!cmn->occlusion) { cmn->occlusion = ak_heap_calloc(gst->heap, cmn, sizeof(*cmn->occlusion)); cmn->occlusion->strength = 1.0f; } q = p; if (gltf_animPtrSeg(&q, end, _s_gltf_strength) && q == end) return gltf_animSetFloatTarget(gst, ch, &cmn->occlusion->strength); cmn->occlusion->textureChannels = AK_TEXTURE_CHANNEL_R; return gltf_animResolveTexTransform( gst, ch, gltf_animEnsureTexRef(gst, &cmn->occlusion->tex, cmn->occlusion, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_R), p, end); } if (gltf_animSegEq(seg, segLen, _s_gltf_emissiveTex)) { if (!cmn->emission) { cmn->emission = ak_heap_calloc(gst->heap, cmn, sizeof(*cmn->emission)); cmn->emission->strength = 1.0f; } return gltf_animResolveTexTransform( gst, ch, gltf_animEnsureTexRef(gst, &cmn->emission->color.texture, cmn->emission, AK_TEXTURE_COLORSPACE_SRGB, AK_TEXTURE_CHANNEL_RGB), p, end); } hasIdx = false; idx = 0; if (p < end) { p++; if (!gltf_animPtrIndex(&p, end, &idx) || p != end) return false; hasIdx = true; } if (gltf_animSegEq(seg, segLen, _s_gltf_emissiveFac)) { if (!cmn->emission) cmn->emission = ak_heap_calloc(gst->heap, cmn, sizeof(*cmn->emission)); color = gltf_animEnsureColor(gst, &cmn->emission->color, cmn->emission, 0.0f, 0.0f, 0.0f, 1.0f); return gltf_animSetFloatArrayTarget(gst, ch, color->vec, 3, idx, hasIdx, AK_TARGET_COLOR); } if (gltf_animSegEq(seg, segLen, _s_gltf_alphaCutoff)) { if (!cmn->transparent) { cmn->transparent = ak_heap_calloc(gst->heap, cmn, sizeof(*cmn->transparent)); cmn->transparent->amount = 1.0f; cmn->transparent->cutoff = 0.5f; } return gltf_animSetFloatTarget(gst, ch, &cmn->transparent->cutoff); } return false; } static bool gltf_animResolveCameraPointer(AkGLTFState * __restrict gst, AkChannel * __restrict ch, const char * __restrict ptr, size_t ptrLen) { const char *p; const char *end; const char *seg; const char *prop; AkCamera *cam; AkProjection *proj; AkPerspective *persp; AkOrthographic *ortho; size_t segLen; size_t propLen; uint32_t camIndex; p = ptr; end = ptr + ptrLen; if (!gltf_animPtrSeg(&p, end, _s_gltf_cameras) || p >= end || *p != '/') return false; p++; if (!gltf_animPtrIndex(&p, end, &camIndex)) return false; GETCHILD(gst->doc->lib.cameras->chld, cam, camIndex); if (!cam || !cam->optics || !(proj = cam->optics->tcommon)) return false; if (!gltf_animPtrReadSeg(&p, end, &seg, &segLen) || !gltf_animPtrReadSeg(&p, end, &prop, &propLen) || p != end) return false; if (proj->type == AK_PROJECTION_PERSPECTIVE && gltf_animSegEq(seg, segLen, _s_gltf_perspective)) { persp = (AkPerspective *)proj; if (gltf_animSegEq(prop, propLen, _s_gltf_xfov)) return gltf_animSetFloatTarget(gst, ch, &persp->xfov); if (gltf_animSegEq(prop, propLen, _s_gltf_yfov)) return gltf_animSetFloatTarget(gst, ch, &persp->yfov); if (gltf_animSegEq(prop, propLen, _s_gltf_aspectRatio)) return gltf_animSetFloatTarget(gst, ch, &persp->aspectRatio); if (gltf_animSegEq(prop, propLen, _s_gltf_znear)) return gltf_animSetFloatTarget(gst, ch, &persp->znear); if (gltf_animSegEq(prop, propLen, _s_gltf_zfar)) return gltf_animSetFloatTarget(gst, ch, &persp->zfar); } if (proj->type == AK_PROJECTION_ORTHOGRAPHIC && gltf_animSegEq(seg, segLen, _s_gltf_orthographic)) { ortho = (AkOrthographic *)proj; if (gltf_animSegEq(prop, propLen, _s_gltf_xmag)) return gltf_animSetFloatTarget(gst, ch, &ortho->xmag); if (gltf_animSegEq(prop, propLen, _s_gltf_ymag)) return gltf_animSetFloatTarget(gst, ch, &ortho->ymag); if (gltf_animSegEq(prop, propLen, _s_gltf_znear)) return gltf_animSetFloatTarget(gst, ch, &ortho->znear); if (gltf_animSegEq(prop, propLen, _s_gltf_zfar)) return gltf_animSetFloatTarget(gst, ch, &ortho->zfar); } return false; } static bool gltf_animResolveLightPointer(AkGLTFState * __restrict gst, AkChannel * __restrict ch, const char * __restrict ptr, size_t ptrLen) { const char *p; const char *end; const char *seg; AkLight *light; AkLightBase *base; AkSpotLight *spot; size_t segLen; uint32_t lightIndex; uint32_t idx; bool hasIdx; p = ptr; end = ptr + ptrLen; if (!gltf_animPtrSeg(&p, end, _s_gltf_extensions) || !gltf_animPtrSeg(&p, end, _s_gltf_KHR_lights_punctual) || !gltf_animPtrSeg(&p, end, _s_gltf_lights) || p >= end || *p != '/') return false; p++; if (!gltf_animPtrIndex(&p, end, &lightIndex)) return false; light = (void *)gst->doc->lib.lights->chld; while (light && lightIndex > 0) { light = light->next; lightIndex--; } if (!light || !(base = light->tcommon)) return false; if (!gltf_animPtrReadSeg(&p, end, &seg, &segLen)) return false; if (base->type == AK_LIGHT_TYPE_SPOT && gltf_animSegEq(seg, segLen, _s_gltf_spot)) { spot = (AkSpotLight *)base; if (!gltf_animPtrReadSeg(&p, end, &seg, &segLen) || p != end) return false; if (gltf_animSegEq(seg, segLen, _s_gltf_innerConeAngle)) return gltf_animSetFloatTarget(gst, ch, &spot->innerConeAngle); if (gltf_animSegEq(seg, segLen, _s_gltf_outerConeAngle)) return gltf_animSetFloatTarget(gst, ch, &spot->outerConeAngle); return false; } hasIdx = false; idx = 0; if (p < end) { p++; if (!gltf_animPtrIndex(&p, end, &idx) || p != end) return false; hasIdx = true; } if (gltf_animSegEq(seg, segLen, _s_gltf_color)) return gltf_animSetFloatArrayTarget(gst, ch, base->color.vec, 3, idx, hasIdx, AK_TARGET_COLOR); if (gltf_animSegEq(seg, segLen, _s_gltf_intensity)) return gltf_animSetFloatTarget(gst, ch, &base->intensity); if (gltf_animSegEq(seg, segLen, _s_gltf_range)) return gltf_animSetFloatTarget(gst, ch, &base->range); return false; } static bool gltf_animResolvePointer(AkGLTFState * __restrict gst, AkChannel * __restrict ch, const json_t * __restrict jext) { const json_t *jptrExt; const json_t *jptr; const char *ptr; if (!(jptrExt = json_get(jext, _s_gltf_KHR_animation_pointer)) || !(jptr = json_get(jptrExt, _s_gltf_pointer)) || !(ptr = json_string(jptr))) return false; if (gltf_animResolveNodePointer(gst, ch, ptr, jptr->valsize)) return true; if (gltf_animResolveMaterialPointer(gst, ch, ptr, jptr->valsize)) return true; if (gltf_animResolveCameraPointer(gst, ch, ptr, jptr->valsize)) return true; if (gltf_animResolveLightPointer(gst, ch, ptr, jptr->valsize)) return true; return false; } AK_HIDE void gltf_animations(json_t * __restrict janim, void * __restrict userdata) { AkGLTFState *gst; AkHeap *heap; AkDoc *doc; const json_array_t *janims; AkLibrary *lib; AkAnimation *anim; if (!(janims = json_array(janim))) return; gst = userdata; heap = gst->heap; doc = gst->doc; janim = janims->base.value; lib = ak_heap_calloc(heap, doc, sizeof(*lib)); while (janim) { json_t *anim_it; json_objmap_t animMap[] = { JSON_OBJMAP_OBJ(_s_gltf_samplers, I2P k_anim_samplers), JSON_OBJMAP_OBJ(_s_gltf_channels, I2P k_anim_channels), JSON_OBJMAP_OBJ(_s_gltf_name, I2P k_anim_name), }; json_objmap(janim, animMap, JSON_ARR_LEN(animMap)); anim = ak_heap_calloc(heap, lib, sizeof(*anim)); if ((anim_it = animMap[k_anim_name].object)) { anim->name = json_strdup(anim_it, heap, anim); } if ((anim_it = animMap[k_anim_samplers].object)) { AkAnimSampler *sampler; json_array_t *jsamplers; json_t *jsampler; if (!(jsamplers = json_array(anim_it))) goto anm_nxt; jsampler = jsamplers->base.value; /* samplers */ while (jsampler) { json_t *jsampVal; jsampVal = jsampler->value; sampler = ak_heap_calloc(heap, anim, sizeof(*sampler)); while (jsampVal) { if (json_key_eq(jsampVal, _s_gltf_input)) { AkInput *inp; inp = ak_heap_calloc(heap, sampler, sizeof(*inp)); inp->semanticRaw = ak_heap_strdup(gst->heap, anim, _s_gltf_input); inp->semantic = AK_INPUT_INPUT; inp->accessor = flist_sp_at(&doc->lib.accessors, json_int32(jsampVal, -1)); ak_retain(inp->accessor); inp->next = sampler->input; sampler->input = inp; } else if (json_key_eq(jsampVal, _s_gltf_interpolation)) { sampler->uniInterpolation = gltf_interp(jsampVal); } else if (json_key_eq(jsampVal, _s_gltf_output)) { AkInput *inp; inp = ak_heap_calloc(heap, sampler, sizeof(*inp)); inp->semanticRaw = ak_heap_strdup(gst->heap, anim, _s_gltf_output); inp->semantic = AK_INPUT_OUTPUT; inp->accessor = flist_sp_at(&doc->lib.accessors, json_int32(jsampVal, -1)); ak_retain(inp->accessor); inp->next = sampler->input; sampler->input = inp; } /* Default is LINEAR */ if (sampler->uniInterpolation == AK_INTERPOLATION_UNKNOWN) { sampler->uniInterpolation = AK_INTERPOLATION_LINEAR; } jsampVal = jsampVal->next; } sampler->base.next = (void *)anim->sampler; anim->sampler = sampler; jsampler = jsampler->next; } } if ((anim_it = animMap[k_anim_channels].object)) { AkChannel *ch; json_array_t *jchannels; json_t *jchannel; if (!(jchannels = json_array(anim_it))) goto anm_nxt; jchannel = jchannels->base.value; while (jchannel) { json_t *jchVal; ch = ak_heap_calloc(heap, anim, sizeof(*ch)); jchVal = jchannel->value; while (jchVal) { if (json_key_eq(jchVal, _s_gltf_sampler)) { AkAnimSampler *sampler; int32_t samplerIndex; samplerIndex = json_int32(jchVal, -1); GETCHILD(anim->sampler, sampler, samplerIndex); ch->source.ptr = sampler; } else if (json_key_eq(jchVal, _s_gltf_target)) { const char *path; AkNode *node; json_t *it; uint32_t pathLen; json_objmap_t targetMap[] = { JSON_OBJMAP_OBJ(_s_gltf_path, I2P k_path), JSON_OBJMAP_OBJ(_s_gltf_node, I2P k_node), JSON_OBJMAP_OBJ(_s_gltf_extensions, I2P k_ext) }; json_objmap(jchVal, targetMap, JSON_ARR_LEN(targetMap)); path = NULL; pathLen = 0; if ((it = targetMap[k_path].object)) { path = json_string(it); pathLen = it->valsize; } if ((it = targetMap[k_ext].object) && json_get(it, _s_gltf_KHR_animation_pointer)) { if (!gltf_animResolvePointer(gst, ch, it)) gst->stop = gst->animPointerRequired; } else if (path && (it = targetMap[k_node].object)) { char nodeid[16]; int32_t nodeIndex; if ((nodeIndex = json_int32(it, -1)) > -1) { sprintf(nodeid, "%s%d", _s_gltf_node, nodeIndex); if ((node = ak_getObjectById(doc, nodeid))) { AkObject *xform = NULL; /* glTF always animates whole vec/quat (no partial component), so isPartial = false and off = 0 for all paths below. */ if (strncasecmp(path, _s_gltf_rotation, pathLen) == 0) { ch->targetType = AK_TARGET_QUAT; xform = ak_getTransformTRS(node, AKT_QUATERNION); } else if (strncasecmp(path, _s_gltf_translation, pathLen) == 0) { ch->targetType = AK_TARGET_POSITION; xform = ak_getTransformTRS(node, AKT_TRANSLATE); } else if (strncasecmp(path, _s_gltf_scale, pathLen) == 0) { ch->targetType = AK_TARGET_SCALE; xform = ak_getTransformTRS(node, AKT_SCALE); } else if (strncasecmp(path, _s_gltf_weights, pathLen) == 0) { AkInstanceGeometry *instGeom; AkInstanceMorph *morpher; ch->targetType = AK_TARGET_WEIGHTS; if ((instGeom = node->geometry) && (morpher = instGeom->morpher)) { AkResolvedTarget *rt; rt = ak_heap_calloc(heap, ch, sizeof(*rt)); ak_setypeid(rt, AKT_RESOLVED_TARGET); rt->target = morpher; rt->off = 0; rt->isPartial = false; ch->resolvedTarget = rt; } /* else: morpher not yet set, channel left un-resolved */ } /* common: wrap transform component in AkResolvedTarget */ if (xform) { AkResolvedTarget *rt; rt = ak_heap_calloc(heap, ch, sizeof(*rt)); ak_setypeid(rt, AKT_RESOLVED_TARGET); rt->target = xform; rt->off = 0; rt->isPartial = false; ch->resolvedTarget = rt; } } } /* if nodeIndex */ } /* if k_node */ } /* if _s_gltf_target */ jchVal = jchVal->next; } ch->next = anim->channel; anim->channel = ch; jchannel = jchannel->next; } } anm_nxt: anim->base.next = (void *)lib->chld; lib->chld = (void *)anim; lib->count++; janim = janim->next; } doc->lib.animations = lib; } ================================================ FILE: src/io/gltf/imp/core/anim.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_imp_core_anim_h #define gltf_imp_core_anim_h #include "../common.h" AK_HIDE void gltf_animations(json_t * __restrict janim, void * __restrict userdata); #endif /* gltf_imp_core_anim_h */ ================================================ FILE: src/io/gltf/imp/core/asset.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "asset.h" void gltf_asset(json_t * __restrict json, void * __restrict userdata) { AkGLTFState *gst; AkAssetInf *inf; AkDoc *doc; AkHeap *heap; AkContributor *contrib; gst = userdata; heap = gst->heap; doc = gst->doc; inf = &doc->inf->base; contrib = ak_heap_calloc(heap, inf, sizeof(*contrib)); inf->contributor = contrib; json = json->value; while (json) { if (json_key_eq(json, _s_gltf_version)) { if (json_float(json, 0.0f) < 2.0f) { gst->stop = true; return; /* unsupported version */ } } else if (json_key_eq(json, _s_gltf_copyright)) { contrib->copyright = json_strdup(json, heap, contrib); } else if (json_key_eq(json, _s_gltf_generator)) { contrib->authoringTool = json_strdup(json, heap, contrib); } json = json->next; } /* glTF default definitions */ /* CoordSys is Y_UP */ inf->coordSys = AK_YUP; /* Unit is meter */ inf->unit = ak_heap_calloc(heap, inf, sizeof(*inf->unit)); inf->unit->dist = 1.0; inf->unit->name = ak_heap_strdup(heap, inf->unit, _s_gltf_meter); *(AkAssetInf **)ak_heap_ext_add(heap, ak__alignof(doc), AK_HEAP_NODE_FLAGS_INF) = inf; doc->coordSys = inf->coordSys; doc->unit = inf->unit; } ================================================ FILE: src/io/gltf/imp/core/asset.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_imp_core_asset_h #define gltf_imp_core_asset_h #include "../common.h" AK_HIDE void gltf_asset(json_t * __restrict json, void * __restrict userdata); #endif /* gltf_imp_core_asset_h */ ================================================ FILE: src/io/gltf/imp/core/buffer.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "buffer.h" #include "ext.h" #include "../../../../utils.h" #include "../../../../base64.h" void gltf_bufferViews(json_t * __restrict jbuffView, void * __restrict userdata) { AkGLTFState *gst; const json_array_t *jbuffers; const json_t *jbuffVal; const json_t *jext; AkBufferView *buffView; int32_t buffIndex; if (!(jbuffers = json_array(jbuffView))) return; gst = userdata; jbuffView = jbuffers->base.value; while (jbuffView) { buffView = ak_heap_calloc(gst->heap, gst->tmpParent, sizeof(*buffView)); jbuffVal = jbuffView->value; jext = NULL; while (jbuffVal) { if (json_key_eq(jbuffVal, _s_gltf_buffer)) { if ((buffIndex = json_int32(jbuffVal, -1)) > -1) buffView->buffer = flist_sp_at(&gst->buffers, buffIndex); } else if (json_key_eq(jbuffVal, _s_gltf_byteLength)) { buffView->byteLength = (size_t)json_uint64(jbuffVal, 0); } else if (json_key_eq(jbuffVal, _s_gltf_byteOffset)) { buffView->byteOffset = (size_t)json_uint64(jbuffVal, 0); } else if (json_key_eq(jbuffVal, _s_gltf_byteStride)) { buffView->byteStride = (size_t)json_uint64(jbuffVal, 0); } else if (json_key_eq(jbuffVal, _s_gltf_name)) { buffView->name = json_strdup(jbuffVal, gst->heap, buffView); } else if (json_key_eq(jbuffVal, _s_gltf_extensions)) { jext = jbuffVal; } jbuffVal = jbuffVal->next; } if (!gltf_ext_bufferView(gst, buffView, jext)) { gst->stop = true; return; } flist_sp_insert(&gst->bufferViews, buffView); jbuffView = jbuffView->next; } } void gltf_buffers(json_t * __restrict jbuff, void * __restrict userdata) { AkGLTFState *gst; AkHeap *heap; const json_array_t *jbuffers; const json_t *jbuffVal; char *localurl; char *uri; AkBuffer *buff; if (!(jbuffers = json_array(jbuff))) return; gst = userdata; heap = gst->heap; jbuff = jbuffers->base.value; while (jbuff) { bool foundUri; buff = ak_heap_calloc(heap, gst->tmpParent, sizeof(*buff)); jbuffVal = jbuff->value; foundUri = false; while (jbuffVal) { if (json_key_eq(jbuffVal, _s_gltf_uri)) { uri = json_string_dup(jbuffVal); if (strncmp(uri, _s_gltf_b64d, strlen(_s_gltf_b64d)) == 0) { base64_buff(uri, jbuffVal->valsize, buff); } else { localurl = ak_getFileFrom(gst->doc, uri); ak_readfile(localurl, buff, &buff->data, &buff->length); ak_free(localurl); } if (uri) free(uri); foundUri = true; /* TODO: log if logging enabled (or by log level) */ } else if (json_key_eq(jbuffVal, _s_gltf_name)) { buff->name = json_strdup(jbuffVal, heap, buff); } jbuffVal = jbuffVal->next; } if (!foundUri && gst->bindata) { buff->data = gst->bindata; buff->length = gst->bindataLen; } flist_sp_insert(&gst->buffers, buff); jbuff = jbuff->next; } } ================================================ FILE: src/io/gltf/imp/core/buffer.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_imp_core_buffer_h #define gltf_imp_core_buffer_h #include "../common.h" void gltf_buffers(json_t * __restrict json, void * __restrict userdata); void gltf_bufferViews(json_t * __restrict json, void * __restrict userdata); #endif /* gltf_imp_core_buffer_h */ ================================================ FILE: src/io/gltf/imp/core/camera.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "camera.h" #include "../extra.h" #define k_name 0 #define k_type 1 #define k_perspective 2 #define k_orthographic 3 AK_HIDE void gltf_cameras(json_t * __restrict jcam, void * __restrict userdata) { AkGLTFState *gst; AkHeap *heap; AkDoc *doc; const json_array_t *jcams; AkLibrary *lib; json_t *it; if (!(jcams = json_array(jcam))) return; gst = userdata; heap = gst->heap; doc = gst->doc; jcam = jcams->base.value; lib = ak_heap_calloc(heap, doc, sizeof(*lib)); while (jcam) { AkCamera *cam; AkOptics *optics; json_t *jtechn; cam = ak_heap_calloc(heap, lib, sizeof(*cam)); optics = ak_heap_calloc(heap, cam, sizeof(*optics)); cam->optics = optics; gltf_extra(gst, cam, json_get(jcam, _s_gltf_extras), json_get(jcam, _s_gltf_extensions)); json_objmap_t camMap[] = { JSON_OBJMAP_OBJ(_s_gltf_name, I2P k_name), JSON_OBJMAP_OBJ(_s_gltf_type, I2P k_type), JSON_OBJMAP_OBJ(_s_gltf_perspective, I2P k_perspective), JSON_OBJMAP_OBJ(_s_gltf_orthographic, I2P k_orthographic) }; json_objmap(jcam, camMap, JSON_ARR_LEN(camMap)); if ((it = camMap[k_name].object)) { cam->name = json_strdup(it, heap, cam); } if (!(it = camMap[k_type].object)) { ak_free(cam); continue; } if (json_val_eqsz(it, _s_gltf_perspective, it->valsize)) { AkPerspective *persp; persp = ak_heap_calloc(heap, optics, sizeof(*persp)); persp->base.type = AK_PROJECTION_PERSPECTIVE; if ((it = camMap[k_perspective].object) && (jtechn = json_json(it))) { while (jtechn) { if (json_key_eq(jtechn, _s_gltf_xfov)) { persp->xfov = json_float(jtechn, 0.0f); } else if (json_key_eq(jtechn, _s_gltf_yfov)) { persp->yfov = json_float(jtechn, 0.0f); } else if (json_key_eq(jtechn, _s_gltf_znear)) { persp->znear = json_float(jtechn, 0.0f); } else if (json_key_eq(jtechn, _s_gltf_zfar)) { persp->zfar = json_float(jtechn, 0.0f); } else if (json_key_eq(jtechn, _s_gltf_aspectRatio)) { persp->aspectRatio = json_float(jtechn, 0.0f); } jtechn = jtechn->next; } } if (!persp->aspectRatio && persp->yfov && persp->xfov) { persp->aspectRatio = persp->xfov / persp->yfov; } else if (!persp->yfov && persp->aspectRatio && persp->xfov) { persp->yfov = persp->xfov / persp->aspectRatio; } else if (!persp->xfov && persp->aspectRatio && persp->yfov) { persp->xfov = persp->yfov * persp->aspectRatio; } optics->tcommon = &persp->base; } else if (json_val_eqsz(it, _s_gltf_orthographic, it->valsize)) { AkOrthographic *ortho; ortho = ak_heap_calloc(heap, optics, sizeof(*ortho)); ortho->base.type = AK_PROJECTION_ORTHOGRAPHIC; if ((it = camMap[k_orthographic].object) && (jtechn = json_json(it))) { while (jtechn) { if (json_key_eq(jtechn, _s_gltf_xmag)) { ortho->xmag = json_float(jtechn, 0.0f); } else if (json_key_eq(jtechn, _s_gltf_ymag)) { ortho->ymag = json_float(jtechn, 0.0f); } else if (json_key_eq(jtechn, _s_gltf_znear)) { ortho->znear = json_float(jtechn, 0.0f); } else if (json_key_eq(jtechn, _s_gltf_zfar)) { ortho->zfar = json_float(jtechn, 0.0f); } jtechn = jtechn->next; } } if (ortho->ymag && ortho->xmag) ortho->aspectRatio = ortho->xmag / ortho->ymag; optics->tcommon = &ortho->base; } cam->base.next = lib->chld; lib->chld = (void *)cam; lib->count++; jcam = jcam->next; } gst->doc->lib.cameras = lib; } ================================================ FILE: src/io/gltf/imp/core/camera.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_imp_core_camera_h #define gltf_imp_core_camera_h #include "../common.h" AK_HIDE void gltf_cameras(json_t * __restrict jcam, void * __restrict userdata); #endif /* gltf_imp_core_camera_h */ ================================================ FILE: src/io/gltf/imp/core/enum.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "enum.h" AK_HIDE AkEnum gltf_enumInputSemantic(const char * name) { AkEnum val; long glenums_len; long i; dae_enum glenums[] = { {_s_gltf_COLOR, AK_INPUT_COLOR}, {_s_gltf_JOINTS, AK_INPUT_JOINT}, {_s_gltf_NORMAL, AK_INPUT_NORMAL}, {_s_gltf_POSITION, AK_INPUT_POSITION}, {_s_gltf_TANGENT, AK_INPUT_TANGENT}, {_s_gltf_TEXCOORD, AK_INPUT_TEXCOORD}, {_s_gltf_WEIGHTS, AK_INPUT_WEIGHT}, }; val = AK_INPUT_OTHER; glenums_len = AK_ARRAY_LEN(glenums); for (i = 0; i < glenums_len; i++) { if (strcasecmp(name, glenums[i].name) == 0) { val = glenums[i].val; break; } } return val; } AK_HIDE AkEnum gltf_componentType(int type) { switch (type) { case 5120: return AKT_BYTE; break; case 5121: return AKT_UBYTE; break; case 5122: return AKT_SHORT; break; case 5123: return AKT_USHORT; break; case 5125: return AKT_UINT; break; case 5126: return AKT_FLOAT; break; default: break; } return AKT_NONE; } AK_HIDE int gltf_componentLen(int type) { switch (type) { case 5120: /* AKT_BYTE */ case 5121: return 1; /* AKT_UBYTE */ case 5122: /* AKT_SHORT */ case 5123: return 2; /* AKT_USHORT */ case 5125: /* AKT_UINT */ case 5126: return 4; /* AKT_FLOAT */ default: return 1; } } AK_HIDE AkComponentSize gltf_type(const json_t * __restrict json) { AkComponentSize val; long glenums_len; long i; dae_enum glenums[] = { {_s_gltf_SCALAR, AK_COMPONENT_SIZE_SCALAR}, {_s_gltf_VEC2, AK_COMPONENT_SIZE_VEC2}, {_s_gltf_VEC3, AK_COMPONENT_SIZE_VEC3}, {_s_gltf_VEC4, AK_COMPONENT_SIZE_VEC4}, {_s_gltf_MAT2, AK_COMPONENT_SIZE_MAT2}, {_s_gltf_MAT3, AK_COMPONENT_SIZE_MAT3}, {_s_gltf_MAT4, AK_COMPONENT_SIZE_MAT4}, }; val = AK_COMPONENT_SIZE_UNKNOWN; glenums_len = AK_ARRAY_LEN(glenums); for (i = 0; i < glenums_len; i++) { if (json_val_eq(json, glenums[i].name)) { val = glenums[i].val; break; } } return val; } AK_HIDE AkEnum gltf_minFilter(int type) { switch (type) { case 9728: return AK_MAGFILTER_NEAREST; break; case 9729: return AK_MAGFILTER_LINEAR; break; case 9984: return AK_NEAREST_MIPMAP_NEAREST; break; case 9985: return AK_LINEAR_MIPMAP_NEAREST; break; case 9986: return AK_NEAREST_MIPMAP_LINEAR; break; case 9987: return AK_LINEAR_MIPMAP_LINEAR; break; default: break; } return 0; } AK_HIDE AkEnum gltf_magFilter(int type) { switch (type) { case 9728: return AK_MINFILTER_NEAREST; break; case 9729: return AK_MINFILTER_LINEAR; break; default: break; } return 0; } AK_HIDE AkEnum gltf_wrapMode(int type) { switch (type) { case 33071: return AK_WRAP_MODE_CLAMP; break; case 33648: return AK_WRAP_MODE_MIRROR; break; case 10497: return AK_WRAP_MODE_WRAP; break; default: break; } return AK_WRAP_MODE_WRAP; } AK_HIDE AkOpaque gltf_alphaMode(const json_t * __restrict json) { AkEnum val; long glenums_len; long i; dae_enum glenums[] = { {_s_gltf_OPAQUE, AK_OPAQUE_OPAQUE}, {_s_gltf_MASK, AK_OPAQUE_MASK}, {_s_gltf_BLEND, AK_OPAQUE_BLEND} }; val = AK_OPAQUE_OPAQUE; glenums_len = AK_ARRAY_LEN(glenums); for (i = 0; i < glenums_len; i++) { if (json_val_eq(json, glenums[i].name)) { val = glenums[i].val; break; } } return val; } AK_HIDE AkInterpolationType gltf_interp(const json_t * __restrict json) { AkEnum val; long glenums_len; long i; dae_enum glenums[] = { {_s_gltf_LINEAR, AK_INTERPOLATION_LINEAR}, {_s_gltf_STEP, AK_INTERPOLATION_STEP}, {_s_gltf_CUBICSPLINE, AK_INTERPOLATION_HERMITE} }; val = AK_INTERPOLATION_LINEAR; glenums_len = AK_ARRAY_LEN(glenums); for (i = 0; i < glenums_len; i++) { if (json_val_eq(json, glenums[i].name)) { val = glenums[i].val; break; } } return val; } ================================================ FILE: src/io/gltf/imp/core/enum.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_imp_core_enums_h #define gltf_imp_core_enums_h #include "../common.h" AK_HIDE AkEnum gltf_enumInputSemantic(const char *name); AK_HIDE AkEnum gltf_componentType(int type); AK_HIDE int gltf_componentLen(int type) ; AK_HIDE AkComponentSize gltf_type(const json_t * __restrict json); AK_HIDE AkEnum gltf_minFilter(int type); AK_HIDE AkEnum gltf_magFilter(int type); AK_HIDE AkEnum gltf_wrapMode(int type); AK_HIDE AkOpaque gltf_alphaMode(const json_t * __restrict json); AK_HIDE AkInterpolationType gltf_interp(const json_t * __restrict json); #endif /* gltf_imp_core_enums_h */ ================================================ FILE: src/io/gltf/imp/core/ext.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ext.h" #include "../ext/decoder.h" #include "../ext/instancing.h" #include "../ext/lights.h" #include "../ext/variants.h" typedef struct AkGLTFExtName { const char *name; size_t nameSize; } AkGLTFExtName; #define GLTF_EXT_NAME(NAME) { _s_gltf_ ## NAME, sizeof(#NAME) - 1 } static int gltf_extNameCmp(const void * __restrict a, const void * __restrict b) { const AkGLTFExtName *extname; const char *val; size_t valSize; size_t minSize; int res; extname = b; if (!(val = json_string(a))) return -1; valSize = (size_t)((const json_t *)a)->valsize; minSize = valSize < extname->nameSize ? valSize : extname->nameSize; res = strncmp(val, extname->name, minSize); if (res != 0) return res; if (valSize < extname->nameSize) return -1; if (valSize > extname->nameSize) return 1; return 0; } static inline bool gltf_extNameSearch(const json_t * __restrict ext, const AkGLTFExtName * __restrict names, size_t count) { return ext && bsearch(ext, names, count, sizeof(names[0]), gltf_extNameCmp) != NULL; } static bool gltf_ext_preserved_supported(const json_t * __restrict ext) { static const AkGLTFExtName names[] = { GLTF_EXT_NAME(ADOBE_materials_clearcoat_specular), GLTF_EXT_NAME(ADOBE_materials_clearcoat_tint), GLTF_EXT_NAME(ADOBE_materials_thin_transparency), GLTF_EXT_NAME(AGI_articulations), GLTF_EXT_NAME(AGI_stk_metadata), GLTF_EXT_NAME(CESIUM_primitive_outline), GLTF_EXT_NAME(EXT_gsplat_compression_spz), GLTF_EXT_NAME(EXT_lights_ies), GLTF_EXT_NAME(EXT_lights_image_based), GLTF_EXT_NAME(EXT_mesh_manifold), GLTF_EXT_NAME(EXT_mesh_primitive_restart), GLTF_EXT_NAME(EXT_texture_astc), GLTF_EXT_NAME(FB_geometry_metadata), GLTF_EXT_NAME(GODOT_single_root), GLTF_EXT_NAME(GRIFFEL_bim_data), GLTF_EXT_NAME(KHR_techniques_webgl), GLTF_EXT_NAME(KHR_xmp), GLTF_EXT_NAME(MPEG_accessor_timed), GLTF_EXT_NAME(MPEG_animation_timing), GLTF_EXT_NAME(MPEG_audio_spatial), GLTF_EXT_NAME(MPEG_buffer_circular), GLTF_EXT_NAME(MPEG_media), GLTF_EXT_NAME(MPEG_mesh_linking), GLTF_EXT_NAME(MPEG_scene_dynamic), GLTF_EXT_NAME(MPEG_texture_video), GLTF_EXT_NAME(MPEG_viewport_recommended), GLTF_EXT_NAME(MSFT_lod), GLTF_EXT_NAME(MSFT_packing_normalRoughnessMetallic), GLTF_EXT_NAME(MSFT_packing_occlusionRoughnessMetallic), GLTF_EXT_NAME(MSFT_texture_dds), GLTF_EXT_NAME(NV_materials_mdl) }; return gltf_extNameSearch(ext, names, AK_ARRAY_LEN(names)); } static bool gltf_ext_supported(AkGLTFState * __restrict gst, const json_t * __restrict ext) { static const AkGLTFExtName names[] = { GLTF_EXT_NAME(EXT_mesh_gpu_instancing), GLTF_EXT_NAME(EXT_texture_webp), GLTF_EXT_NAME(KHR_animation_pointer), GLTF_EXT_NAME(KHR_gaussian_splatting), GLTF_EXT_NAME(KHR_lights_punctual), GLTF_EXT_NAME(KHR_materials_anisotropy), GLTF_EXT_NAME(KHR_materials_clearcoat), GLTF_EXT_NAME(KHR_materials_diffuse_transmission), GLTF_EXT_NAME(KHR_materials_dispersion), GLTF_EXT_NAME(KHR_materials_emissive_strength), GLTF_EXT_NAME(KHR_materials_ior), GLTF_EXT_NAME(KHR_materials_iridescence), GLTF_EXT_NAME(KHR_materials_pbrSpecularGlossiness), GLTF_EXT_NAME(KHR_materials_sheen), GLTF_EXT_NAME(KHR_materials_specular), GLTF_EXT_NAME(KHR_materials_transmission), GLTF_EXT_NAME(KHR_materials_unlit), GLTF_EXT_NAME(KHR_materials_variants), GLTF_EXT_NAME(KHR_materials_volume), GLTF_EXT_NAME(KHR_mesh_quantization), GLTF_EXT_NAME(KHR_node_visibility), GLTF_EXT_NAME(KHR_texture_transform), GLTF_EXT_NAME(KHR_xmp_json_ld) }; if (!ext) return false; if (gltf_extNameSearch(ext, names, AK_ARRAY_LEN(names))) return true; if (json_val_eq(ext, _s_gltf_EXT_meshopt_compression) || json_val_eq(ext, _s_gltf_KHR_meshopt_compression)) return gltf_ext_meshopt(gst); if (json_val_eq(ext, _s_gltf_KHR_draco_mesh_compression)) return gltf_ext_draco(gst); if (json_val_eq(ext, _s_gltf_KHR_texture_basisu)) return gltf_ext_ktx2(gst); if (gltf_ext_preserved_supported(ext)) return true; return false; } AK_HIDE void gltf_exts(json_t * __restrict jext, void * __restrict userdata) { AkGLTFState *gst; const json_array_t *jexts; json_t *it; gst = userdata; if (!(jexts = json_array(jext))) return; for (it = (void *)jexts->base.value; it; it = it->next) { if (json_val_eq(it, _s_gltf_KHR_animation_pointer)) gst->animPointerRequired = true; if (!gltf_ext_supported(gst, it)) { gst->stop = true; return; } } } AK_HIDE void gltf_ext_root(json_t * __restrict jext, void * __restrict userdata) { AkGLTFState *gst; json_t *jpunctual; json_t *jlights; json_t *jvariantsExt; json_t *jvariants; gst = userdata; if (!jext) return; if ((jpunctual = json_get(jext, _s_gltf_KHR_lights_punctual)) && (jlights = json_get(jpunctual, _s_gltf_lights))) { gltf_ext_lights(gst, jlights); } if ((jvariantsExt = json_get(jext, _s_gltf_KHR_materials_variants)) && (jvariants = json_get(jvariantsExt, _s_gltf_variants))) { gltf_ext_materialVariants(gst, jvariants); } } AK_HIDE bool gltf_ext_node(AkGLTFState * __restrict gst, AkNode * __restrict node, const json_t * __restrict jext) { json_t *jvis; json_t *jvisible; json_t *jinstancing; if (!gst || !node || !jext) return true; jvis = json_get(jext, _s_gltf_KHR_node_visibility); jvisible = jvis ? json_get(jvis, _s_gltf_visible) : NULL; if (jvisible) node->visible = json_bool(jvisible, true); if ((jinstancing = json_get(jext, _s_gltf_EXT_mesh_gpu_instancing))) { node->instancing = gltf_ext_meshGPUInstancing(gst, node, jinstancing); if (gst->stop) return false; } return gltf_ext_nodeLight(gst, node, jext); } AK_HIDE void gltf_ext_close(AkGLTFState * __restrict gst) { gltf_ext_decoderClose(gst); } ================================================ FILE: src/io/gltf/imp/core/ext.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_imp_core_ext_h #define gltf_imp_core_ext_h #include "../common.h" AK_HIDE void gltf_exts(json_t * __restrict jext, void * __restrict userdata); AK_HIDE void gltf_ext_root(json_t * __restrict jext, void * __restrict userdata); AK_HIDE bool gltf_ext_node(AkGLTFState * __restrict gst, AkNode * __restrict node, const json_t * __restrict jext); AK_HIDE void gltf_ext_close(AkGLTFState * __restrict gst); AK_HIDE bool gltf_ext_bufferView(AkGLTFState * __restrict gst, AkBufferView * __restrict buffView, const json_t * __restrict jext); AK_HIDE bool gltf_ext_textureBasisu(AkGLTFState * __restrict gst); AK_HIDE bool gltf_ext_dracoPrimitive(AkGLTFState * __restrict gst, AkMeshPrimitive * __restrict prim, const json_t * __restrict jprim); /*! * @brief Parse `KHR_materials_variants` mappings on a mesh primitive. * * Reads `primitive.extensions.KHR_materials_variants.mappings[]` from * `jprim` and populates `prim->variantMappings`. The doc-level variant * list must already be resolved (gltf_ext_root). Returns true on * success or absence; false only on irrecoverable error. */ AK_HIDE bool gltf_ext_primitiveVariants(AkGLTFState * __restrict gst, AkMeshPrimitive * __restrict prim, const json_t * __restrict jprim); /*! * @brief Parse `KHR_gaussian_splatting` on a mesh primitive. * * Reads `primitive.extensions.KHR_gaussian_splatting.{kernel,colorSpace, * projection,sortingMethod}` and populates `prim->gsplat`. If the * extension carries a compression sub-extension (future spec) and an * external Gaussian splat decoder is loaded (see * AK_OPT_GLTF_GSPLAT_DECODER_PATH), the decoder is invoked to populate * `gsplat->decodedData`. Without compression and without a decoder the * primitive's standard accessor chain stays authoritative — renderers * that don't recognize the extension fall back to point-cloud display. */ AK_HIDE bool gltf_ext_primitiveGaussianSplat(AkGLTFState * __restrict gst, AkMeshPrimitive * __restrict prim, const json_t * __restrict jprim); #endif /* gltf_imp_core_ext_h */ ================================================ FILE: src/io/gltf/imp/core/image.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "image.h" #include "../extra.h" #include "../../../../base64.h" #define k_name 0 #define k_bufferView 1 #define k_uri 2 #define k_mimeType 3 static char* gltf_imageDataUriMime(AkHeap * __restrict heap, void * __restrict parent, const char * __restrict uri, int len) { const char *start; const char *end; int prefixLen; if (!uri || len <= 0) return NULL; prefixLen = (int)strlen(_s_gltf_b64d); if (len <= prefixLen || strncmp(uri, _s_gltf_b64d, (size_t)prefixLen) != 0) return NULL; start = uri + prefixLen; end = memchr(start, ';', (size_t)(len - prefixLen)); if (!end || end <= start) return NULL; return ak_heap_strndup(heap, parent, start, (size_t)(end - start)); } AK_HIDE void gltf_images(json_t * __restrict jimage, void * __restrict userdata) { AkGLTFState *gst; AkHeap *heap; const json_array_t *jimages; AkImage *image; json_t *it; if (!(jimages = json_array(jimage))) return; gst = userdata; heap = gst->heap; jimage = jimages->base.value; while (jimage) { AkInitFrom *initFrom; image = ak_heap_calloc(gst->heap, gst->doc, sizeof(*image)); initFrom = NULL; gltf_extra(gst, image, json_get(jimage, _s_gltf_extras), json_get(jimage, _s_gltf_extensions)); json_objmap_t imgMap[] = { JSON_OBJMAP_OBJ(_s_gltf_name, I2P k_name), JSON_OBJMAP_OBJ(_s_gltf_bufferView, I2P k_bufferView), JSON_OBJMAP_OBJ(_s_gltf_uri, I2P k_uri), JSON_OBJMAP_OBJ("mimeType", I2P k_mimeType) }; json_objmap(jimage, imgMap, JSON_ARR_LEN(imgMap)); if ((it = imgMap[k_name].object)) { image->name = json_strdup(it, gst->heap, image); } if ((it = imgMap[k_bufferView].object)) { AkBuffer *tmpbuff; AkBufferView *buffView; int32_t buffViewIndex; if ((buffViewIndex = json_int32(it, -1)) > -1 && (buffView = flist_sp_at(&gst->bufferViews, buffViewIndex)) && (tmpbuff = buffView->buffer)) { initFrom = ak_heap_calloc(heap, image, sizeof(*initFrom)); initFrom->buff = ak_heap_calloc(heap, gst->doc, sizeof(*initFrom->buff)); initFrom->buff->length = buffView->byteLength; if (gst->borrowBufferViews) { initFrom->buff->data = (char *)tmpbuff->data + buffView->byteOffset; initFrom->buff->name = "assetkit:gltf-buffer-view-slice"; } else { initFrom->buff->data = ak_heap_alloc(heap, initFrom->buff, buffView->byteLength); memcpy(initFrom->buff->data, (char *)tmpbuff->data + buffView->byteOffset, buffView->byteLength); } if ((it = imgMap[k_mimeType].object)) initFrom->buffMime = json_strdup(it, heap, initFrom); image->initFrom = initFrom; } } if (!initFrom && (it = imgMap[k_uri].object)) { initFrom = ak_heap_calloc(heap, image, sizeof(*initFrom)); if (!strncmp(it->value, _s_gltf_b64d, strlen(_s_gltf_b64d))) { char *uri; uri = it->value; initFrom->buff = ak_heap_calloc(heap, gst->doc, sizeof(*initFrom->buff)); base64_buff(uri, it->valsize, initFrom->buff); if (imgMap[k_mimeType].object) initFrom->buffMime = json_strdup(imgMap[k_mimeType].object, heap, initFrom); else initFrom->buffMime = gltf_imageDataUriMime(heap, initFrom, uri, it->valsize); } else { initFrom->ref = json_strdup(it, gst->heap, initFrom); } image->initFrom = initFrom; } flist_sp_insert(&gst->doc->lib.images, image); jimage = jimage->next; } } ================================================ FILE: src/io/gltf/imp/core/image.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_imp_core_image_h #define gltf_imp_core_image_h #include "../common.h" AK_HIDE void gltf_images(json_t * __restrict jimage, void * __restrict userdata); #endif /* gltf_imp_core_image_h */ ================================================ FILE: src/io/gltf/imp/core/material.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "material.h" #include "profile.h" #include "sampler.h" #include "texture.h" #include "enum.h" #include "../extra.h" #include "../../../../default/material.h" AK_HIDE AkMaterial* gltf_default_mat(AkGLTFState *gst, AkLibrary *libmat) { AkHeap *heap; AkInstanceEffect *ieff; AkEffect *effect; AkProfileCommon *pcommon; AkTechniqueFx *technfx; AkTechniqueFxCommon *cmnTechn; AkMaterial *mat; AkMaterialMetallicProp *metalness, *roughness; AkColorDesc *colorDesc; AkTransparent *transp; heap = gst->heap; pcommon = gltf_cmnEffect(gst); effect = ak_mem_parent(pcommon); technfx = ak_heap_calloc(heap, pcommon, sizeof(*technfx)); mat = ak_heap_calloc(heap, libmat, sizeof(*mat)); cmnTechn = ak_heap_calloc(heap, technfx, sizeof(*cmnTechn));; pcommon->technique = technfx; ak_setypeid(technfx, AKT_TECHNIQUE_FX); cmnTechn->type = AK_MATERIAL_PBR; metalness = ak_heap_calloc(heap, cmnTechn, sizeof(*metalness)); roughness = ak_heap_calloc(heap, cmnTechn, sizeof(*roughness)); metalness->intensity = 1.0f; roughness->intensity = 1.0f; cmnTechn->metalness = metalness; cmnTechn->roughness = roughness; cmnTechn->albedo = ak_heap_calloc(heap, cmnTechn, sizeof(*cmnTechn->albedo)); cmnTechn->albedo->color = ak_heap_calloc(heap, cmnTechn, sizeof(*cmnTechn->albedo->color)); /* DEFAULT value by spec */ glm_vec4_copy(GLM_VEC4_ONE, cmnTechn->albedo->color->vec); /* emissive */ cmnTechn->emission = ak_heap_calloc(heap, technfx, sizeof(*cmnTechn->emission)); colorDesc = &cmnTechn->emission->color; colorDesc->color = ak_heap_calloc(heap, colorDesc, sizeof(*colorDesc->color)); colorDesc->color->vec[3] = 1.0f; cmnTechn->emission->strength = 1.0f; /* transparent */ transp = ak_heap_calloc(heap, cmnTechn, sizeof(*transp)); transp->amount = 1.0f; transp->opaque = AK_OPAQUE_OPAQUE; transp->cutoff = 0.5f; cmnTechn->transparent = transp; technfx->common = cmnTechn; ieff = ak_heap_calloc(heap, mat, sizeof(*ieff)); ieff->base.type = AK_INSTANCE_EFFECT; ieff->base.url.ptr = effect; mat->effect = ieff; return mat; } static AkColorDesc* gltf_materialColorDesc(AkGLTFState * __restrict gst, void * __restrict parent, float r, float g, float b, float a) { AkColorDesc *desc; desc = ak_heap_calloc(gst->heap, parent, sizeof(*desc)); desc->color = ak_heap_calloc(gst->heap, desc, sizeof(*desc->color)); desc->color->vec[0] = r; desc->color->vec[1] = g; desc->color->vec[2] = b; desc->color->vec[3] = a; return desc; } static AkTextureRef* gltf_materialTexRef(AkGLTFState * __restrict gst, void * __restrict parent, json_t * __restrict jtexinfo, AkTextureColorSpace colorSpace, AkTextureChannels channels) { return ak_texref_usage(gltf_texref(gst, parent, jtexinfo), colorSpace, channels); } static void gltf_materialParseSpecular(AkGLTFState * __restrict gst, AkTechniqueFxCommon * __restrict cmnTechn, json_t * __restrict jspec) { AkMaterialSpecularProp *specularProp; json_t *jval; if (!(specularProp = cmnTechn->specular)) { specularProp = ak_heap_calloc(gst->heap, cmnTechn, sizeof(*specularProp)); specularProp->strength = 1.0f; specularProp->color = gltf_materialColorDesc(gst, specularProp, 1.0f, 1.0f, 1.0f, 1.0f); cmnTechn->specular = specularProp; } jval = jspec->value; while (jval) { if (json_key_eq(jval, _s_gltf_specularFactor)) { specularProp->strength = json_float(jval, 1.0f); } else if (json_key_eq(jval, _s_gltf_specularTexture)) { specularProp->specularTex = gltf_materialTexRef(gst, cmnTechn, jval, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_A); specularProp->textureChannels = AK_TEXTURE_CHANNEL_A; } else if (json_key_eq(jval, _s_gltf_specularColorFactor)) { json_array_float(specularProp->color->color->vec, jval, 0.0f, 3, true); specularProp->color->color->vec[3] = 1.0f; } else if (json_key_eq(jval, _s_gltf_specularColorTexture)) { specularProp->color->texture = gltf_materialTexRef(gst, cmnTechn, jval, AK_TEXTURE_COLORSPACE_SRGB, AK_TEXTURE_CHANNEL_RGB); } jval = jval->next; } } static void gltf_materialParseClearcoat(AkGLTFState * __restrict gst, AkTechniqueFxCommon * __restrict cmnTechn, json_t * __restrict jspec) { AkMaterialClearcoat *clearcoat; json_t *jval; if (!(clearcoat = cmnTechn->clearcoat)) { clearcoat = ak_heap_calloc(gst->heap, cmnTechn, sizeof(*clearcoat)); clearcoat->normalScale = 1.0f; cmnTechn->clearcoat = clearcoat; } jval = jspec->value; while (jval) { if (json_key_eq(jval, _s_gltf_clearcoatFactor)) { clearcoat->intensity = json_float(jval, 0.0f); } else if (json_key_eq(jval, _s_gltf_clearcoatTexture)) { clearcoat->texture = gltf_materialTexRef(gst, cmnTechn, jval, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_R); clearcoat->textureChannels = AK_TEXTURE_CHANNEL_R; } else if (json_key_eq(jval, _s_gltf_clearcoatRoughnessFactor)) { clearcoat->roughness = json_float(jval, 0.0f); } else if (json_key_eq(jval, _s_gltf_clearcoatRoughnessTexture)) { clearcoat->roughnessTexture = gltf_materialTexRef(gst, cmnTechn, jval, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_G); clearcoat->roughnessTextureChannels = AK_TEXTURE_CHANNEL_G; } else if (json_key_eq(jval, _s_gltf_clearcoatNormalTexture)) { clearcoat->normalTexture = gltf_materialTexRef(gst, cmnTechn, jval, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_RGB); clearcoat->normalScale = json_float(json_get(jval, _s_gltf_scale), 1.0f); } jval = jval->next; } } static void gltf_materialParseTransmission(AkGLTFState * __restrict gst, AkTechniqueFxCommon * __restrict cmnTechn, json_t * __restrict jspec) { AkMaterialTransmissionProp *transmissionProp; json_t *jval; if (!(transmissionProp = cmnTechn->transmission)) { transmissionProp = ak_heap_calloc(gst->heap, cmnTechn, sizeof(*transmissionProp)); cmnTechn->transmission = transmissionProp; } jval = jspec->value; while (jval) { if (json_key_eq(jval, _s_gltf_transmissionFactor)) { transmissionProp->factor = json_float(jval, 0.0f); } else if (json_key_eq(jval, _s_gltf_transmissionTexture)) { transmissionProp->texture = gltf_materialTexRef(gst, cmnTechn, jval, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_R); transmissionProp->textureChannels = AK_TEXTURE_CHANNEL_R; } jval = jval->next; } } static void gltf_materialParseSheen(AkGLTFState * __restrict gst, AkTechniqueFxCommon * __restrict cmnTechn, json_t * __restrict jspec) { AkMaterialSheen *sheen; json_t *jval; if (!(sheen = cmnTechn->sheen)) { sheen = ak_heap_calloc(gst->heap, cmnTechn, sizeof(*sheen)); sheen->color = gltf_materialColorDesc(gst, sheen, 0.0f, 0.0f, 0.0f, 1.0f); cmnTechn->sheen = sheen; } jval = jspec->value; while (jval) { if (json_key_eq(jval, _s_gltf_sheenColorFactor)) { json_array_float(sheen->color->color->vec, jval, 0.0f, 3, true); sheen->color->color->vec[3] = 1.0f; } else if (json_key_eq(jval, _s_gltf_sheenColorTexture)) { sheen->color->texture = gltf_materialTexRef(gst, sheen, jval, AK_TEXTURE_COLORSPACE_SRGB, AK_TEXTURE_CHANNEL_RGB); } else if (json_key_eq(jval, _s_gltf_sheenRoughnessFactor)) { sheen->roughness = json_float(jval, 0.0f); } else if (json_key_eq(jval, _s_gltf_sheenRoughnessTexture)) { sheen->roughnessTexture = gltf_materialTexRef(gst, sheen, jval, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_A); sheen->roughnessTextureChannels = AK_TEXTURE_CHANNEL_A; } jval = jval->next; } } static void gltf_materialParseIridescence(AkGLTFState * __restrict gst, AkTechniqueFxCommon * __restrict cmnTechn, json_t * __restrict jspec) { AkMaterialIridescence *iri; json_t *jval; if (!(iri = cmnTechn->iridescence)) { iri = ak_heap_calloc(gst->heap, cmnTechn, sizeof(*iri)); iri->ior = 1.3f; iri->thicknessMinimum = 100.0f; iri->thicknessMaximum = 400.0f; cmnTechn->iridescence = iri; } jval = jspec->value; while (jval) { if (json_key_eq(jval, _s_gltf_iridescenceFactor)) { iri->factor = json_float(jval, 0.0f); } else if (json_key_eq(jval, _s_gltf_iridescenceTexture)) { iri->texture = gltf_materialTexRef(gst, iri, jval, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_R); iri->textureChannels = AK_TEXTURE_CHANNEL_R; } else if (json_key_eq(jval, _s_gltf_iridescenceIor)) { iri->ior = json_float(jval, 1.3f); } else if (json_key_eq(jval, _s_gltf_iridescenceThicknessMinimum)) { iri->thicknessMinimum = json_float(jval, 100.0f); } else if (json_key_eq(jval, _s_gltf_iridescenceThicknessMaximum)) { iri->thicknessMaximum = json_float(jval, 400.0f); } else if (json_key_eq(jval, _s_gltf_iridescenceThicknessTexture)) { iri->thicknessTexture = gltf_materialTexRef(gst, iri, jval, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_G); iri->thicknessTextureChannels = AK_TEXTURE_CHANNEL_G; } jval = jval->next; } } static void gltf_materialParseVolume(AkGLTFState * __restrict gst, AkTechniqueFxCommon * __restrict cmnTechn, json_t * __restrict jspec) { AkMaterialVolume *vol; json_t *jval; if (!(vol = cmnTechn->volume)) { vol = ak_heap_calloc(gst->heap, cmnTechn, sizeof(*vol)); vol->attenuationColor.vec[0] = 1.0f; vol->attenuationColor.vec[1] = 1.0f; vol->attenuationColor.vec[2] = 1.0f; vol->attenuationColor.vec[3] = 1.0f; vol->attenuationDistance = INFINITY; cmnTechn->volume = vol; } jval = jspec->value; while (jval) { if (json_key_eq(jval, _s_gltf_thicknessFactor)) { vol->thicknessFactor = json_float(jval, 0.0f); } else if (json_key_eq(jval, _s_gltf_thicknessTexture)) { vol->thicknessTexture = gltf_materialTexRef(gst, vol, jval, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_G); vol->thicknessTextureChannels = AK_TEXTURE_CHANNEL_G; } else if (json_key_eq(jval, _s_gltf_attenuationDistance)) { vol->attenuationDistance = json_float(jval, INFINITY); } else if (json_key_eq(jval, _s_gltf_attenuationColor)) { json_array_float(vol->attenuationColor.vec, jval, 1.0f, 3, true); vol->attenuationColor.vec[3] = 1.0f; } jval = jval->next; } } static void gltf_materialParseAnisotropy(AkGLTFState * __restrict gst, AkTechniqueFxCommon * __restrict cmnTechn, json_t * __restrict jspec) { AkMaterialAnisotropy *aniso; json_t *jval; if (!(aniso = cmnTechn->anisotropy)) { aniso = ak_heap_calloc(gst->heap, cmnTechn, sizeof(*aniso)); cmnTechn->anisotropy = aniso; } jval = jspec->value; while (jval) { if (json_key_eq(jval, _s_gltf_anisotropyStrength)) { aniso->strength = json_float(jval, 0.0f); } else if (json_key_eq(jval, _s_gltf_anisotropyRotation)) { aniso->rotation = json_float(jval, 0.0f); } else if (json_key_eq(jval, _s_gltf_anisotropyTexture)) { aniso->texture = gltf_materialTexRef(gst, aniso, jval, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_RGB); } jval = jval->next; } } static void gltf_materialParseDispersion(AkGLTFState * __restrict gst, AkTechniqueFxCommon * __restrict cmnTechn, json_t * __restrict jspec) { AkMaterialDispersion *disp; if (!(disp = cmnTechn->dispersion)) { disp = ak_heap_calloc(gst->heap, cmnTechn, sizeof(*disp)); cmnTechn->dispersion = disp; } disp->dispersion = json_float(json_get(jspec, _s_gltf_dispersion), 0.0f); } static void gltf_materialParseDiffuseTransmission(AkGLTFState * __restrict gst, AkTechniqueFxCommon * __restrict cmnTechn, json_t * __restrict jspec) { AkMaterialDiffuseTransmission *dt; json_t *jval; if (!(dt = cmnTechn->diffuseTransmission)) { dt = ak_heap_calloc(gst->heap, cmnTechn, sizeof(*dt)); dt->color = gltf_materialColorDesc(gst, dt, 1.0f, 1.0f, 1.0f, 1.0f); cmnTechn->diffuseTransmission = dt; } jval = jspec->value; while (jval) { if (json_key_eq(jval, _s_gltf_diffuseTransmissionFactor)) { dt->factor = json_float(jval, 0.0f); } else if (json_key_eq(jval, _s_gltf_diffuseTransmissionTexture)) { dt->texture = gltf_materialTexRef(gst, dt, jval, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_A); dt->textureChannels = AK_TEXTURE_CHANNEL_A; } else if (json_key_eq(jval, _s_gltf_diffuseTransmissionColorFactor)) { json_array_float(dt->color->color->vec, jval, 1.0f, 3, true); dt->color->color->vec[3] = 1.0f; } else if (json_key_eq(jval, _s_gltf_diffuseTransmissionColorTexture)) { dt->color->texture = gltf_materialTexRef(gst, dt, jval, AK_TEXTURE_COLORSPACE_SRGB, AK_TEXTURE_CHANNEL_RGB); } jval = jval->next; } } AK_HIDE void gltf_materials(json_t * __restrict jmaterial, void * __restrict userdata) { AkGLTFState *gst; AkHeap *heap; AkDoc *doc; const json_array_t *jmaterials; AkLibrary *libmat; gst = userdata; heap = gst->heap; doc = gst->doc; libmat = ak_heap_calloc(heap, doc, sizeof(*libmat)); doc->lib.materials = libmat; gst->defaultMaterial = gltf_default_mat(gst, libmat); if (!(jmaterials = json_array(jmaterial))) return; jmaterial = jmaterials->base.value; while (jmaterial) { json_t *jmatVal, *jext; AkProfileCommon *pcommon; AkTechniqueFx *technfx; AkTechniqueFxCommon *cmnTechn; AkMaterial *mat; AkEffect *effect; AkInstanceEffect *ieff; pcommon = gltf_cmnEffect(gst); effect = ak_mem_parent(pcommon); technfx = ak_heap_calloc(heap, pcommon, sizeof(*technfx)); mat = ak_heap_calloc(heap, libmat, sizeof(*mat)); cmnTechn = ak_heap_calloc(heap, technfx, sizeof(*cmnTechn));; pcommon->technique = technfx; ak_setypeid(technfx, AKT_TECHNIQUE_FX); cmnTechn->type = AK_MATERIAL_PBR; cmnTechn->ior = 1.5f; jmatVal = jmaterial->value; gltf_extra(gst, mat, json_get(jmaterial, _s_gltf_extras), json_get(jmaterial, _s_gltf_extensions)); if ((jext = json_get(jmaterial, _s_gltf_extensions))) { json_t *jspec, *jval; if ((jspec = json_get(jext, _s_gltf_KHR_materials_specular))) gltf_materialParseSpecular(gst, cmnTechn, jspec); if ((jspec = json_get(jext, _s_gltf_KHR_materials_clearcoat))) gltf_materialParseClearcoat(gst, cmnTechn, jspec); if ((jspec = json_get(jext, _s_gltf_KHR_materials_unlit))) cmnTechn->type = AK_MATERIAL_CONSTANT; if ((jspec = json_get(jext, _s_gltf_KHR_materials_emissive_strength))) { AkMaterialEmissionProp *emission; if (!(emission = cmnTechn->emission)) { emission = ak_heap_calloc(heap, cmnTechn, sizeof(*emission)); cmnTechn->emission = emission; } emission->strength = json_float(json_get(jspec, _s_gltf_emissiveStrength), 1.0f); } if ((jspec = json_get(jext, _s_gltf_KHR_materials_ior))) cmnTechn->ior = json_float(json_get(jspec, _s_gltf_ior), 1.5f); if ((jspec = json_get(jext, _s_gltf_KHR_materials_transmission))) gltf_materialParseTransmission(gst, cmnTechn, jspec); if ((jspec = json_get(jext, _s_gltf_KHR_materials_sheen))) gltf_materialParseSheen(gst, cmnTechn, jspec); if ((jspec = json_get(jext, _s_gltf_KHR_materials_iridescence))) gltf_materialParseIridescence(gst, cmnTechn, jspec); if ((jspec = json_get(jext, _s_gltf_KHR_materials_volume))) gltf_materialParseVolume(gst, cmnTechn, jspec); if ((jspec = json_get(jext, _s_gltf_KHR_materials_anisotropy))) gltf_materialParseAnisotropy(gst, cmnTechn, jspec); if ((jspec = json_get(jext, _s_gltf_KHR_materials_dispersion))) gltf_materialParseDispersion(gst, cmnTechn, jspec); if ((jspec = json_get(jext, _s_gltf_KHR_materials_diffuse_transmission))) gltf_materialParseDiffuseTransmission(gst, cmnTechn, jspec); /* ARCHIVED: Superseded by KHR_materials_specular */ if ((jspec = json_get(jext, _s_gltf_KHR_materials_pbrSpecularGlossiness))) { AkMaterialSpecularProp *specularProp; AkColorDesc *specularColor; specularProp = ak_heap_calloc(heap, cmnTechn, sizeof(*specularProp)); specularProp->strength = 1.0f; cmnTechn->specular = specularProp; cmnTechn->type = AK_MATERIAL_SPECULAR_GLOSSINES; specularColor = ak_heap_calloc(heap, specularProp, sizeof(*specularColor)); specularColor->color = ak_heap_calloc(heap, specularColor, sizeof(*specularColor->color)); specularProp->color = specularColor; if (!cmnTechn->albedo) { cmnTechn->diffuse = ak_heap_calloc(heap, cmnTechn, sizeof(*cmnTechn->diffuse)); } cmnTechn->diffuse->color = ak_heap_calloc(heap, cmnTechn, sizeof(*cmnTechn->diffuse->color)); glm_vec4_copy(GLM_VEC4_ONE, cmnTechn->diffuse->color->vec); glm_vec4_copy(GLM_VEC4_ONE, specularColor->color->vec); jval = jspec->value; while (jval) { if (json_key_eq(jval, _s_gltf_diffuseFactor)) { json_array_float(cmnTechn->diffuse->color->vec, jval, 0.0f, 4, true); } else if (json_key_eq(jval, _s_gltf_specFactor)) { json_array_float(specularColor->color->vec, jval, 0.0f, 3, true); specularColor->color->vec[3] = 1.0f; } else if (json_key_eq(jval, _s_gltf_glossFactor)) { specularProp->strength = json_float(jval, 1.0f); } else if (json_key_eq(jval, _s_gltf_diffuseTexture)) { cmnTechn->diffuse->texture = gltf_materialTexRef(gst, cmnTechn, jval, AK_TEXTURE_COLORSPACE_SRGB, AK_TEXTURE_CHANNEL_RGBA); } else if (json_key_eq(jval, _s_gltf_specGlossTex)) { specularProp->specularTex = gltf_materialTexRef(gst, cmnTechn, jval, AK_TEXTURE_COLORSPACE_SRGB, AK_TEXTURE_CHANNEL_RGBA); } jval = jval->next; } } } /* _s_gltf_extensions */ while (jmatVal) { /* Metallic Roughness */ if (json_key_eq(jmatVal, _s_gltf_name)) { mat->name = json_strdup(jmatVal, heap, mat); } else if (json_key_eq(jmatVal, _s_gltf_pbrMetalRough)) { AkMaterialMetallicProp *metalness, *roughness; json_t *jmrVal; metalness = ak_heap_calloc(heap, cmnTechn, sizeof(*metalness)); roughness = ak_heap_calloc(heap, cmnTechn, sizeof(*roughness)); metalness->intensity = 1.0f; roughness->intensity = 1.0f; cmnTechn->metalness = metalness; cmnTechn->roughness = roughness; if (!cmnTechn->albedo) { cmnTechn->albedo = ak_heap_calloc(heap, cmnTechn, sizeof(*cmnTechn->albedo)); } cmnTechn->albedo->color = ak_heap_calloc(heap, cmnTechn, sizeof(*cmnTechn->albedo->color)); /* DEFAULT value by spec */ glm_vec4_copy(GLM_VEC4_ONE, cmnTechn->albedo->color->vec); jmrVal = jmatVal->value; while (jmrVal) { if (json_key_eq(jmrVal, _s_gltf_baseColor)) { json_array_float(cmnTechn->albedo->color->vec, jmrVal, 0.0f, 4, true); } else if (json_key_eq(jmrVal, _s_gltf_metalFac)) { metalness->intensity = json_float(jmrVal, 0.0f); } else if (json_key_eq(jmrVal, _s_gltf_roughFac)) { roughness->intensity = json_float(jmrVal, 0.0f); } else if (json_key_eq(jmrVal, _s_gltf_metalRoughTex)) { metalness->tex = gltf_materialTexRef(gst, metalness, jmrVal, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_GB); metalness->textureChannels = AK_TEXTURE_CHANNEL_B; roughness->tex = metalness->tex; roughness->textureChannels = AK_TEXTURE_CHANNEL_G; } else if (json_key_eq(jmrVal, _s_gltf_baseColorTex)) { cmnTechn->albedo->texture = gltf_materialTexRef(gst, cmnTechn->albedo, jmrVal, AK_TEXTURE_COLORSPACE_SRGB, AK_TEXTURE_CHANNEL_RGBA); } jmrVal = jmrVal->next; } } else if (json_key_eq(jmatVal, _s_gltf_emissiveFac)) { AkMaterialEmissionProp *emission; AkColor *color; if (!(emission = cmnTechn->emission)) { emission = ak_heap_calloc(heap, technfx, sizeof(*emission)); emission->strength = 1.0f; cmnTechn->emission = emission; } if (!(color = emission->color.color)) { emission->color.color = color = ak_heap_calloc(heap, emission, sizeof(*color)); } json_array_float(color->vec, jmatVal, 0.0f, 3, true); color->vec[3] = 1.0f; } else if (json_key_eq(jmatVal, _s_gltf_emissiveTex)) { AkMaterialEmissionProp *emission; if (!(emission = cmnTechn->emission)) { emission = ak_heap_calloc(heap, technfx, sizeof(*emission)); emission->strength = 1.0f; cmnTechn->emission = emission; } emission->color.texture = gltf_materialTexRef(gst, emission, jmatVal, AK_TEXTURE_COLORSPACE_SRGB, AK_TEXTURE_CHANNEL_RGB); } else if (json_key_eq(jmatVal, _s_gltf_occlusionTex)) { /* Occlusion Map */ AkOcclusion *occl; occl = ak_heap_calloc(heap, technfx, sizeof(*occl)); occl->tex = gltf_materialTexRef(gst, occl, jmatVal, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_R); occl->strength = json_float(json_get(jmatVal, _s_gltf_strength), 1.0f); occl->textureChannels = AK_TEXTURE_CHANNEL_R; cmnTechn->occlusion = occl; } else if (json_key_eq(jmatVal, _s_gltf_normalTex)) { /* Normap Map */ AkNormalMap *normal; normal = ak_heap_calloc(heap, technfx, sizeof(*normal)); normal->tex = gltf_materialTexRef(gst, normal, jmatVal, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_RGB); normal->scale = json_float(json_get(jmatVal, _s_gltf_scale), 1.0f); cmnTechn->normal = normal; } else if (json_key_eq(jmatVal, _s_gltf_doubleSided)) { /* doubleSided */ cmnTechn->doubleSided = json_bool(jmatVal, 0); } else if (json_key_eq(jmatVal, _s_gltf_alphaMode)) { AkTransparent *transp; if (!(transp = cmnTechn->transparent)) { transp = ak_heap_calloc(heap, cmnTechn, sizeof(*transp)); transp->amount = 1.0f; transp->cutoff = 0.5f; transp->opaque = AK_OPAQUE_OPAQUE; cmnTechn->transparent = transp; } transp->opaque = gltf_alphaMode(jmatVal); } else if (json_key_eq(jmatVal, _s_gltf_alphaCutoff)) { AkTransparent *transp; if (!(transp = cmnTechn->transparent)) { transp = ak_heap_calloc(heap, cmnTechn, sizeof(*transp)); transp->amount = 1.0f; transp->cutoff = 0.5f; transp->opaque = AK_OPAQUE_OPAQUE; cmnTechn->transparent = transp; } transp->cutoff = json_float(jmatVal, 0.5f); } jmatVal = jmatVal->next; } technfx->common = cmnTechn; ieff = ak_heap_calloc(heap, mat, sizeof(*ieff)); ieff->base.type = AK_INSTANCE_EFFECT; ieff->base.url.ptr = effect; mat->effect = ieff; mat->base.next = libmat->chld; libmat->chld = (void *)mat; libmat->count++; jmaterial = jmaterial->next; } } ================================================ FILE: src/io/gltf/imp/core/material.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_imp_core_material_h #define gltf_imp_core_material_h #include "../common.h" AK_HIDE void gltf_materials(json_t * __restrict json, void * __restrict userdata); #endif /* gltf_imp_core_material_h */ ================================================ FILE: src/io/gltf/imp/core/mesh.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "mesh.h" #include "enum.h" #include "accessor.h" #include "buffer.h" #include "ext.h" #include "../extra.h" #include "../../../../accessor.h" #include "../../../common/util.h" #include /* glTF meshes -> AkGeometry > AkMesh glTF primitives -> AkMeshPrimitive */ static bool gltf_attrIndexedSemantic(const char * __restrict key, size_t keysize, const char ** __restrict semantic) { const char *sep, *end, *c; if (!key || keysize == 0) return false; sep = memchr(key, '_', keysize); end = key + keysize; if (!sep || sep == key || sep + 1 >= end) return false; for (c = sep + 1; c < end; c++) { if (*c < '0' || *c > '9') return false; } if (semantic) *semantic = sep; return true; } static void gltf_inputSemantic(AkHeap * __restrict heap, AkInput * __restrict inp, json_t * __restrict jattrib) { const char *semantic; if (gltf_attrIndexedSemantic(jattrib->key, jattrib->keysize, &semantic)) { inp->semanticRaw = ak_heap_strndup(heap, inp, jattrib->key, semantic - jattrib->key); inp->set = (uint32_t)strtol(semantic + 1, NULL, 10); } else { inp->semanticRaw = ak_heap_strndup(heap, inp, jattrib->key, jattrib->keysize); } } static AkMorphPreset* gltf_meshMorphPresets(AkGLTFState * __restrict gst, void * __restrict parent, json_t * __restrict jpresets, uint32_t * __restrict count) { json_array_t *jarr; json_t *jpreset; json_t *jname; json_t *jweights; json_array_t *jwarr; AkMorphPreset *presets; AkMorphPreset *preset; AkFloatArray *weights; AkHeap *heap; uint32_t n, i; *count = 0; if (!(jarr = json_array(jpresets)) || jarr->count == 0) return NULL; heap = gst->heap; n = (uint32_t)jarr->count; presets = ak_heap_calloc(heap, parent, sizeof(*presets) * n); jpreset = jarr->base.value; i = 0; while (jpreset && i < n) { preset = &presets[n - 1 - i]; if ((jname = json_get(jpreset, _s_gltf_name))) preset->name = json_strdup(jname, heap, parent); if ((jweights = json_get(jpreset, _s_gltf_weights)) && (jwarr = json_array(jweights)) && jwarr->count > 0) { weights = ak_heap_alloc(heap, parent, sizeof(*weights) + sizeof(float) * jwarr->count); json_array_float(weights->items, jweights, 0.0f, jwarr->count, true); weights->count = jwarr->count; preset->weights = weights; } i++; jpreset = jpreset->next; } *count = n; return presets; } AK_HIDE void gltf_meshes(json_t * __restrict jmesh, void * __restrict userdata) { AkGLTFState *gst; AkHeap *heap; AkDoc *doc; AkLibrary *lib; const json_array_t *jmeshes; const json_t *jmeshVal; if (!(jmeshes = json_array(jmesh))) return; gst = userdata; heap = gst->heap; doc = gst->doc; lib = ak_heap_calloc(heap, doc, sizeof(*lib)); jmesh = jmeshes->base.value; while (jmesh) { AkGeometry *geom; AkMesh *mesh; AkObject *meshObj; uint32_t mode; /* mesh-level morph state — built lazily on first primitive that has targets, then re-used by subsequent primitives so that all primitives of the mesh share one AkMorph (per glTF spec, weights are mesh-level). */ AkMorph *meshMorph = NULL; AkMorphTarget **meshTargetArr = NULL; uint32_t meshTargetCnt = 0; /* mesh.extras.targetNames (blend shape names) — parsed independently of primitive order; attached to meshMorph after all keys are processed. */ const char **morphTargetNames = NULL; uint32_t morphTargetNamesN = 0; AkMorphPreset *morphPresets = NULL; uint32_t morphPresetCount = 0; uint32_t morphPresetIdx; uint32_t morphPresetWrite; mesh = ak_allocMesh(heap, lib, &geom); meshObj = ak_objFrom(mesh); mesh->primitiveCount = 0; gltf_extra(gst, meshObj, json_get(jmesh, _s_gltf_extras), json_get(jmesh, _s_gltf_extensions)); mesh->extra = ak_extra(meshObj); jmeshVal = jmesh->value; while (jmeshVal) { if (json_key_eq(jmeshVal, _s_gltf_primitives) && json_is_array(jmeshVal)) { json_t *jprim; jprim = jmeshVal->value; while (jprim) { AkMeshPrimitive *prim; json_t *jprimVal; mode = json_int32(json_get(jprim, _s_gltf_mode), 4); prim = gltf_allocPrim(heap, meshObj, mode); prim->input = NULL; prim->inputCount = 0; prim->mesh = mesh; gltf_extra(gst, prim, json_get(jprim, _s_gltf_extras), json_get(jprim, _s_gltf_extensions)); prim->extra = ak_extra(prim); jprimVal = jprim->value; while (jprimVal) { if (json_key_eq(jprimVal, _s_gltf_attributes)) { json_t *jattrib; /* attributes */ jattrib = jprimVal->value; while (jattrib) { AkInput *inp; inp = ak_heap_calloc(heap, prim, sizeof(*inp)); gltf_inputSemantic(heap, inp, jattrib); inp->semantic = gltf_enumInputSemantic(inp->semanticRaw); inp->accessor = flist_sp_at(&doc->lib.accessors, json_int32(jattrib, -1)); if (!inp->accessor) { jattrib = jattrib->next; continue; } if (inp->semantic == AK_INPUT_POSITION) prim->pos = inp; inp->next = prim->input; prim->input = inp; prim->inputCount++; jattrib = jattrib->next; } /* jprimAttrib */ } else if (json_key_eq(jprimVal, _s_gltf_indices)) { AkAccessor *acc; AkBuffer *indicesBuff; AkUIntArray *indices; AkUInt *it1; char *it2; size_t count, k, itemSize; if (!(acc = flist_sp_at(&doc->lib.accessors, json_int32(jprimVal, -1))) || !(indicesBuff = acc->buffer)) goto prim_next; itemSize = acc->bytesPerComponent; count = acc->count; indices = ak_heap_calloc(heap, prim, sizeof(*indices) + sizeof(AkUInt) * count); indices->count = count; it1 = indices->items; it2 = ((char *)indicesBuff->data) + acc->byteOffset; /* we cannot use memcpy here, because we will promote short, byte type to int32 (for now) */ for (k = 0; k < count; k++) { memcpy(&it1[k], it2 + itemSize * k, itemSize); } prim->indices = indices; prim->indexStride = 1; } else if (json_key_eq(jprimVal, _s_gltf_material)) { AkMaterial *mat; int32_t matIndex; matIndex = json_int32(jprimVal, -1); GETCHILD(gst->doc->lib.materials->chld, mat, matIndex); if (mat) { prim->material = mat; } else { prim->material = gst->defaultMaterial; } } else if (json_key_eq(jprimVal, _s_gltf_targets)) { json_array_t *jtargets; json_t *jtarget, *jattrib; AkMorphTarget *target; AkMorphable *morphable; uint32_t targetIdx; if (!(jtargets = json_array(jprimVal))) goto prmv_nxt; /* Lazy-init the mesh-level morph and pre-allocate one AkMorphTarget per blend shape. All primitives of the mesh share these targets — each adds one AkMorphable to each target's chain. (glTF spec: every primitive in the mesh has the same number of morph targets in the same order.) */ if (!meshMorph) { AkMorphTarget *prev; uint32_t i; meshMorph = ak_heap_calloc(heap, doc, sizeof(*meshMorph)); meshMorph->method = AK_MORPH_METHOD_ADDITIVE; meshTargetCnt = (uint32_t)jtargets->count; meshTargetArr = ak_heap_calloc(heap, meshMorph, sizeof(*meshTargetArr) * meshTargetCnt); prev = NULL; for (i = 0; i < meshTargetCnt; i++) { target = ak_heap_calloc(heap, meshMorph, sizeof(*target)); meshTargetArr[i] = target; if (prev) prev->next = target; else meshMorph->target = target; prev = target; meshMorph->targetCount++; } } /* For this primitive, prepend one AkMorphable to each blend shape's chain (one per target). The first primitive's morphable is wrapped in an AkObject (carries the type tag used by intr.c switch dispatch); subsequent primitives' morphables are plain calloc'd and linked via .next. The JSON parser walks the targets array in reverse source order (an artifact of the reader's reverse-prepend strategy). The pre-allocated meshTargetArr[] is in chain (forward) order, so we mirror the index when filling: the first jtarget we visit (source LAST) populates the last chain slot, and the last jtarget visited (source FIRST) populates chain[0] — i.e. meshMorph->target ends up = source targets[0], matching glTF semantics where weight[i] drives mesh.primitives[].targets[i]. Without this swap, weight[0] animates the wrong blend shape and the morph visually plays in reverse target order. Primitive nodes below are linked with head-prepend. Prepending morphables here keeps each target's morphable chain in the same order as mesh->primitive, so inspect can walk both chains lock-step for multi-primitive morphs. */ jtarget = jtargets->base.value; targetIdx = 0; while (jtarget && targetIdx < meshTargetCnt) { target = meshTargetArr[meshTargetCnt - 1 - targetIdx]; if (!target->target) { AkObject *targetObj; targetObj = ak_objAlloc(heap, target, sizeof(*morphable), AK_MORPHABLE_MORPHABLE, true); morphable = ak_objGet(targetObj); target->target = targetObj; } else { AkMorphable *head, *oldHead; head = ak_objGet(target->target); oldHead = ak_heap_calloc(heap, target, sizeof(*oldHead)); memcpy(oldHead, head, sizeof(*oldHead)); memset(head, 0, sizeof(*head)); head->next = oldHead; morphable = head; } target->primitiveCount++; /* fill morphable->input from the target's attribute deltas. IMPORTANT: do NOT touch prim->pos here — that's the base primitive's POSITION binding; target POSITION is a delta. */ jattrib = jtarget->value; while (jattrib) { AkInput *inp; inp = ak_heap_calloc(heap, prim, sizeof(*inp)); gltf_inputSemantic(heap, inp, jattrib); inp->semantic = gltf_enumInputSemantic(inp->semanticRaw); inp->accessor = flist_sp_at(&doc->lib.accessors, json_int32(jattrib, -1)); if (!inp->accessor) { jattrib = jattrib->next; continue; } inp->next = morphable->input; morphable->input = inp; morphable->inputCount++; jattrib = jattrib->next; } /* jattrib */ jtarget = jtarget->next; targetIdx++; } /* jtarget */ } else if (json_key_eq(jprimVal, _s_gltf_extensions)) { if (!gltf_ext_dracoPrimitive(gst, prim, jprim)) { gst->stop = true; return; } if (!gltf_ext_primitiveVariants(gst, prim, jprim) || !gltf_ext_primitiveGaussianSplat(gst, prim, jprim)) { gst->stop = true; return; } } prmv_nxt: jprimVal = jprimVal->next; } prim->next = mesh->primitive; mesh->primitive = prim; mesh->primitiveCount++; prim->nPolygons = gltf_polyCount(prim, mode); if (!prim->material) { prim->material = gst->defaultMaterial; } prim_next: jprim = jprim->next; } } else if (json_key_eq(jmeshVal, _s_gltf_weights)) { AkFloatArray *weights; json_array_t *jarr; if ((jarr = json_array(jmeshVal))) { weights = ak_heap_alloc(heap, meshObj, sizeof(*weights) + sizeof(float) * jarr->count); /* Pass jmeshVal (the array NODE), not jmeshVal->value (its first child). json_array_float internally casts arg via json_array(). */ json_array_float(weights->items, jmeshVal, 0.0f, jarr->count, true); weights->count = jarr->count; mesh->weights = weights; } } else if (json_key_eq(jmeshVal, _s_gltf_name)) { mesh->name = json_strdup(jmeshVal, heap, meshObj); } else if (json_key_eq(jmeshVal, _s_gltf_extras)) { json_t *jnames; json_t *jpresets; json_array_t *jarr; if ((jnames = json_get(jmeshVal, _s_gltf_targetNames)) && (jarr = json_array(jnames)) && jarr->count > 0) { json_t *jname; uint32_t i; morphTargetNamesN = (uint32_t)jarr->count; morphTargetNames = ak_heap_calloc(heap, meshObj, sizeof(const char *) * morphTargetNamesN); jname = jarr->base.value; i = 0; while (jname && i < morphTargetNamesN) { morphTargetNames[morphTargetNamesN - 1 - i] = json_strdup(jname, heap, meshObj); i++; jname = jname->next; } } if ((jpresets = json_get(jmeshVal, _s_gltf_morphPresets))) { morphPresets = gltf_meshMorphPresets(gst, meshObj, jpresets, &morphPresetCount); } } jmeshVal = jmeshVal->next; } /* Register the mesh-level morph once all keys are processed, so that extras.targetNames (which can come after primitives in JSON order) is attached as well. Keyed by geometry so node.c finds it via meshTargets. */ if (meshMorph) { if (morphTargetNames && morphTargetNamesN > 0) { meshMorph->targetNames = morphTargetNames; } if (morphPresets && morphPresetCount > 0) { morphPresetWrite = 0; for (morphPresetIdx = 0; morphPresetIdx < morphPresetCount; morphPresetIdx++) { if (!morphPresets[morphPresetIdx].weights || morphPresets[morphPresetIdx].weights->count != meshMorph->targetCount) continue; if (morphPresetWrite != morphPresetIdx) morphPresets[morphPresetWrite] = morphPresets[morphPresetIdx]; morphPresetWrite++; } if (morphPresetWrite > 0) { meshMorph->presets = morphPresets; meshMorph->presetCount = morphPresetWrite; } } if (doc->lib.morphs) meshMorph->base.next = &doc->lib.morphs->base; doc->lib.morphs = meshMorph; rb_insert(gst->meshTargets, geom, meshMorph); } /* Reversed */ geom->base.next = lib->chld; lib->chld = (void *)geom; lib->count++; jmesh = jmesh->next; } doc->lib.geometries = lib; } AK_HIDE AkMeshPrimitive* gltf_allocPrim(AkHeap * __restrict heap, void * __restrict memParent, int mode) { switch (mode) { case 0: { AkMeshPrimitive *prim; prim = ak_heap_calloc(heap, memParent, sizeof(*prim)); prim->type = AK_PRIMITIVE_POINTS; return prim; } case 1: { AkLines *lines; lines = ak_heap_calloc(heap, memParent, sizeof(*lines)); lines->base.type = AK_PRIMITIVE_LINES; lines->mode = AK_LINES; return &lines->base; } case 2: { AkLines *lines; lines = ak_heap_calloc(heap, memParent, sizeof(*lines)); lines->base.type = AK_PRIMITIVE_LINES; lines->mode = AK_LINE_LOOP; return &lines->base; } case 3: { AkLines *lines; lines = ak_heap_calloc(heap, memParent, sizeof(*lines)); lines->base.type = AK_PRIMITIVE_LINES; lines->mode = AK_LINE_STRIP; return &lines->base; } case 4: { AkTriangles *tri; tri = ak_heap_calloc(heap, memParent, sizeof(*tri)); tri->base.type = AK_PRIMITIVE_TRIANGLES; tri->mode = AK_TRIANGLES; return &tri->base; } case 5: { AkTriangles *tri; tri = ak_heap_calloc(heap, memParent, sizeof(*tri)); tri->base.type = AK_PRIMITIVE_TRIANGLES; tri->mode = AK_TRIANGLE_STRIP; return &tri->base; } case 6: { AkTriangles *tri; tri = ak_heap_calloc(heap, memParent, sizeof(*tri)); tri->base.type = AK_PRIMITIVE_TRIANGLES; tri->mode = AK_TRIANGLE_FAN; return &tri->base; } } return NULL; } AK_HIDE uint32_t gltf_polyCount(AkMeshPrimitive *prim, uint32_t mode) { AkInput *pos; AkAccessor *acc; uint32_t n; if (prim->indices) { n = (uint32_t)prim->indices->count; } else if ((pos = prim->pos) && (acc = pos->accessor)) { n = (uint32_t)acc->count; } else { goto err; } switch (mode) { /* 0: points, 2: line loops */ case 0: case 2: return n; /* 1: lines */ case 1: return n * 0.5; /* 3: line strip */ case 3: return n - 1; /* 4: triangles */ case 4: return n / 3; /* 5: triangle strip, 6: triangle fan */ case 5: case 6: return n - 2; } err: return 0; } ================================================ FILE: src/io/gltf/imp/core/mesh.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_imp_core_mesh_h #define gltf_imp_core_mesh_h #include "../common.h" AK_HIDE void gltf_meshes(json_t * __restrict json, void * __restrict userdata); AK_HIDE AkMeshPrimitive* gltf_allocPrim(AkHeap * __restrict heap, void * __restrict memParent, int mode); AK_HIDE uint32_t gltf_polyCount(AkMeshPrimitive *prim, uint32_t mode); #endif /* gltf_imp_core_mesh_h */ ================================================ FILE: src/io/gltf/imp/core/node.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "node.h" #include "ext.h" #include "../extra.h" #include "../../../../id.h" #include #include #define k_name 0 #define k_camera 1 #define k_mesh 2 #define k_skin 3 #define k_children 4 #define k_matrix 5 #define k_translation 6 #define k_rotation 7 #define k_scale 8 #define k_weights 9 #define k_extensions 10 #define k_extras 11 AK_HIDE void gltf_nodes(json_t * __restrict jnode, void * __restrict userdata) { AkGLTFState *gst; AkHeap *heap; AkDoc *doc; AkLibrary *lib; AkNode *node; const json_array_t *jnodes; FListItem *nodes; AkNode **nodechld, *parentNode; char nodeid[32]; int i, jnodeCount2; if (!(jnodes = json_array(jnode))) return; gst = userdata; heap = gst->heap; doc = gst->doc; lib = ak_heap_calloc(heap, doc, sizeof(*lib)); nodechld = ak_calloc(NULL, sizeof(*nodechld) * jnodes->count * 2); nodes = NULL; jnode = jnodes->base.value; i = jnodes->count - 1; jnodeCount2 = jnodes->count * 2; while (jnode) { nodechld[i * 2] = node = gltf_node(gst, lib, jnode, nodechld); /* JSON parse is reverse */ sprintf(nodeid, "%s%d", _s_gltf_node, i); ak_heap_setId(heap, ak__alignof(node), ak_heap_strdup(heap, node, nodeid)); i--; jnode = jnode->next; } for (i = jnodeCount2 - 2; i >= 0; i -= 2) { node = nodechld[i]; /* this node has parent node, move this into parent children link. */ if ((parentNode = nodechld[i + 1])) { AkNode *chld; chld = parentNode->chld; if (chld) { chld->prev = node; node->next = chld; } parentNode->chld = node; node->parent = parentNode; /* Keep heap ownership in the node library; parent is logical scene graph. */ } /* it is root node, add to library_nodes */ else { node->next = (void *)lib->chld; lib->chld = (void *)node; lib->count++; } } flist_sp_destroy(&nodes); ak_free(nodechld); doc->lib.nodes = lib; } AK_HIDE AkNode* gltf_node(AkGLTFState * __restrict gst, void * __restrict memParent, json_t * __restrict jnode, AkNode ** __restrict nodechld) { AkHeap *heap; AkNode *node; AkGeometry *geomIter; AkInstanceGeometry *instGeom; void *it; AkMorph *morph; int32_t i32val; heap = gst->heap; geomIter = NULL; instGeom = NULL; node = ak_heap_calloc(heap, memParent, sizeof(*node)); ak_setypeid(node, AKT_NODE); node->visible = true; json_objmap_t nodeMap[] = { JSON_OBJMAP_OBJ(_s_gltf_name, I2P k_name), JSON_OBJMAP_OBJ(_s_gltf_camera, I2P k_camera), JSON_OBJMAP_OBJ(_s_gltf_mesh, I2P k_mesh), JSON_OBJMAP_OBJ(_s_gltf_skin, I2P k_skin), JSON_OBJMAP_OBJ(_s_gltf_children, I2P k_children), JSON_OBJMAP_OBJ(_s_gltf_matrix, I2P k_matrix), JSON_OBJMAP_OBJ(_s_gltf_translation, I2P k_translation), JSON_OBJMAP_OBJ(_s_gltf_rotation, I2P k_rotation), JSON_OBJMAP_OBJ(_s_gltf_scale, I2P k_scale), JSON_OBJMAP_OBJ(_s_gltf_weights, I2P k_weights), JSON_OBJMAP_OBJ(_s_gltf_extensions, I2P k_extensions), JSON_OBJMAP_OBJ(_s_gltf_extras, I2P k_extras) }; json_objmap(jnode, nodeMap, JSON_ARR_LEN(nodeMap)); if ((it = nodeMap[k_name].object)) { node->name = json_strdup(it, heap, node); } gltf_extra(gst, node, nodeMap[k_extras].object, nodeMap[k_extensions].object); if ((it = nodeMap[k_extensions].object) && !gltf_ext_node(gst, node, it)) { gst->stop = true; return node; } if (gst->doc->lib.cameras && (i32val = json_int32(nodeMap[k_camera].object, -1)) > -1) { AkCamera *camIter; GETCHILD(gst->doc->lib.cameras->chld, camIter, i32val); if (camIter) { AkInstanceBase *instCamera; instCamera = ak_heap_calloc(heap, node, sizeof(*instCamera)); instCamera->node = node; instCamera->type = AK_INSTANCE_CAMERA; instCamera->url.ptr = camIter; node->camera = instCamera; } } /* instance geometries */ if ((i32val = json_int32(nodeMap[k_mesh].object, -1)) > -1) { GETCHILD(gst->doc->lib.geometries->chld, geomIter, i32val); /* instance geometry */ if (geomIter) { instGeom = ak_heap_calloc(heap, node, sizeof(*instGeom)); instGeom->base.node = node; instGeom->base.type = AK_INSTANCE_GEOMETRY; instGeom->base.url.ptr = geomIter; node->geometry = instGeom; } /* if (geomIter) */ } /* children */ if ((it = nodeMap[k_children].object)) { json_array_t *jchildren; json_t *jchld; int chldIndex; if ((jchildren = json_array(it))) { jchld = jchildren->base.value; while (jchld) { if ((chldIndex = json_int32(jchld, -1)) > -1) { chldIndex = chldIndex * 2 + 1; if (!nodechld[chldIndex]) { nodechld[chldIndex] = node; } /* else: this node is already child of another, it cannot be child of two node at same time */ } jchld = jchld->next; } } /* if children */ } /* first parsed is added to the end so TRS. */ /* matrix */ if ((it = nodeMap[k_matrix].object)) { AkObject *obj; AkMatrix *matrix; obj = ak_objAlloc(heap, node, sizeof(*matrix), AKT_MATRIX, true); matrix = ak_objGet(obj); json_array_float(matrix->val[0], it, 0.0f, 16, true); if (!node->transform) { node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform)); } obj->next = node->transform->item; node->transform->item = obj; } /* scale */ if ((it = nodeMap[k_scale].object)) { AkObject *obj; AkScale *scale; obj = ak_objAlloc(heap, node, sizeof(*obj), AKT_SCALE, true); scale = ak_objGet(obj); json_array_float(scale->val, it, 0.0f, 3, true); if (!node->transform) { node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform)); } obj->next = node->transform->item; node->transform->item = obj; } /* rotation */ if ((it = nodeMap[k_rotation].object)) { AkObject *obj; AkQuaternion *rot; obj = ak_objAlloc(heap, node, sizeof(*obj), AKT_QUATERNION, true); rot = ak_objGet(obj); json_array_float(rot->val, it, 0.0f, 4, true); if (!node->transform) { node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform)); } obj->next = node->transform->item; node->transform->item = obj; } /* translation */ if ((it = nodeMap[k_translation].object)) { AkObject *obj; AkTranslate *translate; obj = ak_objAlloc(heap, node, sizeof(*translate), AKT_TRANSLATE, true); translate = ak_objGet(obj); json_array_float(translate->val, it, 0.0f, 3, true); if (!node->transform) { node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform)); } obj->next = node->transform->item; node->transform->item = obj; } /* morph instance + (optional) node-level weight override */ if (geomIter && instGeom && (morph = rb_find(gst->meshTargets, geomIter))) { AkInstanceMorph *morpher; morpher = ak_heap_calloc(heap, node, sizeof(*morpher)); ak_setypeid(morpher, AKT_MORPH_INST); morpher->morph = morph; instGeom->morpher = morpher; /* overrideWeights is set ONLY when the glTF node carries an explicit "weights" property. Otherwise it stays NULL so that: - ak_morphHasOverride() returns false - defaults flow through: morph.defaultWeights → mesh.weights → 0 Allocating a zero-filled array unconditionally would silently override any defaults — see ak_morphInspect_initialWeight precedence. */ if ((it = json_array(nodeMap[k_weights].object))) { AkFloatArray *weights; json_array_t *jsonArr; jsonArr = it; weights = ak_heap_calloc(heap, morpher, sizeof(*weights) + sizeof(weights->items[0]) * morph->targetCount); json_array_float(weights->items, it, 0.0f, jsonArr->count, true); weights->count = morph->targetCount; morpher->overrideWeights = weights; } } /* bind skinnerr after skin is loaded */ if (instGeom && (i32val = json_int32(nodeMap[k_skin].object, -1)) > -1) { rb_insert(gst->skinBound, node, I2P i32val); /* TODO: what if there is no Geomerty? */ } return node; } ================================================ FILE: src/io/gltf/imp/core/node.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_imp_core_node_h #define gltf_imp_core_node_h #include "../common.h" AK_HIDE void gltf_nodes(json_t * __restrict jnode, void * __restrict userdata); AK_HIDE AkNode* gltf_node(AkGLTFState * __restrict gst, void * __restrict memParent, json_t * __restrict jnode, AkNode ** __restrict nodechld); AK_HIDE void gltf_bindMaterials(AkGLTFState * __restrict gst, AkInstanceGeometry * __restrict instGeom, int32_t meshIndex); #endif /* gltf_imp_core_node_h */ ================================================ FILE: src/io/gltf/imp/core/profile.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "profile.h" AK_HIDE AkProfileCommon* gltf_cmnEffect(AkGLTFState * __restrict gst) { AkLibrary *lib; AkEffect *effect; AkProfileCommon *profile; if (!(lib = gst->doc->lib.effects)) { lib = ak_heap_calloc(gst->heap, gst->doc, sizeof(*lib)); gst->doc->lib.effects = lib; } effect = ak_heap_calloc(gst->heap, lib, sizeof(*effect)); profile = ak_heap_calloc(gst->heap, effect, sizeof(*profile)); profile->type = AK_PROFILE_TYPE_COMMON; lib->count++; effect->profile = profile; effect->next = (void *)lib->chld; lib->chld = (void *)effect; ak_setypeid(profile, AKT_PROFILE); ak_setypeid(effect, AKT_EFFECT); return effect->profile; } ================================================ FILE: src/io/gltf/imp/core/profile.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_imp_core_profile_h #define gltf_imp_core_profile_h #include "../common.h" AK_HIDE AkProfileCommon* gltf_cmnEffect(AkGLTFState * __restrict gst); #endif /* gltf_imp_core_profile_h */ ================================================ FILE: src/io/gltf/imp/core/sampler.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "sampler.h" #include "profile.h" #include "enum.h" #include "../extra.h" AK_HIDE void gltf_samplers(json_t * __restrict jsampler, void * __restrict userdata) { AkGLTFState *gst; const json_array_t *jsamplers; const json_t *jsamplerVal; AkSampler *sampler; if (!(jsamplers = json_array(jsampler))) return; gst = userdata; jsampler = jsamplers->base.value; while (jsampler) { jsamplerVal = jsampler->value; sampler = ak_heap_calloc(gst->heap, gst->doc, sizeof(*sampler)); sampler->wrapS = AK_WRAP_MODE_WRAP; sampler->wrapT = AK_WRAP_MODE_WRAP; ak_setypeid(sampler, AKT_SAMPLER2D); gltf_extra(gst, sampler, json_get(jsampler, _s_gltf_extras), json_get(jsampler, _s_gltf_extensions)); while (jsamplerVal) { if (json_key_eq(jsamplerVal, _s_gltf_wrapS)) { sampler->wrapS = gltf_wrapMode(json_int32(jsamplerVal, AK_WRAP_MODE_WRAP)); } else if (json_key_eq(jsamplerVal, _s_gltf_wrapT)) { sampler->wrapT = gltf_wrapMode(json_int32(jsamplerVal, AK_WRAP_MODE_WRAP)); } else if (json_key_eq(jsamplerVal, _s_gltf_minFilter)) { sampler->minfilter = gltf_minFilter(json_int32(jsamplerVal, 0)); } else if (json_key_eq(jsamplerVal, _s_gltf_magFilter)) { sampler->magfilter = gltf_magFilter(json_int32(jsamplerVal, 0)); } else if (json_key_eq(jsamplerVal, _s_gltf_name)) { sampler->name = json_strdup(jsamplerVal, gst->heap, sampler); } jsamplerVal = jsamplerVal->next; } flist_sp_insert(&gst->doc->lib.samplers, sampler); jsampler = jsampler->next; } } ================================================ FILE: src/io/gltf/imp/core/sampler.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_imp_core_sampler_h #define gltf_imp_core_sampler_h #include "../common.h" AK_HIDE void gltf_samplers(json_t * __restrict jsampler, void * __restrict userdata); #endif /* gltf_imp_core_sampler_h */ ================================================ FILE: src/io/gltf/imp/core/scene.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "scene.h" #include "../extra.h" static void gltf_setFirstCamera(AkVisualScene *scene, AkNode *node); static AkNode * gltf_findNodeById(AkNode *node, const char *nodeid); static AkNode * gltf_nodeByIndex(AkDoc *doc, int32_t nodeIndex); AK_HIDE void gltf_scenes(json_t * __restrict jscene, void * __restrict userdata) { AkGLTFState *gst; AkHeap *heap; AkDoc *doc; const json_array_t *jscenes; AkLibrary *lib; if (!(jscenes = json_array(jscene))) return; gst = userdata; heap = gst->heap; doc = gst->doc; jscene = jscenes->base.value; lib = ak_heap_calloc(heap, doc, sizeof(*lib)); while (jscene) { AkVisualScene *scene; json_t *jsceneVal; jsceneVal = jscene->value; scene = ak_heap_calloc(heap, lib, sizeof(*scene)); ak_setypeid(scene, AKT_SCENE); gltf_extra(gst, scene, json_get(jscene, _s_gltf_extras), json_get(jscene, _s_gltf_extensions)); scene->cameras = ak_heap_calloc(heap, scene, sizeof(*scene->cameras)); /* root node: to store node instances */ scene->node = ak_heap_calloc(heap, scene, sizeof(*scene->node)); scene->node->visible = true; while (jsceneVal) { if (json_key_eq(jsceneVal, _s_gltf_name)) { scene->name = json_strdup(jsceneVal, heap, scene); } else if (json_key_eq(jsceneVal, _s_gltf_nodes)) { json_array_t *jnodes; json_t *jnode; int32_t nodeIndex; if (!(jnodes = json_array(jsceneVal))) goto scn_nxt; /* create instanceNode for each node */ jnode = jnodes->base.value; while (jnode) { AkNode *node; AkInstanceNode *instNode; instNode = ak_heap_calloc(heap, scene, sizeof(*instNode)); if ((nodeIndex = json_int32(jnode, -1)) < 0) goto jnode_nxt; if (!(node = gltf_nodeByIndex(doc, nodeIndex))) goto jnode_nxt; instNode->base.node = node; instNode->base.url.ptr = node; instNode->base.type = AK_INSTANCE_NODE; if (scene->node) { if (scene->node->node) instNode->base.next = &scene->node->node->base; scene->node->node = instNode; } if (!scene->firstCamNode) gltf_setFirstCamera(scene, node); jnode_nxt: jnode = jnode->next; } } jsceneVal = jsceneVal->next; } scn_nxt: scene->base.next = lib->chld; lib->chld = (void *)scene; lib->count++; jscene = jscene->next; } doc->lib.visualScenes = lib; } AK_HIDE void gltf_scene(json_t * __restrict jscene, void * __restrict userdata) { AkGLTFState *gst; AkHeap *heap; AkDoc *doc; AkVisualScene *scene; int32_t sceneIndex; gst = userdata; heap = gst->heap; doc = gst->doc; /* set default scene */ sceneIndex = json_int32(jscene, -1); GETCHILD(doc->lib.visualScenes->chld, scene, sceneIndex); /* set first scene as default scene if not specified */ if (scene) { AkInstanceBase *instScene; instScene = ak_heap_calloc(heap, doc, sizeof(*instScene)); instScene->url.ptr = scene; doc->scene.visualScene = instScene; } } static AkNode * gltf_findNodeById(AkNode *node, const char *nodeid) { const char *id; AkNode *found; while (node) { id = (const char *)ak_mem_getId(node); if (id && strcmp(id, nodeid) == 0) return node; if (node->chld && (found = gltf_findNodeById(node->chld, nodeid))) return found; node = node->next; } return NULL; } static AkNode * gltf_nodeByIndex(AkDoc *doc, int32_t nodeIndex) { char nodeid[16]; if (!doc || !doc->lib.nodes || nodeIndex < 0) return NULL; sprintf(nodeid, "%s%d", _s_gltf_node, nodeIndex); return gltf_findNodeById((AkNode *)doc->lib.nodes->chld, nodeid); } static void gltf_setFirstCamera(AkVisualScene *scene, AkNode *node) { if (node->camera) { if (!scene->firstCamNode) scene->firstCamNode = node; ak_instanceListAdd(scene->cameras, node->camera); return; } if (node->chld) { AkNode *nodei; nodei = node->chld; while (nodei) { gltf_setFirstCamera(scene, nodei); nodei = nodei->next; } } } ================================================ FILE: src/io/gltf/imp/core/scene.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_imp_core_scene_h #define gltf_imp_core_scene_h #include "../common.h" AK_HIDE void gltf_scenes(json_t * __restrict jscene, void * __restrict userdata); AK_HIDE void gltf_scene(json_t * __restrict jscene, void * __restrict userdata); #endif /* gltf_imp_core_scene_h */ ================================================ FILE: src/io/gltf/imp/core/skin.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "skin.h" #include "accessor.h" AK_HIDE void gltf_skin(json_t * __restrict jskin, void * __restrict userdata) { AkGLTFState *gst; AkHeap *heap; AkDoc *doc; const json_array_t *jskins; uint32_t skinIndex; if (!(jskins = json_array(jskin))) return; gst = userdata; heap = gst->heap; doc = gst->doc; jskin = jskins->base.value; skinIndex = jskins->count - 1; while (jskin) { AkSkin *skin; json_t *jskinVal; char skinid[16]; jskinVal = jskin->value; skin = ak_heap_calloc(heap, doc, sizeof(*skin)); sprintf(skinid, "%s%d", _s_gltf_skin, skinIndex); ak_heap_setId(heap, ak__alignof(skin), ak_heap_strdup(heap, skin, (void *)skinid)); glm_mat4_identity(skin->bindShapeMatrix); while (jskinVal) { if (json_key_eq(jskinVal, _s_gltf_inverseBindMatrices)) { AkAccessor *acc; AkBuffer *buff; float *pbuff; if ((acc = flist_sp_at(&doc->lib.accessors, json_int32(jskinVal, -1))) && (buff = acc->buffer)) { pbuff = (void *)((char *)buff->data + acc->byteOffset); skin->invBindPoses = ak_heap_alloc(heap, skin, acc->count * sizeof(mat4)); for (size_t k = 0; k < acc->count * 16; k++) { *((float *)skin->invBindPoses + k) = *(pbuff + k); } } } else if (json_key_eq(jskinVal, _s_gltf_joints)) { json_array_t *jjoints; json_t *jjoint; uint32_t j; if ((jjoints = json_array(jskinVal))) { skin->joints = ak_heap_alloc(heap, skin, sizeof(void **) * jjoints->count); jjoint = jjoints->base.value; j = jjoints->count - 1; /* json parser is reverse */ while (jjoint) { char nodeid[16]; AkNode *node; int32_t nodeIndex; if ((nodeIndex = json_int32(jjoint, -1)) > -1) { sprintf(nodeid, "%s%d", _s_gltf_node, nodeIndex); if ((node = ak_getObjectById(doc, nodeid))) { skin->joints[j] = node; } else { skin->joints[j] = NULL; } } else { skin->joints[j] = NULL; } j--; skin->nJoints++; jjoint = jjoint->next; } /* while (jjoint) */ } } else if (json_key_eq(jskinVal, _s_gltf_skeleton)) { /* glTF spec: optional hint to the closest common ancestor of the joints. Engines (Apple SCNSkinner.skeleton, Three.js Skeleton) use it as the coordinate-space reference. We resolve the node id to AkNode* — fall back to NULL if missing or unresolved (callers default to joints[0]). */ char skelid[16]; int32_t skelIndex; if ((skelIndex = json_int32(jskinVal, -1)) > -1) { sprintf(skelid, "%s%d", _s_gltf_node, skelIndex); skin->skeleton = ak_getObjectById(doc, skelid); } } /* if _s_gltf_joints */ jskinVal = jskinVal->next; } if (doc->lib.skins) skin->base.next = &doc->lib.skins->base; doc->lib.skins = skin; skinIndex--; jskin = jskin->next; } } ================================================ FILE: src/io/gltf/imp/core/skin.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_imp_core_skin_h #define gltf_imp_core_skin_h #include "../common.h" AK_HIDE void gltf_skin(json_t * __restrict jskin, void * __restrict userdata); #endif /* gltf_imp_core_skin_h */ ================================================ FILE: src/io/gltf/imp/core/texture.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "texture.h" #include "ext.h" #include "profile.h" #include "sampler.h" #include "../extra.h" AK_INLINE char* coordInputName(AkHeap * __restrict heap, void * __restrict parent, int set) { char *coordInputName; size_t len; if (set == 0) { coordInputName = ak_heap_strdup(heap, parent, _s_gltf_texcoordPrefix); } else { len = strlen(_s_gltf_texcoordPrefix) + ak_digitsize(set); coordInputName = ak_heap_alloc(heap, parent, len + 1); coordInputName[len] = '\0'; /* sprintf(coordInputName, "%s%d", _s_gltf_texcoordPrefix, set); */ snprintf(coordInputName, len + 1, "%s%d", _s_gltf_texcoordPrefix, set); } return coordInputName; } AK_HIDE AkTextureRef* gltf_texref(AkGLTFState * __restrict gst, void * __restrict parent, json_t * __restrict jtexinfo) { AkHeap *heap; AkDoc *doc; AkTextureRef *texref; AkTexture *tex; json_t *jext; int32_t texindex, set; heap = gst->heap; doc = gst->doc; texindex = json_int32(json_get(jtexinfo, _s_gltf_index), 0); set = json_int32(json_get(jtexinfo, _s_gltf_texCoord), 0); tex = flist_sp_at(&doc->lib.textures, texindex); texref = ak_heap_calloc(heap, parent, sizeof(*texref)); ak_setypeid(texref, AKT_TEXTURE_REF); gltf_extra(gst, texref, json_get(jtexinfo, _s_gltf_extras), json_get(jtexinfo, _s_gltf_extensions)); texref->coordInputName = coordInputName(heap, texref, set); if ((jext = json_get(jtexinfo, _s_gltf_extensions))) { json_t *jval; if ((jval = json_get(jext, _s_gltf_KHR_texture_transform))) { AkTextureTransform *texTransf; texTransf = ak_heap_calloc(heap, texref, sizeof(*texTransf)); texref->transform = texTransf; texTransf->slot = -1; texTransf->scale[0] = 1.0; texTransf->scale[1] = 1.0; jval = jval->value; while (jval) { if (json_key_eq(jval, _s_gltf_offset)) { json_array_float(texTransf->offset, jval, 0.0f, 2, true); } else if (json_key_eq(jval, _s_gltf_rotation)) { texTransf->rotation = json_float(jval, 0.0f); } else if (json_key_eq(jval, _s_gltf_scale)) { json_array_float(texTransf->scale, jval, 0.0f, 2, true); } else if (json_key_eq(jval, _s_gltf_texCoord)) { texTransf->slot = json_int32(jval, -1); texTransf->coordInputName = coordInputName(heap, texTransf, texTransf->slot); } jval = jval->next; } } } texref->texture = tex; texref->slot = set; return texref; } AK_HIDE void gltf_textures(json_t * __restrict jtex, void * __restrict userdata) { AkGLTFState *gst; AkDoc *doc; const json_array_t *jtextures; const json_t *jtexVal; AkTexture *tex; if (!(jtextures = json_array(jtex))) return; gst = userdata; doc = gst->doc; jtex = jtextures->base.value; while (jtex) { AkSampler *sampler; jtexVal = jtex->value; tex = ak_heap_calloc(gst->heap, gst->doc, sizeof(*tex)); tex->type = AKT_SAMPLER2D; sampler = NULL; gltf_extra(gst, tex, json_get(jtex, _s_gltf_extras), json_get(jtex, _s_gltf_extensions)); while (jtexVal) { if (json_key_eq(jtexVal, _s_gltf_sampler)) { sampler = flist_sp_at(&doc->lib.samplers, json_int32(jtexVal, -1)); } else if (json_key_eq(jtexVal, _s_gltf_source)) { tex->image = flist_sp_at(&doc->lib.images, json_int32(jtexVal, -1)); } else if (json_key_eq(jtexVal, _s_gltf_name)) { tex->name = json_strdup(jtexVal, gst->heap, tex); } else if (json_key_eq(jtexVal, _s_gltf_extensions)) { /* Texture-source extensions. WebP can go through the normal image loader. KTX2/BasisU is selected only when the optional decoder shim is available, so a PNG/JPEG fallback remains intact. */ json_t *jwebp; json_t *jktx2; json_t *jaltSrc; jwebp = json_get(jtexVal, _s_gltf_EXT_texture_webp); jktx2 = json_get(jtexVal, _s_gltf_KHR_texture_basisu); if (jktx2 && gltf_ext_textureBasisu(gst) && (jaltSrc = json_get(jktx2, _s_gltf_source))) { AkImage *altImage = flist_sp_at(&doc->lib.images, json_int32(jaltSrc, -1)); if (altImage) tex->image = altImage; } if (jwebp && (jaltSrc = json_get(jwebp, _s_gltf_source))) { AkImage *altImage = flist_sp_at(&doc->lib.images, json_int32(jaltSrc, -1)); if (altImage) tex->image = altImage; } } jtexVal = jtexVal->next; } /* TODO: add option for this */ /* create default sampler */ if (!sampler) { sampler = ak_heap_calloc(gst->heap, gst->doc, sizeof(*sampler)); sampler->wrapS = AK_WRAP_MODE_WRAP; sampler->wrapT = AK_WRAP_MODE_WRAP; ak_setypeid(sampler, AKT_SAMPLER2D); } tex->sampler = sampler; flist_sp_insert(&gst->doc->lib.textures, tex); jtex = jtex->next; } } ================================================ FILE: src/io/gltf/imp/core/texture.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_imp_core_texture_h #define gltf_imp_core_texture_h #include "../common.h" AK_HIDE AkTextureRef* gltf_texref(AkGLTFState * __restrict gst, void * __restrict parent, json_t * __restrict jtexinfo); AK_HIDE void gltf_textures(json_t * __restrict jtex, void * __restrict userdata); #endif /* gltf_imp_core_texture_h */ ================================================ FILE: src/io/gltf/imp/ext/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/io/gltf/imp/ext/compression.c ================================================ /* * Copyright (C) 2026 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); */ #include "decoder.h" #include "../core/ext.h" typedef enum AkGLTFMeshoptMode { AK_GLTF_MESHOPT_MODE_UNKNOWN = 0, AK_GLTF_MESHOPT_MODE_ATTRIBUTES, AK_GLTF_MESHOPT_MODE_TRIANGLES, AK_GLTF_MESHOPT_MODE_INDICES } AkGLTFMeshoptMode; typedef enum AkGLTFMeshoptFilter { AK_GLTF_MESHOPT_FILTER_NONE = 0, AK_GLTF_MESHOPT_FILTER_OCTAHEDRAL, AK_GLTF_MESHOPT_FILTER_QUATERNION, AK_GLTF_MESHOPT_FILTER_EXPONENTIAL } AkGLTFMeshoptFilter; static AkGLTFMeshoptMode gltf_ext_meshoptMode(const json_t * __restrict jmode) { if (!jmode) return AK_GLTF_MESHOPT_MODE_UNKNOWN; if (json_val_eq(jmode, _s_gltf_ATTRIBUTES)) return AK_GLTF_MESHOPT_MODE_ATTRIBUTES; if (json_val_eq(jmode, _s_gltf_TRIANGLES)) return AK_GLTF_MESHOPT_MODE_TRIANGLES; if (json_val_eq(jmode, _s_gltf_INDICES)) return AK_GLTF_MESHOPT_MODE_INDICES; return AK_GLTF_MESHOPT_MODE_UNKNOWN; } static AkGLTFMeshoptFilter gltf_ext_meshoptFilter(const json_t * __restrict jfilter) { if (!jfilter || json_val_eq(jfilter, _s_gltf_NONE)) return AK_GLTF_MESHOPT_FILTER_NONE; if (json_val_eq(jfilter, _s_gltf_OCTAHEDRAL)) return AK_GLTF_MESHOPT_FILTER_OCTAHEDRAL; if (json_val_eq(jfilter, _s_gltf_QUATERNION)) return AK_GLTF_MESHOPT_FILTER_QUATERNION; if (json_val_eq(jfilter, _s_gltf_EXPONENTIAL)) return AK_GLTF_MESHOPT_FILTER_EXPONENTIAL; return AK_GLTF_MESHOPT_FILTER_NONE; } AK_HIDE bool gltf_ext_bufferView(AkGLTFState * __restrict gst, AkBufferView * __restrict buffView, const json_t * __restrict jext) { const json_t *jmo; const json_t *it; AkBuffer *srcBuff; AkBuffer *dstBuff; const unsigned char *src; size_t srcOff; size_t srcLen; size_t stride; size_t count; size_t dstLen; int32_t buffIdx; AkGLTFMeshoptMode mode; AkGLTFMeshoptFilter filter; if (!gst || !buffView || !jext) return true; jmo = json_get(jext, _s_gltf_EXT_meshopt_compression); if (!jmo) jmo = json_get(jext, _s_gltf_KHR_meshopt_compression); if (!jmo) return true; if (!gltf_ext_meshopt(gst)) { if (buffView->buffer && buffView->buffer->data) return true; return false; } it = json_get(jmo, _s_gltf_buffer); buffIdx = it ? json_int32(it, -1) : -1; if (buffIdx < 0 || !(srcBuff = flist_sp_at(&gst->buffers, buffIdx)) || !srcBuff->data) return false; srcOff = (it = json_get(jmo, _s_gltf_byteOffset)) ? (size_t)json_uint64(it, 0) : 0; srcLen = (it = json_get(jmo, _s_gltf_byteLength)) ? (size_t)json_uint64(it, 0) : 0; stride = (it = json_get(jmo, _s_gltf_byteStride)) ? (size_t)json_uint64(it, 0) : 0; count = (it = json_get(jmo, _s_gltf_count)) ? (size_t)json_uint64(it, 0) : 0; mode = gltf_ext_meshoptMode(json_get(jmo, _s_gltf_mode)); filter = gltf_ext_meshoptFilter(json_get(jmo, _s_gltf_filter)); if (srcOff > srcBuff->length || srcLen == 0 || srcLen > srcBuff->length - srcOff || stride == 0 || count == 0) return false; dstLen = buffView->byteLength; if (dstLen == 0) { if (count > SIZE_MAX / stride) return false; dstLen = count * stride; } dstBuff = ak_heap_calloc(gst->heap, gst->doc, sizeof(*dstBuff)); dstBuff->data = ak_heap_alloc(gst->heap, dstBuff, dstLen); dstBuff->length = dstLen; src = (const unsigned char *)srcBuff->data + srcOff; if (!gltf_ext_meshoptDecode(gst, dstBuff->data, dstLen, src, srcLen, count, stride, mode, filter)) return false; buffView->buffer = dstBuff; buffView->byteOffset = 0; buffView->byteLength = dstLen; buffView->byteStride = stride; return true; } ================================================ FILE: src/io/gltf/imp/ext/decoder.c ================================================ /* * Copyright (C) 2026 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); */ #include "decoder.h" #include "../../../../platform/dylib.h" #include "../../../../../include/ak/gsplat.h" typedef int (*AkMeshoptDecodeBufferFn)(void *destination, size_t destination_size, const unsigned char *buffer, size_t buffer_size, size_t count, size_t stride, int mode, int filter); typedef int (*AkDracoDecodePrimitiveFn)(AkGLTFState *gst, AkMeshPrimitive *prim, const json_t *jprim, const json_t *jext); struct AkGLTFMeshoptLib { void *lib; AkMeshoptDecodeBufferFn decodeBuffer; bool tried; }; struct AkGLTFDracoLib { void *lib; AkDracoDecodePrimitiveFn decodePrimitive; bool tried; }; struct AkGLTFSPZLib { void *lib; AkGaussianSplatDecoder decoder; bool tried; }; typedef struct AkKTX2MipLevel { uint32_t width; uint32_t height; uint32_t byteOffset; uint32_t byteLength; } AkKTX2MipLevel; typedef struct AkKTX2DecodedImage { uint8_t *data; size_t dataLength; uint32_t width; uint32_t height; uint32_t channels; uint32_t mipCount; AkKTX2MipLevel *mips; uint32_t reserved[2]; } AkKTX2DecodedImage; typedef int (*AkKTX2DecodeFn)(const uint8_t *data, size_t size, AkKTX2DecodedImage *out); struct AkGLTFKTX2Lib { void *lib; AkKTX2DecodeFn decode; bool tried; }; static void* gltf_ext_openLib(AkOption opt, const char * __restrict name) { const char *path; void *lib; path = (const char *)ak_opt_get(opt); if (path && (lib = ak_dylib_open(path))) return lib; if (!ak_opt_get(AK_OPT_GLTF_EXT_DECODER_AUTOLOAD)) return NULL; return ak_dylib_openName(name); } AK_HIDE bool gltf_ext_meshopt(AkGLTFState * __restrict gst) { AkGLTFMeshoptLib *mo; void *lib; if (gst->meshopt) return gst->meshopt->lib != NULL; mo = ak_calloc(NULL, sizeof(*mo)); mo->tried = true; gst->meshopt = mo; lib = gltf_ext_openLib(AK_OPT_GLTF_MESHOPT_DECODER_PATH, "assetkit_meshoptimizer"); if (!lib) return false; mo->decodeBuffer = (AkMeshoptDecodeBufferFn) ak_dylib_sym(lib, "ak_meshopt_decode_gltf_buffer"); if (!mo->decodeBuffer) { ak_dylib_close(lib); return false; } mo->lib = lib; return true; } AK_HIDE bool gltf_ext_spz(AkGLTFState * __restrict gst) { AkGLTFSPZLib *sp; AkGaussianSplatDecoderCreateFn createFn; void *lib; if (gst->spz) return gst->spz->lib != NULL; sp = ak_calloc(NULL, sizeof(*sp)); sp->tried = true; gst->spz = sp; lib = gltf_ext_openLib(AK_OPT_GLTF_GSPLAT_DECODER_PATH, "assetkit_spz"); if (!lib) return false; createFn = (AkGaussianSplatDecoderCreateFn) ak_dylib_sym(lib, "assetkit_gsplat_create"); if (!createFn || createFn(&sp->decoder) != 0 || (!sp->decoder.decodeBytes && !sp->decoder.decodePrimitive)) { ak_dylib_close(lib); return false; } sp->lib = lib; return true; } AK_HIDE bool gltf_ext_ktx2(AkGLTFState * __restrict gst) { AkGLTFKTX2Lib *kx; void *lib; if (gst->ktx2) return gst->ktx2->lib != NULL; kx = ak_calloc(NULL, sizeof(*kx)); kx->tried = true; gst->ktx2 = kx; lib = gltf_ext_openLib(AK_OPT_GLTF_KTX2_DECODER_PATH, "assetkit_ktx2"); if (!lib) return false; kx->decode = (AkKTX2DecodeFn)ak_dylib_sym(lib, "assetkit_ktx2_decode"); if (!kx->decode) { ak_dylib_close(lib); return false; } kx->lib = lib; return true; } AK_HIDE bool gltf_ext_textureBasisu(AkGLTFState * __restrict gst) { return gltf_ext_ktx2(gst); } AK_HIDE bool gltf_ext_draco(AkGLTFState * __restrict gst) { AkGLTFDracoLib *dr; void *lib; if (gst->draco) return gst->draco->lib != NULL; dr = ak_calloc(NULL, sizeof(*dr)); dr->tried = true; gst->draco = dr; lib = gltf_ext_openLib(AK_OPT_GLTF_DRACO_DECODER_PATH, "assetkit_draco"); if (!lib) return false; dr->decodePrimitive = (AkDracoDecodePrimitiveFn) ak_dylib_sym(lib, "ak_draco_decode_gltf_primitive"); if (!dr->decodePrimitive) { ak_dylib_close(lib); return false; } dr->lib = lib; return true; } AK_HIDE bool gltf_ext_spzDecodeBytes(AkGLTFState * __restrict gst, AkMeshPrimitive * __restrict prim, const uint8_t * __restrict data, size_t size) { if (!gltf_ext_spz(gst) || !gst->spz->decoder.decodeBytes) return false; return gst->spz->decoder.decodeBytes(gst->heap, prim, data, size) == 0; } AK_HIDE bool gltf_ext_dracoPrimitive(AkGLTFState * __restrict gst, AkMeshPrimitive * __restrict prim, const json_t * __restrict jprim) { const json_t *jext; const json_t *jdraco; if (!gst || !prim || !jprim) return true; jext = json_get(jprim, _s_gltf_extensions); if (!jext) return true; jdraco = json_get(jext, _s_gltf_KHR_draco_mesh_compression); if (!jdraco) return true; if (!gltf_ext_draco(gst)) return true; return gst->draco->decodePrimitive(gst, prim, jprim, jdraco) == 0; } AK_HIDE bool gltf_ext_meshoptDecode(AkGLTFState * __restrict gst, void * __restrict dst, size_t dstSize, const unsigned char * __restrict src, size_t srcSize, size_t count, size_t stride, int mode, int filter) { AkGLTFMeshoptLib *mo; int res; if (!gltf_ext_meshopt(gst)) return false; mo = gst->meshopt; if (!dst || !src || stride == 0 || count > SIZE_MAX / stride || dstSize < count * stride) return false; res = mo->decodeBuffer(dst, dstSize, src, srcSize, count, stride, mode, filter); return res == 0; } AK_HIDE void gltf_ext_decoderClose(AkGLTFState * __restrict gst) { if (!gst) return; if (gst->meshopt) { if (gst->meshopt->lib) ak_dylib_close(gst->meshopt->lib); ak_free(gst->meshopt); gst->meshopt = NULL; } if (gst->draco) { if (gst->draco->lib) ak_dylib_close(gst->draco->lib); ak_free(gst->draco); gst->draco = NULL; } if (gst->spz) { if (gst->spz->decoder.close) gst->spz->decoder.close(gst->spz->decoder.userdata); if (gst->spz->lib) ak_dylib_close(gst->spz->lib); ak_free(gst->spz); gst->spz = NULL; } if (gst->ktx2) { if (gst->ktx2->lib) ak_dylib_close(gst->ktx2->lib); ak_free(gst->ktx2); gst->ktx2 = NULL; } } ================================================ FILE: src/io/gltf/imp/ext/decoder.h ================================================ /* * Copyright (C) 2026 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); */ #ifndef gltf_imp_ext_decoder_h #define gltf_imp_ext_decoder_h #include "../common.h" AK_HIDE bool gltf_ext_meshopt(AkGLTFState * __restrict gst); AK_HIDE bool gltf_ext_draco(AkGLTFState * __restrict gst); AK_HIDE bool gltf_ext_spz(AkGLTFState * __restrict gst); AK_HIDE bool gltf_ext_ktx2(AkGLTFState * __restrict gst); AK_HIDE bool gltf_ext_textureBasisu(AkGLTFState * __restrict gst); AK_HIDE bool gltf_ext_spzDecodeBytes(AkGLTFState * __restrict gst, AkMeshPrimitive * __restrict prim, const uint8_t * __restrict data, size_t size); AK_HIDE bool gltf_ext_meshoptDecode(AkGLTFState * __restrict gst, void * __restrict dst, size_t dstSize, const unsigned char * __restrict src, size_t srcSize, size_t count, size_t stride, int mode, int filter); AK_HIDE void gltf_ext_decoderClose(AkGLTFState * __restrict gst); #endif /* gltf_imp_ext_decoder_h */ ================================================ FILE: src/io/gltf/imp/ext/gsplat.c ================================================ /* * Copyright (C) 2026 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); */ #include "decoder.h" #include "../core/ext.h" #include "../../../../../include/ak/gsplat.h" static AkGaussianSplatColorSpace gltf_gsplatColorSpace(const json_t * __restrict v) { size_t sz; const char *s; if (!v) return AK_GSPLAT_COLOR_UNKNOWN; s = json_string(v); sz = v->valsize; if (sz == 19 && memcmp(s, "srgb_rec709_display", 19) == 0) return AK_GSPLAT_COLOR_SRGB_REC709_DISPLAY; if (sz == 18 && memcmp(s, "lin_rec709_display", 18) == 0) return AK_GSPLAT_COLOR_LIN_REC709_DISPLAY; return AK_GSPLAT_COLOR_UNKNOWN; } static AkGaussianSplatProjection gltf_gsplatProjection(const json_t * __restrict v) { size_t sz; const char *s; if (!v) return AK_GSPLAT_PROJECTION_PERSPECTIVE; s = json_string(v); sz = v->valsize; if (sz == 12 && memcmp(s, "orthographic", 12) == 0) return AK_GSPLAT_PROJECTION_ORTHOGRAPHIC; return AK_GSPLAT_PROJECTION_PERSPECTIVE; } static AkGaussianSplatSortingMethod gltf_gsplatSorting(const json_t * __restrict v) { size_t sz; const char *s; if (!v) return AK_GSPLAT_SORTING_CAMERA_DISTANCE; s = json_string(v); sz = v->valsize; if (sz == 4 && memcmp(s, "none", 4) == 0) return AK_GSPLAT_SORTING_NONE; return AK_GSPLAT_SORTING_CAMERA_DISTANCE; } AK_HIDE bool gltf_ext_primitiveGaussianSplat(AkGLTFState * __restrict gst, AkMeshPrimitive * __restrict prim, const json_t * __restrict jprim) { const json_t *jext; const json_t *jgsplat; json_t *jkernel; json_t *jcolor; json_t *jproj; json_t *jsort; AkGaussianSplat *gs; if (!gst || !prim || !jprim) return true; jext = json_get(jprim, _s_gltf_extensions); jgsplat = jext ? json_get(jext, _s_gltf_KHR_gaussian_splatting) : NULL; if (!jgsplat) return true; gs = ak_heap_calloc(gst->heap, prim, sizeof(*gs)); jkernel = json_get(jgsplat, _s_gltf_kernel); jcolor = json_get(jgsplat, _s_gltf_colorSpace); jproj = json_get(jgsplat, _s_gltf_projection); jsort = json_get(jgsplat, _s_gltf_sortingMethod); (void)jkernel; gs->kernel = AK_GSPLAT_KERNEL_ELLIPSE; gs->colorSpace = gltf_gsplatColorSpace(jcolor); gs->projection = gltf_gsplatProjection(jproj); gs->sortingMethod = gltf_gsplatSorting(jsort); prim->gsplat = gs; { const json_t *jcomp; const json_t *jformat; const json_t *jbv; int32_t bvIdx; AkBufferView *bv; if ((jcomp = json_get(jgsplat, _s_gltf_compression))) { jformat = json_get(jcomp, _s_gltf_format); jbv = json_get(jcomp, _s_gltf_bufferView); if (jformat && !json_val_eq(jformat, _s_gltf_spz)) return false; if (!jbv) return false; bvIdx = json_int32(jbv, -1); bv = (bvIdx >= 0) ? flist_sp_at(&gst->bufferViews, bvIdx) : NULL; if (!bv || !bv->buffer || !bv->buffer->data || bv->byteLength == 0) return false; { const uint8_t *bytes; bytes = (const uint8_t *)bv->buffer->data + bv->byteOffset; if (!gltf_ext_spzDecodeBytes(gst, prim, bytes, bv->byteLength)) return false; } } } return true; } ================================================ FILE: src/io/gltf/imp/ext/instancing.c ================================================ /* * Copyright (C) 2026 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); */ #include "instancing.h" static bool gltf_ext_instanceAccOK(AkAccessor * __restrict acc, uint32_t componentCount) { return acc && acc->componentType == AKT_FLOAT && acc->componentCount == componentCount && acc->count > 0; } /* EXT_mesh_gpu_instancing: pull the per-instance TRS accessor refs out of the extension's `attributes` object. Each attribute is optional; present attributes must all have the same accessor count per the spec. */ AK_HIDE AkInstanceAttribs* gltf_ext_meshGPUInstancing(AkGLTFState * __restrict gst, AkNode * __restrict node, const json_t * __restrict jinstancing) { AkInstanceAttribs *attribs; AkDoc *doc; json_t *jattrs; json_t *jattr; AkAccessor *acc; int32_t accIdx; uint32_t count; uint32_t compCount; bool known; if (!(jattrs = json_get(jinstancing, _s_gltf_attributes))) return NULL; doc = gst->doc; attribs = ak_heap_calloc(gst->heap, node, sizeof(*attribs)); count = 0; for (jattr = jattrs->value; jattr; jattr = jattr->next) { accIdx = json_int32(jattr, -1); known = false; compCount = 0; if (jattr->keysize == 11 && memcmp(jattr->key, "TRANSLATION", 11) == 0) { known = true; compCount = 3; } else if (jattr->keysize == 8 && memcmp(jattr->key, "ROTATION", 8) == 0) { known = true; compCount = 4; } else if (jattr->keysize == 5 && memcmp(jattr->key, "SCALE", 5) == 0) { known = true; compCount = 3; } if (!known) continue; if (accIdx < 0) { gst->stop = true; return NULL; } if (!(acc = flist_sp_at(&doc->lib.accessors, accIdx))) goto malformed; if (!gltf_ext_instanceAccOK(acc, compCount)) goto malformed; if (count == 0) { count = (uint32_t)acc->count; } else if (count != (uint32_t)acc->count) { goto malformed; } ak_retain(acc); if (jattr->keysize == 11 && memcmp(jattr->key, "TRANSLATION", 11) == 0) { attribs->translation = acc; } else if (jattr->keysize == 8 && memcmp(jattr->key, "ROTATION", 8) == 0) { attribs->rotation = acc; } else if (jattr->keysize == 5 && memcmp(jattr->key, "SCALE", 5) == 0) { attribs->scale = acc; } } if (count == 0) return NULL; attribs->count = count; return attribs; malformed: gst->stop = true; return NULL; } ================================================ FILE: src/io/gltf/imp/ext/instancing.h ================================================ /* * Copyright (C) 2026 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); */ #ifndef gltf_imp_ext_instancing_h #define gltf_imp_ext_instancing_h #include "../common.h" AK_HIDE AkInstanceAttribs* gltf_ext_meshGPUInstancing(AkGLTFState * __restrict gst, AkNode * __restrict node, const json_t * __restrict jinstancing); #endif /* gltf_imp_ext_instancing_h */ ================================================ FILE: src/io/gltf/imp/ext/lights.c ================================================ /* * Copyright (C) 2026 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); */ #include "lights.h" #include "../extra.h" static AkLightType gltf_ext_lightType(const json_t * __restrict jtype) { if (!jtype) return AK_LIGHT_TYPE_POINT; if (json_val_eq(jtype, _s_gltf_directional)) return AK_LIGHT_TYPE_DIRECTIONAL; if (json_val_eq(jtype, _s_gltf_spot)) return AK_LIGHT_TYPE_SPOT; if (json_val_eq(jtype, _s_gltf_point)) return AK_LIGHT_TYPE_POINT; return AK_LIGHT_TYPE_POINT; } AK_HIDE void gltf_ext_lights(AkGLTFState * __restrict gst, json_t * __restrict jlights) { const json_array_t *jarr; json_t *jlight; json_t *it; AkLight *light; AkLightBase *base; AkSpotLight *spot; if (!gst || !(jarr = json_array(jlights))) return; jlight = jarr->base.value; while (jlight) { it = json_get(jlight, _s_gltf_type); light = ak_lightMake(gst->doc, gst->doc, gltf_ext_lightType(it)); if (!light) goto nxt; gltf_extra(gst, light, json_get(jlight, _s_gltf_extras), json_get(jlight, _s_gltf_extensions)); base = light->tcommon; if ((it = json_get(jlight, _s_gltf_name))) light->name = json_strdup(it, gst->heap, light); if ((it = json_get(jlight, _s_gltf_color))) { json_array_float(base->color.vec, it, 1.0f, 3, true); base->color.vec[3] = 1.0f; } base->intensity = json_float(json_get(jlight, _s_gltf_intensity), 1.0f); base->range = json_float(json_get(jlight, _s_gltf_range), 0.0f); if (base->type == AK_LIGHT_TYPE_SPOT && (it = json_get(jlight, _s_gltf_spot))) { spot = (AkSpotLight *)base; spot->innerConeAngle = json_float(json_get(it, _s_gltf_innerConeAngle), 0.0f); spot->outerConeAngle = json_float(json_get(it, _s_gltf_outerConeAngle), GLM_PI_4f); } nxt: jlight = jlight->next; } } AK_HIDE bool gltf_ext_nodeLight(AkGLTFState * __restrict gst, AkNode * __restrict node, const json_t * __restrict jext) { json_t *jpunctual; json_t *jlight; AkLight *light; int32_t lightIndex; jpunctual = json_get(jext, _s_gltf_KHR_lights_punctual); jlight = jpunctual ? json_get(jpunctual, _s_gltf_light) : NULL; if (!jlight) return true; lightIndex = json_int32(jlight, -1); if (lightIndex < 0 || !gst->doc->lib.lights) return false; light = (void *)gst->doc->lib.lights->chld; while (light && lightIndex > 0) { light = light->next; lightIndex--; } if (!light) return false; return ak_nodeAttachLight(node, light) != NULL; } ================================================ FILE: src/io/gltf/imp/ext/lights.h ================================================ /* * Copyright (C) 2026 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); */ #ifndef gltf_imp_ext_lights_h #define gltf_imp_ext_lights_h #include "../common.h" AK_HIDE void gltf_ext_lights(AkGLTFState * __restrict gst, json_t * __restrict jlights); AK_HIDE bool gltf_ext_nodeLight(AkGLTFState * __restrict gst, AkNode * __restrict node, const json_t * __restrict jext); #endif /* gltf_imp_ext_lights_h */ ================================================ FILE: src/io/gltf/imp/ext/variants.c ================================================ /* * Copyright (C) 2026 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); */ #include "variants.h" #include "../core/ext.h" /* KHR_materials_variants — root-level: capture the document's variant list so primitive parser can resolve `variants[]` indices later. The doc field stays in declaration-order (head→tail = index 0→N), matching the indices the primitive `mappings[].variants[]` arrays use. */ AK_HIDE void gltf_ext_materialVariants(AkGLTFState * __restrict gst, json_t * __restrict jvariants) { json_array_t *jarr; json_t *jvariant; AkMaterialVariant *variant; AkDoc *doc; uint32_t count; json_t *jname; if (!(jarr = json_array(jvariants)) || jarr->count == 0) return; doc = gst->doc; jvariant = jarr->base.value; count = 0; doc->materialVariants = NULL; while (jvariant) { variant = ak_heap_calloc(gst->heap, doc, sizeof(*variant)); if ((jname = json_get(jvariant, _s_gltf_name))) variant->name = json_strdup(jname, gst->heap, variant); /* json_parse(..., true) gives array children in reverse. Prepend restores source/index order: variants[0] is list head. */ variant->next = doc->materialVariants; doc->materialVariants = variant; count++; jvariant = jvariant->next; } doc->materialVariantCount = count; } AK_HIDE bool gltf_ext_primitiveVariants(AkGLTFState * __restrict gst, AkMeshPrimitive * __restrict prim, const json_t * __restrict jprim) { const json_t *jext; const json_t *jvariantsExt; const json_t *jmappings; json_array_t *jmapArr; json_t *jmap; json_t *jmaterial; json_t *jvariants; json_array_t *jvarsArr; json_t *jvarIdx; AkMaterialVariantMapping *mapping; AkMaterialVariantMapping *tail; AkMaterial *material; int32_t matIdx; uint32_t variantIdx; uint32_t count; if (!gst || !prim || !jprim) return true; jext = json_get(jprim, _s_gltf_extensions); jvariantsExt = jext ? json_get(jext, _s_gltf_KHR_materials_variants) : NULL; jmappings = jvariantsExt ? json_get(jvariantsExt, _s_gltf_mappings) : NULL; if (!jmappings || !(jmapArr = json_array(jmappings)) || jmapArr->count == 0) return true; count = 0; tail = NULL; /* Each `mapping` entry references a single material and the list of variant indices that should resolve to it. We unroll the variants list into a flat per-(variant, material) chain so runtime swap is O(numVariants) — typical N is small (<10) so the unroll is cheap. */ for (jmap = jmapArr->base.value; jmap; jmap = jmap->next) { jmaterial = json_get(jmap, _s_gltf_material); jvariants = json_get(jmap, _s_gltf_variants); if (!jmaterial || !jvariants) continue; matIdx = json_int32(jmaterial, -1); if (matIdx < 0) continue; material = NULL; if (gst->doc->lib.materials) GETCHILD(gst->doc->lib.materials->chld, material, matIdx); if (!material) continue; if (!(jvarsArr = json_array(jvariants))) continue; for (jvarIdx = jvarsArr->base.value; jvarIdx; jvarIdx = jvarIdx->next) { variantIdx = (uint32_t)json_int32(jvarIdx, -1); if ((int32_t)variantIdx < 0 || variantIdx >= gst->doc->materialVariantCount) continue; mapping = ak_heap_calloc(gst->heap, prim, sizeof(*mapping)); mapping->material = material; mapping->variantIndex = variantIdx; if (tail) { tail->next = mapping; } else { prim->variantMappings = mapping; } tail = mapping; count++; } } prim->variantMappingCount = count; return true; } ================================================ FILE: src/io/gltf/imp/ext/variants.h ================================================ /* * Copyright (C) 2026 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); */ #ifndef gltf_imp_ext_variants_h #define gltf_imp_ext_variants_h #include "../common.h" AK_HIDE void gltf_ext_materialVariants(AkGLTFState * __restrict gst, json_t * __restrict jvariants); #endif /* gltf_imp_ext_variants_h */ ================================================ FILE: src/io/gltf/imp/extra.c ================================================ /* * Copyright (C) 2026 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); */ #include "extra.h" static AkTreeNodeAttr* gltf_treeAttr(AkHeap * __restrict heap, AkTreeNode * __restrict node, const char * __restrict name, const char * __restrict val) { AkTreeNodeAttr *attr; attr = ak_heap_calloc(heap, node, sizeof(*attr)); attr->name = ak_heap_strdup(heap, attr, name); attr->val = ak_heap_strdup(heap, attr, val); attr->next = node->attribs; if (node->attribs) node->attribs->prev = attr; node->attribs = attr; node->attrc++; return attr; } static void gltf_treeAppend(AkTreeNode * __restrict parent, AkTreeNode * __restrict child) { child->next = parent->chld; if (parent->chld) parent->chld->prev = child; parent->chld = child; child->parent = parent; parent->chldc++; } static const char* gltf_treeType(const json_t * __restrict json) { if (!json) return "null"; switch (json->type) { case JSON_OBJECT: return "object"; case JSON_ARRAY: return "array"; case JSON_STRING: return "value"; default: break; } return "unknown"; } static AkTreeNode* gltf_treeFromJson(AkHeap * __restrict heap, void * __restrict owner, const json_t * __restrict json, const char * __restrict name, size_t nameLen) { AkTreeNode *node; json_t *child; node = ak_heap_calloc(heap, owner, sizeof(*node)); if (name && nameLen > 0) node->name = ak_heap_strndup(heap, node, name, nameLen); else node->name = ak_heap_strdup(heap, node, "item"); gltf_treeAttr(heap, node, "type", gltf_treeType(json)); if (!json) return node; if (json->type == JSON_STRING) { if (json->value && json->valsize > 0) node->val = ak_heap_strndup(heap, node, json->value, json->valsize); return node; } if (json->type != JSON_OBJECT && json->type != JSON_ARRAY) return node; child = json->value; while (child) { AkTreeNode *tnode; if (json->type == JSON_OBJECT) { tnode = gltf_treeFromJson(heap, node, child, child->key, child->keysize); } else { tnode = gltf_treeFromJson(heap, node, child, "item", 4); } /* json_parse(..., true) stores children in reverse. Prepend restores source order for arrays while object order remains immaterial. */ gltf_treeAppend(node, tnode); child = child->next; } return node; } static AkTreeNode* gltf_extraRoot(AkGLTFState * __restrict gst, void * __restrict owner) { AkHeap *heap; AkTreeNode *root; heap = gst->heap; root = ak_extra(owner); if (!root) { root = ak_heap_calloc(heap, owner, sizeof(*root)); root->name = ak_heap_strdup(heap, root, "extra"); ak_extra_set(owner, root); } return root; } AK_HIDE void gltf_extra_node(AkGLTFState * __restrict gst, void * __restrict owner, const char * __restrict name, const json_t * __restrict json) { AkTreeNode *root; AkTreeNode *node; if (!gst || !owner || !name || !json) return; root = gltf_extraRoot(gst, owner); node = gltf_treeFromJson(gst->heap, root, json, name, strlen(name)); gltf_treeAppend(root, node); } AK_HIDE void gltf_extra(AkGLTFState * __restrict gst, void * __restrict owner, const json_t * __restrict jextras, const json_t * __restrict jextensions) { if (!gst || !owner || (!jextras && !jextensions)) return; gltf_extra_node(gst, owner, _s_gltf_extensions, jextensions); gltf_extra_node(gst, owner, _s_gltf_extras, jextras); } ================================================ FILE: src/io/gltf/imp/extra.h ================================================ /* * Copyright (C) 2026 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); */ #ifndef gltf_imp_extra_h #define gltf_imp_extra_h #include "common.h" AK_HIDE void gltf_extra(AkGLTFState * __restrict gst, void * __restrict owner, const json_t * __restrict jextras, const json_t * __restrict jextensions); AK_HIDE void gltf_extra_node(AkGLTFState * __restrict gst, void * __restrict owner, const char * __restrict name, const json_t * __restrict json); #endif /* gltf_imp_extra_h */ ================================================ FILE: src/io/gltf/imp/gltf.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../../../../include/ak/path.h" #include "../../../id.h" #include "../../../endian.h" #include "gltf.h" #include "core/asset.h" #include "core/buffer.h" #include "core/accessor.h" #include "core/mesh.h" #include "core/node.h" #include "core/scene.h" #include "core/camera.h" #include "core/image.h" #include "core/profile.h" #include "core/sampler.h" #include "core/texture.h" #include "core/material.h" #include "core/anim.h" #include "core/skin.h" #include "core/ext.h" #include "extra.h" #include "postscript.h" static AkResult gltf_parse(AkDoc ** __restrict dest, const char * __restrict filepath, const char * __restrict contents, void * __restrict bindata, size_t bindataLen); static void ak_gltfFreeDupl(RBTree *tree, RBNode *node); AK_HIDE AkResult gltf_glb(AkDoc ** __restrict dest, const char * __restrict filepath) { void *data, *bindata; char *pdata, *jsonData, *binData; size_t fileSize, bindataLen; AkResult ret; uint32_t magic, version, length, chunkLength, chunkType; uint32_t buffLen, buffType; if ((ret = ak_readfile(filepath, NULL, &data, &fileSize)) != AK_OK) return ret; pdata = data; bindata = NULL; bindataLen = 0; /* check if the is is glTF */ le_32(magic, pdata); if (magic != 0x46546C67) { ret = AK_ERR; goto done; } le_32(version, pdata); le_32(length, pdata); le_32(chunkLength, pdata); le_32(chunkType , pdata); if (chunkType != 0x4E4F534A || length > fileSize || chunkLength > fileSize || (size_t)(pdata - (char *)data) > fileSize - chunkLength) { ret = AK_ERR; goto done; } jsonData = pdata; binData = jsonData + chunkLength; if ((size_t)(binData - (char *)data) <= fileSize - 8) { le_32(buffLen, binData); le_32(buffType, binData); if (buffType == 0x004E4942 && buffLen <= fileSize && (size_t)(binData - (char *)data) <= fileSize - buffLen) { bindata = binData; bindataLen = buffLen; } } /* make the json NULL terminated */ /* pdata[chunkLength] = '\0'; */ ret = gltf_parse(dest, filepath, jsonData, bindata, bindataLen); done: if (data) { if (ret == AK_OK && dest && *dest && bindata && ak_opt_get(AK_OPT_USE_MMAP)) ak_mmap_attach(*dest, data, fileSize); else ak_releasefile(data, fileSize); } return ret; } AK_HIDE AkResult gltf_gltf(AkDoc ** __restrict dest, const char * __restrict filepath) { void *jsonString; size_t jsonSize; AkResult ret; if ((ret = ak_readfile(filepath, NULL, &jsonString, &jsonSize)) != AK_OK) return ret; ret = gltf_parse(dest, filepath, jsonString, NULL, 0); if (jsonString) ak_releasefile(jsonString, jsonSize); return ret; } static AkResult gltf_parse(AkDoc ** __restrict dest, const char * __restrict filepath, const char * __restrict contents, void * __restrict bindata, size_t bindataLen) { AkHeap *heap; AkDoc *doc; const json_doc_t *gltfRawDoc; json_t *json; AkGLTFState gstVal, *gst; AkResult ret; if (!contents) return AK_ERR; ret = AK_OK; heap = ak_heap_new(NULL, NULL, NULL); doc = ak_heap_calloc(heap, NULL, sizeof(*doc)); doc->inf = ak_heap_calloc(heap, doc, sizeof(*doc->inf)); doc->inf->dir = ak_path_dir(heap, doc, filepath); doc->inf->name = filepath; doc->inf->flipImage = false; doc->inf->ftype = AK_FILE_TYPE_GLTF; /* for fixing skin and morph vertices */ doc->reserved = rb_newtree_ptr(); ((RBTree *)doc->reserved)->onFreeNode = ak_gltfFreeDupl; if (doc->inf->dir) doc->inf->dirlen = strlen(doc->inf->dir); ak_heap_setdata(heap, doc); ak_id_newheap(heap); memset(&gstVal, 0, sizeof(gstVal)); gst = &gstVal; gstVal.doc = doc; gstVal.heap = heap; gstVal.bindata = bindata; gstVal.bindataLen = bindataLen; gstVal.borrowBufferViews = bindata != NULL && ak_opt_get(AK_OPT_USE_MMAP); gstVal.tmpParent = ak_heap_alloc(heap, doc, sizeof(void*)); gst->bufferMap = rb_newtree_ptr(); gst->meshTargets = rb_newtree_ptr(); gst->skinBound = rb_newtree_ptr(); gst->skinBound->userData = gst; gltfRawDoc = json_parse(contents, true); if (!gltfRawDoc || !gltfRawDoc->root) { ak_free(doc); if (gltfRawDoc) free((void *)gltfRawDoc); return AK_ERR; } if (doc->inf->dir) doc->inf->dirlen = strlen(doc->inf->dir); json = gltfRawDoc->root; gltf_extra(gst, doc, json_get(json, _s_gltf_extras), json_get(json, _s_gltf_extensions)); gltf_extra_node(gst, doc, _s_gltf_extensionsUsed, json_get(json, _s_gltf_extensionsUsed)); gltf_extra_node(gst, doc, _s_gltf_extensionsRequired, json_get(json, _s_gltf_extensionsRequired)); gltf_ext_root(json_get(json, _s_gltf_extensions), gst); /* json_print_human(stderr, gltfRawDoc->root); */ json_objmap_t gltfMap[] = { JSON_OBJMAP_FN(_s_gltf_asset, gltf_asset, gst), JSON_OBJMAP_FN(_s_gltf_buffers, gltf_buffers, gst), JSON_OBJMAP_FN(_s_gltf_bufferViews, gltf_bufferViews, gst), JSON_OBJMAP_FN(_s_gltf_accessors, gltf_accessors, gst), JSON_OBJMAP_FN(_s_gltf_images, gltf_images, gst), JSON_OBJMAP_FN(_s_gltf_samplers, gltf_samplers, gst), JSON_OBJMAP_FN(_s_gltf_textures, gltf_textures, gst), JSON_OBJMAP_FN(_s_gltf_extensionsRequired, gltf_exts, gst), JSON_OBJMAP_FN(_s_gltf_materials, gltf_materials, gst), JSON_OBJMAP_FN(_s_gltf_meshes, gltf_meshes, gst), JSON_OBJMAP_FN(_s_gltf_cameras, gltf_cameras, gst), JSON_OBJMAP_FN(_s_gltf_nodes, gltf_nodes, gst), JSON_OBJMAP_FN(_s_gltf_skins, gltf_skin, gst), JSON_OBJMAP_FN(_s_gltf_scenes, gltf_scenes, gst), JSON_OBJMAP_FN(_s_gltf_scene, gltf_scene, gst), JSON_OBJMAP_FN(_s_gltf_animations, gltf_animations, gst) }; while (json) { json_objmap_call(json, gltfMap, JSON_ARR_LEN(gltfMap), &gstVal.stop); if (gstVal.stop) { ret = AK_EBADF; goto err; } json = json->next; } err: if (gltfRawDoc) free((void *)gltfRawDoc); gltf_ext_close(gst); /* probably unsupportted version or verion is missing */ if (ret == AK_EBADF) { ak_free(doc); return ret; } /* TODO: release resources in GLTFState */ /* set first scene as default scene if not specified */ if (!doc->scene.visualScene) { if (doc->lib.visualScenes->chld) { AkInstanceBase *instScene; instScene = ak_heap_calloc(heap, doc, sizeof(*instScene)); instScene->url.ptr = doc->lib.visualScenes->chld; doc->scene.visualScene = instScene; } } *dest = doc; /* post-parse operations */ gltf_postscript(gst); ak_free(gstVal.tmpParent); rb_destroy(gst->bufferMap); rb_destroy(gst->meshTargets); rb_destroy(gst->skinBound); return ret; } static void ak_gltfFreeDupl(RBTree *tree, RBNode *node) { if (node == tree->nullNode) return; ak_free(node->val); } ================================================ FILE: src/io/gltf/imp/gltf.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_imp_h #define gltf_imp_h #include "common.h" AK_HIDE AkResult gltf_gltf(AkDoc ** __restrict dest, const char * __restrict filepath); AK_HIDE AkResult gltf_glb(AkDoc ** __restrict dest, const char * __restrict filepath); #endif /* gltf_imp_h */ ================================================ FILE: src/io/gltf/imp/mesh_fixup.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "mesh_fixup.h" #include "../../../mesh/index.h" #include "../../../topo/topo.h" AK_HIDE void gltf_mesh_fixup(AkGLTFState * __restrict gst) { AkDoc *doc; AkLibrary *geomLib; AkGeometry *geom; doc = gst->doc; geomLib = doc->lib.geometries; while (geomLib) { geom = (void *)geomLib->chld; while (geom) { AkObject *primitive; primitive = geom->gdata; switch ((AkGeometryType)primitive->type) { case AK_GEOMETRY_MESH: { AkMesh *mesh; mesh = ak_objGet(primitive); topofix(mesh); /* first fixup coord system because verts will be duplicated, reduce extra process */ if (ak_opt_get(AK_OPT_COORD_CONVERT_TYPE) == AK_COORD_CVT_ALL && (void *)ak_opt_get(AK_OPT_COORD) != doc->coordSys) ak_changeCoordSysMesh(mesh, (void *)ak_opt_get(AK_OPT_COORD)); if (ak_opt_get(AK_OPT_COMPUTE_BBOX)) ak_bbox_mesh(mesh); if (ak_opt_get(AK_OPT_GEN_NORMALS_IF_NEEDED)) if (ak_meshNeedsNormals(mesh)) ak_meshGenNormals(mesh); } default: break; } geom = (void *)geom->base.next; } geomLib = geomLib->next; } } ================================================ FILE: src/io/gltf/imp/mesh_fixup.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_imp_mesh_fixup_h #define gltf_imp_mesh_fixup_h #include "common.h" AK_HIDE void gltf_mesh_fixup(AkGLTFState * __restrict gst); #endif /* gltf_imp_mesh_fixup_h */ ================================================ FILE: src/io/gltf/imp/postscript.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "postscript.h" #include "mesh_fixup.h" #include "ak/coord-util.h" AK_HIDE void gltf_setskinners(RBTree *tree, RBNode *rbnode); AK_HIDE void gltf_postscript(AkGLTFState * __restrict gst) { AkCoordCvtType coordCvtType; AkCoordSys *sourceCoordSys, *targetCoordSys; AkVisualScene *vscn; bool fixTransform; coordCvtType = (AkCoordCvtType)ak_opt_get(AK_OPT_COORD_CONVERT_TYPE); sourceCoordSys = gst->doc ? gst->doc->coordSys : NULL; targetCoordSys = (void *)ak_opt_get(AK_OPT_COORD); fixTransform = coordCvtType == AK_COORD_CVT_FIX_TRANSFORM && sourceCoordSys && targetCoordSys && sourceCoordSys != targetCoordSys && !ak_coordOrientationIsEq(sourceCoordSys, targetCoordSys); gltf_mesh_fixup(gst); if (coordCvtType != AK_COORD_CVT_DISABLED) gst->doc->coordSys = targetCoordSys; if (gst->doc && gst->doc->lib.visualScenes) { for (vscn = (void *)gst->doc->lib.visualScenes->chld; vscn; vscn = (void *)vscn->base.next) { if (fixTransform) ak_fixSceneCoordSys(vscn); } } rb_walk(gst->skinBound, gltf_setskinners); } AK_HIDE void gltf_setskinners(RBTree *tree, RBNode *rbnode) { char skinid[16]; AkGLTFState *gst; AkInstanceSkin *skinner; AkNode *node; AkInstanceGeometry *instGeom; int32_t i32val; gst = tree->userData; node = rbnode->key; instGeom = node->geometry; i32val = (int32_t)(intptr_t)rbnode->val; sprintf(skinid, "%s%d", _s_gltf_skin, i32val); skinner = ak_heap_calloc(gst->heap, node, sizeof(*skinner)); skinner->skin = ak_getObjectById(gst->doc, skinid); instGeom->skinner = skinner; } ================================================ FILE: src/io/gltf/imp/postscript.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_imp_postscript_h #define gltf_imp_postscript_h #include "common.h" AK_HIDE void gltf_postscript(AkGLTFState * __restrict gst); #endif /* gltf_imp_postscript_h */ ================================================ FILE: src/io/gltf/strpool.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _GLTF_STRPOOL_ # define _GLTF_STRPOOL_ #endif #include "strpool.h" #include const char _s_gltf_pool_0[] = " \0" "gltf\0" "asset\0" "version\0" "generator\0" "copyright\0" "meter\0" "buffers\0" "uri\0" "byteLength\0" "bufferViews\0" "buffer\0" "byteOffset\0" "byteStride\0" "bufferView\0" "componentType\0" "normalized\0" "count\0" "type\0" "max\0" "min\0" "values\0" "meshes\0" "primitives\0" "attributes\0" "indices\0" "mode\0" "nodes\0" "node\0" "mesh\0" "scene\0" "scenes\0" "name\0" "accessors\0" "POSITION\0" "NORMAL\0" "TANGENT\0" "TEXCOORD\0" "COLOR\0" "JOINTS\0" "WEIGHTS\0" "SCALAR\0" "VEC2\0" "VEC3\0" "VEC4\0" "MAT2\0" "MAT3\0" "MAT4\0" "children\0" "matrix\0" "rotation\0" "scale\0" "translation\0" "weights\0" "extensions\0" "extensionsUsed\0" "extensionsRequired\0" "extras\0" "skin\0" "morph\0" "skins\0" "camera\0" "cameras\0" "light\0" "lights\0" "visible\0" "color\0" "intensity\0" "range\0" "spot\0" "innerConeAngle\0" "outerConeAngle\0" "directional\0" "point\0" "perspective\0" "orthographic\0" "aspectRatio\0" "xfov\0" "yfov\0" "zfar\0" "znear\0" "xmag\0" "ymag\0" "ak_flg\0" "VERTEX\0" "images\0" "image\0" "sampler\0" "samplers\0" "wrapS\0" "wrapT\0" "magFilter\0" "minFilter\0" "textures\0" "source\0" "material\0" "materials\0" "pbrMetallicRoughness\0" "normalTexture\0" "occlusionTexture\0" "strength\0" "emissiveTexture\0" "emissiveFactor\0" "alphaMode\0" "alphaCutoff\0" "doubleSided\0" "baseColorFactor\0" "baseColorTexture\0" "metallicFactor\0" "roughnessFactor\0" "metallicRoughnessTexture\0" "index\0" "texCoord\0" "TEXCOORD\0" "smp\0" "specgloss\0" "KHR_materials_pbrSpecularGlossiness\0" "KHR_materials_specular\0" "KHR_materials_ior\0" "specularFactor\0" "specularTexture\0" "specularColorFactor\0" "specularColorTexture\0" "diffuseFactor\0" "specularFactor\0" "diffuseTexture\0" "glossinessFactor\0" "specularGlossinessTexture\0" "BLEND\0" "MASK\0" "OPAQUE\0" "animations\0" "channels\0" "target\0" "targets\0" "targetNames\0" "morphPresets\0" "path\0" "pointer\0" "interpolation\0" "input\0" "output\0" "anim\0" "LINEAR\0" "STEP\0" "CUBICSPLINE\0" "inverseBindMatrices\0" "joints\0" "skeleton\0" "sparse\0" "data:\0" "offset\0" "fallback\0" "filter\0" "ATTRIBUTES\0" "TRIANGLES\0" "INDICES\0" "NONE\0" "OCTAHEDRAL\0" "QUATERNION\0" "EXPONENTIAL\0" "KHR_texture_transform\0" "KHR_animation_pointer\0" "KHR_node_visibility\0" "KHR_draco_mesh_compression\0" "KHR_mesh_quantization\0" "KHR_meshopt_compression\0" "EXT_meshopt_compression\0" "EXT_mesh_gpu_instancing\0" "EXT_texture_webp\0" "EXT_lights_image_based\0" "EXT_lights_ies\0" "EXT_mesh_manifold\0" "EXT_mesh_primitive_restart\0" "EXT_texture_astc\0" "EXT_gsplat_compression_spz\0" "ADOBE_materials_clearcoat_specular\0" "ADOBE_materials_clearcoat_tint\0" "ADOBE_materials_thin_transparency\0" "AGI_articulations\0" "AGI_stk_metadata\0" "CESIUM_primitive_outline\0" "FB_geometry_metadata\0" ; const char _s_gltf_pool_1[] = "GODOT_single_root\0" "GRIFFEL_bim_data\0" "MPEG_accessor_timed\0" "MPEG_animation_timing\0" "MPEG_audio_spatial\0" "MPEG_buffer_circular\0" "MPEG_media\0" "MPEG_mesh_linking\0" "MPEG_scene_dynamic\0" "MPEG_texture_video\0" "MPEG_viewport_recommended\0" "MSFT_lod\0" "MSFT_packing_normalRoughnessMetallic\0" "MSFT_packing_occlusionRoughnessMetallic\0" "MSFT_texture_dds\0" "NV_materials_mdl\0" "KHR_lights_punctual\0" "KHR_materials_variants\0" "KHR_texture_basisu\0" "KHR_techniques_webgl\0" "KHR_xmp\0" "KHR_xmp_json_ld\0" "KHR_gaussian_splatting\0" "packet\0" "packets\0" "kernel\0" "colorSpace\0" "projection\0" "sortingMethod\0" "compression\0" "format\0" "spz\0" "variants\0" "mappings\0" "variant\0" "KHR_materials_clearcoat\0" "clearcoatFactor\0" "clearcoatTexture\0" "clearcoatRoughnessFactor\0" "clearcoatRoughnessTexture\0" "clearcoatNormalTexture\0" "KHR_materials_ior\0" "ior\0" "KHR_materials_unlit\0" "KHR_materials_emissive_strength\0" "emissiveStrength\0" "KHR_materials_transmission\0" "transmissionFactor\0" "transmissionTexture\0" "KHR_materials_sheen\0" "sheenColorFactor\0" "sheenColorTexture\0" "sheenRoughnessFactor\0" "sheenRoughnessTexture\0" "KHR_materials_iridescence\0" "iridescenceFactor\0" "iridescenceTexture\0" "iridescenceIor\0" "iridescenceThicknessMinimum\0" "iridescenceThicknessMaximum\0" "iridescenceThicknessTexture\0" "KHR_materials_volume\0" "thicknessFactor\0" "thicknessTexture\0" "attenuationDistance\0" "attenuationColor\0" "KHR_materials_anisotropy\0" "anisotropyStrength\0" "anisotropyRotation\0" "anisotropyTexture\0" "KHR_materials_dispersion\0" "dispersion\0" "KHR_materials_diffuse_transmission\0" "diffuseTransmissionFactor\0" "diffuseTransmissionTexture\0" "diffuseTransmissionColorFactor\0" "diffuseTransmissionColorTexture\0" ; #undef _GLTF_STRPOOL_ ================================================ FILE: src/io/gltf/strpool.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef gltf_strpool_h # define gltf_strpool_h #ifndef _GLTF_STRPOOL_ # define _AK_EXTERN extern #else # define _AK_EXTERN #endif _AK_EXTERN const char _s_gltf_pool_0[]; _AK_EXTERN const char _s_gltf_pool_1[]; #define _s_gltf_0(x) (_s_gltf_pool_0 + x) #define _s_gltf_1(x) (_s_gltf_pool_1 + x) /* _s_gltf_pool_0 */ #define _s_gltf_space _s_gltf_0(0) #define _s_gltf_gltf _s_gltf_0(2) #define _s_gltf_asset _s_gltf_0(7) #define _s_gltf_version _s_gltf_0(13) #define _s_gltf_generator _s_gltf_0(21) #define _s_gltf_copyright _s_gltf_0(31) #define _s_gltf_meter _s_gltf_0(41) #define _s_gltf_buffers _s_gltf_0(47) #define _s_gltf_uri _s_gltf_0(55) #define _s_gltf_byteLength _s_gltf_0(59) #define _s_gltf_bufferViews _s_gltf_0(70) #define _s_gltf_buffer _s_gltf_0(82) #define _s_gltf_byteOffset _s_gltf_0(89) #define _s_gltf_byteStride _s_gltf_0(100) #define _s_gltf_bufferView _s_gltf_0(111) #define _s_gltf_componentType _s_gltf_0(122) #define _s_gltf_normalized _s_gltf_0(136) #define _s_gltf_count _s_gltf_0(147) #define _s_gltf_type _s_gltf_0(153) #define _s_gltf_max _s_gltf_0(158) #define _s_gltf_min _s_gltf_0(162) #define _s_gltf_values _s_gltf_0(166) #define _s_gltf_meshes _s_gltf_0(173) #define _s_gltf_primitives _s_gltf_0(180) #define _s_gltf_attributes _s_gltf_0(191) #define _s_gltf_indices _s_gltf_0(202) #define _s_gltf_mode _s_gltf_0(210) #define _s_gltf_nodes _s_gltf_0(215) #define _s_gltf_node _s_gltf_0(221) #define _s_gltf_mesh _s_gltf_0(226) #define _s_gltf_scene _s_gltf_0(231) #define _s_gltf_scenes _s_gltf_0(237) #define _s_gltf_name _s_gltf_0(244) #define _s_gltf_accessors _s_gltf_0(249) #define _s_gltf_POSITION _s_gltf_0(259) #define _s_gltf_NORMAL _s_gltf_0(268) #define _s_gltf_TANGENT _s_gltf_0(275) #define _s_gltf_TEXCOORD _s_gltf_0(283) #define _s_gltf_COLOR _s_gltf_0(292) #define _s_gltf_JOINTS _s_gltf_0(298) #define _s_gltf_WEIGHTS _s_gltf_0(305) #define _s_gltf_SCALAR _s_gltf_0(313) #define _s_gltf_VEC2 _s_gltf_0(320) #define _s_gltf_VEC3 _s_gltf_0(325) #define _s_gltf_VEC4 _s_gltf_0(330) #define _s_gltf_MAT2 _s_gltf_0(335) #define _s_gltf_MAT3 _s_gltf_0(340) #define _s_gltf_MAT4 _s_gltf_0(345) #define _s_gltf_children _s_gltf_0(350) #define _s_gltf_matrix _s_gltf_0(359) #define _s_gltf_rotation _s_gltf_0(366) #define _s_gltf_scale _s_gltf_0(375) #define _s_gltf_translation _s_gltf_0(381) #define _s_gltf_weights _s_gltf_0(393) #define _s_gltf_extensions _s_gltf_0(401) #define _s_gltf_extensionsUsed _s_gltf_0(412) #define _s_gltf_extensionsRequired _s_gltf_0(427) #define _s_gltf_extras _s_gltf_0(446) #define _s_gltf_skin _s_gltf_0(453) #define _s_gltf_morph _s_gltf_0(458) #define _s_gltf_skins _s_gltf_0(464) #define _s_gltf_camera _s_gltf_0(470) #define _s_gltf_cameras _s_gltf_0(477) #define _s_gltf_light _s_gltf_0(485) #define _s_gltf_lights _s_gltf_0(491) #define _s_gltf_visible _s_gltf_0(498) #define _s_gltf_color _s_gltf_0(506) #define _s_gltf_intensity _s_gltf_0(512) #define _s_gltf_range _s_gltf_0(522) #define _s_gltf_spot _s_gltf_0(528) #define _s_gltf_innerConeAngle _s_gltf_0(533) #define _s_gltf_outerConeAngle _s_gltf_0(548) #define _s_gltf_directional _s_gltf_0(563) #define _s_gltf_point _s_gltf_0(575) #define _s_gltf_perspective _s_gltf_0(581) #define _s_gltf_orthographic _s_gltf_0(593) #define _s_gltf_aspectRatio _s_gltf_0(606) #define _s_gltf_xfov _s_gltf_0(618) #define _s_gltf_yfov _s_gltf_0(623) #define _s_gltf_zfar _s_gltf_0(628) #define _s_gltf_znear _s_gltf_0(633) #define _s_gltf_xmag _s_gltf_0(639) #define _s_gltf_ymag _s_gltf_0(644) #define _s_gltf_ak_flg _s_gltf_0(649) #define _s_gltf_VERTEX _s_gltf_0(656) #define _s_gltf_images _s_gltf_0(663) #define _s_gltf_image _s_gltf_0(670) #define _s_gltf_sampler _s_gltf_0(676) #define _s_gltf_samplers _s_gltf_0(684) #define _s_gltf_wrapS _s_gltf_0(693) #define _s_gltf_wrapT _s_gltf_0(699) #define _s_gltf_magFilter _s_gltf_0(705) #define _s_gltf_minFilter _s_gltf_0(715) #define _s_gltf_textures _s_gltf_0(725) #define _s_gltf_source _s_gltf_0(734) #define _s_gltf_material _s_gltf_0(741) #define _s_gltf_materials _s_gltf_0(750) #define _s_gltf_pbrMetalRough _s_gltf_0(760) #define _s_gltf_normalTex _s_gltf_0(781) #define _s_gltf_occlusionTex _s_gltf_0(795) #define _s_gltf_strength _s_gltf_0(812) #define _s_gltf_emissiveTex _s_gltf_0(821) #define _s_gltf_emissiveFac _s_gltf_0(837) #define _s_gltf_alphaMode _s_gltf_0(852) #define _s_gltf_alphaCutoff _s_gltf_0(862) #define _s_gltf_doubleSided _s_gltf_0(874) #define _s_gltf_baseColor _s_gltf_0(886) #define _s_gltf_baseColorTex _s_gltf_0(902) #define _s_gltf_metalFac _s_gltf_0(919) #define _s_gltf_roughFac _s_gltf_0(934) #define _s_gltf_metalRoughTex _s_gltf_0(950) #define _s_gltf_index _s_gltf_0(975) #define _s_gltf_texCoord _s_gltf_0(981) #define _s_gltf_texcoordPrefix _s_gltf_0(990) #define _s_gltf_sid_sampler _s_gltf_0(999) #define _s_gltf_id_specgloss _s_gltf_0(1003) #define _s_gltf_KHR_materials_pbrSpecularGlossiness _s_gltf_0(1013) #define _s_gltf_KHR_materials_specular _s_gltf_0(1049) #define _s_gltf_ext_KHR_materials_ior _s_gltf_0(1072) #define _s_gltf_specularFactor _s_gltf_0(1090) #define _s_gltf_specularTexture _s_gltf_0(1105) #define _s_gltf_specularColorFactor _s_gltf_0(1121) #define _s_gltf_specularColorTexture _s_gltf_0(1141) #define _s_gltf_diffuseFactor _s_gltf_0(1162) #define _s_gltf_specFactor _s_gltf_0(1176) #define _s_gltf_diffuseTexture _s_gltf_0(1191) #define _s_gltf_glossFactor _s_gltf_0(1206) #define _s_gltf_specGlossTex _s_gltf_0(1223) #define _s_gltf_BLEND _s_gltf_0(1249) #define _s_gltf_MASK _s_gltf_0(1255) #define _s_gltf_OPAQUE _s_gltf_0(1260) #define _s_gltf_animations _s_gltf_0(1267) #define _s_gltf_channels _s_gltf_0(1278) #define _s_gltf_target _s_gltf_0(1287) #define _s_gltf_targets _s_gltf_0(1294) #define _s_gltf_targetNames _s_gltf_0(1302) #define _s_gltf_morphPresets _s_gltf_0(1314) #define _s_gltf_path _s_gltf_0(1327) #define _s_gltf_pointer _s_gltf_0(1332) #define _s_gltf_interpolation _s_gltf_0(1340) #define _s_gltf_input _s_gltf_0(1354) #define _s_gltf_output _s_gltf_0(1360) #define _s_gltf_anim _s_gltf_0(1367) #define _s_gltf_LINEAR _s_gltf_0(1372) #define _s_gltf_STEP _s_gltf_0(1379) #define _s_gltf_CUBICSPLINE _s_gltf_0(1384) #define _s_gltf_inverseBindMatrices _s_gltf_0(1396) #define _s_gltf_joints _s_gltf_0(1416) #define _s_gltf_skeleton _s_gltf_0(1423) #define _s_gltf_sparse _s_gltf_0(1432) #define _s_gltf_b64d _s_gltf_0(1439) #define _s_gltf_offset _s_gltf_0(1445) #define _s_gltf_fallback _s_gltf_0(1452) #define _s_gltf_filter _s_gltf_0(1461) #define _s_gltf_ATTRIBUTES _s_gltf_0(1468) #define _s_gltf_TRIANGLES _s_gltf_0(1479) #define _s_gltf_INDICES _s_gltf_0(1489) #define _s_gltf_NONE _s_gltf_0(1497) #define _s_gltf_OCTAHEDRAL _s_gltf_0(1502) #define _s_gltf_QUATERNION _s_gltf_0(1513) #define _s_gltf_EXPONENTIAL _s_gltf_0(1524) #define _s_gltf_KHR_texture_transform _s_gltf_0(1536) #define _s_gltf_KHR_animation_pointer _s_gltf_0(1558) #define _s_gltf_KHR_node_visibility _s_gltf_0(1580) #define _s_gltf_KHR_draco_mesh_compression _s_gltf_0(1600) #define _s_gltf_KHR_mesh_quantization _s_gltf_0(1627) #define _s_gltf_KHR_meshopt_compression _s_gltf_0(1649) #define _s_gltf_EXT_meshopt_compression _s_gltf_0(1673) #define _s_gltf_EXT_mesh_gpu_instancing _s_gltf_0(1697) #define _s_gltf_EXT_texture_webp _s_gltf_0(1721) #define _s_gltf_EXT_lights_image_based _s_gltf_0(1738) #define _s_gltf_EXT_lights_ies _s_gltf_0(1761) #define _s_gltf_EXT_mesh_manifold _s_gltf_0(1776) #define _s_gltf_EXT_mesh_primitive_restart _s_gltf_0(1794) #define _s_gltf_EXT_texture_astc _s_gltf_0(1821) #define _s_gltf_EXT_gsplat_compression_spz _s_gltf_0(1838) #define _s_gltf_ADOBE_materials_clearcoat_specular _s_gltf_0(1865) #define _s_gltf_ADOBE_materials_clearcoat_tint _s_gltf_0(1900) #define _s_gltf_ADOBE_materials_thin_transparency _s_gltf_0(1931) #define _s_gltf_AGI_articulations _s_gltf_0(1965) #define _s_gltf_AGI_stk_metadata _s_gltf_0(1983) #define _s_gltf_CESIUM_primitive_outline _s_gltf_0(2000) #define _s_gltf_FB_geometry_metadata _s_gltf_0(2025) /* _s_gltf_pool_1 */ #define _s_gltf_GODOT_single_root _s_gltf_1(0) #define _s_gltf_GRIFFEL_bim_data _s_gltf_1(18) #define _s_gltf_MPEG_accessor_timed _s_gltf_1(35) #define _s_gltf_MPEG_animation_timing _s_gltf_1(55) #define _s_gltf_MPEG_audio_spatial _s_gltf_1(77) #define _s_gltf_MPEG_buffer_circular _s_gltf_1(96) #define _s_gltf_MPEG_media _s_gltf_1(117) #define _s_gltf_MPEG_mesh_linking _s_gltf_1(128) #define _s_gltf_MPEG_scene_dynamic _s_gltf_1(146) #define _s_gltf_MPEG_texture_video _s_gltf_1(165) #define _s_gltf_MPEG_viewport_recommended _s_gltf_1(184) #define _s_gltf_MSFT_lod _s_gltf_1(210) #define _s_gltf_MSFT_packing_normalRoughnessMetallic _s_gltf_1(219) #define _s_gltf_MSFT_packing_occlusionRoughnessMetallic _s_gltf_1(256) #define _s_gltf_MSFT_texture_dds _s_gltf_1(296) #define _s_gltf_NV_materials_mdl _s_gltf_1(313) #define _s_gltf_KHR_lights_punctual _s_gltf_1(330) #define _s_gltf_KHR_materials_variants _s_gltf_1(350) #define _s_gltf_KHR_texture_basisu _s_gltf_1(373) #define _s_gltf_KHR_techniques_webgl _s_gltf_1(392) #define _s_gltf_KHR_xmp _s_gltf_1(413) #define _s_gltf_KHR_xmp_json_ld _s_gltf_1(421) #define _s_gltf_KHR_gaussian_splatting _s_gltf_1(437) #define _s_gltf_packet _s_gltf_1(460) #define _s_gltf_packets _s_gltf_1(467) #define _s_gltf_kernel _s_gltf_1(475) #define _s_gltf_colorSpace _s_gltf_1(482) #define _s_gltf_projection _s_gltf_1(493) #define _s_gltf_sortingMethod _s_gltf_1(504) #define _s_gltf_compression _s_gltf_1(518) #define _s_gltf_format _s_gltf_1(530) #define _s_gltf_spz _s_gltf_1(537) #define _s_gltf_variants _s_gltf_1(541) #define _s_gltf_mappings _s_gltf_1(550) #define _s_gltf_variant _s_gltf_1(559) #define _s_gltf_KHR_materials_clearcoat _s_gltf_1(567) #define _s_gltf_clearcoatFactor _s_gltf_1(591) #define _s_gltf_clearcoatTexture _s_gltf_1(607) #define _s_gltf_clearcoatRoughnessFactor _s_gltf_1(624) #define _s_gltf_clearcoatRoughnessTexture _s_gltf_1(649) #define _s_gltf_clearcoatNormalTexture _s_gltf_1(675) #define _s_gltf_KHR_materials_ior _s_gltf_1(698) #define _s_gltf_ior _s_gltf_1(716) #define _s_gltf_KHR_materials_unlit _s_gltf_1(720) #define _s_gltf_KHR_materials_emissive_strength _s_gltf_1(740) #define _s_gltf_emissiveStrength _s_gltf_1(772) #define _s_gltf_KHR_materials_transmission _s_gltf_1(789) #define _s_gltf_transmissionFactor _s_gltf_1(816) #define _s_gltf_transmissionTexture _s_gltf_1(835) #define _s_gltf_KHR_materials_sheen _s_gltf_1(855) #define _s_gltf_sheenColorFactor _s_gltf_1(875) #define _s_gltf_sheenColorTexture _s_gltf_1(892) #define _s_gltf_sheenRoughnessFactor _s_gltf_1(910) #define _s_gltf_sheenRoughnessTexture _s_gltf_1(931) #define _s_gltf_KHR_materials_iridescence _s_gltf_1(953) #define _s_gltf_iridescenceFactor _s_gltf_1(979) #define _s_gltf_iridescenceTexture _s_gltf_1(997) #define _s_gltf_iridescenceIor _s_gltf_1(1016) #define _s_gltf_iridescenceThicknessMinimum _s_gltf_1(1031) #define _s_gltf_iridescenceThicknessMaximum _s_gltf_1(1059) #define _s_gltf_iridescenceThicknessTexture _s_gltf_1(1087) #define _s_gltf_KHR_materials_volume _s_gltf_1(1115) #define _s_gltf_thicknessFactor _s_gltf_1(1136) #define _s_gltf_thicknessTexture _s_gltf_1(1152) #define _s_gltf_attenuationDistance _s_gltf_1(1169) #define _s_gltf_attenuationColor _s_gltf_1(1189) #define _s_gltf_KHR_materials_anisotropy _s_gltf_1(1206) #define _s_gltf_anisotropyStrength _s_gltf_1(1231) #define _s_gltf_anisotropyRotation _s_gltf_1(1250) #define _s_gltf_anisotropyTexture _s_gltf_1(1269) #define _s_gltf_KHR_materials_dispersion _s_gltf_1(1287) #define _s_gltf_dispersion _s_gltf_1(1312) #define _s_gltf_KHR_materials_diffuse_transmission _s_gltf_1(1323) #define _s_gltf_diffuseTransmissionFactor _s_gltf_1(1358) #define _s_gltf_diffuseTransmissionTexture _s_gltf_1(1384) #define _s_gltf_diffuseTransmissionColorFactor _s_gltf_1(1411) #define _s_gltf_diffuseTransmissionColorTexture _s_gltf_1(1442) #endif /* gltf_strpool_h */ ================================================ FILE: src/io/gltf/strpool.json ================================================ { "space": " ", "gltf": "gltf", "asset": "asset", "version": "version", "generator": "generator", "copyright": "copyright", "meter": "meter", "buffers": "buffers", "uri": "uri", "byteLength": "byteLength", "bufferViews": "bufferViews", "buffer": "buffer", "byteOffset": "byteOffset", "byteStride": "byteStride", "bufferView": "bufferView", "componentType": "componentType", "normalized": "normalized", "count": "count", "type": "type", "max": "max", "min": "min", "values": "values", "meshes": "meshes", "primitives": "primitives", "attributes": "attributes", "indices": "indices", "mode": "mode", "nodes": "nodes", "node": "node", "mesh": "mesh", "scene": "scene", "scenes": "scenes", "name": "name", "accessors": "accessors", "POSITION": "POSITION", "NORMAL": "NORMAL", "TANGENT": "TANGENT", "TEXCOORD": "TEXCOORD", "COLOR": "COLOR", "JOINTS": "JOINTS", "WEIGHTS": "WEIGHTS", "SCALAR": "SCALAR", "VEC2": "VEC2", "VEC3": "VEC3", "VEC4": "VEC4", "MAT2": "MAT2", "MAT3": "MAT3", "MAT4": "MAT4", "children": "children", "matrix": "matrix", "rotation": "rotation", "scale": "scale", "translation": "translation", "weights": "weights", "extensions": "extensions", "extensionsUsed": "extensionsUsed", "extensionsRequired": "extensionsRequired", "extras": "extras", "skin": "skin", "morph": "morph", "skins": "skins", "camera": "camera", "cameras": "cameras", "light": "light", "lights": "lights", "visible": "visible", "color": "color", "intensity": "intensity", "range": "range", "spot": "spot", "innerConeAngle": "innerConeAngle", "outerConeAngle": "outerConeAngle", "directional": "directional", "point": "point", "perspective": "perspective", "orthographic": "orthographic", "aspectRatio": "aspectRatio", "xfov": "xfov", "yfov": "yfov", "zfar": "zfar", "znear": "znear", "xmag": "xmag", "ymag": "ymag", "ak_flg": "ak_flg", "VERTEX": "VERTEX", "images": "images", "image": "image", "sampler": "sampler", "samplers": "samplers", "wrapS": "wrapS", "wrapT": "wrapT", "magFilter": "magFilter", "minFilter": "minFilter", "textures": "textures", "source": "source", "material": "material", "materials": "materials", "pbrMetalRough": "pbrMetallicRoughness", "normalTex": "normalTexture", "occlusionTex": "occlusionTexture", "strength": "strength", "emissiveTex": "emissiveTexture", "emissiveFac": "emissiveFactor", "alphaMode": "alphaMode", "alphaCutoff": "alphaCutoff", "doubleSided": "doubleSided", "baseColor": "baseColorFactor", "baseColorTex": "baseColorTexture", "metalFac": "metallicFactor", "roughFac": "roughnessFactor", "metalRoughTex": "metallicRoughnessTexture", "index": "index", "texCoord": "texCoord", "texcoordPrefix": "TEXCOORD", "sid_sampler": "smp", "id_specgloss": "specgloss", "KHR_materials_pbrSpecularGlossiness": "KHR_materials_pbrSpecularGlossiness", "KHR_materials_specular": "KHR_materials_specular", "ext_KHR_materials_ior": "KHR_materials_ior", "specularFactor": "specularFactor", "specularTexture": "specularTexture", "specularColorFactor": "specularColorFactor", "specularColorTexture": "specularColorTexture", "diffuseFactor": "diffuseFactor", "specFactor": "specularFactor", "diffuseTexture": "diffuseTexture", "glossFactor": "glossinessFactor", "specGlossTex": "specularGlossinessTexture", "BLEND": "BLEND", "MASK": "MASK", "OPAQUE": "OPAQUE", "animations": "animations", "channels": "channels", "target": "target", "targets": "targets", "targetNames": "targetNames", "morphPresets": "morphPresets", "path": "path", "pointer": "pointer", "interpolation": "interpolation", "input": "input", "output": "output", "anim": "anim", "LINEAR": "LINEAR", "STEP": "STEP", "CUBICSPLINE": "CUBICSPLINE", "inverseBindMatrices": "inverseBindMatrices", "joints": "joints", "skeleton": "skeleton", "sparse": "sparse", "b64d": "data:", "offset": "offset", "fallback": "fallback", "filter": "filter", "ATTRIBUTES": "ATTRIBUTES", "TRIANGLES": "TRIANGLES", "INDICES": "INDICES", "NONE": "NONE", "OCTAHEDRAL": "OCTAHEDRAL", "QUATERNION": "QUATERNION", "EXPONENTIAL": "EXPONENTIAL", "KHR_texture_transform": "KHR_texture_transform", "KHR_animation_pointer": "KHR_animation_pointer", "KHR_node_visibility": "KHR_node_visibility", "KHR_draco_mesh_compression": "KHR_draco_mesh_compression", "KHR_mesh_quantization": "KHR_mesh_quantization", "KHR_meshopt_compression": "KHR_meshopt_compression", "EXT_meshopt_compression": "EXT_meshopt_compression", "EXT_mesh_gpu_instancing": "EXT_mesh_gpu_instancing", "EXT_texture_webp": "EXT_texture_webp", "EXT_lights_image_based": "EXT_lights_image_based", "EXT_lights_ies": "EXT_lights_ies", "EXT_mesh_manifold": "EXT_mesh_manifold", "EXT_mesh_primitive_restart": "EXT_mesh_primitive_restart", "EXT_texture_astc": "EXT_texture_astc", "EXT_gsplat_compression_spz": "EXT_gsplat_compression_spz", "ADOBE_materials_clearcoat_specular": "ADOBE_materials_clearcoat_specular", "ADOBE_materials_clearcoat_tint": "ADOBE_materials_clearcoat_tint", "ADOBE_materials_thin_transparency": "ADOBE_materials_thin_transparency", "AGI_articulations": "AGI_articulations", "AGI_stk_metadata": "AGI_stk_metadata", "CESIUM_primitive_outline": "CESIUM_primitive_outline", "FB_geometry_metadata": "FB_geometry_metadata", "GODOT_single_root": "GODOT_single_root", "GRIFFEL_bim_data": "GRIFFEL_bim_data", "MPEG_accessor_timed": "MPEG_accessor_timed", "MPEG_animation_timing": "MPEG_animation_timing", "MPEG_audio_spatial": "MPEG_audio_spatial", "MPEG_buffer_circular": "MPEG_buffer_circular", "MPEG_media": "MPEG_media", "MPEG_mesh_linking": "MPEG_mesh_linking", "MPEG_scene_dynamic": "MPEG_scene_dynamic", "MPEG_texture_video": "MPEG_texture_video", "MPEG_viewport_recommended": "MPEG_viewport_recommended", "MSFT_lod": "MSFT_lod", "MSFT_packing_normalRoughnessMetallic": "MSFT_packing_normalRoughnessMetallic", "MSFT_packing_occlusionRoughnessMetallic": "MSFT_packing_occlusionRoughnessMetallic", "MSFT_texture_dds": "MSFT_texture_dds", "NV_materials_mdl": "NV_materials_mdl", "KHR_lights_punctual": "KHR_lights_punctual", "KHR_materials_variants": "KHR_materials_variants", "KHR_texture_basisu": "KHR_texture_basisu", "KHR_techniques_webgl": "KHR_techniques_webgl", "KHR_xmp": "KHR_xmp", "KHR_xmp_json_ld": "KHR_xmp_json_ld", "KHR_gaussian_splatting": "KHR_gaussian_splatting", "packet": "packet", "packets": "packets", "kernel": "kernel", "colorSpace": "colorSpace", "projection": "projection", "sortingMethod": "sortingMethod", "compression": "compression", "format": "format", "spz": "spz", "variants": "variants", "mappings": "mappings", "variant": "variant", "KHR_materials_clearcoat": "KHR_materials_clearcoat", "clearcoatFactor": "clearcoatFactor", "clearcoatTexture": "clearcoatTexture", "clearcoatRoughnessFactor": "clearcoatRoughnessFactor", "clearcoatRoughnessTexture": "clearcoatRoughnessTexture", "clearcoatNormalTexture": "clearcoatNormalTexture", "KHR_materials_ior": "KHR_materials_ior", "ior": "ior", "KHR_materials_unlit": "KHR_materials_unlit", "KHR_materials_emissive_strength": "KHR_materials_emissive_strength", "emissiveStrength": "emissiveStrength", "KHR_materials_transmission": "KHR_materials_transmission", "transmissionFactor": "transmissionFactor", "transmissionTexture": "transmissionTexture", "KHR_materials_sheen": "KHR_materials_sheen", "sheenColorFactor": "sheenColorFactor", "sheenColorTexture": "sheenColorTexture", "sheenRoughnessFactor": "sheenRoughnessFactor", "sheenRoughnessTexture": "sheenRoughnessTexture", "KHR_materials_iridescence": "KHR_materials_iridescence", "iridescenceFactor": "iridescenceFactor", "iridescenceTexture": "iridescenceTexture", "iridescenceIor": "iridescenceIor", "iridescenceThicknessMinimum": "iridescenceThicknessMinimum", "iridescenceThicknessMaximum": "iridescenceThicknessMaximum", "iridescenceThicknessTexture": "iridescenceThicknessTexture", "KHR_materials_volume": "KHR_materials_volume", "thicknessFactor": "thicknessFactor", "thicknessTexture": "thicknessTexture", "attenuationDistance": "attenuationDistance", "attenuationColor": "attenuationColor", "KHR_materials_anisotropy": "KHR_materials_anisotropy", "anisotropyStrength": "anisotropyStrength", "anisotropyRotation": "anisotropyRotation", "anisotropyTexture": "anisotropyTexture", "KHR_materials_dispersion": "KHR_materials_dispersion", "dispersion": "dispersion", "KHR_materials_diffuse_transmission": "KHR_materials_diffuse_transmission", "diffuseTransmissionFactor": "diffuseTransmissionFactor", "diffuseTransmissionTexture": "diffuseTransmissionTexture", "diffuseTransmissionColorFactor": "diffuseTransmissionColorFactor", "diffuseTransmissionColorTexture": "diffuseTransmissionColorTexture" } ================================================ FILE: src/io/gltf/strpool.py ================================================ #!/usr/bin/python3 # # Copyright (C) 2020 Recep Aslantas # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # import json from collections import OrderedDict from os.path import realpath from os.path import dirname headerContents = [ ] destdir = dirname(realpath(__file__)) spidx = 0 pos = 0 fspoolJson = open(destdir + "/strpool.json") spool = json.loads(fspoolJson.read(), object_pairs_hook=OrderedDict) fspoolJson.close() fspool_h = open(destdir + "/strpool.h", "wb"); fspool_c = open(destdir + "/strpool.c", "wb"); copyright_str = """\ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ """ fspool_h.write(copyright_str.encode()) fspool_c.write(copyright_str.encode()) fspool_h.write(""" #ifndef gltf_strpool_h # define gltf_strpool_h #ifndef _GLTF_STRPOOL_ # define _AK_EXTERN extern #else # define _AK_EXTERN #endif """.encode()) fspool_c.write(""" #ifndef _GLTF_STRPOOL_ # define _GLTF_STRPOOL_ #endif #include "strpool.h" #include const char _s_gltf_pool_0[] = """.encode()) headerContents.append("\n/* _s_gltf_pool_0 */\n") for name, val in spool.items(): valLen = len(val) + 1 # string literal size: 2048 if pos + valLen > 2048: pos = 0 spidx += 1 fspool_c.write(";\n\nconst char _s_gltf_pool_{0}[] =\n" .format(str(spidx)).encode()) headerContents.append("\n/* _s_gltf_pool_{0} */\n" .format(spidx)) fspool_c.write("\"{0}\\0\"\n".format(val).encode()) headerContents.append("#define _s_gltf_{0} _s_gltf_{1}({2})\n" .format(name, str(spidx), str(pos))) pos += valLen # source file, then close it fspool_c.write(";\n\n#undef _GLTF_STRPOOL_\n".encode()) fspool_c.close() # header file for idx in range(spidx + 1): fspool_h.write("\n_AK_EXTERN const char _s_gltf_pool_{0}[];" .format(str(idx)).encode()) fspool_h.write("\n\n".encode()) for idx in range(spidx + 1): fspool_h.write("#define _s_gltf_{0}(x) (_s_gltf_pool_{0} + x)\n" .format(str(idx)).encode()) # write header contents, then close it fspool_h.writelines(map(lambda x: x.encode(), headerContents)) fspool_h.write("\n#endif /* gltf_strpool_h */\n".encode()) fspool_h.close() # try free array del headerContents[:] ================================================ FILE: src/io/obj/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/io/obj/README.md ================================================ # AssetKit: Wavefront Object Status - [x] v - Positions - [x] vt - Texture Coords - [x] vn - Normals - [x] f - Faces/Indices - [ ] Materials - [x] d - Dissolve - [x] Tr - Transparent (1 - d) - [x] Ka - Ambient - [x] Kd - Diffuse - [x] Ks - Specular - [x] Ke - Emission - [x] map_Ka - Ambient Texture - [x] map_Kd - Diffuse Texture - [x] map_Ks - Specular Texture - [x] map_Ke - Emission Texture - [x] Ns - Shininess/specular exponent - [x] Ni - Index of refraction - [x] bump - Bump/Normal map - [x] illum: Constant - [x] illum: Lambert - [x] illum: Blinn - [ ] illum: Others - [x] Convert Multi Indices to Single Index Buffer - [x] Group/Separate mesh primitives by usemtl - [ ] ... ================================================ FILE: src/io/obj/common.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wobj_commoh_h #define wobj_commoh_h #include "../../../include/ak/assetkit.h" #include "../../common.h" #include "../../utils.h" #include "../../tree.h" #include "../../json.h" #include "../../data.h" #include #include typedef struct WOMtl { char *name; char *map_Ka; char *map_Kd; char *map_Ks; char *map_Ke; char *map_Ns; char *map_d; char *decal; char *disp; char *bump; vec3 Ka; vec3 Kd; vec3 Ks; vec3 Ke; vec3 Tf; float Ni; float Ns; /* exponent */ float d; /* dissolve */ float dHalo; /* dissolve halo */ float Tr; /* Transparent (1 - d) */ float sharpness; /* The default is 60 */ int illum; bool map_aat; bool has_Ns; } WOMtl; typedef struct WOMtlLib { char *name; RBTree *materials; } WOMtlLib; typedef struct WOPrim { struct WOPrim *next; const char *mtlname; AkDataContext *dc_face; AkDataContext *dc_vcount; uint32_t maxVC; bool hasTexture; bool hasNormal; } WOPrim; typedef struct WOObject { struct WOObject *next; AkGeometry *geom; WOPrim *prim; } WOObject; typedef struct WOState { AkHeap *heap; AkDoc *doc; void *tmp; AkLibrary *lib_geom; AkNode *node; WOMtlLib *mtlib; AkDataContext *dc_pos, *dc_tex, *dc_nor; AkAccessor *ac_pos, *ac_tex, *ac_nor; WOObject *obj; } WOState; #ifdef SKIP_SPACES # undef SKIP_SPACES #endif #define SKIP_SPACES \ { \ c = p ? *p : '\0'; \ while (c != '\0' && AK_ARRAY_SPACE_CHECK) c = *++p; \ if (c == '\0') \ break; /* to break loop */ \ } #ifdef NEXT_LINE # undef NEXT_LINE #endif #define NEXT_LINE \ do { \ c = p ? *p : '\0'; \ while (p \ && p[0] != '\0' \ && !AK_ARRAY_NLINE_CHECK \ && (c = *++p) != '\0' \ && !AK_ARRAY_NLINE_CHECK); \ \ while (p \ && p[0] != '\0' \ && AK_ARRAY_NLINE_CHECK \ && (c = *++p) != '\0' \ && AK_ARRAY_NLINE_CHECK); \ } while(0); #endif /* wobj_commoh_h */ ================================================ FILE: src/io/obj/group.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "group.h" #include "util.h" #include "../../strpool.h" /* Buffer > Accessor > Input > Prim > Mesh > Geom > InstanceGeom > Node */ static void wobj_finishPrim(WOState * __restrict wst, WOObject * __restrict wo, WOPrim * __restrict wp); static void wobj_finishPrim(WOState * __restrict wst, WOObject * __restrict wo, WOPrim * __restrict wp) { AkHeap *heap; AkGeometry *geom; AkMesh *mesh; AkMeshPrimitive *prim; uint32_t inputOffset; if (wp->maxVC == 0) return; heap = wst->heap; geom = wo->geom; mesh = ak_objGet(geom->gdata); inputOffset = 0; /* finish prim */ if (wp->maxVC == 3) { AkTriangles *tri; tri = ak_heap_calloc(heap, ak_objFrom(mesh), sizeof(*tri)); tri->mode = AK_TRIANGLES; tri->base.type = AK_PRIMITIVE_TRIANGLES; prim = (AkMeshPrimitive *)tri; } else { AkPolygon *poly; poly = ak_heap_calloc(heap, ak_objFrom(mesh), sizeof(*poly)); poly->base.type = AK_PRIMITIVE_POLYGONS; poly->vcount = ak_heap_calloc(heap, poly, sizeof(*poly->vcount) + wp->dc_vcount->usedsize); poly->vcount->count = wp->dc_vcount->itemcount; poly->base.nPolygons = (uint32_t)poly->vcount->count; ak_data_join(wp->dc_vcount, poly->vcount->items, 0, 0); prim = (AkMeshPrimitive *)poly; } prim->mesh = mesh; prim->next = mesh->primitive; mesh->primitive = prim; mesh->primitiveCount++; prim->pos = wobj_input(wst, prim, wst->ac_pos, AK_INPUT_POSITION, _s_POSITION, inputOffset++); if (wst->mtlib && wp->mtlname) prim->material = rb_find(wst->mtlib->materials, (void *)wp->mtlname); if (wp->hasTexture && wst->dc_tex->itemcount > 0) wobj_input(wst, prim, wst->ac_tex, AK_INPUT_TEXCOORD, _s_TEXCOORD, inputOffset++); if (wp->hasNormal && wst->dc_nor->itemcount > 0) wobj_input(wst, prim, wst->ac_nor, AK_INPUT_NORMAL, _s_NORMAL, inputOffset); /* fix indices */ wobj_joinIndices(wst, wp, prim); if (wp->maxVC == 3) { prim->nPolygons = (uint32_t)prim->indices->count / 3; } } AK_HIDE WOPrim* wobj_switchPrim(WOState * __restrict wst, const char * __restrict mtlname) { WOPrim *wp; if ((wp = wst->obj->prim) && wp->dc_face->itemcount == 0) { wp->mtlname = mtlname; return wst->obj->prim; } wp = ak_heap_calloc(wst->heap, wst->tmp, sizeof(*wp)); wp->dc_face = ak_data_new(wst->tmp, 128, sizeof(ivec3), ak_cmp_ivec3); wp->dc_vcount = ak_data_new(wst->tmp, 128, sizeof(int32_t), NULL); wp->mtlname = mtlname; wp->next = wst->obj->prim; wst->obj->prim = wp; return wp; } AK_HIDE void wobj_finishObject(WOState * __restrict wst, WOObject * __restrict obj) { WOPrim *wp, *next; AkInstanceGeometry *instGeom; AkGeometry *geom; if (!obj->geom) return; /* clean the geom if none resource is found for default state */ if (wst->dc_pos->itemcount < 1) return; geom = obj->geom; /* add to library */ geom->base.next = wst->lib_geom->chld; wst->lib_geom->chld = &geom->base; wst->lib_geom->count++; /* make instance geeometry and attach to the root node */ instGeom = ak_instanceMakeGeom(wst->heap, wst->node, geom); if (wst->node->geometry) { wst->node->geometry->base.prev = (void *)instGeom; instGeom->base.next = (void *)wst->node->geometry; } wst->node->geometry = instGeom; /* mesh primitives */ wp = obj->prim; do { next = wp->next; wobj_finishPrim(wst, obj, wp); } while ((wp = next)); } AK_HIDE void wobj_finishObjects(WOState * __restrict wst) { WOObject *obj; obj = wst->obj; while (obj) { wobj_finishObject(wst, obj); obj = obj->next; } } AK_HIDE void wobj_switchObject(WOState * __restrict wst) { WOObject *obj; AkGeometry *geom; if (wst->obj && wst->obj->prim && wst->obj->prim->dc_face->itemcount == 0) return; obj = ak_heap_calloc(wst->heap, wst->tmp, sizeof(*obj)); obj->next = wst->obj; wst->obj = obj; ak_allocMesh(wst->heap, wst->lib_geom, &geom); /* set current geometry */ obj->geom = geom; wobj_switchPrim(wst, NULL); } ================================================ FILE: src/io/obj/group.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wobj_group_h #define wobj_group_h #include "common.h" AK_HIDE WOPrim* wobj_switchPrim(WOState * __restrict wst, const char * __restrict mtlname); AK_HIDE void wobj_finishObject(WOState * __restrict wst, WOObject * __restrict obj); AK_HIDE void wobj_finishObjects(WOState * __restrict wst); AK_HIDE void wobj_switchObject(WOState * __restrict wst); #endif /* wobj_group_h */ ================================================ FILE: src/io/obj/mtl.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "mtl.h" #include "../../strpool.h" /* Resources: https://all3dp.com/1/obj-file-format-3d-printing-cad/ http://paulbourke.net/dataformats/obj/ http://paulbourke.net/dataformats/mtl/ https://en.wikipedia.org/wiki/Wavefront_.obj_file#Material_template_library */ static AkProfileCommon* wobj_cmnEffect(WOState * __restrict wst); static void wobj_handleMaterial(WOState * __restrict wst, WOMtlLib * __restrict mtllib, WOMtl * __restrict mtl); static AkTextureRef* wobj_texref(WOState * __restrict wst, void * __restrict memp, char * name, AkTextureColorSpace colorSpace, AkTextureChannels channels); AK_HIDE WOMtlLib* wobj_mtl(WOState * __restrict wst, const char * __restrict name) { AkHeap *heap; void *mtlstr; char *p, *localurl, *begin, *end; WOMtlLib *mtllib; WOMtl *mtl; size_t mtlstrSize; char c; mtllib = NULL; localurl = ak_getFileFrom(wst->doc, name); if (ak_readfile(localurl, NULL, &mtlstr, &mtlstrSize) != AK_OK || !((p = mtlstr) && (c = *p) != '\0')) goto ret; heap = wst->heap; mtl = NULL; mtllib = ak_heap_calloc(heap, wst->tmp, sizeof(*mtllib)); mtllib->materials = rb_newtree_str(); /* parse .mtl */ do { /* skip spaces */ SKIP_SPACES if (p[0] == 'n' && p[1] == 'e' && p[2] == 'w' && p[3] == 'm' && p[4] == 't' && p[5] == 'l' && (p[6] == ' ' || p[6] == '\t')) { p += 6; SKIP_SPACES begin = p; while ((c = *++p) != '\0' && !AK_ARRAY_NLINE_CHECK); end = p; if (end > begin) { if (mtl) wobj_handleMaterial(wst, mtllib, mtl); mtl = ak_heap_calloc(heap, wst->tmp, sizeof(*mtl)); mtl->name = ak_heap_strndup(heap, mtl, begin, end - begin); /* default params */ mtl->Tr = 0.0f; mtl->d = 1.0f; mtl->illum = 1; } } else if (mtl) { if (p[1] == ' ' || p[1] == '\t') { if (p[0] == 'd') { p++; ak_strtof_line(p, 0, 1, &mtl->d); } } else if (p[2] == ' ' || p[2] == '\t') { switch (p[0]) { case 'K': switch (p[1]) { case 'a': p += 2; ak_strtof_line(p, 0, 3, mtl->Ka); break; case 'd': p += 2; ak_strtof_line(p, 0, 3, mtl->Kd); break; case 's': p += 2; ak_strtof_line(p, 0, 3, mtl->Ks); break; case 'e': p += 2; ak_strtof_line(p, 0, 3, mtl->Ke); break; default: p += 2; break; } break; case 'N': switch (p[1]) { case 's': p += 2; ak_strtof_line(p, 0, 1, &mtl->Ns); mtl->has_Ns = true; break; case 'i': p += 2; ak_strtof_line(p, 0, 1, &mtl->Ni); break; default: p += 2; break; } break; case 'T': switch (p[1]) { case 'r': p += 2; ak_strtof_line(p, 0, 1, &mtl->Tr); break; default: p += 2; break; } break; default: p += 2; break; } } else if (p[0] == 'm' && p[1] == 'a' && p[2] == 'p' && p[3] == '_') { p += 4; switch (p[0]) { case 'K': switch (p[1]) { case 'a': p += 2; SKIP_SPACES begin = p; while ((c = *++p) != '\0' && !AK_ARRAY_NLINE_CHECK); end = p; mtl->map_Ka = ak_heap_strndup(heap, mtl, begin, end - begin); break; case 'd': p += 2; SKIP_SPACES begin = p; while ((c = *++p) != '\0' && !AK_ARRAY_NLINE_CHECK); end = p; mtl->map_Kd = ak_heap_strndup(heap, mtl, begin, end - begin); break; case 's': p += 2; SKIP_SPACES begin = p; while ((c = *++p) != '\0' && !AK_ARRAY_NLINE_CHECK); end = p; mtl->map_Ks = ak_heap_strndup(heap, mtl, begin, end - begin); break; case 'e': p += 2; SKIP_SPACES begin = p; while ((c = *++p) != '\0' && !AK_ARRAY_NLINE_CHECK); end = p; mtl->map_Ke = ak_heap_strndup(heap, mtl, begin, end - begin); break; default: break; } break; default: break; } } else if (p[0] == 'b' && p[1] == 'u' && p[2] == 'm' && p[3] == 'p') { p += 4; SKIP_SPACES begin = p; while ((c = *++p) != '\0' && !AK_ARRAY_NLINE_CHECK); end = p; mtl->bump = ak_heap_strndup(heap, mtl, begin, end - begin); } else if (p[0] == 'i' && p[1] == 'l' && p[2] == 'l' && p[3] == 'u' && p[4] == 'm' && (p[5] == ' ' || p[5] == '\t')) { p += 5; ak_strtoi_line(p, 0, 1, &mtl->illum); } } NEXT_LINE } while (p && p[0] != '\0'/* && (c = *++p) != '\0'*/); if (mtl) wobj_handleMaterial(wst, mtllib, mtl); ret: if (mtlstr) ak_releasefile(mtlstr, mtlstrSize); ak_free((void *)localurl); return mtllib; } static AkProfileCommon* wobj_cmnEffect(WOState * __restrict wst) { AkLibrary *lib; AkEffect *effect; AkProfileCommon *profile; if (!(lib = wst->doc->lib.effects)) { lib = ak_heap_calloc(wst->heap, wst->doc, sizeof(*lib)); wst->doc->lib.effects = lib; } effect = ak_heap_calloc(wst->heap, lib, sizeof(*effect)); profile = ak_heap_calloc(wst->heap, effect, sizeof(*profile)); profile->type = AK_PROFILE_TYPE_COMMON; lib->count++; effect->profile = profile; effect->next = (void *)lib->chld; lib->chld = (void *)effect; ak_setypeid(profile, AKT_PROFILE); ak_setypeid(effect, AKT_EFFECT); return effect->profile; } AK_INLINE void wobj_clrtexset(WOState * __restrict wst, void * __restrict memp, float * rgb, char * __restrict map, AkColorDesc * __restrict clr, AkTextureColorSpace colorSpace, AkTextureChannels channels) { if (rgb) { clr->color = ak_heap_calloc(wst->heap, clr, sizeof(*clr->color)); glm_vec3_copy(rgb, clr->color->vec); clr->color->vec[3] = 1.0f; } if (map) { clr->texture = wobj_texref(wst, memp, map, colorSpace, channels); } } AK_INLINE AkColorDesc* wobj_clrtex(WOState * __restrict wst, void * __restrict memp, float * rgb, char * __restrict map, AkTextureColorSpace colorSpace, AkTextureChannels channels) { AkColorDesc *clr; clr = ak_heap_calloc(wst->heap, memp, sizeof(*clr)); wobj_clrtexset(wst, memp, rgb, map, clr, colorSpace, channels); return clr; } //AK_INLINE //AkFloatOrParam* //wobj_flt(AkHeap * __restrict heap, // void * __restrict memp, // float val) { // AkFloatOrParam *flt; // // flt = ak_heap_calloc(heap, memp, sizeof(*flt)); // flt->val = ak_heap_calloc(heap, flt, sizeof(*flt->val)); // *flt->val = val; // // return flt; //} static void wobj_handleMaterial(WOState * __restrict wst, WOMtlLib * __restrict mtllib, WOMtl * __restrict mtl) { AkHeap *heap; AkDoc *doc; AkLibrary *libmat; AkProfileCommon *pcommon; AkTechniqueFx *technfx; AkTechniqueFxCommon *cmnTechn; AkEffect *effect; AkInstanceEffect *ieff; AkMaterial *mat; AkMaterialSpecularProp *specularProp; AkMaterialEmissionProp *emissionProp; heap = wst->heap; doc = wst->doc; if (!(libmat = doc->lib.materials)) { libmat = ak_heap_calloc(heap, wst->doc, sizeof(*libmat)); doc->lib.materials = libmat; } pcommon = wobj_cmnEffect(wst); effect = ak_mem_parent(pcommon); technfx = ak_heap_calloc(heap, pcommon, sizeof(*technfx)); mat = ak_heap_calloc(heap, libmat, sizeof(*mat)); ak_setypeid(technfx, AKT_TECHNIQUE_FX); cmnTechn = ak_heap_calloc(heap, technfx, sizeof(*cmnTechn)); switch (mtl->illum) { case 0: /* Constant */ cmnTechn->type = AK_MATERIAL_CONSTANT; break; case 1: /* Lambert */ cmnTechn->type = AK_MATERIAL_LAMBERT; break; case 2: /* TODO: Currently all others are Blinn */ cmnTechn->type = AK_MATERIAL_BLINN; default: break; } cmnTechn->ambient = wobj_clrtex(wst, cmnTechn, mtl->Ka, mtl->map_Ka, AK_TEXTURE_COLORSPACE_SRGB, AK_TEXTURE_CHANNEL_RGB); cmnTechn->diffuse = wobj_clrtex(wst, cmnTechn, mtl->Kd, mtl->map_Kd, AK_TEXTURE_COLORSPACE_SRGB, AK_TEXTURE_CHANNEL_RGBA); specularProp = ak_heap_calloc(heap, cmnTechn, sizeof(*specularProp)); cmnTechn->specular = specularProp; specularProp->shininess = mtl->Ns; specularProp->color = wobj_clrtex(wst, cmnTechn, mtl->Ks, mtl->map_Ks, AK_TEXTURE_COLORSPACE_SRGB, AK_TEXTURE_CHANNEL_RGB); emissionProp = ak_heap_calloc(heap, cmnTechn, sizeof(*emissionProp)); cmnTechn->emission = emissionProp; emissionProp->strength = 1.0f; wobj_clrtexset(wst, cmnTechn, mtl->Ke, mtl->map_Ke, &emissionProp->color, AK_TEXTURE_COLORSPACE_SRGB, AK_TEXTURE_CHANNEL_RGB); cmnTechn->ior = mtl->Ni; if (mtl->bump) { cmnTechn->normal = ak_heap_calloc(heap, cmnTechn, sizeof(*cmnTechn->normal)); cmnTechn->normal->scale = 1.0f; cmnTechn->normal->tex = wobj_texref(wst, cmnTechn, mtl->bump, AK_TEXTURE_COLORSPACE_LINEAR, AK_TEXTURE_CHANNEL_RGB); } if (mtl->Tr > 0.0f || mtl->d < 1.0f) { AkTransparent *transp; float t; if (mtl->d < 1.0f) t = mtl->d; else t = 1.0f - mtl->Tr; transp = ak_heap_calloc(heap, cmnTechn, sizeof(*transp)); transp->amount = t; transp->opaque = AK_OPAQUE_BLEND; cmnTechn->transparent = transp; } technfx->common = cmnTechn; technfx->next = pcommon->technique; pcommon->technique = technfx; ieff = ak_heap_calloc(heap, mat, sizeof(*ieff)); ieff->base.type = AK_INSTANCE_EFFECT; ieff->base.url.ptr = effect; mat->effect = ieff; mat->base.next = libmat->chld; libmat->chld = (void *)mat; libmat->count++; rb_insert(mtllib->materials, mtl->name, mat); } static AkTextureRef* wobj_texref(WOState * __restrict wst, void * __restrict memp, char * name, AkTextureColorSpace colorSpace, AkTextureChannels channels) { AkHeap *heap; AkDoc *doc; AkImage *image; AkInitFrom *initFrom; AkTexture *tex; AkSampler *sampler; AkTextureRef *texref; heap = wst->heap; doc = wst->doc; /* create image */ image = ak_heap_calloc(heap, doc, sizeof(*image)); initFrom = ak_heap_calloc(heap, image, sizeof(*initFrom)); initFrom->ref = name; image->initFrom = initFrom; ak_mem_setp(name, initFrom); flist_sp_insert(&doc->lib.images, image); /* create sampler */ sampler = ak_heap_calloc(heap, doc, sizeof(*sampler)); sampler->wrapS = AK_WRAP_MODE_WRAP; sampler->wrapT = AK_WRAP_MODE_WRAP; ak_setypeid(sampler, AKT_SAMPLER2D); /* create texture */ tex = ak_heap_calloc(heap, doc, sizeof(*tex)); tex->type = AKT_SAMPLER2D; tex->image = image; tex->sampler = sampler; flist_sp_insert(&doc->lib.textures, tex); /* create texture ref */ texref = ak_heap_calloc(heap, memp, sizeof(*texref)); ak_setypeid(texref, AKT_TEXTURE_REF); texref->coordInputName = _s_TEXCOORD; texref->texture = tex; texref->slot = 0; ak_texref_usage(texref, colorSpace, channels); return texref; } ================================================ FILE: src/io/obj/mtl.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wobj_mtl_h #define wobj_mtl_h #include "common.h" AK_HIDE WOMtlLib* wobj_mtl(WOState * __restrict wst, const char * __restrict name); #endif /* wobj_mtl_h */ ================================================ FILE: src/io/obj/obj.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* Resources: https://all3dp.com/1/obj-file-format-3d-printing-cad/ http://paulbourke.net/dataformats/obj/ http://paulbourke.net/dataformats/mtl/ https://en.wikipedia.org/wiki/Wavefront_.obj_file */ #include "obj.h" #include "common.h" #include "group.h" #include "mtl.h" #include "util.h" #include "../common/postscript.h" #include "../../id.h" #include "../../data.h" #include "../../../include/ak/path.h" static void ak_wobjFreeDupl(RBTree *tree, RBNode *node); AK_HIDE AkResult wobj_obj(AkDoc ** __restrict dest, const char * __restrict filepath) { AkHeap *heap; AkDoc *doc; void *objstr; char *p, *begin, *end, *m; AkLibrary *lib_vscene; AkVisualScene *scene; WOPrim *prim; WOState wstVal = {0}, *wst; float v[4]; size_t objstrSize; AkResult ret; uint32_t vc; char c; if ((ret = ak_readfile(filepath, NULL, &objstr, &objstrSize)) != AK_OK) return ret; heap = ak_heap_new(NULL, NULL, NULL); doc = ak_heap_calloc(heap, NULL, sizeof(*doc)); doc->inf = ak_heap_calloc(heap, doc, sizeof(*doc->inf)); doc->inf->name = filepath; doc->inf->dir = ak_path_dir(heap, doc, filepath); doc->inf->flipImage = true; doc->inf->ftype = AK_FILE_TYPE_WAVEFRONT; doc->inf->base.coordSys = AK_YUP; doc->coordSys = AK_YUP; /* Default */ if (!((p = objstr) && (c = *p) != '\0')) { ak_free(doc); ak_releasefile(objstr, objstrSize); return AK_ERR; } /* for fixing skin and morph vertices */ doc->reserved = rb_newtree_ptr(); ((RBTree *)doc->reserved)->onFreeNode = ak_wobjFreeDupl; ak_heap_setdata(heap, doc); ak_id_newheap(heap); /* libraries */ doc->lib.geometries = ak_heap_calloc(heap, doc, sizeof(AkLibrary)); lib_vscene = ak_heap_calloc(heap, doc, sizeof(*lib_vscene)); /* default scene */ scene = ak_heap_calloc(heap, doc, sizeof(*scene)); scene->node = ak_heap_calloc(heap, doc, sizeof(*scene->node)); lib_vscene->chld = &scene->base; lib_vscene->count = 1; doc->lib.visualScenes = lib_vscene; doc->scene.visualScene = ak_instanceMake(heap, doc, scene); /* parse state */ memset(&wstVal, 0, sizeof(wstVal)); wst = &wstVal; wstVal.doc = doc; wstVal.heap = heap; wstVal.tmp = ak_heap_alloc(heap, doc, sizeof(void*)); wstVal.node = scene->node; wstVal.lib_geom = doc->lib.geometries; /* vertex data (shared across file) */ wst->dc_pos = ak_data_new(wst->tmp, 128, sizeof(vec3), NULL); wst->dc_tex = ak_data_new(wst->tmp, 128, sizeof(vec2), NULL); wst->dc_nor = ak_data_new(wst->tmp, 128, sizeof(vec3), NULL); /* default group */ wobj_switchObject(wst); prim = wst->obj->prim; /* parse .obj */ do { /* skip spaces */ SKIP_SPACES if (p[1] == ' ' || p[1] == '\t') { switch (c) { case '#': { /* ignore comments */ while ((c = *++p) != '\0' && !AK_ARRAY_NLINE_CHECK); /* while ((c = *++p) != '\0' && AK_ARRAY_NLINE_CHECK); */ break; } case 'v': { if (*++p == '\0') goto err; /* TODO: handle 4 components */ ak_strtof_line(p, 0, 3, v); ak_data_append(wst->dc_pos, v); break; } case 'f': { if ((c = *(p += 2)) == '\0') goto err; vc = 0; do { ivec3 face; /* vertex index */ SKIP_SPACES if (AK_ARRAY_NLINE_CHECK) break; face[0] = (int32_t)strtol(p, &p, 10); face[1] = 0; face[2] = 0; /* texture index */ SKIP_SPACES if (p && p[0] == '/') { if (p[1] != '/') { face[1] = (int32_t)strtol(++p, &p, 10); if (!prim->hasTexture) prim->hasTexture = true; } else { p++; } } /* normal index */ SKIP_SPACES if (p && p[0] == '/') { face[2] = (int32_t)strtol(++p, &p, 10); if (!prim->hasNormal) prim->hasNormal = true; } ak_data_append(prim->dc_face, face); vc += 1; c = *p; } while (p && (c = p[0]) != '\0' && !AK_ARRAY_NLINE_CHECK && (c = *++p) != '\0' && !AK_ARRAY_NLINE_CHECK); prim->maxVC = GLM_MAX(prim->maxVC, vc); ak_data_append(prim->dc_vcount, &vc); break; } case 'o': case 'g': { wobj_switchObject(wst); prim = wst->obj->prim; break; } default: break; } } else if (p[2] == ' ' || p[2] == '\t') { if (p[0] == 'v' && p[1] == 'n') { if (*(p += 2) == '\0') goto err; ak_strtof_line(p, 0, 3, v); ak_data_append(wst->dc_nor, v); } else if (p[0] == 'v' && p[1] == 't') { if (*(p += 2) == '\0') goto err; ak_strtof_line(p, 0, 2, v); ak_data_append(wst->dc_tex, v); } } else if (p[0] == 'm' && p[1] == 't' && p[2] == 'l' && p[3] == 'l' && p[4] == 'i' && p[5] == 'b' && (p[6] == ' ' || p[6] == '\t')) { p += 7; SKIP_SPACES begin = p; while ((c = *++p) != '\0' && !AK_ARRAY_NLINE_CHECK); end = p; if (end > begin && (m = ak_heap_strndup(heap, wst->doc, begin, end - begin))) wst->mtlib = wobj_mtl(wst, m); } else if (p[0] == 'u' && p[1] == 's' && p[2] == 'e' && p[3] == 'm' && p[4] == 't' && p[5] == 'l' && (p[6] == ' ' || p[6] == '\t')) { p += 7; SKIP_SPACES begin = p; while ((c = *++p) != '\0' && !AK_ARRAY_NLINE_CHECK); end = p; m = NULL; if (end > begin) m = ak_heap_strndup(heap, wst->doc, begin, end - begin); prim = wobj_switchPrim(wst, m); } NEXT_LINE } while (p && p[0] != '\0'/* && (c = *++p) != '\0'*/); wst->ac_pos = wobj_acc(wst, wst->dc_pos, AK_COMPONENT_SIZE_VEC3, AKT_FLOAT); wst->ac_nor = wobj_acc(wst, wst->dc_nor, AK_COMPONENT_SIZE_VEC3, AKT_FLOAT); wst->ac_tex = wobj_acc(wst, wst->dc_tex, AK_COMPONENT_SIZE_VEC2, AKT_FLOAT); wobj_finishObjects(wst); io_postscript(doc); *dest = doc; /* cleanup */ ak_free(wst->tmp); ak_releasefile(objstr, objstrSize); return AK_OK; err: ak_free(doc); if (objstr) ak_releasefile(objstr, objstrSize); return AK_ERR; } static void ak_wobjFreeDupl(RBTree *tree, RBNode *node) { if (node == tree->nullNode) return; ak_free(node->val); } ================================================ FILE: src/io/obj/obj.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wobj_h #define wobj_h #include "common.h" AK_HIDE AkResult wobj_obj(AkDoc ** __restrict dest, const char * __restrict filepath); #endif /* wobj_h */ ================================================ FILE: src/io/obj/util.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "util.h" #define wobj_real_index(count, val) val > 0 ? val - 1 : count - val AK_HIDE AkAccessor* wobj_acc(WOState * __restrict wst, AkDataContext * __restrict dctx, AkComponentSize compSize, AkTypeId type) { AkHeap *heap; AkBuffer *buff; AkAccessor *acc; AkTypeDesc *typeDesc; int nComponents; heap = wst->heap; typeDesc = ak_typeDesc(type); nComponents = (int)compSize; buff = ak_heap_calloc(heap, wst->doc, sizeof(*buff)); buff->data = ak_heap_alloc(heap, buff, dctx->usedsize); buff->length = dctx->usedsize; ak_data_join(dctx, buff->data, 0, 0); flist_sp_insert(&wst->doc->lib.buffers, buff); acc = ak_heap_calloc(heap, wst->doc, sizeof(*acc)); acc->buffer = buff; acc->byteLength = buff->length; acc->byteStride = typeDesc->size * nComponents; acc->componentSize = compSize; acc->componentType = type; acc->bytesPerComponent = typeDesc->size; acc->componentCount = nComponents; acc->fillByteSize = typeDesc->size * nComponents; acc->count = (uint32_t)dctx->itemcount; return acc; } AK_HIDE AkInput* wobj_input(WOState * __restrict wst, AkMeshPrimitive * __restrict prim, AkAccessor * __restrict acc, AkInputSemantic sem, const char * __restrict semRaw, uint32_t offset) { AkInput *inp; inp = ak_heap_calloc(wst->heap, prim, sizeof(*inp)); inp->accessor = acc; inp->semantic = sem; inp->semanticRaw = ak_heap_strdup(wst->heap, inp, semRaw); inp->offset = offset; inp->next = prim->input; prim->input = inp; prim->inputCount++; ak_retain(acc); return inp; } AK_HIDE void wobj_joinIndices(WOState * __restrict wst, WOPrim * __restrict wp, AkMeshPrimitive * __restrict prim) { AkDataChunk *chunk; AkUInt *it, *it2, val; size_t count; size_t isz, csz, i; uint32_t istride, count_pos, count_tex, count_nor; if (!wp->dc_face->data) return; count = wp->dc_face->itemcount; istride = 1; count_pos = wst->ac_pos->count; count_tex = wst->ac_tex->count; count_nor = wst->ac_nor->count; if (wp->hasTexture || wp->hasNormal) { istride += (int)wp->hasNormal + (int)wp->hasTexture; count *= istride; } prim->indices = ak_heap_calloc(wst->heap, prim, sizeof(*prim->indices) + count * sizeof(AkUInt)); prim->indices->count = count; prim->indexStride = istride; it = prim->indices->items; /* join index buffer chunks */ isz = wp->dc_face->itemsize; chunk = wp->dc_face->data; /* to make it faster split cases */ if (wp->hasNormal && wp->hasTexture) { while (chunk) { csz = chunk->usedsize; it2 = (void *)chunk->data; for (i = 0; i < csz; i += isz) { /* position */ val = *it2; *it = wobj_real_index(count_pos, val); /* texture */ val = *(it2 + 1); *(it + 1) = wobj_real_index(count_tex, val); /* normal */ val = *(it2 + 2); *(it + 2) = wobj_real_index(count_nor, val); it += 3; it2 += 3; } chunk = chunk->next; } } else if (wp->hasNormal) { while (chunk) { csz = chunk->usedsize; it2 = (void *)chunk->data; for (i = 0; i < csz; i += isz) { /* position */ val = *it2; *it = wobj_real_index(count_pos, val); /* normal */ val = *(it2 + 2); *(it + 1) = wobj_real_index(count_nor, val); it += 2; it2 += 3; } chunk = chunk->next; } } else if (wp->hasTexture) { while (chunk) { csz = chunk->usedsize; it2 = (void *)chunk->data; for (i = 0; i < csz; i += isz) { /* position */ val = *it2; *it = wobj_real_index(count_pos, val); /* texture */ val = *(it2 + 1); *(it + 1) = wobj_real_index(count_tex, val); it += 2; it2 += 3; } chunk = chunk->next; } } else { while (chunk) { csz = chunk->usedsize; it2 = (void *)chunk->data; for (i = 0; i < csz; i += isz) { /* position */ val = *it2; *it = wobj_real_index(count_pos, val); it += 1; it2 += 3; } chunk = chunk->next; } } } ================================================ FILE: src/io/obj/util.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wobj_util_h #define wobj_util_h #include "common.h" #include "../common/util.h" AK_HIDE AkAccessor* wobj_acc(WOState * __restrict wst, AkDataContext * __restrict dctx, AkComponentSize compSize, AkTypeId type); AK_HIDE AkInput* wobj_input(WOState * __restrict wst, AkMeshPrimitive * __restrict prim, AkAccessor * __restrict acc, AkInputSemantic sem, const char * __restrict semRaw, uint32_t offset); AK_HIDE void wobj_joinIndices(WOState * __restrict wst, WOPrim * __restrict wp, AkMeshPrimitive * __restrict prim); #endif /* wobj_util_h */ ================================================ FILE: src/io/ply/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/io/ply/README.md ================================================ # AssetKit: Polygon File Format (ply) Status - [x] ASCII - [x] Binary - [x] X, Y, Z - [x] NX, NY, NZ - [x] S, T, U, V - [x] R, G, B - [x] Stop loading is one of X, Y, Z property is missing - [x] Ignore tuple if one of them is not exist e.g. ignore normals if NY is missing ================================================ FILE: src/io/ply/ascii.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ply.h" #include "common.h" #include "util.h" #include "../common/util.h" #include "../../data.h" AK_HIDE void ply_ascii(char * __restrict src, PLYState * __restrict pst) { char *p; float *b; PLYElement *elem; PLYProperty *prop; AkBuffer *buff; char c; uint32_t i, stride; p = src; elem = pst->element; while (elem) { if (elem->type == PLY_ELEM_VERTEX) { buff = elem->buff; b = buff->data; /* TODO: all vertices are floats for now */ stride = elem->knownCount; i = 0; c = *p; /* stop */ if (!elem->buff || elem->buff->length == 0) return; do { SKIP_SPACES prop = elem->property; while (prop) { if (!prop->ignore) b[prop->slot] = strtof(p, &p); prop = prop->next; } b += stride; NEXT_LINE if (++i >= elem->count) break; } while (p && p[0] != '\0'); } else if (elem->type == PLY_ELEM_FACE) { AkUInt *f, center, fc, j, count, last_fc; pst->dc_ind = ak_data_new(pst->tmp, 128, sizeof(AkUInt), NULL); c = *p; f = NULL; i = 0; count = 0; last_fc = 0; do { SKIP_SPACES fc = (AkUInt)strtol(p, &p, 10); if (fc >= 3) { if (!f || last_fc < fc) f = alloca(sizeof(AkUInt) * fc); for (j = 0; j < fc; j++) f[j] = (AkUInt)strtol(p, &p, 10); center = f[0]; for (j = 0; j < fc - 2; j++) { ak_data_append(pst->dc_ind, ¢er); ak_data_append(pst->dc_ind, &f[j + 1]); ak_data_append(pst->dc_ind, &f[j + 2]); count += 3; } } last_fc = fc; NEXT_LINE if (++i >= elem->count) break; } while (p && p[0] != '\0'); pst->count = count; } else { /* skip unsupported elements */ for (i = 0; i < elem->count; i++) { NEXT_LINE } } elem = elem->next; } ply_finish(pst); } ================================================ FILE: src/io/ply/bin.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ply.h" #include "common.h" #include "util.h" #include "../common/util.h" #include "../../data.h" #include "../../endian.h" AK_HIDE void ply_bin(char * __restrict src, PLYState * __restrict pst, bool le) { char *p; float *b; PLYElement *elem; PLYProperty *prop; AkBuffer *buff; uint32_t i, stride, vertcount; p = src; elem = pst->element; vertcount = pst->vertcount; while (elem) { if (elem->type == PLY_ELEM_VERTEX) { AkUInt elemc; elemc = elem->count; buff = elem->buff; b = buff->data; /* TODO: all vertices are floats for now */ stride = elem->knownCount; i = 0; /* stop */ if (!elem->buff || elem->buff->length == 0) return; while (i++ < elemc) { prop = elem->property; while (prop) { if (!prop->ignore) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstrict-aliasing" ply_val(p, prop->typeDesc, le, float, b[prop->slot], 0.0f); #pragma GCC diagnostic pop } prop = prop->next; } b += stride; } } else if (elem->type == PLY_ELEM_FACE) { char *e; AkUInt *f, center, fc, j, count, last_fc, valid, elemc; pst->dc_ind = ak_data_new(pst->tmp, 128, sizeof(AkUInt), NULL); elemc = elem->count; e = pst->end; f = NULL; i = 0; count = 0; last_fc = 0; while (i++ < elemc) { prop = elem->property; /* iterate thorough list and other properties */ while (prop) { if (!prop->ignore && prop->islist) { /* TODO: */ if ((p + prop->typeDesc->size) > e) goto fns; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstrict-aliasing" ply_val(p, prop->listCountTypeDesc, le, AkUInt, fc, 0); #pragma GCC diagnostic pop if (fc >= 3) { if (!f || fc > last_fc) f = alloca(sizeof(*f) * fc); valid = 0; /* copy data */ for (j = 0; j < fc; j++) { if ((p + prop->typeDesc->size) > e) goto fns; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstrict-aliasing" ply_val(p, prop->typeDesc, le, uint32_t, f[j], 0); #pragma GCC diagnostic pop valid += f[j] < vertcount; } /* check valid loop */ if (valid == fc) { center = f[0]; for (j = 0; j < fc - 2; j++) { ak_data_append(pst->dc_ind, ¢er); ak_data_append(pst->dc_ind, &f[j + 1]); ak_data_append(pst->dc_ind, &f[j + 2]); count += 3; } } } else if (fc > 0) { for (j = 0; j < fc; j++) p += prop->typeDesc->size; } last_fc = fc; } else { /* do ignore */ /* TODO: */ } prop = prop->next; } } pst->count = count; } else { /* skip unsupported elements */ /* TODO: */ } elem = elem->next; } fns: ply_finish(pst); } ================================================ FILE: src/io/ply/common.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ply_commoh_h #define ply_commoh_h #include "../../../include/ak/assetkit.h" #include "../../common.h" #include "../../utils.h" #include "../../tree.h" #include "../../json.h" #include "../../data.h" #include #include typedef enum PLYPropertyType { PLY_PROP_UNSUPPORTED = 0, PLY_PROP_X, PLY_PROP_Y, PLY_PROP_Z, PLY_PROP_S, PLY_PROP_T, PLY_PROP_NX, PLY_PROP_NY, PLY_PROP_NZ, PLY_PROP_R, PLY_PROP_G, PLY_PROP_B } PLYPropertyType; typedef struct PLYProperty { struct PLYProperty *prev; struct PLYProperty *next; char *name; char *typestr; AkTypeDesc *typeDesc; char *listCountType; AkTypeDesc *listCountTypeDesc; PLYPropertyType semantic; uint32_t slot; size_t off; bool islist; bool ignore; } PLYProperty; typedef enum PLYElementType { PLY_ELEM_UNKNOWN = 0, PLY_ELEM_VERTEX = 1, PLY_ELEM_FACE = 2 } PLYElementType; typedef struct PLYElement { struct PLYElement *next; PLYProperty *property; AkBuffer *buff; char *name; uint32_t count; uint32_t knownCount; PLYElementType type; size_t buffsize; } PLYElement; typedef struct PLYState { AkHeap *heap; AkDoc *doc; void *tmp; char *end; AkLibrary *lib_geom; AkGeometry *geom; AkDataContext *dc_ind; AkAccessor *ac_pos, *ac_nor, *ac_tex, *ac_rgb; AkNode *node; PLYElement *element; PLYElement *lastElement; size_t vertBuffsize; uint32_t byteStride; uint32_t count; uint32_t vertcount; } PLYState; #define SKIP_SPACES \ { \ while (c != '\0' && AK_ARRAY_SPACE_CHECK) c = *++p; \ if (c == '\0') \ break; /* to break loop */ \ } #define NEXT_LINE \ do { \ c = p ? *p : '\0'; \ while (p \ && p[0] != '\0' \ && !AK_ARRAY_NLINE_CHECK \ && (c = *++p) != '\0' \ && !AK_ARRAY_NLINE_CHECK); \ \ while (p \ && p[0] != '\0' \ && AK_ARRAY_NLINE_CHECK \ && (c = *++p) != '\0' \ && AK_ARRAY_NLINE_CHECK); \ } while(0); #define EQ4(c1,c2,c3,c4) \ (p[0] == c1 \ && p[1] == c2 \ && p[2] == c3 \ && p[3] == c4 \ && (p[4] == ' ' || p[4] == '\t')) #define EQ5(c1,c2,c3,c4,c5) \ (p[0] == c1 \ && p[1] == c2 \ && p[2] == c3 \ && p[3] == c4 \ && p[4] == c5 \ && (p[5] == ' ' || p[5] == '\t')) #define EQ6(c1,c2,c3,c4,c5,c6) \ (p[0] == c1 \ && p[1] == c2 \ && p[2] == c3 \ && p[3] == c4 \ && p[4] == c5 \ && p[5] == c6 \ && (p[6] == ' ' || p[6] == '\t')) #define EQ7(c1,c2,c3,c4,c5,c6,c7) \ (p[0] == c1 \ && p[1] == c2 \ && p[2] == c3 \ && p[3] == c4 \ && p[4] == c5 \ && p[5] == c6 \ && p[6] == c7 \ && (p[7] == ' ' || p[7] == '\t')) #define EQ8(c1,c2,c3,c4,c5,c6,c7,c8) \ (p[0] == c1 \ && p[1] == c2 \ && p[2] == c3 \ && p[3] == c4 \ && p[4] == c5 \ && p[5] == c6 \ && p[6] == c7 \ && p[7] == c8 \ && (p[8] == ' ' || p[8] == '\t')) #define EQT7(c1,c2,c3,c4,c5,c6,c7) \ (p[0] == c1 \ && p[1] == c2 \ && p[2] == c3 \ && p[3] == c4 \ && p[4] == c5 \ && p[5] == c6 \ && p[6] == c7) #endif /* ply_commoh_h */ ================================================ FILE: src/io/ply/ply.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* Resources: http://people.math.sc.edu/Burkardt/data/ply/ply.txt http://paulbourke.net/dataformats/ply/ https://en.wikipedia.org/wiki/PLY_(file_format) http://gamma.cs.unc.edu/POWERPLANT/papers/ply.pdf https://people.sc.fsu.edu/~jburkardt/data/ply/ply.html (samples) */ #include "ply.h" #include "common.h" #include "util.h" #include "../../id.h" #include "../../data.h" #include "../../../include/ak/path.h" #include "../common/util.h" #include "../common/postscript.h" #include "../../strpool.h" AK_HIDE AkResult ply_ply(AkDoc ** __restrict dest, const char * __restrict filepath) { AkHeap *heap; AkDoc *doc; void *plystr; char *p, *b, *e; AkLibrary *lib_vscene; AkVisualScene *scene; PLYElement *elem; PLYProperty *prop, *pit; PLYState pstVal = {0}, *pst; size_t plystrSize, off; uint32_t i; bool isAscii, isLittleEndian; char c; if (ak_readfile(filepath, NULL, &plystr, &plystrSize) != AK_OK || !((p = plystr) && *p != '\0')) { if (plystr) ak_releasefile(plystr, plystrSize); return AK_ERR; } if (!(tolower(p[0]) == 'p' && tolower(p[1]) == 'l' && tolower(p[2]) == 'y')) { ak_releasefile(plystr, plystrSize); return AK_ERR; } p += 3; /* c = *p; */ NEXT_LINE elem = NULL; prop = NULL; heap = ak_heap_new(NULL, NULL, NULL); doc = ak_heap_calloc(heap, NULL, sizeof(*doc)); /* for fixing skin and morph vertices */ doc->reserved = rb_newtree_ptr(); doc->inf = ak_heap_calloc(heap, doc, sizeof(*doc->inf)); doc->inf->name = filepath; doc->inf->dir = ak_path_dir(heap, doc, filepath); doc->inf->flipImage = true; doc->inf->ftype = AK_FILE_TYPE_PLY; doc->inf->base.coordSys = AK_YUP; doc->coordSys = AK_YUP; /* Default */ ak_heap_setdata(heap, doc); ak_id_newheap(heap); /* libraries */ doc->lib.geometries = ak_heap_calloc(heap, doc, sizeof(AkLibrary)); lib_vscene = ak_heap_calloc(heap, doc, sizeof(*lib_vscene)); /* default scene */ scene = ak_heap_calloc(heap, doc, sizeof(*scene)); scene->node = ak_heap_calloc(heap, doc, sizeof(*scene->node)); lib_vscene->chld = &scene->base; lib_vscene->count = 1; doc->lib.visualScenes = lib_vscene; doc->scene.visualScene = ak_instanceMake(heap, doc, scene); /* parse state */ memset(&pstVal, 0, sizeof(pstVal)); pst = &pstVal; pstVal.doc = doc; pstVal.heap = heap; pstVal.tmp = ak_heap_alloc(heap, doc, sizeof(void*)); pstVal.node = scene->node; pstVal.lib_geom = doc->lib.geometries; isAscii = false; isLittleEndian = false; pst->end = (char *)plystr + plystrSize; /* parse header */ do { /* skip spaces */ SKIP_SPACES /* parse format but ignore version (for now maybe) */ if (EQ6('f', 'o', 'r', 'm', 'a', 't')) { p += 7; SKIP_SPACES if (EQ5('a', 's', 'c', 'i', 'i')) { isAscii = true; } else if (p[0] == 'b' && p[1] == 'i' && p[2] == 'n' && p[7] == 'l' && p[8] == 'i' && p[9] == 't') { /* strncmp(p, "binary_little_endian", 20) == 0 */ isLittleEndian = true; } else if (p[0] == 'b' && p[1] == 'i' && p[2] == 'n' && p[7] == 'b' && p[8] == 'i' && p[9] == 'g') { /* strncmp(p, "binary_big_endian", 17) == 0 */ isLittleEndian = false; } else { goto err; /* unknown format */ } } else if (EQ7('e', 'l', 'e', 'm', 'e', 'n', 't')) { p += 8; elem = ak_heap_calloc(heap, pst->tmp, sizeof(*elem)); if (!pst->element) pst->element = elem; if (pst->lastElement) pst->lastElement->next = elem; pst->lastElement = elem; if (EQ6('v', 'e', 'r', 't', 'e', 'x')) { p += 7; SKIP_SPACES elem->count = (uint32_t)strtoul(p, &p, 10); elem->type = PLY_ELEM_VERTEX; pst->vertcount = elem->count; } else if (EQ4('f', 'a', 'c', 'e')) { p += 5; SKIP_SPACES elem->count = (uint32_t)strtoul(p, &p, 10); elem->type = PLY_ELEM_FACE; } } else if (elem && EQ8('p', 'r', 'o', 'p', 'e', 'r', 't', 'y')) { p += 9; SKIP_SPACES prop = ak_heap_calloc(heap, pst->tmp, sizeof(*prop)); /* 1. type */ b = p; while ((c = *++p) != '\0' && !AK_ARRAY_SPACE_CHECK); e = p; prop->islist = b[0] == 'l' && b[1] == 'i' && b[2] == 's' && b[3] == 't'; if (!prop->islist) { prop->typestr = ak_heap_strndup(heap, doc, b, e - b); } else { /* 1.1 count type */ SKIP_SPACES b = p; while ((c = *++p) != '\0' && !AK_ARRAY_SEP_CHECK); e = p; prop->listCountType = ak_heap_strndup(heap, doc, b, e - b); prop->listCountTypeDesc = ak_typeDescByName(prop->listCountType); /* 1.2 type */ SKIP_SPACES b = p; while ((c = *++p) != '\0' && !AK_ARRAY_SEP_CHECK); e = p; prop->typestr = ak_heap_strndup(heap, doc, b, e - b); } prop->typeDesc = ak_typeDescByName(prop->typestr); /* 2. name */ SKIP_SPACES b = p; while ((c = *++p) != '\0' && !AK_ARRAY_SEP_CHECK); e = p; prop->name = ak_heap_strndup(heap, doc, b, e - b); if (prop->typeDesc) { elem->buffsize += prop->typeDesc->size; } else if (!prop->islist && !isAscii) { /* we cannot traverse the binary because we don't know some types */ goto err; } if (e - b == 1) { switch (b[0]) { case 'x': prop->semantic = PLY_PROP_X; break; case 'y': prop->semantic = PLY_PROP_Y; break; case 'z': prop->semantic = PLY_PROP_Z; break; case 's': case 'u': prop->semantic = PLY_PROP_S; break; case 't': case 'v': prop->semantic = PLY_PROP_T; break; case 'r': prop->semantic = PLY_PROP_R; break; case 'g': prop->semantic = PLY_PROP_G; break; case 'b': prop->semantic = PLY_PROP_B; break; default: prop->semantic = PLY_PROP_UNSUPPORTED; prop->ignore = true; break; } } else if (e - b == 2) { switch (b[0]) { case 'n': switch (b[1]) { case 'x': prop->semantic = PLY_PROP_NX; break; case 'y': prop->semantic = PLY_PROP_NY; break; case 'z': prop->semantic = PLY_PROP_NZ; break; default: prop->semantic = PLY_PROP_UNSUPPORTED; prop->ignore = true; break; } break; default: prop->semantic = PLY_PROP_UNSUPPORTED; prop->ignore = true; break; } } if (!elem->property) { elem->property = prop; } else { PLYProperty *last_prop; /* insert propety by ORDER */ last_prop = pit = elem->property; while (pit) { if ((int)prop->semantic < (int)pit->semantic) { if (pit->prev) { pit->prev->next = prop; prop->prev = pit->prev; } prop->next = pit; pit->prev = prop; if (pit == elem->property) elem->property = prop; break; } last_prop = pit; pit = pit->next; } /* couldn't add, so add to last */ if (!pit && last_prop) { last_prop->next = prop; prop->prev = last_prop; } } } else if (EQT7('e', 'n', 'd', '_', 'h', 'e', 'a')) { NEXT_LINE break; } NEXT_LINE } while (p && p[0] != '\0'/* && (c = *++p) != '\0'*/); /* prepare property offsets/slots */ i = 0; off = 0; elem = pst->element; while (elem) { pit = elem->property; if (elem->type == PLY_ELEM_VERTEX) { size_t byteSffset; byteSffset = 0; elem->buff = ak_heap_calloc(heap, pst->doc, sizeof(*elem->buff)); while (pit) { if (pit->ignore) goto ign; /* validate, check missing properties in the group */ if (pit->semantic == PLY_PROP_X) { if ((!pit->next || pit->next->semantic != PLY_PROP_Y) ||(!pit->next->next || pit->next->next->semantic != PLY_PROP_Z)) goto err; /* we cannot load this PLY, TODO: */ /* alloc input and accessor for positions */ pst->ac_pos = io_acc(heap, doc, AK_COMPONENT_SIZE_VEC3, AKT_FLOAT, elem->count, elem->buff); } if (pit->semantic == PLY_PROP_NX) { if ((!pit->next || pit->next->semantic != PLY_PROP_NY) ||(!pit->next->next || pit->next->next->semantic != PLY_PROP_NZ)) { pit->ignore = true; if (pit->next) { pit->next->ignore = true; if (pit->next->next) pit->next->next->ignore = true; } goto ign; /* we cannot load this PLY, TODO: */ } /* alloc input and accessor for normals */ pst->ac_nor = io_acc(heap, doc, AK_COMPONENT_SIZE_VEC3, AKT_FLOAT, elem->count, elem->buff); } if (pit->semantic == PLY_PROP_S) { if (!pit->next || pit->next->semantic != PLY_PROP_T) { pit->ignore = true; if (pit->next && pit->next->next) pit->next->ignore = true; goto ign; /* ignore, TODO: */ } /* alloc input and accessor for tex coords */ pst->ac_tex = io_acc(heap, doc, AK_COMPONENT_SIZE_VEC2, AKT_FLOAT, elem->count, elem->buff); } if (pit->semantic == PLY_PROP_R) { if ((!pit->next || pit->next->semantic != PLY_PROP_G) || (!pit->next->next || pit->next->next->semantic != PLY_PROP_B)) { pit->ignore = true; if (pit->next) { pit->next->ignore = true; if (pit->next->next) pit->next->next->ignore = true; } goto ign; /* ignore, TODO: */ } /* alloc input and accessor for vertex colors */ pst->ac_rgb = io_acc(heap, doc, AK_COMPONENT_SIZE_VEC3, AKT_FLOAT, elem->count, elem->buff); } pit->slot = i++; pit->off = off; /* TODO: currently all are floats */ off += sizeof(float); /* pit->typeDesc->size; */ if (pit->typeDesc) pst->byteStride += pit->typeDesc->size; elem->knownCount++; ign: pit = pit->next; } /* empty buffer */ if (off < 1) goto err; /* alloc buffer for vertex element */ pst->vertBuffsize = off * elem->count; elem->buff->length = pst->vertBuffsize; elem->buff->data = ak_heap_alloc(heap, elem->buff, elem->buff->length); flist_sp_insert(&pst->doc->lib.buffers, elem->buff); /* prepare accessors' misssing params */ if (pst->ac_pos) { pst->ac_pos->byteLength = pst->vertBuffsize; pst->ac_pos->byteStride = pst->byteStride; byteSffset += sizeof(float) * 3; } if (pst->ac_nor) { pst->ac_nor->byteLength = pst->vertBuffsize; pst->ac_nor->byteStride = pst->byteStride; pst->ac_nor->byteOffset = byteSffset; byteSffset += sizeof(float) * 3; } if (pst->ac_tex) { pst->ac_tex->byteStride = pst->byteStride; pst->ac_tex->byteLength = pst->vertBuffsize; pst->ac_tex->byteOffset = byteSffset; byteSffset += sizeof(float) * 2; } if (pst->ac_rgb) { pst->ac_rgb->byteStride = pst->byteStride; pst->ac_rgb->byteLength = pst->vertBuffsize; pst->ac_rgb->byteOffset = byteSffset; /* byteSffset += sizeof(float) * 3; */ } } elem = elem->next; } /* parse */ if (isAscii) { ply_ascii(p, pst); } else { ply_bin(p, pst, isLittleEndian); } io_postscript(doc); *dest = doc; /* cleanup */ ak_free(pst->tmp); ak_releasefile(plystr, plystrSize); return AK_OK; err: ak_free(pst->tmp); ak_free(doc); ak_releasefile(plystr, plystrSize); return AK_ERR; } AK_HIDE void ply_finish(PLYState * __restrict pst) { AkHeap *heap; AkGeometry *geom; AkMesh *mesh; AkMeshPrimitive *prim; AkInstanceGeometry *instGeom; AkTriangles *tri; /* Buffer > Accessor > Input > Prim > Mesh > Geom > InstanceGeom > Node */ heap = pst->heap; mesh = ak_allocMesh(pst->heap, pst->lib_geom, &geom); tri = ak_heap_calloc(pst->heap, ak_objFrom(mesh), sizeof(*tri)); tri->mode = AK_TRIANGLES; tri->base.type = AK_PRIMITIVE_TRIANGLES; prim = (AkMeshPrimitive *)tri; prim->indexStride = 1; prim->nPolygons = pst->count; prim->mesh = mesh; mesh->primitive = prim; mesh->primitiveCount = 1; /* add to library */ geom->base.next = pst->lib_geom->chld; pst->lib_geom->chld = &geom->base; pst->lib_geom->count = 1; /* make instance geeometry and attach to the root node */ instGeom = ak_instanceMakeGeom(heap, pst->node, geom); if (pst->node->geometry) { pst->node->geometry->base.prev = (void *)instGeom; instGeom->base.next = (void *)pst->node->geometry; } pst->node->geometry = instGeom; /* positions */ if (pst->ac_pos) prim->pos = io_input(heap, prim, pst->ac_pos, AK_INPUT_POSITION, _s_POSITION, 0); /* normals */ if (pst->ac_nor) io_input(heap, prim, pst->ac_nor, AK_INPUT_NORMAL, _s_NORMAL, 0); /* tex coords */ if (pst->ac_tex) io_input(heap, prim, pst->ac_tex, AK_INPUT_TEXCOORD, _s_TEXCOORD, 0); /* vertex colors */ if (pst->ac_rgb) io_input(heap, prim, pst->ac_rgb, AK_INPUT_COLOR, _s_COLOR, 0); /* indices */ prim->indices = ak_heap_calloc(heap, tri, sizeof(*prim->indices) + pst->dc_ind->usedsize); prim->indices->count = pst->dc_ind->itemcount; ak_data_join(pst->dc_ind, prim->indices->items, 0, 0); } ================================================ FILE: src/io/ply/ply.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ply_h #define ply_h #include "common.h" AK_HIDE AkResult ply_ply(AkDoc ** __restrict dest, const char * __restrict filepath); AK_HIDE void ply_ascii(char * __restrict src, PLYState * __restrict pst); AK_HIDE void ply_bin(char * __restrict src, PLYState * __restrict pst, bool le); AK_HIDE void ply_finish(PLYState * __restrict pst); #endif /* stl_h */ ================================================ FILE: src/io/ply/util.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ply_util_h #define ply_util_h #include "common.h" #include "../../endian.h" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstrict-aliasing" #define ply_val(p, typeDesc, leEndian, T, DEST, DEFAULT) \ do { \ uint64_t buf; \ \ buf = 0; \ switch (typeDesc->typeId) { \ case AKT_FLOAT: \ case AKT_INT: \ case AKT_UINT: memcpy_endian32(leEndian, buf, p); break; \ case AKT_DOUBLE: \ case AKT_INT64: \ case AKT_UINT64: memcpy_endian64(leEndian, buf, p); break; \ case AKT_SHORT: \ case AKT_USHORT: memcpy_endian16(leEndian, buf, p); break; \ case AKT_BYTE: \ case AKT_UBYTE: memcpy(&buf, p++, 1); break; \ default: DEST = DEFAULT; break; \ } \ \ switch (typeDesc->typeId) { \ case AKT_FLOAT: DEST = (T)(*(float *)(void *)&buf); break; \ case AKT_INT: DEST = (T)(*(int32_t *)(void *)&buf); break; \ case AKT_UINT: DEST = (T)(*(uint32_t *)(void *)&buf); break; \ case AKT_DOUBLE: DEST = (T)(*(double *)(void *)&buf); break; \ case AKT_INT64: DEST = (T)(*(int64_t *)(void *)&buf); break; \ case AKT_UINT64: DEST = (T)(*(uint64_t *)(void *)&buf); break; \ case AKT_SHORT: DEST = (T)(*(int16_t *)(void *)&buf); break; \ case AKT_USHORT: DEST = (T)(*(uint16_t *)(void *)&buf); break; \ case AKT_BYTE: DEST = (T)(*(int8_t *)(void *)&buf); break; \ case AKT_UBYTE: DEST = (T)(*(uint8_t *)(void *)&buf); break; \ default: DEST = DEFAULT; break; \ } \ } while (0) #pragma GCC diagnostic pop #endif /* ply_util_h */ ================================================ FILE: src/io/stl/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/io/stl/README.md ================================================ # AssetKit: Standard Tesselated Geometry File Format (stl) Status - [x] ASCII - [x] Binary ================================================ FILE: src/io/stl/common.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef stl_commoh_h #define stl_commoh_h #include "../../../include/ak/assetkit.h" #include "../../common.h" #include "../../utils.h" #include "../../tree.h" #include "../../json.h" #include "../../data.h" #include #include typedef struct STLState { AkHeap *heap; AkDoc *doc; void *tmp; AkLibrary *lib_geom; AkGeometry *geom; AkDataContext *dc_ind, *dc_pos, *dc_nor, *dc_vcount; AkNode *node; uint32_t maxVC; uint32_t count; } STLState; #ifdef SKIP_SPACES # undef SKIP_SPACES #endif #define SKIP_SPACES \ { \ while (c != '\0' && AK_ARRAY_SPACE_CHECK) c = *++p; \ if (c == '\0') \ break; /* to break loop */ \ } #ifdef NEXT_LINE # undef NEXT_LINE #endif #define NEXT_LINE \ do { \ c = p ? *p : '\0'; \ while (p \ && p[0] != '\0' \ && !AK_ARRAY_NLINE_CHECK \ && (c = *++p) != '\0' \ && !AK_ARRAY_NLINE_CHECK); \ \ while (p \ && p[0] != '\0' \ && AK_ARRAY_NLINE_CHECK \ && (c = *++p) != '\0' \ && AK_ARRAY_NLINE_CHECK); \ } while(0); #define EQ4(c1,c2,c3,c4) \ (p[0] == c1 \ && p[1] == c2 \ && p[2] == c3 \ && p[3] == c4 \ && (p[4] == ' ' || p[4] == '\t')) #define EQ5(c1,c2,c3,c4,c5) \ (p[0] == c1 \ && p[1] == c2 \ && p[2] == c3 \ && p[3] == c4 \ && p[4] == c5 \ && (p[5] == ' ' || p[5] == '\t')) #define EQ6(c1,c2,c3,c4,c5,c6) \ (p[0] == c1 \ && p[1] == c2 \ && p[2] == c3 \ && p[3] == c4 \ && p[4] == c5 \ && p[5] == c6 \ && (p[6] == ' ' || p[6] == '\t')) #define EQ7(c1,c2,c3,c4,c5,c6,c7) \ (p[0] == c1 \ && p[1] == c2 \ && p[2] == c3 \ && p[3] == c4 \ && p[4] == c5 \ && p[5] == c6 \ && p[6] == c7 \ && (p[7] == ' ' || p[7] == '\t')) #define EQT7(c1,c2,c3,c4,c5,c6,c7) \ (p[0] == c1 \ && p[1] == c2 \ && p[2] == c3 \ && p[3] == c4 \ && p[4] == c5 \ && p[5] == c6 \ && p[6] == c7) #endif /* stl_commoh_h */ ================================================ FILE: src/io/stl/stl.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* Resources: https://all3dp.com/what-is-stl-file-format-extension-3d-printing/ https://danbscott.ghost.io/writing-an-stl-file-from-scratch/ https://en.wikipedia.org/wiki/STL_%28file_format%29 */ #include "stl.h" #include "common.h" #include "../../id.h" #include "../../data.h" #include "../../../include/ak/path.h" #include "../common/util.h" #include "../common/postscript.h" #include "../../endian.h" #include "../../strpool.h" AK_HIDE AkResult stl_stl(AkDoc ** __restrict dest, const char * __restrict filepath) { AkHeap *heap; AkDoc *doc; void *stlstr; char *p; AkLibrary *lib_vscene; AkVisualScene *scene; STLState sstVal = {0}, *sst; size_t stlstrSize; bool isAscii; if (ak_readfile(filepath, NULL, &stlstr, &stlstrSize) != AK_OK || !((p = stlstr) && *p != '\0')) return AK_ERR; if (p[0] == 's' && p[1] == 'o' && p[2] == 'l' && p[3] == 'i' && p[4] == 'd') { isAscii = true; } else if (p[0] != '\0' && stlstrSize > 80) { isAscii = false; } else { return AK_ERR; } heap = ak_heap_new(NULL, NULL, NULL); doc = ak_heap_calloc(heap, NULL, sizeof(*doc)); doc->inf = ak_heap_calloc(heap, doc, sizeof(*doc->inf)); doc->inf->name = filepath; doc->inf->dir = ak_path_dir(heap, doc, filepath); doc->inf->flipImage = true; doc->inf->ftype = AK_FILE_TYPE_STL; doc->inf->base.coordSys = AK_YUP; doc->coordSys = AK_YUP; /* Default */ ak_heap_setdata(heap, doc); ak_id_newheap(heap); /* libraries */ doc->lib.geometries = ak_heap_calloc(heap, doc, sizeof(AkGeometry)); lib_vscene = ak_heap_calloc(heap, doc, sizeof(*lib_vscene)); /* default scene */ scene = ak_heap_calloc(heap, doc, sizeof(*scene)); scene->node = ak_heap_calloc(heap, doc, sizeof(*scene->node)); lib_vscene->chld = &scene->base; lib_vscene->count = 1; doc->lib.visualScenes = lib_vscene; doc->scene.visualScene = ak_instanceMake(heap, doc, scene); /* parse state */ memset(&sstVal, 0, sizeof(sstVal)); sst = &sstVal; sstVal.doc = doc; sstVal.heap = heap; sstVal.tmp = ak_heap_alloc(heap, doc, sizeof(void*)); sstVal.node = scene->node; sstVal.lib_geom = doc->lib.geometries; sst->dc_ind = ak_data_new(sst->tmp, 128, sizeof(int32_t), NULL); sst->dc_pos = ak_data_new(sst->tmp, 128, sizeof(vec3), NULL); sst->dc_nor = ak_data_new(sst->tmp, 128, sizeof(vec3), NULL); sst->dc_vcount = ak_data_new(sst->tmp, 128, sizeof(int32_t), NULL); if (!isAscii) { stl_binary(sst, p); } else { stl_ascii(sst, p); } sst_finish(sst); io_postscript(doc); *dest = doc; /* cleanup */ ak_free(sst->tmp); ak_releasefile(stlstr, stlstrSize); return AK_OK; } AK_HIDE void stl_binary(STLState * __restrict sst, char * __restrict p) { vec4 v, n; uint32_t count, nTriangles, i; /* skip 80-char header */ p += 80; /* parse integers from little endian to native */ le_32(nTriangles, p); count = nTriangles * 3; sst->maxVC = 3; for (i = 0; i < nTriangles; i++) { /* normal */ le_32(n[0], p); le_32(n[1], p); le_32(n[2], p); ak_data_append(sst->dc_nor, n); ak_data_append(sst->dc_nor, n); ak_data_append(sst->dc_nor, n); /* vertex */ le_32(v[0], p); le_32(v[1], p); le_32(v[2], p); ak_data_append(sst->dc_pos, v); le_32(v[0], p); le_32(v[1], p); le_32(v[2], p); ak_data_append(sst->dc_pos, v); le_32(v[0], p); le_32(v[1], p); le_32(v[2], p); ak_data_append(sst->dc_pos, v); p += 2; } sst->count = count; } AK_HIDE void stl_ascii(STLState * __restrict sst, char * __restrict p) { vec4 v, n; uint32_t vc, count; char c; /* c = '\0'; */ count = 0; NEXT_LINE /* parse ASCII STL */ do { /* skip spaces */ SKIP_SPACES if (EQ5('f', 'a', 'c', 'e', 't')) { p += 6; SKIP_SPACES if (EQ6('n', 'o', 'r', 'm', 'a', 'l')) { p += 7; memset(v, 0, sizeof(vec4)); ak_strtof_line(p, 0, 3, n); /* ak_data_append(sst->dc_nor, n); */ NEXT_LINE SKIP_SPACES /* parse each vertex */ if (EQ5('o', 'u', 't', 'e', 'r')) { NEXT_LINE vc = 0; /* parse vertices */ while (c != '\0') { SKIP_SPACES if (EQ6('v', 'e', 'r', 't', 'e', 'x')) { p += 7; memset(v, 0, sizeof(vec4)); ak_strtof_line(p, 0, 3, v); ak_data_append(sst->dc_nor, n); /* duplicate normal */ ak_data_append(sst->dc_pos, v); vc++; } else if (EQT7('e', 'n', 'd', 'l', 'o', 'o', 'p')) { break; } NEXT_LINE } /* vertex */ count += vc; sst->maxVC = GLM_MAX(sst->maxVC, vc); ak_data_append(sst->dc_vcount, &vc); } /* outer loop */ } /* normal */ } /* facet */ NEXT_LINE } while (p && p[0] != '\0'/* && (c = *++p) != '\0'*/); sst->count = count; } AK_HIDE void sst_finish(STLState * __restrict sst) { AkHeap *heap; AkGeometry *geom; AkMesh *mesh; AkMeshPrimitive *prim; AkInstanceGeometry *instGeom; /* Buffer > Accessor > Input > Prim > Mesh > Geom > InstanceGeom > Node */ heap = sst->heap; mesh = ak_allocMesh(sst->heap, sst->lib_geom, &geom); if (sst->maxVC == 3) { AkTriangles *tri; tri = ak_heap_calloc(sst->heap, ak_objFrom(mesh), sizeof(*tri)); tri->mode = AK_TRIANGLES; tri->base.type = AK_PRIMITIVE_TRIANGLES; prim = (AkMeshPrimitive *)tri; } else { AkPolygon *poly; poly = ak_heap_calloc(sst->heap, ak_objFrom(mesh), sizeof(*poly)); poly->base.type = AK_PRIMITIVE_POLYGONS; poly->vcount = ak_heap_calloc(heap, poly, sizeof(*poly->vcount) + sst->dc_vcount->usedsize); poly->vcount->count = sst->dc_vcount->itemcount; ak_data_join(sst->dc_vcount, poly->vcount->items, 0, 0); prim = (AkMeshPrimitive *)poly; } prim->nPolygons = sst->count; prim->mesh = mesh; mesh->primitive = prim; mesh->primitiveCount = 1; /* add to library */ geom->base.next = sst->lib_geom->chld; sst->lib_geom->chld = &geom->base; sst->lib_geom->count = 1; /* make instance geeometry and attach to the root node */ instGeom = ak_instanceMakeGeom(heap, sst->node, geom); if (sst->node->geometry) { sst->node->geometry->base.prev = (void *)instGeom; instGeom->base.next = (void *)sst->node->geometry; } sst->node->geometry = instGeom; prim->pos = io_addInput(heap, sst->dc_pos, prim, AK_INPUT_POSITION, _s_POSITION, AK_COMPONENT_SIZE_VEC3, AKT_FLOAT, 0); if (sst->dc_nor->itemcount > 0) { io_addInput(heap, sst->dc_nor, prim, AK_INPUT_NORMAL, _s_NORMAL, AK_COMPONENT_SIZE_VEC3, AKT_FLOAT, 1); } /* cleanup */ if (sst->dc_ind) { ak_free(sst->dc_ind); ak_free(sst->dc_pos); ak_free(sst->dc_nor); ak_free(sst->dc_vcount); } sst->dc_ind = NULL; sst->dc_pos = NULL; sst->dc_nor = NULL; sst->dc_vcount = NULL; } ================================================ FILE: src/io/stl/stl.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef stl_h #define stl_h #include "common.h" AK_HIDE AkResult stl_stl(AkDoc ** __restrict dest, const char * __restrict filepath); AK_HIDE void sst_finish(STLState * __restrict sst); AK_HIDE void stl_ascii(STLState * __restrict sst, char * __restrict p); AK_HIDE void stl_binary(STLState * __restrict sst, char * __restrict p); #endif /* stl_h */ ================================================ FILE: src/json.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_json_h #define ak_json_h #include #include /* JSON parser */ #include AK_INLINE char * json_strdup(const json_t * __restrict jsonObject, AkHeap * __restrict heap, void * __restrict parent) { return ak_heap_strndup(heap, parent, json_string(jsonObject), jsonObject->valsize); } AK_INLINE void json_array_set(void * __restrict p, AkTypeId typeId, int index, const json_t * __restrict json) { switch (typeId) { case AKT_FLOAT: ((float *)p)[index] = json_float(json, 0.0f); break; case AKT_INT: ((int32_t *)p)[index] = json_int32(json, 0); break; case AKT_UINT: ((int32_t *)p)[index] = json_uint32(json, 0); break; case AKT_SHORT: ((int16_t *)p)[index] = json_int32(json, 0); break; case AKT_USHORT: ((uint16_t *)p)[index] = json_uint32(json, 0); break; case AKT_BYTE: ((char *)p)[index] = json_int32(json, 0); break; case AKT_UBYTE: ((unsigned char *)p)[index] = json_uint32(json, 0); break; default: break; } } #endif /* ak_json_h */ ================================================ FILE: src/lib/CMakeLists.txt ================================================ target_sources(${PROJECT_NAME} PRIVATE geom.c lib.c ) ================================================ FILE: src/lib/geom.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" AK_EXPORT AkGeometry * ak_libFirstGeom(AkDoc * __restrict doc) { if (!doc->lib.geometries) return NULL; return (void *)doc->lib.geometries->chld; } ================================================ FILE: src/lib/lib.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" AK_EXPORT AkResult ak_libAddCamera(AkDoc * __restrict doc, AkCamera * __restrict cam) { AkHeap *heap; AkLibrary *libItem; AkCamera *cami; heap = ak_heap_getheap(doc); libItem = doc->lib.cameras; if (!libItem) { libItem = ak_heap_calloc(heap, doc, sizeof(*libItem)); doc->lib.cameras = libItem; } cami = (void *)libItem->chld; if (cami) { cam->base.next = &cami->base; } libItem->chld = (void *)cam; /* Increment on every add — earlier this was set to 1 on library creation only, so adding a second / third camera left `count` at 1 and downstream code that sized arrays from this value silently truncated. */ libItem->count++; return AK_OK; } AK_EXPORT AkResult ak_libAddLight(AkDoc * __restrict doc, AkLight * __restrict light) { AkHeap *heap; AkLibrary *libItem; AkLight *lighti; heap = ak_heap_getheap(doc); libItem = doc->lib.lights; if (!libItem) { libItem = ak_heap_calloc(heap, doc, sizeof(*libItem)); doc->lib.lights = libItem; } lighti = (void *)libItem->chld; if (lighti) { light->next = lighti; } libItem->chld = (void *)light; libItem->count++; return AK_OK; } AK_EXPORT void ak_libInsertInto(AkLibrary *lib, void *item, int32_t prevOffset, int32_t nextOffset) { char *libChld, *lastLibChld; libChld = lastLibChld = (void *)lib->chld; while (libChld) { libChld = *(char **)(lastLibChld + nextOffset); if (libChld) lastLibChld = libChld; } if (lastLibChld) *(void **)(lastLibChld + nextOffset) = item; else lib->chld = item; if (prevOffset > -1) *(void **)((char *)item + prevOffset) = lastLibChld; } AK_EXPORT AkLibrary* ak_libFirstOrCreat(AkDoc * __restrict doc, uint32_t itemOffset) { AkHeap *heap; AkLibrary *lib; heap = ak_heap_getheap(doc); lib = *(void **)((char *)&doc->lib + itemOffset); if (lib) return lib; lib = ak_heap_calloc(heap, doc, sizeof(*lib)); doc->lib.libimages = lib; return lib; } AK_EXPORT AkLibrary* ak_libImageFirstOrCreat(AkDoc * __restrict doc) { return ak_libFirstOrCreat(doc, offsetof(AkLibraries, libimages)); } ================================================ FILE: src/light/CMakeLists.txt ================================================ target_sources(${PROJECT_NAME} PRIVATE light.c ) ================================================ FILE: src/light/light.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "../default/light.h" #include /* this duplicates default light to new light, because we want to keep default not modified, users may want to modify imported light, they don't know this is default or not, and we don't wan to force them to check lights are default or not. */ AK_EXPORT AkLight * ak_lightMake(AkDoc * __restrict doc, void * __restrict memparent, AkLightType type) { AkHeap *heap; AkLight *light; AkLightBase *base; size_t baseSize; if (!doc) return NULL; heap = ak_heap_getheap(doc); /* Pick the right concrete sub-struct for the type. Ambient and directional don't have extra fields beyond AkLightBase, so they share its allocation. Point and Spot extend the base with attenuation (and Spot adds falloff). */ switch (type) { case AK_LIGHT_TYPE_POINT: baseSize = sizeof(AkPointLight); break; case AK_LIGHT_TYPE_SPOT: baseSize = sizeof(AkSpotLight); break; default: baseSize = sizeof(AkLightBase); break; } light = ak_heap_calloc(heap, memparent ? memparent : (void *)doc, sizeof(*light)); light->tcommon = ak_heap_calloc(heap, light, baseSize); base = light->tcommon; base->type = type; /* White color, full alpha — sensible default for any light kind. */ base->color.rgba.R = 1.0f; base->color.rgba.G = 1.0f; base->color.rgba.B = 1.0f; base->color.rgba.A = 1.0f; base->intensity = 1.0f; base->range = 0.0f; /* Point downward by convention. Ignored for ambient/point but harmless to set; directional/spot use it as the beam axis. */ base->direction[0] = 0.0f; base->direction[1] = -1.0f; base->direction[2] = 0.0f; /* Per-type attenuation defaults. constAttn=1 means "no attenuation at the source"; linear/quad stay 0 so the light reaches forever until the consumer caps the range. Spot adds a 30° cone with linear falloff. */ if (type == AK_LIGHT_TYPE_POINT) { AkPointLight *p = (AkPointLight *)base; p->constAttn = 1.0f; p->linearAttn = 0.0f; p->quadAttn = 0.0f; } else if (type == AK_LIGHT_TYPE_SPOT) { AkSpotLight *s = (AkSpotLight *)base; s->constAttn = 1.0f; s->linearAttn = 0.0f; s->quadAttn = 0.0f; s->innerConeAngle = 0.0f; s->outerConeAngle = GLM_PI_4f; s->falloffAngle = glm_rad(30.0f); s->falloffExp = 1.0f; } ak_libAddLight(doc, light); return light; } AK_EXPORT AkLight* ak_defaultLight(void * __restrict memparent) { AkHeap *heap; AkDoc *doc; AkLight *light; AkCoordSys *coordsys; const AkLight *deflight; deflight = ak_def_light(); if (memparent) heap = ak_heap_getheap(memparent); else heap = ak_heap_default(); doc = ak_heap_data(heap); light = ak_heap_calloc(heap, memparent, sizeof(*light)); memcpy(light, deflight, sizeof(*deflight)); light->tcommon = ak_heap_calloc(heap, light, sizeof(AkDirectionalLight)); memcpy(light->tcommon, deflight->tcommon, sizeof(AkDirectionalLight)); /* convert light direction */ if (ak_opt_get(AK_OPT_COORD_CONVERT_TYPE) != AK_COORD_CVT_DISABLED) coordsys = (void *)ak_opt_get(AK_OPT_COORD); else coordsys = doc->coordSys; ak_coordCvtVector(AK_YUP, light->tcommon->direction, coordsys); return light; } ================================================ FILE: src/main.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "common.h" #include "mem/rb.h" #include "mem/lt.h" #include "trash.h" #include "sid.h" #include "profile.h" #include "type.h" #include "resc/resource.h" void AK_CONSTRUCTOR ak__init(void); void AK_DESTRUCTOR ak__cleanup(void); void AK_CONSTRUCTOR ak__init(void) { ak_mem_init(); ak_trash_init(); ak_resc_init(); ak_type_init(); ak_sid_init(); ak_profile_init(); } void AK_DESTRUCTOR ak__cleanup(void) { ak_profile_deinit(); ak_sid_deinit(); ak_type_deinit(); ak_resc_deinit(); ak_trash_deinit(); ak_mem_deinit(); } ================================================ FILE: src/map.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../include/ak/map.h" #include "../include/ak/util.h" #include "mem/common.h" AkMap * ak_map_new(AkMapCmp cmp) { AkMap *map; AkHeap *heap; heap = ak_heap_new(NULL, cmp ? cmp : ak_cmp_ptr, NULL); map = ak_heap_alloc(heap, NULL, sizeof(*map)); ak_heap_setdata(heap, map); map->heap = heap; map->root = NULL; return map; } void ak_map_addptr(AkMap *map, void *ptr) { AkHeapNode *hnode; AkMapItem *mi; AkResult ret; ret = ak_heap_getNodeById(map->heap, ptr, &hnode); if (ret == AK_OK) return; mi = ak_heap_alloc(map->heap, NULL, sizeof(*mi)); ak_heap_setId(map->heap, ak__alignof(mi), ptr); mi->prev = NULL; mi->next = map->root; if (map->root) map->root->prev = mi; map->root = mi; } void* ak_map_find(AkMap *map, void *id) { AkMapItem *mi; mi = ak_map_findm(map, id); if (mi) return mi->data; return NULL; } AkMapItem* ak_map_findm(AkMap *map, void *id) { void *mem; AkResult ret; ret = ak_heap_getMemById(map->heap, id, &mem); if (ret == AK_OK) return mem; return NULL; } void ak_map_add(AkMap *map, void *value, void *id) { AkHeapNode *hnode; AkMapItem *mi; AkResult ret; ret = ak_heap_getNodeById(map->heap, id, &hnode); if (ret == AK_OK) return; mi = ak_heap_alloc(map->heap, NULL, sizeof(*mi)); mi->data = value; ak_heap_setId(map->heap, ak__alignof(mi), id); mi->prev = NULL; mi->next = map->root; if (map->root) map->root->prev = mi; map->root = mi; } void ak_multimap_add(AkMap *map, void *value, void *id) { AkHeapNode *hnode; AkMapItem *mi; AkMapItem *subItm; AkResult ret; ret = ak_heap_getNodeById(map->heap, id, &hnode); if (ret == AK_OK) { AkMapItem *mii; mi = ak__alignas(hnode); subItm = ak_heap_calloc(map->heap, NULL, sizeof(*mi)); subItm->data = value; mii = (AkMapItem *)mi->data; if (mii) mii->prev = subItm; subItm->next = mii; mi->data = subItm; return; } mi = ak_heap_calloc(map->heap, NULL, sizeof(*mi)); subItm = ak_heap_calloc(map->heap, NULL, sizeof(*mi)); mi->isMapItem = true; mi->data = subItm; subItm->data = value; ak_heap_setId(map->heap, ak__alignof(mi), id); mi->prev = NULL; mi->next = map->root; if (map->root) map->root->prev = mi; map->root = mi; } void ak_map_destroy(AkMap *map) { ak_free(map); } ================================================ FILE: src/mat/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/mat/mat.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "../../include/ak/material.h" AK_EXPORT AkMaterialVariant* ak_materialVariantByName(AkDoc * __restrict doc, const char * __restrict name) { AkMaterialVariant *it; if (!doc || !name) return NULL; it = doc->materialVariants; while (it) { if (it->name && strcmp(it->name, name) == 0) return it; it = it->next; } return NULL; } AK_EXPORT AkEffect* ak_effectForBindMaterial(AkBindMaterial * __restrict bindMat, AkMeshPrimitive * __restrict meshPrim, AkInstanceMaterial ** __restrict foundInstMat) { AkMaterial *material; AkInstanceMaterial *materialInst; AkGeometry *geom; AkMap *materialMap; AkMapItem *mi; if (!meshPrim || !meshPrim->mesh || !meshPrim->mesh->geom) return NULL; geom = meshPrim->mesh->geom; materialInst = bindMat->tcommon; materialMap = geom->materialMap; while (materialInst) { /* there is symbol, bind only to specified primitive */ if (materialInst->symbol) { mi = ak_map_find(materialMap, (void *)materialInst->symbol); while (mi) { if ((AkMeshPrimitive *)mi->data == meshPrim) { material = ak_instanceObject(&materialInst->base); if (material && material->effect) { *foundInstMat = materialInst; return ak_instanceObject(&material->effect->base); } else { return NULL; } } mi = mi->next; } } /* bind to whole geometry, TODO: is this OK ? */ else { material = ak_instanceObject(&materialInst->base); if (material && material->effect) { *foundInstMat = materialInst; return ak_instanceObject(&material->effect->base); } else { return NULL; } } materialInst = (AkInstanceMaterial *)materialInst->base.next; } return NULL; } ================================================ FILE: src/mem/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/mem/common.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_memory_h #define ak_memory_h #include "../../include/ak/memory.h" #include "../common.h" #include #define ak__align_size 8 #define ak__heapnd_sz offsetof(AkHeapNode, data) #define ak__align(size) ((size + ak__align_size - 1) \ &~ (uintptr_t)(ak__align_size - 1)) #define ak__alignof(p) ((AkHeapNode *)(((char *)p) - ak__heapnd_sz)) #define ak__alignas(m) ((void *)(((char *)m) + ak__heapnd_sz)) #define AK__BST_LEFT 0 #define AK__BST_RIGHT 1 /* * Binary Search Tree Node (Red Black) */ typedef struct AkHeapSrchNode { void *key; struct AkHeapSrchNode *chld[2]; } AkHeapSrchNode; struct AkHeapSrchCtx { AkHeapSrchNode *root; AkHeapSrchNode *nullNode; AkHeapSrchCmpFn cmp; AkHeapSrchPrintFn print; }; typedef struct AkSIDNode { /* | offset0 | sid_ptr0 | ... | uint16_t | uintptr_t | ... */ void *sids; void *refs; const char *sid; } AkSIDNode; typedef struct AkUrlNode { size_t len; void **urls; } AkUrlNode; typedef struct AkMemoryMapNode { struct AkMemoryMapNode *prev; struct AkMemoryMapNode *next; void *mapped; size_t sized; } AkMemoryMapNode; #define AK__HEAPNODE(X) \ (((AkHeapNodeExt *)((char *)X - offsetof(AkHeapNodeExt, data)))->node) /* - prev - AkHeapNode - next - | AkHeapNode o AkHeapNodeExt | chld | */ struct AkHeapNode { AkHeapNode *prev; /* parent */ AkHeapNode *next; /* right */ void *chld; /* left */ uint32_t heapid; uint16_t typeid; uint16_t flags; char data[]; }; /* - prev - AkHeapNode - next - | AkHeapNodeExt - data | chld | data: data must contain items with these order with these data types *-----------------------------------------------------------------------------* | id | sid | refc | extra | inf | usr | url | | SrchNode | SidNode | size_t | uintptr_t | uintptr_t | uintptr_t | UrlNode | */ typedef struct AkHeapNodeExt { AkHeapNode *node; AkHeapNode *chld; char data[]; } AkHeapNodeExt; struct AkHeap { AkHeapAllocator *allocator; AkHeapNode *root; AkHeapNode *trash; AkHeapSrchCtx *srchctx; AkHeap *chld; /* attached heaps, free all with this */ AkHeap *next; void *data; AkHeap *idheap; uint32_t heapid; AkEnum flags; }; void ak_sid_destroy(AkHeap * __restrict heap, AkSIDNode * __restrict snode); void * ak_heap_ext_get(AkHeapNode * __restrict hnode, uint16_t flag); void * ak_heap_ext_add(AkHeap * __restrict heap, AkHeapNode * __restrict hnode, uint16_t flag); void ak_heap_ext_rm(AkHeap * __restrict heap, AkHeapNode * __restrict hnode, uint16_t flag); void ak_heap_ext_free(AkHeap * __restrict heap, AkHeapNode * __restrict hnode); AK_HIDE void ak_freeh(AkHeapNode * __restrict heapNode); void ak_mem_init(void); void ak_mem_deinit(void); AK_HIDE void ak_dsSetAllocator(AkHeapAllocator * __restrict alc, DsAllocator * __restrict dsalc); #endif /* ak_memory_h */ ================================================ FILE: src/mem/ext.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "common.h" #include "rb.h" #include "../bitwise/bitwise.h" static uint8_t ak__heap_ext_sz[] = { (uint8_t)sizeof(AkHeapSrchNode), /* AK_HEAP_NODE_FLAGS_SRCH */ (uint8_t)sizeof(AkSIDNode), /* AK_HEAP_NODE_FLAGS_SID */ (uint8_t)sizeof(int), /* AK_HEAP_NODE_FLAGS_REFC */ (uint8_t)sizeof(uintptr_t), /* AK_HEAP_NODE_FLAGS_EXTRA */ (uint8_t)sizeof(uintptr_t), /* AK_HEAP_NODE_FLAGS_INF */ (uint8_t)sizeof(AkUrlNode), /* AK_HEAP_NODE_FLAGS_URL */ (uint8_t)sizeof(uintptr_t), /* AK_HEAP_NODE_FLAGS_USR */ (uint8_t)0, /* AK_HEAP_NODE_FLAGS_USRF */ (uint8_t)sizeof(uintptr_t) /* AK_HEAP_NODE_FLAGS_MMAP */ }; AK_INLINE uint32_t ak_heap_ext_size(uint16_t flags) { uint32_t sz, flag, i, ctz; sz = 0; ctz = (31 - ak_bitw_clz(flags)); flag = 1 << ctz; i = ctz - ak_bitw_ctz(AK_HEAP_NODE_FLAGS_EXT_FRST); while (flag >= AK_HEAP_NODE_FLAGS_EXT_FRST) { if (flags & flag) sz += ak__heap_ext_sz[i]; flag >>= 1; i--; } return sz; } AK_INLINE uint32_t ak_heap_ext_off(uint16_t flags, uint16_t flag) { uint32_t sz, i; sz = 0; i = ak_bitw_ctz(flag) - ak_bitw_ctz(AK_HEAP_NODE_FLAGS_EXT_FRST); while ((flag >>= 1) >= AK_HEAP_NODE_FLAGS_EXT_FRST) { i--; if (flags & flag) sz += ak__heap_ext_sz[i]; } return sz; } AK_INLINE void ak_heap_ext_freeurl(AkHeapNode * __restrict hnode) { AkUrlNode *urlNode; void *urlobj; void **it, *last; size_t len; urlNode = ak_heap_ext_get(hnode, AK_HEAP_NODE_FLAGS_REFC); len = urlNode->len; it = urlNode->urls[0]; last = urlNode->urls[len]; while (it != last) { /* check if object is available */ if ((urlobj = ak_getObjectByUrl(*it))) ak_release(urlobj); it++; } } void * ak_heap_ext_get(AkHeapNode * __restrict hnode, uint16_t flag) { AkHeapNodeExt *exnode; uint32_t ofst; if (!(hnode->flags & flag)) return NULL; exnode = hnode->chld; ofst = ak_heap_ext_off(hnode->flags & ~flag, flag); return &exnode->data[ofst]; } void * ak_heap_ext_add(AkHeap * __restrict heap, AkHeapNode * __restrict hnode, uint16_t flag) { AkHeapAllocator *alc; AkHeapNodeExt *exnode; uint32_t sz, ofst, isz, flag_off; ofst = ak_heap_ext_off(hnode->flags, flag); /* nothing to do */ if (hnode->flags & flag) { exnode = hnode->chld; return &exnode->data[ofst]; } flag_off = ak_bitw_ctz(AK_HEAP_NODE_FLAGS_EXT_FRST); alc = heap->allocator; sz = ak_heap_ext_size(hnode->flags | flag); isz = ak__heap_ext_sz[ak_bitw_ctz(flag >> flag_off)]; if (!(hnode->flags & AK_HEAP_NODE_FLAGS_EXT)) { exnode = alc->malloc(sizeof(*exnode) + sz); exnode->node = hnode; exnode->chld = hnode->chld; hnode->flags |= AK_HEAP_NODE_FLAGS_EXT; } else { AkHeapSrchNode *curr, *parent; int side, tomove; exnode = hnode->chld; parent = NULL; side = 1; /* save link */ if (hnode->flags & AK_HEAP_NODE_FLAGS_SRCH) { curr = (AkHeapSrchNode *)exnode->data; side = ak_heap_rb_parent(heap->srchctx, curr->key, &parent); } exnode = alc->realloc(exnode, sizeof(*exnode) + sz); tomove = sz - isz - ofst; if (tomove > 0) memmove(&exnode->data[ofst + isz], &exnode->data[ofst], tomove); /* update link */ if (parent) { curr = (AkHeapSrchNode *)exnode->data; parent->chld[side] = curr; } } memset(&exnode->data[ofst], 0, isz); hnode->chld = exnode; hnode->flags |= flag; return &exnode->data[ofst]; } void ak_heap_ext_rm(AkHeap * __restrict heap, AkHeapNode * __restrict hnode, uint16_t flag) { AkHeapAllocator *alc; AkHeapNodeExt *exnode; uint32_t sz, ofst, isz, flag_off; /* nothing to do */ if (!(hnode->flags & flag)) return; flag_off = ak_bitw_ctz(AK_HEAP_NODE_FLAGS_EXT_FRST); alc = heap->allocator; sz = ak_heap_ext_size(hnode->flags & ~flag); ofst = ak_heap_ext_off(hnode->flags, flag); isz = ak__heap_ext_sz[ak_bitw_ctz(flag >> flag_off)]; exnode = hnode->chld; /* free items */ switch (flag) { case AK_HEAP_NODE_FLAGS_SRCH: ak_heap_rb_remove(heap->srchctx, (AkHeapSrchNode *)&exnode->data[ofst]); hnode->flags &= ~AK_HEAP_NODE_FLAGS_RED; break; case AK_HEAP_NODE_FLAGS_SID: ak_sid_destroy(heap, (AkSIDNode *)&exnode->data[ofst]); break; case AK_HEAP_NODE_FLAGS_EXTRA: case AK_HEAP_NODE_FLAGS_INF: /* ak_free(&exnode->data[ofst]); */ break; case AK_HEAP_NODE_FLAGS_USR: if (hnode->flags & AK_HEAP_NODE_FLAGS_USRF) alc->free(&exnode->data[ofst]); break; case AK_HEAP_NODE_FLAGS_URL: ak_heap_ext_freeurl(hnode); break; } hnode->flags &= ~flag; if (sz > 0) { AkHeapSrchNode *curr, *parent; int side; parent = NULL; side = 1; /* save link */ if (hnode->flags & AK_HEAP_NODE_FLAGS_SRCH) { curr = (AkHeapSrchNode *)exnode->data; side = ak_heap_rb_parent(heap->srchctx, curr->key, &parent); } if (sz > ofst) memmove(&exnode->data[ofst] + isz, &exnode->data[ofst], sz - ofst); exnode = alc->realloc(exnode, sz + sizeof(*exnode)); /* update link */ if (parent) { curr = (AkHeapSrchNode *)exnode->data; parent->chld[side] = curr; } hnode->chld = exnode; } else { hnode->chld = exnode->chld; alc->free(exnode); hnode->flags &= ~AK_HEAP_NODE_FLAGS_EXT_ALL; } } void ak_heap_ext_free(AkHeap * __restrict heap, AkHeapNode * __restrict hnode) { AkHeapAllocator *alc; AkHeapNodeExt *exnode; uint32_t ofst; /* nothing to do */ if (!(hnode->flags & AK_HEAP_NODE_FLAGS_EXT)) return; alc = heap->allocator; exnode = hnode->chld; /* free items */ if (hnode->flags & AK_HEAP_NODE_FLAGS_SRCH) { ofst = ak_heap_ext_off(hnode->flags, AK_HEAP_NODE_FLAGS_SRCH); ak_heap_rb_remove(heap->srchctx, (AkHeapSrchNode *)&exnode->data[ofst]); } if (hnode->flags & AK_HEAP_NODE_FLAGS_SID) { ofst = ak_heap_ext_off(hnode->flags, AK_HEAP_NODE_FLAGS_SID); ak_sid_destroy(heap, (AkSIDNode *)&exnode->data[ofst]); } if (hnode->flags & AK_HEAP_NODE_FLAGS_EXTRA) { /* ofst = ak_heap_ext_off(hnode->flags, AK_HEAP_NODE_FLAGS_EXTRA); ak_free(&exnode->data[ofst]); */ } if (hnode->flags & AK_HEAP_NODE_FLAGS_INF) { /* ofst = ak_heap_ext_off(hnode->flags, AK_HEAP_NODE_FLAGS_INF); ak_free(&exnode->data[ofst]); */ } if (hnode->flags & AK_HEAP_NODE_FLAGS_URL) ak_heap_ext_freeurl(hnode); if (hnode->flags & AK_HEAP_NODE_FLAGS_USR) { ofst = ak_heap_ext_off(hnode->flags, AK_HEAP_NODE_FLAGS_INF); if (hnode->flags & AK_HEAP_NODE_FLAGS_USRF) alc->free(&exnode->data[ofst]); } if (hnode->flags & AK_HEAP_NODE_FLAGS_MMAP) ak_unmmap_attached(ak__alignas(hnode)); hnode->chld = exnode->chld; alc->free(exnode); hnode->flags &= ~AK_HEAP_NODE_FLAGS_EXT_ALL; } ================================================ FILE: src/mem/intr.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "common.h" AK_HIDE void ak_dsSetAllocator(AkHeapAllocator * __restrict alc, DsAllocator * __restrict dsalc) { dsalc->calloc = alc->calloc; dsalc->free = alc->free; dsalc->malloc = alc->malloc; dsalc->memalign = alc->memalign; dsalc->realloc = alc->realloc; dsalc->size = alc->size; dsalc->strdup = alc->strdup; } ================================================ FILE: src/mem/lt.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "lt.h" #include #include static AkHeapBucket ak__heap_bucket = { .heapEntry = NULL, .firstAvailEntry = 1, .count = 1, .bucketIndex = 0 }; static AkHeapLookupTable ak__heap_lt = { .rootBucket = &ak__heap_bucket, .lastBucket = &ak__heap_bucket, .firstAvailBucket = &ak__heap_bucket, .size = 1, .bucketSize = 4, }; static AkHeapBucket * ak__heap_lt_find_bucket(uint32_t bucketIndex, AkHeapBucket ** __restrict prev) { AkHeapBucket *bucket, *prevBucket; bucket = ak__heap_lt.rootBucket; prevBucket = NULL; while (bucket && bucket->bucketIndex < bucketIndex) { prevBucket = bucket; bucket = bucket->next; } if (prev) *prev = prevBucket; if (!bucket || bucket->bucketIndex != bucketIndex) return NULL; return bucket; } static AkHeapBucket * ak__heap_lt_find_avail_from(AkHeapBucket * __restrict bucket) { while (bucket && AK__LT_BUCKET_IS_FULL(bucket)) bucket = bucket->next; return bucket; } static bool ak__heap_lt_entry_in_bucket(AkHeapBucket * __restrict bucket, AkHeapBucketEntry * __restrict entry) { return entry >= bucket->heapEntry && entry < bucket->heapEntry + ak__heap_lt.bucketSize; } static void ak__heap_lt_clear_last_used(AkHeapBucket * __restrict bucket, AkHeapBucketEntry * __restrict entry, bool clearBucket) { if (!ak__heap_lt.lastUsedEntry) return; if (ak__heap_lt.lastUsedEntry == entry || (clearBucket && ak__heap_lt_entry_in_bucket(bucket, ak__heap_lt.lastUsedEntry))) ak__heap_lt.lastUsedEntry = NULL; } void ak_heap_lt_init(AkHeap * __restrict initialHeap) { assert(initialHeap && "heap cannot be null!"); ak__heap_bucket.heapEntry = calloc(ak__heap_lt.bucketSize, sizeof(AkHeapBucketEntry)); assert(ak__heap_bucket.heapEntry && "malloc failed"); ak__heap_bucket.heapEntry[0] = (AkHeapBucketEntry){ .heap = initialHeap, .heapid = 0 }; ak__heap_lt.lastUsedEntry = ak__heap_lt.rootBucket->heapEntry; } void ak_heap_lt_insert(AkHeap * __restrict heap) { AkHeapBucket *bucket; AkHeapBucketEntry *bucketEntry; uint32_t entryIndex; uint32_t heapid; bucket = ak__heap_lt.firstAvailBucket; /* all buckets are full */ if (!bucket || AK__LT_BUCKET_IS_FULL(bucket)) { bucket = calloc(1, sizeof(*bucket)); assert(bucket && "malloc failed"); bucket->heapEntry = calloc(ak__heap_lt.bucketSize, sizeof(*bucket->heapEntry)); assert(bucket->heapEntry && "malloc failed"); bucket->bucketIndex = ak__heap_lt.lastBucket->bucketIndex + 1; ak__heap_lt.size++; ak__heap_lt.lastBucket->next = bucket; ak__heap_lt.lastBucket = bucket; ak__heap_lt.firstAvailBucket = bucket; } entryIndex = bucket->firstAvailEntry; bucketEntry = &bucket->heapEntry[entryIndex]; bucketEntry->heap = heap; heapid = bucket->bucketIndex * ak__heap_lt.bucketSize + entryIndex; bucketEntry->heapid = heapid; heap->heapid = heapid; /* find next avail entry */ while (++bucket->firstAvailEntry < ak__heap_lt.bucketSize) { if (!bucket->heapEntry[bucket->firstAvailEntry].heap) break; } if (AK__LT_BUCKET_IS_FULL(bucket)) ak__heap_lt.firstAvailBucket = NULL; bucket->count++; ak__heap_lt.lastUsedEntry = bucketEntry; } AkHeap * ak_heap_lt_find(uint32_t heapid) { AkHeapBucket *bucket; AkHeapBucketEntry *entry; uint32_t bucketIndex; uint32_t entryIndex; if (ak__heap_lt.lastUsedEntry && ak__heap_lt.lastUsedEntry->heap && ak__heap_lt.lastUsedEntry->heapid == heapid) return ak__heap_lt.lastUsedEntry->heap; bucketIndex = heapid / ak__heap_lt.bucketSize; entryIndex = heapid % ak__heap_lt.bucketSize; bucket = ak__heap_lt_find_bucket(bucketIndex, NULL); if (!bucket) return NULL; entry = &bucket->heapEntry[entryIndex]; if (!entry->heap || entry->heapid != heapid) return NULL; ak__heap_lt.lastUsedEntry = entry; return entry->heap; } void ak_heap_lt_remove(uint32_t heapid) { AkHeapBucket *prevBucket; AkHeapBucket *bucket; AkHeapBucketEntry *entry; uint32_t bucketIndex; uint32_t entryIndex; bool destroyBucket; bucketIndex = heapid / ak__heap_lt.bucketSize; entryIndex = heapid % ak__heap_lt.bucketSize; bucket = ak__heap_lt_find_bucket(bucketIndex, &prevBucket); if (!bucket) return; entry = &bucket->heapEntry[entryIndex]; if (entry && entry->heap && entry->heapid == heapid) { memset(&bucket->heapEntry[entryIndex], '\0', sizeof(AkHeapBucketEntry)); bucket->count--; if (!AK__LT_BUCKET_IS_FULL(bucket)) bucket->firstAvailEntry = entryIndex; destroyBucket = bucket->count < 1 && bucket != &ak__heap_bucket; ak__heap_lt_clear_last_used(bucket, entry, destroyBucket); if (destroyBucket) { if (ak__heap_lt.firstAvailBucket == bucket) { ak__heap_lt.firstAvailBucket = ak__heap_lt_find_avail_from(bucket->next); } if (ak__heap_lt.lastBucket == bucket) ak__heap_lt.lastBucket = prevBucket; /* we know that prevBucket cannot be null because rootBucket is static */ prevBucket->next = bucket->next; free(bucket->heapEntry); free(bucket); ak__heap_lt.size--; } else { if (!ak__heap_lt.firstAvailBucket || bucket->bucketIndex < ak__heap_lt.firstAvailBucket->bucketIndex) ak__heap_lt.firstAvailBucket = bucket; } } } void ak_heap_lt_cleanup(void) { AkHeapBucket *bucket; AkHeapBucket *toFree; bucket = ak__heap_lt.rootBucket->next; while (bucket) { toFree = bucket; bucket = bucket->next; free(toFree->heapEntry); free(toFree); } free(ak__heap_bucket.heapEntry); } ================================================ FILE: src/mem/lt.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_hashtable_h #define ak_hashtable_h #include "common.h" #define AK__LT_BUCKET_IS_FULL(X) (X->count == ak__heap_lt.bucketSize) typedef struct AkHeapBucketEntry { AkHeap *heap; uint32_t heapid; } AkHeapBucketEntry; typedef struct AkHeapBucket { struct AkHeapBucket *next; AkHeapBucketEntry *heapEntry; uint32_t bucketIndex; uint32_t count; uint32_t firstAvailEntry; } AkHeapBucket; typedef struct AkHeapLookupTable { AkHeapBucket *rootBucket; AkHeapBucket *lastBucket; AkHeapBucket *firstAvailBucket; AkHeapBucketEntry *lastUsedEntry; /* cache last */ size_t size; uint32_t bucketSize; /* default = 4 */ } AkHeapLookupTable; void ak_heap_lt_init(AkHeap * __restrict initialHeap); AkHeap * ak_heap_lt_find(uint32_t heapid); void ak_heap_lt_remove(uint32_t heapid); void ak_heap_lt_insert(AkHeap * __restrict heap); void ak_heap_lt_cleanup(void); #endif /* ak_hashtable_h */ ================================================ FILE: src/mem/mem.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "common.h" #include "rb.h" #include "lt.h" #include #include #include #include static int ak__heap_srch_cmp(void * __restrict key1, void * __restrict key2); static void ak__heap_srch_print(void * __restrict key); static char* ak__heap_strdup_def(const char * str); AK_HIDE void ak_heap_moveh_chld(AkHeap * __restrict heap, AkHeap * __restrict newheap, AkHeapNode * __restrict heapNode); static void * ak__emptystr = ""; AkHeapAllocator ak__allocator = { .malloc = malloc, .calloc = calloc, .realloc = realloc, .free = free, #ifndef _WIN32 .memalign = posix_memalign, #endif .strdup = ak__heap_strdup_def }; static AkHeap ak__heap = { .allocator = &ak__allocator, .srchctx = NULL, .root = NULL, .trash = NULL, .flags = 0 }; static RBTree *ak__heap_sub = NULL; static int ak__heap_srch_cmp(void * __restrict key1, void * __restrict key2) { return strcmp((char *)key1, (char *)key2); } static void ak__heap_srch_print(void * __restrict key) { printf("\t'%s'\n", (const char *)key); } static char* ak__heap_strdup_def(const char * str) { void *memptr; size_t memsize; memsize = strlen(str); memptr = ak__heap.allocator->malloc(memsize + 1); memcpy(memptr, str, memsize); /* NULL */ memset((char *)memptr + memsize, '\0', 1); return memptr; } AK_EXPORT char* ak_heap_strdup(AkHeap * __restrict heap, void * __restrict parent, const char * str) { void *memptr; size_t memsize; if (!str) return NULL; memsize = strlen(str); memptr = ak_heap_alloc(heap, parent, memsize + 1); memcpy(memptr, str, memsize); /* NULL */ memset((char *)memptr + memsize, '\0', 1); return memptr; } AK_EXPORT char* ak_heap_strndup(AkHeap * __restrict heap, void * __restrict parent, const char * str, size_t size) { void *memptr; memptr = ak_heap_alloc(heap, parent, size + 1); memcpy(memptr, str, size); /* NULL */ memset((char *)memptr + size, '\0', 1); return memptr; } AK_EXPORT AkHeapAllocator * ak_heap_allocator(AkHeap * __restrict heap) { return heap->allocator; } AK_EXPORT AkHeap * ak_heap_default(void) { return &ak__heap; } AK_EXPORT AkHeap * ak_heap_getheap(void * __restrict memptr) { AkHeapNode *heapNode; heapNode = ak__alignof(memptr); return ak_heap_lt_find(heapNode->heapid); } AK_EXPORT AkHeap * ak_heap_new(AkHeapAllocator *allocator, AkHeapSrchCmpFn cmp, AkHeapSrchPrintFn print) { AkHeapAllocator *alc; AkHeap *heap; alc = allocator ? allocator : &ak__allocator; heap = alc->malloc(sizeof(*heap)); assert(heap && "malloc failed"); heap->flags = AK_HEAP_FLAGS_DYNAMIC; ak_heap_init(heap, allocator, cmp, print); return heap; } AK_EXPORT void ak_heap_attach(AkHeap * __restrict parent, AkHeap * __restrict chld) { chld->next = parent->chld; parent->chld = chld; } AK_EXPORT void ak_heap_dettach(AkHeap * __restrict parent, AkHeap * __restrict chld) { AkHeap *heap; heap = parent->chld; if (heap == chld) { parent->chld = chld->next; chld->next = NULL; return; } while (heap) { if (heap->next == chld) { heap->next = chld->next; chld->next = NULL; break; } heap = heap->next; } } AK_EXPORT void ak_heap_setdata(AkHeap * __restrict heap, void * __restrict memptr) { heap->data = memptr; } AK_EXPORT void* ak_heap_data(AkHeap * __restrict heap) { return heap->data; } AK_EXPORT void ak_heap_init(AkHeap * __restrict heap, AkHeapAllocator * __restrict allocator, AkHeapSrchCmpFn cmp, AkHeapSrchPrintFn print) { AkHeapAllocator *alc; AkHeapSrchCtx *srchctx; AkHeapNode *rootNode, *nullNode; AkHeapNodeExt *rootNodeExt, *nullNodeExt; AkHeapSrchNode *rootSrchNode, *nullSrchNode; if (heap->flags & AK_HEAP_FLAGS_INITIALIZED) return; alc = allocator ? allocator : &ak__allocator; srchctx = alc->malloc(sizeof(*srchctx)); rootNode = alc->calloc(1, ak__heapnd_sz); nullNode = alc->calloc(1, ak__heapnd_sz); rootNodeExt = alc->calloc(1, sizeof(*rootNodeExt) + sizeof(AkHeapSrchNode)); nullNodeExt = alc->calloc(1, sizeof(*nullNodeExt) + sizeof(AkHeapSrchNode)); assert(srchctx && rootNode && nullNode && rootNodeExt && nullNodeExt && "malloc failed"); rootNode->chld = rootNodeExt; rootNodeExt->node = rootNode; rootSrchNode = (AkHeapSrchNode *)rootNodeExt->data; nullNode->chld = nullNodeExt; nullNodeExt->node = nullNode; nullSrchNode = (AkHeapSrchNode *)nullNodeExt->data; nullSrchNode->key = ak__emptystr; nullSrchNode->chld[AK__BST_LEFT] = nullSrchNode; nullSrchNode->chld[AK__BST_RIGHT] = nullSrchNode; rootSrchNode->key = ak__emptystr; rootSrchNode->chld[AK__BST_LEFT] = nullSrchNode; rootSrchNode->chld[AK__BST_RIGHT] = nullSrchNode; rootNode->flags = (AK_HEAP_NODE_FLAGS_EXT | AK_HEAP_NODE_FLAGS_SRCH); nullNode->flags = (AK_HEAP_NODE_FLAGS_EXT | AK_HEAP_NODE_FLAGS_SRCH); srchctx->cmp = cmp ? cmp : ak__heap_srch_cmp; srchctx->print = print ? print : ak__heap_srch_print; srchctx->root = rootSrchNode; srchctx->nullNode = nullSrchNode; heap->chld = NULL; heap->next = NULL; heap->root = NULL; heap->trash = NULL; heap->data = NULL; heap->idheap = NULL; heap->heapid = 0; heap->allocator = alc; heap->srchctx = srchctx; heap->flags |= AK_HEAP_FLAGS_INITIALIZED; if (heap != &ak__heap) ak_heap_lt_insert(heap); } AK_EXPORT void ak_heap_destroy(AkHeap * __restrict heap) { AkHeapAllocator *alc; AkHeapNode *rootNode, *nullNode; AkHeap *it, *toDestroy; if (!(heap->flags & AK_HEAP_FLAGS_INITIALIZED)) return; alc = heap->allocator; /* first destroy all attached heaps */ if (heap->chld) { it = heap->chld; do { toDestroy = it; it = it->next; ak_heap_destroy(toDestroy); } while (it); } ak_heap_cleanup(heap); rootNode = AK__HEAPNODE(heap->srchctx->root); nullNode = AK__HEAPNODE(heap->srchctx->nullNode); alc->free(rootNode->chld); alc->free(nullNode->chld); alc->free(rootNode); alc->free(nullNode); alc->free(heap->srchctx); heap->data = NULL; ak_heap_lt_remove(heap->heapid); if (heap->flags & AK_HEAP_FLAGS_DYNAMIC && heap != &ak__heap) alc->free(heap); } AK_EXPORT void* ak_heap_alloc(AkHeap * __restrict heap, void * __restrict parent, size_t size) { AkHeapNode *currNode; AkHeapNode *parentNode; size_t memsize; assert((!parent || heap->heapid == ak__alignof(parent)->heapid) && "parent and child mem must use same heap"); memsize = ak__heapnd_sz + size; memsize = ak__align(memsize); currNode = heap->allocator->malloc(memsize); assert(currNode && "malloc failed"); currNode->flags = 0; currNode->typeid = 0; currNode->chld = NULL; currNode->heapid = heap->heapid; if (parent) { AkHeapNode *chldNode; parentNode = ak__alignof(parent); chldNode = ak_heap_chld(parentNode); ak_heap_chld_set(parentNode, currNode); if (chldNode) chldNode->prev = currNode; currNode->next = chldNode; } else { if (heap->root) heap->root->prev = currNode; currNode->next = heap->root; currNode->prev = NULL; heap->root = currNode; } return ak__alignas(currNode); } AK_EXPORT void* ak_heap_calloc(AkHeap * __restrict heap, void * __restrict parent, size_t size) { void *memptr; memptr = ak_heap_alloc(heap, parent, size); memset(memptr, '\0', size); return memptr; } AK_EXPORT void* ak_heap_realloc(AkHeap * __restrict heap, void * __restrict parent, void * __restrict memptr, size_t newsize) { AkHeapNode *oldNode; AkHeapNode *newNode; AkHeapNode *chld; if (!memptr) return ak_heap_alloc(heap, parent, newsize); oldNode = ak__alignof(memptr); if (newsize == 0) { ak_heap_free(heap, oldNode); return NULL; } newsize = ak__heapnd_sz + newsize; newsize = ak__align(newsize); newNode = heap->allocator->realloc(oldNode, newsize); assert(newNode && "realloc failed"); if (heap->root == oldNode) heap->root = newNode; if (heap->trash == oldNode) heap->trash = newNode; chld = newNode->chld; if (chld) { if (newNode->flags & AK_HEAP_NODE_FLAGS_EXT) { AkHeapNodeExt *exnode; exnode = newNode->chld; exnode->node = newNode; if (exnode->chld) exnode->chld->prev = newNode; } else { chld->prev = newNode; } } if (newNode->next) newNode->next->prev = newNode; if (newNode->prev) { chld = ak_heap_chld(newNode->prev); if (chld == oldNode) ak_heap_chld_set(newNode->prev, newNode); else newNode->prev->next = newNode; } return ak__alignas(newNode); } AK_EXPORT void * ak_heap_chld(AkHeapNode *heapNode) { if (heapNode->flags & AK_HEAP_NODE_FLAGS_EXT) return ((AkHeapNodeExt *)heapNode->chld)->chld; return heapNode->chld; } AK_EXPORT void ak_heap_chld_set(AkHeapNode * __restrict heapNode, AkHeapNode * __restrict chldNode) { if (heapNode->flags & AK_HEAP_NODE_FLAGS_EXT) ((AkHeapNodeExt *)heapNode->chld)->chld = chldNode; else heapNode->chld = chldNode; if (chldNode) chldNode->prev = heapNode; } AK_EXPORT AkHeapNode * ak_heap_parent(AkHeapNode *heapNode) { AkHeapNode *p, *it; p = NULL; it = heapNode; while (it->prev) { /* we found near parent */ if (ak_heap_chld(it->prev) == it) { p = it->prev; break; } it = it->prev; } return p; } AK_EXPORT void ak_heap_setp(AkHeapNode * __restrict heapNode, AkHeapNode * __restrict newParent) { AkHeap *oldheap, *newheap; oldheap = ak_heap_lt_find(heapNode->heapid); newheap = ak_heap_lt_find(newParent->heapid); if (heapNode->prev) { if (ak_heap_chld(heapNode->prev) == heapNode) ak_heap_chld_set(heapNode->prev, heapNode->next); else heapNode->prev->next = heapNode->next; if (heapNode->next) heapNode->next->prev = heapNode->prev; heapNode->prev = NULL; } else if (heapNode == oldheap->root) { /* root->prev = NULL */ oldheap->root = heapNode->next; if (heapNode->next) heapNode->next->prev = NULL; } heapNode->next = NULL; if (heapNode == oldheap->trash) oldheap->trash = heapNode->next; /* move all ids to new heap (if it is different) */ if (newParent->heapid != heapNode->heapid) ak_heap_moveh(heapNode, newheap); heapNode->next = ak_heap_chld(newParent); ak_heap_chld_set(newParent, heapNode); if (heapNode->next) heapNode->next->prev = heapNode; } AK_HIDE void ak_heap_moveh_chld(AkHeap * __restrict heap, AkHeap * __restrict newheap, AkHeapNode * __restrict heapNode) { do { AkHeapNode *chld; if (heapNode->flags & AK_HEAP_NODE_FLAGS_SRCH) { AkHeapSrchNode *srchNode; srchNode = (AkHeapSrchNode *)((AkHeapNodeExt *)heapNode->chld)->data; ak_heap_rb_remove(heap->srchctx, srchNode); ak_heap_rb_insert(newheap->srchctx, srchNode); } heapNode->heapid = newheap->heapid; chld = ak_heap_chld(heapNode); if (chld) ak_heap_moveh_chld(heap, newheap, chld); heapNode = heapNode->next; } while (heapNode); } AK_EXPORT void ak_heap_moveh(AkHeapNode * __restrict heapNode, AkHeap * __restrict newheap) { AkHeapNode *chld; AkHeap *heap; heap = ak_heap_lt_find(heapNode->heapid); if (heapNode->flags & AK_HEAP_NODE_FLAGS_SRCH) { AkHeapSrchNode *srchNode; srchNode = (AkHeapSrchNode *)((AkHeapNodeExt *)heapNode->chld)->data; ak_heap_rb_remove(heap->srchctx, srchNode); ak_heap_rb_insert(newheap->srchctx, srchNode); } heapNode->heapid = newheap->heapid; chld = ak_heap_chld(heapNode); if (chld) ak_heap_moveh_chld(heap, newheap, chld); } AK_EXPORT void ak_heap_setpm(void * __restrict memptr, void * __restrict newparent) { ak_heap_setp(ak__alignof(memptr), ak__alignof(newparent)); } AK_HIDE void ak_freeh(AkHeapNode * __restrict heapNode) { if (heapNode->flags & AK_HEAP_NODE_FLAGS_HEAP_CHLD) { AkHeap *attachedHeap; attachedHeap = ak_attachedHeap(ak__alignas(heapNode)); if (attachedHeap) { ak_heap_destroy(attachedHeap); ak_setAttachedHeap(ak__alignas(heapNode), NULL); } } } AK_EXPORT void ak_heap_free(AkHeap * __restrict heap, AkHeapNode * __restrict heapNode) { AkHeapAllocator * __restrict alc; alc = heap->allocator; if (heapNode->flags & AK_HEAP_NODE_FLAGS_EXT) ak_heap_ext_free(heap, heapNode); /* free attached heap */ if (heapNode->flags & AK_HEAP_NODE_FLAGS_HEAP_CHLD) ak_freeh(heapNode); /* free all child nodes */ if (heapNode->chld) { AkHeapNode *toFree; AkHeapNode *nextFree; toFree = heapNode->chld; do { nextFree = toFree->next; if (toFree->flags & AK_HEAP_NODE_FLAGS_EXT) ak_heap_ext_free(heap, toFree); /* free attached heap */ if (toFree->flags & AK_HEAP_NODE_FLAGS_HEAP_CHLD) ak_freeh(toFree); if (toFree->chld) { if (heap->trash) { AkHeapNode *lastNode; lastNode = toFree->chld; while (lastNode->next) lastNode = lastNode->next; lastNode->next = heap->trash; } heap->trash = toFree->chld; } alc->free(toFree); toFree = nextFree; /* empty trash */ if (!toFree && heap->trash) { toFree = heap->trash; heap->trash = NULL; } } while (toFree); } if (heapNode->prev) { if (ak_heap_chld(heapNode->prev) == heapNode) ak_heap_chld_set(heapNode->prev, heapNode->next); else heapNode->prev->next = heapNode->next; } /* heap->root == heapNode we know that root->prev always is NULL */ else { heap->root = heapNode->next; } if (heapNode->next) heapNode->next->prev = heapNode->prev; alc->free(heapNode); } AK_EXPORT void ak_heap_cleanup(AkHeap * __restrict heap) { while (heap->root) ak_heap_free(heap, heap->root); } AK_EXPORT void * ak_heap_getId(AkHeap * __restrict heap, AkHeapNode * __restrict heapNode) { AkHeapSrchNode *snode; if (!(heapNode->flags & AK_HEAP_NODE_FLAGS_SRCH)) return NULL; snode = (AkHeapSrchNode *)((AkHeapNodeExt *)heapNode->chld)->data; return snode->key; AK__UNUSED(heap); } AK_EXPORT void ak_heap_setId(AkHeap * __restrict heap, AkHeapNode * __restrict heapNode, void * __restrict memId) { AkHeapSrchNode *snode; if (!memId) { ak_heap_ext_rm(heap, heapNode, AK_HEAP_NODE_FLAGS_SRCH); return; } snode = ak_heap_ext_add(heap, heapNode, AK_HEAP_NODE_FLAGS_SRCH); if (snode->key) ak_heap_rb_remove(heap->srchctx, snode); heapNode->flags |= AK_HEAP_NODE_FLAGS_RED; snode->chld[AK__BST_LEFT] = heap->srchctx->nullNode; snode->chld[AK__BST_RIGHT] = heap->srchctx->nullNode; snode->key = memId; ak_heap_rb_insert(heap->srchctx, snode); } AK_EXPORT AkResult ak_heap_getNodeById(AkHeap * __restrict heap, void * __restrict memId, AkHeapNode ** __restrict dest) { AkHeapSrchNode *srchNode; srchNode = ak_heap_rb_find(heap->srchctx, memId); if (!srchNode || srchNode == heap->srchctx->nullNode) { *dest = NULL; return AK_EFOUND; } if ((*dest = AK__HEAPNODE(srchNode))) return AK_OK; return AK_EFOUND; } AK_EXPORT AkResult ak_heap_getNodeByURL(AkHeap * __restrict heap, struct AkURL * __restrict url, AkHeapNode ** __restrict dest) { if (url->doc) return ak_heap_getNodeById(heap, (char *)url->url + 1, dest); return AK_EFOUND; } AK_EXPORT AkResult ak_heap_getMemById(AkHeap * __restrict heap, void * __restrict memId, void ** __restrict dest) { AkHeapSrchNode *srchNode; srchNode = ak_heap_rb_find(heap->srchctx, memId); if (!srchNode || srchNode == heap->srchctx->nullNode) { *dest = NULL; return AK_EFOUND; } if ((*dest = ak__alignas(AK__HEAPNODE(srchNode)))) return AK_OK; return AK_EFOUND; } AK_EXPORT int ak_heap_refc(AkHeapNode * __restrict heapNode) { int *refc; refc = ak_heap_ext_get(heapNode, AK_HEAP_NODE_FLAGS_REFC); if (!refc) return -1; return *refc; } AK_EXPORT int ak_heap_retain(AkHeapNode * __restrict heapNode) { int *refc; if (!(refc = ak_heap_ext_get(heapNode, AK_HEAP_NODE_FLAGS_REFC))) refc = ak_heap_ext_add(ak_heap_getheap(ak__alignas(heapNode)), heapNode, AK_HEAP_NODE_FLAGS_REFC); return ++(*refc); } AK_EXPORT void ak_heap_release(AkHeapNode * __restrict heapNode) { int *refc; refc = ak_heap_ext_get(heapNode, AK_HEAP_NODE_FLAGS_REFC); if (!refc || !(*refc)) goto fr; if (--(*refc) > 0) return; fr: ak_free(ak__alignas(heapNode)); } AK_EXPORT void ak_heap_printKeys(AkHeap * __restrict heap) { ak_heap_rb_print(heap->srchctx); } AK_EXPORT AkHeap* ak_attachedHeap(void * __restrict memptr) { return rb_find(ak__heap_sub, ak__alignof(memptr)); } AK_EXPORT void ak_setAttachedHeap(void * __restrict memptr, AkHeap * __restrict heap) { RBNode *found; AkHeapNode *heapNode; heapNode = ak__alignof(memptr); if (!heap) { rb_remove(ak__heap_sub, heapNode); heapNode->flags &= ~AK_HEAP_NODE_FLAGS_HEAP_CHLD; return; } found = rb_find_node(ak__heap_sub, heapNode); if (found) { found->val = heap; return; } rb_insert(ak__heap_sub, heapNode, heap); heapNode->flags |= AK_HEAP_NODE_FLAGS_HEAP_CHLD; } AK_EXPORT AkHeapAllocator * ak_mem_allocator(void) { return ak__heap.allocator; } AK_EXPORT void ak_mem_printKeys(void) { ak_heap_rb_print(ak__heap.srchctx); } AK_EXPORT void * ak_mem_getId(void * __restrict memptr) { AkHeap *heap; AkHeapNode *heapNode; heapNode = ak__alignof(memptr); if (heapNode->heapid == 0) heap = &ak__heap; else heap = ak_heap_lt_find(heapNode->heapid); return ak_heap_getId(heap, heapNode); } AK_EXPORT void ak_mem_setId(void * __restrict memptr, void * __restrict memId) { AkHeap *heap; AkHeapNode *heapNode; heapNode = ak__alignof(memptr); if (heapNode->heapid == 0) heap = &ak__heap; else heap = ak_heap_lt_find(heapNode->heapid); ak_heap_setId(heap, heapNode, memId); } AK_EXPORT AkResult ak_mem_getMemById(void * __restrict ctx, void * __restrict memId, void ** __restrict dest) { AkHeap *heap; AkHeapNode *heapNode; heapNode = ak__alignof(ctx); if (heapNode->heapid == 0) heap = &ak__heap; else heap = ak_heap_lt_find(heapNode->heapid); return ak_heap_getMemById(heap, memId, dest); } AK_EXPORT void ak_mem_setp(void * __restrict memptr, void * __restrict newparent) { ak_heap_setp(ak__alignof(memptr), ak__alignof(newparent)); } AK_EXPORT void * ak_mem_parent(void *mem) { AkHeapNode *hnode; if (!mem) return NULL; hnode = ak_heap_parent(ak__alignof(mem)); if (!hnode) return NULL; return ak__alignas(hnode); } AK_EXPORT void* ak_malloc(void * __restrict parent, size_t size) { return ak_heap_alloc(&ak__heap, parent, size); } AK_EXPORT void* ak_calloc(void * __restrict parent, size_t size) { void *memptr; memptr = ak_heap_alloc(&ak__heap, parent, size); memset(memptr, '\0', size); return memptr; } AK_EXPORT void* ak_realloc(void * __restrict parent, void * __restrict memptr, size_t newsize) { return ak_heap_realloc(&ak__heap, parent, memptr, newsize); } AK_EXPORT char* ak_strdup(void * __restrict parent, const char * __restrict str) { AkHeap *heap; void *memptr; size_t memsize; if (parent) { heap = ak_heap_getheap(parent); } else { heap = &ak__heap; } memsize = strlen(str); memptr = ak_heap_alloc(heap, parent, memsize + 1); memcpy(memptr, str, memsize); /* NULL */ memset((char *)memptr + memsize, '\0', 1); return memptr; } AK_EXPORT int ak_refc(void * __restrict mem) { return ak_heap_refc(ak__alignof(mem)); } AK_EXPORT int ak_retain(void * __restrict mem) { if (!mem) return 0; return ak_heap_retain(ak__alignof(mem)); } AK_EXPORT void ak_release(void * __restrict mem) { if (!mem) return; ak_heap_release(ak__alignof(mem)); } AK_EXPORT void ak_free(void * __restrict memptr) { AkHeap *heap; AkHeapNode *heapNode; if (!memptr) return; heap = &ak__heap; heapNode = ak__alignof(memptr); if (heapNode->heapid != heap->heapid) heap = ak_heap_lt_find(heapNode->heapid); if (!heap) return; /* free heap self instead of single free if this node attached to heap */ if (heap->data == memptr) ak_heap_destroy(heap); else ak_heap_free(heap, heapNode); } AK_EXPORT AkObject* ak_objAlloc(AkHeap * __restrict heap, void * __restrict memParent, size_t typeSize, AkEnum typeEnum, bool zeroed) { AkObject * obj; assert(typeSize > 0 && "invalid parameter value"); obj = ak_heap_alloc(heap, memParent, sizeof(*obj) + typeSize); obj->size = typeSize; obj->type = typeEnum; obj->next = NULL; obj->pData = (void *)((char *)obj + offsetof(AkObject, data)); if (zeroed) memset(obj->pData, '\0', typeSize); ak_setypeid(obj, AKT_OBJECT); return obj; } AK_EXPORT void* ak_userData(void * __restrict mem) { void *r; uintptr_t tmp; if (!mem) return NULL; /* Keep `r` as void* (not uintptr_t*) — the storage may be misaligned for uintptr_t, and UBSan flags the typed pointer even when the only read is a memcpy. memcpy on void* is alignment-agnostic. */ if ((r = ak_heap_ext_get(ak__alignof(mem), AK_HEAP_NODE_FLAGS_USR))) { memcpy(&tmp, r, sizeof(tmp)); return (void *)tmp; } return NULL; } AK_EXPORT void* ak_heap_setUserData(AkHeap * __restrict heap, void * __restrict mem, void * __restrict userData) { uintptr_t tmp; void *ext; if (!mem) return NULL; if (!(ext = ak_heap_ext_add(heap, ak__alignof(mem), AK_HEAP_NODE_FLAGS_USR))) return NULL; tmp = (uintptr_t)userData; memcpy(ext, &tmp, sizeof(void *)); return ext; } AK_EXPORT void* ak_setUserData(void * __restrict mem, void * __restrict userData) { AkHeap *heap; if (!mem || !(heap = ak_heap_getheap(mem))) return NULL; return ak_heap_setUserData(heap, mem, userData); } AK_EXPORT AkObject* ak_objFrom(void * __restrict memptr) { AkObject *obj; obj = (void *)((char *)memptr - offsetof(AkObject, data)); assert(obj->pData == memptr && "invalid cas to AkObject"); return obj; } void ak_mem_init(void) { ak__heap_sub = rb_newtree_ptr(); ak_heap_init(&ak__heap, NULL, NULL, NULL); ak_heap_lt_init(&ak__heap); ak_dsSetAllocator(ak__heap.allocator, ak__heap_sub->alc); } void ak_mem_deinit(void) { ak_heap_destroy(&ak__heap); ak_heap_lt_cleanup(); rb_destroy(ak__heap_sub); } ================================================ FILE: src/mem/mmap.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "common.h" #ifndef AK_WINAPI # include #else # define WIN32_LEAN_AND_MEAN # include # include #endif AK_EXPORT void* ak_mmap_rdonly(int fd, size_t size) { void *mapped; mapped = NULL; #ifndef AK_WINAPI mapped = mmap(0, size, PROT_READ, MAP_SHARED, fd, 0); if (!mapped || mapped == MAP_FAILED) return NULL; madvise(mapped, size, MADV_SEQUENTIAL); #else HANDLE hmap; if (!((hmap = CreateFileMapping((HANDLE)_get_osfhandle(fd), 0, PAGE_READONLY, 0, 0, 0)) && (mapped = MapViewOfFileEx(hmap, FILE_MAP_READ, 0, 0, size, 0)))) return NULL; CloseHandle(hmap); #endif return mapped; } AK_EXPORT void ak_unmap(void *file, size_t size) { #ifndef AK_WINAPI munmap(file, size); #else AK__UNUSED(size); UnmapViewOfFile(file); #endif } AK_EXPORT void ak_mmap_attach(void * __restrict obj, void * __restrict mapped, size_t sized) { AkHeap *heap; AkHeapNode *hnode; AkMemoryMapNode **mmapNode, *mmapNodeNew; heap = ak_heap_getheap(obj); hnode = ak__alignof(obj); mmapNode = (AkMemoryMapNode **)ak_heap_ext_add(heap, hnode, AK_HEAP_NODE_FLAGS_MMAP); if (mmapNode) { mmapNodeNew = ak_heap_calloc(heap, obj, sizeof(*mmapNodeNew)); mmapNodeNew->mapped = mapped; mmapNodeNew->sized = sized; if (*mmapNode) { mmapNodeNew->next = *mmapNode; (*mmapNode)->prev = mmapNodeNew; } *mmapNode = mmapNodeNew; } } AK_EXPORT void ak_unmmap_attached(void * __restrict obj) { AkHeapNode *hnode; AkMemoryMapNode **mmapNode, *it; void *tofree; hnode = ak__alignof(obj); if ((mmapNode = ak_heap_ext_get(hnode, AK_HEAP_NODE_FLAGS_MMAP)) && (it = *mmapNode)) { while (it) { ak_unmap(it->mapped, it->sized); tofree = it; it = it->next; ak_free(tofree); } *mmapNode = NULL; } } ================================================ FILE: src/mem/rb.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * --- * * Insertion and Deletion are Top-Down * * X: Current Node * P: Parent Node (Parent of X) * T: X's Sibling Node * G: Grand Parent Node (Parent of P) * Q: Great Parent Node (Parent of Grand Parent) * * Y: X's left child * Z: X's right child * * sX: side of X (if X is left then sX=0, right sX=1) * sP: side of P * sG: side of GD * sN: side of Next X (Side of Next Current Node) * * cX: color of X */ #include "rb.h" #include void ak_heap_rb_printNode(AkHeapSrchCtx * __restrict srchctx, AkHeapSrchNode * __restrict srchNode); /* simple assertion for rbtree source: Eternally Confuzzled */ int ak_heap_rb_assert(AkHeapSrchCtx * srchctx, AkHeapSrchNode * root) { int lh, rh; if (root == srchctx->nullNode) { return 1; } else { struct AkHeapSrchNode *ln = root->chld[0]; struct AkHeapSrchNode *rn = root->chld[1]; /* Consecutive red links */ if (AK__RB_ISRED(root)) { if (AK__RB_ISRED(ln) || AK__RB_ISRED(rn)) { puts("Red violation"); exit(0); return 0; } } lh = ak_heap_rb_assert(srchctx, ln); rh = ak_heap_rb_assert(srchctx, rn); /* Invalid binary search tree */ if ((ln != srchctx->nullNode && srchctx->cmp(ln->key, root->key) > 0) || (rn != srchctx->nullNode && srchctx->cmp(rn->key, root->key) < 0)) { puts("Binary tree violation"); exit(0); return 0; } /* Black height mismatch */ if (lh != 0 && rh != 0 && lh != rh) { puts("Black violation"); exit(0); return 0; } /* Only count black links */ if (lh != 0 && rh != 0) return AK__RB_ISRED(root) ? lh : lh + 1; return 0; } } void ak_heap_rb_insert(AkHeapSrchCtx * __restrict srchctx, AkHeapSrchNode * __restrict srchNode) { AkHeapSrchNode *X, *P, *G, *Q, *W; int sQ, sG, sP, sX; if (srchctx->root->chld[AK__BST_RIGHT] == srchctx->nullNode) { AK__RB_MKBLACK(srchNode); srchctx->root->chld[AK__BST_RIGHT] = srchNode; return; } sQ = sG = sP = sX = 1; W = P = G = Q = srchctx->root; X = P->chld[AK__BST_RIGHT]; /* Top-Down Insert */ do { /* main case : two children are red */ if (AK__RB_ISRED(X->chld[AK__BST_LEFT]) && AK__RB_ISRED(X->chld[AK__BST_RIGHT])) { /* case 1: P is black */ if (!AK__RB_ISRED(P)) { /* make X red */ AK__RB_MKRED(X); /* make two children black */ AK__RB_MKBLACK(X->chld[AK__BST_LEFT]); AK__RB_MKBLACK(X->chld[AK__BST_RIGHT]); } /* P is red */ else { AK__RB_MKRED(G); /* case 2: X and P are both left/right children */ if (sX == sP){ /* single rotation */ AK__RB_MKRED(X); AK__RB_MKBLACK(P); AK__RB_MKBLACK(X->chld[AK__BST_LEFT]); AK__RB_MKBLACK(X->chld[AK__BST_RIGHT]); Q->chld[sG] = P; G->chld[sP] = P->chld[!sP]; P->chld[!sP] = G; G = Q; Q = W; } /* case 3: X and P are opposide side */ else { AK__RB_MKBLACK(X); AK__RB_MKBLACK(X->chld[AK__BST_LEFT]); AK__RB_MKBLACK(X->chld[AK__BST_RIGHT]); Q->chld[sG] = X; P->chld[sX] = X->chld[sP]; G->chld[sP] = X->chld[sX]; X->chld[sP] = P; X->chld[sX] = G; G = W; P = Q; sX = sG; sP = sQ; } } } sQ = sG; sG = sP; sP = sX; sX = !(srchctx->cmp(srchNode->key, X->key) < 0); W = Q; Q = G; G = P; P = X; X = X->chld[sX]; } while (X != srchctx->nullNode); X = P->chld[sX] = srchNode; /* make current red */ AK__RB_MKRED(X); /* check for red violation, we know uncle is black */ if (AK__RB_ISRED(P)) { AK__RB_MKRED(G); /* double rotation */ if (sX != sP){ AK__RB_MKBLACK(X); Q->chld[sG] = X; P->chld[sX] = X->chld[!sX]; G->chld[sP] = X->chld[sX]; X->chld[!sX] = P; X->chld[sX] = G; } /* single rotation */ else { AK__RB_MKBLACK(P); G->chld[sP] = P->chld[!sP]; P->chld[!sP] = G; Q->chld[sG] = P; } } /* make root black */ AK__RB_MKBLACK(srchctx->root->chld[AK__BST_RIGHT]); } void ak_heap_rb_remove(AkHeapSrchCtx * __restrict srchctx, AkHeapSrchNode * __restrict srchNode) { AkHeapSrchNode *X, *P, *T, *G, *toDel, *toDelP; int sP, sX, cmpRet, sDel; int c2b; if (srchNode == srchctx->nullNode) return; sX = AK__BST_RIGHT; G = srchctx->root; P = G; X = P->chld[AK__BST_RIGHT]; toDel = srchctx->nullNode; toDelP = srchctx->nullNode; sDel = 0; /* step 1: examine the root */ if (AK__RB_ISBLACK(X->chld[AK__BST_LEFT]) && AK__RB_ISBLACK(X->chld[AK__BST_RIGHT])) { AK__RB_MKRED(X); c2b = 0; } else { /* step 2B */ c2b = 1; } goto l0; /* Top-Down Deletion */ do { /* case 2b continue: check new X */ if (c2b) { c2b = 0; /* if new X is red continue down again */ if (AK__RB_ISRED(X)) goto l0; G->chld[sP] = T; P->chld[!sX] = T->chld[sX]; T->chld[sX] = P; AK__RB_MKRED(P); AK__RB_MKBLACK(T); if (toDelP == G) { toDelP = T; sDel = sX; } G = T; T = P->chld[!sX]; sP = sX; /* if new X is black back to case 2 */ } /* case 2: X has two black children */ if (AK__RB_ISBLACK(X->chld[AK__BST_LEFT]) && AK__RB_ISBLACK(X->chld[AK__BST_RIGHT])) { /* case 1.a: T has two black children */ if (T != srchctx->nullNode && AK__RB_ISBLACK(T->chld[AK__BST_LEFT]) && AK__RB_ISBLACK(T->chld[AK__BST_RIGHT])) { /* color flip */ AK__RB_MKRED(X); AK__RB_MKRED(T); AK__RB_MKBLACK(P); } /* case 1.b: T's left child is red */ else if (AK__RB_ISRED(T->chld[sX])) { AkHeapSrchNode *R; R = T->chld[sX]; /* double rotate: rotate R around T, then R around P */ T->chld[sX] = R->chld[!sX]; P->chld[!sX] = R->chld[sX]; R->chld[sX] = P; R->chld[!sX] = T; G->chld[sP] = R; AK__RB_MKRED(X); AK__RB_MKBLACK(P); if (toDelP == G) { toDelP = R; sDel = sX; } } /* case 1.c: T's right child is red */ else if (AK__RB_ISRED(T->chld[!sX])) { AkHeapSrchNode *R; R = T->chld[!sX]; /* single rotate rotate T around P */ P->chld[!sX] = T->chld[sX]; T->chld[sX] = P; G->chld[sP] = T; AK__RB_MKRED(X); AK__RB_MKRED(T); AK__RB_MKBLACK(P); AK__RB_MKBLACK(R); if (toDelP == G) { toDelP = T; sDel = sX; } } } else { /* case 2b: X's one child is red, advence to next level */ c2b = 1; } l0: sP = sX; if (toDel != srchctx->nullNode) { sX = toDel->chld[AK__BST_RIGHT] == srchctx->nullNode; } else { cmpRet = srchctx->cmp(srchNode->key, X->key); if (cmpRet != 0) { sX = !(cmpRet < 0); } else { toDelP = P; toDel = X; sDel = sP; sX = toDel->chld[AK__BST_RIGHT] != srchctx->nullNode; } } G = P; P = X; X = P->chld[sX]; T = P->chld[!sX]; } while (X != srchctx->nullNode); /* make root black */ AK__RB_MKBLACK(srchctx->root->chld[AK__BST_RIGHT]); if (toDel == srchctx->nullNode) return; /* toDel has least one child */ if (P != toDel) { /* P is black, save black height */ if (c2b) AK__RB_MKBLACK(P->chld[!sX]); /* change color */ if (AK__RB_ISRED(toDel)) AK__RB_MKRED(P); else AK__RB_MKBLACK(P); /* replace P with its left child */ G->chld[sP] = P->chld[!sX]; /* replace X with in-order predecessor */ P->chld[AK__BST_RIGHT] = toDel->chld[AK__BST_RIGHT]; P->chld[AK__BST_LEFT] = toDel->chld[AK__BST_LEFT]; toDelP->chld[sDel] = P; } /* P is toDel; there is no child */ else { G->chld[sP] = srchctx->nullNode; } } AkHeapSrchNode * ak_heap_rb_find(AkHeapSrchCtx * __restrict srchctx, void * __restrict key) { AkHeapSrchNode *iter; iter = srchctx->root->chld[AK__BST_RIGHT]; while (iter != srchctx->nullNode) { int cmpRet; cmpRet = srchctx->cmp(iter->key, key); if (cmpRet == 0) break; iter = iter->chld[cmpRet < 0]; } return iter; } int ak_heap_rb_parent(AkHeapSrchCtx * __restrict srchctx, void * __restrict key, AkHeapSrchNode ** dest) { AkHeapSrchNode *iter, *parent; int side, cmpRet; side = AK__BST_RIGHT; iter = srchctx->root->chld[side]; parent = srchctx->root; cmpRet = -1; while (iter != srchctx->nullNode) { cmpRet = srchctx->cmp(iter->key, key); if (cmpRet == 0) break; side = cmpRet < 0; parent = iter; iter = iter->chld[side]; } if (cmpRet != 0) *dest = NULL; else *dest = parent; return side; } void ak_heap_rb_printNode(AkHeapSrchCtx * __restrict srchctx, AkHeapSrchNode * __restrict srchNode) { if(srchNode != srchctx->nullNode) { ak_heap_rb_printNode(srchctx, srchNode->chld[AK__BST_LEFT]); srchctx->print(srchNode->key); ak_heap_rb_printNode(srchctx, srchNode->chld[AK__BST_RIGHT]); } } void ak_heap_rb_print(AkHeapSrchCtx * __restrict srchctx) { printf("\nAssetKit Memory Id Dump:\n"); printf("------------------------\n"); if(srchctx->root->chld[AK__BST_RIGHT] == srchctx->nullNode) printf("Empty tree\n"); else ak_heap_rb_printNode(srchctx, srchctx->root->chld[AK__BST_RIGHT]); printf("------------------------\n"); } ================================================ FILE: src/mem/rb.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_memory_redblack_h #define ak_memory_redblack_h #include "common.h" #define AK__RB_ISBLACK(X) !((AK__HEAPNODE(X)->flags & AK_HEAP_NODE_FLAGS_RED)) #define AK__RB_ISRED(X) (AK__HEAPNODE(X)->flags & AK_HEAP_NODE_FLAGS_RED) #define AK__RB_MKRED(X) AK__HEAPNODE(X)->flags |= AK_HEAP_NODE_FLAGS_RED #define AK__RB_MKBLACK(X) AK__HEAPNODE(X)->flags &= ~AK_HEAP_NODE_FLAGS_RED void ak_heap_rb_insert(AkHeapSrchCtx * __restrict srchctx, AkHeapSrchNode * __restrict srchNode); void ak_heap_rb_remove(AkHeapSrchCtx * __restrict srchctx, AkHeapSrchNode * __restrict srchNode); AkHeapSrchNode * ak_heap_rb_find(AkHeapSrchCtx * __restrict srchctx, void * __restrict key); int ak_heap_rb_parent(AkHeapSrchCtx * __restrict srchctx, void * __restrict key, AkHeapSrchNode ** dest); void ak_heap_rb_print(AkHeapSrchCtx * __restrict srchctx); int ak_heap_rb_assert(AkHeapSrchCtx * srchctx, AkHeapSrchNode * root); #endif /* ak_memory_redblack_h */ ================================================ FILE: src/mesh/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/mesh/duplicator.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "index.h" #include "edit_common.h" #include AK_EXPORT AkDuplicator* ak_meshDuplicatorForIndices(AkMesh * __restrict mesh, AkMeshPrimitive * __restrict prim) { AkHeap *heap; AkDoc *doc; AkObject *meshobj; AkDuplicator *dupl; AkDuplicatorRange *dupr; AkUIntArray *dupc, *ind, *newind, *dupcsum; uint32_t *it, *it2, *posflgs, *inp; AkAccessor *posAcc; uint8_t *flg; size_t count, ccount, icount, chk_start, chk_end, inpsz, vertc, i, j; uint32_t chk, iter, st, vo, posno, idxp; if (!prim->pos || !(posAcc = prim->pos->accessor)) return NULL; vertc = posAcc->count; meshobj = ak_objFrom(mesh); heap = ak_heap_getheap(meshobj); doc = ak_heap_data(heap); if ((dupl = rb_find(doc->reserved, prim))) { rb_remove(doc->reserved, prim); ak_free(dupl); /* or cache maybe if mesh is not edited ? */ } dupl = ak_heap_calloc(heap, NULL, sizeof(*dupl)); /* TODO: cache this for multiple primitives */ dupc = ak_heap_calloc(heap, dupl, sizeof(AkUIntArray) + sizeof(AkUInt) * vertc * 3); dupc->count = posAcc->count; st = prim->indexStride; vo = prim->pos->offset; ind = prim->indices; icount = (uint32_t)ind->count / st; newind = ak_meshIndicesArrayFor(mesh, prim, true); chk_end = icount; it = ind->items; it2 = newind->items; inpsz = sizeof(AkUInt) * st; flg = ak_heap_calloc(heap, dupl, sizeof(uint8_t) * icount); posflgs = ak_heap_calloc(heap, dupl, sizeof(AkUInt) * vertc * (st + 1)); chk_start = ccount = count = posno = 0; iter = chk = 1; while (ccount < icount) { /* nothing to check */ if (chk_start >= chk_end) break; j = chk_start; i = j * st; for (; j < chk_end; i += st, j++) { if (flg[j] == chk) continue; idxp = it[i + vo]; inp = posflgs + idxp * (st + 1); if (inp[0] < iter) { /* skip first squence */ if (iter > 1) { dupc->items[3 * idxp + 1]++; count++; } else { dupc->items[3 * idxp] = posno++; dupc->items[3 * idxp + 2] = idxp + 1; } inp[0] = iter; memcpy(&inp[1], &it[i], inpsz); } else if (memcmp(&it[i], &inp[1], inpsz) != 0) { it2[j]++; continue; } ccount++; flg[j] = chk; /* shrink the check range for next iter */ if (j == chk_start) chk_start++; else if (j == chk_end) chk_end--; } iter++; } dupcsum = ak_heap_calloc(heap, dupc, sizeof(AkUIntArray) + sizeof(AkUInt) * (posno + 1)); dupcsum->count = posno; for (i = 0; i < dupc->count; i++) { uint32_t pno, d; if (dupc->items[3 * i + 2] == 0) continue; pno = dupc->items[i * 3]; d = dupc->items[i * 3 + 1]; dupcsum->items[pno + 1] = d; } for (i = 1; i < dupcsum->count; i++) dupcsum->items[i] += dupcsum->items[i - 1]; dupr = ak_heap_alloc(heap, dupl, sizeof(*dupr)); dupr->dupc = dupc; dupr->startIndex = 0; dupr->endIndex = posAcc->count; dupr->next = NULL; dupr->dupcsum = dupcsum; dupl->range = dupr; dupl->dupCount = count; dupl->bufCount = posno; ak_free(flg); ak_free(posflgs); rb_insert(doc->reserved, prim, dupl); return dupl; } AK_EXPORT void ak_meshFixIndexBuffer(AkMesh * __restrict mesh, AkMeshPrimitive * __restrict prim, AkDuplicator * __restrict duplicator) { AkDuplicatorRange *dupr; AkUIntArray *dupc, *dupcsum, *newind; AkUInt *it, *it2; uint32_t i, j, c, st, vo, idxp, nidxp; dupr = duplicator->range; dupc = dupr->dupc; dupcsum = dupr->dupcsum; newind = ak_meshIndicesArrayFor(mesh, prim, true); it = prim->indices->items; it2 = newind->items; st = prim->indexStride; vo = prim->pos->offset; c = (uint32_t)prim->indices->count; if (duplicator->dupCount > 0) { for (i = j = 0; i < c; i += st, j++) { idxp = it[i + vo]; nidxp = dupc->items[idxp * 3]; it2[j] = it2[j] + nidxp + dupcsum->items[nidxp]; } } else { for (i = j = 0; i < c; i += st, j++) { idxp = it[i + vo]; nidxp = dupc->items[idxp * 3]; it2[j] = nidxp; } } } ================================================ FILE: src/mesh/edit.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "../skin/fix.h" #include "edit_common.h" const char* ak_mesh_edit_assert1 = "you must call ak_meshBeginEdit* before this op"; AK_EXPORT void ak_meshBeginEdit(AkMesh * __restrict mesh) { ak_meshBeginEditA(mesh, AK_GEOM_EDIT_FLAG_ARRAYS | AK_GEOM_EDIT_FLAG_INDICES); } AK_EXPORT void ak_meshBeginEditA(AkMesh * __restrict mesh, AkGeometryEditFlags flags) { AkHeap *heap; AkObject *meshobj; AkMeshEditHelper *edith; edith = mesh->edith; meshobj = ak_objFrom(mesh); heap = ak_heap_getheap(meshobj); if (edith && ak_retain(edith) > 1) return; if (!edith) { mesh->edith = edith = ak_heap_calloc(heap, ak_objFrom(mesh), sizeof(*edith)); ak_heap_ext_add(heap, ak__alignof(edith), AK_HEAP_NODE_FLAGS_REFC); } if ((flags & AK_GEOM_EDIT_FLAG_ARRAYS) && !edith->buffers) { edith->buffers = rb_newtree_ptr(); edith->inputBufferMap = ak_map_new(ak_cmp_ptr); ak_dsSetAllocator(heap->allocator, edith->buffers->alc); edith->buffers->onFreeNode = ak_meshFreeRsvBuff; edith->flags |= AK_GEOM_EDIT_FLAG_ARRAYS; } if ((flags & AK_GEOM_EDIT_FLAG_INDICES) && !edith->indices) { edith->indices = rb_newtree_ptr(); edith->flags |= AK_GEOM_EDIT_FLAG_INDICES; ak_dsSetAllocator(heap->allocator, edith->indices->alc); } ak_meshReIndexInputs(mesh); ak_retain(edith); } AK_EXPORT void ak_meshEndEdit(AkMesh * __restrict mesh) { AkMeshEditHelper *edith; edith = mesh->edith; if (!edith) return; if (ak_refc(edith) > 1) { ak_release(edith); return; } /* fix skin weights */ ak_skinFixWeights(mesh); /* finish edit */ ak_meshFillBuffers(mesh); ak_moveIndices(mesh); ak_meshMoveBuffers(mesh); if (edith->buffers) rb_destroy(edith->buffers); if (edith->indices) rb_destroy(edith->indices); if (edith->inputBufferMap) ak_map_destroy(edith->inputBufferMap); ak_release(edith); mesh->edith = NULL; } ================================================ FILE: src/mesh/edit_buff.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "../accessor.h" #include "../id.h" #include #include "edit_common.h" extern const char* ak_mesh_edit_assert1; void ak_meshFreeRsvBuff(RBTree *tree, RBNode *node) { AkSourceBuffState *buffstate; if (node == tree->nullNode) return; buffstate = node->val; ak_free(buffstate); } AK_EXPORT AkSourceBuffState* ak_meshReserveBuffer(AkMesh * __restrict mesh, void * __restrict buffid, size_t itemSize, uint32_t stride, size_t acc_count) { AkHeap *heap; AkSourceBuffState *buffstate; AkBuffer *buff; AkMeshEditHelper *edith; AkObject *meshobj; size_t newsize, count; edith = mesh->edith; assert(edith && ak_mesh_edit_assert1); meshobj = ak_objFrom(mesh); heap = ak_heap_getheap(meshobj); count = acc_count * stride; if (!(edith->flags & AK_GEOM_EDIT_FLAG_ARRAYS) || !edith->buffers) { edith->buffers = rb_newtree_str(); edith->flags |= AK_GEOM_EDIT_FLAG_ARRAYS; ak_dsSetAllocator(heap->allocator, edith->buffers->alc); } buffstate = rb_find(edith->buffers, buffid); newsize = itemSize * count; if (!buffstate) { buffstate = ak_heap_calloc(heap, meshobj, sizeof(*buffstate)); buff = ak_heap_calloc(heap, meshobj, sizeof(*buff)); buff->length = newsize; buff->data = ak_heap_calloc(heap, buff, newsize); buffstate->buff = buff; buffstate->count = count; buffstate->stride = stride; rb_insert(edith->buffers, buffid, buffstate); return buffstate; } buff = buffstate->buff; if (buff->length < newsize) { buff->data = ak_heap_realloc(heap, meshobj, buff->data, newsize); buff->length = newsize; } return buffstate; } AK_EXPORT void ak_meshReserveBufferForInput(AkMesh * __restrict mesh, AkInput * __restrict input, size_t count) { AkHeap *heap; AkObject *meshobj; AkMeshEditHelper *edith; AkSourceEditHelper *srch; AkSourceBuffState *buffstate; AkAccessor *acci, *newacc; AkBuffer *buffi; void *buffid; meshobj = ak_objFrom(mesh); heap = ak_heap_getheap(meshobj); edith = mesh->edith; assert(edith && ak_mesh_edit_assert1); if (!(acci = input->accessor) || !acci->buffer) return; /* generate new accesor for input */ newacc = ak_accessor_dup(acci); newacc->count = (uint32_t)count; buffid = input; buffstate = ak_meshReserveBuffer(mesh, buffid, acci->bytesPerComponent, acci->componentCount, count); buffi = buffstate->buff; newacc->byteOffset = 0; /* New buffer is tightly-packed for this input alone (one accessor → one buffer). ak_accessor_dup memcpy'd the SOURCE byteStride which is wrong here when the source was interleaved (stride > fillByteSize): ak_meshFillBuffers would then write at `newByteSt * newidx` past the buffer end. Reset to fillByteSize so writes match the layout we allocated for. */ newacc->byteStride = newacc->fillByteSize; srch = ak_heap_calloc(heap, meshobj, sizeof(*srch)); srch->oldsource = acci; srch->source = newacc; newacc->buffer = buffi; newacc->byteLength = newacc->count * newacc->fillByteSize; ak_heap_setpm(newacc, srch->source); ak_map_add(edith->inputBufferMap, srch, input); } AK_EXPORT void ak_meshReserveBuffers(AkMesh * __restrict mesh, AkMeshPrimitive * __restrict prim, size_t count) { AkInput *input; input = prim->input; while (input) { ak_meshReserveBufferForInput(mesh, input, count); input = input->next; } } AK_EXPORT AkSourceEditHelper* ak_meshSourceEditHelper(AkMesh * __restrict mesh, AkInput * __restrict input) { AkMeshEditHelper *edith; AkSourceEditHelper *srch; edith = mesh->edith; assert(edith && ak_mesh_edit_assert1); srch = (AkSourceEditHelper *)ak_map_find(edith->inputBufferMap, input); /* use old source as new */ if (!srch) { /* TODO: */ } return srch; } AK_EXPORT void ak_meshMoveBuffers(AkMesh * __restrict mesh) { AkHeap *mapHeap; AkMeshEditHelper *edith; AkSourceEditHelper *srch; AkMapItem *mi; AkInput *input; AkMeshPrimitive *prim; edith = mesh->edith; mapHeap = edith->inputBufferMap->heap; mi = edith->inputBufferMap->root; while (mi) { input = ak_heap_getId(mapHeap, ak__alignof(mi)); srch = (AkSourceEditHelper *)mi->data; prim = ak_mem_parent(input); /* TODO */ // ak_release(input->accessor); ak_release(srch->oldsource); ak_retain(srch->source); input->accessor = srch->source; if (input->semantic == AK_INPUT_POSITION) prim->pos = input; mi = mi->next; } } ================================================ FILE: src/mesh/edit_buff_fixup.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include extern const char* ak_mesh_edit_assert1; AK_EXPORT AkResult ak_meshFillBuffers(AkMesh * __restrict mesh) { AkMeshEditHelper *edith; AkInput *input; AkMeshPrimitive *primi; AkAccessor *acc, *newacc; AkUIntArray *ind1, *ind2; AkUInt *ind1_it, *ind2_it; AkBuffer *oldbuff, *newbuff; AkSourceBuffState *buffstate; AkSourceEditHelper *srch; char *olditms, *newitms; size_t icount, i; AkUInt oldidx, newidx, oldByteSt, newByteSt, fillSize, indxSt, inpOff; edith = mesh->edith; primi = mesh->primitive; /* per-primitive inputs */ while (primi) { ind1 = primi->indices; ind2 = ak_meshIndicesArrayFor(mesh, primi, false); /* same index buff */ if (!ind1 || ind1 == ind2) { primi = primi->next; continue; } ind1_it = ind1->items; ind2_it = ind2->items; input = primi->input; while (input) { if (input->semantic == AK_INPUT_POSITION || !(acc = input->accessor) || !(oldbuff = acc->buffer)) goto cont; /* copy buff to mesh */ if ((buffstate = rb_find(edith->buffers, input))) { srch = ak_meshSourceEditHelper(mesh, input); newbuff = buffstate->buff; newacc = srch->source; oldByteSt = (AkUInt)acc->byteStride; newByteSt = (AkUInt)newacc->byteStride; fillSize = (AkUInt)acc->fillByteSize; assert(newacc && "accessor is needed!"); inpOff = input->offset; indxSt = primi->indexStride; icount = primi->indices->count / indxSt; newitms = (char *)newbuff->data + newacc->byteOffset; olditms = (char *)oldbuff->data + acc->byteOffset; for (i = 0; i < icount; i++) { oldidx = ind1_it[i * indxSt + inpOff]; newidx = ind2_it[i]; memcpy(newitms + newByteSt * newidx, olditms + oldByteSt * oldidx, fillSize); } /* to prevent duplication operation for next time */ input->offset = 0; } cont: input = input->next; } primi = primi->next; } return AK_OK; } ================================================ FILE: src/mesh/edit_common.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_mesh_edit_common_h #define ak_mesh_edit_common_h #include "../common.h" typedef struct AkPrimProxy { struct AkPrimProxy *next; AkMeshPrimitive *orig; uint8_t *flg; AkUIntArray *ind; AkUIntArray *newind; AkInput *input; uint32_t *inpi; uint32_t chk_start; uint32_t chk_end; uint32_t vo; /* vertOffset */ uint32_t st; uint32_t count; uint32_t icount; uint32_t ccount; /* checked count */ } AkPrimProxy; typedef struct AkInputDesc { struct AkInputDesc *next; const char *semantic; AkURL *source; AkInput *input; AkPrimProxy *pp; uint32_t set; int32_t index; } AkInputDesc; void ak_meshFreeRsvBuff(RBTree *tree, RBNode *node); #endif /* ak_meh_edit_common_h */ ================================================ FILE: src/mesh/edit_index.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include extern const char* ak_mesh_edit_assert1; AK_EXPORT AkUIntArray* ak_meshIndicesArrayFor(AkMesh * __restrict mesh, AkMeshPrimitive * __restrict prim, bool creat) { AkHeap *heap; AkObject *meshobj; AkMeshEditHelper *edith; AkUIntArray *indices; size_t count; edith = mesh->edith; assert(edith && ak_mesh_edit_assert1); meshobj = ak_objFrom(mesh); heap = ak_heap_getheap(meshobj); if (!(edith->flags & AK_GEOM_EDIT_FLAG_INDICES) || !edith->indices) { edith->indices = rb_newtree_ptr(); edith->flags |= AK_GEOM_EDIT_FLAG_INDICES; ak_dsSetAllocator(heap->allocator, edith->indices->alc); } indices = rb_find(edith->indices, prim); if (!indices) { if (!creat) return prim->indices; if (prim->indices) { count = prim->indices->count / prim->indexStride; } else { AkAccessor *posacc; if (!prim->pos || !(posacc = prim->pos->accessor) || !posacc->buffer) return NULL; count = posacc->componentCount * posacc->count; } indices = ak_heap_calloc(heap, prim, sizeof(*indices) + sizeof(AkUInt) * count); indices->count = count; rb_insert(edith->indices, prim, indices); return indices; } return indices; } AK_EXPORT void ak_moveIndices(AkMesh * __restrict mesh) { AkMeshPrimitive *prim; AkInput *input; /* fix indices */ prim = mesh->primitive; while (prim) { AkUIntArray *indices; indices = ak_meshIndicesArrayFor(mesh, prim, false); /* same index buff */ if (!indices || indices == prim->indices) { prim = prim->next; continue; } ak_free(prim->indices); prim->indices = indices; /* mark primitive as single index */ prim->indexStride = 1; /* make all offsets 0 */ input = prim->input; while (input) { input->offset = 0; input = input->next; } prim = prim->next; } } ================================================ FILE: src/mesh/index.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "index.h" #include "../../include/ak/trash.h" #include #include extern const char* ak_mesh_edit_assert1; AK_HIDE AkResult ak_movePositions(AkMesh *mesh, AkMeshPrimitive *prim, AkDuplicator *duplicator) { AkMeshEditHelper *edith; AkSourceEditHelper *srch; AkSourceBuffState *buffstate; AkAccessor *acc, *newacc; AkUIntArray *dupc, *dupcsum; AkBuffer *oldbuff, *newbuff; char *olditms, *newitms; size_t vc, d, s, pno, poo, byteStride; uint32_t i, j; if (!prim->pos || !(edith = mesh->edith) || !(acc = prim->pos->accessor) || !(oldbuff = acc->buffer) || !(buffstate = rb_find(edith->buffers, prim->pos))) return AK_ERR; newbuff = buffstate->buff; srch = ak_meshSourceEditHelper(mesh, prim->pos); newacc = srch->source; if (!newacc) return AK_ERR; dupc = duplicator->range->dupc; dupcsum = duplicator->range->dupcsum; vc = dupc->count; newitms = newbuff->data; olditms = oldbuff->data; byteStride = acc->byteStride; /* copy vert positions to new location */ for (i = 0; i < vc; i++) { if ((poo = dupc->items[3 * i + 2]) == 0) continue; pno = dupc->items[3 * i]; d = dupc->items[3 * i + 1]; s = dupcsum->items[pno]; for (j = 0; j <= d; j++) { memcpy(newitms + byteStride * (pno + j + s), olditms + byteStride * (poo - 1), byteStride); } } return AK_OK; } AK_HIDE AkResult ak_primFixIndices(AkMesh *mesh, AkMeshPrimitive *prim) { AkDuplicator *dupl; if ((prim->indexStride == 1) || !prim->indices || !(dupl = ak_meshDuplicatorForIndices(mesh, prim))) return AK_ERR; ak_meshFixIndexBuffer(mesh, prim, dupl); ak_meshReserveBuffers(mesh, prim, dupl->dupCount + dupl->bufCount); ak_movePositions(mesh, prim, dupl); return AK_OK; } AK_HIDE AkResult ak_meshFixIndicesDefault(AkMesh *mesh) { AkMeshPrimitive *prim; prim = mesh->primitive; while (prim) { ak_primFixIndices(mesh, prim); prim = prim->next; } return AK_OK; } AK_HIDE AkResult ak_meshFixIndices(AkMesh *mesh) { AkResult ret; ak_meshBeginEdit(mesh); /* currently only default option */ ret = ak_meshFixIndicesDefault(mesh); ak_meshEndEdit(mesh); return ret; } ================================================ FILE: src/mesh/index.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_mesh_index_h #define ak_mesh_index_h #include "../common.h" AK_HIDE AkResult ak_meshFixIndices(AkMesh *mesh); AK_HIDE AkResult ak_primFixIndices(AkMesh *mesh, AkMeshPrimitive *prim); AK_HIDE AkResult ak_meshFixIndicesDefault(AkMesh *mesh); AK_HIDE AkResult ak_movePositions(AkMesh *mesh, AkMeshPrimitive *prim, AkDuplicator *duplicator); #endif /* ak_mesh_index_h */ ================================================ FILE: src/mesh/input.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "index.h" #include "edit_common.h" #include #include void ak_meshReIndexInputs(AkMesh * __restrict mesh) { AkHeap *heap; AkObject *meshobj; RBTree *tree; AkInput *inp; AkMeshPrimitive *prim; RBNode *found; meshobj = ak_objFrom(mesh); heap = ak_heap_getheap(meshobj); tree = rb_newtree_str(); prim = mesh->primitive; ak_dsSetAllocator(heap->allocator, tree->alc); if (prim) { do { inp = prim->input; while (inp) { found = rb_find_node(tree, (void *)inp->semanticRaw); if (found) { found->val = (void *)((uintptr_t)found->val) + 1; inp->index = (int32_t)(uintptr_t)found->val; inp->isIndexed = true; } else { rb_insert(tree, (void *)inp->semanticRaw, NULL); inp->index = 0; inp->isIndexed = false; } inp = inp->next; } if (prim->next) rb_empty(tree); prim = prim->next; } while (prim); /* check for correct zero postfix e.g. TECOORD0 */ if (ak_opt_get(AK_OPT_ZERO_INDEXED_INPUT)) { prim = mesh->primitive; do { inp = prim->input; while (inp) { found = rb_find_node(tree, (void *)inp->semanticRaw); if (found && !inp->index && found->val) inp->isIndexed = true; inp = inp->next; } rb_empty(tree); prim = prim->next; } while (prim); } } rb_destroy(tree); } void ak_inputNameIndexed(AkInput * __restrict input, char * __restrict buf) { if (!input->semanticRaw) return; if (input->isIndexed) sprintf(buf, "%s%d", input->semanticRaw, input->index); else strcpy(buf, input->semanticRaw); } AK_EXPORT void ak_inputNameBySet(AkInput * __restrict input, char * __restrict buf) { // if (!input->semanticRaw) // return; // // if (input->set > 0) // sprintf(buf, "%s%u", input->semanticRaw, input->set); // else // strcpy(buf, input->semanticRaw); ak_inputNameIndexed(input, buf); } AK_EXPORT AkInput* ak_meshInputGet(AkMeshPrimitive *prim, const char *inputSemantic, uint32_t set) { AkInput *input; /* first search in primitive */ input = prim->input; while (input) { if (!input->semanticRaw) goto cont1; if (strcmp(input->semanticRaw, inputSemantic) == 0 && input->set == set) return input; cont1: input = input->next; } return NULL; } ================================================ FILE: src/mesh/isolate.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" AK_EXPORT bool ak_meshIsPrimIsolated(AkMeshPrimitive *prim) { AkInput *inp; AkAccessor *acc; AkBuffer *buff; if (!(inp = prim->input)) return true; do { /* check accessor reference count */ if ((acc = inp->accessor) && ak_refc(acc) > 1) return false; /* check buffer reference count */ if ((buff = acc->buffer) && ak_refc(buff) > 1) return false; } while ((inp = inp->next)); return true; } AK_EXPORT bool ak_meshIsIsolated(AkMesh *mesh) { AkMeshPrimitive *prim; if (mesh && (prim = mesh->primitive)) { do { if (!ak_meshIsPrimIsolated(prim)) return false; } while ((prim = prim->next)); } return true; } AK_EXPORT void ak_meshIsolatePrim(AkMeshPrimitive *prim) { if (!prim) return; /* detach accessors */ /* detach buffers */ } AK_EXPORT void ak_meshIsolate(AkMesh *mesh) { AkMeshPrimitive *prim; if (!mesh) return; if ((prim = mesh->primitive)) { do { ak_meshIsolatePrim(prim); } while ((prim = prim->next)); } } ================================================ FILE: src/mesh/material.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include AK_EXPORT AkResult ak_meshSetMaterial(AkMeshPrimitive *prim, const char *material) { AkGeometry *geom; AkMap *map; geom = prim->mesh->geom; #ifdef DEBUG assert(geom && "set geometry for this primitive!"); assert(geom->materialMap && "set materialMap for this geom!"); #endif map = geom->materialMap; /* TODO: remove first */ ak_multimap_add(map, prim, (void *)material); prim->bindmaterial = material; return AK_OK; } ================================================ FILE: src/mesh/normal.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "../id.h" #include "../data.h" #include "index.h" #include AK_HIDE void ak_meshPrimGenNormals(AkMeshPrimitive * __restrict prim); AK_EXPORT bool ak_meshPrimNeedsNormals(AkMeshPrimitive * __restrict prim) { AkAccessor *acc; AkInput *input; bool ret; if (!prim || (prim->type != AK_PRIMITIVE_TRIANGLES && prim->type != AK_PRIMITIVE_POLYGONS)) return false; ret = true; input = prim->input; while (input) { if (input->semantic == AK_INPUT_NORMAL) { if (!(acc = input->accessor) || !acc->buffer) return ret; ret = false; break; } input = input->next; } return ret; } AK_EXPORT bool ak_meshNeedsNormals(AkMesh * __restrict mesh) { AkMeshPrimitive *prim; bool ret; ret = false; prim = mesh->primitive; while (prim) { ret |= ak_meshPrimNeedsNormals(prim); if (ret) break; prim = prim->next; } return ret; } AK_HIDE void ak_meshPrimGenNormals(AkMeshPrimitive * __restrict prim) { AkDataContext *dctx; AkDoc *doc; AkUIntArray *inpIndices; AkFloat *pos; AkUInt *it, *it2; AkHeap *heap; AkInput *input, *nextInput; AkBuffer *posBuff, *buff; AkAccessor *posAcc, *acc; AkUInt st, newst; AkInt vo, pos_st; uint32_t count; if ((prim->type != AK_PRIMITIVE_TRIANGLES && prim->type != AK_PRIMITIVE_POLYGONS) || !prim->pos || !(posAcc = prim->pos->accessor) || !(posBuff = posAcc->buffer) || (vo = prim->pos->offset) == -1) return; dctx = ak_data_new(prim, 64, sizeof(vec3), ak_cmp_vec3); heap = ak_heap_getheap(prim); doc = ak_heap_data(heap); pos = posBuff->data; pos_st = posAcc->componentCount; if (!prim->indices || prim->indices->count == 0) return; it = prim->indices->items + vo; st = prim->indexStride; count = (uint32_t)prim->indices->count / st; newst = st + 1; /* TODO: for now join this into existing indices, but in the future use separate to fix indices */ inpIndices = ak_heap_calloc(heap, prim, sizeof(*inpIndices) + count * newst * sizeof(*it)); inpIndices->count = count * newst; it2 = inpIndices->items; switch (prim->type) { case AK_PRIMITIVE_POLYGONS: { AkPolygon *poly; AkUInt *vc_it; float *a, *b, *c; vec3 v1, v2, n; size_t i, j, k, vc, ist; AkUInt idx; poly = (AkPolygon *)prim; /* polygon ha triangulated */ if (!poly->vcount) goto tri; vc_it = poly->vcount->items; for (i = k = 0; k < poly->vcount->count; k++) { vc = vc_it[k]; /* TODO: normals for lines or points ? */ if (vc < 3) continue; ist = i * st + vo; a = pos + it[ist] * pos_st; b = pos + it[ist + st] * pos_st; c = pos + it[ist + st + st] * pos_st; glm_vec3_sub(a, b, v1); glm_vec3_sub(b, c, v2); glm_vec3_cross(v1, v2, n); glm_vec3_normalize(n); idx = ak_data_append(dctx, n); for (j = i; j < i + vc; j++) { /* other inputs */ memcpy(it2 + j * newst, it + j * st, sizeof(*it) * st); /* normal */ it2[j * newst + st] = idx; } i += vc; } break; } case AK_PRIMITIVE_TRIANGLES: tri: { AkTriangles *tri; float *a, *b, *c; vec3 v1, v2, n; AkUInt i, j, idx, ist; tri = (AkTriangles *)prim; switch (tri->mode) { case AK_TRIANGLES: for (i = 0; i < count; i += 3 /* 3: triangle */) { ist = i * st + vo; a = pos + it[ist] * pos_st; b = pos + it[ist + st] * pos_st; c = pos + it[ist + st + st] * pos_st; glm_vec3_sub(a, b, v1); glm_vec3_sub(b, c, v2); glm_vec3_cross(v1, v2, n); glm_vec3_normalize(n); idx = ak_data_append(dctx, n); for (j = i; j < i + 3; j++) { /* other inputs */ memcpy(it2 + j * newst, it + j * st, sizeof(*it) * st); /* normal */ it2[j * newst + st] = idx; } } break; case AK_TRIANGLE_FAN: { float *central = pos + it[vo] * pos_st; // Central vertex for (i = 1; i < count - 1; i++) { a = central; b = pos + it[vo + i * st] * pos_st; c = pos + it[vo + (i + 1) * st] * pos_st; // Calculate normal glm_vec3_sub(b, a, v1); glm_vec3_sub(c, a, v2); glm_vec3_cross(v1, v2, n); glm_vec3_normalize(n); idx = ak_data_append(dctx, n); // Assign normals to central, current, and next vertex it2[vo * newst + st] = idx; // Central vertex normal (may need adjustment) it2[(vo + i * st) * newst + st] = idx; // Current vertex normal it2[(vo + (i + 1) * st) * newst + st] = idx; // Next vertex normal } break; } case AK_TRIANGLE_STRIP: { for (i = 0; i < count - 2; i++) { a = pos + it[vo + i * st] * pos_st; b = pos + it[vo + (i + 1) * st] * pos_st; c = pos + it[vo + (i + 2) * st] * pos_st; // Calculate normal glm_vec3_sub(b, a, v1); glm_vec3_sub(c, b, v2); glm_vec3_cross(v1, v2, n); glm_vec3_normalize(n); idx = ak_data_append(dctx, n); // Assign normals to the three vertices of the triangle it2[(vo + i * st) * newst + st] = idx; it2[(vo + (i + 1) * st) * newst + st] = idx; it2[(vo + (i + 2) * st) * newst + st] = idx; } break; } default: break; } break; } default: ak_free(inpIndices); return; } acc = ak_heap_calloc(heap, doc, sizeof(*acc)); ak_setypeid(acc, AKT_ACCESSOR); acc->componentCount = 3; acc->count = (uint32_t)dctx->itemcount; acc->componentType = AKT_FLOAT; acc->componentSize = AK_COMPONENT_SIZE_VEC3; acc->bytesPerComponent = ak_typeDesc(acc->componentType)->size; acc->byteStride = acc->componentCount * acc->bytesPerComponent; acc->fillByteSize = acc->byteStride; acc->byteLength = acc->count * acc->byteStride; buff = ak_heap_calloc(heap, doc, sizeof(*buff)); buff->data = ak_heap_alloc(heap, buff, acc->byteLength); buff->length = acc->byteLength; acc->buffer = buff; flist_sp_insert(&doc->lib.accessors, acc); flist_sp_insert(&doc->lib.buffers, buff); /* add input */ input = ak_heap_calloc(heap, prim, sizeof(*input)); input->offset = st; input->semantic = AK_INPUT_NORMAL; input->semanticRaw = "NORMAL"; nextInput = prim->input; while (nextInput->next) nextInput = nextInput->next; nextInput->next = input; input->accessor = acc; prim->inputCount++; prim->indexStride++; ak_free(prim->indices); prim->indices = inpIndices; (void)ak_data_join(dctx, buff->data, 0, 0); ak_free(dctx); } AK_EXPORT void ak_meshGenNormals(AkMesh * __restrict mesh) { AkMeshEditHelper *edith; AkMeshPrimitive *prim; ak_meshBeginEdit(mesh); prim = mesh->primitive; edith = mesh->edith; while (prim) { ak_meshPrimGenNormals(prim); if (!edith->skipFixIndices) ak_primFixIndices(mesh, prim); prim = prim->next; } ak_meshEndEdit(mesh); } ================================================ FILE: src/mesh/triangulate.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" AK_HIDE uint32_t ak_meshTriangulatePoly_noindices(AkPolygon * __restrict poly); AK_INLINE void ak_meshPolyMarkTriangles(AkPolygon * __restrict poly, AkUInt nTrigs) { poly->base.nPolygons = nTrigs; poly->base.type = AK_PRIMITIVE_TRIANGLES; ak_free(poly->vcount); poly->vcount = NULL; } /* not tested yet! */ AK_HIDE uint32_t ak_meshTriangulatePoly_noindices(AkPolygon * __restrict poly) { AkBuffer *buff, *newbuff; AkHeap *heap; AkUInt *vc_it; AkAccessor *acc; AkFloat *it_new, *it_old; AkUInt nGenTrigs, nTrigs, i, isz; AkBool isTriList; size_t st; if (!poly->base.pos || !(acc = poly->base.pos->accessor) || !(buff = acc->buffer)) return 0; nTrigs = 0; nGenTrigs = 0; isTriList = true; vc_it = poly->vcount->items; for (i = 0; i < poly->vcount->count; i++) { if (vc_it[i] > 3) { nGenTrigs += vc_it[i] - 2; isTriList = false; } else { nTrigs += 1; if (vc_it[i] != 3) isTriList = false; } } if (!nGenTrigs) { if (isTriList && nTrigs > 0) ak_meshPolyMarkTriangles(poly, nTrigs); return 0; } isz = sizeof(AkFloat); st = acc->byteStride; heap = ak_heap_getheap(poly->vcount); newbuff = ak_heap_calloc(heap, poly, sizeof(*newbuff)); newbuff->data = ak_heap_alloc(heap, newbuff, isz * (nTrigs + nGenTrigs) * 3 * st); newbuff->length = isz * nGenTrigs * 3 * st; it_old = newbuff->data; it_new = buff->data; for (i = 0; i < poly->vcount->count; i++) { uint32_t vc, j; vc = vc_it[i]; if (vc > 2) { for (j = 1; j < vc - 1; j++) { memcpy(it_new, it_old, st * isz); it_new += st; memcpy(it_new, it_old + j * st, 2 * st * isz); it_new += 2 * st; } } else { memcpy(it_new, it_old, vc * st * isz); it_new += vc * st; } it_old += vc * st; } return nGenTrigs; } AK_EXPORT uint32_t ak_meshTriangulatePoly(AkPolygon * __restrict poly) { AkHeap *heap; AkUIntArray *newind; AkUInt *vc_it, *ind_it, *newind_it; AkUInt nGenTrigs, nTrigs, i, st; AkUInt isz; AkBool isTriList; if (!poly->vcount) return 0; /* we have only primitives for direct draw, no indexes :( */ if (!poly->base.indices) return ak_meshTriangulatePoly_noindices(poly); nTrigs = 0; nGenTrigs = 0; isTriList = true; vc_it = poly->vcount->items; for (i = 0; i < poly->vcount->count; i++) { if (vc_it[i] > 3) { nGenTrigs += vc_it[i] - 2; isTriList = false; } else { nTrigs += 1; if (vc_it[i] != 3) isTriList = false; } } if (!nGenTrigs) { if (isTriList && nTrigs > 0) ak_meshPolyMarkTriangles(poly, nTrigs); return 0; } isz = sizeof(AkUInt); heap = ak_heap_getheap(poly->vcount); ind_it = poly->base.indices->items; st = poly->base.indexStride; newind = ak_heap_alloc(heap, poly, sizeof(*newind) + isz * ((nTrigs + nGenTrigs) * 3) * st); newind->count = ((nTrigs + nGenTrigs) * 3) * st; newind_it = newind->items; for (i = 0; i < poly->vcount->count; i++) { uint32_t vc, j; vc = vc_it[i]; if (vc > 2) { for (j = 1; j < vc - 1; j++) { memcpy(newind_it, ind_it, st * isz); newind_it += st; memcpy(newind_it, ind_it + j * st, 2 * st * isz); newind_it += 2 * st; } } else { memcpy(newind_it, ind_it, vc * st * isz); newind_it += vc * st; } ind_it += vc * st; } ak_free(poly->base.indices); poly->base.indices = newind; ak_meshPolyMarkTriangles(poly, nGenTrigs + nTrigs); return nGenTrigs; } AK_EXPORT uint32_t ak_meshTriangulate(AkMesh * __restrict mesh) { AkMeshPrimitive *prim; uint32_t extc; extc = 0; prim = mesh->primitive; while (prim) { if (prim->type == AK_PRIMITIVE_POLYGONS) extc += ak_meshTriangulatePoly((AkPolygon *)prim); prim = prim->next; } return extc; } ================================================ FILE: src/miniz/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/miniz/LICENSE ================================================ Copyright 2013-2014 RAD Game Tools and Valve Software Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC All Rights Reserved. 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. ================================================ FILE: src/miniz/miniz.c ================================================ #include "miniz.h" /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * All Rights Reserved. * * 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. * **************************************************************************/ typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; #ifdef __cplusplus extern "C" { #endif /* ------------------- zlib-style API's */ mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) { mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552; if (!ptr) return MZ_ADLER32_INIT; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for (; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } return (s2 << 16) + s1; } /* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ */ #if 0 mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; mz_uint32 crcu32 = (mz_uint32)crc; if (!ptr) return MZ_CRC32_INIT; crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; } return ~crcu32; } #elif defined(USE_EXTERNAL_MZCRC) /* If USE_EXTERNAL_CRC is defined, an external module will export the * mz_crc32() symbol for us to use, e.g. an SSE-accelerated version. * Depending on the impl, it may be necessary to ~ the input/output crc values. */ mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len); #else /* Faster, but larger CPU cache footprint. */ mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { static const mz_uint32 s_crc_table[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D }; mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF; const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr; while (buf_len >= 4) { crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF]; crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF]; crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF]; pByte_buf += 4; buf_len -= 4; } while (buf_len) { crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; ++pByte_buf; --buf_len; } return ~crc32; } #endif void mz_free(void *p) { MZ_FREE(p); } MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); } MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); } MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); } const char *mz_version(void) { return MZ_VERSION; } #ifndef MINIZ_NO_ZLIB_APIS int mz_deflateInit(mz_streamp pStream, int level) { return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); } int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) { tdefl_compressor *pComp; mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); if (!pStream) return MZ_STREAM_ERROR; if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR; pStream->data_type = 0; pStream->adler = MZ_ADLER32_INIT; pStream->msg = NULL; pStream->reserved = 0; pStream->total_in = 0; pStream->total_out = 0; if (!pStream->zalloc) pStream->zalloc = miniz_def_alloc_func; if (!pStream->zfree) pStream->zfree = miniz_def_free_func; pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); if (!pComp) return MZ_MEM_ERROR; pStream->state = (struct mz_internal_state *)pComp; if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) { mz_deflateEnd(pStream); return MZ_PARAM_ERROR; } return MZ_OK; } int mz_deflateReset(mz_streamp pStream) { if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR; pStream->total_in = pStream->total_out = 0; tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags); return MZ_OK; } int mz_deflate(mz_streamp pStream, int flush) { size_t in_bytes, out_bytes; mz_ulong orig_total_in, orig_total_out; int mz_status = MZ_OK; if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR; if (!pStream->avail_out) return MZ_BUF_ERROR; if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; if (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; orig_total_in = pStream->total_in; orig_total_out = pStream->total_out; for (;;) { tdefl_status defl_status; in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state); pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; if (defl_status < 0) { mz_status = MZ_STREAM_ERROR; break; } else if (defl_status == TDEFL_STATUS_DONE) { mz_status = MZ_STREAM_END; break; } else if (!pStream->avail_out) break; else if ((!pStream->avail_in) && (flush != MZ_FINISH)) { if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) break; return MZ_BUF_ERROR; /* Can't make forward progress without some input. */ } } return mz_status; } int mz_deflateEnd(mz_streamp pStream) { if (!pStream) return MZ_STREAM_ERROR; if (pStream->state) { pStream->zfree(pStream->opaque, pStream->state); pStream->state = NULL; } return MZ_OK; } mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) { (void)pStream; /* This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) */ return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); } int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) { int status; mz_stream stream; memset(&stream, 0, sizeof(stream)); /* In case mz_ulong is 64-bits (argh I hate longs). */ if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; stream.avail_in = (mz_uint32)source_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; status = mz_deflateInit(&stream, level); if (status != MZ_OK) return status; status = mz_deflate(&stream, MZ_FINISH); if (status != MZ_STREAM_END) { mz_deflateEnd(&stream); return (status == MZ_OK) ? MZ_BUF_ERROR : status; } *pDest_len = stream.total_out; return mz_deflateEnd(&stream); } int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); } mz_ulong mz_compressBound(mz_ulong source_len) { return mz_deflateBound(NULL, source_len); } typedef struct { tinfl_decompressor m_decomp; mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits; mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; tinfl_status m_last_status; } inflate_state; int mz_inflateInit2(mz_streamp pStream, int window_bits) { inflate_state *pDecomp; if (!pStream) return MZ_STREAM_ERROR; if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR; pStream->data_type = 0; pStream->adler = 0; pStream->msg = NULL; pStream->total_in = 0; pStream->total_out = 0; pStream->reserved = 0; if (!pStream->zalloc) pStream->zalloc = miniz_def_alloc_func; if (!pStream->zfree) pStream->zfree = miniz_def_free_func; pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); if (!pDecomp) return MZ_MEM_ERROR; pStream->state = (struct mz_internal_state *)pDecomp; tinfl_init(&pDecomp->m_decomp); pDecomp->m_dict_ofs = 0; pDecomp->m_dict_avail = 0; pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; pDecomp->m_first_call = 1; pDecomp->m_has_flushed = 0; pDecomp->m_window_bits = window_bits; return MZ_OK; } int mz_inflateInit(mz_streamp pStream) { return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); } int mz_inflateReset(mz_streamp pStream) { inflate_state *pDecomp; if (!pStream) return MZ_STREAM_ERROR; pStream->data_type = 0; pStream->adler = 0; pStream->msg = NULL; pStream->total_in = 0; pStream->total_out = 0; pStream->reserved = 0; pDecomp = (inflate_state *)pStream->state; tinfl_init(&pDecomp->m_decomp); pDecomp->m_dict_ofs = 0; pDecomp->m_dict_avail = 0; pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; pDecomp->m_first_call = 1; pDecomp->m_has_flushed = 0; /* pDecomp->m_window_bits = window_bits */; return MZ_OK; } int mz_inflate(mz_streamp pStream, int flush) { inflate_state *pState; mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; size_t in_bytes, out_bytes, orig_avail_in; tinfl_status status; if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; pState = (inflate_state *)pStream->state; if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; orig_avail_in = pStream->avail_in; first_call = pState->m_first_call; pState->m_first_call = 0; if (pState->m_last_status < 0) return MZ_DATA_ERROR; if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; pState->m_has_flushed |= (flush == MZ_FINISH); if ((flush == MZ_FINISH) && (first_call)) { /* MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. */ decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); pState->m_last_status = status; pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; if (status < 0) return MZ_DATA_ERROR; else if (status != TINFL_STATUS_DONE) { pState->m_last_status = TINFL_STATUS_FAILED; return MZ_BUF_ERROR; } return MZ_STREAM_END; } /* flush != MZ_FINISH then we must assume there's more input. */ if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; if (pState->m_dict_avail) { n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; } for (;;) { in_bytes = pStream->avail_in; out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); pState->m_last_status = status; pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); pState->m_dict_avail = (mz_uint)out_bytes; n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); if (status < 0) return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */ else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */ else if (flush == MZ_FINISH) { /* The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. */ if (status == TINFL_STATUS_DONE) return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; /* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. */ else if (!pStream->avail_out) return MZ_BUF_ERROR; } else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) break; } return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; } int mz_inflateEnd(mz_streamp pStream) { if (!pStream) return MZ_STREAM_ERROR; if (pStream->state) { pStream->zfree(pStream->opaque, pStream->state); pStream->state = NULL; } return MZ_OK; } int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len) { mz_stream stream; int status; memset(&stream, 0, sizeof(stream)); /* In case mz_ulong is 64-bits (argh I hate longs). */ if ((*pSource_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; stream.avail_in = (mz_uint32)*pSource_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; status = mz_inflateInit(&stream); if (status != MZ_OK) return status; status = mz_inflate(&stream, MZ_FINISH); *pSource_len = *pSource_len - stream.avail_in; if (status != MZ_STREAM_END) { mz_inflateEnd(&stream); return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; } *pDest_len = stream.total_out; return mz_inflateEnd(&stream); } int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { return mz_uncompress2(pDest, pDest_len, pSource, &source_len); } const char *mz_error(int err) { static struct { int m_err; const char *m_pDesc; } s_error_descs[] = { { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } }; mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc; return NULL; } #endif /*MINIZ_NO_ZLIB_APIS */ #ifdef __cplusplus } #endif /* 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. For more information, please refer to */ /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * All Rights Reserved. * * 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. * **************************************************************************/ #ifdef __cplusplus extern "C" { #endif /* ------------------- Low-level Compression (independent from all decompression API's) */ /* Purposely making these tables static for faster init and thread safety. */ static const mz_uint16 s_tdefl_len_sym[256] = { 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272, 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285 }; static const mz_uint8 s_tdefl_len_extra[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0 }; static const mz_uint8 s_tdefl_small_dist_sym[512] = { 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17 }; static const mz_uint8 s_tdefl_small_dist_extra[512] = { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }; static const mz_uint8 s_tdefl_large_dist_sym[128] = { 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 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, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 }; static const mz_uint8 s_tdefl_large_dist_extra[128] = { 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13 }; /* Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. */ typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq; static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1) { mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_OBJ(hist); for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; } while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) { const mz_uint32 *pHist = &hist[pass << 8]; mz_uint offsets[256], cur_ofs = 0; for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; { tdefl_sym_freq *t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; } } return pCur_syms; } /* tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. */ static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) { int root, leaf, next, avbl, used, dpth; if (n == 0) return; else if (n == 1) { A[0].m_key = 1; return; } A[0].m_key += A[1].m_key; root = 0; leaf = 2; for (next = 1; next < n - 1; next++) { if (leaf >= n || A[root].m_key < A[leaf].m_key) { A[next].m_key = A[root].m_key; A[root++].m_key = (mz_uint16)next; } else A[next].m_key = A[leaf++].m_key; if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) { A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); A[root++].m_key = (mz_uint16)next; } else A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); } A[n - 2].m_key = 0; for (next = n - 3; next >= 0; next--) A[next].m_key = A[A[next].m_key].m_key + 1; avbl = 1; used = dpth = 0; root = n - 2; next = n - 1; while (avbl > 0) { while (root >= 0 && (int)A[root].m_key == dpth) { used++; root--; } while (avbl > used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; } avbl = 2 * used; dpth++; used = 0; } } /* Limits canonical Huffman code table's max code size. */ enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) { int i; mz_uint32 total = 0; if (code_list_len <= 1) return; for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); while (total != (1UL << max_code_size)) { pNum_codes[max_code_size]--; for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } total--; } } static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) { int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_OBJ(num_codes); if (static_table) { for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; } else { tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; int num_used_syms = 0; const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; } pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); for (i = 1, j = num_used_syms; i <= code_size_limit; i++) for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); } next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); for (i = 0; i < table_len; i++) { mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; } } #define TDEFL_PUT_BITS(b, l) \ do \ { \ mz_uint bits = b; \ mz_uint len = l; \ MZ_ASSERT(bits <= ((1U << len) - 1U)); \ d->m_bit_buffer |= (bits << d->m_bits_in); \ d->m_bits_in += len; \ while (d->m_bits_in >= 8) \ { \ if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ d->m_bit_buffer >>= 8; \ d->m_bits_in -= 8; \ } \ } \ MZ_MACRO_END #define TDEFL_RLE_PREV_CODE_SIZE() \ { \ if (rle_repeat_count) \ { \ if (rle_repeat_count < 3) \ { \ d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ while (rle_repeat_count--) \ packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ } \ else \ { \ d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \ packed_code_sizes[num_packed_code_sizes++] = 16; \ packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ } \ rle_repeat_count = 0; \ } \ } #define TDEFL_RLE_ZERO_CODE_SIZE() \ { \ if (rle_z_count) \ { \ if (rle_z_count < 3) \ { \ d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \ while (rle_z_count--) \ packed_code_sizes[num_packed_code_sizes++] = 0; \ } \ else if (rle_z_count <= 10) \ { \ d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \ packed_code_sizes[num_packed_code_sizes++] = 17; \ packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ } \ else \ { \ d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \ packed_code_sizes[num_packed_code_sizes++] = 18; \ packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ } \ rle_z_count = 0; \ } \ } static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; static void tdefl_start_dynamic_block(tdefl_compressor *d) { int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; d->m_huff_count[0][256] = 1; tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0; memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); for (i = 0; i < total_code_sizes_to_pack; i++) { mz_uint8 code_size = code_sizes_to_pack[i]; if (!code_size) { TDEFL_RLE_PREV_CODE_SIZE(); if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); } } else { TDEFL_RLE_ZERO_CODE_SIZE(); if (code_size != prev_code_size) { TDEFL_RLE_PREV_CODE_SIZE(); d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size; } else if (++rle_repeat_count == 6) { TDEFL_RLE_PREV_CODE_SIZE(); } } prev_code_size = code_size; } if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); } tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); TDEFL_PUT_BITS(2, 2); TDEFL_PUT_BITS(num_lit_codes - 257, 5); TDEFL_PUT_BITS(num_dist_codes - 1, 5); for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break; num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4); for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;) { mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); } } static void tdefl_start_static_block(tdefl_compressor *d) { mz_uint i; mz_uint8 *p = &d->m_huff_code_sizes[0][0]; for (i = 0; i <= 143; ++i) *p++ = 8; for (; i <= 255; ++i) *p++ = 9; for (; i <= 279; ++i) *p++ = 7; for (; i <= 287; ++i) *p++ = 8; memset(d->m_huff_code_sizes[1], 5, 32); tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); TDEFL_PUT_BITS(1, 2); } static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; mz_uint8 *pLZ_codes; mz_uint8 *pOutput_buf = d->m_pOutput_buf; mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; mz_uint64 bit_buffer = d->m_bit_buffer; mz_uint bits_in = d->m_bits_in; #define TDEFL_PUT_BITS_FAST(b, l) \ { \ bit_buffer |= (((mz_uint64)(b)) << bits_in); \ bits_in += (l); \ } flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint s0, s1, n0, n1, sym, num_extra_bits; mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); /* This sequence coaxes MSVC into using cmov's vs. jmp's. */ s0 = s_tdefl_small_dist_sym[match_dist & 511]; n0 = s_tdefl_small_dist_extra[match_dist & 511]; s1 = s_tdefl_large_dist_sym[match_dist >> 8]; n1 = s_tdefl_large_dist_extra[match_dist >> 8]; sym = (match_dist < 512) ? s0 : s1; num_extra_bits = (match_dist < 512) ? n0 : n1; MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } } if (pOutput_buf >= d->m_pOutput_buf_end) return MZ_FALSE; *(mz_uint64 *)pOutput_buf = bit_buffer; pOutput_buf += (bits_in >> 3); bit_buffer >>= (bits_in & ~7); bits_in &= 7; } #undef TDEFL_PUT_BITS_FAST d->m_pOutput_buf = pOutput_buf; d->m_bits_in = 0; d->m_bit_buffer = 0; while (bits_in) { mz_uint32 n = MZ_MIN(bits_in, 16); TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); bit_buffer >>= n; bits_in -= n; } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #else static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; mz_uint8 *pLZ_codes; flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint sym, num_extra_bits; mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); if (match_dist < 512) { sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist]; } else { sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; } MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS */ static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) { if (static_block) tdefl_start_static_block(d); else tdefl_start_dynamic_block(d); return tdefl_compress_lz_codes(d); } static int tdefl_flush_block(tdefl_compressor *d, int flush) { mz_uint saved_bit_buf, saved_bits_in; mz_uint8 *pSaved_output_buf; mz_bool comp_block_succeeded = MZ_FALSE; int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; d->m_pOutput_buf = pOutput_buf_start; d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; MZ_ASSERT(!d->m_output_flush_remaining); d->m_output_flush_ofs = 0; d->m_output_flush_remaining = 0; *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) { TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8); } TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in; if (!use_raw_block) comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); /* If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. */ if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) { mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; TDEFL_PUT_BITS(0, 2); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) { TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); } for (i = 0; i < d->m_total_lz_bytes; ++i) { TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); } } /* Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. */ else if (!comp_block_succeeded) { d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; tdefl_compress_block(d, MZ_TRUE); } if (flush) { if (flush == TDEFL_FINISH) { if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } } } else { mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); } } } MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++; if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) { if (d->m_pPut_buf_func) { *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); } else if (pOutput_buf_start == d->m_output_buf) { int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); d->m_out_buf_ofs += bytes_to_copy; if ((n -= bytes_to_copy) != 0) { d->m_output_flush_ofs = bytes_to_copy; d->m_output_flush_remaining = n; } } else { d->m_out_buf_ofs += n; } } return d->m_output_flush_remaining; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES #ifdef MINIZ_UNALIGNED_USE_MEMCPY static mz_uint16 TDEFL_READ_UNALIGNED_WORD(const mz_uint8* p) { mz_uint16 ret; memcpy(&ret, p, sizeof(mz_uint16)); return ret; } static mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16* p) { mz_uint16 ret; memcpy(&ret, p, sizeof(mz_uint16)); return ret; } #else #define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16 *)(p) #define TDEFL_READ_UNALIGNED_WORD2(p) *(const mz_uint16 *)(p) #endif static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q; mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD2(s); MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for (;;) { for (;;) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \ break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; q = (const mz_uint16 *)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD2(q) != s01) continue; p = s; probe_len = 32; do { } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); if (!probe_len) { *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN); break; } else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q)) > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); } } } #else static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const mz_uint8 *s = d->m_dict + pos, *p, *q; mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for (;;) { for (;;) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) \ break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break; if (probe_len > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return; c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1]; } } } #endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */ #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN #ifdef MINIZ_UNALIGNED_USE_MEMCPY static mz_uint32 TDEFL_READ_UNALIGNED_WORD32(const mz_uint8* p) { mz_uint32 ret; memcpy(&ret, p, sizeof(mz_uint32)); return ret; } #else #define TDEFL_READ_UNALIGNED_WORD32(p) *(const mz_uint32 *)(p) #endif static mz_bool tdefl_compress_fast(tdefl_compressor *d) { /* Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. */ mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) { const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); d->m_src_buf_left -= num_bytes_to_process; lookahead_size += num_bytes_to_process; while (num_bytes_to_process) { mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); memcpy(d->m_dict + dst_pos, d->m_pSrc, n); if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); d->m_pSrc += n; dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; num_bytes_to_process -= n; } dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; while (lookahead_size >= 4) { mz_uint cur_match_dist, cur_match_len = 1; mz_uint8 *pCur_dict = d->m_dict + cur_pos; mz_uint first_trigram = TDEFL_READ_UNALIGNED_WORD32(pCur_dict) & 0xFFFFFF; mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; mz_uint probe_pos = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)lookahead_pos; if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((TDEFL_READ_UNALIGNED_WORD32(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) { const mz_uint16 *p = (const mz_uint16 *)pCur_dict; const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); mz_uint32 probe_len = 32; do { } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); if (!probe_len) cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U))) { cur_match_len = 1; *pLZ_code_buf++ = (mz_uint8)first_trigram; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); d->m_huff_count[0][(mz_uint8)first_trigram]++; } else { mz_uint32 s0, s1; cur_match_len = MZ_MIN(cur_match_len, lookahead_size); MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); cur_match_dist--; pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); #ifdef MINIZ_UNALIGNED_USE_MEMCPY memcpy(&pLZ_code_buf[1], &cur_match_dist, sizeof(cur_match_dist)); #else *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; #endif pLZ_code_buf += 3; *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; } } else { *pLZ_code_buf++ = (mz_uint8)first_trigram; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); d->m_huff_count[0][(mz_uint8)first_trigram]++; } if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } total_lz_bytes += cur_match_len; lookahead_pos += cur_match_len; dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; MZ_ASSERT(lookahead_size >= cur_match_len); lookahead_size -= cur_match_len; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } while (lookahead_size) { mz_uint8 lit = d->m_dict[cur_pos]; total_lz_bytes++; *pLZ_code_buf++ = lit; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } d->m_huff_count[0][lit]++; lookahead_pos++; dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; lookahead_size--; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } } d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; return MZ_TRUE; } #endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) { d->m_total_lz_bytes++; *d->m_pLZ_code_buf++ = lit; *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } d->m_huff_count[0][lit]++; } static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) { mz_uint32 s0, s1; MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); d->m_total_lz_bytes += match_len; d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); match_dist -= 1; d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3; *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; } static mz_bool tdefl_compress_normal(tdefl_compressor *d) { const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left; tdefl_flush flush = d->m_flush; while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) { mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; /* Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. */ if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) { mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; src_buf_left -= num_bytes_to_process; d->m_lookahead_size += num_bytes_to_process; while (pSrc != pSrc_end) { mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++; } } else { while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) { mz_uint8 c = *pSrc++; mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; src_buf_left--; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) { mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); } } } d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) break; /* Simple lazy/greedy parsing state machine. */ len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) { if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) { mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; } if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1; } } else { tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); } if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) { cur_match_dist = cur_match_len = 0; } if (d->m_saved_match_len) { if (cur_match_len > d->m_saved_match_len) { tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); if (cur_match_len >= 128) { tdefl_record_match(d, cur_match_len, cur_match_dist); d->m_saved_match_len = 0; len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } } else { tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0; } } else if (!cur_match_dist) tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) { tdefl_record_match(d, cur_match_len, cur_match_dist); len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } /* Move the lookahead forward by len_to_move bytes. */ d->m_lookahead_pos += len_to_move; MZ_ASSERT(d->m_lookahead_size >= len_to_move); d->m_lookahead_size -= len_to_move; d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE); /* Check if it's time to flush the current LZ codes to the internal output buffer. */ if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))) { int n; d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; } } d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; return MZ_TRUE; } static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) { if (d->m_pIn_buf_size) { *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; } if (d->m_pOut_buf_size) { size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); d->m_output_flush_ofs += (mz_uint)n; d->m_output_flush_remaining -= (mz_uint)n; d->m_out_buf_ofs += n; *d->m_pOut_buf_size = d->m_out_buf_ofs; } return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; } tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) { if (!d) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return TDEFL_STATUS_BAD_PARAM; } d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size; d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size; d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; d->m_out_buf_ofs = 0; d->m_flush = flush; if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf)) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); } d->m_wants_to_finish |= (flush == TDEFL_FINISH); if ((d->m_output_flush_remaining) || (d->m_finished)) return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) { if (!tdefl_compress_fast(d)) return d->m_prev_return_status; } else #endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ { if (!tdefl_compress_normal(d)) return d->m_prev_return_status; } if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) { if (tdefl_flush_block(d, flush) < 0) return d->m_prev_return_status; d->m_finished = (flush == TDEFL_FINISH); if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; } } return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); } tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) { MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); } tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user; d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash); d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; *d->m_pLZ_flags = 0; d->m_num_flags_left = 8; d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1; d->m_pIn_buf = NULL; d->m_pOut_buf = NULL; d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL; d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_dict); memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); return TDEFL_STATUS_OKAY; } tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) { return d->m_prev_return_status; } mz_uint32 tdefl_get_adler32(tdefl_compressor *d) { return d->m_adler32; } mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE; pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE; succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); MZ_FREE(pComp); return succeeded; } typedef struct { size_t m_size, m_capacity; mz_uint8 *m_pBuf; mz_bool m_expandable; } tdefl_output_buffer; static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) { tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; size_t new_size = p->m_size + len; if (new_size > p->m_capacity) { size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE; do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity); pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE; p->m_pBuf = pNew_buf; p->m_capacity = new_capacity; } memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size; return MZ_TRUE; } void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); if (!pOut_len) return MZ_FALSE; else *pOut_len = 0; out_buf.m_expandable = MZ_TRUE; if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL; *pOut_len = out_buf.m_size; return out_buf.m_pBuf; } size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); if (!pOut_buf) return 0; out_buf.m_pBuf = (mz_uint8 *)pOut_buf; out_buf.m_capacity = out_buf_len; if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0; return out_buf.m_size; } static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; /* level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). */ mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) { mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER; if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES; else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK; else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES; return comp_flags; } #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4204) /* nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) */ #endif /* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. */ void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) { /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. */ static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0; if (!pComp) return NULL; MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; } /* write dummy header */ for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf); /* compress image data */ tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); } if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } /* write real header */ *pLen_out = out_buf.m_size - 41; { static const mz_uint8 chans[] = { 0x00, 0x00, 0x04, 0x02, 0x06 }; mz_uint8 pnghdr[41] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x44, 0x41, 0x54 }; pnghdr[18] = (mz_uint8)(w >> 8); pnghdr[19] = (mz_uint8)w; pnghdr[22] = (mz_uint8)(h >> 8); pnghdr[23] = (mz_uint8)h; pnghdr[25] = chans[num_chans]; pnghdr[33] = (mz_uint8)(*pLen_out >> 24); pnghdr[34] = (mz_uint8)(*pLen_out >> 16); pnghdr[35] = (mz_uint8)(*pLen_out >> 8); pnghdr[36] = (mz_uint8)*pLen_out; c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); for (i = 0; i < 4; ++i, c <<= 8) ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); memcpy(out_buf.m_pBuf, pnghdr, 41); } /* write footer (IDAT CRC-32, followed by IEND chunk) */ if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4); for (i = 0; i < 4; ++i, c <<= 8) (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); /* compute final size of file, grab compressed data buffer and return */ *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf; } void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) { /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) */ return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); } #ifndef MINIZ_NO_MALLOC /* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ /* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ /* structure size and allocation mechanism. */ tdefl_compressor *tdefl_compressor_alloc() { return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); } void tdefl_compressor_free(tdefl_compressor *pComp) { MZ_FREE(pComp); } #endif #ifdef _MSC_VER #pragma warning(pop) #endif #ifdef __cplusplus } #endif /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * All Rights Reserved. * * 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. * **************************************************************************/ #ifdef __cplusplus extern "C" { #endif /* ------------------- Low-level Decompression (completely independent from all compression API's) */ #define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) #define TINFL_MEMSET(p, c, l) memset(p, c, l) #define TINFL_CR_BEGIN \ switch (r->m_state) \ { \ case 0: #define TINFL_CR_RETURN(state_index, result) \ do \ { \ status = result; \ r->m_state = state_index; \ goto common_exit; \ case state_index:; \ } \ MZ_MACRO_END #define TINFL_CR_RETURN_FOREVER(state_index, result) \ do \ { \ for (;;) \ { \ TINFL_CR_RETURN(state_index, result); \ } \ } \ MZ_MACRO_END #define TINFL_CR_FINISH } #define TINFL_GET_BYTE(state_index, c) \ do \ { \ while (pIn_buf_cur >= pIn_buf_end) \ { \ TINFL_CR_RETURN(state_index, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \ } \ c = *pIn_buf_cur++; \ } \ MZ_MACRO_END #define TINFL_NEED_BITS(state_index, n) \ do \ { \ mz_uint c; \ TINFL_GET_BYTE(state_index, c); \ bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ num_bits += 8; \ } while (num_bits < (mz_uint)(n)) #define TINFL_SKIP_BITS(state_index, n) \ do \ { \ if (num_bits < (mz_uint)(n)) \ { \ TINFL_NEED_BITS(state_index, n); \ } \ bit_buf >>= (n); \ num_bits -= (n); \ } \ MZ_MACRO_END #define TINFL_GET_BITS(state_index, b, n) \ do \ { \ if (num_bits < (mz_uint)(n)) \ { \ TINFL_NEED_BITS(state_index, n); \ } \ b = bit_buf & ((1 << (n)) - 1); \ bit_buf >>= (n); \ num_bits -= (n); \ } \ MZ_MACRO_END /* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */ /* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */ /* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */ /* bit buffer contains >=15 bits (deflate's max. Huffman code size). */ #define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ do \ { \ temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ if (temp >= 0) \ { \ code_len = temp >> 9; \ if ((code_len) && (num_bits >= code_len)) \ break; \ } \ else if (num_bits > TINFL_FAST_LOOKUP_BITS) \ { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do \ { \ temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while ((temp < 0) && (num_bits >= (code_len + 1))); \ if (temp >= 0) \ break; \ } \ TINFL_GET_BYTE(state_index, c); \ bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ num_bits += 8; \ } while (num_bits < 15); /* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */ /* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */ /* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */ /* The slow path is only executed at the very end of the input buffer. */ /* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */ /* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */ #define TINFL_HUFF_DECODE(state_index, sym, pHuff) \ do \ { \ int temp; \ mz_uint code_len, c; \ if (num_bits < 15) \ { \ if ((pIn_buf_end - pIn_buf_cur) < 2) \ { \ TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ } \ else \ { \ bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \ pIn_buf_cur += 2; \ num_bits += 16; \ } \ } \ if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ code_len = temp >> 9, temp &= 511; \ else \ { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do \ { \ temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while (temp < 0); \ } \ sym = temp; \ bit_buf >>= code_len; \ num_bits -= code_len; \ } \ MZ_MACRO_END tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) { static const int s_length_base[31] = { 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, 0, 0 }; static const int s_length_extra[31] = { 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, 0, 0 }; static const int s_dist_base[32] = { 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, 0, 0 }; static const int s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; static const int s_min_table_sizes[3] = { 257, 1, 4 }; tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */ if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; TINFL_CR_BEGIN bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } } do { TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; if (r->m_type == 0) { TINFL_SKIP_BITS(5, num_bits & 7); for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } while ((counter) && (num_bits)) { TINFL_GET_BITS(51, dist, 8); while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)dist; counter--; } while (counter) { size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } while (pIn_buf_cur >= pIn_buf_end) { TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); } n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; } } else if (r->m_type == 3) { TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); } else { if (r->m_type == 1) { mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); for (i = 0; i <= 143; ++i) *p++ = 8; for (; i <= 255; ++i) *p++ = 9; for (; i <= 279; ++i) *p++ = 7; for (; i <= 287; ++i) *p++ = 8; } else { for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } r->m_table_sizes[2] = 19; } for (; (int)r->m_type >= 0; r->m_type--) { int tree_next, tree_cur; tinfl_huff_table *pTable; mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree); for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } if ((65536 != total) && (used_syms > 1)) { TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); } for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) { mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; } if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) { tree_cur -= ((rev_code >>= 1) & 1); if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1]; } tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; } if (r->m_type == 2) { for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) { mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } if ((dist == 16) && (!counter)) { TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); } num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; } if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) { TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); } TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); } } for (;;) { mz_uint8 *pSrc; for (;;) { if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) { TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); if (counter >= 256) break; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)counter; } else { int sym2; mz_uint code_len; #if TINFL_USE_64BIT_BITBUF if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } #else if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } counter = sym2; bit_buf >>= code_len; num_bits -= code_len; if (counter & 256) break; #if !TINFL_USE_64BIT_BITBUF if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } bit_buf >>= code_len; num_bits -= code_len; pOut_buf_cur[0] = (mz_uint8)counter; if (sym2 & 256) { pOut_buf_cur++; counter = sym2; break; } pOut_buf_cur[1] = (mz_uint8)sym2; pOut_buf_cur += 2; } } if ((counter &= 511) == 256) break; num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; if ((dist == 0 || dist > dist_from_out_buf_start || dist_from_out_buf_start == 0) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) { TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); } pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) { while (counter--) { while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; } continue; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES else if ((counter >= 9) && (counter <= dist)) { const mz_uint8 *pSrc_end = pSrc + (counter & ~7); do { #ifdef MINIZ_UNALIGNED_USE_MEMCPY memcpy(pOut_buf_cur, pSrc, sizeof(mz_uint32)*2); #else ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; #endif pOut_buf_cur += 8; } while ((pSrc += 8) < pSrc_end); if ((counter &= 7) < 3) { if (counter) { pOut_buf_cur[0] = pSrc[0]; if (counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } continue; } } #endif while(counter>2) { pOut_buf_cur[0] = pSrc[0]; pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur[2] = pSrc[2]; pOut_buf_cur += 3; pSrc += 3; counter -= 3; } if (counter > 0) { pOut_buf_cur[0] = pSrc[0]; if (counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } } } } while (!(r->m_final & 1)); /* Ensure byte alignment and put back any bytes from the bitbuf if we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ /* I'm being super conservative here. A number of simplifications can be made to the byte alignment part, and the Adler32 check shouldn't ever need to worry about reading from the bitbuf now. */ TINFL_SKIP_BITS(32, num_bits & 7); while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) { --pIn_buf_cur; num_bits -= 8; } bit_buf &= (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */ if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } } TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); TINFL_CR_FINISH common_exit: /* As long as we aren't telling the caller that we NEED more input to make forward progress: */ /* Put back any bytes from the bitbuf in case we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ /* We need to be very careful here to NOT push back any bytes we definitely know we need to make forward progress, though, or we'll lock the caller up into an inf loop. */ if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS)) { while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) { --pIn_buf_cur; num_bits -= 8; } } r->m_num_bits = num_bits; r->m_bit_buf = bit_buf & (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) { const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for (; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; } return status; } /* Higher level helper functions. */ void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; *pOut_len = 0; tinfl_init(&decomp); for (;;) { size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } src_buf_ofs += src_buf_size; *pOut_len += dst_buf_size; if (status == TINFL_STATUS_DONE) break; new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); if (!pNew_buf) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; } return pBuf; } size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; } int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { int result = 0; tinfl_decompressor decomp; mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; if (!pDict) return TINFL_STATUS_FAILED; tinfl_init(&decomp); for (;;) { size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); in_buf_ofs += in_buf_size; if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) break; if (status != TINFL_STATUS_HAS_MORE_OUTPUT) { result = (status == TINFL_STATUS_DONE); break; } dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); } MZ_FREE(pDict); *pIn_buf_size = in_buf_ofs; return result; } #ifndef MINIZ_NO_MALLOC tinfl_decompressor *tinfl_decompressor_alloc() { tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); if (pDecomp) tinfl_init(pDecomp); return pDecomp; } void tinfl_decompressor_free(tinfl_decompressor *pDecomp) { MZ_FREE(pDecomp); } #endif #ifdef __cplusplus } #endif /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * Copyright 2016 Martin Raiber * All Rights Reserved. * * 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 MINIZ_NO_ARCHIVE_APIS #ifdef __cplusplus extern "C" { #endif /* ------------------- .ZIP archive reading */ #ifdef MINIZ_NO_STDIO #define MZ_FILE void * #else #include #if defined(_MSC_VER) || defined(__MINGW64__) static FILE *mz_fopen(const char *pFilename, const char *pMode) { FILE *pFile = NULL; fopen_s(&pFile, pFilename, pMode); return pFile; } static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) { FILE *pFile = NULL; if (freopen_s(&pFile, pPath, pMode, pStream)) return NULL; return pFile; } #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN mz_fopen #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 _ftelli64 #define MZ_FSEEK64 _fseeki64 #define MZ_FILE_STAT_STRUCT _stat64 #define MZ_FILE_STAT _stat64 #define MZ_FFLUSH fflush #define MZ_FREOPEN mz_freopen #define MZ_DELETE_FILE remove #elif defined(__MINGW32__) #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello64 #define MZ_FSEEK64 fseeko64 #define MZ_FILE_STAT_STRUCT _stat #define MZ_FILE_STAT _stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #elif defined(__TINYC__) #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftell #define MZ_FSEEK64 fseek #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #elif defined(__USE_LARGEFILE64) /* gcc, clang */ #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen64(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello64 #define MZ_FSEEK64 fseeko64 #define MZ_FILE_STAT_STRUCT stat64 #define MZ_FILE_STAT stat64 #define MZ_FFLUSH fflush #define MZ_FREOPEN(p, m, s) freopen64(p, m, s) #define MZ_DELETE_FILE remove #elif defined(__APPLE__) #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello #define MZ_FSEEK64 fseeko #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(p, m, s) freopen(p, m, s) #define MZ_DELETE_FILE remove #else #pragma message("Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.") #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #ifdef __STRICT_ANSI__ #define MZ_FTELL64 ftell #define MZ_FSEEK64 fseek #else #define MZ_FTELL64 ftello #define MZ_FSEEK64 fseeko #endif #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #endif /* #ifdef _MSC_VER */ #endif /* #ifdef MINIZ_NO_STDIO */ #define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) /* Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. */ enum { /* ZIP archive identifiers and record sizes */ MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, /* ZIP64 archive identifier and record sizes */ MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, /* Central directory header record offsets */ MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8, MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16, MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, /* Local directory header offsets */ MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10, MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3, /* End of central directory offsets */ MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, /* ZIP64 End of central directory locator offsets */ MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ /* ZIP64 End of central directory header offsets */ MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 }; typedef struct { void *m_p; size_t m_size, m_capacity; mz_uint m_element_size; } mz_zip_array; struct mz_zip_internal_state_tag { mz_zip_array m_central_dir; mz_zip_array m_central_dir_offsets; mz_zip_array m_sorted_central_dir_offsets; /* The flags passed in when the archive is initially opened. */ uint32_t m_init_flags; /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */ mz_bool m_zip64; /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 will also be slammed to true too, even if we didn't find a zip64 end of central dir header, etc.) */ mz_bool m_zip64_has_extended_info_fields; /* These fields are used by the file, FILE, memory, and memory/heap read/write helpers. */ MZ_FILE *m_pFile; mz_uint64 m_file_archive_start_ofs; void *m_pMem; size_t m_mem_size; size_t m_mem_capacity; }; #define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size #if defined(DEBUG) || defined(_DEBUG) static MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array *pArray, mz_uint index) { MZ_ASSERT(index < pArray->m_size); return index; } #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[mz_zip_array_range_check(array_ptr, index)] #else #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] #endif static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size) { memset(pArray, 0, sizeof(mz_zip_array)); pArray->m_element_size = element_size; } static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) { pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); memset(pArray, 0, sizeof(mz_zip_array)); } static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) { void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE; if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; } if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE; pArray->m_p = pNew_p; pArray->m_capacity = new_capacity; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) { if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; } return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) { if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; } pArray->m_size = new_size; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) { return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); } static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) { size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; if (n > 0) memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); return MZ_TRUE; } #ifndef MINIZ_NO_TIME static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date) { struct tm tm; memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31; tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62; return mktime(&tm); } #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS static void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) { #ifdef _MSC_VER struct tm tm_struct; struct tm *tm = &tm_struct; errno_t err = localtime_s(tm, &time); if (err) { *pDOS_date = 0; *pDOS_time = 0; return; } #else struct tm *tm = localtime(&time); #endif /* #ifdef _MSC_VER */ *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); } #endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ #ifndef MINIZ_NO_STDIO #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS static mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime) { struct MZ_FILE_STAT_STRUCT file_stat; /* On Linux with x86 glibc, this call will fail on large files (I think >= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ if (MZ_FILE_STAT(pFilename, &file_stat) != 0) return MZ_FALSE; *pTime = file_stat.st_mtime; return MZ_TRUE; } #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ static mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time) { struct utimbuf t; memset(&t, 0, sizeof(t)); t.actime = access_time; t.modtime = modified_time; return !utime(pFilename, &t); } #endif /* #ifndef MINIZ_NO_STDIO */ #endif /* #ifndef MINIZ_NO_TIME */ static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num) { if (pZip) pZip->m_last_error = err_num; return MZ_FALSE; } static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint flags) { (void)flags; if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!pZip->m_pAlloc) pZip->m_pAlloc = miniz_def_alloc_func; if (!pZip->m_pFree) pZip->m_pFree = miniz_def_free_func; if (!pZip->m_pRealloc) pZip->m_pRealloc = miniz_def_realloc_func; pZip->m_archive_size = 0; pZip->m_central_directory_file_ofs = 0; pZip->m_total_files = 0; pZip->m_last_error = MZ_ZIP_NO_ERROR; if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); pZip->m_pState->m_init_flags = flags; pZip->m_pState->m_zip64 = MZ_FALSE; pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE; pZip->m_zip_mode = MZ_ZIP_MODE_READING; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) { const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); mz_uint8 l = 0, r = 0; pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pE = pL + MZ_MIN(l_len, r_len); while (pL < pE) { if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) break; pL++; pR++; } return (pL == pE) ? (l_len < r_len) : (l < r); } #define MZ_SWAP_UINT32(a, b) \ do \ { \ mz_uint32 t = a; \ a = b; \ b = t; \ } \ MZ_MACRO_END /* Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) */ static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) { mz_zip_internal_state *pState = pZip->m_pState; const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; const mz_zip_array *pCentral_dir = &pState->m_central_dir; mz_uint32 *pIndices; mz_uint32 start, end; const mz_uint32 size = pZip->m_total_files; if (size <= 1U) return; pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); start = (size - 2U) >> 1U; for (;;) { mz_uint64 child, root = start; for (;;) { if ((child = (root << 1U) + 1U) >= size) break; child += (((child + 1U) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U]))); if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) break; MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; } if (!start) break; start--; } end = size - 1; while (end > 0) { mz_uint64 child, root = 0; MZ_SWAP_UINT32(pIndices[end], pIndices[0]); for (;;) { if ((child = (root << 1U) + 1U) >= end) break; child += (((child + 1U) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U])); if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) break; MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; } end--; } } static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, mz_uint32 record_sig, mz_uint32 record_size, mz_int64 *pOfs) { mz_int64 cur_file_ofs; mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; /* Basic sanity checks - reject files which are too small */ if (pZip->m_archive_size < record_size) return MZ_FALSE; /* Find the record by scanning the file from the end towards the beginning. */ cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); for (;;) { int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) return MZ_FALSE; for (i = n - 4; i >= 0; --i) { mz_uint s = MZ_READ_LE32(pBuf + i); if (s == record_sig) { if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) break; } } if (i >= 0) { cur_file_ofs += i; break; } /* Give up if we've searched the entire file, or we've gone back "too far" (~64kb) */ if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size))) return MZ_FALSE; cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); } *pOfs = cur_file_ofs; return MZ_TRUE; } static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags) { mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0; mz_uint64 cdir_ofs = 0; mz_int64 cur_file_ofs = 0; const mz_uint8 *p; mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); mz_uint32 zip64_end_of_central_dir_locator_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; mz_uint32 zip64_end_of_central_dir_header_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pZip64_end_of_central_dir = (mz_uint8 *)zip64_end_of_central_dir_header_u32; mz_uint64 zip64_end_of_central_dir_ofs = 0; /* Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. */ if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); if (!mz_zip_reader_locate_header_sig(pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); /* Read and verify the end of central directory record. */ if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) { if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) { if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) { zip64_end_of_central_dir_ofs = MZ_READ_LE64(pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); if (zip64_end_of_central_dir_ofs > (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) { if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) { pZip->m_pState->m_zip64 = MZ_TRUE; } } } } } pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); if (pZip->m_pState->m_zip64) { mz_uint32 zip64_total_num_of_disks = MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); mz_uint64 zip64_size_of_central_directory = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (zip64_total_num_of_disks != 1U) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); /* Check for miniz's practical limits */ if (zip64_cdir_total_entries > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk; /* Check for miniz's current practical limits (sorry, this should be enough for millions of files) */ if (zip64_size_of_central_directory > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); cdir_size = (mz_uint32)zip64_size_of_central_directory; num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); } if (pZip->m_total_files != cdir_entries_on_this_disk) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pZip->m_central_directory_file_ofs = cdir_ofs; if (pZip->m_total_files) { mz_uint i, n; /* Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and possibly another to hold the sorted indices. */ if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (sort_central_dir) { if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); /* Now create an index into the central directory file records, do some basic sanity checking on each record */ p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) { mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size; mz_uint64 comp_size, decomp_size, local_header_ofs; if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); if (sort_central_dir) MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && (ext_data_size) && (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX)) { /* Attempt to find zip64 extended information field in the entry's extra data */ mz_uint32 extra_size_remaining = ext_data_size; if (extra_size_remaining) { const mz_uint8 *pExtra_data; void* buf = NULL; if (MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + ext_data_size > n) { buf = MZ_MALLOC(ext_data_size); if(buf==NULL) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size, buf, ext_data_size) != ext_data_size) { MZ_FREE(buf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } pExtra_data = (mz_uint8*)buf; } else { pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; } do { mz_uint32 field_id; mz_uint32 field_data_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) { MZ_FREE(buf); return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) { MZ_FREE(buf); return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { /* Ok, the archive didn't have any zip64 headers but it uses a zip64 extended information field so mark it as zip64 anyway (this can occur with infozip's zip util when it reads compresses files from stdin). */ pZip->m_pState->m_zip64 = MZ_TRUE; pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; break; } pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; } while (extra_size_remaining); MZ_FREE(buf); } } /* I've seen archives that aren't marked as zip64 that uses zip64 ext data, argh */ if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) { if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1))) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); if (comp_size != MZ_UINT32_MAX) { if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); n -= total_header_size; p += total_header_size; } } if (sort_central_dir) mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); return MZ_TRUE; } void mz_zip_zero_struct(mz_zip_archive *pZip) { if (pZip) MZ_CLEAR_OBJ(*pZip); } static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) { mz_bool status = MZ_TRUE; if (!pZip) return MZ_FALSE; if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) { if (set_last_error) pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER; return MZ_FALSE; } if (pZip->m_pState) { mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL; mz_zip_array_clear(pZip, &pState->m_central_dir); mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); #ifndef MINIZ_NO_STDIO if (pState->m_pFile) { if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) { if (MZ_FCLOSE(pState->m_pFile) == EOF) { if (set_last_error) pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED; status = MZ_FALSE; } } pState->m_pFile = NULL; } #endif /* #ifndef MINIZ_NO_STDIO */ pZip->m_pFree(pZip->m_pAlloc_opaque, pState); } pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; return status; } mz_bool mz_zip_reader_end(mz_zip_archive *pZip) { return mz_zip_reader_end_internal(pZip, MZ_TRUE); } mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags) { if ((!pZip) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_zip_type = MZ_ZIP_TYPE_USER; pZip->m_archive_size = size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); return s; } mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags) { if (!pMem) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY; pZip->m_archive_size = size; pZip->m_pRead = mz_zip_mem_read_func; pZip->m_pIO_opaque = pZip; pZip->m_pNeeds_keepalive = NULL; #ifdef __cplusplus pZip->m_pState->m_pMem = const_cast(pMem); #else pZip->m_pState->m_pMem = (void *)pMem; #endif pZip->m_pState->m_mem_size = size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); file_ofs += pZip->m_pState->m_file_archive_start_ofs; if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) return 0; return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); } mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) { return mz_zip_reader_init_file_v2(pZip, pFilename, flags, 0, 0); } mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size) { mz_uint64 file_size; MZ_FILE *pFile; if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pFile = MZ_FOPEN(pFilename, "rb"); if (!pFile) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); file_size = archive_size; if (!file_size) { if (MZ_FSEEK64(pFile, 0, SEEK_END)) { MZ_FCLOSE(pFile); return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); } file_size = MZ_FTELL64(pFile); } /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) { MZ_FCLOSE(pFile); return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); } if (!mz_zip_reader_init_internal(pZip, flags)) { MZ_FCLOSE(pFile); return MZ_FALSE; } pZip->m_zip_type = MZ_ZIP_TYPE_FILE; pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; pZip->m_pState->m_pFile = pFile; pZip->m_archive_size = file_size; pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags) { mz_uint64 cur_file_ofs; if ((!pZip) || (!pFile)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); cur_file_ofs = MZ_FTELL64(pFile); if (!archive_size) { if (MZ_FSEEK64(pFile, 0, SEEK_END)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); archive_size = MZ_FTELL64(pFile) - cur_file_ofs; if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); } if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; pZip->m_pState->m_pFile = pFile; pZip->m_archive_size = archive_size; pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } #endif /* #ifndef MINIZ_NO_STDIO */ static MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index) { if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files)) return NULL; return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); } mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) { mz_uint m_bit_flag; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); if (!p) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return MZ_FALSE; } m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0; } mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index) { mz_uint bit_flag; mz_uint method; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); if (!p) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return MZ_FALSE; } method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); if ((method != 0) && (method != MZ_DEFLATED)) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); return MZ_FALSE; } if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); return MZ_FALSE; } if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); return MZ_FALSE; } return MZ_TRUE; } mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) { mz_uint filename_len, attribute_mapping_id, external_attr; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); if (!p) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return MZ_FALSE; } filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); if (filename_len) { if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') return MZ_TRUE; } /* Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. */ /* Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. */ /* FIXME: Remove this check? Is it necessary - we already check the filename. */ attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8; (void)attribute_mapping_id; external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0) { return MZ_TRUE; } return MZ_FALSE; } static mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_index, const mz_uint8 *pCentral_dir_header, mz_zip_archive_file_stat *pStat, mz_bool *pFound_zip64_extra_data) { mz_uint n; const mz_uint8 *p = pCentral_dir_header; if (pFound_zip64_extra_data) *pFound_zip64_extra_data = MZ_FALSE; if ((!p) || (!pStat)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Extract fields from the central directory record. */ pStat->m_file_index = file_index; pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); #ifndef MINIZ_NO_TIME pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); #endif pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); /* Copy as much of the filename and comment as possible. */ n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0'; n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); pStat->m_comment_size = n; memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0'; /* Set some flags for convienance */ pStat->m_is_directory = mz_zip_reader_is_file_a_directory(pZip, file_index); pStat->m_is_encrypted = mz_zip_reader_is_file_encrypted(pZip, file_index); pStat->m_is_supported = mz_zip_reader_is_file_supported(pZip, file_index); /* See if we need to read any zip64 extended information fields. */ /* Confusingly, these zip64 fields can be present even on non-zip64 archives (Debian zip on a huge files from stdin piped to stdout creates them). */ if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == MZ_UINT32_MAX) { /* Attempt to find zip64 extended information field in the entry's extra data */ mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); if (extra_size_remaining) { const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); do { mz_uint32 field_id; mz_uint32 field_data_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2; mz_uint32 field_data_remaining = field_data_size; if (pFound_zip64_extra_data) *pFound_zip64_extra_data = MZ_TRUE; if (pStat->m_uncomp_size == MZ_UINT32_MAX) { if (field_data_remaining < sizeof(mz_uint64)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pStat->m_uncomp_size = MZ_READ_LE64(pField_data); pField_data += sizeof(mz_uint64); field_data_remaining -= sizeof(mz_uint64); } if (pStat->m_comp_size == MZ_UINT32_MAX) { if (field_data_remaining < sizeof(mz_uint64)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pStat->m_comp_size = MZ_READ_LE64(pField_data); pField_data += sizeof(mz_uint64); field_data_remaining -= sizeof(mz_uint64); } if (pStat->m_local_header_ofs == MZ_UINT32_MAX) { if (field_data_remaining < sizeof(mz_uint64)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pStat->m_local_header_ofs = MZ_READ_LE64(pField_data); pField_data += sizeof(mz_uint64); field_data_remaining -= sizeof(mz_uint64); } break; } pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; } while (extra_size_remaining); } } return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) { mz_uint i; if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) return 0 == memcmp(pA, pB, len); for (i = 0; i < len; ++i) if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) return MZ_FALSE; return MZ_TRUE; } static MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) { const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); mz_uint8 l = 0, r = 0; pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pE = pL + MZ_MIN(l_len, r_len); while (pL < pE) { if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) break; pL++; pR++; } return (pL == pE) ? (int)(l_len - r_len) : (l - r); } static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex) { mz_zip_internal_state *pState = pZip->m_pState; const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; const mz_zip_array *pCentral_dir = &pState->m_central_dir; mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); const uint32_t size = pZip->m_total_files; const mz_uint filename_len = (mz_uint)strlen(pFilename); if (pIndex) *pIndex = 0; if (size) { /* yes I could use uint32_t's, but then we would have to add some special case checks in the loop, argh, and */ /* honestly the major expense here on 32-bit CPU's will still be the filename compare */ mz_int64 l = 0, h = (mz_int64)size - 1; while (l <= h) { mz_int64 m = l + ((h - l) >> 1); uint32_t file_index = pIndices[(uint32_t)m]; int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); if (!comp) { if (pIndex) *pIndex = file_index; return MZ_TRUE; } else if (comp < 0) l = m + 1; else h = m - 1; } } return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); } int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) { mz_uint32 index; if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index)) return -1; else return (int)index; } mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex) { mz_uint file_index; size_t name_len, comment_len; if (pIndex) *pIndex = 0; if ((!pZip) || (!pZip->m_pState) || (!pName)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* See if we can use a binary search */ if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) && (pZip->m_zip_mode == MZ_ZIP_MODE_READING) && ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) { return mz_zip_locate_file_binary_search(pZip, pName, pIndex); } /* Locate the entry by scanning the entire central directory */ name_len = strlen(pName); if (name_len > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); comment_len = pComment ? strlen(pComment) : 0; if (comment_len > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); for (file_index = 0; file_index < pZip->m_total_files; file_index++) { const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; if (filename_len < name_len) continue; if (comment_len) { mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); const char *pFile_comment = pFilename + filename_len + file_extra_len; if ((file_comment_len != comment_len) || (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags))) continue; } if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) { int ofs = filename_len - 1; do { if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) break; } while (--ofs >= 0); ofs++; pFilename += ofs; filename_len -= ofs; } if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags))) { if (pIndex) *pIndex = file_index; return MZ_TRUE; } } return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); } mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { int status = TINFL_STATUS_DONE; mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; mz_zip_archive_file_stat file_stat; void *pRead_buf; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; tinfl_decompressor inflator; if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; /* A directory or zero length file */ if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) return MZ_TRUE; /* Encryption and patch files are not supported. */ if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); /* This function only supports decompressing stored and deflate. */ if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); /* Ensure supplied output buffer is large enough. */ needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; if (buf_size < needed_size) return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL); /* Read and parse the local directory entry. */ cur_file_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { /* The file is stored or the caller has requested the compressed data. */ if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0) { if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); } #endif return MZ_TRUE; } /* Decompress the file either directly from memory or from a file input buffer. */ tinfl_init(&inflator); if (pZip->m_pState->m_pMem) { /* Read directly from the archive in memory. */ pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; read_buf_size = read_buf_avail = file_stat.m_comp_size; comp_remaining = 0; } else if (pUser_read_buf) { /* Use a user provided read buffer. */ if (!user_read_buf_size) return MZ_FALSE; pRead_buf = (mz_uint8 *)pUser_read_buf; read_buf_size = user_read_buf_size; read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } else { /* Temporarily allocate a read buffer. */ read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } do { /* The size_t cast here should be OK because we've verified that the output buffer is >= file_stat.m_uncomp_size above */ size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { status = TINFL_STATUS_FAILED; mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); break; } cur_file_ofs += read_buf_avail; comp_remaining -= read_buf_avail; read_buf_ofs = 0; } in_buf_size = (size_t)read_buf_avail; status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); read_buf_avail -= in_buf_size; read_buf_ofs += in_buf_size; out_buf_ofs += out_buf_size; } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); if (status == TINFL_STATUS_DONE) { /* Make sure the entire file was decompressed, and check its CRC. */ if (out_buf_ofs != file_stat.m_uncomp_size) { mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); status = TINFL_STATUS_FAILED; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) { mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); status = TINFL_STATUS_FAILED; } #endif } if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return status == TINFL_STATUS_DONE; } mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) return MZ_FALSE; return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); } mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) { return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); } mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) { return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); } void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) { mz_uint64 comp_size, uncomp_size, alloc_size; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); void *pBuf; if (pSize) *pSize = 0; if (!p) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return NULL; } comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) { mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); return NULL; } if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); return NULL; } if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return NULL; } if (pSize) *pSize = (size_t)alloc_size; return pBuf; } void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) { if (pSize) *pSize = 0; return MZ_FALSE; } return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); } mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) { int status = TINFL_STATUS_DONE; #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS mz_uint file_crc32 = MZ_CRC32_INIT; #endif mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; mz_zip_archive_file_stat file_stat; void *pRead_buf = NULL; void *pWrite_buf = NULL; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; /* A directory or zero length file */ if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) return MZ_TRUE; /* Encryption and patch files are not supported. */ if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); /* This function only supports decompressing stored and deflate. */ if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); /* Read and do some minimal validation of the local directory entry (this doesn't crack the zip64 stuff, which we already have from the central dir) */ cur_file_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); /* Decompress the file either directly from memory or from a file input buffer. */ if (pZip->m_pState->m_pMem) { pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; read_buf_size = read_buf_avail = file_stat.m_comp_size; comp_remaining = 0; } else { read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { /* The file is stored or the caller has requested the compressed data. */ if (pZip->m_pState->m_pMem) { if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) { mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); status = TINFL_STATUS_FAILED; } else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); #endif } cur_file_ofs += file_stat.m_comp_size; out_buf_ofs += file_stat.m_comp_size; comp_remaining = 0; } else { while (comp_remaining) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); status = TINFL_STATUS_FAILED; break; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); } #endif if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; out_buf_ofs += read_buf_avail; comp_remaining -= read_buf_avail; } } } else { tinfl_decompressor inflator; tinfl_init(&inflator); if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); status = TINFL_STATUS_FAILED; } else { do { mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; comp_remaining -= read_buf_avail; read_buf_ofs = 0; } in_buf_size = (size_t)read_buf_avail; status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); read_buf_avail -= in_buf_size; read_buf_ofs += in_buf_size; if (out_buf_size) { if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) { mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); status = TINFL_STATUS_FAILED; break; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); #endif if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) { mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); status = TINFL_STATUS_FAILED; break; } } } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); } } if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) { /* Make sure the entire file was decompressed, and check its CRC. */ if (out_buf_ofs != file_stat.m_uncomp_size) { mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); status = TINFL_STATUS_FAILED; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS else if (file_crc32 != file_stat.m_crc32) { mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); status = TINFL_STATUS_FAILED; } #endif } if (!pZip->m_pState->m_pMem) pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); if (pWrite_buf) pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); return status == TINFL_STATUS_DONE; } mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) return MZ_FALSE; return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); } mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) { mz_zip_reader_extract_iter_state *pState; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; /* Argument sanity check */ if ((!pZip) || (!pZip->m_pState)) return NULL; /* Allocate an iterator status structure */ pState = (mz_zip_reader_extract_iter_state*)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_reader_extract_iter_state)); if (!pState) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); return NULL; } /* Fetch file details */ if (!mz_zip_reader_file_stat(pZip, file_index, &pState->file_stat)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } /* Encryption and patch files are not supported. */ if (pState->file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } /* This function only supports decompressing stored and deflate. */ if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (pState->file_stat.m_method != 0) && (pState->file_stat.m_method != MZ_DEFLATED)) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } /* Init state - save args */ pState->pZip = pZip; pState->flags = flags; /* Init state - reset variables to defaults */ pState->status = TINFL_STATUS_DONE; #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS pState->file_crc32 = MZ_CRC32_INIT; #endif pState->read_buf_ofs = 0; pState->out_buf_ofs = 0; pState->pRead_buf = NULL; pState->pWrite_buf = NULL; pState->out_blk_remain = 0; /* Read and parse the local directory entry. */ pState->cur_file_ofs = pState->file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } pState->cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > pZip->m_archive_size) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } /* Decompress the file either directly from memory or from a file input buffer. */ if (pZip->m_pState->m_pMem) { pState->pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + pState->cur_file_ofs; pState->read_buf_size = pState->read_buf_avail = pState->file_stat.m_comp_size; pState->comp_remaining = pState->file_stat.m_comp_size; } else { if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) { /* Decompression required, therefore intermediate read buffer required */ pState->read_buf_size = MZ_MIN(pState->file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); if (NULL == (pState->pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)pState->read_buf_size))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } } else { /* Decompression not required - we will be reading directly into user buffer, no temp buf required */ pState->read_buf_size = 0; } pState->read_buf_avail = 0; pState->comp_remaining = pState->file_stat.m_comp_size; } if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) { /* Decompression required, init decompressor */ tinfl_init( &pState->inflator ); /* Allocate write buffer */ if (NULL == (pState->pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (pState->pRead_buf) pZip->m_pFree(pZip->m_pAlloc_opaque, pState->pRead_buf); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } } return pState; } mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) { mz_uint32 file_index; /* Locate file index by name */ if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) return NULL; /* Construct iterator */ return mz_zip_reader_extract_iter_new(pZip, file_index, flags); } size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size) { size_t copied_to_caller = 0; /* Argument sanity check */ if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState) || (!pvBuf)) return 0; if ((pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)) { /* The file is stored or the caller has requested the compressed data, calc amount to return. */ copied_to_caller = (size_t)MZ_MIN( buf_size, pState->comp_remaining ); /* Zip is in memory....or requires reading from a file? */ if (pState->pZip->m_pState->m_pMem) { /* Copy data to caller's buffer */ memcpy( pvBuf, pState->pRead_buf, copied_to_caller ); pState->pRead_buf = ((mz_uint8*)pState->pRead_buf) + copied_to_caller; } else { /* Read directly into caller's buffer */ if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pvBuf, copied_to_caller) != copied_to_caller) { /* Failed to read all that was asked for, flag failure and alert user */ mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); pState->status = TINFL_STATUS_FAILED; copied_to_caller = 0; } } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS /* Compute CRC if not returning compressed data only */ if (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, (const mz_uint8 *)pvBuf, copied_to_caller); #endif /* Advance offsets, dec counters */ pState->cur_file_ofs += copied_to_caller; pState->out_buf_ofs += copied_to_caller; pState->comp_remaining -= copied_to_caller; } else { do { /* Calc ptr to write buffer - given current output pos and block size */ mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pState->pWrite_buf + (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); /* Calc max output size - given current output pos and block size */ size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); if (!pState->out_blk_remain) { /* Read more data from file if none available (and reading from file) */ if ((!pState->read_buf_avail) && (!pState->pZip->m_pState->m_pMem)) { /* Calc read size */ pState->read_buf_avail = MZ_MIN(pState->read_buf_size, pState->comp_remaining); if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pState->pRead_buf, (size_t)pState->read_buf_avail) != pState->read_buf_avail) { mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); pState->status = TINFL_STATUS_FAILED; break; } /* Advance offsets, dec counters */ pState->cur_file_ofs += pState->read_buf_avail; pState->comp_remaining -= pState->read_buf_avail; pState->read_buf_ofs = 0; } /* Perform decompression */ in_buf_size = (size_t)pState->read_buf_avail; pState->status = tinfl_decompress(&pState->inflator, (const mz_uint8 *)pState->pRead_buf + pState->read_buf_ofs, &in_buf_size, (mz_uint8 *)pState->pWrite_buf, pWrite_buf_cur, &out_buf_size, pState->comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); pState->read_buf_avail -= in_buf_size; pState->read_buf_ofs += in_buf_size; /* Update current output block size remaining */ pState->out_blk_remain = out_buf_size; } if (pState->out_blk_remain) { /* Calc amount to return. */ size_t to_copy = MZ_MIN( (buf_size - copied_to_caller), pState->out_blk_remain ); /* Copy data to caller's buffer */ memcpy( (uint8_t*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy ); #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS /* Perform CRC */ pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, pWrite_buf_cur, to_copy); #endif /* Decrement data consumed from block */ pState->out_blk_remain -= to_copy; /* Inc output offset, while performing sanity check */ if ((pState->out_buf_ofs += to_copy) > pState->file_stat.m_uncomp_size) { mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); pState->status = TINFL_STATUS_FAILED; break; } /* Increment counter of data copied to caller */ copied_to_caller += to_copy; } } while ( (copied_to_caller < buf_size) && ((pState->status == TINFL_STATUS_NEEDS_MORE_INPUT) || (pState->status == TINFL_STATUS_HAS_MORE_OUTPUT)) ); } /* Return how many bytes were copied into user buffer */ return copied_to_caller; } mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState) { int status; /* Argument sanity check */ if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState)) return MZ_FALSE; /* Was decompression completed and requested? */ if ((pState->status == TINFL_STATUS_DONE) && (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) { /* Make sure the entire file was decompressed, and check its CRC. */ if (pState->out_buf_ofs != pState->file_stat.m_uncomp_size) { mz_zip_set_error(pState->pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); pState->status = TINFL_STATUS_FAILED; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS else if (pState->file_crc32 != pState->file_stat.m_crc32) { mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); pState->status = TINFL_STATUS_FAILED; } #endif } /* Free buffers */ if (!pState->pZip->m_pState->m_pMem) pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pRead_buf); if (pState->pWrite_buf) pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pWrite_buf); /* Save status */ status = pState->status; /* Free context */ pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState); return status == TINFL_STATUS_DONE; } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) { (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque); } mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) { mz_bool status; mz_zip_archive_file_stat file_stat; MZ_FILE *pFile; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); pFile = MZ_FOPEN(pDst_filename, "wb"); if (!pFile) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); if (MZ_FCLOSE(pFile) == EOF) { if (status) mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); status = MZ_FALSE; } #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) if (status) mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); #endif return status; } mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) return MZ_FALSE; return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); } mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags) { mz_zip_archive_file_stat file_stat; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); return mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); } mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) return MZ_FALSE; return mz_zip_reader_extract_to_cfile(pZip, file_index, pFile, flags); } #endif /* #ifndef MINIZ_NO_STDIO */ static size_t mz_zip_compute_crc32_callback(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_uint32 *p = (mz_uint32 *)pOpaque; (void)file_ofs; *p = (mz_uint32)mz_crc32(*p, (const mz_uint8 *)pBuf, n); return n; } mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) { mz_zip_archive_file_stat file_stat; mz_zip_internal_state *pState; const mz_uint8 *pCentral_dir_header; mz_bool found_zip64_ext_data_in_cdir = MZ_FALSE; mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; mz_uint64 local_header_ofs = 0; mz_uint32 local_header_filename_len, local_header_extra_len, local_header_crc32; mz_uint64 local_header_comp_size, local_header_uncomp_size; mz_uint32 uncomp_crc32 = MZ_CRC32_INIT; mz_bool has_data_descriptor; mz_uint32 local_header_bit_flags; mz_zip_array file_data_array; mz_zip_array_init(&file_data_array, 1); if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (file_index > pZip->m_total_files) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; pCentral_dir_header = mz_zip_get_cdh(pZip, file_index); if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, &found_zip64_ext_data_in_cdir)) return MZ_FALSE; /* A directory or zero length file */ if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size)) return MZ_TRUE; /* Encryption and patch files are not supported. */ if (file_stat.m_is_encrypted) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); /* This function only supports stored and deflate. */ if ((file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); if (!file_stat.m_is_supported) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); /* Read and parse the local directory entry. */ local_header_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); local_header_filename_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); local_header_crc32 = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_CRC32_OFS); local_header_bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); has_data_descriptor = (local_header_bit_flags & 8) != 0; if (local_header_filename_len != strlen(file_stat.m_filename)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if ((local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (!mz_zip_array_resize(pZip, &file_data_array, MZ_MAX(local_header_filename_len, local_header_extra_len), MZ_FALSE)) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); goto handle_failure; } if (local_header_filename_len) { if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE, file_data_array.m_p, local_header_filename_len) != local_header_filename_len) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); goto handle_failure; } /* I've seen 1 archive that had the same pathname, but used backslashes in the local dir and forward slashes in the central dir. Do we care about this? For now, this case will fail validation. */ if (memcmp(file_stat.m_filename, file_data_array.m_p, local_header_filename_len) != 0) { mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); goto handle_failure; } } if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) { mz_uint32 extra_size_remaining = local_header_extra_len; const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p; if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); goto handle_failure; } do { mz_uint32 field_id, field_data_size, field_total_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); goto handle_failure; } field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); field_total_size = field_data_size + sizeof(mz_uint16) * 2; if (field_total_size > extra_size_remaining) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); goto handle_failure; } if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); if (field_data_size < sizeof(mz_uint64) * 2) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); goto handle_failure; } local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); found_zip64_ext_data_in_ldir = MZ_TRUE; break; } pExtra_data += field_total_size; extra_size_remaining -= field_total_size; } while (extra_size_remaining); } /* TODO: parse local header extra data when local_header_comp_size is 0xFFFFFFFF! (big_descriptor.zip) */ /* I've seen zips in the wild with the data descriptor bit set, but proper local header values and bogus data descriptors */ if ((has_data_descriptor) && (!local_header_comp_size) && (!local_header_crc32)) { mz_uint8 descriptor_buf[32]; mz_bool has_id; const mz_uint8 *pSrc; mz_uint32 file_crc32; mz_uint64 comp_size = 0, uncomp_size = 0; mz_uint32 num_descriptor_uint32s = ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4; if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size, descriptor_buf, sizeof(mz_uint32) * num_descriptor_uint32s) != (sizeof(mz_uint32) * num_descriptor_uint32s)) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); goto handle_failure; } has_id = (MZ_READ_LE32(descriptor_buf) == MZ_ZIP_DATA_DESCRIPTOR_ID); pSrc = has_id ? (descriptor_buf + sizeof(mz_uint32)) : descriptor_buf; file_crc32 = MZ_READ_LE32(pSrc); if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) { comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32)); uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64)); } else { comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32)); uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32)); } if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || (uncomp_size != file_stat.m_uncomp_size)) { mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); goto handle_failure; } } else { if ((local_header_crc32 != file_stat.m_crc32) || (local_header_comp_size != file_stat.m_comp_size) || (local_header_uncomp_size != file_stat.m_uncomp_size)) { mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); goto handle_failure; } } mz_zip_array_clear(pZip, &file_data_array); if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0) { if (!mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_compute_crc32_callback, &uncomp_crc32, 0)) return MZ_FALSE; /* 1 more check to be sure, although the extract checks too. */ if (uncomp_crc32 != file_stat.m_crc32) { mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); return MZ_FALSE; } } return MZ_TRUE; handle_failure: mz_zip_array_clear(pZip, &file_data_array); return MZ_FALSE; } mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags) { mz_zip_internal_state *pState; uint32_t i; if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; /* Basic sanity checks */ if (!pState->m_zip64) { if (pZip->m_total_files > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (pZip->m_archive_size > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); } else { if (pZip->m_total_files >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); } for (i = 0; i < pZip->m_total_files; i++) { if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags) { mz_uint32 found_index; mz_zip_archive_file_stat stat; if (!mz_zip_reader_file_stat(pZip, i, &stat)) return MZ_FALSE; if (!mz_zip_reader_locate_file_v2(pZip, stat.m_filename, NULL, 0, &found_index)) return MZ_FALSE; /* This check can fail if there are duplicate filenames in the archive (which we don't check for when writing - that's up to the user) */ if (found_index != i) return mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); } if (!mz_zip_validate_file(pZip, i, flags)) return MZ_FALSE; } return MZ_TRUE; } mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr) { mz_bool success = MZ_TRUE; mz_zip_archive zip; mz_zip_error actual_err = MZ_ZIP_NO_ERROR; if ((!pMem) || (!size)) { if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; return MZ_FALSE; } mz_zip_zero_struct(&zip); if (!mz_zip_reader_init_mem(&zip, pMem, size, flags)) { if (pErr) *pErr = zip.m_last_error; return MZ_FALSE; } if (!mz_zip_validate_archive(&zip, flags)) { actual_err = zip.m_last_error; success = MZ_FALSE; } if (!mz_zip_reader_end_internal(&zip, success)) { if (!actual_err) actual_err = zip.m_last_error; success = MZ_FALSE; } if (pErr) *pErr = actual_err; return success; } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr) { mz_bool success = MZ_TRUE; mz_zip_archive zip; mz_zip_error actual_err = MZ_ZIP_NO_ERROR; if (!pFilename) { if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; return MZ_FALSE; } mz_zip_zero_struct(&zip); if (!mz_zip_reader_init_file_v2(&zip, pFilename, flags, 0, 0)) { if (pErr) *pErr = zip.m_last_error; return MZ_FALSE; } if (!mz_zip_validate_archive(&zip, flags)) { actual_err = zip.m_last_error; success = MZ_FALSE; } if (!mz_zip_reader_end_internal(&zip, success)) { if (!actual_err) actual_err = zip.m_last_error; success = MZ_FALSE; } if (pErr) *pErr = actual_err; return success; } #endif /* #ifndef MINIZ_NO_STDIO */ /* ------------------- .ZIP archive writing */ #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); } static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); } static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v) { mz_write_le32(p, (mz_uint32)v); mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32)); } #define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) #define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) #define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v)) static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_zip_internal_state *pState = pZip->m_pState; mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); if (!n) return 0; /* An allocation this big is likely to just fail on 32-bit systems, so don't even go there. */ if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)) { mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); return 0; } if (new_size > pState->m_mem_capacity) { void *pNew_block; size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2; if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); return 0; } pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity; } memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); pState->m_mem_size = (size_t)new_size; return n; } static mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) { mz_zip_internal_state *pState; mz_bool status = MZ_TRUE; if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) { if (set_last_error) mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return MZ_FALSE; } pState = pZip->m_pState; pZip->m_pState = NULL; mz_zip_array_clear(pZip, &pState->m_central_dir); mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); #ifndef MINIZ_NO_STDIO if (pState->m_pFile) { if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) { if (MZ_FCLOSE(pState->m_pFile) == EOF) { if (set_last_error) mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); status = MZ_FALSE; } } pState->m_pFile = NULL; } #endif /* #ifndef MINIZ_NO_STDIO */ if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); pState->m_pMem = NULL; } pZip->m_pFree(pZip->m_pAlloc_opaque, pState); pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; return status; } mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags) { mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0; if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) { if (!pZip->m_pRead) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } if (pZip->m_file_offset_alignment) { /* Ensure user specified file offset alignment is a power of 2. */ if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } if (!pZip->m_pAlloc) pZip->m_pAlloc = miniz_def_alloc_func; if (!pZip->m_pFree) pZip->m_pFree = miniz_def_free_func; if (!pZip->m_pRealloc) pZip->m_pRealloc = miniz_def_realloc_func; pZip->m_archive_size = existing_size; pZip->m_central_directory_file_ofs = 0; pZip->m_total_files = 0; if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); pZip->m_pState->m_zip64 = zip64; pZip->m_pState->m_zip64_has_extended_info_fields = zip64; pZip->m_zip_type = MZ_ZIP_TYPE_USER; pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; return MZ_TRUE; } mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) { return mz_zip_writer_init_v2(pZip, existing_size, 0); } mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags) { pZip->m_pWrite = mz_zip_heap_write_func; pZip->m_pNeeds_keepalive = NULL; if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) pZip->m_pRead = mz_zip_mem_read_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) return MZ_FALSE; pZip->m_zip_type = MZ_ZIP_TYPE_HEAP; if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) { if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) { mz_zip_writer_end_internal(pZip, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } pZip->m_pState->m_mem_capacity = initial_allocation_size; } return MZ_TRUE; } mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) { return mz_zip_writer_init_heap_v2(pZip, size_to_reserve_at_beginning, initial_allocation_size, 0); } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); file_ofs += pZip->m_pState->m_file_archive_start_ofs; if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) { mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); return 0; } return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); } mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) { return mz_zip_writer_init_file_v2(pZip, pFilename, size_to_reserve_at_beginning, 0); } mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags) { MZ_FILE *pFile; pZip->m_pWrite = mz_zip_file_write_func; pZip->m_pNeeds_keepalive = NULL; if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) return MZ_FALSE; if (NULL == (pFile = MZ_FOPEN(pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb"))) { mz_zip_writer_end(pZip); return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); } pZip->m_pState->m_pFile = pFile; pZip->m_zip_type = MZ_ZIP_TYPE_FILE; if (size_to_reserve_at_beginning) { mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf); do { size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) { mz_zip_writer_end(pZip); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_ofs += n; size_to_reserve_at_beginning -= n; } while (size_to_reserve_at_beginning); } return MZ_TRUE; } mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags) { pZip->m_pWrite = mz_zip_file_write_func; pZip->m_pNeeds_keepalive = NULL; if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init_v2(pZip, 0, flags)) return MZ_FALSE; pZip->m_pState->m_pFile = pFile; pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; return MZ_TRUE; } #endif /* #ifndef MINIZ_NO_STDIO */ mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) { mz_zip_internal_state *pState; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) { /* We don't support converting a non-zip64 file to zip64 - this seems like more trouble than it's worth. (What about the existing 32-bit data descriptors that could follow the compressed data?) */ if (!pZip->m_pState->m_zip64) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } /* No sense in trying to write to an archive that's already at the support max size */ if (pZip->m_pState->m_zip64) { if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if (pZip->m_total_files == MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); } pState = pZip->m_pState; if (pState->m_pFile) { #ifdef MINIZ_NO_STDIO (void)pFilename; return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); #else if (pZip->m_pIO_opaque != pZip) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) { if (!pFilename) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Archive is being read from stdio and was originally opened only for reading. Try to reopen as writable. */ if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) { /* The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. */ mz_zip_reader_end_internal(pZip, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); } } pZip->m_pWrite = mz_zip_file_write_func; pZip->m_pNeeds_keepalive = NULL; #endif /* #ifdef MINIZ_NO_STDIO */ } else if (pState->m_pMem) { /* Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. */ if (pZip->m_pIO_opaque != pZip) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState->m_mem_capacity = pState->m_mem_size; pZip->m_pWrite = mz_zip_heap_write_func; pZip->m_pNeeds_keepalive = NULL; } /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */ else if (!pZip->m_pWrite) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Start writing new files at the archive's current central directory location. */ /* TODO: We could add a flag that lets the user start writing immediately AFTER the existing central dir - this would be safer. */ pZip->m_archive_size = pZip->m_central_directory_file_ofs; pZip->m_central_directory_file_ofs = 0; /* Clear the sorted central dir offsets, they aren't useful or maintained now. */ /* Even though we're now in write mode, files can still be extracted and verified, but file locates will be slow. */ /* TODO: We could easily maintain the sorted central directory offsets. */ mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; return MZ_TRUE; } mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) { return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0); } /* TODO: pArchive_name is a terrible name here! */ mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) { return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); } typedef struct { mz_zip_archive *m_pZip; mz_uint64 m_cur_archive_file_ofs; mz_uint64 m_comp_size; } mz_zip_writer_add_state; static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser) { mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) return MZ_FALSE; pState->m_cur_archive_file_ofs += len; pState->m_comp_size += len; return MZ_TRUE; } #define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2) #define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3) static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 *pUncomp_size, mz_uint64 *pComp_size, mz_uint64 *pLocal_header_ofs) { mz_uint8 *pDst = pBuf; mz_uint32 field_size = 0; MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); MZ_WRITE_LE16(pDst + 2, 0); pDst += sizeof(mz_uint16) * 2; if (pUncomp_size) { MZ_WRITE_LE64(pDst, *pUncomp_size); pDst += sizeof(mz_uint64); field_size += sizeof(mz_uint64); } if (pComp_size) { MZ_WRITE_LE64(pDst, *pComp_size); pDst += sizeof(mz_uint64); field_size += sizeof(mz_uint64); } if (pLocal_header_ofs) { MZ_WRITE_LE64(pDst, *pLocal_header_ofs); pDst += sizeof(mz_uint64); field_size += sizeof(mz_uint64); } MZ_WRITE_LE16(pBuf + 2, field_size); return (mz_uint32)(pDst - pBuf); } static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) { (void)pZip; memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); return MZ_TRUE; } static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) { (void)pZip; memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX)); return MZ_TRUE; } static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes, const char *user_extra_data, mz_uint user_extra_data_len) { mz_zip_internal_state *pState = pZip->m_pState; mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; size_t orig_central_dir_size = pState->m_central_dir.m_size; mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; if (!pZip->m_pState->m_zip64) { if (local_header_ofs > 0xFFFFFFFF) return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); } /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + user_extra_data_len + comment_size) >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, (mz_uint16)(extra_size + user_extra_data_len), comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, user_extra_data, user_extra_data_len)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) { /* Try to resize the central directory array back into its original state. */ mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } return MZ_TRUE; } static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) { /* Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. */ if (*pArchive_name == '/') return MZ_FALSE; /* Making sure the name does not contain drive letters or DOS style backward slashes is the responsibility of the program using miniz*/ return MZ_TRUE; } static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) { mz_uint32 n; if (!pZip->m_file_offset_alignment) return 0; n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1)); } static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) { char buf[4096]; memset(buf, 0, MZ_MIN(sizeof(buf), n)); while (n) { mz_uint32 s = MZ_MIN(sizeof(buf), n); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_file_ofs += s; n -= s; } return MZ_TRUE; } mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) { return mz_zip_writer_add_mem_ex_v2(pZip, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, uncomp_size, uncomp_crc32, NULL, NULL, 0, NULL, 0); } mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) { mz_uint16 method = 0, dos_time = 0, dos_date = 0; mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; size_t archive_name_size; mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; tdefl_compressor *pComp = NULL; mz_bool store_data_uncompressed; mz_zip_internal_state *pState; mz_uint8 *pExtra_data = NULL; mz_uint32 extra_size = 0; mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; mz_uint16 bit_flags = 0; if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; if (uncomp_size || (buf_size && !(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) bit_flags |= MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) bit_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; level = level_and_flags & 0xF; store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; if (pState->m_zip64) { if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if (pZip->m_total_files == MZ_UINT16_MAX) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ } if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ } } if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_writer_validate_archive_name(pArchive_name)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); #ifndef MINIZ_NO_TIME if (last_modified != NULL) { mz_zip_time_t_to_dos_time(*last_modified, &dos_time, &dos_date); } else { MZ_TIME_T cur_time; time(&cur_time); mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); } #endif /* #ifndef MINIZ_NO_TIME */ if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); uncomp_size = buf_size; if (uncomp_size <= 3) { level = 0; store_data_uncompressed = MZ_TRUE; } } archive_name_size = strlen(pArchive_name); if (archive_name_size > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); if (!pState->m_zip64) { /* Bail early if the archive would obviously become too large */ if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + user_extra_data_central_len + MZ_ZIP_DATA_DESCRIPTER_SIZE32) > 0xFFFFFFFF) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ } } if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) { /* Set DOS Subdirectory attribute bit. */ ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; /* Subdirectories cannot contain data. */ if ((buf_size) || (uncomp_size)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } /* Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) */ if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if ((!store_data_uncompressed) && (buf_size)) { if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return MZ_FALSE; } local_dir_header_ofs += num_alignment_padding_bytes; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } cur_archive_file_ofs += num_alignment_padding_bytes; MZ_CLEAR_OBJ(local_dir_header); if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { method = MZ_DEFLATED; } if (pState->m_zip64) { if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) { pExtra_data = extra_data; extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, bit_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += archive_name_size; if (pExtra_data != NULL) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += extra_size; } } else { if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += archive_name_size; } if (user_extra_data_len > 0) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += user_extra_data_len; } if (store_data_uncompressed) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += buf_size; comp_size = buf_size; } else if (buf_size) { mz_zip_writer_add_state state; state.m_pZip = pZip; state.m_cur_archive_file_ofs = cur_archive_file_ofs; state.m_comp_size = 0; if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); } comp_size = state.m_comp_size; cur_archive_file_ofs = state.m_cur_archive_file_ofs; } pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); pComp = NULL; if (uncomp_size) { mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; MZ_ASSERT(bit_flags & MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR); MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); if (pExtra_data == NULL) { if (comp_size > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); MZ_WRITE_LE32(local_dir_footer + 8, comp_size); MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); } else { MZ_WRITE_LE64(local_dir_footer + 8, comp_size); MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) return MZ_FALSE; cur_archive_file_ofs += local_dir_footer_size; } if (pExtra_data != NULL) { extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, user_extra_data_central, user_extra_data_central_len)) return MZ_FALSE; pZip->m_total_files++; pZip->m_archive_size = cur_archive_file_ofs; return MZ_TRUE; } mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) { mz_uint16 gen_flags = (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) ? 0 : MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; size_t archive_name_size; mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; mz_uint8 *pExtra_data = NULL; mz_uint32 extra_size = 0; mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; mz_zip_internal_state *pState; mz_uint64 file_ofs = 0, cur_archive_header_file_ofs; if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; level = level_and_flags & 0xF; /* Sanity checks */ if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; if ((!pState->m_zip64) && (max_size > MZ_UINT32_MAX)) { /* Source file is too large for non-zip64 */ /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ pState->m_zip64 = MZ_TRUE; } /* We could support this, but why? */ if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_writer_validate_archive_name(pArchive_name)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); if (pState->m_zip64) { if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if (pZip->m_total_files == MZ_UINT16_MAX) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ } } archive_name_size = strlen(pArchive_name); if (archive_name_size > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); if (!pState->m_zip64) { /* Bail early if the archive would obviously become too large */ if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 1024 + MZ_ZIP_DATA_DESCRIPTER_SIZE32 + user_extra_data_central_len) > 0xFFFFFFFF) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ } } #ifndef MINIZ_NO_TIME if (pFile_time) { mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date); } #endif if (max_size <= 3) level = 0; if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) { return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += num_alignment_padding_bytes; local_dir_header_ofs = cur_archive_file_ofs; if (pZip->m_file_offset_alignment) { MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } if (max_size && level) { method = MZ_DEFLATED; } MZ_CLEAR_OBJ(local_dir_header); if (pState->m_zip64) { if (max_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) { pExtra_data = extra_data; if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); else extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, NULL, NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, gen_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += archive_name_size; if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += extra_size; } else { if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += archive_name_size; } if (user_extra_data_len > 0) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += user_extra_data_len; } if (max_size) { void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); if (!pRead_buf) { return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!level) { while (1) { size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE); if (n == 0) break; if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } file_ofs += n; uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); cur_archive_file_ofs += n; } uncomp_size = file_ofs; comp_size = uncomp_size; } else { mz_bool result = MZ_FALSE; mz_zip_writer_add_state state; tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); if (!pComp) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } state.m_pZip = pZip; state.m_cur_archive_file_ofs = cur_archive_file_ofs; state.m_comp_size = 0; if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); } for (;;) { tdefl_status status; tdefl_flush flush = TDEFL_NO_FLUSH; size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE); if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size)) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); break; } file_ofs += n; uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque)) flush = TDEFL_FULL_FLUSH; if (n == 0) flush = TDEFL_FINISH; status = tdefl_compress_buffer(pComp, pRead_buf, n, flush); if (status == TDEFL_STATUS_DONE) { result = MZ_TRUE; break; } else if (status != TDEFL_STATUS_OKAY) { mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); break; } } pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); if (!result) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return MZ_FALSE; } uncomp_size = file_ofs; comp_size = state.m_comp_size; cur_archive_file_ofs = state.m_cur_archive_file_ofs; } pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); } if (!(level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE)) { mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); if (pExtra_data == NULL) { if (comp_size > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); MZ_WRITE_LE32(local_dir_footer + 8, comp_size); MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); } else { MZ_WRITE_LE64(local_dir_footer + 8, comp_size); MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) return MZ_FALSE; cur_archive_file_ofs += local_dir_footer_size; } if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) { if (pExtra_data != NULL) { extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : uncomp_size, (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); cur_archive_header_file_ofs = local_dir_header_ofs; if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); if (pExtra_data != NULL) { cur_archive_header_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_header_file_ofs += archive_name_size; if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, extra_data, extra_size) != extra_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_header_file_ofs += extra_size; } } if (pExtra_data != NULL) { extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, user_extra_data_central, user_extra_data_central_len)) return MZ_FALSE; pZip->m_total_files++; pZip->m_archive_size = cur_archive_file_ofs; return MZ_TRUE; } #ifndef MINIZ_NO_STDIO static size_t mz_file_read_func_stdio(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { MZ_FILE *pSrc_file = (MZ_FILE *)pOpaque; mz_int64 cur_ofs = MZ_FTELL64(pSrc_file); if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pSrc_file, (mz_int64)file_ofs, SEEK_SET)))) return 0; return MZ_FREAD(pBuf, 1, n, pSrc_file); } mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) { return mz_zip_writer_add_read_buf_callback(pZip, pArchive_name, mz_file_read_func_stdio, pSrc_file, max_size, pFile_time, pComment, comment_size, level_and_flags, user_extra_data, user_extra_data_len, user_extra_data_central, user_extra_data_central_len); } mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) { MZ_FILE *pSrc_file = NULL; mz_uint64 uncomp_size = 0; MZ_TIME_T file_modified_time; MZ_TIME_T *pFile_time = NULL; mz_bool status; memset(&file_modified_time, 0, sizeof(file_modified_time)); #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) pFile_time = &file_modified_time; if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED); #endif pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); if (!pSrc_file) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); MZ_FSEEK64(pSrc_file, 0, SEEK_END); uncomp_size = MZ_FTELL64(pSrc_file); MZ_FSEEK64(pSrc_file, 0, SEEK_SET); status = mz_zip_writer_add_cfile(pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, pComment, comment_size, level_and_flags, NULL, 0, NULL, 0); MZ_FCLOSE(pSrc_file); return status; } #endif /* #ifndef MINIZ_NO_STDIO */ static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, uint32_t ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start) { /* + 64 should be enough for any new zip64 data */ if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); mz_zip_array_resize(pZip, pNew_ext, 0, MZ_FALSE); if ((pUncomp_size) || (pComp_size) || (pLocal_header_ofs) || (pDisk_start)) { mz_uint8 new_ext_block[64]; mz_uint8 *pDst = new_ext_block; mz_write_le16(pDst, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); mz_write_le16(pDst + sizeof(mz_uint16), 0); pDst += sizeof(mz_uint16) * 2; if (pUncomp_size) { mz_write_le64(pDst, *pUncomp_size); pDst += sizeof(mz_uint64); } if (pComp_size) { mz_write_le64(pDst, *pComp_size); pDst += sizeof(mz_uint64); } if (pLocal_header_ofs) { mz_write_le64(pDst, *pLocal_header_ofs); pDst += sizeof(mz_uint64); } if (pDisk_start) { mz_write_le32(pDst, *pDisk_start); pDst += sizeof(mz_uint32); } mz_write_le16(new_ext_block + sizeof(mz_uint16), (mz_uint16)((pDst - new_ext_block) - sizeof(mz_uint16) * 2)); if (!mz_zip_array_push_back(pZip, pNew_ext, new_ext_block, pDst - new_ext_block)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if ((pExt) && (ext_len)) { mz_uint32 extra_size_remaining = ext_len; const mz_uint8 *pExtra_data = pExt; do { mz_uint32 field_id, field_data_size, field_total_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); field_total_size = field_data_size + sizeof(mz_uint16) * 2; if (field_total_size > extra_size_remaining) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (field_id != MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { if (!mz_zip_array_push_back(pZip, pNew_ext, pExtra_data, field_total_size)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } pExtra_data += field_total_size; extra_size_remaining -= field_total_size; } while (extra_size_remaining); } return MZ_TRUE; } /* TODO: This func is now pretty freakin complex due to zip64, split it up? */ mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index) { mz_uint n, bit_flags, num_alignment_padding_bytes, src_central_dir_following_data_size; mz_uint64 src_archive_bytes_remaining, local_dir_header_ofs; mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; mz_uint8 new_central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; size_t orig_central_dir_size; mz_zip_internal_state *pState; void *pBuf; const mz_uint8 *pSrc_central_header; mz_zip_archive_file_stat src_file_stat; mz_uint32 src_filename_len, src_comment_len, src_ext_len; mz_uint32 local_header_filename_size, local_header_extra_len; mz_uint64 local_header_comp_size, local_header_uncomp_size; mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; /* Sanity checks */ if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pSource_zip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; /* Don't support copying files from zip64 archives to non-zip64, even though in some cases this is possible */ if ((pSource_zip->m_pState->m_zip64) && (!pZip->m_pState->m_zip64)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Get pointer to the source central dir header and crack it */ if (NULL == (pSrc_central_header = mz_zip_get_cdh(pSource_zip, src_file_index))) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_SIG_OFS) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); src_filename_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS); src_comment_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); src_ext_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS); src_central_dir_following_data_size = src_filename_len + src_ext_len + src_comment_len; /* TODO: We don't support central dir's >= MZ_UINT32_MAX bytes right now (+32 fudge factor in case we need to add more extra data) */ if ((pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + 32) >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); if (!pState->m_zip64) { if (pZip->m_total_files == MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { /* TODO: Our zip64 support still has some 32-bit limits that may not be worth fixing. */ if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } if (!mz_zip_file_stat_internal(pSource_zip, src_file_index, pSrc_central_header, &src_file_stat, NULL)) return MZ_FALSE; cur_src_file_ofs = src_file_stat.m_local_header_ofs; cur_dst_file_ofs = pZip->m_archive_size; /* Read the source archive's local dir header */ if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; /* Compute the total size we need to copy (filename+extra data+compressed data) */ local_header_filename_size = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); src_archive_bytes_remaining = local_header_filename_size + local_header_extra_len + src_file_stat.m_comp_size; /* Try to find a zip64 extended information field */ if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) { mz_zip_array file_data_array; const mz_uint8 *pExtra_data; mz_uint32 extra_size_remaining = local_header_extra_len; mz_zip_array_init(&file_data_array, 1); if (!mz_zip_array_resize(pZip, &file_data_array, local_header_extra_len, MZ_FALSE)) { return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, src_file_stat.m_local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_size, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) { mz_zip_array_clear(pZip, &file_data_array); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } pExtra_data = (const mz_uint8 *)file_data_array.m_p; do { mz_uint32 field_id, field_data_size, field_total_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) { mz_zip_array_clear(pZip, &file_data_array); return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); field_total_size = field_data_size + sizeof(mz_uint16) * 2; if (field_total_size > extra_size_remaining) { mz_zip_array_clear(pZip, &file_data_array); return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); if (field_data_size < sizeof(mz_uint64) * 2) { mz_zip_array_clear(pZip, &file_data_array); return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); /* may be 0 if there's a descriptor */ found_zip64_ext_data_in_ldir = MZ_TRUE; break; } pExtra_data += field_total_size; extra_size_remaining -= field_total_size; } while (extra_size_remaining); mz_zip_array_clear(pZip, &file_data_array); } if (!pState->m_zip64) { /* Try to detect if the new archive will most likely wind up too big and bail early (+(sizeof(mz_uint32) * 4) is for the optional descriptor which could be present, +64 is a fudge factor). */ /* We also check when the archive is finalized so this doesn't need to be perfect. */ mz_uint64 approx_new_archive_size = cur_dst_file_ofs + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + src_archive_bytes_remaining + (sizeof(mz_uint32) * 4) + pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 64; if (approx_new_archive_size >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); } /* Write dest archive padding */ if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) return MZ_FALSE; cur_dst_file_ofs += num_alignment_padding_bytes; local_dir_header_ofs = cur_dst_file_ofs; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } /* The original zip's local header+ext block doesn't change, even with zip64, so we can just copy it over to the dest zip */ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; /* Copy over the source archive bytes to the dest archive, also ensure we have enough buf space to handle optional data descriptor */ if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(32U, MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining))))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); while (src_archive_bytes_remaining) { n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining); if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } cur_src_file_ofs += n; if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_dst_file_ofs += n; src_archive_bytes_remaining -= n; } /* Now deal with the optional data descriptor */ bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); if (bit_flags & 8) { /* Copy data descriptor */ if ((pSource_zip->m_pState->m_zip64) || (found_zip64_ext_data_in_ldir)) { /* src is zip64, dest must be zip64 */ /* name uint32_t's */ /* id 1 (optional in zip64?) */ /* crc 1 */ /* comp_size 2 */ /* uncomp_size 2 */ if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, (sizeof(mz_uint32) * 6)) != (sizeof(mz_uint32) * 6)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID) ? 6 : 5); } else { /* src is NOT zip64 */ mz_bool has_id; if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } has_id = (MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID); if (pZip->m_pState->m_zip64) { /* dest is zip64, so upgrade the data descriptor */ const mz_uint32 *pSrc_descriptor = (const mz_uint32 *)((const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0)); const mz_uint32 src_crc32 = pSrc_descriptor[0]; const mz_uint64 src_comp_size = pSrc_descriptor[1]; const mz_uint64 src_uncomp_size = pSrc_descriptor[2]; mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID); mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32); mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 2, src_comp_size); mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 4, src_uncomp_size); n = sizeof(mz_uint32) * 6; } else { /* dest is NOT zip64, just copy it as-is */ n = sizeof(mz_uint32) * (has_id ? 4 : 3); } } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_src_file_ofs += n; cur_dst_file_ofs += n; } pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); /* Finally, add the new central dir header */ orig_central_dir_size = pState->m_central_dir.m_size; memcpy(new_central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); if (pState->m_zip64) { /* This is the painful part: We need to write a new central dir header + ext block with updated zip64 fields, and ensure the old fields (if any) are not included. */ const mz_uint8 *pSrc_ext = pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len; mz_zip_array new_ext_block; mz_zip_array_init(&new_ext_block, sizeof(mz_uint8)); MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_UINT32_MAX); MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_UINT32_MAX); MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_UINT32_MAX); if (!mz_zip_writer_update_zip64_extension_block(&new_ext_block, pZip, pSrc_ext, src_ext_len, &src_file_stat.m_comp_size, &src_file_stat.m_uncomp_size, &local_dir_header_ofs, NULL)) { mz_zip_array_clear(pZip, &new_ext_block); return MZ_FALSE; } MZ_WRITE_LE16(new_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS, new_ext_block.m_size); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) { mz_zip_array_clear(pZip, &new_ext_block); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_filename_len)) { mz_zip_array_clear(pZip, &new_ext_block); mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_ext_block.m_p, new_ext_block.m_size)) { mz_zip_array_clear(pZip, &new_ext_block); mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len + src_ext_len, src_comment_len)) { mz_zip_array_clear(pZip, &new_ext_block); mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } mz_zip_array_clear(pZip, &new_ext_block); } else { /* sanity checks */ if (cur_dst_file_ofs > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (local_dir_header_ofs >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_central_dir_following_data_size)) { mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } } /* This shouldn't trigger unless we screwed up during the initial sanity checks */ if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) { /* TODO: Support central dirs >= 32-bits in size */ mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); } n = (mz_uint32)orig_central_dir_size; if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) { mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } pZip->m_total_files++; pZip->m_archive_size = cur_dst_file_ofs; return MZ_TRUE; } mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) { mz_zip_internal_state *pState; mz_uint64 central_dir_ofs, central_dir_size; mz_uint8 hdr[256]; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; if (pState->m_zip64) { if ((pZip->m_total_files > MZ_UINT32_MAX) || (pState->m_central_dir.m_size >= MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if ((pZip->m_total_files > MZ_UINT16_MAX) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } central_dir_ofs = 0; central_dir_size = 0; if (pZip->m_total_files) { /* Write central directory */ central_dir_ofs = pZip->m_archive_size; central_dir_size = pState->m_central_dir.m_size; pZip->m_central_directory_file_ofs = central_dir_ofs; if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); pZip->m_archive_size += central_dir_size; } if (pState->m_zip64) { /* Write zip64 end of central directory header */ mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; MZ_CLEAR_OBJ(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64)); MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */ MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs); if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; /* Write zip64 end of central directory locator */ MZ_CLEAR_OBJ(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1); if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE; } /* Write end of central directory record */ MZ_CLEAR_OBJ(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size)); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs)); if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); #ifndef MINIZ_NO_STDIO if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); #endif /* #ifndef MINIZ_NO_STDIO */ pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE; pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; return MZ_TRUE; } mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize) { if ((!ppBuf) || (!pSize)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); *ppBuf = NULL; *pSize = 0; if ((!pZip) || (!pZip->m_pState)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (pZip->m_pWrite != mz_zip_heap_write_func) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_writer_finalize_archive(pZip)) return MZ_FALSE; *ppBuf = pZip->m_pState->m_pMem; *pSize = pZip->m_pState->m_mem_size; pZip->m_pState->m_pMem = NULL; pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; return MZ_TRUE; } mz_bool mz_zip_writer_end(mz_zip_archive *pZip) { return mz_zip_writer_end_internal(pZip, MZ_TRUE); } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) { return mz_zip_add_mem_to_archive_file_in_place_v2(pZip_filename, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, NULL); } mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr) { mz_bool status, created_new_archive = MZ_FALSE; mz_zip_archive zip_archive; struct MZ_FILE_STAT_STRUCT file_stat; mz_zip_error actual_err = MZ_ZIP_NO_ERROR; mz_zip_zero_struct(&zip_archive); if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) { if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; return MZ_FALSE; } if (!mz_zip_writer_validate_archive_name(pArchive_name)) { if (pErr) *pErr = MZ_ZIP_INVALID_FILENAME; return MZ_FALSE; } /* Important: The regular non-64 bit version of stat() can fail here if the file is very large, which could cause the archive to be overwritten. */ /* So be sure to compile with _LARGEFILE64_SOURCE 1 */ if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) { /* Create a new archive. */ if (!mz_zip_writer_init_file_v2(&zip_archive, pZip_filename, 0, level_and_flags)) { if (pErr) *pErr = zip_archive.m_last_error; return MZ_FALSE; } created_new_archive = MZ_TRUE; } else { /* Append to an existing archive. */ if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) { if (pErr) *pErr = zip_archive.m_last_error; return MZ_FALSE; } if (!mz_zip_writer_init_from_reader_v2(&zip_archive, pZip_filename, level_and_flags)) { if (pErr) *pErr = zip_archive.m_last_error; mz_zip_reader_end_internal(&zip_archive, MZ_FALSE); return MZ_FALSE; } } status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); actual_err = zip_archive.m_last_error; /* Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) */ if (!mz_zip_writer_finalize_archive(&zip_archive)) { if (!actual_err) actual_err = zip_archive.m_last_error; status = MZ_FALSE; } if (!mz_zip_writer_end_internal(&zip_archive, status)) { if (!actual_err) actual_err = zip_archive.m_last_error; status = MZ_FALSE; } if ((!status) && (created_new_archive)) { /* It's a new archive and something went wrong, so just delete it. */ int ignoredStatus = MZ_DELETE_FILE(pZip_filename); (void)ignoredStatus; } if (pErr) *pErr = actual_err; return status; } void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr) { mz_uint32 file_index; mz_zip_archive zip_archive; void *p = NULL; if (pSize) *pSize = 0; if ((!pZip_filename) || (!pArchive_name)) { if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; return NULL; } mz_zip_zero_struct(&zip_archive); if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) { if (pErr) *pErr = zip_archive.m_last_error; return NULL; } if (mz_zip_reader_locate_file_v2(&zip_archive, pArchive_name, pComment, flags, &file_index)) { p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); } mz_zip_reader_end_internal(&zip_archive, p != NULL); if (pErr) *pErr = zip_archive.m_last_error; return p; } void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) { return mz_zip_extract_archive_file_to_heap_v2(pZip_filename, pArchive_name, NULL, pSize, flags, NULL); } #endif /* #ifndef MINIZ_NO_STDIO */ #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ /* ------------------- Misc utils */ mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip) { return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID; } mz_zip_type mz_zip_get_type(mz_zip_archive *pZip) { return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID; } mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num) { mz_zip_error prev_err; if (!pZip) return MZ_ZIP_INVALID_PARAMETER; prev_err = pZip->m_last_error; pZip->m_last_error = err_num; return prev_err; } mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip) { if (!pZip) return MZ_ZIP_INVALID_PARAMETER; return pZip->m_last_error; } mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip) { return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR); } mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip) { mz_zip_error prev_err; if (!pZip) return MZ_ZIP_INVALID_PARAMETER; prev_err = pZip->m_last_error; pZip->m_last_error = MZ_ZIP_NO_ERROR; return prev_err; } const char *mz_zip_get_error_string(mz_zip_error mz_err) { switch (mz_err) { case MZ_ZIP_NO_ERROR: return "no error"; case MZ_ZIP_UNDEFINED_ERROR: return "undefined error"; case MZ_ZIP_TOO_MANY_FILES: return "too many files"; case MZ_ZIP_FILE_TOO_LARGE: return "file too large"; case MZ_ZIP_UNSUPPORTED_METHOD: return "unsupported method"; case MZ_ZIP_UNSUPPORTED_ENCRYPTION: return "unsupported encryption"; case MZ_ZIP_UNSUPPORTED_FEATURE: return "unsupported feature"; case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: return "failed finding central directory"; case MZ_ZIP_NOT_AN_ARCHIVE: return "not a ZIP archive"; case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: return "invalid header or archive is corrupted"; case MZ_ZIP_UNSUPPORTED_MULTIDISK: return "unsupported multidisk archive"; case MZ_ZIP_DECOMPRESSION_FAILED: return "decompression failed or archive is corrupted"; case MZ_ZIP_COMPRESSION_FAILED: return "compression failed"; case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: return "unexpected decompressed size"; case MZ_ZIP_CRC_CHECK_FAILED: return "CRC-32 check failed"; case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: return "unsupported central directory size"; case MZ_ZIP_ALLOC_FAILED: return "allocation failed"; case MZ_ZIP_FILE_OPEN_FAILED: return "file open failed"; case MZ_ZIP_FILE_CREATE_FAILED: return "file create failed"; case MZ_ZIP_FILE_WRITE_FAILED: return "file write failed"; case MZ_ZIP_FILE_READ_FAILED: return "file read failed"; case MZ_ZIP_FILE_CLOSE_FAILED: return "file close failed"; case MZ_ZIP_FILE_SEEK_FAILED: return "file seek failed"; case MZ_ZIP_FILE_STAT_FAILED: return "file stat failed"; case MZ_ZIP_INVALID_PARAMETER: return "invalid parameter"; case MZ_ZIP_INVALID_FILENAME: return "invalid filename"; case MZ_ZIP_BUF_TOO_SMALL: return "buffer too small"; case MZ_ZIP_INTERNAL_ERROR: return "internal error"; case MZ_ZIP_FILE_NOT_FOUND: return "file not found"; case MZ_ZIP_ARCHIVE_TOO_LARGE: return "archive is too large"; case MZ_ZIP_VALIDATION_FAILED: return "validation failed"; case MZ_ZIP_WRITE_CALLBACK_FAILED: return "write calledback failed"; default: break; } return "unknown error"; } /* Note: Just because the archive is not zip64 doesn't necessarily mean it doesn't have Zip64 extended information extra field, argh. */ mz_bool mz_zip_is_zip64(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState)) return MZ_FALSE; return pZip->m_pState->m_zip64; } size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState)) return 0; return pZip->m_pState->m_central_dir.m_size; } mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) { return pZip ? pZip->m_total_files : 0; } mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip) { if (!pZip) return 0; return pZip->m_archive_size; } mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState)) return 0; return pZip->m_pState->m_file_archive_start_ofs; } MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState)) return 0; return pZip->m_pState->m_pFile; } size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n) { if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n); } mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) { mz_uint n; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); if (!p) { if (filename_buf_size) pFilename[0] = '\0'; mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return 0; } n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); if (filename_buf_size) { n = MZ_MIN(n, filename_buf_size - 1); memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pFilename[n] = '\0'; } return n + 1; } mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) { return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL); } mz_bool mz_zip_end(mz_zip_archive *pZip) { if (!pZip) return MZ_FALSE; if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) return mz_zip_reader_end(pZip); #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)) return mz_zip_writer_end(pZip); #endif return MZ_FALSE; } #ifdef __cplusplus } #endif #endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/ ================================================ FILE: src/miniz/miniz.h ================================================ #define MINIZ_NO_TIME #define MINIZ_NO_ZLIB_APIS #define MINIZ_NO_MALLOC #define MINIZ_NO_ARCHIVE_APIS #define MINIZ_NO_STDIO #define MINIZ_NO_ARCHIVE_WRITING_APIS #define MINIZ_NO_ZLIB_COMPATIBLE_NAME #define MINIZ_NO_ZLIB_COMPATIBLE_NAMES #define MINIZ_EXPORT /* miniz.c 2.2.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing See "unlicense" statement at the end of this file. Rich Geldreich , last updated Oct. 13, 2013 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). * Low-level Deflate/Inflate implementation notes: Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses approximately as well as zlib. Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory block large enough to hold the entire file. The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. * zlib-style API notes: miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in zlib replacement in many apps: The z_stream struct, optional memory allocation callbacks deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound inflateInit/inflateInit2/inflate/inflateReset/inflateEnd compress, compress2, compressBound, uncompress CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. Supports raw deflate streams or standard zlib streams with adler-32 checking. Limitations: The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but there are no guarantees that miniz.c pulls this off perfectly. * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by Alex Evans. Supports 1-4 bytes/pixel images. * ZIP archive API notes: The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to get the job done with minimal fuss. There are simple API's to retrieve file information, read files from existing archives, create new archives, append new files to existing archives, or clone archive data from one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), or you can specify custom file read/write callbacks. - Archive reading: Just call this function to read a single file from a disk archive: void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); The locate operation can optionally check file comments too, which (as one example) can be used to identify multiple versions of the same file in an archive. This function uses a simple linear search through the central directory, so it's not very fast. Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and retrieve detailed info on each file by calling mz_zip_reader_file_stat(). - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data to disk and builds an exact image of the central directory in memory. The central directory image is written all at once at the end of the archive file when the archive is finalized. The archive writer can optionally align each file's local header and file data to any power of 2 alignment, which can be useful when the archive will be read from optical media. Also, the writer supports placing arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still readable by any ZIP tool. - Archive appending: The simple way to add a single file to an archive is to call this function: mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); The archive will be created if it doesn't already exist, otherwise it'll be appended to. Note the appending is done in-place and is not an atomic operation, so if something goes wrong during the operation it's possible the archive could be left without a central directory (although the local file headers and file data will be fine, so the archive will be recoverable). For more complex archive modification scenarios: 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and you're done. This is safe but requires a bunch of temporary disk space or heap memory. 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), append new files as needed, then finalize the archive which will write an updated central directory to the original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a possibility that the archive's central directory could be lost with this method if anything goes wrong, though. - ZIP archive support limitations: No spanning support. Extraction functions can only handle unencrypted, stored or deflated files. Requires streams capable of seeking. * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. * Important: For best perf. be sure to customize the below macros for your target platform: #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #define MINIZ_LITTLE_ENDIAN 1 #define MINIZ_HAS_64BIT_REGISTERS 1 * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). */ #pragma once /* Defines to completely disable specific portions of miniz.c: If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. */ /* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */ /*#define MINIZ_NO_STDIO */ /* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or */ /* get/set file times, and the C run-time funcs that get/set times won't be called. */ /* The current downside is the times written to your archives will be from 1979. */ /*#define MINIZ_NO_TIME */ /* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ /*#define MINIZ_NO_ARCHIVE_APIS */ /* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP archive API's. */ /*#define MINIZ_NO_ARCHIVE_WRITING_APIS */ /* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */ /*#define MINIZ_NO_ZLIB_APIS */ /* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */ /*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ /* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ /*#define MINIZ_NO_MALLOC */ #if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) /* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */ #define MINIZ_NO_TIME #endif #include #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) #include #endif #if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) /* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */ #define MINIZ_X86_OR_X64_CPU 1 #else #define MINIZ_X86_OR_X64_CPU 0 #endif #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU /* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ #define MINIZ_LITTLE_ENDIAN 1 #else #define MINIZ_LITTLE_ENDIAN 0 #endif /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ #if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) #if MINIZ_X86_OR_X64_CPU /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #define MINIZ_UNALIGNED_USE_MEMCPY #else #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 #endif #endif #if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) /* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */ #define MINIZ_HAS_64BIT_REGISTERS 1 #else #define MINIZ_HAS_64BIT_REGISTERS 0 #endif #ifdef __cplusplus extern "C" { #endif /* ------------------- zlib-style API Definitions. */ /* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */ typedef unsigned long mz_ulong; /* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */ MINIZ_EXPORT void mz_free(void *p); #define MZ_ADLER32_INIT (1) /* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */ MINIZ_EXPORT mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); #define MZ_CRC32_INIT (0) /* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */ MINIZ_EXPORT mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); /* Compression strategies. */ enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 }; /* Method */ #define MZ_DEFLATED 8 /* Heap allocation callbacks. Note that mz_alloc_func parameter types purposely differ from zlib's: items/size is size_t, not unsigned long. */ typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); typedef void (*mz_free_func)(void *opaque, void *address); typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); /* Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */ enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 }; #define MZ_VERSION "10.2.0" #define MZ_VERNUM 0xA100 #define MZ_VER_MAJOR 10 #define MZ_VER_MINOR 2 #define MZ_VER_REVISION 0 #define MZ_VER_SUBREVISION 0 #ifndef MINIZ_NO_ZLIB_APIS /* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). */ enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; /* Return status codes. MZ_PARAM_ERROR is non-standard. */ enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 }; /* Window bits */ #define MZ_DEFAULT_WINDOW_BITS 15 struct mz_internal_state; /* Compression/decompression stream struct. */ typedef struct mz_stream_s { const unsigned char *next_in; /* pointer to next byte to read */ unsigned int avail_in; /* number of bytes available at next_in */ mz_ulong total_in; /* total number of bytes consumed so far */ unsigned char *next_out; /* pointer to next byte to write */ unsigned int avail_out; /* number of bytes that can be written to next_out */ mz_ulong total_out; /* total number of bytes produced so far */ char *msg; /* error msg (unused) */ struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */ mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */ mz_free_func zfree; /* optional heap free function (defaults to free) */ void *opaque; /* heap alloc function user pointer */ int data_type; /* data_type (unused) */ mz_ulong adler; /* adler32 of the source or uncompressed data */ mz_ulong reserved; /* not used */ } mz_stream; typedef mz_stream *mz_streamp; /* Returns the version string of miniz.c. */ MINIZ_EXPORT const char *mz_version(void); /* mz_deflateInit() initializes a compressor with default options: */ /* Parameters: */ /* pStream must point to an initialized mz_stream struct. */ /* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */ /* level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. */ /* (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */ /* Return values: */ /* MZ_OK on success. */ /* MZ_STREAM_ERROR if the stream is bogus. */ /* MZ_PARAM_ERROR if the input parameters are bogus. */ /* MZ_MEM_ERROR on out of memory. */ MINIZ_EXPORT int mz_deflateInit(mz_streamp pStream, int level); /* mz_deflateInit2() is like mz_deflate(), except with more control: */ /* Additional parameters: */ /* method must be MZ_DEFLATED */ /* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */ /* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */ MINIZ_EXPORT int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); /* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ MINIZ_EXPORT int mz_deflateReset(mz_streamp pStream); /* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */ /* Parameters: */ /* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ /* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. */ /* Return values: */ /* MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). */ /* MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. */ /* MZ_STREAM_ERROR if the stream is bogus. */ /* MZ_PARAM_ERROR if one of the parameters is invalid. */ /* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */ MINIZ_EXPORT int mz_deflate(mz_streamp pStream, int flush); /* mz_deflateEnd() deinitializes a compressor: */ /* Return values: */ /* MZ_OK on success. */ /* MZ_STREAM_ERROR if the stream is bogus. */ MINIZ_EXPORT int mz_deflateEnd(mz_streamp pStream); /* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */ MINIZ_EXPORT mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); /* Single-call compression functions mz_compress() and mz_compress2(): */ /* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */ MINIZ_EXPORT int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); MINIZ_EXPORT int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); /* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ MINIZ_EXPORT mz_ulong mz_compressBound(mz_ulong source_len); /* Initializes a decompressor. */ MINIZ_EXPORT int mz_inflateInit(mz_streamp pStream); /* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */ /* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */ MINIZ_EXPORT int mz_inflateInit2(mz_streamp pStream, int window_bits); /* Quickly resets a compressor without having to reallocate anything. Same as calling mz_inflateEnd() followed by mz_inflateInit()/mz_inflateInit2(). */ MINIZ_EXPORT int mz_inflateReset(mz_streamp pStream); /* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */ /* Parameters: */ /* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ /* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */ /* On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). */ /* MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. */ /* Return values: */ /* MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. */ /* MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. */ /* MZ_STREAM_ERROR if the stream is bogus. */ /* MZ_DATA_ERROR if the deflate stream is invalid. */ /* MZ_PARAM_ERROR if one of the parameters is invalid. */ /* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */ /* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */ MINIZ_EXPORT int mz_inflate(mz_streamp pStream, int flush); /* Deinitializes a decompressor. */ MINIZ_EXPORT int mz_inflateEnd(mz_streamp pStream); /* Single-call decompression. */ /* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */ MINIZ_EXPORT int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); MINIZ_EXPORT int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len); /* Returns a string description of the specified error code, or NULL if the error code is invalid. */ MINIZ_EXPORT const char *mz_error(int err); /* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */ /* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */ #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES typedef unsigned char Byte; typedef unsigned int uInt; typedef mz_ulong uLong; typedef Byte Bytef; typedef uInt uIntf; typedef char charf; typedef int intf; typedef void *voidpf; typedef uLong uLongf; typedef void *voidp; typedef void *const voidpc; #define Z_NULL 0 #define Z_NO_FLUSH MZ_NO_FLUSH #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH #define Z_SYNC_FLUSH MZ_SYNC_FLUSH #define Z_FULL_FLUSH MZ_FULL_FLUSH #define Z_FINISH MZ_FINISH #define Z_BLOCK MZ_BLOCK #define Z_OK MZ_OK #define Z_STREAM_END MZ_STREAM_END #define Z_NEED_DICT MZ_NEED_DICT #define Z_ERRNO MZ_ERRNO #define Z_STREAM_ERROR MZ_STREAM_ERROR #define Z_DATA_ERROR MZ_DATA_ERROR #define Z_MEM_ERROR MZ_MEM_ERROR #define Z_BUF_ERROR MZ_BUF_ERROR #define Z_VERSION_ERROR MZ_VERSION_ERROR #define Z_PARAM_ERROR MZ_PARAM_ERROR #define Z_NO_COMPRESSION MZ_NO_COMPRESSION #define Z_BEST_SPEED MZ_BEST_SPEED #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY #define Z_FILTERED MZ_FILTERED #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY #define Z_RLE MZ_RLE #define Z_FIXED MZ_FIXED #define Z_DEFLATED MZ_DEFLATED #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS #define alloc_func mz_alloc_func #define free_func mz_free_func #define internal_state mz_internal_state #define z_stream mz_stream #define deflateInit mz_deflateInit #define deflateInit2 mz_deflateInit2 #define deflateReset mz_deflateReset #define deflate mz_deflate #define deflateEnd mz_deflateEnd #define deflateBound mz_deflateBound #define compress mz_compress #define compress2 mz_compress2 #define compressBound mz_compressBound #define inflateInit mz_inflateInit #define inflateInit2 mz_inflateInit2 #define inflateReset mz_inflateReset #define inflate mz_inflate #define inflateEnd mz_inflateEnd #define uncompress mz_uncompress #define uncompress2 mz_uncompress2 #define crc32 mz_crc32 #define adler32 mz_adler32 #define MAX_WBITS 15 #define MAX_MEM_LEVEL 9 #define zError mz_error #define ZLIB_VERSION MZ_VERSION #define ZLIB_VERNUM MZ_VERNUM #define ZLIB_VER_MAJOR MZ_VER_MAJOR #define ZLIB_VER_MINOR MZ_VER_MINOR #define ZLIB_VER_REVISION MZ_VER_REVISION #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION #define zlibVersion mz_version #define zlib_version mz_version() #endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ #endif /* MINIZ_NO_ZLIB_APIS */ #ifdef __cplusplus } #endif #pragma once #include #include #include #include /* ------------------- Types and macros */ typedef unsigned char mz_uint8; typedef signed short mz_int16; typedef unsigned short mz_uint16; typedef unsigned int mz_uint32; typedef unsigned int mz_uint; typedef int64_t mz_int64; typedef uint64_t mz_uint64; typedef int mz_bool; #define MZ_FALSE (0) #define MZ_TRUE (1) /* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */ #ifdef _MSC_VER #define MZ_MACRO_END while (0, 0) #else #define MZ_MACRO_END while (0) #endif #ifdef MINIZ_NO_STDIO #define MZ_FILE void * #else #include #define MZ_FILE FILE #endif /* #ifdef MINIZ_NO_STDIO */ #ifdef MINIZ_NO_TIME typedef struct mz_dummy_time_t_tag { int m_dummy; } mz_dummy_time_t; #define MZ_TIME_T mz_dummy_time_t #else #define MZ_TIME_T time_t #endif #define MZ_ASSERT(x) assert(x) #ifdef MINIZ_NO_MALLOC #define MZ_MALLOC(x) NULL #define MZ_FREE(x) (void)x, ((void)0) #define MZ_REALLOC(p, x) NULL #else #define MZ_MALLOC(x) malloc(x) #define MZ_FREE(x) free(x) #define MZ_REALLOC(p, x) realloc(p, x) #endif #define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) #else #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) #endif #define MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U)) #ifdef _MSC_VER #define MZ_FORCEINLINE __forceinline #elif defined(__GNUC__) #define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__)) #else #define MZ_FORCEINLINE inline #endif #ifdef __cplusplus extern "C" { #endif extern MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); extern MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address); extern MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); #define MZ_UINT16_MAX (0xFFFFU) #define MZ_UINT32_MAX (0xFFFFFFFFU) #ifdef __cplusplus } #endif #pragma once #ifdef __cplusplus extern "C" { #endif /* ------------------- Low-level Compression API Definitions */ /* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). */ #define TDEFL_LESS_MEMORY 0 /* tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): */ /* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). */ enum { TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF }; /* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. */ /* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). */ /* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. */ /* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). */ /* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */ /* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */ /* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */ /* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */ /* The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). */ enum { TDEFL_WRITE_ZLIB_HEADER = 0x01000, TDEFL_COMPUTE_ADLER32 = 0x02000, TDEFL_GREEDY_PARSING_FLAG = 0x04000, TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, TDEFL_RLE_MATCHES = 0x10000, TDEFL_FILTER_MATCHES = 0x20000, TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 }; /* High level compression functions: */ /* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). */ /* On entry: */ /* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */ /* flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. */ /* On return: */ /* Function returns a pointer to the compressed data, or NULL on failure. */ /* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */ /* The caller must free() the returned block when it's no longer needed. */ MINIZ_EXPORT void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); /* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */ /* Returns 0 on failure. */ MINIZ_EXPORT size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); /* Compresses an image to a compressed PNG file in memory. */ /* On entry: */ /* pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. */ /* The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. */ /* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */ /* If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). */ /* On return: */ /* Function returns a pointer to the compressed data, or NULL on failure. */ /* *pLen_out will be set to the size of the PNG image file. */ /* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */ MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); /* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); /* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */ MINIZ_EXPORT mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 }; /* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). */ #if TDEFL_LESS_MEMORY enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #else enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #endif /* The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. */ typedef enum { TDEFL_STATUS_BAD_PARAM = -2, TDEFL_STATUS_PUT_BUF_FAILED = -1, TDEFL_STATUS_OKAY = 0, TDEFL_STATUS_DONE = 1 } tdefl_status; /* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */ typedef enum { TDEFL_NO_FLUSH = 0, TDEFL_SYNC_FLUSH = 2, TDEFL_FULL_FLUSH = 3, TDEFL_FINISH = 4 } tdefl_flush; /* tdefl's compression state structure. */ typedef struct { tdefl_put_buf_func_ptr m_pPut_buf_func; void *m_pPut_buf_user; mz_uint m_flags, m_max_probes[2]; int m_greedy_parsing; mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; tdefl_status m_prev_return_status; const void *m_pIn_buf; void *m_pOut_buf; size_t *m_pIn_buf_size, *m_pOut_buf_size; tdefl_flush m_flush; const mz_uint8 *m_pSrc; size_t m_src_buf_left, m_out_buf_ofs; mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; } tdefl_compressor; /* Initializes the compressor. */ /* There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. */ /* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */ /* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */ /* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */ MINIZ_EXPORT tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); /* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */ MINIZ_EXPORT tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); /* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */ /* tdefl_compress_buffer() always consumes the entire input buffer. */ MINIZ_EXPORT tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); MINIZ_EXPORT tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); MINIZ_EXPORT mz_uint32 tdefl_get_adler32(tdefl_compressor *d); /* Create tdefl_compress() flags given zlib-style compression parameters. */ /* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */ /* window_bits may be -15 (raw deflate) or 15 (zlib) */ /* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */ MINIZ_EXPORT mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); #ifndef MINIZ_NO_MALLOC /* Allocate the tdefl_compressor structure in C so that */ /* non-C language bindings to tdefl_ API don't need to worry about */ /* structure size and allocation mechanism. */ MINIZ_EXPORT tdefl_compressor *tdefl_compressor_alloc(void); MINIZ_EXPORT void tdefl_compressor_free(tdefl_compressor *pComp); #endif #ifdef __cplusplus } #endif #pragma once /* ------------------- Low-level Decompression API Definitions */ #ifdef __cplusplus extern "C" { #endif /* Decompression flags used by tinfl_decompress(). */ /* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */ /* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */ /* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */ /* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */ enum { TINFL_FLAG_PARSE_ZLIB_HEADER = 1, TINFL_FLAG_HAS_MORE_INPUT = 2, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, TINFL_FLAG_COMPUTE_ADLER32 = 8 }; /* High level decompression functions: */ /* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */ /* On entry: */ /* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */ /* On return: */ /* Function returns a pointer to the decompressed data, or NULL on failure. */ /* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */ /* The caller must call mz_free() on the returned block when it's no longer needed. */ MINIZ_EXPORT void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); /* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */ /* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */ #define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) MINIZ_EXPORT size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); /* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */ /* Returns 1 on success or 0 on failure. */ typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); MINIZ_EXPORT int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; #ifndef MINIZ_NO_MALLOC /* Allocate the tinfl_decompressor structure in C so that */ /* non-C language bindings to tinfl_ API don't need to worry about */ /* structure size and allocation mechanism. */ MINIZ_EXPORT tinfl_decompressor *tinfl_decompressor_alloc(void); MINIZ_EXPORT void tinfl_decompressor_free(tinfl_decompressor *pDecomp); #endif /* Max size of LZ dictionary. */ #define TINFL_LZ_DICT_SIZE 32768 /* Return status. */ typedef enum { /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */ /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */ /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */ TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4, /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */ TINFL_STATUS_BAD_PARAM = -3, /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */ TINFL_STATUS_ADLER32_MISMATCH = -2, /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */ TINFL_STATUS_FAILED = -1, /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */ /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */ /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */ TINFL_STATUS_DONE = 0, /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */ /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */ /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */ TINFL_STATUS_NEEDS_MORE_INPUT = 1, /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */ /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */ /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */ /* so I may need to add some code to address this. */ TINFL_STATUS_HAS_MORE_OUTPUT = 2 } tinfl_status; /* Initializes the decompressor to its initial state. */ #define tinfl_init(r) \ do \ { \ (r)->m_state = 0; \ } \ MZ_MACRO_END #define tinfl_get_adler32(r) (r)->m_check_adler32 /* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */ /* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */ MINIZ_EXPORT tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); /* Internal/private bits follow. */ enum { TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS }; typedef struct { mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; } tinfl_huff_table; #if MINIZ_HAS_64BIT_REGISTERS #define TINFL_USE_64BIT_BITBUF 1 #else #define TINFL_USE_64BIT_BITBUF 0 #endif #if TINFL_USE_64BIT_BITBUF typedef mz_uint64 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (64) #else typedef mz_uint32 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (32) #endif struct tinfl_decompressor_tag { mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; tinfl_bit_buf_t m_bit_buf; size_t m_dist_from_out_buf_start; tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; }; #ifdef __cplusplus } #endif #pragma once /* ------------------- ZIP archive reading/writing */ #ifndef MINIZ_NO_ARCHIVE_APIS #ifdef __cplusplus extern "C" { #endif enum { /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */ MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 }; typedef struct { /* Central directory file index. */ mz_uint32 m_file_index; /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */ mz_uint64 m_central_dir_ofs; /* These fields are copied directly from the zip's central dir. */ mz_uint16 m_version_made_by; mz_uint16 m_version_needed; mz_uint16 m_bit_flag; mz_uint16 m_method; #ifndef MINIZ_NO_TIME MZ_TIME_T m_time; #endif /* CRC-32 of uncompressed data. */ mz_uint32 m_crc32; /* File's compressed size. */ mz_uint64 m_comp_size; /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */ mz_uint64 m_uncomp_size; /* Zip internal and external file attributes. */ mz_uint16 m_internal_attr; mz_uint32 m_external_attr; /* Entry's local header file offset in bytes. */ mz_uint64 m_local_header_ofs; /* Size of comment in bytes. */ mz_uint32 m_comment_size; /* MZ_TRUE if the entry appears to be a directory. */ mz_bool m_is_directory; /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */ mz_bool m_is_encrypted; /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */ mz_bool m_is_supported; /* Filename. If string ends in '/' it's a subdirectory entry. */ /* Guaranteed to be zero terminated, may be truncated to fit. */ char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; /* Comment field. */ /* Guaranteed to be zero terminated, may be truncated to fit. */ char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; } mz_zip_archive_file_stat; typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); struct mz_zip_internal_state_tag; typedef struct mz_zip_internal_state_tag mz_zip_internal_state; typedef enum { MZ_ZIP_MODE_INVALID = 0, MZ_ZIP_MODE_READING = 1, MZ_ZIP_MODE_WRITING = 2, MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 } mz_zip_mode; typedef enum { MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000, /*After adding a compressed file, seek back to local file header and set the correct sizes*/ MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE = 0x20000 } mz_zip_flags; typedef enum { MZ_ZIP_TYPE_INVALID = 0, MZ_ZIP_TYPE_USER, MZ_ZIP_TYPE_MEMORY, MZ_ZIP_TYPE_HEAP, MZ_ZIP_TYPE_FILE, MZ_ZIP_TYPE_CFILE, MZ_ZIP_TOTAL_TYPES } mz_zip_type; /* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */ typedef enum { MZ_ZIP_NO_ERROR = 0, MZ_ZIP_UNDEFINED_ERROR, MZ_ZIP_TOO_MANY_FILES, MZ_ZIP_FILE_TOO_LARGE, MZ_ZIP_UNSUPPORTED_METHOD, MZ_ZIP_UNSUPPORTED_ENCRYPTION, MZ_ZIP_UNSUPPORTED_FEATURE, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, MZ_ZIP_NOT_AN_ARCHIVE, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, MZ_ZIP_UNSUPPORTED_MULTIDISK, MZ_ZIP_DECOMPRESSION_FAILED, MZ_ZIP_COMPRESSION_FAILED, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, MZ_ZIP_CRC_CHECK_FAILED, MZ_ZIP_UNSUPPORTED_CDIR_SIZE, MZ_ZIP_ALLOC_FAILED, MZ_ZIP_FILE_OPEN_FAILED, MZ_ZIP_FILE_CREATE_FAILED, MZ_ZIP_FILE_WRITE_FAILED, MZ_ZIP_FILE_READ_FAILED, MZ_ZIP_FILE_CLOSE_FAILED, MZ_ZIP_FILE_SEEK_FAILED, MZ_ZIP_FILE_STAT_FAILED, MZ_ZIP_INVALID_PARAMETER, MZ_ZIP_INVALID_FILENAME, MZ_ZIP_BUF_TOO_SMALL, MZ_ZIP_INTERNAL_ERROR, MZ_ZIP_FILE_NOT_FOUND, MZ_ZIP_ARCHIVE_TOO_LARGE, MZ_ZIP_VALIDATION_FAILED, MZ_ZIP_WRITE_CALLBACK_FAILED, MZ_ZIP_TOTAL_ERRORS } mz_zip_error; typedef struct { mz_uint64 m_archive_size; mz_uint64 m_central_directory_file_ofs; /* We only support up to UINT32_MAX files in zip64 mode. */ mz_uint32 m_total_files; mz_zip_mode m_zip_mode; mz_zip_type m_zip_type; mz_zip_error m_last_error; mz_uint64 m_file_offset_alignment; mz_alloc_func m_pAlloc; mz_free_func m_pFree; mz_realloc_func m_pRealloc; void *m_pAlloc_opaque; mz_file_read_func m_pRead; mz_file_write_func m_pWrite; mz_file_needs_keepalive m_pNeeds_keepalive; void *m_pIO_opaque; mz_zip_internal_state *m_pState; } mz_zip_archive; typedef struct { mz_zip_archive *pZip; mz_uint flags; int status; #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS mz_uint file_crc32; #endif mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; mz_zip_archive_file_stat file_stat; void *pRead_buf; void *pWrite_buf; size_t out_blk_remain; tinfl_decompressor inflator; } mz_zip_reader_extract_iter_state; /* -------- ZIP reading */ /* Inits a ZIP archive reader. */ /* These functions read and validate the archive's central directory. */ MINIZ_EXPORT mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); #ifndef MINIZ_NO_STDIO /* Read a archive from a disk file. */ /* file_start_ofs is the file offset where the archive actually begins, or 0. */ /* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */ MINIZ_EXPORT mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); MINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); /* Read an archive from an already opened FILE, beginning at the current file position. */ /* The archive is assumed to be archive_size bytes long. If archive_size is 0, then the entire rest of the file is assumed to contain the archive. */ /* The FILE will NOT be closed when mz_zip_reader_end() is called. */ MINIZ_EXPORT mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); #endif /* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */ MINIZ_EXPORT mz_bool mz_zip_reader_end(mz_zip_archive *pZip); /* -------- ZIP reading or writing */ /* Clears a mz_zip_archive struct to all zeros. */ /* Important: This must be done before passing the struct to any mz_zip functions. */ MINIZ_EXPORT void mz_zip_zero_struct(mz_zip_archive *pZip); MINIZ_EXPORT mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); MINIZ_EXPORT mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); /* Returns the total number of files in the archive. */ MINIZ_EXPORT mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); MINIZ_EXPORT mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); MINIZ_EXPORT mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); MINIZ_EXPORT MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); /* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ MINIZ_EXPORT size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); /* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */ /* Note that the m_last_error functionality is not thread safe. */ MINIZ_EXPORT mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); MINIZ_EXPORT mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); MINIZ_EXPORT mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); MINIZ_EXPORT mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); MINIZ_EXPORT const char *mz_zip_get_error_string(mz_zip_error mz_err); /* MZ_TRUE if the archive file entry is a directory entry. */ MINIZ_EXPORT mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); /* MZ_TRUE if the file is encrypted/strong encrypted. */ MINIZ_EXPORT mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); /* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */ MINIZ_EXPORT mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); /* Retrieves the filename of an archive file entry. */ /* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */ MINIZ_EXPORT mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); /* Attempts to locates a file in the archive's central directory. */ /* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ /* Returns -1 if the file cannot be found. */ MINIZ_EXPORT int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); /* Returns detailed information about an archive file entry. */ MINIZ_EXPORT mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); /* MZ_TRUE if the file is in zip64 format. */ /* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */ MINIZ_EXPORT mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); /* Returns the total central directory size in bytes. */ /* The current max supported size is <= MZ_UINT32_MAX. */ MINIZ_EXPORT size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); /* Extracts a archive file to a memory buffer using no memory allocation. */ /* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */ MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); /* Extracts a archive file to a memory buffer. */ MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); /* Extracts a archive file to a dynamically allocated heap buffer. */ /* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */ /* Returns NULL and sets the last error on failure. */ MINIZ_EXPORT void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); MINIZ_EXPORT void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); /* Extracts a archive file using a callback function to output the file's data. */ MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); /* Extract a file iteratively */ MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); MINIZ_EXPORT size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size); MINIZ_EXPORT mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState); #ifndef MINIZ_NO_STDIO /* Extracts a archive file to a disk file and sets its last accessed and modified times. */ /* This function only extracts files, not archive directory records. */ MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); /* Extracts a archive file starting at the current position in the destination FILE stream. */ MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); #endif #if 0 /* TODO */ typedef void *mz_zip_streaming_extract_state_ptr; mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); uint64_t mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); uint64_t mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, uint64_t new_ofs); size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); #endif /* This function compares the archive's local headers, the optional local zip64 extended information block, and the optional descriptor following the compressed data vs. the data in the central directory. */ /* It also validates that each file can be successfully uncompressed unless the MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */ MINIZ_EXPORT mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); /* Validates an entire archive by calling mz_zip_validate_file() on each file. */ MINIZ_EXPORT mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags); /* Misc utils/helpers, valid for ZIP reading or writing */ MINIZ_EXPORT mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); MINIZ_EXPORT mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); /* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ MINIZ_EXPORT mz_bool mz_zip_end(mz_zip_archive *pZip); /* -------- ZIP writing */ #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS /* Inits a ZIP archive writer. */ /*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/ /*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/ MINIZ_EXPORT mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); MINIZ_EXPORT mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); MINIZ_EXPORT mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); #ifndef MINIZ_NO_STDIO MINIZ_EXPORT mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); MINIZ_EXPORT mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); #endif /* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */ /* For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. */ /* For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). */ /* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */ /* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */ /* the archive is finalized the file's central directory will be hosed. */ MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); /* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */ /* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */ /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); /* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */ /* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len); /* Adds the contents of a file to an archive. This function also records the disk file's modified time into the archive. */ /* File data is supplied via a read callback function. User mz_zip_writer_add_(c)file to add a file directly.*/ MINIZ_EXPORT mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len); #ifndef MINIZ_NO_STDIO /* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */ /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); /* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len); #endif /* Adds a file to an archive by fully cloning the data from another archive. */ /* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index); /* Finalizes the archive by writing the central directory records followed by the end of central directory record. */ /* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */ /* An archive must be manually finalized by calling this function for it to be valid. */ MINIZ_EXPORT mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); /* Finalizes a heap archive, returning a poiner to the heap block and its size. */ /* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */ MINIZ_EXPORT mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); /* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */ /* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */ MINIZ_EXPORT mz_bool mz_zip_writer_end(mz_zip_archive *pZip); /* -------- Misc. high-level helper functions: */ /* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. */ /* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */ /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ /* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */ MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); /* Reads a single file from an archive into a heap block. */ /* If pComment is not NULL, only the file with the specified comment will be extracted. */ /* Returns NULL on failure. */ MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ #ifdef __cplusplus } #endif #endif /* MINIZ_NO_ARCHIVE_APIS */ ================================================ FILE: src/morph/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/morph/intr.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Morph inspect / layout / interleave * =================================== * * Inspect builds a hierarchical view over a morph controller and its targets, * suitable for GPU-side blending. The view mirrors the diagram in * controller.h: * * AkMorphInspectView * ├─ base : AkMorphInspectTargetView (always built) * │ └─ morphable[] : per primitive * │ └─ input[] : per semantic (POSITION, NORMAL, ...) * └─ targets : AkMorphInspectTargetView linked list (one per blend shape; * base is prepended here when includeBaseShape == true) * * Inspect is static analysis: collects inputs, computes per-vertex stride and * per-target buffer size. It does NOT apply weights — those are runtime values * (see ak_morphHasOverride and the runtime evaluator/uniform path). * * Multi-primitive: each TargetView contains one Morphable per primitive of * the base mesh; multi-primitive support is structural here. Whether the * loaders populate multi-primitive chains is a separate concern. */ #include "../common.h" #include "../accessor.h" #include /*============================================================================ * Inspect helpers (file-local) *============================================================================*/ AK_EXPORT const AkMorphPreset* ak_morphPresetByName(AkMorph * __restrict morph, const char * __restrict name) { AkMorphPreset *preset; uint32_t i; if (!morph || !name || !morph->presets || morph->presetCount == 0) return NULL; preset = morph->presets; for (i = 0; i < morph->presetCount; i++) { if (preset[i].name && strcmp(preset[i].name, name) == 0) return &preset[i]; } return NULL; } AK_EXPORT bool ak_morphApplyPreset(AkMorph * __restrict morph, const char * __restrict presetName, float * __restrict outWeights, uint32_t capacity) { const AkMorphPreset *preset; AkFloatArray *weights; uint32_t i; if (!morph || !outWeights || capacity < morph->targetCount) return false; preset = ak_morphPresetByName(morph, presetName); if (!preset || !(weights = preset->weights)) return false; if (weights->count != morph->targetCount) return false; for (i = 0; i < morph->targetCount; i++) outWeights[i] = weights->items[i]; return true; } /** * @brief initial weight for the i-th morph target. * * Precedence: * morph.defaultWeights[idx] → controller-level default (highest) * mesh.weights[idx] → mesh-level default * 0.0 → fallback * * Instance-level overrideWeights is intentionally NOT consulted here: * inspect is static prep, override is a runtime concern. */ AK_INLINE float ak_morphInspect_initialWeight(const AkMorph *morph, const AkMesh *mesh, uint32_t targetIdx) { AkFloatArray *arr; if ((arr = morph->defaultWeights) && targetIdx < arr->count) return arr->items[targetIdx]; if (mesh && (arr = mesh->weights) && targetIdx < arr->count) return arr->items[targetIdx]; return 0.0f; } /** * @brief true iff the input semantic is in the desired filter (or no filter). */ AK_INLINE bool ak_morphInspect_isDesired(AkInputSemantic sem, AkInputSemantic *desired, uint8_t desiredCount) { uint8_t i; if (desiredCount == 0) return true; for (i = 0; i < desiredCount; i++) { if (desired[i] == sem) return true; } return false; } /** * @brief true iff `inp` matches one of the base inputs by (semantic, set). */ AK_INLINE bool ak_morphInspect_inBase(const AkInput *inp, const AkMorphInspectMorphable *base) { AkMorphInspectInput *bi; AkInput *binp; if (!inp || !base) return false; for (bi = base->input; bi; bi = bi->next) { binp = bi->input; if (binp->semantic == inp->semantic && binp->set == inp->set) return true; } return false; } /** * @brief append one AkMorphInspectInput to a morphable's input chain. */ AK_INLINE AkMorphInspectInput * ak_morphInspect_appendInput(AkHeap *heap, AkMorphInspectMorphable *m, AkMorphInspectInput **last, AkInput *input, bool inBaseMesh) { AkMorphInspectInput *ii; ii = ak_heap_calloc(heap, m, sizeof(*ii)); ii->input = input; ii->inBaseMesh = inBaseMesh; AK_APPEND_FLINK(m->input, (*last), ii); m->inputsCount++; return ii; } /** * @brief allocate a Morphable, append to the TargetView's morphable chain. */ AK_INLINE AkMorphInspectMorphable * ak_morphInspect_appendMorphable(AkHeap *heap, AkMorphInspectTargetView *tv, AkMorphInspectMorphable **last, float weight) { AkMorphInspectMorphable *m; m = ak_heap_calloc(heap, tv, sizeof(*m)); m->weight = weight; AK_APPEND_FLINK(tv->morphable, (*last), m); tv->nTargets++; /* nTargets here = primitive count of this target view */ return m; } /** * @brief allocate a TargetView, append to the View's targets chain. */ AK_INLINE AkMorphInspectTargetView * ak_morphInspect_appendTargetView(AkHeap *heap, AkMorphInspectView *view, AkMorphInspectTargetView **last) { AkMorphInspectTargetView *tv; tv = ak_heap_calloc(heap, view, sizeof(*tv)); AK_APPEND_FLINK(view->targets, (*last), tv); view->nTargets++; return tv; } /** * @brief find the POSITION input in an input chain (NULL if absent). */ AK_INLINE AkInput * ak_morphInspect_findPosition(AkInput *first) { AkInput *inp; for (inp = first; inp; inp = inp->next) { if (inp->semantic == AK_INPUT_POSITION) return inp; } return NULL; } /*============================================================================ * ak_morphInspect *============================================================================*/ AK_EXPORT AkResult ak_morphInspect(AkGeometry * __restrict baseMesh, AkMorph * __restrict morph, AkInputSemantic desiredInputs[], uint8_t desiredInputsCount, bool includeBaseShape, bool ignoreUncommonInputs) { AkHeap *heap; AkMorphInspectView *view; AkMorphInspectTargetView *tv, *lastTV; AkMorphInspectMorphable *m, *lastM; AkMorphInspectMorphable *baseM, *mIter; AkMorphInspectInput *lastInput; AkMorphTarget *target; AkObject *gdataObj, *targetObj; AkMesh *mesh, *targetMesh; AkMeshPrimitive *prim, *targetPrim; AkInput *inp, *posInp; AkAccessor *acc; AkMorphable *morphable; AkGeometry *targetGeom; AkFloatArray *iw; void *targetPtr; uint32_t primIdx, primCount; uint32_t baseInputsCount; uint32_t targetIdx; uint32_t primVertCount, targetVertCount; uint32_t primStride, targetStride; uint32_t expectedCount, i; size_t baseBufOff, targetBufOff, tvSize; bool inBase; if (!baseMesh || !morph) return AK_ERR; heap = ak_heap_getheap(morph); view = ak_heap_calloc(heap, morph, sizeof(*view)); view->layout = AK_MORPH_UNKNOWN; view->includeBaseShape = includeBaseShape; view->ignoreUncommonInputs = ignoreUncommonInputs; /*========================================================================*/ /* Phase 1: validate base mesh */ /*========================================================================*/ if (!(gdataObj = baseMesh->gdata) || gdataObj->type != AK_GEOMETRY_MESH || !(mesh = ak_objGet(gdataObj)) || !(prim = mesh->primitive)) { return AK_ERR; } primCount = mesh->primitiveCount; if (primCount == 0) return AK_ERR; /*========================================================================*/ /* Phase 2: build base TargetView */ /* */ /* Always built. Even when includeBaseShape == false, base inputs are */ /* needed to match against target inputs (ignoreUncommonInputs filter). */ /*========================================================================*/ tv = ak_heap_calloc(heap, view, sizeof(*tv)); view->base = tv; baseInputsCount = 0; lastM = NULL; baseBufOff = 0; primIdx = 0; for (prim = mesh->primitive; prim && primIdx < primCount; prim = prim->next, primIdx++) { if (!prim->pos || !(acc = prim->pos->accessor)) continue; primVertCount = (uint32_t)acc->count; if (primVertCount == 0) continue; m = ak_morphInspect_appendMorphable(heap, tv, &lastM, 0.0f); m->vertexCount = primVertCount; lastInput = NULL; primStride = 0; for (inp = prim->input; inp; inp = inp->next) { if (!ak_morphInspect_isDesired(inp->semantic, desiredInputs, desiredInputsCount)) continue; if (!(acc = inp->accessor) || acc->count != primVertCount) continue; ak_morphInspect_appendInput(heap, m, &lastInput, inp, true); primStride += (uint32_t)acc->fillByteSize; baseInputsCount++; } m->stridePerVertex = primStride; m->bufferOffset = baseBufOff; m->bufferSize = (size_t)primStride * primVertCount; baseBufOff += m->bufferSize; m->lastInput = lastInput; } if (baseInputsCount == 0) return AK_ERR; /*========================================================================*/ /* Phase 3: build TargetView per AkMorphTarget */ /*========================================================================*/ lastTV = NULL; targetIdx = 0; for (target = morph->target; target; target = target->next, targetIdx++) { if (!(targetObj = target->target) || !(targetPtr = ak_objGet(targetObj))) continue; tv = ak_morphInspect_appendTargetView(heap, view, &lastTV); lastM = NULL; targetBufOff = 0; baseM = view->base ? view->base->morphable : NULL; /* polymorphic dispatch on AkMorphableType (kept as enum slot in AkObject.type — see controller.h) */ switch (targetObj->type) { case AK_MORPHABLE_MORPHABLE: { /* glTF-style: AkMorphable chain, one per primitive. Walk the target's morphable chain in lockstep with the base morphables so each target primitive gets sized against its corresponding base primitive's vertex count rather than a single global anchor. */ morphable = targetPtr; primIdx = 0; for (; morphable && primIdx < primCount; morphable = morphable->next, primIdx++) { expectedCount = baseM ? baseM->vertexCount : 0; if (!(posInp = ak_morphInspect_findPosition(morphable->input)) || !(acc = posInp->accessor) || (expectedCount > 0 && (uint32_t)acc->count != expectedCount)) goto stepBaseM_a; targetVertCount = (uint32_t)acc->count; m = ak_morphInspect_appendMorphable( heap, tv, &lastM, ak_morphInspect_initialWeight(morph, mesh, targetIdx)); m->vertexCount = targetVertCount; lastInput = NULL; targetStride = 0; for (inp = morphable->input; inp; inp = inp->next) { if (!ak_morphInspect_isDesired(inp->semantic, desiredInputs, desiredInputsCount)) continue; if (!(acc = inp->accessor) || acc->count != targetVertCount) continue; inBase = ak_morphInspect_inBase(inp, baseM); if (ignoreUncommonInputs && !inBase) continue; ak_morphInspect_appendInput(heap, m, &lastInput, inp, inBase); targetStride += (uint32_t)acc->fillByteSize; } m->stridePerVertex = targetStride; m->bufferOffset = targetBufOff; m->bufferSize = (size_t)targetStride * targetVertCount; targetBufOff += m->bufferSize; m->lastInput = lastInput; stepBaseM_a: if (baseM) baseM = baseM->next; } break; } case AK_MORPHABLE_GEOMETRY: { /* DAE-style: pointer-storage payload — wrap holds an AkGeometry* in its inline slot. ak_objGetTarget hides the deref. */ targetGeom = ak_objGetTarget(targetObj); if (!targetGeom || !(gdataObj = targetGeom->gdata) || gdataObj->type != AK_GEOMETRY_MESH || !(targetMesh = ak_objGet(gdataObj))) continue; primIdx = 0; for (targetPrim = targetMesh->primitive; targetPrim && primIdx < primCount; targetPrim = targetPrim->next, primIdx++) { expectedCount = baseM ? baseM->vertexCount : 0; if (!targetPrim->pos || !(acc = targetPrim->pos->accessor) || (expectedCount > 0 && (uint32_t)acc->count != expectedCount)) goto stepBaseM_b; targetVertCount = (uint32_t)acc->count; m = ak_morphInspect_appendMorphable( heap, tv, &lastM, ak_morphInspect_initialWeight(morph, mesh, targetIdx)); m->vertexCount = targetVertCount; lastInput = NULL; targetStride = 0; for (inp = targetPrim->input; inp; inp = inp->next) { if (!ak_morphInspect_isDesired(inp->semantic, desiredInputs, desiredInputsCount)) continue; if (!(acc = inp->accessor) || acc->count != targetVertCount) continue; inBase = ak_morphInspect_inBase(inp, baseM); if (ignoreUncommonInputs && !inBase) continue; ak_morphInspect_appendInput(heap, m, &lastInput, inp, inBase); targetStride += (uint32_t)acc->fillByteSize; } m->stridePerVertex = targetStride; m->bufferOffset = targetBufOff; m->bufferSize = (size_t)targetStride * targetVertCount; targetBufOff += m->bufferSize; m->lastInput = lastInput; stepBaseM_b: if (baseM) baseM = baseM->next; } break; } default: /* unknown target kind — TargetView already linked, leave empty */ break; } } /*========================================================================*/ /* Phase 4: prepend base to view->targets when includeBaseShape */ /*========================================================================*/ if (includeBaseShape) { view->base->next = view->targets; view->targets = view->base; view->nTargets++; } /*========================================================================*/ /* Phase 5: target slice sizes */ /*========================================================================*/ /* Per-morphable bufferSize is the source of truth — sum across a target's morphables to get its slice size, then sum across targets for the whole-buffer size. */ view->interleaveTotalBufferSize = 0; for (tv = view->targets; tv; tv = tv->next) { tvSize = 0; for (mIter = tv->morphable; mIter; mIter = mIter->next) { tvSize += mIter->bufferSize; } tv->interleaveBufferSize = tvSize; view->interleaveTotalBufferSize += tvSize; } /* convenience cache: per-blend-shape initial weights for runtime (length = morph->targetCount; base shape is NOT included here) */ if (morph->targetCount > 0) { iw = ak_heap_calloc(heap, view, sizeof(*iw) + sizeof(AkFloat) * morph->targetCount); iw->count = morph->targetCount; for (i = 0; i < morph->targetCount; i++) { iw->items[i] = ak_morphInspect_initialWeight(morph, mesh, i); } view->initialWeights = iw; } morph->inspectResult = view; return AK_OK; } /*============================================================================ * ak_morphInspectPrepareLayout *============================================================================*/ AK_EXPORT AkResult ak_morphInspectPrepareLayout(AkMorphInspectView * __restrict inspectView, AkMorphInterleaveLayout layout) { AkMorphInspectTargetView *targetView, *base; AkMorphInspectMorphable *inspMorphable; AkMorphInspectMorphable *baseMorph; AkMorphInspectInput *tinp, *tinpt; AkInput *inp; AkAccessor *acc; uint32_t inpOff; bool includeBaseShape, ignoreUncommonInputs; if (!inspectView || !(base = inspectView->base) || !(inspMorphable = base->morphable) || !(tinp = inspMorphable->input)) return AK_ERR; if (inspectView->layout == layout) return AK_OK; includeBaseShape = inspectView->includeBaseShape; ignoreUncommonInputs = inspectView->ignoreUncommonInputs; /* Layout model: * * destBuff = [target0 slice][target1 slice][target2 slice] ... * target slice = [morphable0 sub-slice][morphable1 sub-slice] ... * sub-slice = vertexCount × stridePerVertex bytes * * `intrOffset` for each input is the byte offset *within* its * morphable's stride row (0 .. morphable.stridePerVertex). The * morphable's `bufferOffset` (set during inspect) places the * sub-slice within its target's slice. The interleave pass * advances `dst` by `targetView->interleaveBufferSize` per * target, then for each morphable computes * `dst_morph = dst_target + morphable.bufferOffset` * and writes inputs at * `dst_morph + input.intrOffset + stride*k`. * * P1P2N1N2 = within each morphable, walk inputs grouped by * base-input order (POSITION → NORMAL → TANGENT, ...) * NATURAL = within each morphable, walk inputs in authored * order. */ switch (layout) { case AK_MORPH_P1P2N1N2: { for (targetView = inspectView->targets; targetView; targetView = targetView->next) { baseMorph = inspectView->base ? inspectView->base->morphable : NULL; inspMorphable = targetView->morphable; while (inspMorphable) { inpOff = 0; /* Use the paired base morphable's input ordering as the template, when available; this gives target inputs offsets matching base order. */ if (baseMorph) { for (tinp = baseMorph->input; tinp; tinp = tinp->next) { if (!tinp->inTarget && !includeBaseShape) continue; for (tinpt = inspMorphable->input; tinpt; tinpt = tinpt->next) { if (!(tinpt->input->semantic == tinp->input->semantic && tinpt->input->set == tinp->input->set) || (ignoreUncommonInputs && !tinpt->inBaseMesh)) continue; inp = tinpt->input; acc = inp->accessor; tinpt->intrOffset = inpOff; inpOff += (uint32_t)acc->fillByteSize; break; /* one target input assigned per base input */ } } } /* Trailing target-only inputs (rare). */ if (!ignoreUncommonInputs) { for (tinpt = inspMorphable->input; tinpt; tinpt = tinpt->next) { if (tinpt->inBaseMesh) continue; inp = tinpt->input; acc = inp->accessor; tinpt->intrOffset = inpOff; inpOff += (uint32_t)acc->fillByteSize; } } inspMorphable = inspMorphable->next; if (baseMorph) baseMorph = baseMorph->next; } } inspectView->layout = layout; return AK_OK; } case AK_MORPH_NATURAL: { /* Per-morphable scoped offsets in authored input order. */ for (targetView = inspectView->targets; targetView; targetView = targetView->next) { for (inspMorphable = targetView->morphable; inspMorphable; inspMorphable = inspMorphable->next) { inpOff = 0; for (tinp = inspMorphable->input; tinp; tinp = tinp->next) { inp = tinp->input; acc = inp->accessor; tinp->intrOffset = inpOff; inpOff += (uint32_t)acc->fillByteSize; } } } inspectView->layout = layout; return AK_OK; } default: break; } return AK_ERR; } static AkResult ak_morphInterleaveInternal(AkGeometry * __restrict baseMesh, AkMorph * __restrict morph, AkMorphInterleaveLayout layout, void * __restrict destBuff, AkMorphProgressFn progress, void * __restrict userdata) { AkMorphInspectView *morphView; AkMorphInspectTargetView *targetView; AkMorphInspectMorphable *inspMorphable; AkMorphInspectInput *tinp; AkInput *inp; AkAccessor *acc; AkBuffer *buf; char *src, *dst; char *targetDst, *morphDst; char *sp, *dp; uint32_t srcStride, compSize; uint32_t k, intrOffset; uint32_t mStride, mCount; uint32_t targetIdx; /* lazy-inspect with default options if caller didn't run it explicitly */ if (!(morphView = morph->inspectResult)) { if (ak_morphInspect(baseMesh, morph, NULL, 0, false, true) != AK_OK || !(morphView = morph->inspectResult)) return AK_ERR; } if (layout != morphView->layout && ak_morphInspectPrepareLayout(morphView, layout) != AK_OK) return AK_ERR; if (!(targetView = morphView->targets)) return AK_ERR; dst = (char *)destBuff; targetIdx = 0; for (; targetView; targetView = targetView->next) { if (progress && !progress(morph, targetIdx, morphView->nTargets, userdata)) return AK_ERR; /* Per morphable: walk inputs, write at the morphable's sub-slice using its own per-primitive stride and vertex count. */ targetDst = dst; for (inspMorphable = targetView->morphable; inspMorphable && (tinp = inspMorphable->input); inspMorphable = inspMorphable->next) { morphDst = targetDst + inspMorphable->bufferOffset; mStride = inspMorphable->stridePerVertex; mCount = inspMorphable->vertexCount; for (; tinp && (inp = tinp->input) && (acc = inp->accessor) && (buf = acc->buffer) && (src = (char *)buf->data + acc->byteOffset); tinp = tinp->next) { srcStride = (uint32_t)acc->byteStride; compSize = (uint32_t)acc->fillByteSize; intrOffset = tinp->intrOffset; dp = morphDst + intrOffset; sp = src; switch (compSize) { case 4: for (k = 0; k < mCount; k++) { memcpy(dp, sp, 4); dp += mStride; sp += srcStride; } break; case 8: for (k = 0; k < mCount; k++) { memcpy(dp, sp, 8); dp += mStride; sp += srcStride; } break; case 12: for (k = 0; k < mCount; k++) { memcpy(dp, sp, 12); dp += mStride; sp += srcStride; } break; case 16: for (k = 0; k < mCount; k++) { memcpy(dp, sp, 16); dp += mStride; sp += srcStride; } break; default: for (k = 0; k < mCount; k++) { memcpy(dp, sp, compSize); dp += mStride; sp += srcStride; } break; } } } /* Advance to the next target's slice. */ dst += targetView->interleaveBufferSize; targetIdx++; } if (progress && !progress(morph, targetIdx, morphView->nTargets, userdata)) return AK_ERR; return AK_OK; } /*============================================================================ * ak_morphInterleave *============================================================================*/ AK_EXPORT AkResult ak_morphInterleave(AkGeometry * __restrict baseMesh, AkMorph * __restrict morph, AkMorphInterleaveLayout layout, void * __restrict destBuff) { return ak_morphInterleaveInternal(baseMesh, morph, layout, destBuff, NULL, NULL); } AK_EXPORT AkResult ak_morphInterleaveWithProgress(AkGeometry * __restrict baseMesh, AkMorph * __restrict morph, AkMorphInterleaveLayout layout, void * __restrict destBuff, AkMorphProgressFn progress, void * __restrict userdata) { return ak_morphInterleaveInternal(baseMesh, morph, layout, destBuff, progress, userdata); } ================================================ FILE: src/node/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/node/node.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "../../include/ak/assetkit.h" #include "../../include/ak/node.h" #include "../../include/ak/coord-util.h" #include "../../include/ak/instance.h" #include #include AK_EXPORT void ak_addSubNode(AkNode * __restrict parent, AkNode * __restrict subnode, bool fixCoordSys) { assert(parent != subnode); if (subnode->parent) { if (subnode->parent->chld == subnode) { if (subnode->next) subnode->parent->chld = subnode->next; else subnode->parent->chld = subnode->prev; } } if (subnode->next) subnode->next->prev = subnode->prev; if (subnode->prev) subnode->prev->next = subnode->next; if (parent->chld) parent->chld->prev = subnode; subnode->next = parent->chld; subnode->prev = NULL; subnode->parent = parent; parent->chld = subnode; /* fix node transforms after attached to new node */ if (fixCoordSys) ak_fixNodeCoordSys(subnode); } AK_EXPORT AkNode * ak_nodeMake(AkDoc * __restrict doc, AkNode * __restrict parent, const char * name) { AkHeap *heap; AkNode *node; void *memparent; if (!doc) return NULL; heap = ak_heap_getheap(doc); memparent = parent ? (void *)parent : (void *)doc; node = ak_heap_calloc(heap, memparent, sizeof(*node)); node->visible = true; if (name) node->name = ak_heap_strdup(heap, node, name); /* Attach to the parent's children chain. fixCoordSys=false — the caller is presumed to know the desired orientation; baking coord-sys conversions into a programmatically created node would surprise more often than help. */ if (parent) ak_addSubNode(parent, node, false); return node; } AK_EXPORT AkNode * ak_nodeFindChildByName(AkNode * __restrict parent, const char * name) { AkNode *child; if (!parent || !name) return NULL; for (child = parent->chld; child; child = child->next) { if (child->name && strcmp(child->name, name) == 0) return child; } return NULL; } AK_EXPORT AkNode * ak_nodeFindOrMakeChild(AkDoc * __restrict doc, AkNode * __restrict parent, const char * name) { AkNode *existing; existing = ak_nodeFindChildByName(parent, name); if (existing) return existing; return ak_nodeMake(doc, parent, name); } AK_EXPORT AkInstanceBase * ak_nodeAttachCamera(AkNode * __restrict node, AkCamera * __restrict cam) { AkHeap *heap; AkInstanceBase *inst, *head; if (!node || !cam) return NULL; heap = ak_heap_getheap(node); inst = ak_instanceMake(heap, node, cam); inst->type = AK_INSTANCE_CAMERA; /* `ak_instanceMake` doesn't backfill `node`; do it here so downstream APIs (`ak_instanceName`, list-unlink, move-to-subnode) don't see a NULL parent. */ inst->node = node; /* Chain in front of any existing camera instance(s) on the node. Maintain `prev` on the existing head so the chain stays doubly-linked. */ head = node->camera; inst->next = head; inst->prev = NULL; if (head) { head->prev = inst; } node->camera = inst; return inst; } AK_EXPORT AkInstanceBase * ak_nodeAttachLight(AkNode * __restrict node, AkLight * __restrict light) { AkHeap *heap; AkInstanceBase *inst, *head; if (!node || !light) return NULL; heap = ak_heap_getheap(node); inst = ak_instanceMake(heap, node, light); inst->type = AK_INSTANCE_LIGHT; inst->node = node; head = node->light; inst->next = head; inst->prev = NULL; if (head) { head->prev = inst; } node->light = inst; return inst; } AK_EXPORT void ak_nodeSetTransformMatrix(AkNode * __restrict node, const float matrix[16]) { AkHeap *heap; AkObject *obj; AkMatrix *mat; if (!node || !matrix) return; heap = ak_heap_getheap(node); /* Wrap the matrix in a typed AkObject (AKT_MATRIX). The inline payload `data[]` is sized to hold one AkMatrix; ak_objAlloc also sets pData to point at it. */ obj = ak_objAlloc(heap, node, sizeof(AkMatrix), AKT_MATRIX, true); mat = (AkMatrix *)ak_objGet(obj); memcpy(mat->val, matrix, sizeof(float) * 16); /* Lazily allocate the AkTransform shell — newly-created nodes (e.g. via ak_nodeMake) have transform == NULL until the first write. */ if (!node->transform) node->transform = ak_heap_calloc(heap, node, sizeof(AkTransform)); /* Replace any existing transform item chain with the single matrix. The AKT_MATRIX form composes the same world-pose, so callers don't have to track a TRS chain themselves. */ node->transform->item = obj; } AK_EXPORT AkNode * ak_sceneFindRoot(AkVisualScene * __restrict scene, const char * name) { AkNode *node; if (!scene || !name) return NULL; for (node = scene->node; node; node = node->next) { if (node->name && strcmp(node->name, name) == 0) return node; } return NULL; } AK_EXPORT AkNode * ak_sceneFindOrMakeRoot(AkDoc * __restrict doc, AkVisualScene * __restrict scene, const char * name) { AkHeap *heap; AkNode *node; AkNode *last; if (!doc || !scene) return NULL; if ((node = ak_sceneFindRoot(scene, name))) return node; /* Allocate the new root node parented on the visual scene so its lifetime tracks the scene's. ak_nodeMake parents under another AkNode, which is the wrong shape here — we want a root, not a child — so we allocate directly. */ heap = ak_heap_getheap(doc); node = ak_heap_calloc(heap, scene, sizeof(*node)); node->visible = true; if (name) node->name = ak_heap_strdup(heap, node, name); /* Append to the visual scene's root chain. Empty scene → become the head; otherwise walk to the tail and link in. */ if (!scene->node) { scene->node = node; } else { for (last = scene->node; last->next; last = last->next) { } last->next = node; node->prev = last; } return node; } ================================================ FILE: src/platform/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/platform/dylib.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include "dylib.h" #ifdef AK_WINAPI # include #else # include #endif #include static const char AK_DYLIB_ANCHOR = 0; AK_HIDE void* ak_dylib_open(const char * __restrict path) { if (!path) return NULL; #ifdef AK_WINAPI return (void *)LoadLibraryA(path); #else return dlopen(path, RTLD_LAZY | RTLD_LOCAL); #endif } static void* ak_dylib_openSibling(const char * __restrict file) { char modpath[1024]; char path[1024]; char *sep; char *sep2; size_t dirlen; size_t filelen; int len; if (!file) return NULL; #ifdef AK_WINAPI { HMODULE mod; DWORD n; mod = NULL; if (!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)&AK_DYLIB_ANCHOR, &mod)) return NULL; n = GetModuleFileNameA(mod, modpath, sizeof(modpath)); if (n == 0 || n >= sizeof(modpath)) return NULL; } #else { Dl_info info; if (!dladdr((const void *)&AK_DYLIB_ANCHOR, &info) || !info.dli_fname) return NULL; len = snprintf(modpath, sizeof(modpath), "%s", info.dli_fname); if (len <= 0 || (size_t)len >= sizeof(modpath)) return NULL; } #endif sep = strrchr(modpath, '/'); sep2 = strrchr(modpath, '\\'); if (!sep || (sep2 && sep2 > sep)) sep = sep2; if (!sep) return NULL; dirlen = (size_t)(sep - modpath) + 1; filelen = strlen(file); if (dirlen + filelen >= sizeof(path)) return NULL; memcpy(path, modpath, dirlen); memcpy(path + dirlen, file, filelen + 1); return ak_dylib_open(path); } AK_HIDE void* ak_dylib_openName(const char * __restrict name) { char path[256]; void *lib; int len; if (!name) return NULL; #ifdef AK_WINAPI len = snprintf(path, sizeof(path), "%s.dll", name); #elif defined(__APPLE__) len = snprintf(path, sizeof(path), "lib%s.dylib", name); #else len = snprintf(path, sizeof(path), "lib%s.so", name); #endif if (len <= 0 || (size_t)len >= sizeof(path)) return NULL; if ((lib = ak_dylib_openSibling(path))) return lib; return ak_dylib_open(path); } AK_HIDE void* ak_dylib_sym(void * __restrict lib, const char * __restrict name) { if (!lib || !name) return NULL; #ifdef AK_WINAPI return (void *)GetProcAddress((HMODULE)lib, name); #else return dlsym(lib, name); #endif } AK_HIDE void ak_dylib_close(void * __restrict lib) { if (!lib) return; #ifdef AK_WINAPI FreeLibrary((HMODULE)lib); #else dlclose(lib); #endif } ================================================ FILE: src/platform/dylib.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_platform_dylib_h #define ak_platform_dylib_h #include "../common.h" AK_HIDE void* ak_dylib_open(const char * __restrict path); AK_HIDE void* ak_dylib_openName(const char * __restrict name); AK_HIDE void* ak_dylib_sym(void * __restrict lib, const char * __restrict name); AK_HIDE void ak_dylib_close(void * __restrict lib); #endif /* ak_platform_dylib_h */ ================================================ FILE: src/profile.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "common.h" #include "profile.h" #include #include AkProfileType *ak__profileTypes; uint32_t ak__profileTypesCount; char ak__platform[64]; AkProfile* ak_profile(struct AkEffect * __restrict effect, AkProfile * __restrict after) { AkHeapNode *hnodeParent, *hnode; AkProfileType profileType; hnodeParent = ak__alignof(effect); profileType = ak_profileType(effect); /* get next profile */ if (after) { hnode = ak__alignof(after); hnode = hnode->next; } else { hnode = hnodeParent->chld; } while (hnode) { if (ak_typeidh(hnode) == AKT_PROFILE) { AkProfile *profile; profile = ak__alignas(hnode); if (profile->type == profileType) return profile; } hnode = hnode->next; } return NULL; } AkProfileType ak_profileType(struct AkEffect * __restrict effect) { AkProfileType defaultProfile; bool useEffectProfile; uint32_t i; bool foundProfile; useEffectProfile = ak_opt_get(AK_OPT_EFFECT_PROFILE); foundProfile = false; /* check if profile type is overridden by effect or not if option is set */ /* to use this the AK_OPT_EFFECT_PROFILE option must set to true */ if (useEffectProfile) { for (i = 0; i < ak__profileTypesCount; i++) { if (effect->bestProfile == ak__profileTypes[i]) { foundProfile = true; break; } } if (foundProfile) return effect->bestProfile; } /* check default profile is supported or not */ defaultProfile = (AkProfileType)ak_opt_get(AK_OPT_DEFAULT_PROFILE); for (i = 0; i < ak__profileTypesCount; i++) { if (defaultProfile == ak__profileTypes[i]) { foundProfile = true; break; } } if (foundProfile) return defaultProfile; /* return first supported profile */ /* default is profile_COMMON -> [0] or [count - 1] */ return ak__profileTypes[0]; } uint32_t ak_supportedProfiles(AkProfileType ** profileTypes) { *profileTypes = ak__profileTypes; return ak__profileTypesCount; } void ak_setSupportedProfiles(AkProfileType profileTypes[], uint32_t count) { uint32_t i; bool foundCommon; foundCommon = false; for (i = 0; i < count; i++) { if (profileTypes[i] == AK_PROFILE_TYPE_COMMON) { foundCommon = true; break; } } if (!foundCommon) count++; if (ak__profileTypes) ak_free(ak__profileTypes); ak__profileTypes = ak_malloc(NULL, sizeof(AkProfileType) * count); memcpy(ak__profileTypes, profileTypes, sizeof(AkProfileType) * count); /* make sure that common is exist */ ak__profileTypes[count - 1] = AK_PROFILE_TYPE_COMMON; ak__profileTypesCount = count; } const char* ak_platform(void) { return ak__platform; } void ak_setPlatform(const char platform[64]) { strcpy(ak__platform, platform); } AK_HIDE void ak_profile_init(void) { /* default platform */ strcpy(ak__platform, "GL"); ak__profileTypes = ak_malloc(NULL, sizeof(AkProfileType)); *ak__profileTypes = AK_PROFILE_TYPE_COMMON; ak__profileTypesCount = 1; } AK_HIDE void ak_profile_deinit(void) { ak_free(ak__profileTypes); } AK_EXPORT AkProfileCommon* ak_getProfileCommon(struct AkEffect * __restrict effect) { AkProfile *profile; profile = effect->profile; while (profile) { if (profile->type == AK_PROFILE_TYPE_COMMON) break; profile = profile->next; } return (AkProfileCommon *)profile; } AK_EXPORT AkTechniqueFxCommon* ak_getProfileTechniqueCommon(struct AkEffect * __restrict effect) { AkProfileCommon *profileCommon; AkTechniqueFx *techn; AkTechniqueFxCommon *technCommon; if ((profileCommon = ak_getProfileCommon(effect)) && (techn = profileCommon->technique)) { return techn->common; } return NULL; } ================================================ FILE: src/profile.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_src_profile_h #define ak_src_profile_h #include "common.h" AK_HIDE void ak_profile_init(void); AK_HIDE void ak_profile_deinit(void); #endif /* ak_src_profile_h */ ================================================ FILE: src/resc/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/resc/path.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "../../include/ak/path.h" #include "../../include/ak/string.h" #include #ifdef _MSC_VER # ifndef PATH_MAX # define PATH_MAX 260 # endif #endif #define CHR_SLASH '/' #define CHR_BACK_SLASH '\\' #define CHR_COLON ':' #define STR_SLASH "/" #define APPEND_SLASH \ do { \ *buf++ = CHR_SLASH; \ len++; \ } while (0) #define AK_STRTRM_SET(X) \ do { \ *it2 = *it1; \ len++; \ it1++; \ it2++; \ skp = X; \ } while (0) AK_EXPORT const char * ak_path_fragment(const char *path) { return strrchr(path, '#'); } AK_EXPORT int ak_path_isfile(const char *path) { const char *it; it = path; while (*it == ' ') it++; if (*it == '/' || *it == '\\') return 1; if (strstr(it, "://")) if (strncasecmp("file", it, strstr(it, "://") - it) != 0) return 0; return 1; } AK_EXPORT char* ak_path_dir(AkHeap * __restrict heap, void * __restrict memparent, const char * __restrict path) { char *dir; char *slash; slash = strrchr(path, '/'); if (!slash) return ak_heap_strdup(heap, memparent, "./"); dir = ak_heap_strndup(heap, memparent, path, slash - path); return dir; } AK_EXPORT size_t ak_path_trim(const char *path, char *trimmed) { const char *it1; char *it2; size_t len; int skp; int proto; int local; len = skp = proto = 0; it1 = path; it2 = trimmed; while (*it1 == ' ') it1++; local = *it1 == '/' || *it1 == '\\'; while (*it1) { if (*it1 == '/' || *it1 == '\\') { if (skp != 0) { if (proto != 1) { it1++; continue; } proto = 2; } AK_STRTRM_SET(1); } else { proto = !proto && *it1 == ':' && !local; AK_STRTRM_SET(0); } } while (*--it2 && (*it2 == ' ' || *it2 == '/' || *it2 == '\\')) len--; *(it2 + (len > 0)) = '\0'; return len; } AK_EXPORT int ak_path_join(char *fragments[], char *buf, size_t *size) { const char *frag; const char *frag_end; size_t len; size_t frag_len; size_t frag_idx; size_t frag_idx_v; char c; if (!fragments || !*fragments /* there are no fragments to join */ || !buf || !size) return -EINVAL; frag_idx_v = frag_idx = len = 0; /* build path */ while ((frag = fragments[frag_idx++])) { frag_len = strlen(frag); frag_end = frag + frag_len - 1; if (frag_len == 0) continue; /* starts with slash e.g. /usr */ c = *frag; if (frag_idx_v == 0 && (c == CHR_SLASH || c == CHR_BACK_SLASH)) { APPEND_SLASH; frag++; /* avoid extra slash after non-protocol frag like /http:/ -> /http:// */ frag_idx_v++; } /* l-trim */ while ((c = *frag) != '\0' && (c == CHR_SLASH || c == CHR_BACK_SLASH)) frag++; /* r-trim */ while (frag_end > frag && (c = *frag_end) != '\0' && (c == CHR_SLASH || c == CHR_BACK_SLASH)) frag_end--; /* only slashes */ if (!*frag) { if (len == 0) APPEND_SLASH; continue; } if (len > 1 && *(buf - 1) != CHR_SLASH) APPEND_SLASH; /* protocol e.g. http://, tcp:// */ if (len > 1 && frag_idx_v == 1 && *(buf - 2) == CHR_COLON) APPEND_SLASH; frag_len = ++frag_end - frag; len += frag_len; memcpy(buf, frag, frag_len); buf += frag_len; frag_idx_v++; } memset(buf, '\0', 1); *size = len; return 0; } AK_EXPORT const char* ak_fullpath(AkDoc * __restrict doc, const char * __restrict ref, char * __restrict buf) { size_t pathlen; const char *ptr; char *fileprefix = "file:///"; char *fragments[] = { (char *)doc->inf->dir, "/", (char *)ref, NULL }; if (strncmp(ref, fileprefix, strlen(fileprefix)) == 0) { return ref + strlen(fileprefix) #ifndef _WIN32 - 1 #endif ; } ak_path_join(fragments, buf, &pathlen); ptr = ak_strltrim_fast(buf); return ptr; } ================================================ FILE: src/resc/resource.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "resource.h" #include "../../include/ak/path.h" #include #include #include #include #include #include static AkFetchFromURLHandler ak__urlhandler = NULL; static AkHeap ak__resc_heap = { .flags = 0 }; #define resc_heap &ak__resc_heap AK_EXPORT void ak_setFetchFromURLHandler(AkFetchFromURLHandler handler) { ak__urlhandler = handler; } AkResource * ak_resc_ins(const char *url) { void *pFoundResc; AkResource *resc, *foundResc; char *tmp, *trimmedURL; const char *fragment; size_t trimmedURLSize; AkResult ret; tmp = malloc(sizeof(*tmp) * strlen(url)); ak_path_trim(url, tmp); /* ak_path_fragment returns strrchr(path, '#') which is NULL when the URL has no fragment — common for malformed DAE source attributes ("source=foo" instead of "source=#foo", typical of Blender/Maya exports). Treat the whole string as the resource identifier in that case rather than dereferencing NULL. */ fragment = ak_path_fragment(tmp); if (fragment && strlen(fragment) > 0) trimmedURLSize = fragment - tmp; else trimmedURLSize = strlen(tmp); trimmedURL = ak_heap_strndup(resc_heap, NULL, tmp, trimmedURLSize); free(tmp); ret = ak_heap_getMemById(resc_heap, trimmedURL, &pFoundResc); if (ret != AK_EFOUND) { foundResc = pFoundResc; foundResc->refc++; ak_free(trimmedURL); return foundResc; } resc = ak_heap_calloc(resc_heap, NULL, sizeof(*resc)); resc->url = trimmedURL; ak_heap_setpm(trimmedURL, resc); ak_heap_setId(resc_heap, ak__alignof(resc), trimmedURL); resc->refc++; if (ak_path_isfile(url)) { resc->isdwn = true; resc->islocal = true; resc->localurl = resc->url; resc->result = ak_load(&resc->doc, resc->url, AK_FILE_TYPE_COLLADA); return resc; } /* download the file, file must be downloadable, e.g not live stream * to do this the remote file must send file size */ if (!ak__urlhandler) { ak_free(resc); return NULL; } resc->localurl = ak__urlhandler(resc->url); ak_heap_setpm((void *)resc->localurl, resc); resc->result = ak_load(&resc->doc, resc->url, AK_FILE_TYPE_COLLADA); return resc; } void ak_resc_ref(AkResource *resc) { resc->refc++; } int ak_resc_unref(AkResource *resc) { resc->refc--; if (resc->refc <= 0) { if (resc->doc) ak_free(resc->doc); ak_free(resc); return -1; } return 0; } int ak_resc_unref_url(const char *url) { void *resc; char *trimmed; AkResult ret; trimmed = NULL; ak_path_trim(url, trimmed); if (trimmed) { ret = ak_heap_getMemById(resc_heap, trimmed, &resc); free(trimmed); if (ret != AK_EFOUND) return ak_resc_unref(resc); } return -1; } void ak_resc_print(void) { ak_heap_printKeys(resc_heap); } AK_HIDE void ak_resc_init(void) { ak_heap_init(resc_heap, NULL, NULL, NULL); } AK_HIDE void ak_resc_deinit(void) { ak_heap_destroy(resc_heap); } ================================================ FILE: src/resc/resource.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_resource_h #define ak_resource_h #include "../common.h" #include typedef struct AkResource { AkDoc *doc; const char *url; const char *localurl; int64_t refc; time_t dwntime; bool isdwn; bool islocal; AkResult result; } AkResource; AK_HIDE void ak_resc_init(void); AK_HIDE void ak_resc_deinit(void); AkResource * ak_resc_ins(const char *url); void ak_resc_ref(AkResource *resc); int ak_resc_unref(AkResource *resc); int ak_resc_unref_url(const char *url); void ak_resc_print(void); #endif /* ak_resource_h */ ================================================ FILE: src/resc/url.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include "../../include/ak/path.h" #include "resource.h" #include #include #include #include #ifdef _MSC_VER # ifndef PATH_MAX # define PATH_MAX 260 # endif #endif void ak_url_init(void *parent, char *urlstring, AkURL *dest) { assert(parent && "parent must be malloced by ak_heap_alloc/calloc"); if (*urlstring == '#') { AkHeap *heap; dest->reserved = NULL; heap = ak_heap_getheap(parent); dest->doc = ak_heap_data(heap); dest->url = ak_heap_strdup(heap, parent, urlstring); return; } /* TODO: */ dest->reserved = ak_resc_ins(urlstring); dest->url = ak_path_fragment(urlstring); dest->doc = ((AkResource *)dest->reserved)->doc; } void ak_url_dup(AkURL *src, void *parent, AkURL *dest) { assert(parent && "parent must be malloced by ak_heap_alloc/calloc"); memcpy(dest, src, sizeof(AkURL)); if (!src->reserved) { AkHeap *heap; heap = ak_heap_getheap(parent); dest->url = ak_heap_strdup(heap, parent, dest->url); return; } /* TODO: */ } void ak_url_init_with_id(AkHeapAllocator *alc, void *parent, char *idstirng, AkURL *dest) { char *urlstring; urlstring = ak_url_string(alc, idstirng); ak_url_init(parent, urlstring, dest); alc->free(urlstring); } char * ak_url_string(AkHeapAllocator *alc, char *id) { char *urlstring; urlstring = alc->malloc(strlen(id) + 2); *urlstring = '#'; strcpy(urlstring + 1, id); return urlstring; } void ak_url_ref(AkURL *url) { if (!url->reserved) return; ak_resc_ref(url->reserved); } void ak_url_unref(AkURL *url) { if (!url->reserved) return; if (ak_resc_unref(url->reserved) < 0) { url->reserved = NULL; url->url = NULL; } } AK_EXPORT void * ak_getObjectByUrl(AkURL * __restrict url) { if (url->ptr) return url->ptr; if (url->doc) return ak_getObjectById(url->doc, url->url + 1); return NULL; } const char* ak_getFile(const char *url) { if (ak_path_isfile(url)) return url; /* download the file, file must be downloadable, e.g not live stream * to do this the remote file must send file size */ /* return local URL TODO: option for cache time, option for how to store this file, option for when to remove it */ return NULL; } char* ak_getFileFrom(AkDoc *doc, const char *url) { char pathbuf[PATH_MAX]; const char *path; path = ak_fullpath(doc, url, pathbuf); return ak_strdup(NULL, path); } AK_EXPORT void ak_retainURL(void * __restrict obj, AkURL * __restrict url) { AkHeap *heap; AkHeapNode *hnode; AkUrlNode *urlNode; int *refc; void *found, *last, **emptyslot; size_t len; AkResult ret; heap = ak_heap_getheap(obj); hnode = ak__alignof(obj); /* check if object is available */ ret = ak_heap_getNodeByURL(heap, url, &hnode); if (ret != AK_OK || !hnode) return; urlNode = ak_heap_ext_add(heap, hnode, AK_HEAP_NODE_FLAGS_URL); refc = ak_heap_ext_add(heap, hnode, AK_HEAP_NODE_FLAGS_REFC); len = urlNode->len; found = urlNode->urls[0]; last = urlNode->urls[len]; emptyslot = NULL; while (found != last) { /* already retained */ if (found == url) return; if (found == NULL && !emptyslot) emptyslot = found; } /* retain */ (*refc)++; if (emptyslot) { *emptyslot = url; return; } /* append url to retained url lists */ urlNode->len = len + 1; urlNode->urls = heap->allocator->realloc(urlNode->urls, sizeof(void *) * urlNode->len); urlNode->urls[len] = url; } AK_EXPORT void ak_releaseURL(void * __restrict obj, AkURL * __restrict url) { AkHeapNode *hnode; AkUrlNode *urlNode; void *urlobj; void **found, **it, *last; size_t len; hnode = ak__alignof(obj); /* check if object is available */ if (!(urlobj = ak_getObjectByUrl(url))) return; urlNode = ak_heap_ext_get(hnode, AK_HEAP_NODE_FLAGS_URL); len = urlNode->len; it = urlNode->urls[0]; last = urlNode->urls[len]; found = NULL; while (it != last) { /* already retained */ if (*it == url) { found = it; break; } it++; } if (!found) return; /* empty slot */ *found = NULL; ak_release(urlobj); } ================================================ FILE: src/sid.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "common.h" #include "../include/ak/profile.h" #include "sid.h" #include #include #include AkHeap ak__sidconst_heap = { .flags = 0 }; #define sidconst_heap &ak__sidconst_heap AK_EXPORT const char * ak_sid_get(void *memnode) { AkHeapNode *heapNode; AkSIDNode *sidnode; heapNode = ak__alignof(memnode); sidnode = ak_heap_ext_get(heapNode, AK_HEAP_NODE_FLAGS_SID); if (!sidnode) return NULL; return sidnode->sid; } AK_EXPORT const char * ak_sid_geta(void *memnode, void *memptr) { AkHeapNode *heapNode; AkSIDNode *sidnode; char *ptr; uint16_t off; size_t sidCount; size_t i; heapNode = ak__alignof(memnode); sidnode = ak_heap_ext_get(heapNode, AK_HEAP_NODE_FLAGS_SID); if (!sidnode) return NULL; sidCount = *(size_t *)sidnode->sid; off = (uint16_t)((char *)memptr - (char *)memnode); ptr = (char *)sidnode->sid + sizeof(size_t); for (i = 0; i < sidCount; i++) { if (*(uint16_t *)ptr == off) return ptr + sizeof(uint16_t); ptr += sizeof(uint16_t) + sizeof(const char **); } return NULL; } AK_EXPORT void ak_sid_set(void *memnode, const char * __restrict sid) { AkSIDNode *sidnode; AkHeapNode *heapNode; AkHeap *heap; heapNode = ak__alignof(memnode); heap = ak_heap_getheap(memnode); sidnode = ak_heap_ext_add(heap, heapNode, AK_HEAP_NODE_FLAGS_SID); sidnode->sid = sid; /* mark parents */ while (heapNode->prev) { /* we foound parent */ if (ak_heap_chld(heapNode->prev) == heapNode) heapNode->prev->flags |= AK_HEAP_NODE_FLAGS_SID_CHLD; heapNode = heapNode->prev; } /* TODO: invalidate refs */ } AK_EXPORT void ak_sid_seta(void *memnode, void *memptr, const char * __restrict sid) { AkSIDNode *sidnode; AkHeapNode *heapNode; AkHeap *heap; char *sidptr; uint16_t off; uintptr_t sidptrval; int off0; int itmsize; heapNode = ak__alignof(memnode); heap = ak_heap_getheap(memnode); sidnode = ak_heap_ext_add(heap, heapNode, AK_HEAP_NODE_FLAGS_SID); off0 = sizeof(size_t); off = (uint16_t)((char *)memptr - (char *)memnode); itmsize = sizeof(uint16_t) + sizeof(uintptr_t); if (!sidnode->sids) { sidnode->sids = heap->allocator->calloc(1, off0 + itmsize); } else { size_t newsize; newsize = *(size_t *)((char *)sidnode->sids) + 1; newsize = newsize * itmsize; sidnode->sids = heap->allocator->realloc(sidnode->sids, newsize + off0); } sidptr = sidnode->sids; sidptr += off0 + itmsize * (*(size_t *)sidptr)++; *(uint16_t *)sidptr = off; sidptr += sizeof(uint16_t); sidptrval = (uintptr_t)sid; memcpy(sidptr, &sidptrval, sizeof(uintptr_t)); /* mark parents */ while (heapNode->prev) { /* we foound parent */ if (ak_heap_chld(heapNode->prev) == heapNode) heapNode->prev->flags |= AK_HEAP_NODE_FLAGS_SID_CHLD; heapNode = heapNode->prev; } /* TODO: invalidate refs */ } AK_EXPORT void ak_sid_dup(void *newMemnode, void *oldMemnode) { ak_sid_set(newMemnode, ak_heap_strdup(ak_heap_getheap(newMemnode), newMemnode, ak_sid_get(oldMemnode))); } void ak_sid_destroy(AkHeap * __restrict heap, AkSIDNode * __restrict snode) { AkHeapAllocator *alc; alc = heap->allocator; if (snode->refs) alc->free(snode->refs); if (snode->sids) alc->free(snode->sids); } AK_HIDE ptrdiff_t ak_sidElement(AkContext * __restrict ctx, const char * __restrict target, void ** __restrict idnode, bool * __restrict isdot) { AkHeap *heap; AkDoc *doc; AkHeapNode *hnode, *parent; const char *it; char *sidp; AkResult ret; heap = NULL; it = ak_strltrim_fast(target); sidp = strchr(target, '/'); *isdot = *it == '.' || !sidp; if (*idnode && *isdot) { hnode = ak__alignof(*idnode); goto again; } doc = ctx->doc; heap = ak_heap_getheap(doc); /* if there is no "/" then assume that this is "./" */ if (*isdot) { AkSidConstr *constr; /* .[attr] */ if (*it == '.' && !sidp) { it++; goto ret; } /* else ./sid.../sid.[attr] */ /* find id node */ hnode = ak__alignof(target); again: constr = ak_sidConstraintsOf(ak_typeid((void *)target)); parent = ak_heap_parent(hnode); /* find by constraint / semantic */ if (parent && constr) { while (parent) { AkSidConstrItem *constrItem; AkTypeId pTypeId; bool found; found = false; pTypeId = ak_typeid(ak__alignas(parent)); constrItem = constr->item; while (constrItem) { if (constrItem->constr == pTypeId) { found = true; break; } constrItem = constrItem->next; } if (found) break; parent = ak_heap_parent(parent); } } /* check id nodes */ else { while (parent) { void *id; id = ak_heap_getId(heap, parent); if (id) break; parent = ak_heap_parent(parent); } } *idnode = ak__alignas(parent); /* no need to offset again */ if (*idnode) return 0; } else if (sidp) { char *id; ptrdiff_t idlen; /* TODO: check # character and external ID element ? */ idlen = sidp - it; id = malloc(sizeof(char) * idlen + 1); memcpy(id, it, idlen); id[idlen] = '\0'; ret = ak_heap_getNodeById(heap, (void *)id, &hnode); free(id); if (ret != AK_OK || !idnode) goto err; *idnode = ak__alignas(hnode); it = sidp; } else { goto err; } ret: return it - target + 1; err: return -1; } AK_HIDE AkHeapNode* ak_sid_profile(AkContext * __restrict ctx, AkHeapNode * __restrict parent, AkHeapNode * __restrict after) { AkProfile *profile; AkTechniqueHint *hint; const char *platform; profile = ak_profile(ak__alignas(parent), after ? ak__alignas(after) : NULL); /* check hint for profile */ if ((!ctx->techniqueHint || !ctx->techniqueHint->profile) && profile) return ak__alignof(profile); hint = ctx->techniqueHint; platform = ak_platform(); while (profile) { if (profile->type == hint->profileType) goto ret; if (!(profile = ak_profile(ak__alignas(parent), profile))) goto err; /* check platform */ if (hint->platform) { if (strcmp(platform, hint->platform) == 0) profile = ak_profile(ak__alignas(parent), profile); } } ret: if (profile) return ak__alignof(profile); err: return NULL; } AK_HIDE AkHeapNode* ak_sid_technique(AkContext * __restrict ctx, AkHeapNode * __restrict chld) { AkHeapNode *orig; orig = chld; /* first check hint for technique */ if (ctx->techniqueHint && ctx->techniqueHint->ref) { AkTechniqueHint *hint; const char *platform; hint = ctx->techniqueHint; platform = ak_platform(); /* get desired technique */ while (chld) { AkSIDNode *snode; snode = ak_heap_ext_get(chld, AK_HEAP_NODE_FLAGS_SID); if (snode->sid && strcmp(snode->sid, hint->ref) == 0) goto ret; if (!(chld = chld->next)) goto err; /* check platform */ if (hint->platform) { if (strcmp(platform, hint->platform) == 0) chld = chld->next; } } } /* check global options to get active technique name */ else { /* default: try to get sid="common" or technique_common */ char **tnames, *tname; /* get global techniques with order */ if (ak_typeidh(chld) == AKT_TECHNIQUE_FX) tnames = (char **)ak_opt_get(AK_OPT_TECHNIQUE_FX); else tnames = (char **)ak_opt_get(AK_OPT_TECHNIQUE); if (!*tnames) goto err; /* get desired technique */ while (chld) { tname = *tnames; do { AkSIDNode *snode; uint32_t i; i = 0; snode = ak_heap_ext_get(chld, AK_HEAP_NODE_FLAGS_SID); if (snode && snode->sid && strcmp(snode->sid, tname) == 0) goto ret; tname = tnames[++i]; } while (tname); chld = chld->next; } } /* default: no technique found use first one */ chld = orig; ret: return chld; err: return NULL; } AK_HIDE AkHeapNode* ak_sid_chldh(AkContext * __restrict ctx, AkHeapNode * __restrict parent, AkHeapNode * __restrict after) { AkHeapNode *chld; /* select active profile if parent is */ if (ak_typeidh(parent) == AKT_EFFECT) return ak_sid_profile(ctx, parent, after); /* get child element */ if (after) chld = after->next; else chld = ak_heap_chld(parent); if (!chld) return NULL; /* check active technique if child element is */ if (ak_typeidh(chld) == AKT_TECHNIQUE_FX || ak_typeidh(chld) == AKT_TECHNIQUE) chld = ak_sid_technique(ctx, chld); return chld; } AK_EXPORT void * ak_sid_resolve(AkContext * __restrict ctx, const char * __restrict target, const char ** __restrict attribString) { AkHeapNode *idnode, *sidnode, *it, *chld; char *siddup, *sid_it, *saveptr, *attrLoc; void *found; AkHeapNode **buf[2]; void *elm; size_t bufl[2], bufc[2], bufi[2]; int bufidx; ptrdiff_t sidoff; bool isdot; elm = NULL; sidoff = ak_sidElement(ctx, target, &elm, &isdot); if (sidoff == -1 || !elm) return NULL; bufi[0] = bufi[1] = bufc[0] = bufc[1] = bufidx = 0; bufl[0] = bufl[1] = 4; /* start point */ buf[0] = malloc(sizeof(*buf[0]) * bufl[0]); buf[1] = malloc(sizeof(*buf[0]) * bufl[0]); if (attribString) *attribString = NULL; again: idnode = ak__alignof(elm); sidnode = NULL; found = NULL; siddup = strdup(target + sidoff); attrLoc = strchr(siddup, '.'); sid_it = strtok_r(siddup, "/ \t", &saveptr); if (attrLoc) attrLoc[0] = '\0'; if (!idnode) goto err; /* maybe id node have sid too! */ if (idnode->flags & AK_HEAP_NODE_FLAGS_SID) { chld = idnode; buf[bufidx][bufi[bufidx]] = chld; bufc[bufidx] = 1; } else { chld = ak_sid_chldh(ctx, idnode, NULL); if (!chld) goto err; while (chld) { if (bufi[bufidx] == bufl[bufidx]) { bufl[bufidx] += 16; buf[bufidx] = realloc(buf[bufidx], sizeof(*buf[bufidx]) * bufl[bufidx]); } buf[bufidx][bufi[bufidx]] = chld; bufi[bufidx]++; bufc[bufidx]++; chld = ak_sid_chldh(ctx, idnode, chld); } } bufi[bufidx] = bufi[!bufidx] = 0; /* breadth-first search */ while (bufc[bufidx] > 0) { it = buf[bufidx][bufi[bufidx]]; if (it->flags & AK_HEAP_NODE_FLAGS_SID) { AkSIDNode *snode; snode = ak_heap_ext_get(it, AK_HEAP_NODE_FLAGS_SID); if (snode->sid && strcmp(snode->sid, sid_it) == 0) { char *tok; sidnode = it; /* go for next */ tok = strtok_r(NULL, "/ \t", &saveptr); if (!tok) goto ret; sid_it = tok; } /* check attrs */ if (snode->sids) { char *p, *end; size_t c; c = *(size_t *)snode->sids; p = (char *)snode->sids + sizeof(size_t); end = p + c * (sizeof(char **) + sizeof(uint16_t)); while (p != end) { p += sizeof(uint16_t); /* found sid in attr */ if (strcmp(*(char **)p, sid_it) == 0) { char *tok; sidnode = it; /* there is no way to go down */ tok = strtok_r(NULL, "/ \t", &saveptr); if (tok) goto err; goto ret; } p += sizeof(char **) + sizeof(uint16_t); } } } if (it->flags & AK_HEAP_NODE_FLAGS_SID_CHLD) { /* keep all children */ AkHeapNode *it2; it2 = ak_sid_chldh(ctx, it, NULL); while (it2) { if (bufi[!bufidx] == bufl[!bufidx]) { bufl[!bufidx] += 16; buf[!bufidx] = realloc(buf[!bufidx], sizeof(*buf[!bufidx]) * bufl[!bufidx]); } if (it2->flags & (AK_HEAP_NODE_FLAGS_SID_CHLD | AK_HEAP_NODE_FLAGS_SID)) { buf[!bufidx][bufi[!bufidx]] = it2; bufi[!bufidx]++; bufc[!bufidx]++; } it2 = ak_sid_chldh(ctx, it, it2); } } if (++bufi[bufidx] == bufc[bufidx]) { bufc[bufidx] = bufi[bufidx] = bufi[!bufidx] = 0; bufidx = !bufidx; } } if (isdot) { /* pick next parent*/ (void)ak_sidElement(ctx, target, &elm, &isdot); if (!elm) goto err; free(siddup); goto again; } ret: if (sidnode) { found = ak__alignas(sidnode); if (attribString && attrLoc) *attribString = ak_strdup(NULL, attrLoc + 1); } err: free(buf[0]); free(buf[1]); free(siddup); return found; } AK_EXPORT void * ak_sid_resolve_from(AkContext * __restrict ctx, const char * __restrict id, const char * __restrict sid, const char ** __restrict attribString) { char target[1024]; char *pTarget; target[0] = '\0'; strcat(target, id); strcat(target, "/"); strcat(target, sid); pTarget = target[0] == '#' ? &target[1] : target; return ak_sid_resolve(ctx, pTarget, attribString); } AK_EXPORT uint32_t ak_sid_attr_offset(const char *attr) { const char *attrs[] = {"X", "Y", "Z", "R", "G", "B", "A", "ANGLE", "S", "T", "U", "V", "W", "P", "Q"}; uint32_t offs[] = { 0, 1, 2, 0, 1, 2, 3, 3, 0, 1, 0, 1, 3, 2, 3 }; uint32_t i, len; if (attr) { len = sizeof(offs) / sizeof(offs[0]); for (i = 0; i < len; i++) { /* TODO: use strcmp after cache uppercased attr */ if (strcasecmp(attr, attrs[i]) == 0) { return offs[i]; } } return UINT32_MAX; } return 0; } AK_EXPORT void* ak_sid_resolve_val(AkContext * __restrict ctx, const char * __restrict target) { const char *attr; void *r; attr = NULL; r = ak_sid_resolve(ctx, target, &attr); /* currently only transform elements */ if (attr && ak_typeid(r) == AKT_OBJECT) { AkObject *obj; int off; obj = r; off = ak_sid_attr_offset(attr); switch (obj->type) { case AKT_ROTATE: { AkRotate *t; t = ak_objGet(obj); return &t->val[off]; } case AKT_TRANSLATE: { AkTranslate *t; t = ak_objGet(obj); return &t->val[off]; break; } case AKT_SCALE: { AkScale *t; t = ak_objGet(obj); return &t->val[off]; break; } default: break; } return (char *)obj->pData + off; } return r; } AK_HIDE void ak_sid_init(void) { ak_heap_init(sidconst_heap, NULL, ak_cmp_i32, NULL); ak_sidInitConstr(); } AK_HIDE void ak_sid_deinit(void) { ak_heap_destroy(sidconst_heap); } ================================================ FILE: src/sid.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_src_sid_h #define ak_src_sid_h #include "common.h" typedef struct AkSidConstrItem { AkTypeId constr; uint32_t chldCound; AkTypeId *constrChld; struct AkSidConstrItem *next; } AkSidConstrItem; typedef struct AkSidConstr { AkTypeId typeId; AkEnum method; /* 0: block-scope */ AkSidConstrItem *item; } AkSidConstr; AK_HIDE void ak_sid_init(void); AK_HIDE void ak_sid_deinit(void); void ak_sidInitConstr(void); AkSidConstr* ak_sidConstraintsOf(AkTypeId typeId); void ak_sidConstraintTo(AkTypeId typeId, AkSidConstrItem *constrs, AkEnum method); AK_HIDE AkHeapNode* ak_sid_profile(AkContext * __restrict ctx, AkHeapNode * __restrict parent, AkHeapNode * __restrict after); AK_HIDE AkHeapNode* ak_sid_technique(AkContext * __restrict ctx, AkHeapNode * __restrict chld); AK_HIDE AkHeapNode* ak_sid_chldh(AkContext * __restrict ctx, AkHeapNode * __restrict parent, AkHeapNode * __restrict after); AK_HIDE ptrdiff_t ak_sidElement(AkContext * __restrict ctx, const char * __restrict target, void ** __restrict idnode, bool * __restrict isdot); #endif /* ak_src_sid_h */ ================================================ FILE: src/sid_constr.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "common.h" #include "sid.h" #include #include #include extern AkHeap ak__sidconst_heap; #define sidconst_heap &ak__sidconst_heap AkTypeId ak__sidConstrChldParam[] = { AKT_NEWPARAM, AKT_PARAM, AKT_SETPARAM }; AkSidConstrItem ak__sidConstrEffect = { .constr = AKT_TECHNIQUE_FX, .chldCound = 3, .constrChld = ak__sidConstrChldParam, .next = &(AkSidConstrItem){ .constr = AKT_PROFILE, .chldCound = 3, .constrChld = ak__sidConstrChldParam, .next = &(AkSidConstrItem){ .constr = AKT_EFFECT, .chldCound = 3, .constrChld = ak__sidConstrChldParam, } } }; AkSidConstr* ak_sidConstraintsOf(AkTypeId typeId) { void *found; AkResult ret; ret = ak_heap_getMemById(sidconst_heap, &typeId, &found); if (ret == AK_OK) return found; return NULL; } void ak_sidConstraintTo(AkTypeId typeId, AkSidConstrItem *constrs, AkEnum method) { AkHeap *heap; AkSidConstr *constr; void *found; AkResult ret; heap = sidconst_heap; ret = ak_heap_getMemById(heap, &typeId, &found); if (ret == AK_OK) ak_free(found); if (!constrs) return; constr = ak_heap_alloc(heap, NULL, sizeof(*constr)); constr->method = method; constr->item = constrs; constr->typeId = typeId; ak_heap_setId(heap, ak__alignof(constr), &constr[0]); } void ak_sidInitConstr(void) { ak_sidConstraintTo(AKT_TEXTURE_NAME, &ak__sidConstrEffect, 0); ak_sidConstraintTo(AKT_TEXCOORD, &ak__sidConstrEffect, 0); ak_sidConstraintTo(AKT_TEXTURE, &ak__sidConstrEffect, 0); } ================================================ FILE: src/simd/arm.h ================================================ /* * Copyright (C) 2026 Recep Aslantas * * Licensed under the Apache License, Version 2.0. */ #ifndef assetkit_simd_arm_h #define assetkit_simd_arm_h #if defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM) # if defined(_MSC_VER) && (defined(_M_ARM64) || defined(_M_ARM64EC)) # include # else # include # endif # define AK_SIMD_ARM 1 # if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) # define AK_SIMD_ARM64 1 # endif #endif #endif /* assetkit_simd_arm_h */ ================================================ FILE: src/simd/base64.h ================================================ /* * Copyright (C) 2026 Recep Aslantas * * Licensed under the Apache License, Version 2.0. * * Portions of the ARM64 base64 decode mapping follow the technique used by * libbase64: * Copyright (c) 2005-2007 Nick Galbreath * Copyright (c) 2015-2018 Wojciech Mula * Copyright (c) 2016-2017 Matthieu Darbois * Copyright (c) 2013-2022 Alfred Klomp * BSD 2-Clause license. */ #ifndef assetkit_simd_base64_h #define assetkit_simd_base64_h #include "intrin.h" #include #include #if defined(AK_SIMD_ARM64) AK_INLINE int ak_simd_base64_decode_arm64(const uint8_t * __restrict src, size_t len, uint8_t * __restrict dst, size_t * __restrict consumed, size_t * __restrict written) { static const uint8_t dec_lut1[] = { 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 62U, 255U, 255U, 255U, 63U, 52U, 53U, 54U, 55U, 56U, 57U, 58U, 59U, 60U, 61U, 255U, 255U, 255U, 255U, 255U, 255U, }; static const uint8_t dec_lut2[] = { 0U, 255U, 0U, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 11U, 12U, 13U, 14U, 15U, 16U, 17U, 18U, 19U, 20U, 21U, 22U, 23U, 24U, 25U, 255U, 255U, 255U, 255U, 255U, 255U, 26U, 27U, 28U, 29U, 30U, 31U, 32U, 33U, 34U, 35U, 36U, 37U, 38U, 39U, 40U, 41U, 42U, 43U, 44U, 45U, 46U, 47U, 48U, 49U, 50U, 51U, 255U, 255U, 255U, 255U, }; const uint8x16x4_t tbl_dec1 = vld1q_u8_x4(dec_lut1); const uint8x16x4_t tbl_dec2 = vld1q_u8_x4(dec_lut2); const uint8x16_t offset = vdupq_n_u8(63U); const uint8x16_t max_valid = vdupq_n_u8(63U); size_t i, rounds; if (len < 68) { *consumed = 0; *written = 0; return 1; } rounds = (len - 4) / 64; for (i = 0; i < rounds; i++) { uint8x16x4_t str, dec1, dec2; uint8x16x3_t dec; uint8x16_t classified; str = vld4q_u8(src); dec2.val[0] = vqsubq_u8(str.val[0], offset); dec2.val[1] = vqsubq_u8(str.val[1], offset); dec2.val[2] = vqsubq_u8(str.val[2], offset); dec2.val[3] = vqsubq_u8(str.val[3], offset); dec1.val[0] = vqtbl4q_u8(tbl_dec1, str.val[0]); dec1.val[1] = vqtbl4q_u8(tbl_dec1, str.val[1]); dec1.val[2] = vqtbl4q_u8(tbl_dec1, str.val[2]); dec1.val[3] = vqtbl4q_u8(tbl_dec1, str.val[3]); dec2.val[0] = vqtbx4q_u8(dec2.val[0], tbl_dec2, dec2.val[0]); dec2.val[1] = vqtbx4q_u8(dec2.val[1], tbl_dec2, dec2.val[1]); dec2.val[2] = vqtbx4q_u8(dec2.val[2], tbl_dec2, dec2.val[2]); dec2.val[3] = vqtbx4q_u8(dec2.val[3], tbl_dec2, dec2.val[3]); str.val[0] = vorrq_u8(dec1.val[0], dec2.val[0]); str.val[1] = vorrq_u8(dec1.val[1], dec2.val[1]); str.val[2] = vorrq_u8(dec1.val[2], dec2.val[2]); str.val[3] = vorrq_u8(dec1.val[3], dec2.val[3]); classified = vorrq_u8(vorrq_u8(vcgtq_u8(str.val[0], max_valid), vcgtq_u8(str.val[1], max_valid)), vorrq_u8(vcgtq_u8(str.val[2], max_valid), vcgtq_u8(str.val[3], max_valid))); if (vmaxvq_u8(classified) != 0U) return 0; dec.val[0] = vorrq_u8(vshlq_n_u8(str.val[0], 2), vshrq_n_u8(str.val[1], 4)); dec.val[1] = vorrq_u8(vshlq_n_u8(str.val[1], 4), vshrq_n_u8(str.val[2], 2)); dec.val[2] = vorrq_u8(vshlq_n_u8(str.val[2], 6), str.val[3]); vst3q_u8(dst, dec); src += 64; dst += 48; } *consumed = rounds * 64; *written = rounds * 48; return 1; } #endif AK_INLINE int ak_simd_base64_decode(const uint8_t * __restrict src, size_t len, uint8_t * __restrict dst, size_t * __restrict consumed, size_t * __restrict written) { *consumed = 0; *written = 0; #if defined(AK_SIMD_ARM64) return ak_simd_base64_decode_arm64(src, len, dst, consumed, written); #else (void)src; (void)len; (void)dst; return 1; #endif } #endif /* assetkit_simd_base64_h */ ================================================ FILE: src/simd/intrin.h ================================================ /* * Copyright (C) 2026 Recep Aslantas * * Licensed under the Apache License, Version 2.0. */ #ifndef assetkit_simd_intrin_h #define assetkit_simd_intrin_h #if defined(__AVX2__) || defined(__AVX__) || defined(__SSSE3__) || defined(__SSE2__) || defined(_M_X64) || defined(_M_IX86) # include "x86.h" #endif #if defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM) # include "arm.h" #endif #if defined(__wasm__) && defined(__wasm_simd128__) # include "wasm.h" #endif #if defined(AK_SIMD_X86) || defined(AK_SIMD_ARM) || defined(AK_SIMD_WASM) # define AK_SIMD 1 #endif #endif /* assetkit_simd_intrin_h */ ================================================ FILE: src/simd/wasm.h ================================================ /* * Copyright (C) 2026 Recep Aslantas * * Licensed under the Apache License, Version 2.0. */ #ifndef assetkit_simd_wasm_h #define assetkit_simd_wasm_h #if defined(__wasm__) && defined(__wasm_simd128__) # include # define AK_SIMD_WASM 1 #endif #endif /* assetkit_simd_wasm_h */ ================================================ FILE: src/simd/x86.h ================================================ /* * Copyright (C) 2026 Recep Aslantas * * Licensed under the Apache License, Version 2.0. */ #ifndef assetkit_simd_x86_h #define assetkit_simd_x86_h #if defined(__AVX2__) # include # define AK_SIMD_X86 1 # define AK_SIMD_AVX2 1 #elif defined(__SSSE3__) # include # define AK_SIMD_X86 1 # define AK_SIMD_SSSE3 1 #elif defined(__SSE2__) || defined(_M_X64) || defined(_M_IX86) # include # define AK_SIMD_X86 1 # define AK_SIMD_SSE2 1 #endif #endif /* assetkit_simd_x86_h */ ================================================ FILE: src/skin/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/skin/fix.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fix.h" static AkBoneWeights* ak_skinFixWeightsForPrimitive(AkSkin * __restrict skin, AkMeshPrimitive * __restrict prim, uint32_t primIdx) { AkMeshPrimitive *it; uint32_t idx; if (!skin || !skin->weights || skin->nPrims == 0) return NULL; if (prim && prim->mesh) { idx = 0; for (it = prim->mesh->primitive; it; it = it->next, idx++) { if (it != prim) continue; if (idx < skin->nPrims && skin->weights[idx]) return skin->weights[idx]; break; } } if (primIdx < skin->nPrims) return skin->weights[primIdx]; return NULL; } AK_HIDE void ak_skinFixWeights(AkMesh * __restrict mesh) { AkHeap *heap; AkDoc *doc; AkMeshPrimitive *prim; AkSkin *skin; FListItem *skinItem; AkBoneWeights *wl; AkBoneWeight *w, *iw, *old, *oiw; AkDuplicator *dupl; AkUIntArray *dupc, *dupcsum; AkAccessor *acci; size_t *pOldIndex, *wi; size_t vc, d, s, pno, poo, nwsum, newidx, next, tmp, count; size_t oldVertex; uint32_t *nj, i, j, k, vcount, primIndex; if (!(skinItem = mesh->skins)) return; heap = ak_heap_getheap(mesh->geom); doc = ak_heap_data(heap); pOldIndex = NULL; /* fix every skin that attached to the mesh */ do { skin = skinItem->data; prim = mesh->primitive; primIndex = 0; while (prim) { if (!(dupl = rb_find(doc->reserved, prim)) || dupl->dupCount < 1 || !dupl->range || !prim->pos || !(acci = prim->pos->accessor)) goto nxt_prim; wl = ak_skinFixWeightsForPrimitive(skin, prim, primIndex); if (!wl || !wl->counts || !wl->indexes || !wl->weights) goto nxt_prim; old = wl->weights; pOldIndex = wl->indexes; oldVertex = wl->nVertex; vc = acci->count; dupc = dupl->range->dupc; dupcsum = dupl->range->dupcsum; if (!dupc || !dupcsum) goto nxt_prim; if (dupc->count < vc) vc = dupc->count; nwsum = 0; wl->nVertex = count = dupl->bufCount + dupl->dupCount; nj = ak_heap_alloc(heap, wl, count * sizeof(uint32_t)); wi = ak_heap_alloc(heap, wl, count * sizeof(size_t)); /* copy to new location and duplicate if needed */ for (i = 0; i < vc; i++) { if ((poo = dupc->items[3 * i + 2]) == 0) continue; if (poo > oldVertex) continue; pno = dupc->items[3 * i]; d = dupc->items[3 * i + 1]; if (pno >= dupcsum->count) continue; s = dupcsum->items[pno]; vcount = wl->counts[poo - 1]; for (j = 0; j <= d; j++) { newidx = pno + j + s; if (newidx >= count) continue; wi[newidx] = vcount; /* weight index */ nj[newidx] = vcount; /* number of joints */ nwsum += vcount; } } /* prepare weight index */ for (next = j = 0; j < wl->nVertex; j++) { tmp = wi[j]; wi[j] = next; next = tmp + next; } /* now we know the size of arrays: weights, pCount, pIndex */ w = ak_heap_alloc(heap, wl, sizeof(*w) * nwsum); nwsum = 0; for (i = 0; i < vc; i++) { if ((poo = dupc->items[3 * i + 2]) == 0) continue; if (poo > oldVertex) continue; pno = dupc->items[3 * i]; d = dupc->items[3 * i + 1]; if (pno >= dupcsum->count) continue; s = dupcsum->items[pno]; vcount = wl->counts[poo - 1]; for (j = 0; j <= d; j++) { tmp = pno + j + s; if (tmp >= count) continue; newidx = wi[tmp]; for (k = 0; k < vcount; k++) { iw = &w[newidx + k]; oiw = &old[pOldIndex[poo - 1] + k]; iw->joint = oiw->joint; iw->weight = oiw->weight; } nwsum += vcount; } } if (pOldIndex) ak_free(pOldIndex); if (old) ak_free(old); if (wl->counts) ak_free(wl->counts); wl->counts = nj; wl->indexes = wi; wl->weights = w; nxt_prim: primIndex++; prim = prim->next; } skinItem = skinItem->next; } while (skinItem); } ================================================ FILE: src/skin/fix.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_src_skin_index_h #define ak_src_skin_index_h #include "../common.h" AK_HIDE void ak_skinFixWeights(AkMesh * __restrict mesh); #endif /* ak_src_skin_index_h */ ================================================ FILE: src/skin/skin.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include #include typedef struct AkSkinAccessorPair { AkAccessor *jointAcc; AkAccessor *weightAcc; AkBuffer *jointBuf; AkBuffer *weightBuf; uint32_t jStride; uint32_t wStride; uint32_t slotCount; } AkSkinAccessorPair; typedef struct AkSkinInspectPrimitive { struct AkSkinInspectPrimitive *next; AkMeshPrimitive *prim; AkSkinAccessorPair *pairs; size_t vertexCount; uint32_t pairCount; } AkSkinInspectPrimitive; typedef struct AkSkinInspectView { AkSkinInspectPrimitive *primitive; } AkSkinInspectView; AK_INLINE size_t ak_skinPrimitiveVertexCount(AkMeshPrimitive * __restrict prim) { AkInput *inp; if (!prim) return 0; for (inp = prim->input; inp; inp = inp->next) { if (inp->semantic == AK_INPUT_POSITION && inp->accessor) return inp->accessor->count; } return 0; } AK_INLINE AkBoneWeights* ak_skinWeightsForPrimitive(AkSkin * __restrict skin, AkMeshPrimitive * __restrict prim, uint32_t primIdx) { AkMeshPrimitive *it; uint32_t idx; if (!skin || !skin->weights || skin->nPrims == 0) return NULL; if (prim && prim->mesh) { idx = 0; for (it = prim->mesh->primitive; it; it = it->next, idx++) { if (it != prim) continue; if (idx < skin->nPrims && skin->weights[idx]) return skin->weights[idx]; break; } } if (primIdx < skin->nPrims) return skin->weights[primIdx]; return NULL; } AK_INLINE uint32_t ak_skinAccessorPairCapacity(AkMeshPrimitive * __restrict prim) { AkInput *inp; uint32_t count; count = 0; for (inp = prim->input; inp; inp = inp->next) { if (inp->semantic == AK_INPUT_JOINT) count++; } return count; } AK_INLINE uint32_t ak_skinCollectAccessorPairs(AkMeshPrimitive * __restrict prim, AkSkinAccessorPair * __restrict pairs, uint32_t pairCap, size_t * __restrict vCount) { AkSkinAccessorPair *pair; AkAccessor *jointAcc, *weightAcc; AkBuffer *jointBuf, *weightBuf; AkInput *jointInp, *weightInp, *scan; uint32_t pairCount; size_t count; if (!prim || !pairs || pairCap == 0 || !vCount) return 0; count = ak_skinPrimitiveVertexCount(prim); pairCount = 0; for (jointInp = prim->input; jointInp; jointInp = jointInp->next) { if (jointInp->semantic != AK_INPUT_JOINT) continue; weightInp = NULL; for (scan = prim->input; scan; scan = scan->next) { if (scan->semantic == AK_INPUT_WEIGHT && scan->set == jointInp->set) { weightInp = scan; break; } } if (!weightInp) continue; jointAcc = jointInp->accessor; weightAcc = weightInp->accessor; if (!jointAcc || !weightAcc) continue; jointBuf = jointAcc->buffer; weightBuf = weightAcc->buffer; if (!jointBuf || !jointBuf->data || !weightBuf || !weightBuf->data) continue; if (pairCount >= pairCap) break; pair = &pairs[pairCount]; pair->jointAcc = jointAcc; pair->weightAcc = weightAcc; pair->jointBuf = jointBuf; pair->weightBuf = weightBuf; pair->jStride = (uint32_t)jointAcc->byteStride; pair->wStride = (uint32_t)weightAcc->byteStride; if (!pair->jStride) pair->jStride = (uint32_t)jointAcc->fillByteSize; if (!pair->wStride) pair->wStride = (uint32_t)weightAcc->fillByteSize; pair->slotCount = jointAcc->componentCount; if (weightAcc->componentCount < pair->slotCount) pair->slotCount = weightAcc->componentCount; if (pair->slotCount == 0) continue; if (count == 0 || jointAcc->count < count) count = jointAcc->count; if (weightAcc->count < count) count = weightAcc->count; pairCount++; } *vCount = count; return pairCount; } static AkSkinInspectPrimitive* ak_skinInspectPrimitive(AkSkin * __restrict skin, AkMeshPrimitive * __restrict prim) { AkSkinInspectView *view; AkSkinInspectPrimitive *insp; AkSkinAccessorPair *pairs; AkHeap *heap; uint32_t pairCap, pairCount; size_t vCount; if (!skin || !prim) return NULL; view = skin->inspectResult; if (view) { for (insp = view->primitive; insp; insp = insp->next) { if (insp->prim == prim) return insp; } } else { heap = ak_heap_getheap(skin); view = ak_heap_calloc(heap, skin, sizeof(*view)); if (!view) return NULL; skin->inspectResult = view; } pairCap = ak_skinAccessorPairCapacity(prim); if (pairCap == 0) return NULL; heap = ak_heap_getheap(view); pairs = ak_heap_calloc(heap, view, sizeof(*pairs) * pairCap); if (!pairs) return NULL; pairCount = ak_skinCollectAccessorPairs(prim, pairs, pairCap, &vCount); if (pairCount == 0 || vCount == 0) return NULL; insp = ak_heap_calloc(heap, view, sizeof(*insp)); insp->prim = prim; insp->pairs = pairs; insp->pairCount = pairCount; insp->vertexCount = vCount; insp->next = view->primitive; view->primitive = insp; return insp; } AK_INLINE bool ak_skinJointToU16(uint32_t joint, uint16_t *dest) { if (joint > UINT16_MAX) return false; *dest = (uint16_t)joint; return true; } AK_INLINE bool ak_skinReadJoint(const char *src, AkTypeId componentType, uint32_t k, uint16_t *dest) { switch (componentType) { case AKT_UBYTE: *dest = ((const uint8_t *)src)[k]; return true; case AKT_USHORT: *dest = ((const uint16_t *)src)[k]; return true; case AKT_UINT: return ak_skinJointToU16(((const uint32_t *)src)[k], dest); default: return false; } } AK_INLINE float ak_skinReadWeight(const char *src, AkTypeId componentType, uint32_t k) { switch (componentType) { case AKT_FLOAT: return ((const float *)src)[k]; case AKT_UBYTE: return (float)((const uint8_t *)src)[k] / 255.0f; case AKT_USHORT: return (float)((const uint16_t *)src)[k] / 65535.0f; default: return 0.0f; } } AK_INLINE void ak_skinNormalizeWeights(float * __restrict weights, uint32_t maxJoint) { float sum; float inv; uint32_t k; sum = 0.0f; for (k = 0; k < maxJoint; k++) sum += weights[k]; if (sum > 0.0f && isfinite(sum)) { inv = 1.0f / sum; for (k = 0; k < maxJoint; k++) weights[k] *= inv; } } AK_INLINE void ak_skinWriteInterleavedRow(char * __restrict row, const uint16_t * __restrict joints, const float * __restrict weights, uint32_t maxJoint) { size_t jointBytes; jointBytes = sizeof(uint16_t) * maxJoint; memcpy(row, joints, jointBytes); memcpy(row + jointBytes, weights, sizeof(float) * maxJoint); } AK_INLINE void ak_skinKeepInfluence(uint16_t * __restrict joints, float * __restrict weights, uint32_t maxJoint, uint16_t joint, float weight) { uint32_t k, minIdx; float minW; if (!(weight > 0.0f) || !isfinite(weight)) return; for (k = 0; k < maxJoint; k++) { if (weights[k] > 0.0f && joints[k] == joint) { weights[k] += weight; return; } } for (k = 0; k < maxJoint; k++) { if (weights[k] <= 0.0f) { joints[k] = joint; weights[k] = weight; return; } } minIdx = 0; minW = weights[0]; for (k = 1; k < maxJoint; k++) { if (weights[k] < minW) { minW = weights[k]; minIdx = k; } } if (weight > minW) { joints[minIdx] = joint; weights[minIdx] = weight; } } AK_EXPORT size_t ak_skinInterleave(AkSkin * __restrict skin, AkMeshPrimitive * __restrict prim, uint32_t primIdx, uint32_t maxJoint, void ** __restrict buff) { AkBoneWeights *bw; AkBoneWeight *src; AkSkinInspectPrimitive *insp; AkSkinAccessorPair *pairs; AkSkinAccessorPair *pair; uint16_t *idxScratch; uint16_t joint; float *wgtScratch; char *out; const char *jSrc, *wSrc; size_t vCount, outBytes, v, posCount; size_t rowBytes; uint32_t k, pairCount, pairIdx, slotCount; if (!skin || !prim || !buff || maxJoint == 0) return 0; bw = ak_skinWeightsForPrimitive(skin, prim, primIdx); pairs = NULL; pairCount = 0; if (bw) { vCount = bw->nVertex; posCount = ak_skinPrimitiveVertexCount(prim); if (posCount > 0 && posCount < vCount) vCount = posCount; } else { if (!(insp = ak_skinInspectPrimitive(skin, prim))) return 0; pairs = insp->pairs; pairCount = insp->pairCount; vCount = insp->vertexCount; } if (vCount == 0) return 0; rowBytes = maxJoint * (sizeof(uint16_t) + sizeof(float)); outBytes = vCount * rowBytes; if (!(out = *buff)) out = *buff = ak_calloc(NULL, outBytes); if (!out) return 0; idxScratch = alloca(sizeof(uint16_t) * maxJoint); wgtScratch = alloca(sizeof(float) * maxJoint); for (v = 0; v < vCount; v++) { memset(idxScratch, 0, sizeof(uint16_t) * maxJoint); memset(wgtScratch, 0, sizeof(float) * maxJoint); if (bw) { slotCount = bw->counts[v]; for (k = 0; k < slotCount; k++) { src = &bw->weights[bw->indexes[v] + k]; if (ak_skinJointToU16(src->joint, &joint)) ak_skinKeepInfluence(idxScratch, wgtScratch, maxJoint, joint, src->weight); } } else { for (pairIdx = 0; pairIdx < pairCount; pairIdx++) { pair = &pairs[pairIdx]; jSrc = (const char *)pair->jointBuf->data + pair->jointAcc->byteOffset + (size_t)v * pair->jStride; wSrc = (const char *)pair->weightBuf->data + pair->weightAcc->byteOffset + (size_t)v * pair->wStride; slotCount = pair->slotCount; for (k = 0; k < slotCount; k++) { if (ak_skinReadJoint(jSrc, pair->jointAcc->componentType, (uint32_t)k, &joint)) { ak_skinKeepInfluence(idxScratch, wgtScratch, maxJoint, joint, ak_skinReadWeight(wSrc, pair->weightAcc->componentType, (uint32_t)k)); } } } } ak_skinNormalizeWeights(wgtScratch, maxJoint); ak_skinWriteInterleavedRow(out + v * rowBytes, idxScratch, wgtScratch, maxJoint); } return outBytes; } AK_EXPORT size_t ak_skinFillWeights(AkSkin * __restrict skin, AkMeshPrimitive * __restrict prim, uint32_t primIdx, uint32_t maxJoint, uint16_t * __restrict outIndices, float * __restrict outWeights) { AkBoneWeights *bw; AkBoneWeight *src; AkSkinInspectPrimitive *insp; AkSkinAccessorPair *pairs, *pair; const char *jSrc, *wSrc; size_t vCount, v, k, posCount; size_t outRow; uint32_t pairCount, pairIdx; uint32_t slotCount; uint16_t joint; float weight; if (!skin || !prim || !outIndices || !outWeights || maxJoint == 0) return 0; /*------------------------------------------------------------------*/ /* DAE path: per-primitive CSR layout in skin->weights[primIdx]. */ /* Variable joint count per vertex; pick top-N by weight, zero-pad */ /* missing slots, normalize so sum==1 (graceful degradation when N */ /* < authored joint count). */ /*------------------------------------------------------------------*/ if ((bw = ak_skinWeightsForPrimitive(skin, prim, primIdx))) { vCount = bw->nVertex; posCount = ak_skinPrimitiveVertexCount(prim); if (posCount > 0 && posCount < vCount) vCount = posCount; memset(outIndices, 0, vCount * maxJoint * sizeof(uint16_t)); memset(outWeights, 0, vCount * maxJoint * sizeof(float)); for (v = 0; v < vCount; v++) { slotCount = bw->counts[v]; outRow = (size_t)v * maxJoint; for (k = 0; k < slotCount; k++) { src = &bw->weights[bw->indexes[v] + k]; if (ak_skinJointToU16(src->joint, &joint)) { ak_skinKeepInfluence(&outIndices[outRow], &outWeights[outRow], maxJoint, joint, src->weight); } } ak_skinNormalizeWeights(&outWeights[outRow], maxJoint); } return vCount; } /*------------------------------------------------------------------*/ /* Raw accessor path: collect every JOINTS_n / WEIGHTS_n pair, then */ /* keep the top maxJoint influences per vertex and normalize. */ /*------------------------------------------------------------------*/ if (!(insp = ak_skinInspectPrimitive(skin, prim))) { return 0; } pairs = insp->pairs; pairCount = insp->pairCount; vCount = insp->vertexCount; memset(outIndices, 0, vCount * maxJoint * sizeof(uint16_t)); memset(outWeights, 0, vCount * maxJoint * sizeof(float)); for (v = 0; v < vCount; v++) { outRow = (size_t)v * maxJoint; for (pairIdx = 0; pairIdx < pairCount; pairIdx++) { pair = &pairs[pairIdx]; jSrc = (const char *)pair->jointBuf->data + pair->jointAcc->byteOffset + (size_t)v * pair->jStride; wSrc = (const char *)pair->weightBuf->data + pair->weightAcc->byteOffset + (size_t)v * pair->wStride; slotCount = pair->slotCount; for (k = 0; k < slotCount; k++) { if (!ak_skinReadJoint(jSrc, pair->jointAcc->componentType, (uint32_t)k, &joint)) continue; weight = ak_skinReadWeight(wSrc, pair->weightAcc->componentType, (uint32_t)k); ak_skinKeepInfluence(&outIndices[outRow], &outWeights[outRow], maxJoint, joint, weight); } } ak_skinNormalizeWeights(&outWeights[outRow], maxJoint); } return vCount; } AK_EXPORT size_t ak_skinVerticesForJoint(AkSkin * __restrict skin, AkMeshPrimitive * __restrict prim, uint32_t primIdx, uint32_t jointIdx, uint32_t * __restrict outVertices, size_t capacity) { AkBoneWeights *bw; AkBoneWeight *src; AkSkinInspectPrimitive *insp; AkSkinAccessorPair *pairs, *pair; const char *jSrc, *wSrc; size_t vCount, posCount, v, k; size_t found, written; uint32_t pairCount, pairIdx; uint32_t slotCount; uint16_t joint; float weight; bool matched; if (!skin || !prim) return 0; found = 0; written = 0; if ((bw = ak_skinWeightsForPrimitive(skin, prim, primIdx))) { vCount = bw->nVertex; posCount = ak_skinPrimitiveVertexCount(prim); if (posCount > 0 && posCount < vCount) vCount = posCount; for (v = 0; v < vCount; v++) { matched = false; slotCount = bw->counts[v]; for (k = 0; k < slotCount; k++) { src = &bw->weights[bw->indexes[v] + k]; if (src->joint == jointIdx && src->weight > 0.0f && isfinite(src->weight)) { matched = true; break; } } if (!matched) continue; if (outVertices && written < capacity) outVertices[written++] = (uint32_t)v; found++; } return found; } if (jointIdx > UINT16_MAX) return 0; if (!(insp = ak_skinInspectPrimitive(skin, prim))) return 0; pairs = insp->pairs; pairCount = insp->pairCount; vCount = insp->vertexCount; for (v = 0; v < vCount; v++) { matched = false; for (pairIdx = 0; pairIdx < pairCount && !matched; pairIdx++) { pair = &pairs[pairIdx]; jSrc = (const char *)pair->jointBuf->data + pair->jointAcc->byteOffset + (size_t)v * pair->jStride; wSrc = (const char *)pair->weightBuf->data + pair->weightAcc->byteOffset + (size_t)v * pair->wStride; slotCount = pair->slotCount; for (k = 0; k < slotCount; k++) { if (!ak_skinReadJoint(jSrc, pair->jointAcc->componentType, (uint32_t)k, &joint)) continue; weight = ak_skinReadWeight(wSrc, pair->weightAcc->componentType, (uint32_t)k); if ((uint32_t)joint == jointIdx && weight > 0.0f && isfinite(weight)) { matched = true; break; } } } if (!matched) continue; if (outVertices && written < capacity) outVertices[written++] = (uint32_t)v; found++; } return found; } ================================================ FILE: src/string.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "../include/ak/assetkit.h" #include "common.h" AK_EXPORT const char* ak_strltrim_fast(const char * __restrict str) { const char *ptr; size_t len, i; char c; len = strlen(str); ptr = str; if (len == 0) return ptr; for (i = 0; i < len; i++) { c = str[i]; if (AK_ARRAY_SEP_CHECK) { ptr++; continue; } else { return ptr; } } return ptr; } AK_EXPORT int ak_strtok_count(char * __restrict buff, char * __restrict sep, size_t *len) { int i, count, itemc, buflen, found_sep; buflen = (int)strlen(buff); if (buflen == 0) return 0; count = itemc = 0; /* because of buff[j + 1] */ if (buflen == 1) return 1; found_sep = false; for (i = 0; i < buflen; i++) { if (strchr(sep, buff[i])){ if (!found_sep) { itemc++; found_sep = true; } continue; } found_sep = false; count++; } if (len) *len = buflen - count; /* left trim */ if (strchr(sep, buff[0])) itemc--; /* right trim */ if (strchr(sep, buff[buflen - 1])) itemc--; return itemc + 1; } AK_EXPORT int ak_strtok_count_fast(char * __restrict buff, size_t srclen, size_t *len) { int i, count, itemc, buflen, found_sep; char c; if (srclen != 0) buflen = (int)srclen; else buflen = (int)strlen(buff); if (buflen == 0) return 0; count = itemc = 0; /* because of buff[j + 1] */ if (buflen == 1) return 1; found_sep = false; for (i = 0; i < buflen; i++) { c = buff[i]; if (AK_ARRAY_SEP_CHECK) { if (!found_sep) { itemc++; found_sep = true; } continue; } found_sep = false; count++; } /* left trim */ c = buff[0]; if (AK_ARRAY_SEP_CHECK) itemc--; /* right trim */ c = buff[buflen - 1]; if (AK_ARRAY_SEP_CHECK) itemc--; if (len) *len = buflen - count; return itemc + 1; } AK_EXPORT unsigned long ak_strtof(char * __restrict src, size_t srclen, unsigned long n, AkFloat * __restrict dest) { char *tok, *tok_end, *end; char c; if (n == 0) return 0; dest = dest + n - 1ul; tok = src; if (srclen != 0) { end = src + srclen; do { while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; *(dest - --n) = strtof(tok, &tok_end); tok = tok_end; while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; } while (n > 0ul && tok < end); } else { do { while (((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; *(dest - --n) = strtof(tok, &tok_end); tok = tok_end; while (((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; } while (n > 0ul && *tok != '\0'); } return n; } AK_EXPORT unsigned long ak_strtof_line(char * __restrict src, size_t srclen, unsigned long n, AkFloat * __restrict dest) { char *tok, *tok_end, *end; char c; if (n == 0) return 0; dest = dest + n - 1ul; tok = src; if (srclen != 0) { end = src + srclen; do { while (tok < end && ((void)(c = *tok), AK_ARRAY_SEPLINE_CHECK)) tok++; *(dest - --n) = strtof(tok, &tok_end); tok = tok_end; while (tok < end && ((void)(c = *tok), AK_ARRAY_SEPLINE_CHECK)) tok++; } while (n > 0ul && tok < end); } else { do { while (((void)(c = *tok), AK_ARRAY_SEPLINE_CHECK)) tok++; *(dest - --n) = strtof(tok, &tok_end); tok = tok_end; while (((void)(c = *tok), AK_ARRAY_SEPLINE_CHECK)) tok++; } while (n > 0ul && *tok != '\0'); } return n; } AK_EXPORT unsigned long ak_strtod(char * __restrict src, size_t srclen, unsigned long n, AkDouble * __restrict dest) { char *tok, *tok_end, *end; char c; if (n == 0) return 0; dest = dest + n - 1ul; tok = src; if (srclen != 0) { end = src + srclen; do { while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; *(dest - --n) = strtod(tok, &tok_end); tok = tok_end; while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; } while (n > 0ul && tok < end); } else { do { while (((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; *(dest - --n) = strtod(tok, &tok_end); tok = tok_end; while (((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; } while (n > 0ul && *tok != '\0'); } return n; } AK_EXPORT unsigned long ak_strtoui(char * __restrict src, size_t srclen, unsigned long n, AkUInt * __restrict dest) { char *tok, *tok_end, *end; char c; AkUInt64 val; if (n == 0) return 0; dest = dest + n - 1ul; tok = src; if (srclen != 0) { end = src + srclen; do { while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; val = strtoul(tok, &tok_end, 10); tok = tok_end; /* BUGFIX: some indices may come as -1 as BUG, fix this. */ if (val < UINT32_MAX) { *(dest - --n) = (AkUInt)val; } else { *(dest - --n) = 0; } while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; } while (n > 0ul && tok < end); } else { do { while (((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; val = strtoul(tok, &tok_end, 10); tok = tok_end; /* BUGFIX: some indices may come as -1 as BUG, fix this. */ if (val < UINT32_MAX) { *(dest - --n) = (AkUInt)val; } else { *(dest - --n) = 0; } while (((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; } while (n > 0ul && *tok != '\0'); } return n; } AK_EXPORT unsigned long ak_strtoi(char * __restrict src, size_t srclen, unsigned long n, AkInt * __restrict dest) { char *tok, *tok_end, *end; char c; if (n == 0) return 0; dest = dest + n - 1ul; tok = src; if (srclen != 0) { end = src + srclen; do { while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; *(dest - --n) = (AkUInt)strtol(tok, &tok_end, 10); tok = tok_end; while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; } while (n > 0ul && tok < end); } else { do { while (((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; *(dest - --n) = (AkUInt)strtol(tok, &tok_end, 10); tok = tok_end; while (((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; } while (n > 0ul && *tok != '\0'); } return n; } AK_EXPORT unsigned long ak_strtoi_line(char * __restrict src, size_t srclen, unsigned long n, AkInt * __restrict dest) { char *tok, *tok_end, *end; char c; if (n == 0) return 0; dest = dest + n - 1ul; tok = src; if (srclen != 0) { end = src + srclen; do { while (tok < end && ((void)(c = *tok), AK_ARRAY_SEPLINE_CHECK)) tok++; *(dest - --n) = (AkUInt)strtol(tok, &tok_end, 10); tok = tok_end; while (tok < end && ((void)(c = *tok), AK_ARRAY_SEPLINE_CHECK)) tok++; } while (n > 0ul && tok < end); } else { do { while (((void)(c = *tok), AK_ARRAY_SEPLINE_CHECK)) tok++; *(dest - --n) = (AkUInt)strtol(tok, &tok_end, 10); tok = tok_end; while (((void)(c = *tok), AK_ARRAY_SEPLINE_CHECK)) tok++; } while (n > 0ul && *tok != '\0'); } return n; } AK_EXPORT unsigned long ak_strtob(char * __restrict src, size_t srclen, unsigned long n, AkBool * __restrict dest) { char *tok, *end; char c; if (n == 0) return 0; dest = dest + n - 1ul; tok = src; if (srclen != 0) { end = src + srclen; do { while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; *(dest - --n) = tok[0] == 't' || tok[0] == 'T'; tok++; while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; } while (n > 0ul && tok < end); } else { do { while (((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; *(dest - --n) = tok[0] == 't' || tok[0] == 'T'; tok++; while (((void)(c = *tok), AK_ARRAY_SEP_CHECK)) tok++; } while (n > 0ul && *tok != '\0'); } return n; } AK_EXPORT char* ak_tolower(char *str) { char *p; for (p = str; *p; ++p) *p = tolower(*p); return str; } AK_EXPORT char* ak_toupper(char *str) { char *p; for (p = str; *p; ++p) *p = toupper(*p); return str; } ================================================ FILE: src/strpool.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _AK_STRPOOL_ # define _AK_STRPOOL_ #endif #include "strpool.h" #include const char _s_ak_pool_0[] = "POSITION\0" "NORMAL\0" "TEXCOORD\0" "COLOR\0" ; #undef _AK_STRPOOL_ ================================================ FILE: src/strpool.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_strpool_h # define ak_strpool_h #ifndef AK_STRPOOL_ # define _AK_EXTERN extern #else # define _AK_EXTERN #endif _AK_EXTERN const char _s_ak_pool_0[]; #define _s_ak_0(x) (_s_ak_pool_0 + x) /* _s_ak_pool_0 */ #define _s_POSITION _s_ak_0(0) #define _s_NORMAL _s_ak_0(9) #define _s_TEXCOORD _s_ak_0(16) #define _s_COLOR _s_ak_0(25) #endif /* ak_strpool_h */ ================================================ FILE: src/strpool.json ================================================ { "POSITION": "POSITION", "NORMAL": "NORMAL", "TEXCOORD": "TEXCOORD", "COLOR": "COLOR" } ================================================ FILE: src/strpool.py ================================================ #!/usr/bin/python3 # # Copyright (C) 2020 Recep Aslantas # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # import json from collections import OrderedDict from os.path import realpath from os.path import dirname headerContents = [ ] destdir = dirname(realpath(__file__)) spidx = 0 pos = 0 fspoolJson = open(destdir + "/strpool.json") spool = json.loads(fspoolJson.read(), object_pairs_hook=OrderedDict) fspoolJson.close() fspool_h = open(destdir + "/strpool.h", "wb"); fspool_c = open(destdir + "/strpool.c", "wb"); copyright_str = """\ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ """ fspool_h.write(copyright_str.encode()) fspool_c.write(copyright_str.encode()) fspool_h.write(""" #ifndef ak_strpool_h # define ak_strpool_h #ifndef AK_STRPOOL_ # define _AK_EXTERN extern #else # define _AK_EXTERN #endif """.encode()) fspool_c.write(""" #ifndef _AK_STRPOOL_ # define _AK_STRPOOL_ #endif #include "strpool.h" #include const char _s_ak_pool_0[] = """.encode()) headerContents.append("\n/* _s_ak_pool_0 */\n") for name, val in spool.items(): valLen = len(val) + 1 # string literal size: 2048 if pos + valLen > 2048: pos = 0 spidx += 1 fspool_c.write(";\n\nconst char _s_ak_pool_{0}[] =\n" .format(str(spidx)).encode()) headerContents.append("\n/* _s_ak_pool_{0} */\n" .format(spidx)) fspool_c.write("\"{0}\\0\"\n".format(val).encode()) headerContents.append("#define _s_{0} _s_ak_{1}({2})\n" .format(name, str(spidx), str(pos))) pos += valLen # source file, then close it fspool_c.write(";\n\n#undef _AK_STRPOOL_\n".encode()) fspool_c.close() # header file for idx in range(spidx + 1): fspool_h.write("\n_AK_EXTERN const char _s_ak_pool_{0}[];" .format(str(idx)).encode()) fspool_h.write("\n\n".encode()) for idx in range(spidx + 1): fspool_h.write("#define _s_ak_{0}(x) (_s_ak_pool_{0} + x)\n" .format(str(idx)).encode()) # write header contents, then close it fspool_h.writelines(map(lambda x: x.encode(), headerContents)) fspool_h.write("\n#endif /* ak_strpool_h */\n".encode()) fspool_h.close() # try free array del headerContents[:] ================================================ FILE: src/topo/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/topo/topo.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "topo.h" /* create indices to fix topology, an alternative way could be work with each input, this can be provided by an option maybe in the future. */ AK_HIDE void topofix_noind(AkHeap * __restrict heap, AkMeshPrimitive * __restrict prim, uint8_t trig_fan, uint8_t trig_strip, uint8_t line_loop, uint8_t line_strip) { /* TODO: no indices, handle inputs... */ AkInput *input; AkAccessor *acc; AkBuffer *buff; AkUIntArray *indices; AkUInt *it, nVertices, i, j; if (!(input = prim->pos) || !(acc = input->accessor) || !(buff = acc->buffer)) { return; } nVertices = acc->count; switch (prim->type) { case AK_PRIMITIVE_TRIANGLES: { if (trig_fan || trig_strip) { AkTriangles *trig; AkUInt ntrigs; trig = (AkTriangles *)prim; switch (trig->mode) { case AK_TRIANGLE_FAN: case AK_TRIANGLE_STRIP: break; default: return; } ntrigs = nVertices - 2; indices = ak_heap_calloc(heap, prim, sizeof(*indices) + sizeof(AkUInt) * ntrigs * 3); indices->count = ntrigs * 3; it = indices->items; switch (trig->mode) { case AK_TRIANGLE_FAN: if (trig_fan) { for (i = 0, j = 0; i < ntrigs; ++i) { it[j++] = 0; /* center vertex */ it[j++] = i + 1; it[j++] = i + 2; } } break; case AK_TRIANGLE_STRIP: if (trig_strip) { for (i = 0, j = 0; i < ntrigs; ++i) { if (i % 2 == 0) { it[j++] = i; it[j++] = i + 1; it[j++] = i + 2; } else { it[j++] = i + 2; it[j++] = i + 1; it[j++] = i; } } } break; default: break; } trig->mode = AK_TRIANGLES; prim->indices = indices; prim->indexStride = 1; } break; } case AK_PRIMITIVE_LINES: if (line_loop || line_strip) { AkLines *lines; AkUInt nlines; lines = (AkLines *)prim; switch (lines->mode) { case AK_LINE_LOOP: if (line_loop) { nlines = nVertices; indices = ak_heap_calloc(heap, prim, sizeof(*indices) + sizeof(AkUInt) * nlines * 2); indices->count = nlines * 2; it = indices->items; for (i = 0; i < nVertices - 1; ++i) { it[i * 2] = i; it[i * 2 + 1] = i + 1; } /* close the loop */ it[(nVertices - 1) * 2] = nVertices - 1; it[(nVertices - 1) * 2 + 1] = 0; lines->mode = AK_LINES; prim->indices = indices; prim->indexStride = 1; } break; case AK_LINE_STRIP: if (line_strip) { nlines = nVertices - 1; indices = ak_heap_calloc(heap, prim, sizeof(*indices) + sizeof(AkUInt) * nlines * 2); indices->count = nlines * 2; it = indices->items; for (i = 0; i < nlines; ++i) { it[i * 2] = i; it[i * 2 + 1] = i + 1; } lines->mode = AK_LINES; prim->indices = indices; prim->indexStride = 1; } break; default: break; } } break; default: break; } } AK_HIDE void topofix_ind(AkHeap * __restrict heap, AkMeshPrimitive * __restrict prim, uint8_t trig_fan, uint8_t trig_strip, uint8_t line_loop, uint8_t line_strip) { AkUInt *it, *newIt, nIndices, i, j; AkUIntArray *newIndices; it = prim->indices->items; nIndices = (AkUInt)prim->indices->count; switch (prim->type) { case AK_PRIMITIVE_TRIANGLES: { if (trig_fan || trig_strip) { AkTriangles *trig; AkUInt nTriangles; trig = (AkTriangles *)prim; switch (trig->mode) { case AK_TRIANGLE_FAN: case AK_TRIANGLE_STRIP: break; default: return; } nTriangles = nIndices - 2; newIndices = ak_heap_calloc(heap, prim, sizeof(*newIndices) + sizeof(AkUInt) * nTriangles * 3); newIndices->count = nTriangles * 3; newIt = newIndices->items; switch (trig->mode) { case AK_TRIANGLE_FAN: if (trig_fan) { for (i = 0, j = 0; i < nTriangles; ++i) { newIt[j++] = it[0]; newIt[j++] = it[i + 1]; newIt[j++] = it[i + 2]; } trig->mode = AK_TRIANGLES; } break; case AK_TRIANGLE_STRIP: if (trig_strip) { for (i = 0, j = 0; i < nTriangles; ++i) { newIt[j++] = it[i]; if (i % 2 == 0) { newIt[j++] = it[i + 1]; newIt[j++] = it[i + 2]; } else { newIt[j++] = it[i + 2]; newIt[j++] = it[i + 1]; } } trig->mode = AK_TRIANGLES; } break; default: break; } ak_free(prim->indices); prim->indices = newIndices; } break; } case AK_PRIMITIVE_LINES: if (line_loop || line_strip) { AkLines *lines; AkUInt nLines; lines = (AkLines *)prim; switch (lines->mode) { case AK_LINE_LOOP: if (line_loop) { nLines = nIndices; newIndices = ak_heap_calloc(heap, prim, sizeof(*newIndices) + sizeof(AkUInt) * nLines * 2); newIndices->count = nLines * 2; newIt = newIndices->items; for (i = 0; i < nIndices - 1; ++i) { newIt[i * 2] = it[i]; newIt[i * 2 + 1] = it[i + 1]; } /* close the loop */ newIt[(nIndices - 1) * 2] = it[nIndices - 1]; newIt[(nIndices - 1) * 2 + 1] = it[0]; ak_free(prim->indices); prim->indices = newIndices; lines->mode = AK_LINES; } break; case AK_LINE_STRIP: if (line_strip) { nLines = nIndices - 1; newIndices = ak_heap_calloc(heap, prim, sizeof(*newIndices) + sizeof(AkUInt) * nLines * 2); newIndices->count = nLines * 2; newIt = newIndices->items; for (i = 0; i < nLines; ++i) { newIt[i * 2] = it[i]; newIt[i * 2 + 1] = it[i + 1]; } ak_free(prim->indices); prim->indices = newIndices; lines->mode = AK_LINES; } break; default: break; } } break; default: break; } } AK_HIDE void topofix(AkMesh * mesh) { AkHeap *heap; AkMeshPrimitive *prim; uint8_t trig_fan, trig_strip, line_loop, line_strip; trig_fan = (int)ak_opt_get(AK_OPT_CVT_TRIANGLEFAN); trig_strip = (int)ak_opt_get(AK_OPT_CVT_TRIANGLESTRIP); line_loop = (int)ak_opt_get(AK_OPT_CVT_LINELOOP); line_strip = (int)ak_opt_get(AK_OPT_CVT_LINESTRIP); if (!trig_fan && !trig_strip && !line_loop && !line_strip) return; heap = ak_heap_getheap(mesh->geom); prim = mesh->primitive; while (prim) { if (prim->indices) { topofix_ind(heap, prim, trig_fan, trig_strip, line_loop, line_strip); } else { topofix_noind(heap, prim, trig_fan, trig_strip, line_loop, line_strip); } prim = prim->next; } } ================================================ FILE: src/topo/topo.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef src_topo_h #define src_topo_h #include "../common.h" AK_HIDE void topofix(AkMesh * mesh); #endif /* src_topo_h */ ================================================ FILE: src/transform/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/transform/dup.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include AK_EXPORT void ak_transformDup(AkNode * __restrict srcNode, AkNode * __restrict destNode) { AkHeap *heap; AkTransform *trans; AkObject *transItem, *newTransItem, *lastTransItem; if (!srcNode || !srcNode->transform || !destNode) return; heap = ak_heap_getheap(srcNode); trans = ak_heap_calloc(heap, destNode, sizeof(*trans)); transItem = srcNode->transform->item; lastTransItem = NULL; while (transItem) { switch (transItem->type) { case AKT_MATRIX: { AkMatrix *matrix, *newMatrix; matrix = ak_objGet(transItem); newTransItem = ak_objAlloc(heap, destNode, sizeof(*matrix), AKT_MATRIX, true); newMatrix = ak_objGet(newTransItem); ak_sid_dup(newTransItem, transItem); glm_mat4_ucopy(matrix->val, newMatrix->val); break; } case AKT_LOOKAT: { AkLookAt *lookAt, *newLookAt; lookAt = ak_objGet(transItem); newTransItem = ak_objAlloc(heap, destNode, sizeof(*lookAt), AKT_LOOKAT, true); newLookAt = ak_objGet(newTransItem); ak_sid_dup(newTransItem, transItem); glm_vec3_copy(lookAt->val[0], newLookAt->val[0]); glm_vec3_copy(lookAt->val[1], newLookAt->val[1]); glm_vec3_copy(lookAt->val[2], newLookAt->val[2]); break; } case AKT_ROTATE: { AkRotate *rotate, *newRotate; rotate = ak_objGet(transItem); newTransItem = ak_objAlloc(heap, destNode, sizeof(*rotate), AKT_ROTATE, true); newRotate = ak_objGet(newTransItem); ak_sid_dup(newTransItem, transItem); glm_vec4_copy(rotate->val, newRotate->val); break; } case AKT_QUATERNION: { AkQuaternion *quat, *newQuat; quat = ak_objGet(transItem); newTransItem = ak_objAlloc(heap, destNode, sizeof(*quat), AKT_QUATERNION, true); newQuat = ak_objGet(newTransItem); ak_sid_dup(newTransItem, transItem); glm_vec4_copy(quat->val, newQuat->val); break; } case AKT_SCALE: { AkScale *scale, *newScale; scale = ak_objGet(transItem); newTransItem = ak_objAlloc(heap, destNode, sizeof(*scale), AKT_SCALE, true); newScale = ak_objGet(newTransItem); ak_sid_dup(newTransItem, transItem); glm_vec3_copy(scale->val, newScale->val); break; } case AKT_TRANSLATE: { AkTranslate *translate, *newTranslate; translate = ak_objGet(transItem); newTransItem = ak_objAlloc(heap, destNode, sizeof(*translate), AKT_TRANSLATE, true); newTranslate = ak_objGet(newTransItem); ak_sid_dup(newTransItem, transItem); glm_vec3_copy(translate->val, newTranslate->val); break; } case AKT_SKEW: { AkSkew *skew, *newSkew; skew = ak_objGet(transItem); newTransItem = ak_objAlloc(heap, destNode, sizeof(*skew), AKT_SKEW, true); newSkew = ak_objGet(newTransItem); ak_sid_dup(newTransItem, transItem); newSkew->angle = skew->angle; glm_vec3_copy(skew->aroundAxis, newSkew->aroundAxis); glm_vec3_copy(skew->rotateAxis, newSkew->rotateAxis); break; } default: transItem = transItem->next; continue; } if (lastTransItem) lastTransItem->next = newTransItem; else trans->item = newTransItem; lastTransItem = newTransItem; transItem = transItem->next; } destNode->transform = trans; } ================================================ FILE: src/transform/trans.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include /* Note from OpenCOLLADA repo: Important assumptions on skew and shears: COLLADA uses the RenderMan standard: [ 1+s*dx*ex s*dx*ey s*dx*ez 0 ] [ s*dy*ex 1+s*dy*ey s*dy*ez 0 ] [ s*dz*ex s*dz*ey 1+s*dz*ez 0 ] [ 0 0 0 1 ] where s = tan(skewAngle), if the axises are normalized */ AK_EXPORT void ak_transformSkewMatrix(AkSkew * __restrict skew, float * matrix) { mat4 mat; float s; s = tanf(skew->angle); glm_vec3_scale(skew->aroundAxis, skew->rotateAxis[0] * s, mat[0]); glm_vec3_scale(skew->aroundAxis, skew->rotateAxis[1] * s, mat[1]); glm_vec3_scale(skew->aroundAxis, skew->rotateAxis[2] * s, mat[2]); mat[0][0] += 1.0f; mat[1][1] += 1.0f; mat[2][2] += 1.0f; mat[0][3] = mat[1][3] = mat[2][3] = mat[3][0] = mat[3][1] = mat[3][2] = 0.0f; mat[3][3] = 1.0f; glm_mat4_copy(mat, (vec4 *)matrix); } AK_EXPORT void ak_transformCombine(AkTransform * __restrict transform, float * __restrict matrix) { AkObject *transformItem, *transformGroup; mat4 mat = GLM_MAT4_IDENTITY_INIT; mat4 tmp; if (!transform || !matrix) goto ret; transformGroup = transform->base; again: transformItem = transformGroup; while (transformItem) { switch (transformItem->type) { case AKT_MATRIX: { AkMatrix *matrix; matrix = ak_objGet(transformItem); glm_mat4_mul(mat, matrix->val, mat); break; } case AKT_LOOKAT: { AkLookAt *lookAt; lookAt = ak_objGet(transformItem); glm_lookat(lookAt->val[0], lookAt->val[1], lookAt->val[2], tmp); /* because this is view matrix */ glm_inv_tr(tmp); glm_mat4_mul(mat, tmp, mat); break; } case AKT_ROTATE: { AkRotate *rotate; rotate = ak_objGet(transformItem); glm_rotate_make(tmp, rotate->val[3], rotate->val); glm_mat4_mul(mat, tmp, mat); break; } case AKT_QUATERNION: { AkQuaternion *quat; quat = ak_objGet(transformItem); glm_quat_mat4(quat->val, tmp); glm_mat4_mul(mat, tmp, mat); break; } case AKT_SCALE: { AkScale *scale; scale = ak_objGet(transformItem); glm_scale_make(tmp, scale->val); glm_mat4_mul(mat, tmp, mat); break; } case AKT_TRANSLATE: { AkTranslate *translate; translate = ak_objGet(transformItem); glm_translate_make(tmp, translate->val); glm_mat4_mul(mat, tmp, mat); break; } case AKT_SKEW: { AkSkew *skew; skew = ak_objGet(transformItem); ak_transformSkewMatrix(skew, tmp[0]); glm_mat4_mul(mat, tmp, mat); break; } } transformItem = transformItem->next; } if (transformGroup != transform->item) { transformGroup = transform->item; goto again; } ret: glm_mat4_copy(mat, (vec4 *)matrix); } AK_EXPORT void ak_transformFreeBase(AkTransform * __restrict transform) { AkObject *transItem, *tofree; transItem = transform->base; while (transItem) { tofree = transItem; transItem = transItem->next; ak_free(tofree); } transform->base = NULL; } AK_EXPORT AkObject* ak_getTransformTRS(AkNode *node, AkTypeId transformType) { AkHeap *heap; AkObject *obj, *it, *prev; heap = ak_heap_getheap(node); if (node->transform) { obj = node->transform->item; if (obj) { do { if (obj->type == transformType) return obj; } while ((obj = obj->next)); } } else { node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform)); } switch (transformType) { case AKT_QUATERNION: { AkQuaternion *rot; obj = ak_objAlloc(heap, node, sizeof(*rot), AKT_QUATERNION, true); rot = ak_objGet(obj); glm_quat_identity(rot->val); break; } case AKT_TRANSLATE: { obj = ak_objAlloc(heap, node, sizeof(AkTranslate), AKT_TRANSLATE, true); break; } case AKT_SCALE: { AkScale *scale; obj = ak_objAlloc(heap, node, sizeof(*scale), AKT_SCALE, true); scale = ak_objGet(obj); glm_vec3_one(scale->val); break; } default: return NULL; } it = node->transform->item; if (!it) { node->transform->item = obj; return obj; } prev = NULL; while (it) { if (transformType < it->type) break; prev = it; it = it->next; } if (prev) prev->next = obj; else node->transform->item = obj; obj->next = it; return obj; } ================================================ FILE: src/transform/traverse.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../common.h" #include AK_EXPORT void ak_transformCombineWorld(AkNode * __restrict node, float * matrix) { AkNode *parentNode; mat4 mat; mat4 ptrans; ak_transformCombine(node->transform, mat[0]); parentNode = node->parent; while (parentNode) { ak_transformCombine(parentNode->transform, ptrans[0]); glm_mat4_mul(ptrans, mat, mat); parentNode = parentNode->parent; } glm_mat4_copy(mat, (vec4 *)matrix); } ================================================ FILE: src/trash.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "trash.h" #include "../include/ak/assetkit.h" #include "../include/ak/trash.h" #include "mem/common.h" #include #include typedef struct AkTrashItem { void *tofree; struct AkTrashItem *next; } AkTrashItem; static AkHeap ak__trash_heap = { .flags = 0 }; #define trash_heap &ak__trash_heap AkTrashItem *ak__trash = NULL; static int ak__trash_cmp(void *key1, void *key2) { if (key1 > key2) return 1; else if (key1 < key2) return -1; return 0; } AK_EXPORT void ak_trash_add(void *mem) { AkTrashItem *item; AkHeapNode *found; AkResult ret; ret = ak_heap_getNodeById(trash_heap, mem, &found); if (ret == AK_OK) return; item = ak_heap_alloc(trash_heap, NULL, sizeof(*item)); ak_heap_setId(trash_heap, ak__alignof(item), mem); item->next = ak__trash; item->tofree = mem; ak__trash = item; } AK_EXPORT void ak_trash_empty(void) { while (ak__trash) { AkTrashItem *tofree; tofree = ak__trash; ak__trash = ak__trash->next; ak_free(tofree->tofree); ak_free(tofree); } } AK_HIDE void ak_trash_init(void) { ak_heap_init(trash_heap, NULL, ak__trash_cmp, NULL); } AK_HIDE void ak_trash_deinit(void) { ak_heap_destroy(trash_heap); } ================================================ FILE: src/trash.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_src_trash_h #define ak_src_trash_h #include "common.h" AK_HIDE void ak_trash_init(void); AK_HIDE void ak_trash_deinit(void); #endif /* ak_src_trash_h */ ================================================ FILE: src/tree.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tree.h" #include "common.h" #include "utils.h" #include "xml.h" AK_HIDE AkTreeNode* tree_fromxml(AkHeap * __restrict heap, void * __restrict memParent, xml_t * __restrict xml) { AkTreeNode *tree, *node, *inode/*, *pa*/; AkTreeNodeAttr *att; xml_t *root, *xpa; xml_attr_t *xatt; size_t namelen; tree = ak_heap_calloc(heap, memParent, sizeof(*tree)); root = xml; xml = xml->val; node = tree; /* pa = NULL; */ while (xml) { switch (xml->type) { case XML_ELEMENT: { inode = ak_heap_calloc(heap, node, sizeof(*inode)); namelen = xml->tagsize; if (xml->prefix) namelen += xml->prefixsize; inode->name = ak_heap_alloc(heap, inode, namelen + 1); if (xml->prefix) { memcpy((void *)inode->name, xml->prefix, xml->prefixsize); memcpy((void *)(inode->name + xml->prefixsize), xml->tag, xml->tagsize); } else { memcpy((void *)inode->name, xml->tag, xml->tagsize); } memset((void *)(inode->name + namelen), '\0', 1); if ((xatt = xml->attr)) { do { att = ak_heap_calloc(heap, inode, sizeof(*att)); att->name = ak_heap_strndup(heap, att, xatt->name, xatt->namesize); att->val = ak_heap_strndup(heap, att, xatt->val, xatt->valsize); att->next = inode->attribs; inode->attribs = att; inode->attrc++; } while ((xatt = xatt->next)); } if (node->chld) node->chld->prev = inode; inode->next = node->chld; node->chld = inode; inode->parent = node; node->chldc++; node = inode; if (xml->val) { xml = xml->val; continue; } break; } case XML_STRING: node->val = xml_strdup(xml, heap, node); break; default: break; } if (xml->next) { xml = xml->next; } else if ((xpa = xml->parent) != root) { do { node = node->parent; xml = xpa->next; xpa = xpa->parent; if (xpa == root || xml == root) goto end; } while (!xml && xpa); } else { break; } } /* while (xml) */ end: return tree; } ================================================ FILE: src/tree.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_src_tree_h #define ak_src_tree_h #include "../include/ak/assetkit.h" #include /** * @brief load a tree from xml */ AK_HIDE AkTreeNode* tree_fromxml(AkHeap * __restrict heap, void * __restrict memParent, xml_t * __restrict xml); #endif /* ak_src_tree_h */ ================================================ FILE: src/type.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "common.h" #include "../include/ak/type.h" #include "type.h" #include "default/type.h" #include RBTree *ak__typetree = NULL; RBTree *ak__typetree_byname = NULL; AK_EXPORT AkTypeId ak_typeidh(AkHeapNode * __restrict hnode) { return hnode->typeid; } AK_EXPORT AkTypeId ak_typeid(void * __restrict mem) { AkHeapNode *hnode; hnode = ak__alignof(mem); return hnode->typeid; } AK_EXPORT void ak_setypeid(void * __restrict mem, AkTypeId tid) { AkHeapNode *hnode; hnode = ak__alignof(mem); hnode->typeid = tid; } AK_EXPORT bool ak_isKindOf(void * __restrict mem, void * __restrict other) { AkHeapNode *hnode1, *hnode2; if (!mem || !other) return false; hnode1 = ak__alignof(mem); hnode2 = ak__alignof(other); return hnode1->typeid == hnode2->typeid; } AK_EXPORT AkTypeDesc* ak_typeDesc(AkTypeId typeId) { return rb_find(ak__typetree, (void *)typeId); } AK_EXPORT AkTypeDesc* ak_typeDescByName(const char * __restrict name) { return rb_find(ak__typetree_byname, (void *)name); } AK_EXPORT void ak_registerType(AkTypeId typeId, AkTypeDesc *desc) { rb_insert(ak__typetree, (void *)typeId, desc); if (desc->typeName) rb_insert(ak__typetree_byname, (void *)desc->typeName, desc); } AK_HIDE void ak_type_init(void) { AkTypeDesc *it; ak__typetree = rb_newtree(NULL, ds_cmp_i32p, NULL); ak__typetree_byname = rb_newtree(NULL, ds_cmp_str, NULL); /* register predefined type descs */ it = (AkTypeDesc *)ak_def_typedesc(); if (it) { do { ak_registerType(it->typeId, it); it++; } while (it->size > 0); } } AK_HIDE void ak_type_deinit(void) { rb_destroy(ak__typetree); rb_destroy(ak__typetree_byname); } ================================================ FILE: src/type.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_src_type_h #define ak_src_type_h AK_HIDE void ak_type_init(void); AK_HIDE void ak_type_deinit(void); #endif /* ak_src_type_h */ ================================================ FILE: src/utils.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "utils.h" #include #include #include #include #include #include #include #include #include "../include/ak/options.h" #include "mem/common.h" char * strptime(const char * __restrict buf, const char * __restrict fmt, struct tm * __restrict tm); AkResult ak_readfile(const char * __restrict file, void * __restrict parent, void ** __restrict dest, size_t * __restrict size) { FILE * infile; size_t blksize; size_t fsize; size_t fcontents_size; size_t total_read; size_t nread; struct stat infile_st; int infile_no; infile = fopen(file, "rb"); if (!infile) { fprintf(stderr, "errno: %d: %s", errno, strerror(errno)); goto err; } infile_no = fileno(infile); if (fstat(infile_no, &infile_st) != 0) goto err; fsize = infile_st.st_size; if (ak_opt_get(AK_OPT_USE_MMAP) && (*dest = ak_mmap_rdonly(infile_no, fsize))) { *size = fsize; if (parent) ak_mmap_attach(parent, *dest, fsize); return AK_OK; } #ifndef AK_WINAPI blksize = infile_st.st_blksize; #else blksize = 512; #endif fcontents_size = sizeof(char) * fsize; *size = fcontents_size; *dest = malloc(fcontents_size + 1); assert(*dest && "malloc failed"); memset(*(char **)dest + fcontents_size, '\0', 1); total_read = 0; do { if ((fcontents_size - total_read) < blksize) blksize = fcontents_size - total_read; nread = fread(*(char **)dest + total_read, sizeof(char), blksize, infile); total_read += nread; } while (nread > 0 && total_read < fsize); fclose(infile); return AK_OK; err: *dest = NULL; *size = 0; return AK_ERR; } time_t ak_parse_date(const char * __restrict input, const char ** __restrict ret) { struct tm _tm; const char * cp; memset(&_tm, '\0', sizeof(_tm)); cp = strptime(input, "%Y-%m-%dT%T%Z", &_tm); if (ret) *ret = cp; return mktime(&_tm); } AK_EXPORT int ak_digitsize(size_t number) { if (number == 0) return 1; return (int)floor(log10((double)number)) + 1; } AK_HIDE void ak_releasefile(void *file, size_t size) { if (ak_opt_get(AK_OPT_USE_MMAP)) { ak_unmap(file, size); return; } free(file); } ================================================ FILE: src/utils.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_src_utils_h #define ak_src_utils_h #include "../include/ak/assetkit.h" #include "../include/ak/util.h" AkResult ak_readfile(const char * __restrict file, void * __restrict parent, void ** __restrict dest, size_t * __restrict size); time_t ak_parse_date(const char * __restrict input, const char ** __restrict ret); AK_HIDE void ak_releasefile(void *file, size_t size); #endif /* ak_src_utils_h */ ================================================ FILE: src/win32/CMakeLists.txt ================================================ FILE(GLOB CSources *.h *.c) target_sources(${PROJECT_NAME} PRIVATE ${CSources} ) ================================================ FILE: src/win32/dllmain.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../config.h" #include "../mem/common.h" void AK_CONSTRUCTOR ak__init(); void AK_DESTRUCTOR ak__cleanup(); BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved); BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { (void)hModule; (void)lpReserved; switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: ak__init(); break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: ak__cleanup(); break; } return TRUE; } ================================================ FILE: src/win32/strptime.c ================================================ /* * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc. * All rights reserved. * * This code was contributed to The NetBSD Foundation by Klaus Klein. * Heavily optimised by David Laight * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include typedef unsigned char u_char; /* * We do not implement alternate representations. However, we always * check whether a given modifier is allowed for a certain conversion. */ #define ALT_E 0x01 #define ALT_O 0x02 #define LEGAL_ALT(x) { if (alt_format & ~(x)) return NULL; } static char gmt[] = { "GMT" }; #ifdef TM_ZONE static char utc[] = { "UTC" }; #endif /* RFC-822/RFC-2822 */ static const char * const nast[5] = { "EST", "CST", "MST", "PST", "\0\0\0" }; static const char * const nadt[5] = { "EDT", "CDT", "MDT", "PDT", "\0\0\0" }; #define TM_YEAR_BASE 1900 static const char *day[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; static const char *abday[7] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" }; static const char *mon[12] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; static const char *abmon[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static const char *am_pm[2] = { "AM", "PM" }; static const u_char *conv_num (const unsigned char *, int *, unsigned int, unsigned int); static const u_char *find_string (const u_char *, int *, const char * const *, const char * const *, int); const char * strptime (const char *buf, const char *fmt, struct tm *tm); const char * strptime (const char *buf, const char *fmt, struct tm *tm) { unsigned char c; const unsigned char *bp, *ep; int alt_format, i, split_year = 0, neg = 0, offs; const char *new_fmt; bp = (const u_char *)buf; while (bp != NULL && (c = *fmt++) != '\0') { /* Clear 'alternate' modifier prior to new conversion. */ alt_format = 0; i = 0; /* Eat up white-space. */ if (isspace(c)) { while (isspace(*bp)) bp++; continue; } if (c != '%') goto literal; again: switch (c = *fmt++) { case '%': /* "%%" is converted to "%". */ literal: if (c != *bp++) return NULL; LEGAL_ALT(0); continue; /* * "Alternative" modifiers. Just set the appropriate flag * and start over again. */ case 'E': /* "%E?" alternative conversion modifier. */ LEGAL_ALT(0); alt_format |= ALT_E; goto again; case 'O': /* "%O?" alternative conversion modifier. */ LEGAL_ALT(0); alt_format |= ALT_O; goto again; /* * "Complex" conversion rules, implemented through recursion. */ case 'c': /* Date and time, using the locale's format. */ new_fmt = "%x %X"; goto recurse; case 'D': /* The date as "%m/%d/%y". */ new_fmt = "%m/%d/%y"; LEGAL_ALT(0); goto recurse; case 'F': /* The date as "%Y-%m-%d". */ new_fmt = "%Y-%m-%d"; LEGAL_ALT(0); goto recurse; case 'R': /* The time as "%H:%M". */ new_fmt = "%H:%M"; LEGAL_ALT(0); goto recurse; case 'r': /* The time in 12-hour clock representation. */ new_fmt = "%I:%M:%S %p"; LEGAL_ALT(0); goto recurse; case 'T': /* The time as "%H:%M:%S". */ new_fmt = "%H:%M:%S"; LEGAL_ALT(0); goto recurse; case 'X': /* The time, using the locale's format. */ new_fmt = "%H:%M:%S"; goto recurse; case 'x': /* The date, using the locale's format. */ new_fmt = "%m/%d/%y"; recurse: bp = (const u_char *)strptime((const char *)bp, new_fmt, tm); LEGAL_ALT(ALT_E); continue; /* * "Elementary" conversion rules. */ case 'A': /* The day of week, using the locale's form. */ case 'a': bp = find_string(bp, &tm->tm_wday, day, abday, 7); LEGAL_ALT(0); continue; case 'B': /* The month, using the locale's form. */ case 'b': case 'h': bp = find_string(bp, &tm->tm_mon, mon, abmon, 12); LEGAL_ALT(0); continue; case 'C': /* The century number. */ i = 20; bp = conv_num(bp, &i, 0, 99); i = i * 100 - TM_YEAR_BASE; if (split_year) i += tm->tm_year % 100; split_year = 1; tm->tm_year = i; LEGAL_ALT(ALT_E); continue; case 'd': /* The day of month. */ case 'e': bp = conv_num(bp, &tm->tm_mday, 1, 31); LEGAL_ALT(ALT_O); continue; case 'k': /* The hour (24-hour clock representation). */ LEGAL_ALT(0); /* FALLTHROUGH */ case 'H': bp = conv_num(bp, &tm->tm_hour, 0, 23); LEGAL_ALT(ALT_O); continue; case 'l': /* The hour (12-hour clock representation). */ LEGAL_ALT(0); /* FALLTHROUGH */ case 'I': bp = conv_num(bp, &tm->tm_hour, 1, 12); if (tm->tm_hour == 12) tm->tm_hour = 0; LEGAL_ALT(ALT_O); continue; case 'j': /* The day of year. */ i = 1; bp = conv_num(bp, &i, 1, 366); tm->tm_yday = i - 1; LEGAL_ALT(0); continue; case 'M': /* The minute. */ bp = conv_num(bp, &tm->tm_min, 0, 59); LEGAL_ALT(ALT_O); continue; case 'm': /* The month. */ i = 1; bp = conv_num(bp, &i, 1, 12); tm->tm_mon = i - 1; LEGAL_ALT(ALT_O); continue; case 'p': /* The locale's equivalent of AM/PM. */ bp = find_string(bp, &i, am_pm, NULL, 2); if (tm->tm_hour > 11) return NULL; tm->tm_hour += i * 12; LEGAL_ALT(0); continue; case 'S': /* The seconds. */ bp = conv_num(bp, &tm->tm_sec, 0, 61); LEGAL_ALT(ALT_O); continue; case 'U': /* The week of year, beginning on sunday. */ case 'W': /* The week of year, beginning on monday. */ /* * XXX This is bogus, as we can not assume any valid * information present in the tm structure at this * point to calculate a real value, so just check the * range for now. */ bp = conv_num(bp, &i, 0, 53); LEGAL_ALT(ALT_O); continue; case 'w': /* The day of week, beginning on sunday. */ bp = conv_num(bp, &tm->tm_wday, 0, 6); LEGAL_ALT(ALT_O); continue; case 'u': /* The day of week, monday = 1. */ bp = conv_num(bp, &i, 1, 7); tm->tm_wday = i % 7; LEGAL_ALT(ALT_O); continue; case 'g': /* The year corresponding to the ISO week * number but without the century. */ bp = conv_num(bp, &i, 0, 99); continue; case 'G': /* The year corresponding to the ISO week * number with century. */ do bp++; while (isdigit(*bp)); continue; case 'V': /* The ISO 8601:1988 week number as decimal */ bp = conv_num(bp, &i, 0, 53); continue; case 'Y': /* The year. */ i = TM_YEAR_BASE; /* just for data sanity... */ bp = conv_num(bp, &i, 0, 9999); tm->tm_year = i - TM_YEAR_BASE; LEGAL_ALT(ALT_E); continue; case 'y': /* The year within 100 years of the epoch. */ /* LEGAL_ALT(ALT_E | ALT_O); */ bp = conv_num(bp, &i, 0, 99); if (split_year) /* preserve century */ i += (tm->tm_year / 100) * 100; else { split_year = 1; if (i <= 68) i = i + 2000 - TM_YEAR_BASE; else i = i + 1900 - TM_YEAR_BASE; } tm->tm_year = i; continue; case 'Z': if (strncmp((const char *)bp, gmt, 3) == 0) { tm->tm_isdst = 0; #ifdef TM_GMTOFF tm->TM_GMTOFF = 0; #endif #ifdef TM_ZONE tm->TM_ZONE = gmt; #endif bp += 3; } else { #if defined(_TM_DEFINED) && !defined(_WIN32_WCE) _tzset(); ep = find_string(bp, &i, (const char * const *)tzname, NULL, 2); if (ep != NULL) { tm->tm_isdst = i; #ifdef TM_GMTOFF tm->TM_GMTOFF = -(timezone); #endif #ifdef TM_ZONE tm->TM_ZONE = tzname[i]; #endif } bp = ep; #endif } continue; case 'z': /* * We recognize all ISO 8601 formats: * Z = Zulu time/UTC * [+-]hhmm * [+-]hh:mm * [+-]hh * We recognize all RFC-822/RFC-2822 formats: * UT|GMT * North American : UTC offsets * E[DS]T = Eastern : -4 | -5 * C[DS]T = Central : -5 | -6 * M[DS]T = Mountain: -6 | -7 * P[DS]T = Pacific : -7 | -8 * Military * [A-IL-M] = -1 ... -9 (J not used) * [N-Y] = +1 ... +12 */ while (isspace(*bp)) bp++; switch (*bp++) { case 'G': if (*bp++ != 'M') return NULL; /*FALLTHROUGH*/ case 'U': if (*bp++ != 'T') return NULL; /*FALLTHROUGH*/ case 'Z': tm->tm_isdst = 0; #ifdef TM_GMTOFF tm->TM_GMTOFF = 0; #endif #ifdef TM_ZONE tm->TM_ZONE = utc; #endif continue; case '+': neg = 0; break; case '-': neg = 1; break; default: --bp; ep = find_string(bp, &i, nast, NULL, 4); if (ep != NULL) { #ifdef TM_GMTOFF tm->TM_GMTOFF = -5 - i; #endif #ifdef TM_ZONE tm->TM_ZONE = __UNCONST(nast[i]); #endif bp = ep; continue; } ep = find_string(bp, &i, nadt, NULL, 4); if (ep != NULL) { tm->tm_isdst = 1; #ifdef TM_GMTOFF tm->TM_GMTOFF = -4 - i; #endif #ifdef TM_ZONE tm->TM_ZONE = __UNCONST(nadt[i]); #endif bp = ep; continue; } if ((*bp >= 'A' && *bp <= 'I') || (*bp >= 'L' && *bp <= 'Y')) { #ifdef TM_GMTOFF /* Argh! No 'J'! */ if (*bp >= 'A' && *bp <= 'I') tm->TM_GMTOFF = ('A' - 1) - (int)*bp; else if (*bp >= 'L' && *bp <= 'M') tm->TM_GMTOFF = 'A' - (int)*bp; else if (*bp >= 'N' && *bp <= 'Y') tm->TM_GMTOFF = (int)*bp - 'M'; #endif #ifdef TM_ZONE tm->TM_ZONE = NULL; /* XXX */ #endif bp++; continue; } return NULL; } offs = 0; for (i = 0; i < 4; ) { if (isdigit(*bp)) { offs = offs * 10 + (*bp++ - '0'); i++; continue; } if (i == 2 && *bp == ':') { bp++; continue; } break; } switch (i) { case 2: offs *= 100; break; case 4: i = offs % 100; if (i >= 60) return NULL; /* Convert minutes into decimal */ offs = (offs / 100) * 100 + (i * 50) / 30; break; default: return NULL; } if (neg) offs = -offs; tm->tm_isdst = 0; /* XXX */ #ifdef TM_GMTOFF tm->TM_GMTOFF = offs; #endif #ifdef TM_ZONE tm->TM_ZONE = NULL; /* XXX */ #endif continue; /* * Miscellaneous conversions. */ case 'n': /* Any kind of white-space. */ case 't': while (isspace(*bp)) bp++; LEGAL_ALT(0); continue; default: /* Unknown/unsupported conversion. */ return NULL; } } return ((const char *)bp); } static const u_char * conv_num (const unsigned char *buf, int *dest, unsigned int llim, unsigned int ulim) { unsigned int result = 0; unsigned char ch; /* The limit also determines the number of valid digits. */ unsigned int rulim = ulim; ch = *buf; if (ch < '0' || ch > '9') return NULL; do { result *= 10; result += ch - '0'; rulim /= 10; ch = *++buf; } while ((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9'); if (result < llim || result > ulim) return NULL; *dest = result; return buf; } static int strncasecmp (const char *s1, const char *s2, size_t n) { if (n == 0) return 0; while (n-- != 0 && tolower(*s1) == tolower(*s2)) { if (n == 0 || *s1 == '\0' || *s2 == '\0') break; s1++; s2++; } return tolower(*(const unsigned char *) s1) - tolower(*(const unsigned char *) s2); } static const u_char * find_string (const u_char *bp, int *tgt, const char * const *n1, const char * const *n2, int c) { int i; size_t len; /* check full name - then abbreviated ones */ for (; n1 != NULL; n1 = n2, n2 = NULL) { for (i = 0; i < c; i++, n1++) { len = strlen(*n1); if (strncasecmp(*n1, (const char *)bp, len) == 0) { *tgt = i; return bp + len; } } } /* Nothing matched */ return NULL; } ================================================ FILE: src/xml.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "common.h" #include "xml.h" AK_EXPORT char * xml_strdup(const xml_t * __restrict xobj, AkHeap * __restrict heap, void * __restrict parent) { char *s, *p; const xml_t *v; size_t len; if ((len = xmls_sumlen(xobj)) < 1) return NULL; s = p = ak_heap_alloc(heap, parent, len); if (xobj->type!= XML_STRING) { v = xmls(xobj); /* because len > 0 */ do { memcpy(p, v->val, v->valsize); p += v->valsize; } while ((v = xmls_next(v))); } else { memcpy(p, xobj->val, xobj->valsize); } s[len - 1] = '\0'; return s; } AK_EXPORT unsigned long xml_strtof_fast(const xml_t * __restrict xobj, AkFloat * __restrict dest, unsigned long n) { const xml_t *v; unsigned long rem; /* this step must be done before calling this func. */ /* if (!(v = xmls(xobj))) return 0; */ if (!(v = xobj) || !v->val || (rem = n) < 1) return 0; while ((rem = ak_strtof(v->val, v->valsize, rem, dest + n - rem)) && (v = xmls_next(v))); return rem; } AK_EXPORT unsigned long xml_strtoui_fast(const xml_t * __restrict xobj, AkUInt * __restrict dest, unsigned long n) { const xml_t *v; unsigned long rem; /* this step must be done before calling this func. */ /* if (!(v = xmls(xobj))) return 0; */ if (!(v = xobj) || !v->val || (rem = n) < 1) return 0; while ((rem = ak_strtoui(v->val, v->valsize, rem, dest + n - rem)) && (v = xmls_next(v))); return rem; } AK_EXPORT unsigned long xml_strtoi_fast(const xml_t * __restrict xobj, AkInt * __restrict dest, unsigned long n) { const xml_t *v; unsigned long rem; /* this step must be done before calling this func. */ /* if (!(v = xmls(xobj))) return 0; */ if (!(v = xobj) || !v->val || (rem = n) < 1) return 0; while ((rem = ak_strtoi(v->val, v->valsize, rem, dest + n - rem)) && (v = xmls_next(v))); return rem; } AK_EXPORT unsigned long xml_strtob_fast(const xml_t * __restrict xobj, AkBool * __restrict dest, unsigned long n) { const xml_t *v; unsigned long rem; /* this step must be done before calling this func. */ /* if (!(v = xmls(xobj))) return 0; */ if (!(v = xobj) || !v->val || (rem = n) < 1) return 0; while ((rem = ak_strtob(v->val, v->valsize, rem, dest + n - rem)) && (v = xmls_next(v))); return rem; } AK_EXPORT size_t xml_strtok_count_fast(const xml_t * __restrict xobj, size_t * __restrict len) { const xml_t *v, *p; size_t count, len_total, l; // if (!(v = xmls(xobj))) // return 0; v = xobj; len_total = 0; count = 0; p = v; do { count += ak_strtok_count_fast(p->val, p->valsize, &l); len_total += l; } while ((p = xmls_next(p))); if (len) *len = len_total; return count; } AK_EXPORT AkResult xml_strtof_arrayL(AkHeap * __restrict heap, void * __restrict memp, const xml_t * __restrict xobj, AkFloatArrayL ** __restrict array) { AkFloatArrayL *arr; unsigned long count; if ((count = (unsigned long)xml_strtok_count_fast(xobj, NULL)) == 0) return AK_ERR; arr = ak_heap_alloc(heap, memp, sizeof(*arr) + sizeof(AkFloat) * count); xml_strtof_fast(xobj, arr->items, count); arr->count = count; arr->next = NULL; *array = arr; return AK_OK; } AK_EXPORT AkResult xml_strtoui_array(AkHeap * __restrict heap, void * __restrict memp, const xml_t * __restrict xobj, AkUIntArray ** __restrict array) { AkUIntArray *arr; unsigned long count; if ((count = (unsigned long)xml_strtok_count_fast(xobj, NULL)) == 0) return AK_ERR; arr = ak_heap_alloc(heap, memp, sizeof(*arr) + sizeof(AkUInt) * count); xml_strtoui_fast(xobj, arr->items, count); arr->count = count; *array = arr; return AK_OK; } AK_EXPORT char * xmla_strdup(const xml_attr_t * __restrict attr, AkHeap * __restrict heap, void * __restrict parent) { const char *s; if (!attr || !(s = attr->val)) return NULL; return ak_heap_strndup(heap, parent, s, attr->valsize); } AK_EXPORT char * xmla_strdup_by(const xml_t * __restrict xobject, AkHeap * __restrict heap, const char * __restrict name, void * __restrict parent) { xml_attr_t *att; if ((att = xmla(xobject, name))) return ak_heap_strndup(heap, parent, att->val, att->valsize); return NULL; } AK_EXPORT void xmla_setid(const xml_t * __restrict xobject, AkHeap * __restrict heap, void * __restrict memptr) { xml_attr_t *att; if ((att = xmla(xobject, "id"))) ak_setId(memptr, ak_heap_strndup(heap, memptr, att->val, att->valsize)); } ================================================ FILE: src/xml.h ================================================ /* * Copyright (c), Recep Aslantas. * * MIT License (MIT), http://opensource.org/licenses/MIT * Full license can be found in the LICENSE file */ #ifndef ak_src_xml_h #define ak_src_xml_h #include "common.h" #include AK_EXPORT char * xml_strdup(const xml_t * __restrict xobj, AkHeap * __restrict heap, void * __restrict parent); AK_EXPORT unsigned long xml_strtof_fast(const xml_t * __restrict xobj, AkFloat * __restrict dest, unsigned long n); AK_EXPORT unsigned long xml_strtoui_fast(const xml_t * __restrict xobj, AkUInt * __restrict dest, unsigned long n); AK_EXPORT unsigned long xml_strtoi_fast(const xml_t * __restrict xobj, AkInt * __restrict dest, unsigned long n); AK_EXPORT unsigned long xml_strtob_fast(const xml_t * __restrict xobj, AkBool * __restrict dest, unsigned long n); AK_EXPORT size_t xml_strtok_count_fast(const xml_t * __restrict xobj, size_t * __restrict len); AK_EXPORT AkResult xml_strtof_arrayL(AkHeap * __restrict heap, void * __restrict memp, const xml_t * __restrict xobj, AkFloatArrayL ** __restrict array); AK_EXPORT AkResult xml_strtoui_array(AkHeap * __restrict heap, void * __restrict memp, const xml_t * __restrict xobj, AkUIntArray ** __restrict array); AK_EXPORT char * xmla_strdup(const xml_attr_t * __restrict attr, AkHeap * __restrict heap, void * __restrict parent); AK_EXPORT char * xmla_strdup_by(const xml_t * __restrict xobject, AkHeap * __restrict heap, const char * __restrict name, void * __restrict parent); AK_EXPORT void xmla_setid(const xml_t * __restrict xobject, AkHeap * __restrict heap, void * __restrict memptr); #endif /* ak_src_xml_h */ ================================================ FILE: test/include/common.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef tests_common_h #define tests_common_h #include #include #include typedef struct test_status_t { const char *msg; int status; } test_status_t; typedef test_status_t (*fntest)(void); typedef struct test_entry_t { char *name; fntest entry; int ret; int show_output; } test_entry_t; #define RESET "\033[0m" #define BLACK "\033[30m" /* Black */ #define RED "\033[31m" /* Red */ #define GREEN "\033[32m" /* Green */ #define YELLOW "\033[33m" /* Yellow */ #define BLUE "\033[34m" /* Blue */ #define MAGENTA "\033[35m" /* Magenta */ #define CYAN "\033[36m" /* Cyan */ #define WHITE "\033[37m" /* White */ #define BOLDBLACK "\033[1m\033[30m" /* Bold Black */ #define BOLDRED "\033[1m\033[31m" /* Bold Red */ #define BOLDGREEN "\033[1m\033[32m" /* Bold Green */ #define BOLDYELLOW "\033[1m\033[33m" /* Bold Yellow */ #define BOLDBLUE "\033[1m\033[34m" /* Bold Blue */ #define BOLDMAGENTA "\033[1m\033[35m" /* Bold Magenta */ #define BOLDCYAN "\033[1m\033[36m" /* Bold Cyan */ #define BOLDWHITE "\033[1m\033[37m" /* Bold White */ #define TEST_DECLARE(FUN) test_status_t test_ ## FUN(void); #define TEST_ENTRY(FUN) { #FUN, test_ ## FUN, 0, 0 }, #define TEST_LIST static test_entry_t tests[] = /* __VA_ARGS__ workaround for MSVC: https://stackoverflow.com/a/5134656 */ #define EXPAND(x) x #define TEST_OK 1 #define TEST_SUCCESS return (test_status_t){NULL, TEST_OK}; #define TEST_IMPL_ARG1(FUN) \ test_status_t test_ ## FUN (void); \ test_status_t test_ ## FUN() #define TEST_IMPL_ARG2(PREFIX, FUN) TEST_IMPL_ARG1(PREFIX ## FUN) #define TEST_IMPL_ARG3(arg1, arg2, arg3, ...) arg3 #define TEST_IMPL_CHOOSER(...) \ EXPAND(TEST_IMPL_ARG3(__VA_ARGS__, TEST_IMPL_ARG2, TEST_IMPL_ARG1)) #define TEST_IMPL(...) EXPAND(TEST_IMPL_CHOOSER(__VA_ARGS__)(__VA_ARGS__)) #define ASSERT_EXT(expr, msg) \ if (!(expr)) { \ fprintf(stderr, \ RED " assert fail" RESET \ " in " BOLDCYAN "%s " RESET \ "on " BOLDMAGENTA "line %d" RESET \ " : " BOLDWHITE " ASSERT(%s)\n" RESET, \ __FILE__, \ __LINE__, \ #expr); \ return (test_status_t){msg, 0}; \ } #define ASSERT_ARG1(expr) ASSERT_EXT(expr, NULL) #define ASSERT_ARG2(expr, msg) ASSERT_EXT(expr, msg) #define ASSERT_ARG3(arg1, arg2, arg3, ...) arg3 #define ASSERT_CHOOSER(...) ASSERT_ARG3(__VA_ARGS__, ASSERT_ARG2, ASSERT_ARG1) #define ASSERT(...) do { ASSERT_CHOOSER(__VA_ARGS__)(__VA_ARGS__) } while(0); #define ASSERTIFY(expr) do { \ test_status_t ts; \ ts = expr; \ if (ts.status != TEST_OK) { \ fprintf(stderr, \ RED " assert fail" RESET \ " in " BOLDCYAN "%s " RESET \ "on " BOLDMAGENTA "line %d" RESET \ " : " BOLDWHITE " ASSERTIFY(%s)\n" RESET, \ __FILE__, \ __LINE__, \ #expr); \ return (test_status_t){ts.msg, 0}; \ } \ } while(0); #if defined(_WIN32) # define drand48() ((float)(rand() / (RAND_MAX + 1.0))) # define OK_TEXT "ok:" # define FAIL_TEXT "fail:" # define FINAL_TEXT "^_^" #else # define OK_TEXT "✔︎" # define FAIL_TEXT "𐄂" # define FINAL_TEXT "🎉" #endif #endif /* common_h */ ================================================ FILE: test/runner.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "include/common.h" #include "tests.h" #include #include #include #define TARGET_NAME "AssetKit" int main(int argc, const char * argv[]) { test_entry_t *entry; test_status_t st; int32_t i, count, passed, failed, maxlen; double start, end, elapsed, total; passed = failed = maxlen = 0; total = 0.0; count = sizeof(tests) / sizeof(tests[0]); fprintf(stderr, CYAN "\nWelcome to %s tests\n\n" RESET, TARGET_NAME); for (i = 0; i < count; i++) { int32_t len; entry = tests + i; len = (int32_t)strlen(entry->name); if (len > maxlen) maxlen = len; } maxlen += 5; fprintf(stderr, BOLDWHITE " %-*s %-*s\n", maxlen, "Test Name", maxlen, "Elapsed Time"); for (i = 0; i < count; i++) { entry = tests + i; start = clock(); st = entry->entry(); end = clock(); elapsed = (end - start) / CLOCKS_PER_SEC; total += elapsed; if (!st.status) { fprintf(stderr, BOLDRED " " FAIL_TEXT BOLDWHITE " %s " RESET, entry->name); if (st.msg) { fprintf(stderr, YELLOW "- %s" RESET, st.msg); } fprintf(stderr, "\n"); failed++; } else { fprintf(stderr, GREEN " " OK_TEXT RESET " %-*s ", maxlen, entry->name); if (elapsed > 0.01) fprintf(stderr, YELLOW "%.2fs", elapsed); else fprintf(stderr, "0"); fprintf(stderr, "\n" RESET); passed++; } } if (failed == 0) { fprintf(stderr, BOLDGREEN "\n All tests are passed " FINAL_TEXT "\n" RESET); } fprintf(stderr, CYAN "\n%s test results (%0.2fs):\n" RESET "--------------------------\n" MAGENTA "%d" RESET " tests are runned, " GREEN "%d" RESET " %s passed, " RED "%d" RESET " %s failed\n\n" RESET, TARGET_NAME, total, count, passed, passed > 1 ? "are" : "is", failed, failed > 1 ? "are" : "is"); return failed; } ================================================ FILE: test/src/collada/test_dae_load.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../test_common.h" const char *dae_dir = "./test/sample-models/collada/files"; TEST_IMPL(dae_load_folder) { DIR *dir; struct dirent *ent; AkDoc *doc; chdir(dae_dir); if ((dir = opendir ("./")) != NULL) { while ((ent = readdir (dir)) != NULL) { size_t namelen; namelen = strlen(ent->d_name); if (*ent->d_name == '.') { if (namelen == 1 || (namelen == 2 && ent->d_name[1] == '.') || strcmp(ent->d_name, ".DS_Store") == 0) continue; } ASSERT(ak_load(&doc, ent->d_name, NULL) == AK_OK); ak_free(doc); } closedir(dir); } TEST_SUCCESS } ================================================ FILE: test/src/test_common.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ak_test_common_h #define ak_test_common_h #include "../include/common.h" #include #include #include #include #include #include #include #include #include #include #include #include #endif /* ak_test_common_h */ ================================================ FILE: test/src/test_memory.c ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "test_common.h" #include "../../src/mem_common.h" #include "../../src/mem_lt.h" extern AkHeapAllocator ak__allocator; TEST_IMPL(heap) { AkHeap *heap, *other, staticHeap; uint32_t heapid, data; heap = ak_heap_new(NULL, NULL, NULL); ASSERT(heap->allocator == &ak__allocator); ASSERT(ak_heap_allocator(heap) == &ak__allocator); heapid = heap->heapid; ASSERT(heapid > 0); ASSERT(ak_heap_lt_find(heap->heapid) == heap); other = ak_heap_new(NULL, NULL, NULL); ak_heap_attach(heap, other); ASSERT(heap->chld == other); ak_heap_dettach(heap, other); ASSERT(heap->chld == NULL); ak_heap_attach(heap, other); ASSERT(heap->chld == other); ak_heap_setdata(heap, &data); ASSERT(ak_heap_data(heap) == &data); ak_heap_destroy(heap); ASSERT(ak_heap_lt_find(heapid) == NULL); ak_heap_init(&staticHeap, NULL, NULL, NULL); ASSERT(staticHeap.heapid > 0); ak_heap_lt_remove(staticHeap.heapid); ASSERT(ak_heap_lt_find(staticHeap.heapid) == NULL); TEST_SUCCESS } TEST_IMPL(heap_multiple) { AkHeap *heap, *root; uint32_t i; root = ak_heap_new(NULL, NULL, NULL); /* multiple alloc, leak */ for (i = 0; i < 1000; i++) heap = ak_heap_new(NULL, NULL, NULL); /* multiple alloc 2, leak */ for (i = 0; i < 1000; i++) heap = ak_heap_new(NULL, NULL, NULL); /* multiple alloc-free 1 */ for (i = 0; i < 1000; i++) { heap = ak_heap_new(NULL, NULL, NULL); ak_heap_destroy(heap); } /* multiple alloc-free 2 */ for (i = 0; i < 1000; i++) { heap = ak_heap_new(NULL, NULL, NULL); ak_heap_destroy(heap); } /* multiple alloc, attach to parent */ for (i = 0; i < 1000; i++) { heap = ak_heap_new(NULL, NULL, NULL); ak_heap_attach(root, heap); } /* multiple alloc, attach to parent */ for (i = 0; i < 1000; i++) { heap = ak_heap_new(NULL, NULL, NULL); ak_heap_attach(root, heap); } ak_heap_destroy(root); root = ak_heap_new(NULL, NULL, NULL); /* multiple alloc, attach-detach to parent */ for (i = 0; i < 1000; i++) { heap = ak_heap_new(NULL, NULL, NULL); ak_heap_attach(root, heap); ak_heap_dettach(root, heap); } TEST_SUCCESS } ================================================ FILE: test/tests.h ================================================ /* * Copyright (C) 2020 Recep Aslantas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef tests_h #define tests_h #include "include/common.h" /* * To register a test: * 1. use TEST_DECLARE() to forward declare test * 2. use TEST_ENTRY() to add test to list */ TEST_DECLARE(heap) TEST_DECLARE(heap_multiple) TEST_DECLARE(dae_load_folder) /*****************************************************************************/ TEST_LIST { TEST_ENTRY(heap) TEST_ENTRY(heap_multiple) TEST_ENTRY(dae_load_folder) }; #endif /* tests_h */