diff --git a/3rdparty/ozz-animation/.idea/.gitignore b/3rdparty/ozz-animation/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/3rdparty/ozz-animation/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/3rdparty/ozz-animation/.idea/.name b/3rdparty/ozz-animation/.idea/.name deleted file mode 100644 index 35c3d80..0000000 --- a/3rdparty/ozz-animation/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -ozz \ No newline at end of file diff --git a/3rdparty/ozz-animation/.idea/misc.xml b/3rdparty/ozz-animation/.idea/misc.xml deleted file mode 100644 index 79b3c94..0000000 --- a/3rdparty/ozz-animation/.idea/misc.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/3rdparty/ozz-animation/.idea/modules.xml b/3rdparty/ozz-animation/.idea/modules.xml deleted file mode 100644 index 1c6fd35..0000000 --- a/3rdparty/ozz-animation/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/3rdparty/ozz-animation/.idea/ozz-animation.iml b/3rdparty/ozz-animation/.idea/ozz-animation.iml deleted file mode 100644 index f08604b..0000000 --- a/3rdparty/ozz-animation/.idea/ozz-animation.iml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/3rdparty/ozz-animation/.idea/vcs.xml b/3rdparty/ozz-animation/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/3rdparty/ozz-animation/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/3rdparty/ozz-animation/.travis.yml b/3rdparty/ozz-animation/.travis.yml deleted file mode 100644 index 81f8ceb..0000000 --- a/3rdparty/ozz-animation/.travis.yml +++ /dev/null @@ -1,387 +0,0 @@ -language: cpp - -dist: xenial - -jobs: - include: - - # Emscripten cross compiling - #- os: linux - # compiler: gcc - # env: - # - env_build_emscripten=1 - - # mac os - # Default mac os clang build - - os: osx - osx_image: xcode10 - compiler: clang - - os: osx - compiler: clang - - os: osx - compiler: clang - env: - - env_cmake_configuration=Release - - # Xcode mac os clang build - - os: osx - compiler: clang - env : - - env_cmake_generator=Xcode - - os: osx - compiler: clang - env : - - env_cmake_generator=Xcode - - env_cmake_configuration=Release - - # Linux ARM - - os: linux - compiler: clang - env: - - env_build_fbx=0 - - env_cmake_configuration=Debug - arch: - - arm64 - - os: linux - compiler: clang - env: - - env_build_fbx=0 - - env_cmake_configuration=Release - arch: - - arm64 - - # Linux - # Default linux clang debug - - os: linux - compiler: clang - env: - - env_cmake_configuration=Debug - # Default linux release clang - - os: linux - compiler: clang - env: - - env_cmake_configuration=Release - - # Specific configurations - - os: linux - compiler: clang - env: - - env_build_fbx=0 - - os: linux - compiler: clang - env: - - env_build_gltf=0 - - os: linux - compiler: clang - env: - - env_build_howtos=0 - - os: linux - compiler: clang - env: - - env_build_samples=0 - - os: linux - compiler: clang - env: - - env_build_tools=0 - - os: linux - compiler: clang - env: - - env_build_postfix=0 - - os: linux - compiler: clang - env: - - env_build_simd_ref=1 - - os: linux - compiler: clang - env: - - env_build_tests=0 - - os: linux - compiler: clang - env: - - env_build_fbx=0 - - env_build_gltf=0 - - env_build_samples=0 - - os: linux - compiler: clang - env: - - env_build_fbx=0 - - env_build_gltf=0 - - env_build_tests=0 - - os: linux - compiler: clang - env: - - env_build_data=0 - - env_build_fbx=0 - - env_build_gltf=0 - - env_build_samples=0 - - env_build_tests=0 - - os: linux - compiler: clang - env: - - env_build_data=0 - - os: linux - compiler: clang - env: - - env_src_root="../test/sub/" - - # Default linux gcc debug - - os: linux - compiler: gcc - env: - - env_cmake_configuration=Debug - # Default linux gcc release - - os: linux - compiler: gcc - env: - - env_cmake_configuration=Release - - # Newer gcc - - os: linux - compiler: gcc-4.9 - env: - - env_cmake_cxx_compiler=g++-4.9 - - env_cmake_c_compiler=gcc-4.9 - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-4.9 - - os: linux - compiler: gcc-5 - env: - - env_cmake_cxx_compiler=g++-5 - - env_cmake_c_compiler=gcc-5 - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-5 - - os: linux - compiler: gcc-6 - env: - - env_cmake_cxx_compiler=g++-6 - - env_cmake_c_compiler=gcc-6 - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-6 - - os: linux - compiler: gcc-7 - env: - - env_cmake_cxx_compiler=g++-7 - - env_cmake_c_compiler=gcc-7 - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-7 - - os: linux - compiler: gcc-8 - env: - - env_cmake_cxx_compiler=g++-8 - - env_cmake_c_compiler=gcc-8 - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-8 - - # Newer clang - - os: linux - compiler: clang-3.9 - env: - - env_cmake_cxx_compiler=clang++-3.9 - - env_cmake_c_compiler=clang-3.9 - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-xenial-3.9 - packages: - - clang-3.9 - - os: linux - compiler: clang-4.0 - env: - - env_cmake_cxx_compiler=clang++-4.0 - - env_cmake_c_compiler=clang-4.0 - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-xenial-4.0 - packages: - - clang-4.0 - - os: linux - compiler: clang-5.0 - env: - - env_cmake_cxx_compiler=clang++-5.0 - - env_cmake_c_compiler=clang-5.0 - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-xenial-5.0 - packages: - - clang-5.0 - - os: linux - compiler: clang-6.0 - env: - - env_cmake_cxx_compiler=clang++-6.0 - - env_cmake_c_compiler=clang-6.0 - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-xenial-6.0 - packages: - - clang-6.0 - - os: linux - compiler: clang-7 - env: - - env_cmake_cxx_compiler=clang++-7 - - env_cmake_c_compiler=clang-7 - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-xenial-7 - packages: - - clang-7 - - os: linux - compiler: clang-8 - env: - - env_cmake_cxx_compiler=clang++-8 - - env_cmake_c_compiler=clang-8 - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-xenial-8 - packages: - - clang-8 - - # older fbx sdk - - os: linux - compiler: gcc - env: - - fbx_download=http://download.autodesk.com/us/fbx/2017/2017.1/fbx20171_fbxsdk_linux.tar.gz - - os: osx - compiler: clang - env: - - fbx_download=http://download.autodesk.com/us/fbx/2017/2017.1/fbx20171_fbxsdk_clang_mac.pkg.tgz - -before_install: -- echo before_install---------------------------------------------------------- - -install: -- echo install----------------------------------------------------------------- -# Download and install mesa dev -- if [[ $TRAVIS_OS_NAME == "linux" ]]; then - sudo apt-get install libgl1-mesa-dev libglu1-mesa-dev; - fi -# Download and install fbx sdk -# "|| true" because 2019 sdk would return code 130 when reading the readme -# chmod because fbx2019 will install with 700 -- if [[ $TRAVIS_OS_NAME == "linux" ]]; then - if [ -z "$fbx_download" ]; then - FBX_DOWNLOAD=${fbx_download:-"http://www.autodesk.com/content/dam/autodesk/www/adn/fbx/20192/fbx20192_fbxsdk_linux.tar.gz"}; - else - FBX_DOWNLOAD="$fbx_download"; - fi; - mkdir fbx; - cd fbx; - sudo wget $FBX_DOWNLOAD -O fbx.tar.gz; - sudo tar -xf "fbx.tar.gz"; - (yes yes | sudo ./*_fbxsdk_linux /usr/local) || true; - sudo chmod -R 755 /usr/local/lib; - sudo chmod -R 755 /usr/local/include; - cd ..; - fi -- if [[ $TRAVIS_OS_NAME == "osx" ]]; then - if [ -z "$fbx_download" ]; then - FBX_DOWNLOAD=${fbx_download:-"http://www.autodesk.com/content/dam/autodesk/www/adn/fbx/20192/fbx20192_fbxsdk_clang_mac.pkg.tgz"}; - else - FBX_DOWNLOAD="$fbx_download"; - fi; - mkdir fbx; - cd fbx; - sudo wget $FBX_DOWNLOAD -O fbx.tgz; - sudo tar -xf "fbx.tgz"; - sudo installer -pkg *_fbxsdk_clang_macos.pkg -target /; - cd ..; - fi -# Download and install emscripten sdk -- if [[ $env_build_emscripten ]]; then - sudo apt-get install software-properties-common -y; - sudo add-apt-repository ppa:george-edison55/cmake-3.x -y; - sudo apt-get update; - sudo apt-get install --only-upgrade cmake -y; - git clone https://github.com/juj/emsdk.git; - cd emsdk; - sudo ./emsdk update-tags; - sudo ./emsdk install sdk-nightly-latest-64bit; - sudo ./emsdk activate --embedded sdk-nightly-latest-64bit; - source ./emsdk_env.sh; - cd ..; - fi - -before_script: -- echo before_script----------------------------------------------------------- - -# Setup default environment variables -- if [[ -z $env_src_root ]]; then export env_src_root=".."; fi -- if [[ -z $env_build_fbx ]]; then export env_build_fbx=1; fi -- if [[ -z $env_build_gltf ]]; then export env_build_gltf=1; fi -- if [[ -z $env_build_data ]]; then export env_build_data=1; fi -- if [[ -z $env_build_howtos ]]; then export env_build_howtos=1; fi -- if [[ -z $env_build_tools ]]; then export env_build_tools=1; fi -- if [[ -z $env_build_postfix ]]; then export env_build_postfix=1; fi -- if [[ -z $env_build_samples ]]; then export env_build_samples=1; fi -- if [[ -z $env_build_simd_ref ]]; then export env_build_simd_ref=0; fi -- if [[ -z $env_build_tests ]]; then export env_build_tests=1; fi -- if [[ -z $env_cmake_configuration ]]; then export env_cmake_configuration=Debug; fi -- if [[ -z $env_cmake_cxx_compiler ]]; then export env_cmake_cxx_compiler=$CXX; fi -- if [[ -z $env_cmake_c_compiler ]]; then export env_cmake_c_compiler=$CC; fi -- if [[ -z $env_cmake_generator ]]; then export env_cmake_generator="Unix Makefiles"; fi -- if [[ $env_cmake_generator == "Unix Makefiles" ]]; then export env_cmake_generator_specific="-j2"; fi -- if [[ $env_cmake_generator == "Unix Makefiles" ]]; then export env_ctest_generator_specific="-j16"; fi -- if [[ $EMSCRIPTEN ]]; then env_cmake_toolchain="-DCMAKE_TOOLCHAIN_FILE=$EMSCRIPTEN/cmake/Modules/Platform/Emscripten.cmake"; fi - -# Display cmake version -- cmake --version - -script: -- echo script------------------------------------------------------------------ -# Configure build -- mkdir build -- cd build -- echo $env_cmake_toolchain -- cmake -G "$env_cmake_generator" $env_cmake_toolchain -DCMAKE_CXX_COMPILER=$env_cmake_cxx_compiler -DCMAKE_C_COMPILER=$env_cmake_c_compiler -DCMAKE_BUILD_TYPE=$env_cmake_configuration -Dozz_build_fbx=$env_build_fbx -Dozz_build_data=$env_build_data -Dozz_build_howtos=$env_build_howtos -Dozz_build_samples=$env_build_samples -Dozz_build_postfix=$env_build_postfix -Dozz_build_tools=$env_build_tools -Dozz_build_simd_ref=$env_build_simd_ref -Dozz_build_tests=$env_build_tests $env_src_root -# Build -- cmake --build ./ --config $env_cmake_configuration --use-stderr -- $env_cmake_generator_specific -# Test -- ctest --build-config $env_cmake_configuration --output-on-failure -- $env_ctest_generator_specific - -after_success: -- echo after_success----------------------------------------------------------- - -after_failure: -- echo after_failure----------------------------------------------------------- - -before_deploy: -#- echo before_deploy----------------------------------------------------------- - -deploy: -#- echo deploy------------------------------------------------------------------ - -after_deploy: -#- echo after_deploy------------------------------------------------------------ - -after_script: -#- echo after_script------------------------------------------------------------ diff --git a/3rdparty/ozz-animation/AUTHORS.md b/3rdparty/ozz-animation/AUTHORS.md index cc59ee5..f294ccf 100644 --- a/3rdparty/ozz-animation/AUTHORS.md +++ b/3rdparty/ozz-animation/AUTHORS.md @@ -14,3 +14,4 @@ The following authors have all licensed their contributions to ozz-animation und - Kota Iguchi - Mikołaj Siedlarek - Paul Gruenbacher +- Christophe Meyer diff --git a/3rdparty/ozz-animation/CHANGES.md b/3rdparty/ozz-animation/CHANGES.md index fcf2243..0e3fff3 100644 --- a/3rdparty/ozz-animation/CHANGES.md +++ b/3rdparty/ozz-animation/CHANGES.md @@ -1,3 +1,17 @@ +Release version 0.14.1 +---------------------- + +* Samples + - Allows reusing sample framework outside of sample directory. + - #154 Exposes swap interval + +* Library + - #153 Fixes deprecated implicit copy warning. + - #141 Removes non-ASCII characters in source codes. + +* Build pipeline + - Exposes ozz cmake configuration variables to PARENT_SCOPE, so it can be used/changed by an external project. + Release version 0.14.0 ---------------------- diff --git a/3rdparty/ozz-animation/CMakeLists.txt b/3rdparty/ozz-animation/CMakeLists.txt index 292cad1..07d5bbd 100644 --- a/3rdparty/ozz-animation/CMakeLists.txt +++ b/3rdparty/ozz-animation/CMakeLists.txt @@ -3,10 +3,13 @@ cmake_minimum_required (VERSION 3.3) # Defines the project's name project(ozz) +# Check if project is top level or a sub project +get_directory_property(is_sub_project PARENT_DIRECTORY) + # Current version set(OZZ_VERSION_MAJOR 0) set(OZZ_VERSION_MINOR 14) -set(OZZ_VERSION_PATCH 0) +set(OZZ_VERSION_PATCH 1) set(OZZ_VERSION ${OZZ_VERSION_MAJOR}.${OZZ_VERSION_MINOR}.${OZZ_VERSION_PATCH}) # Add project build options @@ -27,6 +30,9 @@ if(WIN32 AND BUILD_SHARED_LIBS AND NOT ozz_build_msvc_rt_dll) message("Forcing ozz_build_msvc_rt_dll to ON as ozz is being built as dll (BUILD_SHARED_LIBS is ON).") set(ozz_build_msvc_rt_dll ON) endif() +if(is_sub_project) + set(ozz_build_msvc_rt_dll ${ozz_build_msvc_rt_dll} PARENT_SCOPE) +endif() # Include ozz cmake parameters and scripts include(CheckCXXCompilerFlag) @@ -63,12 +69,18 @@ else() # Disables fbx if tools are disabled set(ozz_build_fbx OFF) endif() +if(is_sub_project) + set(ozz_build_fbx ${ozz_build_fbx} PARENT_SCOPE) +endif() # gltf if(ozz_build_tools AND ozz_build_gltf) else() set(ozz_build_gltf OFF) endif() +if(is_sub_project) + set(ozz_build_gltf ${ozz_build_gltf} PARENT_SCOPE) +endif() # Enables unit tests. if(ozz_build_tests) @@ -115,7 +127,6 @@ if(ozz_build_tests AND NOT EMSCRIPTEN) add_subdirectory(test) endif() - install(FILES ${PROJECT_SOURCE_DIR}/CHANGES.md ${PROJECT_SOURCE_DIR}/LICENSE.md diff --git a/3rdparty/ozz-animation/appveyor.yml b/3rdparty/ozz-animation/appveyor.yml deleted file mode 100644 index 76efffa..0000000 --- a/3rdparty/ozz-animation/appveyor.yml +++ /dev/null @@ -1,88 +0,0 @@ -version: '{build}' -image: Visual Studio 2019 -environment: - matrix: - # Comilers matrix - - env_cmake_generator: "Visual Studio 14 2015" - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - env_cmake_configuration: Debug - env_fbxsdk_path: "http://www.autodesk.com/content/dam/autodesk/www/adn/fbx/20195/fbx20195_fbxsdk_vs2015_win.exe" - - env_cmake_generator: "Visual Studio 14 2015 Win64" - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - env_cmake_configuration: Debug - env_fbxsdk_path: "http://www.autodesk.com/content/dam/autodesk/www/adn/fbx/20195/fbx20195_fbxsdk_vs2015_win.exe" - - env_cmake_generator: "Visual Studio 15 2017" - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - env_cmake_configuration: Debug - env_fbxsdk_path: "http://www.autodesk.com/content/dam/autodesk/www/adn/fbx/20195/fbx20195_fbxsdk_vs2017_win.exe" - - env_cmake_generator: "Visual Studio 15 2017 Win64" - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - env_cmake_configuration: Debug - env_fbxsdk_path: "http://www.autodesk.com/content/dam/autodesk/www/adn/fbx/20195/fbx20195_fbxsdk_vs2017_win.exe" - - env_cmake_generator: "Visual Studio 15 2017" - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - env_cmake_configuration: Release - env_fbxsdk_path: "http://www.autodesk.com/content/dam/autodesk/www/adn/fbx/20195/fbx20195_fbxsdk_vs2017_win.exe" - - env_cmake_generator: "Visual Studio 15 2017 Win64" - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - env_cmake_configuration: Release - env_fbxsdk_path: "http://www.autodesk.com/content/dam/autodesk/www/adn/fbx/20195/fbx20195_fbxsdk_vs2017_win.exe" - - env_cmake_generator: "Visual Studio 16 2019" - env_cmake_configuration: Debug - env_fbxsdk_path: "http://www.autodesk.com/content/dam/autodesk/www/adn/fbx/20195/fbx20195_fbxsdk_vs2017_win.exe" - - env_cmake_generator: "Visual Studio 16 2019" - env_cmake_configuration: Release - env_fbxsdk_path: "http://www.autodesk.com/content/dam/autodesk/www/adn/fbx/20195/fbx20195_fbxsdk_vs2017_win.exe" - - env_cmake_generator: "Visual Studio 16 2019" - env_cmake_configuration: Release - env_fbxsdk_path: "http://www.autodesk.com/content/dam/autodesk/www/adn/fbx/20195/fbx20195_fbxsdk_vs2017_win.exe" - env_build_msvc_rt_dll: "1" - - env_cmake_generator: "Visual Studio 16 2019" - env_cmake_configuration: Release - env_fbxsdk_path: "http://www.autodesk.com/content/dam/autodesk/www/adn/fbx/20195/fbx20195_fbxsdk_vs2017_win.exe" - env_build_msvc_rt_dll: "0" - - # No fbx sdk - - env_cmake_generator: "Visual Studio 16 2019" - env_cmake_configuration: Release - - # Use ozz as a sub project (should set msvc rt dll for ozz as this is the default in cmake) - - env_cmake_generator: "Visual Studio 16 2019" - env_cmake_configuration: Release - env_src_root: "../test/sub/" - env_fbxsdk_path: "http://www.autodesk.com/content/dam/autodesk/www/adn/fbx/20195/fbx20195_fbxsdk_vs2017_win.exe" - - env_cmake_generator: "Visual Studio 16 2019" - env_cmake_configuration: Debug - env_src_root: "../test/sub/" - env_fbxsdk_path: "http://www.autodesk.com/content/dam/autodesk/www/adn/fbx/20195/fbx20195_fbxsdk_vs2017_win.exe" - -install: -# Setup Fbx sdk -- ps: >- - if ($Env:env_fbxsdk_path) { - md fbx - cd fbx - Start-FileDownload $Env:env_fbxsdk_path "fbxsdk.exe" - Start-Process -FilePath "fbxsdk.exe" /S -Wait - cd .. - } -# Fixes up env_src_root if it isn't defined -- ps: >- - if (!$Env:env_src_root) { - $Env:env_src_root=".." - } -# Fixes up env_build_msvc_rt_dll if it isn't defined -- ps: >- - if (!$Env:env_build_msvc_rt_dll) { - $Env:env_build_msvc_rt_dll="1" - } - -build_script: -- cmake --version -- mkdir build -- cd build -- cmake -G "%env_cmake_generator%" -Dozz_build_tests=1 -Dozz_build_data=1 -Dozz_build_msvc_rt_dll=%env_build_msvc_rt_dll% -DCMAKE_BUILD_TYPE=%env_cmake_configuration% %env_src_root% -- cmake --build ./ --config %env_cmake_configuration% - -test_script: -- ctest --build-config %env_cmake_configuration% diff --git a/3rdparty/ozz-animation/extern/gtest/fused-src/gtest/gtest.h b/3rdparty/ozz-animation/extern/gtest/fused-src/gtest/gtest.h index 3e3e986..e5da69e 100644 --- a/3rdparty/ozz-animation/extern/gtest/fused-src/gtest/gtest.h +++ b/3rdparty/ozz-animation/extern/gtest/fused-src/gtest/gtest.h @@ -7917,7 +7917,7 @@ namespace edit_distance { // Returns the optimal edits to go from 'left' to 'right'. // All edits cost the same, with replace having lower priority than // add/remove. -// Simple implementation of the Wagner–Fischer algorithm. +// Simple implementation of the Wagner-Fischer algorithm. // See http://en.wikipedia.org/wiki/Wagner-Fischer_algorithm enum EditType { kMatch, kAdd, kRemove, kReplace }; GTEST_API_ std::vector CalculateOptimalEdits( diff --git a/3rdparty/ozz-animation/include/ozz/base/span.h b/3rdparty/ozz-animation/include/ozz/base/span.h index 24a545b..e5722ed 100644 --- a/3rdparty/ozz-animation/include/ozz/base/span.h +++ b/3rdparty/ozz-animation/include/ozz/base/span.h @@ -61,6 +61,9 @@ struct span { // elements. span(_Ty* _begin, size_t _size) : data_(_begin), size_(_size) {} + // Copy constructor. + span(const span& _other) = default; + // Copy operator. void operator=(const span& _other) { data_ = _other.data_; diff --git a/3rdparty/ozz-animation/samples/demo.zip b/3rdparty/ozz-animation/samples/demo.zip deleted file mode 100644 index c5a37bd..0000000 Binary files a/3rdparty/ozz-animation/samples/demo.zip and /dev/null differ diff --git a/3rdparty/ozz-animation/samples/framework/CMakeLists.txt b/3rdparty/ozz-animation/samples/framework/CMakeLists.txt index 4b8e2c0..cf0f361 100644 --- a/3rdparty/ozz-animation/samples/framework/CMakeLists.txt +++ b/3rdparty/ozz-animation/samples/framework/CMakeLists.txt @@ -36,6 +36,10 @@ target_link_libraries(sample_framework ozz_geometry ozz_animation_offline ozz_options) + +target_include_directories(sample_framework PUBLIC + $ + $/samples>) if(TARGET BUILD_DATA_SAMPLE) add_dependencies(sample_framework BUILD_DATA_SAMPLE) diff --git a/3rdparty/ozz-animation/samples/framework/application.cc b/3rdparty/ozz-animation/samples/framework/application.cc index b7778cb..ede6c40 100644 --- a/3rdparty/ozz-animation/samples/framework/application.cc +++ b/3rdparty/ozz-animation/samples/framework/application.cc @@ -98,6 +98,8 @@ Application::Application() time_(0.f), last_idle_time_(0.), show_help_(false), + vertical_sync_(true), + swap_interval_(1), show_grid_(true), show_axes_(true), capture_video_(false), @@ -209,7 +211,7 @@ int Application::Run(int _argc, const char** _argv, const char* _version, #endif // EMSCRIPTEN // Setup the window and installs callbacks. - glfwSwapInterval(1); // Enables vertical sync by default. + glfwSwapInterval(vertical_sync_ ? swap_interval_ : 0); glfwSetWindowSizeCallback(&ResizeCbk); glfwSetWindowCloseCallback(&CloseCbk); @@ -611,10 +613,14 @@ bool Application::FrameworkGui() { GL(Disable(GL_MULTISAMPLE)); } } - // Vertical sync - static bool vertical_sync_ = true; // On by default. - if (im_gui->DoCheckBox("Vertical sync", &vertical_sync_, true)) { - glfwSwapInterval(vertical_sync_ ? 1 : 0); + // Vertical sync & swap interval + bool changed = im_gui->DoCheckBox("Vertical sync", &vertical_sync_); + char szLabel[64]; + std::sprintf(szLabel, "Swap interval: %d", swap_interval_); + changed |= + im_gui->DoSlider(szLabel, 1, 4, &swap_interval_, 1.f, vertical_sync_); + if (changed) { + glfwSwapInterval(vertical_sync_ ? swap_interval_ : 0); } im_gui->DoCheckBox("Show grid", &show_grid_, true); diff --git a/3rdparty/ozz-animation/samples/framework/application.h b/3rdparty/ozz-animation/samples/framework/application.h index fb004cd..c91ecfe 100644 --- a/3rdparty/ozz-animation/samples/framework/application.h +++ b/3rdparty/ozz-animation/samples/framework/application.h @@ -216,6 +216,9 @@ class Application { // Set to true to display help. bool show_help_; + bool vertical_sync_; // On by default. + int swap_interval_; + // Grid display settings. bool show_grid_; bool show_axes_; diff --git a/3rdparty/ozz-animation/samples/framework/utils.cc b/3rdparty/ozz-animation/samples/framework/utils.cc index af2c787..85fca39 100644 --- a/3rdparty/ozz-animation/samples/framework/utils.cc +++ b/3rdparty/ozz-animation/samples/framework/utils.cc @@ -32,6 +32,7 @@ #include "framework/imgui.h" #include "framework/mesh.h" +#include "ozz/animation/offline/raw_animation.h" #include "ozz/animation/offline/raw_skeleton.h" #include "ozz/animation/runtime/animation.h" #include "ozz/animation/runtime/local_to_model_job.h" @@ -326,6 +327,30 @@ bool LoadAnimation(const char* _filename, return true; } +bool LoadRawAnimation(const char* _filename, + ozz::animation::offline::RawAnimation* _animation) { + assert(_filename && _animation); + ozz::log::Out() << "Loading raw animation archive: " << _filename << "." + << std::endl; + ozz::io::File file(_filename, "rb"); + if (!file.opened()) { + ozz::log::Err() << "Failed to open raw animation file " << _filename << "." + << std::endl; + return false; + } + ozz::io::IArchive archive(&file); + if (!archive.TestTag()) { + ozz::log::Err() << "Failed to load raw animation instance from file " + << _filename << "." << std::endl; + return false; + } + + // Once the tag is validated, reading cannot fail. + archive >> *_animation; + + return true; +} + namespace { template bool LoadTrackImpl(const char* _filename, _Track* _track) { @@ -411,7 +436,7 @@ bool LoadMeshes(const char* _filename, } namespace { -// Moller–Trumbore intersection algorithm +// Moller-Trumbore intersection algorithm // https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm bool RayIntersectsTriangle(const ozz::math::Float3& _ray_origin, const ozz::math::Float3& _ray_direction, diff --git a/3rdparty/ozz-animation/samples/framework/utils.h b/3rdparty/ozz-animation/samples/framework/utils.h index a665139..85a6c9a 100644 --- a/3rdparty/ozz-animation/samples/framework/utils.h +++ b/3rdparty/ozz-animation/samples/framework/utils.h @@ -167,6 +167,14 @@ bool LoadSkeleton(const char* _filename, ozz::animation::Skeleton* _skeleton); bool LoadAnimation(const char* _filename, ozz::animation::Animation* _animation); +// Loads a raw animation from an ozz archive file named _filename. +// This function will fail and return false if the file cannot be opened or if +// it is not a valid ozz animation archive. A valid animation archive can be +// produced with ozz tools (fbx2ozz) or using ozz animation serialization API. +// _filename and _animation must be non-nullptr. +bool LoadRawAnimation(const char* _filename, + ozz::animation::offline::RawAnimation* _animation); + // Loads a float track from an ozz archive file named _filename. // This function will fail and return false if the file cannot be opened or if // it is not a valid ozz float track archive. A valid float track archive can be diff --git a/3rdparty/ozz-animation/src/animation/offline/decimate.h b/3rdparty/ozz-animation/src/animation/offline/decimate.h index 3994382..9f450bf 100644 --- a/3rdparty/ozz-animation/src/animation/offline/decimate.h +++ b/3rdparty/ozz-animation/src/animation/offline/decimate.h @@ -41,7 +41,7 @@ namespace ozz { namespace animation { namespace offline { -// Decimation algorithm based on Ramer–Douglas–Peucker. +// Decimation algorithm based on Ramer-Douglas-Peucker. // https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm // _Track must have std::vector interface. // Adapter must have the following interface: diff --git a/3rdparty/ozz-animation/src/animation/runtime/ik_two_bone_job.cc b/3rdparty/ozz-animation/src/animation/runtime/ik_two_bone_job.cc index f429c72..f7d999f 100644 --- a/3rdparty/ozz-animation/src/animation/runtime/ik_two_bone_job.cc +++ b/3rdparty/ozz-animation/src/animation/runtime/ik_two_bone_job.cc @@ -184,7 +184,7 @@ bool SoftenTarget(const IKTwoBoneJob& _job, const IKConstantSetup& _setup, // The maximum distance we can reach is the soften bone chain length: da // (stored in !x). The minimum distance we can reach is the absolute value of - // the difference of the 2 bone lengths, |d1−d2| (stored in z). x is 0 and z + // the difference of the 2 bone lengths, |d1-d2| (stored in z). x is 0 and z // is 1, yw are untested. return (comp_mask & 0x5) == 0x4; } diff --git a/3rdparty/ozz-animation/test/base/containers/intrusive_list_tests.cc b/3rdparty/ozz-animation/test/base/containers/intrusive_list_tests.cc index 6c47090..164c236 100644 --- a/3rdparty/ozz-animation/test/base/containers/intrusive_list_tests.cc +++ b/3rdparty/ozz-animation/test/base/containers/intrusive_list_tests.cc @@ -645,10 +645,9 @@ class is_to_be_removed { public: explicit is_to_be_removed(int _which) : which_(_which) {} bool operator()(typename _List::const_reference) { return which_-- == 0; } + void operator=(const is_to_be_removed&) = delete; private: - void operator=(const is_to_be_removed&); - int which_; }; diff --git a/3rdparty/sokol/CHANGELOG.md b/3rdparty/sokol/CHANGELOG.md index f40ad30..bfe7cce 100644 --- a/3rdparty/sokol/CHANGELOG.md +++ b/3rdparty/sokol/CHANGELOG.md @@ -1,5 +1,691 @@ ## Updates +- **20-Feb-2023**: sokol_gfx.h has a new set of functions to get a 'best-effort' + desc struct with the creation parameters of a specific resource object: + + ```c + sg_buffer_desc sg_query_buffer_desc(sg_buffer buf); + sg_image_desc sg_query_image_desc(sg_image img); + sg_shader_desc sg_query_shader_desc(sg_shader shd); + sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip); + sg_pass_desc sg_query_pass_desc(sg_pass pass); + ``` + + The returned structs will *not* be an exact copy of the desc struct that + was used for creation the resource object, instead: + + - references to external data (like buffer and image content or + shader sources) will be zeroed + - any attributes that have not been kept around internally after + creation will be zeroed (the ```sg_shader_desc``` struct is most + affected by this, the other structs are fairly complete). + + Calling the functions with an invalid or dangling resource handle + will return a completely zeroed struct (thus it may make sense + to first check the resource state via ```sg_query_*_state()```) + + Nevertheless, those functions may be useful to get a partially filled out + 'creation blueprint' for creating similar resources without the need + to keep and pass around the original desc structs. + + >MINOR BREAKING CHANGE: the struct members ```sg_image_info.width``` and + ```sg_image_info.height``` have been removed, this information is now + returned by ```sg_query_image_desc()```. + + PR: https://github.com/floooh/sokol/pull/796, fixes: https://github.com/floooh/sokol/issues/568 + +- **17-Feb-2023**: sokol_app.h on macOS now has a proper fix for the problem + that macOS doesn't send key-up events while the Cmd key is held down. + Previously this was handled through a workaround of immediately sending a + key-up event after its key-down event if the Cmd key is currently held down + to prevent a 'stuck key'. The proper fix is now to install an "event monitor" + callback (many thanks to GLFW for finding and implementing the solution). + Unfortunately there's no such solution for the Emscripten code path, which + also don't send a key-up event while Cmd is pressed on macOS (the workaround + there to send a key-up event right on key-down while Cmd is held down to + prevent a stuck key is still in place) For more details, see: + https://github.com/floooh/sokol/issues/794 + +- **15-Feb-2023**: A fix in the sokol_gfx.h GL backend: due to a bug in the + state cache, the GL backend could only bind a total of + SG_MAX_SHADERSTAGE_IMAGES (= 12) when it actually should be twice that amount + (12 per shader stage). Note however that the total amount of texture bindings + is still internally limited by the GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS + runtime variable (~~currently this is not exposed in sg_limits though~~). Many + thanks to @allcreater for PR https://github.com/floooh/sokol/pull/787. + PS: sg_limits now exposes GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS as + ```sg_limits.gl_max_combined_texture_image_units```, and the + value can also be inspected via the debug UI in sokol_gfx_imgui.h. + +- **13-Feb-2023**: The way logging works has been completely revamped in + the sokol headers. UWP support has been removed from sokol_audio.h + and sokol_app.h (this also means that the sokol headers no longer contain + any C++ code). + + **REQUIRED ACTION**: Since the sokol headers are now completely silent + without a logging callback (explanation below), it is highly recommended + to use the standard logging callback provided by the new header ```sokol_log.h```. + For instance for sokol_gfx.h it looks like this: + + ```c + #include "sokol_log.h" + //... + sg_setup(&(sg_desc){ + //... + .logger.func = slog_func, + }); + ``` + + All sokol samples have been updated to use sokol_log.h for logging. + + The former logging callback is now a combined + logging- and error-reporting callback, and more information is available + to the logging function: + - a 'tag string' which identifies the sokol headers, this string + is identical with the API prefix (e.g. "sg" for sokol_gfx.h, + "sapp" for sokol_app.h etc...) + - a numeric log level: 0=panic, 1=error, 2=warning, 3=info + - a numeric 'log item id' (think of it as error code, but since + not only errors are reported I called it a log item id) + - a human readable error message + - a source file line number where the log item was reported + - the file path of the sokol header + + Log level ```panic``` is special in that it terminates execution inside + the log function. When a sokol header issues a panic log message, it means + that the problem is so big that execution can not continue. By default, + the sokol headers and the standard log function in sokol_log.h call + ```abort()``` when a panic log message is issued. + + In debug mode (NDEBUG not defined, or SOKOL_DEBUG defined), a log message + (in this case from sokol_spine.h) will look like this: + + ``` + [sspine][error][id:12] /Users/floh/projects/sokol/util/sokol_spine.h:3472:0: + SKELETON_DESC_NO_ATLAS: no atlas object provided in sspine_skeleton_desc.atlas + ``` + The information can be 'parsed' like this: + - ```[sspine]```: it's a message from sokol_spine.h + - ```[error]```: it's an error + - ```[id:12]```: the numeric log item id (associated with ```SKELETON_DESC_NO_ATLAS``` below) + - source file path and line number in a compiler-specific format - in some IDEs and terminals + this is a clickable link + - the line below is the human readable log item id and message + + In release mode (NDEBUG is defined and SOKOL_DEBUG is not defined), log messages + are drastically reduced (the reason is to not bloat the executable with all the extra string data): + + ``` + [sspine][error][id:12][line:3472] + ``` + ...this reduced information still gives all the necessary information to identify the location and type of error. + + A custom logging function must adhere to a few rules: + + - must be re-entrant because it might be called from different threads + - must treat **all** provided string pointers as optional (can be null) + - don't store the string pointers, copy the string data instead + - must not return for log level panic + + A new header ```sokol_log.h``` has been added to provide a standard logging callback implementation + which provides logging output on all platforms to stderr and/or platform specific logging + facilities. ```sokol_log.h``` only uses fputs() and platform specific logging function instead + of fprintf() to preverse some executable size. + + **QUESTION**: Why are the sokol headers now silent, unless a logging callback is installed? + This is mainly because a standard logging function which does something meaningful on all + platforms (including Windows and Android) isn't trivial. E.g. printing to stderr is not + enough. It's better to move that stuff into a centralized place in a separate header, + but since the core sokol headers must not (statically) depend on other sokol headers + the only solution that made sense was to provide a standard logging function which must + be 'registered' as a callback. + +- **26-Jan-2023**: Work on SRGB support in sokol_gfx.h has started, but + this requires more effort to be really usable. For now, only a new + pixel format has been added: SG_PIXELFORMAT_SRGB8A8 (see https://github.com/floooh/sokol/pull/758, + many thanks to @allcreater). The sokol-gfx GL backend has a temporary + workaround to align behaviour with D3D11 and Metal: automatic SRGB conversion + is enabled for offscreen render passes, but disabled for the default + framebuffer. A proper fix will require separate work on sokol_app.h to + support an SRGB default framebuffer and communicate to sokol-gfx + whether the default framebuffer is SRGB enabled or not. + +- **24-Jan-2023**: sokol_gfx.h Metal: A minor inconsistency has been fixed in + the validation layer and an assert for the function ```sg_apply_uniforms()``` + which checks the size of the incoming data against the uniform block size. + The validation layer and Metal backend did a ```<=``` test while the D3D11 + and GL backends checked for an exact size match. Both the validation layer + and the Metal backend now also check for an exact match. Thanks to @nmr8acme + for noticing the issue and providing a PR! (https://github.com/floooh/sokol/pull/776) + +- **23-Jan-2023**: A couple more sokol_audio.h updates: + - an AAudio backend has been added for Android, and made the default. This + means you now need to link with ```aaudio``` instead of ```OpenSLES``` when + using sokol_audio.h on Android. The OpenSLES backend code still exists (for + now), but must be explicitly selected by compiling the sokol_audio.h + implementation with the define ```SAUDIO_ANDROID_SLES``` (e.g. there is + no runtime fallback from AAudio to OpenSLES). AAudio is fully supported + since Android 8.1. Many thanks to @oviano for the initial AAudio PR + (https://github.com/floooh/sokol/pull/484) + - in the WebAudio backend, WebAudio is now properly activated on the first + input action again on Chrome for Android (at some point activating WebAudio + via a ```touchstart``` event stopped working and had to be moved to the + ```touchend``` event, see https://github.com/floooh/sokol/issues/701) + - audio backend initialization on iOS and macOS is now a bit more fault-tolerant, + errors during initialization now properly set sokol_audio.h to 'silent mode' + instead of asserting (or in release mode ignoring the error) + - ...and some minor general code cleanup things in sokol_audio.h: backend-specific + functions now generally have a matching prefix (like ```_saudio_alsa_...()```) + for better searchability + +- **16-Jan-2023**: + - sokol_audio.h android: https://github.com/floooh/sokol/pull/747 has been merged + which adds a couple more error checks at OpenSLES startup. + - sokol_gfx.h: support for half-float vertex formats has been added via + PR https://github.com/floooh/sokol/pull/745 + - sokol_imgui.h: fixes for Dear ImGui 1.89 deprecations (via PR https://github.com/floooh/sokol/pull/761) + +- **15-Jan-2023**: two bugfixes in sokol_app.h and sokol_gfx.h: + - sokol_app.h x11: Mouse button events now always return valid mouse + coordinates, also when no mouse movement happened yet + (fixes https://github.com/floooh/sokol/issues/770) + - sokol_gfx.h gl: The GL context is now configured with + GL_UNPACK_ALIGNMENT = 1, this should bring texture creation and updating + behaviour in line with the other backends for tightly packed texture + data that doesn't have a row-pitch with a multiple of 4 + (fixes https://github.com/floooh/sokol/issues/767) + +- **14-Jan-2023**: sokol_app.h x11: a drag'n'drop related bugfix, the + XdndFinished reply event was sent with the wrong window handle which + confused some apps where the drag operation originated + (see https://github.com/floooh/sokol/pull/765#issuecomment-1382750611) + +- **16-Dec-2022**: In the sokol_gfx.h Metal backend: A fix for a Metal + validation layer error which I just discovered yesterday (seems to be new in + macOS 13). When the validation layer is active, and the application window + becomes fully obscured, the validation layer throws an error after a short + time (for details see: https://github.com/floooh/sokol/issues/762). + The reason appears to be that sokol_gfx.h creates a command buffer with + 'unretained references' (e.g. the command buffer doesn't manage the + lifetime of resources used by the commands stored in the buffer). This + seems to clash with MTKView's and/or CAMetalLayer's expectations. I fixed + this now by creating a second command buffer with 'retained references', + which only holds the ```presentDrawable``` command. That way, regular + draw commands don't have the refcounting overhead (because they're stored + in an unretained-references cmdbuffer), while the drawable surface is + still properly lifetime managed (because it's used in a separate command + buffer with retained references). + +- **15-Dec-2022**: A small but important update in sokol_imgui.h which fixes + touch input handling on mobile devices. Many thanks to github user @Xadiant + for the bug investigation and [PR](https://github.com/floooh/sokol/pull/760). + +- **25-Nov-2022**: Some code cleanup around resource creation and destruction in sokol_gfx.h: + - It's now safe to call the destroy, uninit and dealloc functions in any + resource state, in general, the functions will do the right thing without + assertions getting in the way (there are however new log warnings in some + cases though, such as attempting to call an ```sg_dealloc_*()``` function on + a resource object that's not in ALLOC state) + - A related **minor breaking change**: the ```sg_uninit_*()``` functions now return + void instead of bool, this is because ```sg_dealloc_*()``` no longer asserts + when called in the wrong resource state + - Related internal code cleanup in the backend-agnostic resource creation + and cleanup code, better or more consistent function names, etc... + - The validation layer can now be disabled in debug mode with a runtime + flag during setup: ```sg_desc.disable_validation```. This is mainly useful + for test code. + - Creating a pass object with invalid image objects now no longer asserts, + but instead results in a pass object in FAILED state. In debug mode, + the validation layer will still stop at this problem though (it's mostly + an 'undefined API behaviour' fix in release mode). + - Calling ```sg_shutdown()``` with existing resources in ALLOC state will + no longer print a log message about an 'active context mismatch'. + - A new header documentation blurb about the two-step resource creation + and destruction functions (search for RESOURCE CREATION AND DESTRUCTION IN DETAIL) + +- **16-Nov-2022**: Render layer support has been added to sokol_debugtext.h, + same general changes as in sokol_gl.h with two new functions: + sdtx_layer(layer_id) to select the layer to record text into, and + sdtx_draw_layer(layer_id) to draw the recorded text in that layer inside a + sokol-gfx render pass. The new sample [debugtext-layers-sapp](https://floooh.github.io/sokol-html5/debugtext-layers-sapp) demonstrates the feature together with + sokol-gl. + + +- **11-Nov-2022**: sokol_gl.h has 2 new public API functions which enable + layered rendering: sgl_layer(), sgl_draw_layer() (technically it's three + functions: there's also sgl_context_draw_layer(), but that's just a variant of + sgl_draw_layer()). This allows to 'interleave' sokol-gl rendering + with other render operations. The [spine-layers-sapp](https://floooh.github.io/sokol-html5/spine-layers-sapp.html) + sample has been updated to use multiple sokol-gl layers. + +- **09-Nov-2022**: sokol_gfx.h now allows to add 'commit listeners', these + are callback functions which are called from inside sg_commit(). This is + mainly useful for libraries which build on top of sokol-gfx to be notified + about the start/end point of a frame, which in turn may simplify the public + API, or the internal implementation, because the library no longer needs to + 'guess' when a new frame starts. + + For more details, search for 'COMMIT LISTENERS' in the sokol_gfx.h header. + + This also results in a minor breaking change in sokol_spine.h: The function + ```sspine_new_frame()``` has been removed and replaced with an internal commit + listener. + + Likewise, sokol_gl.h now uses a commit listener in the implementation, but + without changing the public API (the feature will be important for an upcoming + sokol-gl feature to support rendering layers, and for this a 'new-frame-function' + would have been needed). + +- **05-Nov-2022** A breaking change in sokol_fetch.h, and a minor change in + sokol_app.h which should only break for very few users: + - An ```sfetch_range_t``` ptr/size pair struct has been added to sokol_fetch.h, + and discrete ptr/size pairs have been replaced with sfetch_range_t + items. This affects the structs ```sfetch_request_t``` and ```sfetch_response_t```, + and the function ```sfetch_bind_buffer()```. + - The required changes in ```sfetch_response_t``` might be a bit non-obviois: To + access the fetched data, previous ```.buffer_ptr``` and ```.fetched_size``` + was used. The fetched data is now accessible through an ```sfetch_range_t data``` + item (```data.ptr``` and ```data.size```). The old ```.fetched_offset``` item + has been renamed to ```.data_offset``` to better conform with the new naming. + - The last two occurrences of discrete ptr/size pairs in sokol_app.h now have also + been replaced with ```sapp_range_t``` items, this only affects the structs + ```sapp_html5_fetch_request``` and ```sapp_html5_fetch_response```. + +- **03-Nov-2022** The language bindings generation has been updated for Zig 0.10.0, + and clang-14 (there was a minor change in the JSON ast-dump format). + Many thanks to github user @kcbanner for the Zig PR! + +- **02-Nov-2022** A new header sokol_spine.h (in the util dir), this is a + renderer and 'handle wrapper' around the spine-c runtime (Spine is a popular 2D + character anim system: http://esotericsoftware.com/). This turned out a much bigger + rabbit-hole than I initially expected, but the effort is justified by being a + experimentation testbed for a couple of things I want to add to other sokol + headers (for instance cleaned up handle pool code, a new logging- and error-reporting + system, render layers which will be useful for sokol_gl.h and sokol_debugtext.h). + +- **22-Oct-2022** All sokol headers now allow to override logging with a + callback function (installed in the setup call) instead of defining a SOKOL_LOG + macro. Overriding SOKOL_LOG still works as default fallback, but this is no + longer documented, consider this deprecated. Many thanks to github user + @Manuzor for the PR (see https://github.com/floooh/sokol/pull/721 for details) + +- **21-Oct-2022** RGB9E5 pixel format support in sokol_gfx.h and a GLES2 related + bugfix in the sokol_app.h Android backend: + - sokol_gfx.h now supports RGB9E5 textures (3*9 bit RGB + 5 bit shared exponent), + this works in all backends except GLES2 and WebGL1 (use ```sg_query_pixelformat()``` + to check for runtime support). Many thanks to github user @allcreater for the PR! + - a bugfix in the sokol_app.h Android backend: when forcing a GLES2 context via + sapp_desc.gl_force_gles2, the Android backend correctly created a GLES2 context, + but then didn't communicate this through the function ```sapp_gles2()``` (which + still returned false in this case). This caused the sokol_gfx.h GL backend to + use the GLES3 code path instead GLES2 (which surprisingly seemed to have worked + fine, at least for the sokol samples which force GLES2). + +- **19-Oct-2022** Some fixes in the embedded Javascript code blocks (via EM_JS) + in sokol_app.h, sokol_args.h, sokol_audio.h and sokol_fetch.h: + - the JS code has been 'modernized' (e.g. const and let instead of var, + ```() => { ... }``` instead of ```function () { ... }``` for callbacks) + - false positives in the Closure static analysis have been suppressed + via inline hints + +- **16-Oct-2022** The Odin bindings generator and the generated bindings have + been simplified (the Odin binding now don't have separate wrapper functions). + Requires the latest Odin release. Also note: On M1 Macs I'm currently seeing + what looks like an ABI problem (in functions which pass color values to the C + side as uint8_t, the colors come out wrong). This also happened with the + previous binding version, so it looks like a regression in Odin. Might be + related to this recent bugfix (which I haven't tested yet): + https://github.com/odin-lang/Odin/issues/2121 Many thanks to @thePHTest for the + PR! (https://github.com/floooh/sokol/pull/719) + +- **15-Oct-2022** + - fixes for Emscripten 3.1.24: the sokol headers now use the new + **EM_JS_DEPS()** macro to declare 'indirect dependencies on JS library functions'. + This is a (much more robust) follow-up fix to the Emscripten related fixes from 10-Sep-2022. + The new Emscripten SDK also displays a couple of Javascript "static analyzer" warnings + by the Closure compiler (used in release mode to optimize and minify the generated + JS code). I fixed a couple of those warnings, but some warnings persist (all of them + false positives). Not sure yet if these can be fixed or need to be suppressed, but + that's for another time. + - the webkitAudioContext() fallback in sokol_audio.h's Emscripten backend + has been removed (only AudioContext is supported now), the fallback also + triggered a Closure warning, so it probably never worked as intended anyway. + - I also had to undo an older workaround in sokol_app.h on iOS (https://github.com/floooh/sokol/issues/645) + because this is now triggering a Metal validation layer error (https://github.com/floooh/sokol/issues/726). + The original case is no longer reproducible, so undoing the old workaround seems to + be a quick fix. Eventually I want to get rid of MTKView though, and go down to + CAMetalLayer. + +- **08-Oct-2022** sokol_app.h Android backend: the ```sapp_touchpoint``` struct + now has a new item ```sapp_android_tooltype android_tooltype;```. This exposes the + result of the Android NDK function ```AMotionEvent_getToolType()```. + Many thanks to @Wertzui123 for the initial PR (https://github.com/floooh/sokol/pull/717). + +- **25-Sep-2022**: sokol_app.h on Linux now optionally supports EGL instead of + GLX for the window system glue code and can create a GLES2 or GLES3 context + instead of a 'desktop GL' context. + To get EGL+GLES2/GLES3, just define SOKOL_GLES2 or SOKOL_GLES3 to compile the + implementation. To get EGL+GL, define SOKOL_GLCORE33 *and* SOKOL_FORCE_EGL. + By default, defining just SOKOL_GLCORE33 uses GLX for the window system glue + (just as before). Many thanks to GH user @billzez for the PR! + +- **10-Sep-2022**: sokol_app.h and sokol_args.h has been fixed for Emscripten 3.21, those headers + used the Emscripten Javascript helper function ```ccall()``` which is now part of the + 'legacy runtime' and causes linker errors. Instead of ```ccall()``` sokol_app.h and sokol_args.h + now drop down to a lower level set of Emscripten JS helper functions (which hopefully won't + go away anytime soon). + +- **05-Aug-2022**: New officially supported and automatically updated language bindings for Odin: + https://github.com/floooh/sokol-odin (also see [gen_odin.py](https://github.com/floooh/sokol/blob/master/bindgen/gen_odin.py)) + +- **10-Jul-2022**: New features in sokol_app.h and sokol_imgui.h: + - In sokol_app.h it's now possible to set a mouse cursor type from a number of predefined + types via the new function ```sapp_set_mouse_cursor(sapp_mouse_cursor cursor)```. The + available cursor types are compatible with GLFW and Dear ImGui. Supported platforms + are: macOS, linux, win32, uwp and web. + - ```sapp_show_mouse(bool shown)``` now also works on the web platform. + - In sokol_app.h, the poorly defined 'user cursor' feature has been removed (```sapp_desc.user_cursor``` + and ```SAPP_EVENTTYPE_UPDATE_CURSOR```). This was a hack to allow changing the mouse cursor and + only worked on Win32 and macOS (with different behaviour). Since setting the cursor type + is now 'properly supported, this hack was removed. + - sokol_imgui.h will now set the cursor type via ```sapp_set_mouse_cursor()```. This can be + disabled with the new ```simgui_desc_t``` item ```disable_set_mouse_cursor```. + - sokol_imgui.h now automatically enables resizing windows from edges (not just the bottom-right corner), + this behaviour can be disabled with the new ```simgui_desc_t``` item ```disable_windows_resize_from_edges```. + - sokol_imgui.h can now optionally write to the alpha channel (useful if you want to render the UI + into a separate render target, which is later composed onto the default framebuffer). The feature + is enabled with the new ```simgui_desc_t``` item ```write_alpha_channel```. + + Many thanks to **@tomc1998** for the initial [Linux/X11 mouse cursor type PR](https://github.com/floooh/sokol/pull/678) and + **@luigi-rosso** for the [sokol_imgui.h alpha channel PR](https://github.com/floooh/sokol/pull/687)! + +- **03-Jul-2022**: A new sokol_gfx.h function ```bool sg_query_buffer_will_overflow(sg_buffer buf, size_t size)``` +which allows to check if a call to ```sg_append_buffer()``` would overflow the buffer. This +is an alternative to the ```sg_query_buffer_overflow()``` function which only reports +the overflow after the fact. Many thanks to @RandyGaul for the PR! + +- **29-Jun-2022**: In sokol_app.h with the D3D11 backend, if SOKOL_DEBUG is +defined, and the D3D11 device creation fails, there's now a fallback code +path which tries to create the device again without the D3D11_CREATE_DEVICE_DEBUG +flag. Turns out the D3D11 debug support may suddenly stop working (just happened +to me, indicated by the Win10 "Graphics Tool" feature being silently uninstalled +and failing to install when asked to do so). This fix at least allows sokol_app.h +applications compiled in debug mode to run, even if the D3D11 debug layer doesn't +work. + +- **29-May-2022**: The code generation scripts for the +[sokol-nim](https://github.com/floooh/sokol-nim) language bindings have been +revised and updated, many thanks to Gustav Olsson for the PR! (I'm planning to +spend a few more days integrating the bindings generation with Github Actions, +so that it's easier to publish new bindings after updates to the sokol headers). + +- **26-May-2022**: The GL backend in sokol_app.h now allows to override the GL + context version via two new items in the ```sapp_desc``` struct: + ```sapp_desc.gl_major_version``` and ```sapp_desc.gl_minor_version```. The + default GL context version remains at 3.2. Overriding the GL version might make + sense if you're not using sokol_app.h together with sokol_gfx.h, or otherwise + want to call GL functions directly. Note that this only works for the + 'desktop GL' backends (Windows, Linux and macOS), but not for the GLES backends + (Android, iOS, web). Furthermore, on macOS only the GL versions 3.2 and 4.1 + are available (plus the special config major=1 minor=0 creates an + NSOpenGLProfileVersionLegacy context). In general: use at your risk :) Many + thanks to Github user @pplux for the PR! + +- **15-May-2022**: The way internal memory allocation can be overridden with + your own functions has been changed from global macros to callbacks + provided in the API setup call. For instance in sokol_gfx.h: + + ```c + void* my_malloc(size_t size, void* userdata) { + (void)userdata; // unused + return malloc(size); + } + + void my_free(void* ptr, void* userdata) { + (void)userdata; // unused + free(ptr); + } + + //... + sg_setup(&(sg_desc){ + //... + .allocator = { + .alloc = my_malloc, + .free = my_free, + .user_data = ..., + } + }); + ``` + + sokol_gfx.h will now call ```my_malloc()``` and ```my_free()``` whenever it needs + to allocate or free memory (note however that allocations inside OS + functions or 3rd party libraries are not affected). + + If no override functions are provided, the standard library functions ```malloc()``` and ```free()``` + will be used, just as before. + + This change breaks source compatibility in the following headers: + + - **sokol_fontstash.h**: the function signature of ```sfons_create()``` has changed, + this now takes a pointer to a new ```sfons_desc_t``` struct instead of + individual parameters. + - **sokol_gfx_imgui.h** (NOT sokol_imgui.h!): likewise, the function signature of + ```sg_imgui_init()``` has changed, this now takes an additional parameter + which is a pointer to a new ```sg_imgui_desc_t``` struct. + + All affected headers also have a preprocessor check for the outdated + macros ```SOKOL_MALLOC```, ```SOKOL_CALLOC``` and ```SOKOL_FREE``` and throw + a compilation error if those macros are detected. + + (if configuration through macros is still desired this could be added back in + the future, but I figured that the new way is more flexible in most situations). + + The header sokol_memtrack.h and the sample [restart-sapp](https://floooh.github.io/sokol-html5/restart-sapp.html) have been updated accordingly. + + Also search for ```MEMORY ALLOCATION OVERRIDE``` in the header documentation block + for more details. + +- **14-May-2022**: added a helper function ```simgui_map_keycode()``` to + sokol_imgui.h to map sokol_app.h keycodes (```sapp_keycode```, + ```SAPP_KEYCODE_*```) to Dear ImGui keycodes (```ImGuiKey```, ```ImGuiKey_*```). + If you're using Dear ImGui function to check for key input, you'll need to + update the code like this: + + - Old: + ```cpp + ImGui::IsKeyPressed(SAPP_KEYCODE_A); + ``` + - New: + ```cpp + ImGui::IsKeyPressed(simgui_map_keycode(SAPP_KEYCODE_A)); + ``` + + This was basically 'fallout' from rewriting the input system in sokol_imgui.h + to the new evented IO system in Dear ImGui. + +- **08-Feb-2022**: sokol_imgui.h has been updated for Dear ImGui 1.87: + - sokol_imgui.h's input code has been rewritten to use the new evented IO + system and extended virtual key codes in Dear ImGui + - on non-Emscripten platforms, mouse buttons are no longer "cancelled" when + the mouse leaves the window (since the native desktop platforms + automatically capture the mouse when mouse buttons are pressed, but mouse + capture is not supported in the sokol_app.h Emscripten backend) + +- **28-Jan-2022**: some window size behaviour changes in sokol_app.h. + - Asking for a default-sized window (via sapp_desc.width/height = 0) now + behaves a bit differently on desktop platforms. Previously this set the + window size to 640x480, now a default window covers more screen area: + - on Windows CW_USEDEFAULT will be used for the size + - on macOS and Linux, the window size will be 4/5 of the + display size + - no behaviour changes on other platforms + - On Windows and Linux, the window is now centered (in a later update, + more control over the initial window position, and new functions for + positioning and sizing might be provided) + - On Windows, when toggling between windowed and fullscreen, the + window position and size will now be restored (on other platforms + this already happened automatically through the window system) + - On all desktop platforms if an application starts in fullscreen and + then is toggled back to windowed, the window will now be of the + expected size (provided in sapp_desc.width/height) + +- **20-Jan-2022**: + - sokol_audio.h: A compatibility fix in the sokol_audio.h WASAPI backend (Windows): On + some configs the IAudioClient::Initialize() call could fail because + of a mismatch between the requested number of channels and speaker config. + See [#614](https://github.com/floooh/sokol/issues/614) for details. + - sokol_app.h D3D11/DXGI: Fix an (uncritical) COM interface leak warning for IDXGIAdapter and + IDXGIFactory at shutdown, introduced with the recent disabling of Alt-Enter. + +- **18-Jan-2022**: + - sokol_app.h now has per-monitor DPI support on Windows and macOS: when + the application window is moved to a monitor with different DPI, the values + returned by sapp_dpi_scale(), sapp_width() and sapp_height() will update + accordingly (only if the application requested high-dpi rendering with + ```sapp_desc.high_dpi=true```, otherwise the dpi scale value remains + fixed at 1.0f). The application will receive an SAPP_EVENTTYPE_RESIZED event + if the default framebuffer size has changed because of a DPI change. + On Windows this feature requires Win10 version 1703 or later (aka the + 'Creators Update'), older Windows version simply behave as before. + Many thank to @tjachmann for the initial PR with the Windows implementation! + - sokol_app.h: DPI scale computation on macOS is now more robust using the + NSScreen.backingScaleFactor value + - sokol_app.h: the new frame timing code in sokol_app.h now detects if the display + refresh rate changes and adjusts itself accordingly (for instance if the + window is moved between displays with different refresh rate) + - sokol_app.h D3D11/DXGI: during window movement and resize, the frame is now + presented with DXGI_PRESENT_DO_NOT_WAIT, this fixes some window system + stuttering issues on Win10 configs with recent NVIDIA drivers. + - sokol_app.h D3D11/DXGI: the application will no longer appear to freeze for + 0.5 seconds when the title bar is grabbed with the mouse for movement, but + then not moving the mouse. + - sokol_app.h D3D11/DXGI: DXGI's automatic windowed/fullscreen switching via + Alt-Enter has been disabled, because this switched to 'real' fullscreen mode, + while sokol_app.h's fullscreen mode uses a borderless window. Use the + programmatic fullscreen/window switching via ```sapp_toggle_fullscreen()``` + instead. + - **BREAKING CHANGE** in sokol_imgui.h: because the applications' DPI scale + can now change at any time, the DPI scale value is now communicated to + sokol_imgui.h in the ```simgui_new_frame()``` function. This has been + changed to accept a pointer to a new ```simgui_frame_desc_t``` struct. + With C99, change the simgui_new_frame() call as follows (if also using + sokol_app.h): + ```c + simgui_new_frame(&(simgui_frame_desc_t){ + .width = sapp_width(), + .height = sapp_height(), + .delta_time = sapp_frame_duration(), + .dpi_scale = sapp_dpi_scale() + }); + ``` + On C++ this works: + ```c++ + simgui_new_frame({ sapp_width(), sapp_height(), sapp_frame_duration(), sapp_dpi_scale() }); + ``` + ...or in C++20: + ```c++ + simgui_new_frame({ + .width = sapp_width(), + .height = sapp_height(), + .delta_time = sapp_frame_duration(), + .dpi_scale = sapp_dpi_scale() + }); + ``` + - **KNOWN ISSUE**: the recent change in sokol-audio's WASAPI backend to directly consume + float samples doesn't appear to work on some configs (see [#614](https://github.com/floooh/sokol/issues/614)), + investigation is underway + +- **15-Jan-2022**: + - A bugfix in the GL backend for uniform arrays using the 'native' uniform block layout. + The bug was a regression in the recent 'uniform data handling' update. See + [PR #611](https://github.com/floooh/sokol/pull/611) for details, and this [new sample/test](https://github.com/floooh/sokol-samples/blob/master/glfw/uniformarrays-glfw.c). + Many thanks to @nmr8acme for the PR! + +- **08-Jan-2022**: some enhancements and cleanup to uniform data handling in sokol_gfx.h + and the sokol-shdc shader compiler: + - *IMPORTANT*: when updating sokol_gfx.h (and you're using the sokol-shdc shader compiler), + don't forget to update the sokol-shdc binaries too! + - The GLSL uniform types int, ivec2, ivec3 and + ivec4 can now be used in shader code, those are exposed to the GL + backends with the new ```sg_uniform_type``` items + ```SG_UNIFORM_TYPE_INT[2,3,4]```. + - A new enum ```sg_uniform_layout```, currently with the values SG_UNIFORMLAYOUT_NATIVE + and SG_UNIFORMLAYOUT_STD140. The enum is used in ```sg_shader_uniform_block_desc``` + as a 'packing rule hint', so that the GL backend can properly locate the offset + of uniform block members. The default (SG_UNIFORMLAYOUT_NATIVE) keeps the same + behaviour, so existing code shouldn't need to be changed. With the packing + rule SG_UNIFORMLAYOUT_STD140 the uniform block interior is expected to be + layed out according to the OpenGL std140 packing rule. + - Note that the SG_UNIFORMLAYOUT_STD140 only allows a subset of the actual std140 + packing rule: arrays are only allowed for the types vec4, int4 and mat4. + This is because the uniform data must still be compatible with + ```glUniform()``` calls in the GL backends (which have different + 'interior alignment' for arrays). + - The sokol-shdc compiler supports the new uniform types and will annotate the + code-generated sg_shader_desc structs with SG_UNIFORMLAYOUT_STD140, + and there are new errors to make sure that uniform blocks are compatible + with all sokol_gfx.h backends. + - Likewise, sokol_gfx.h has tighter validation for the ```sg_shader_uniform_block``` + desc struct, but only when the GL backend is used (in general, the interior + layout of uniform blocks is only relevant for GL backends, on all other backends + sokol_gfx.h just passes the uniform data as an opaque block to the shader) + For more details see: + - [new sections in the sokol_gfx.h documentation](https://github.com/floooh/sokol/blob/ba64add0b67cac16fc86fb6b64d1da5f67e80c0f/sokol_gfx.h#L343-L450) + - [documentation of ```sg_uniform_layout```](https://github.com/floooh/sokol/blob/ba64add0b67cac16fc86fb6b64d1da5f67e80c0f/sokol_gfx.h#L1322-L1355) + - [enhanced sokol-shdc documentation](https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md#glsl-uniform-blocks-and-c-structs) + - [a new sample 'uniformtypes-sapp'](https://floooh.github.io/sokol-html5/uniformtypes-sapp.html) + + PS: and an unrelated change: the frame latency on Win32+D3D11 has been slightly improved + via IDXGIDevice1::SetMaximumFrameLatency() + +- **27-Dec-2021**: sokol_app.h frame timing improvements: + - A new function ```double sapp_frame_duration(void)``` which returns the frame + duration in seconds, averaged over the last 256 frames to smooth out + jittering spikes. If available, this uses platform/backend specific + functions of the swapchain API: + - On Windows: DXGI's GetFrameStatistics().SyncQPCTime. + - On Emscripten: the timestamp provided by the RAF callback, this will + still be clamped and jittered on some browsers, but averaged over + a number of frames yields a pretty accurate approximation + of the actual frame duration. + - On Metal, ```MTLDrawable addPresentedHandler + presentedTime``` + doesn't appear to function correctly on macOS Monterey and/or M1 Macs, so + instead mach_absolute_time() is called at the start of the MTKView + frame callback. + - In all other situations, the same timing method is used as + in sokol_time.h. + - On macOS and iOS, sokol_app.h now queries the maximum display refresh rate + of the main display and uses this as base to compute the preferred frame + rate (by multiplying with ```sapp_desc.swap_interval```), previously the + preferred frame rate was hardwired to ```60 * swap_interval```. This means + that native macOS and iOS applications may now run at 120Hz instead of + 60Hz depending on the device (I realize that this isn't ideal, there + will probably be a different way to hint the preferred interval at + which the frame callback is called, which would also support disabling + vsync and probably also adaptive vsync). + +- **19-Dec-2021**: some sokol_audio.h changes: + - on Windows, sokol_audio.h no longer converts audio samples + from float to int16_t, but instead configures WASAPI to directly accept + float samples. Many thanks to github user iOrange for the PR! + - sokol_audio.h has a new public function ```saudio_suspended()``` which + returns true if the audio device/context is currently in suspended mode. + On all backends except WebAudio this always returns false. This allows + to show a visual hint to the user that audio is muted until the first + input event is received. + +- **18-Dec-2021**: the sokol_gfx.h ```sg_draw()``` function now uses the currently applied + pipeline object to decide if the GL or D3D11 backend's instanced drawing function + should be called instead of the ```num_instances``` argument. This fixes a + bug on some WebGL configs when instanced rendering is configured + but ```sg_draw()``` is called with an instance count of 1. + +- **18-Nov-2021**: sokol_gl.h has a new function to control the point size for + point list rendering: ```void sgl_point_size(float size)```. Note that on D3D11 + the point size is currently ignored (since D3D11 doesn't support a point size at + all, the feature will need to be emulated in sokol_gl.h when the D3D11 backend is active). + Also note that points cannot currently be textured, only colored. + - **08-Oct-2021**: texture compression support in sokol_gfx.h has been revisited: - tighter validation checks on texture creation: - content data validation now also happens in ```sg_make_image()``` (previously only in ```sg_update_image()```) @@ -67,7 +753,7 @@ ago (around iOS 12.x) MTKView started to ignore the contentScaleFactor property, which lead to sokol_app.h always setting up a HighDPI framebuffer even when sapp_desc.high_dpi wasn't set. The fix is to set - the MTKView's drawableSize explicitely now. + the MTKView's drawableSize explicitly now. - The iOS GL backend didn't support MSAA multisampling so far, this has been fixed now, but only one MSAA mode (4x) is available, which will be selected when sapp_desc.sample_count is greater than 1. @@ -121,7 +807,7 @@ uses the **AVAudioSession** class to activate and deactivate audio output as nee This fixes sokol_audio.h for iPhones (so far, sokol_audio.h accidentally only worked for iPads). Please see [this issue](https://github.com/floooh/sokol/issues/431) for details. A somewhat unfortunate side effect of this fix is that sokol_audio.h must now be compiled -as Objective-C when targetting iOS, also note that a new framework must be linked: ```AVFoundation```. +as Objective-C when targeting iOS, also note that a new framework must be linked: ```AVFoundation```. Many thanks to @oviano for providing the PR! - **14-Feb-2021**: The Dear ImGui rendering backend in [sokol_imgui.h](https://github.com/floooh/sokol/blob/master/util/sokol_imgui.h) has been rewritten to only do a single diff --git a/3rdparty/sokol/bindgen/.gitignore b/3rdparty/sokol/bindgen/.gitignore index 9dcbf6f..4528e59 100644 --- a/3rdparty/sokol/bindgen/.gitignore +++ b/3rdparty/sokol/bindgen/.gitignore @@ -4,3 +4,5 @@ __pycache__/ sokol-nim/ sokol-zig/ +sokol-odin/ +sokol-rust/ diff --git a/3rdparty/sokol/bindgen/gen_all.py b/3rdparty/sokol/bindgen/gen_all.py index dc409d2..51d7e55 100644 --- a/3rdparty/sokol/bindgen/gen_all.py +++ b/3rdparty/sokol/bindgen/gen_all.py @@ -1,8 +1,10 @@ -import os, gen_nim, gen_zig +import os, gen_nim, gen_zig, gen_odin, gen_rust tasks = [ + [ '../sokol_log.h', 'slog_', [] ], [ '../sokol_gfx.h', 'sg_', [] ], [ '../sokol_app.h', 'sapp_', [] ], + [ '../sokol_glue.h', 'sapp_sg', ['sg_'] ], [ '../sokol_time.h', 'stm_', [] ], [ '../sokol_audio.h', 'saudio_', [] ], [ '../util/sokol_gl.h', 'sgl_', ['sg_'] ], @@ -10,18 +12,26 @@ tasks = [ [ '../util/sokol_shape.h', 'sshape_', ['sg_'] ], ] +# Odin +gen_odin.prepare() +for task in tasks: + [c_header_path, main_prefix, dep_prefixes] = task + gen_odin.gen(c_header_path, main_prefix, dep_prefixes) + # Nim gen_nim.prepare() for task in tasks: - c_header_path = task[0] - main_prefix = task[1] - dep_prefixes = task[2] + [c_header_path, main_prefix, dep_prefixes] = task gen_nim.gen(c_header_path, main_prefix, dep_prefixes) # Zig gen_zig.prepare() for task in tasks: - c_header_path = task[0] - main_prefix = task[1] - dep_prefixes = task[2] + [c_header_path, main_prefix, dep_prefixes] = task gen_zig.gen(c_header_path, main_prefix, dep_prefixes) + +# Rust +gen_rust.prepare() +for task in tasks: + [c_header_path, main_prefix, dep_prefixes] = task + gen_rust.gen(c_header_path, main_prefix, dep_prefixes) diff --git a/3rdparty/sokol/bindgen/gen_ir.py b/3rdparty/sokol/bindgen/gen_ir.py index c1e2ab5..0089ae2 100644 --- a/3rdparty/sokol/bindgen/gen_ir.py +++ b/3rdparty/sokol/bindgen/gen_ir.py @@ -59,11 +59,11 @@ def parse_enum(decl): if 'inner' in item_decl: const_expr = item_decl['inner'][0] if const_expr['kind'] != 'ConstantExpr': - sys.exit(f"ERROR: Enum values must be a ConstantExpr ({decl['name']})") - if const_expr['valueCategory'] != 'rvalue': - sys.exit(f"ERROR: Enum value ConstantExpr must be 'rvalue' ({decl['name']})") + sys.exit(f"ERROR: Enum values must be a ConstantExpr ({item_decl['name']}), is '{const_expr['kind']}'") + if const_expr['valueCategory'] != 'rvalue' and const_expr['valueCategory'] != 'prvalue': + sys.exit(f"ERROR: Enum value ConstantExpr must be 'rvalue' or 'prvalue' ({item_decl['name']}), is '{const_expr['valueCategory']}'") if not ((len(const_expr['inner']) == 1) and (const_expr['inner'][0]['kind'] == 'IntegerLiteral')): - sys.exit(f"ERROR: Enum value ConstantExpr must have exactly one IntegerLiteral ({decl['name']})") + sys.exit(f"ERROR: Enum value ConstantExpr must have exactly one IntegerLiteral ({item_decl['name']})") item['value'] = const_expr['inner'][0]['value'] if needs_value and 'value' not in item: sys.exit(f"ERROR: anonymous enum items require an explicit value") @@ -79,7 +79,7 @@ def parse_func(decl): if 'inner' in decl: for param in decl['inner']: if param['kind'] != 'ParmVarDecl': - print(f"warning: ignoring func {decl['name']} (unsupported parameter type)") + print(f" >> warning: ignoring func {decl['name']} (unsupported parameter type)") return None outp_param = {} outp_param['name'] = param['name'] @@ -119,4 +119,6 @@ def gen(header_path, source_path, module, main_prefix, dep_prefixes): outp_decl['is_dep'] = is_dep outp_decl['dep_prefix'] = dep_prefix(decl, dep_prefixes) outp['decls'].append(outp_decl) + with open(f'{module}.json', 'w') as f: + f.write(json.dumps(outp, indent=2)); return outp diff --git a/3rdparty/sokol/bindgen/gen_nim.py b/3rdparty/sokol/bindgen/gen_nim.py index fb6ba45..06a3b91 100644 --- a/3rdparty/sokol/bindgen/gen_nim.py +++ b/3rdparty/sokol/bindgen/gen_nim.py @@ -1,16 +1,19 @@ #------------------------------------------------------------------------------- -# Read output of gen_json.py and generate Zig language bindings. +# Generate Nim bindings # # Nim coding style: -# - types and constants are PascalCase -# - functions, parameters, and fields are camelCase +# - type identifiers are PascalCase, everything else is camelCase +# - reference: https://nim-lang.org/docs/nep1.html #------------------------------------------------------------------------------- import gen_ir -import json, re, os, shutil +import gen_util as util +import os, shutil, sys module_names = { + 'slog_': 'log', 'sg_': 'gfx', 'sapp_': 'app', + 'sapp_sg': 'glue', 'stm_': 'time', 'saudio_': 'audio', 'sgl_': 'gl', @@ -19,8 +22,10 @@ module_names = { } c_source_paths = { + 'slog_': 'sokol-nim/src/sokol/c/sokol_log.c', 'sg_': 'sokol-nim/src/sokol/c/sokol_gfx.c', 'sapp_': 'sokol-nim/src/sokol/c/sokol_app.c', + 'sapp_sg': 'sokol-nim/src/sokol/c/sokol_glue.c', 'stm_': 'sokol-nim/src/sokol/c/sokol_time.c', 'saudio_': 'sokol-nim/src/sokol/c/sokol_audio.c', 'sgl_': 'sokol-nim/src/sokol/c/sokol_gl.c', @@ -28,20 +33,57 @@ c_source_paths = { 'sshape_': 'sokol-nim/src/sokol/c/sokol_shape.c', } -func_name_ignores = [ +c_callbacks = [ + 'slog_func', +] + +ignores = [ 'sdtx_printf', 'sdtx_vprintf', ] -func_name_overrides = { - 'sgl_error': 'sgl_get_error', # 'error' is reserved in Zig - 'sgl_deg': 'sgl_as_degrees', - 'sgl_rad': 'sgl_as_radians', -} - -struct_field_type_overrides = { +overrides = { + 'sgl_error': 'sgl_get_error', + 'sgl_deg': 'sgl_as_degrees', + 'sgl_rad': 'sgl_as_radians', 'sg_context_desc.color_format': 'int', 'sg_context_desc.depth_format': 'int', + 'SGL_NO_ERROR': 'SGL_ERROR_NO_ERROR', + 'SG_BUFFERTYPE_VERTEXBUFFER': 'SG_BUFFERTYPE_VERTEX_BUFFER', + 'SG_BUFFERTYPE_INDEXBUFFER': 'SG_BUFFERTYPE_INDEX_BUFFER', + 'SG_ACTION_DONTCARE': 'SG_ACTION_DONT_CARE', + 'ptr': 'addr', # range ptr + 'func': 'fn', + 'slog_func': 'fn', +} + +enumPrefixOverrides = { + # sokol_gfx.h + 'PIXELFORMAT': 'pixelFormat', + 'RESOURCESTATE': 'resourceState', + 'BUFFERTYPE': 'bufferType', + 'INDEXTYPE': 'indexType', + 'IMAGETYPE': 'imageType', + 'SAMPLERTYPE': 'samplerType', + 'CUBEFACE': 'cubeFace', + 'SHADERSTAGE': 'shaderStage', + 'PRIMITIVETYPE': 'primitiveType', + 'BORDERCOLOR': 'borderColor', + 'VERTEXFORMAT': 'vertexFormat', + 'VERTEXSTEP': 'vertexStep', + 'UNIFORMTYPE': 'uniformType', + 'UNIFORMLAYOUT': 'uniformLayout', + 'CULLMODE': 'cullMode', + 'FACEWINDING': 'faceWinding', + 'COMPAREFUNC': 'compareFunc', + 'STENCILOP': 'stencilOp', + 'BLENDFACTOR': 'blendFactor', + 'BLENDOP': 'blendOp', + 'COLORMASK': 'colorMask', + # sokol_app.h + 'EVENTTYPE': 'eventType', + 'KEYCODE': 'keyCode', + 'MOUSEBUTTON': 'mouseButton', } prim_types = { @@ -60,7 +102,7 @@ prim_types = { 'double': 'float64', 'uintptr_t': 'uint', 'intptr_t': 'int', - 'size_t': 'int', + 'size_t': 'int', # not a bug, Nim's sizeof() returns int } prim_defaults = { @@ -74,31 +116,62 @@ prim_defaults = { 'uint32_t': '0', 'int64_t': '0', 'uint64_t': '0', - 'float': '0.0', + 'float': '0.0f', 'double': '0.0', 'uintptr_t': '0', 'intptr_t': '0', 'size_t': '0' } +common_prim_types = """ +array +untyped typed void +bool byte char +int int8 int16 int32 int64 +uint uint8 uint16 uint32 uint64 +float float32 float64 +string +cchar cint csize_t +cfloat cdouble +cstring +pointer +""".split() + +keywords = """ +addr and as asm +bind block break +case cast concept const continue converter +defer discard distinct div do +elif else end enum except export +finally for from func +if import in include interface is isnot iterator +let +macro method mixin mod +nil not notin +object of or out +proc ptr +raise ref return +shl shr static +template try tuple type +using +var +when while +xor +yield +""".split() + common_prim_types + struct_types = [] enum_types = [] -enum_items = {} out_lines = '' def reset_globals(): global struct_types global enum_types - global enum_items global out_lines struct_types = [] enum_types = [] - enum_items = {} out_lines = '' -re_1d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]$") -re_2d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]\[\d*\]$") - def l(s): global out_lines out_lines += s + '\n' @@ -107,90 +180,66 @@ def as_nim_prim_type(s): return prim_types[s] # prefix_bla_blub(_t) => (dep.)BlaBlub -def as_nim_struct_type(s, prefix): +def as_nim_type_name(s, prefix): parts = s.lower().split('_') - outp = '' if s.startswith(prefix) else f'{parts[0]}.' + dep = parts[0] + '_' + outp = '' + if not s.startswith(prefix) and dep in module_names: + outp = module_names[dep] + '.' for part in parts[1:]: + # ignore '_t' type postfix if (part != 't'): outp += part.capitalize() return outp -# prefix_bla_blub(_t) => (dep.)BlaBlub -def as_nim_enum_type(s, prefix): - parts = s.lower().split('_') - outp = '' if s.startswith(prefix) else f'{parts[0]}.' - for part in parts[1:]: - if (part != 't'): - outp += part.capitalize() - return outp - -# prefix_bla_blub(_t) => (dep.)BlaBlub -def as_nim_const_type(s, prefix): - parts = s.lower().split('_') - outp = '' if s.startswith(prefix) else f'{parts[0]}.' - for part in parts[1:]: - if (part != 't'): - outp += part.capitalize() - return outp - -def check_struct_field_type_override(struct_name, field_name, orig_type): - s = f"{struct_name}.{field_name}" - if s in struct_field_type_overrides: - return struct_field_type_overrides[s] +def check_override(name, default=None): + if name in overrides: + return overrides[name] + elif default is None: + return name else: - return orig_type + return default -def check_func_name_ignore(func_name): - return func_name in func_name_ignores +def check_ignore(name): + return name in ignores -def check_func_name_override(func_name): - if func_name in func_name_overrides: - return func_name_overrides[func_name] +def is_power_of_two(val): + return val == 0 or val & (val - 1) == 0 + +def wrap_keywords(s): + if s in keywords: + return f'`{s}`' else: - return func_name - -def trim_prefix(s, prefix): - outp = s; - if outp.lower().startswith(prefix.lower()): - outp = outp[len(prefix):] - return outp - -# PREFIX_BLA_BLUB to bla_blub -def as_snake_case(s, prefix = ""): - return trim_prefix(s, prefix).lower() + return s # prefix_bla_blub => blaBlub -def as_camel_case(s, prefix = ""): - parts = trim_prefix(s, prefix).lower().split('_') +def as_camel_case(s, prefix, wrap=True): + outp = s.lower() + if outp.startswith(prefix): + outp = outp[len(prefix):] + parts = outp.lstrip('_').split('_') outp = parts[0] for part in parts[1:]: outp += part.capitalize() + if wrap: + outp = wrap_keywords(outp) return outp -# prefix_bla_blub => BlaBlub -def as_pascal_case(s, prefix): - parts = trim_prefix(s, prefix).lower().split('_') - outp = "" - for part in parts: +# PREFIX_ENUM_BLA_BLO => blaBlo +def as_enum_item_name(s, wrap=True): + outp = s.lstrip('_') + parts = outp.split('_')[1:] + if parts[0] in enumPrefixOverrides: + parts[0] = enumPrefixOverrides[parts[0]] + else: + parts[0] = parts[0].lower() + outp = parts[0] + for part in parts[1:]: outp += part.capitalize() + if wrap: + outp = wrap_keywords(outp) return outp -# PREFIX_ENUM_BLA => Bla, _PREFIX_ENUM_BLA => Bla -def as_enum_item_name(s): - outp = s - if outp.startswith('_'): - outp = outp[1:] - parts = outp.lower().split('_')[2:] - outp = "" - for part in parts: - outp += part.capitalize() - if outp[0].isdigit(): - outp = 'N' + outp - return outp - -def enum_default_item(enum_name): - return enum_items[enum_name][0] - def is_prim_type(s): return s in prim_types @@ -200,15 +249,6 @@ def is_struct_type(s): def is_enum_type(s): return s in enum_types -def is_string_ptr(s): - return s == "const char *" - -def is_const_void_ptr(s): - return s == "const void *" - -def is_void_ptr(s): - return s == "void *" - def is_const_prim_ptr(s): for prim_type in prim_types: if s == f"const {prim_type} *": @@ -227,266 +267,208 @@ def is_const_struct_ptr(s): return True return False -def is_func_ptr(s): - return '(*)' in s - -def is_1d_array_type(s): - return re_1d_array.match(s) - -def is_2d_array_type(s): - return re_2d_array.match(s) - def type_default_value(s): return prim_defaults[s] -def extract_array_type(s): - return s[:s.index('[')].strip() - -def extract_array_nums(s): - return s[s.index('['):].replace('[', ' ').replace(']', ' ').split() - -def extract_ptr_type(s): - tokens = s.split() - if tokens[0] == 'const': - return tokens[1] - else: - return tokens[0] - -def as_extern_c_arg_type(arg_type, prefix): - if arg_type == "void": - return "void" - elif is_prim_type(arg_type): - return as_nim_prim_type(arg_type) - elif is_struct_type(arg_type): - return as_nim_struct_type(arg_type, prefix) - elif is_enum_type(arg_type): - return as_nim_enum_type(arg_type, prefix) - elif is_void_ptr(arg_type): - return "pointer" - elif is_const_void_ptr(arg_type): - return "pointer" - elif is_string_ptr(arg_type): - return "cstring" - elif is_const_struct_ptr(arg_type): - return f"ptr {as_nim_struct_type(extract_ptr_type(arg_type), prefix)}" - elif is_prim_ptr(arg_type): - return f"[*c] {as_nim_prim_type(extract_ptr_type(arg_type))}" - elif is_const_prim_ptr(arg_type): - return f"ptr {as_nim_prim_type(extract_ptr_type(arg_type))}" - else: - return '??? (as_extern_c_arg_type)' - -def as_nim_arg_type(arg_prefix, arg_type, prefix): - # NOTE: if arg_prefix is None, the result is used as return value - pre = "" if arg_prefix is None else arg_prefix - if arg_type == "void": - if arg_prefix is None: - return "void" - else: - return "" - elif is_prim_type(arg_type): - return pre + as_nim_prim_type(arg_type) - elif is_struct_type(arg_type): - return pre + as_nim_struct_type(arg_type, prefix) - elif is_enum_type(arg_type): - return pre + as_nim_enum_type(arg_type, prefix) - elif is_void_ptr(arg_type): - return pre + "pointer" - elif is_const_void_ptr(arg_type): - return pre + "pointer" - elif is_string_ptr(arg_type): - return pre + "cstring" - elif is_const_struct_ptr(arg_type): - return pre + f"ptr {as_nim_struct_type(extract_ptr_type(arg_type), prefix)}" - elif is_prim_ptr(arg_type): - return pre + f"ptr {as_nim_prim_type(extract_ptr_type(arg_type))}" - elif is_const_prim_ptr(arg_type): - return pre + f"ptr {as_nim_prim_type(extract_ptr_type(arg_type))}" - else: - return arg_prefix + "??? (as_nim_arg_type)" - -# get C-style arguments of a function pointer as string -def funcptr_args_c(field_type, prefix): +def funcptr_args(field_type, prefix): tokens = field_type[field_type.index('(*)')+4:-1].split(',') s = "" n = 0 for token in tokens: n += 1 - arg_type = token.strip() + arg_ctype = token.strip() if s != "": s += ", " - c_arg = f"a{n}:" + as_extern_c_arg_type(arg_type, prefix) - if (c_arg == "void"): - return "" - else: - s += c_arg + arg_nimtype = as_nim_type(arg_ctype, prefix) + if arg_nimtype == "": + return "" # fun(void) + s += f"a{n}:{arg_nimtype}" if s == "a1:void": s = "" return s -# get C-style result of a function pointer as string -def funcptr_res_c(field_type): - res_type = field_type[:field_type.index('(*)')].strip() - if res_type == 'void': - return '' - elif is_const_void_ptr(res_type): - return ':pointer' - else: - return '???' +def funcptr_result(field_type, prefix): + ctype = field_type[:field_type.index('(*)')].strip() + return as_nim_type(ctype, prefix) -def funcdecl_args_c(decl, prefix): - s = "" - for param_decl in decl['params']: - if s != "": - s += ", " - arg_type = param_decl['type'] - s += as_extern_c_arg_type(arg_type, prefix) - return s - -def funcdecl_args_nim(decl, prefix): - s = "" - for param_decl in decl['params']: - if s != "": - s += ", " - arg_name = param_decl['name'] - arg_type = param_decl['type'] - s += f"{as_nim_arg_type(f'{arg_name}:', arg_type, prefix)}" - return s - -def funcdecl_res_c(decl, prefix): - decl_type = decl['type'] - res_type = decl_type[:decl_type.index('(')].strip() - return as_extern_c_arg_type(res_type, prefix) - -def funcdecl_res_nim(decl, prefix): - decl_type = decl['type'] - res_type = decl_type[:decl_type.index('(')].strip() - nim_res_type = as_nim_arg_type(None, res_type, prefix) - if nim_res_type == "": - nim_res_type = "void" - return nim_res_type - -def gen_struct(decl, prefix, use_raw_name=False): - struct_name = decl['name'] - nim_type = struct_name if use_raw_name else as_nim_struct_type(struct_name, prefix) - l(f"type {nim_type}* = object") - isPublic = True - for field in decl['fields']: - field_name = field['name'] - if field_name == "__pad": - # FIXME: these should be guarded by SOKOL_ZIG_BINDINGS, but aren't? - continue - isPublic = not field_name.startswith("_") - field_name = as_camel_case(field_name, "_") - if field_name == "ptr": - field_name = "source" - if field_name == "ref": - field_name = "`ref`" - if field_name == "type": - field_name = "`type`" - if isPublic: - field_name += "*" - field_type = field['type'] - field_type = check_struct_field_type_override(struct_name, field_name, field_type) - if is_prim_type(field_type): - l(f" {field_name}:{as_nim_prim_type(field_type)}") - elif is_struct_type(field_type): - l(f" {field_name}:{as_nim_struct_type(field_type, prefix)}") - elif is_enum_type(field_type): - l(f" {field_name}:{as_nim_enum_type(field_type, prefix)}") - elif is_string_ptr(field_type): - l(f" {field_name}:cstring") - elif is_const_void_ptr(field_type): - l(f" {field_name}:pointer") - elif is_void_ptr(field_type): - l(f" {field_name}:pointer") - elif is_const_prim_ptr(field_type): - l(f" {field_name}:ptr {as_nim_prim_type(extract_ptr_type(field_type))}") - elif is_func_ptr(field_type): - l(f" {field_name}:proc({funcptr_args_c(field_type, prefix)}){funcptr_res_c(field_type)} {{.cdecl.}}") - elif is_1d_array_type(field_type): - array_type = extract_array_type(field_type) - array_nums = extract_array_nums(field_type) - if is_prim_type(array_type) or is_struct_type(array_type): - if is_prim_type(array_type): - nim_type = as_nim_prim_type(array_type) - elif is_struct_type(array_type): - nim_type = as_nim_struct_type(array_type, prefix) - elif is_enum_type(array_type): - nim_type = as_nim_enum_type(array_type, prefix) - else: - nim_type = '??? (array type)' - t0 = f"array[{array_nums[0]}, {nim_type}]" - t0_slice = f"[]const {nim_type}" - t1 = f"[_]{nim_type}" - l(f" {field_name}:{t0}") - elif is_const_void_ptr(array_type): - l(f" {field_name}:array[{array_nums[0]}, pointer]") - else: - l(f"// FIXME: ??? array {field_name}:{field_type} => {array_type} [{array_nums[0]}]") - elif is_2d_array_type(field_type): - array_type = extract_array_type(field_type) - array_nums = extract_array_nums(field_type) - if is_prim_type(array_type): - nim_type = as_nim_prim_type(array_type) - def_val = type_default_value(array_type) - elif is_struct_type(array_type): - nim_type = as_nim_struct_type(array_type, prefix) - def_val = ".{ }" - else: - nim_type = "???" - def_val = "???" - t0 = f"array[{array_nums[0]}, array[{array_nums[1]}, {nim_type}]]" - l(f" {field_name}:{t0}") +def as_nim_type(ctype, prefix, struct_ptr_as_value=False): + if ctype == "void": + return "" + elif is_prim_type(ctype): + return as_nim_prim_type(ctype) + elif is_struct_type(ctype): + return as_nim_type_name(ctype, prefix) + elif is_enum_type(ctype): + return as_nim_type_name(ctype, prefix) + elif util.is_string_ptr(ctype): + return "cstring" + elif util.is_void_ptr(ctype) or util.is_const_void_ptr(ctype): + return "pointer" + elif is_const_struct_ptr(ctype): + nim_type = as_nim_type(util.extract_ptr_type(ctype), prefix) + if struct_ptr_as_value: + return f"{nim_type}" else: - l(f"// FIXME: {field_name}:{field_type};") + return f"ptr {nim_type}" + elif is_prim_ptr(ctype) or is_const_prim_ptr(ctype): + return f"ptr {as_nim_type(util.extract_ptr_type(ctype), prefix)}" + elif util.is_func_ptr(ctype): + args = funcptr_args(ctype, prefix) + res = funcptr_result(ctype, prefix) + if res != "": + res = ":" + res + return f"proc({args}){res} {{.cdecl.}}" + elif util.is_1d_array_type(ctype): + array_ctype = util.extract_array_type(ctype) + array_sizes = util.extract_array_sizes(ctype) + return f'array[{array_sizes[0]}, {as_nim_type(array_ctype, prefix)}]' + elif util.is_2d_array_type(ctype): + array_ctype = util.extract_array_type(ctype) + array_sizes = util.extract_array_sizes(ctype) + return f'array[{array_sizes[0]}, array[{array_sizes[1]}, {as_nim_type(array_ctype, prefix)}]]' + else: + sys.exit(f"ERROR as_nim_type: {ctype}") + +def as_nim_struct_name(struct_decl, prefix): + struct_name = check_override(struct_decl['name']) + nim_type = f'{as_nim_type_name(struct_name, prefix)}' + return nim_type + +def as_nim_field_name(field_decl, prefix, check_private=True): + field_name = as_camel_case(check_override(field_decl['name']), prefix) + if check_private: + is_private = field_decl['name'].startswith('_') + if not is_private: + field_name += "*" + return field_name + +def as_nim_field_type(struct_decl, field_decl, prefix): + return as_nim_type(check_override(f"{struct_decl['name']}.{field_decl['name']}", default=field_decl['type']), prefix) + +def gen_struct(decl, prefix): + l(f"type {as_nim_struct_name(decl, prefix)}* = object") + for field in decl['fields']: + l(f" {as_nim_field_name(field, prefix)}:{as_nim_field_type(decl, field, prefix)}") l("") def gen_consts(decl, prefix): l("const") for item in decl['items']: - l(f" {trim_prefix(item['name'], prefix)}* = {item['value']}") + item_name = check_override(item['name']) + l(f" {as_camel_case(item_name, prefix)}* = {item['value']}") l("") def gen_enum(decl, prefix): item_names_by_value = {} value = -1 - hasForceU32 = False - hasExplicitValues = False + has_explicit_values = False for item in decl['items']: - itemName = item['name'] - if itemName.endswith("_FORCE_U32"): - hasForceU32 = True - elif itemName.endswith("_NUM"): + item_name = check_override(item['name']) + if item_name.endswith("_NUM") or item_name.endswith("_FORCE_U32"): continue else: if 'value' in item: - hasExplicitValues = True + has_explicit_values = True value = int(item['value']) else: value += 1 - item_names_by_value[value] = as_enum_item_name(item['name']); - if hasForceU32: - l(f"type {as_nim_enum_type(decl['name'], prefix)}* {{.pure, size:4.}} = enum") - else: - l(f"type {as_nim_enum_type(decl['name'], prefix)}* {{.pure.}} = enum") - if hasExplicitValues: + item_names_by_value[value] = as_enum_item_name(item_name) + enum_name_nim = as_nim_type_name(decl['name'], prefix) + l('type') + l(f" {enum_name_nim}* {{.size:sizeof(int32).}} = enum") + if has_explicit_values: # Nim requires explicit enum values to be declared in ascending order for value in sorted(item_names_by_value): name = item_names_by_value[value] - l(f" {name} = {value},") + l(f" {name} = {value},") else: for name in item_names_by_value.values(): - l(f" {name},") + l(f" {name},") l("") +# returns C prototype compatible function args (with pointers) +def funcdecl_args_c(decl, prefix): + s = "" + func_name = decl['name'] + for param_decl in decl['params']: + if s != "": + s += ", " + arg_name = param_decl['name'] + arg_type = check_override(f'{func_name}.{arg_name}', default=param_decl['type']) + s += f"{as_camel_case(arg_name, prefix)}:{as_nim_type(arg_type, prefix)}" + return s + +# returns Nim function args (pass structs by value) +def funcdecl_args_nim(decl, prefix): + s = "" + func_name = decl['name'] + for param_decl in decl['params']: + if s != "": + s += ", " + arg_name = param_decl['name'] + arg_type = check_override(f'{func_name}.{arg_name}', default=param_decl['type']) + s += f"{as_camel_case(arg_name, prefix)}:{as_nim_type(arg_type, prefix, struct_ptr_as_value=True)}" + return s + +def funcdecl_result(decl, prefix): + func_name = decl['name'] + decl_type = decl['type'] + result_type = check_override(f'{func_name}.RESULT', default=decl_type[:decl_type.index('(')].strip()) + nim_res_type = as_nim_type(result_type, prefix) + if nim_res_type == "": + nim_res_type = "void" + return nim_res_type + def gen_func_nim(decl, prefix): c_func_name = decl['name'] - nim_func_name = as_camel_case(decl['name'], prefix) - nim_res_type = funcdecl_res_nim(decl, prefix) - l(f"proc {nim_func_name}*({funcdecl_args_nim(decl, prefix)}):{funcdecl_res_nim(decl, prefix)} {{.cdecl, importc:\"{decl['name']}\".}}") + nim_func_name = as_camel_case(check_override(c_func_name), prefix, wrap=False) + nim_res_type = funcdecl_result(decl, prefix) + if c_func_name in c_callbacks: + l(f"proc {nim_func_name}*({funcdecl_args_c(decl, prefix)}):{nim_res_type} {{.cdecl, importc:\"{c_func_name}\".}}") + else: + l(f"proc c_{nim_func_name}({funcdecl_args_c(decl, prefix)}):{nim_res_type} {{.cdecl, importc:\"{c_func_name}\".}}") + l(f"proc {wrap_keywords(nim_func_name)}*({funcdecl_args_nim(decl, prefix)}):{nim_res_type} =") + s = f" c_{nim_func_name}(" + for i, param_decl in enumerate(decl['params']): + if i > 0: + s += ", " + arg_name = param_decl['name'] + arg_type = param_decl['type'] + if is_const_struct_ptr(arg_type): + s += f"unsafeAddr({arg_name})" + else: + s += arg_name + s += ")" + l(s) l("") +def gen_array_converters(decl, prefix): + for field in decl['fields']: + if util.is_array_type(field['type']): + array_type = util.extract_array_type(field['type']) + array_sizes = util.extract_array_sizes(field['type']) + struct_name = as_nim_struct_name(decl, prefix) + field_name = as_nim_field_name(field, prefix, check_private=False) + array_base_type = as_nim_type(array_type, prefix) + if util.is_1d_array_type(field['type']): + n = array_sizes[0] + l(f'converter to{struct_name}{field_name}*[N:static[int]](items: array[N, {array_base_type}]): array[{n}, {array_base_type}] =') + l(f' static: assert(N < {n})') + l(f' for index,item in items.pairs: result[index]=item') + l('') + elif util.is_2d_array_type(field['type']): + x = array_sizes[1] + y = array_sizes[0] + l(f'converter to{struct_name}{field_name}*[Y:static[int], X:static[int]](items: array[Y, array[X, {array_base_type}]]): array[{y}, array[{x}, {array_base_type}]] =') + l(f' static: assert(X < {x})') + l(f' static: assert(Y < {y})') + l(f' for indexY,itemY in items.pairs:') + l(f' for indexX, itemX in itemY.pairs:') + l(f' result[indexY][indexX] = itemX') + l('') + else: + sys.exit('Unsupported converter array dimension (> 2)!') + def pre_parse(inp): global struct_types global enum_types @@ -497,15 +479,92 @@ def pre_parse(inp): elif kind == 'enum': enum_name = decl['name'] enum_types.append(enum_name) - enum_items[enum_name] = [] - for item in decl['items']: - enum_items[enum_name].append(as_enum_item_name(item['name'])) def gen_imports(inp, dep_prefixes): for dep_prefix in dep_prefixes: dep_module_name = module_names[dep_prefix] l(f'import {dep_module_name}') + l('') + +def gen_extra(inp): + if inp['prefix'] in ['sg_']: + # FIXME: remove when sokol-shdc has been integrated! + l('when defined gl:') + l(' const gl* = true') + l(' const d3d11* = false') + l(' const metal* = false') + l('elif defined windows:') + l(' const gl* = false') + l(' const d3d11* = true') + l(' const metal* = false') + l('elif defined macosx:') + l(' const gl* = false') + l(' const d3d11* = false') + l(' const metal* = true') + l('elif defined linux:') + l(' const gl* = true') + l(' const d3d11* = false') + l(' const metal* = false') + l('else:') + l(' error("unsupported platform")') l('') + if inp['prefix'] in ['sg_', 'sapp_']: + l('when defined windows:') + l(' when not defined vcc:') + l(' {.passl:"-lkernel32 -luser32 -lshell32 -lgdi32".}') + l(' when defined gl:') + l(' {.passc:"-DSOKOL_GLCORE33".}') + l(' else:') + l(' {.passc:"-DSOKOL_D3D11".}') + l(' when not defined vcc:') + l(' {.passl:"-ld3d11 -ldxgi".}') + l('elif defined macosx:') + l(' {.passc:"-x objective-c".}') + l(' {.passl:"-framework Cocoa -framework QuartzCore".}') + l(' when defined gl:') + l(' {.passc:"-DSOKOL_GLCORE33".}') + l(' {.passl:"-framework OpenGL".}') + l(' else:') + l(' {.passc:"-DSOKOL_METAL".}') + l(' {.passl:"-framework Metal -framework MetalKit".}') + l('elif defined linux:') + l(' {.passc:"-DSOKOL_GLCORE33".}') + l(' {.passl:"-lX11 -lXi -lXcursor -lGL -lm -ldl -lpthread".}') + l('else:') + l(' error("unsupported platform")') + l('') + if inp['prefix'] in ['saudio_']: + l('when defined windows:') + l(' when not defined vcc:') + l(' {.passl:"-lkernel32 -lole32".}') + l('elif defined macosx:') + l(' {.passl:"-framework AudioToolbox".}') + l('elif defined linux:') + l(' {.passl:"-lasound -lm -lpthread".}') + l('else:') + l(' error("unsupported platform")') + l('') + if inp['prefix'] in ['sg_']: + l('## Convert a 4-element tuple of numbers to a gfx.Color') + l('converter toColor*[R:SomeNumber,G:SomeNumber,B:SomeNumber,A:SomeNumber](rgba: tuple [r:R,g:G,b:B,a:A]):Color =') + l(' Color(r:rgba.r.float32, g:rgba.g.float32, b:rgba.b.float32, a:rgba.a.float32)') + l('') + l('## Convert a 3-element tuple of numbers to a gfx.Color') + l('converter toColor*[R:SomeNumber,G:SomeNumber,B:SomeNumber](rgba: tuple [r:R,g:G,b:B]):Color =') + l(' Color(r:rgba.r.float32, g:rgba.g.float32, b:rgba.b.float32, a:1.float32)') + l('') + # NOTE: this simplistic to_Range() converter has various issues, some of them dangerous: + # - doesn't work as expected for slice types + # - it's very easy to create a range that points to invalid memory + # (so far observed for stack-allocated structs <= 16 bytes) + #if inp['prefix'] in ['sg_', 'sdtx_', 'sshape_']: + # l('# helper function to convert "anything" into a Range') + # l('converter to_Range*[T](source: T): Range =') + # l(' Range(addr: source.unsafeAddr, size: source.sizeof.uint)') + # l('') + c_source_path = '/'.join(c_source_paths[inp['prefix']].split('/')[3:]) + l('{.passc:"-DSOKOL_NIM_IMPL".}') + l(f'{{.compile:"{c_source_path}".}}') def gen_module(inp, dep_prefixes): l('## machine generated, do not edit') @@ -518,22 +577,27 @@ def gen_module(inp, dep_prefixes): kind = decl['kind'] if kind == 'consts': gen_consts(decl, prefix) - elif kind == 'enum': - gen_enum(decl, prefix) - elif kind == 'struct': - gen_struct(decl, prefix) - elif kind == 'func': - if not check_func_name_ignore(decl['name']): + elif not check_ignore(decl['name']): + if kind == 'struct': + gen_struct(decl, prefix) + gen_array_converters(decl, prefix) + elif kind == 'enum': + gen_enum(decl, prefix) + elif kind == 'func': gen_func_nim(decl, prefix) + gen_extra(inp) def prepare(): - print('Generating nim bindings:') + print('=== Generating Nim bindings:') if not os.path.isdir('sokol-nim/src/sokol'): os.makedirs('sokol-nim/src/sokol') if not os.path.isdir('sokol-nim/src/sokol/c'): os.makedirs('sokol-nim/src/sokol/c') def gen(c_header_path, c_prefix, dep_c_prefixes): + if not c_prefix in module_names: + print(f' >> warning: skipping generation for {c_prefix} prefix...') + return global out_lines module_name = module_names[c_prefix] c_source_path = c_source_paths[c_prefix] @@ -543,20 +607,5 @@ def gen(c_header_path, c_prefix, dep_c_prefixes): ir = gen_ir.gen(c_header_path, c_source_path, module_name, c_prefix, dep_c_prefixes) gen_module(ir, dep_c_prefixes) output_path = f"sokol-nim/src/sokol/{ir['module']}.nim" - - ## some changes for readability - out_lines = out_lines.replace("PixelformatInfo", "PixelFormatInfo") - out_lines = out_lines.replace(" Dontcare,", " DontCare,") - out_lines = out_lines.replace(" Vertexbuffer,", " VertexBuffer,") - out_lines = out_lines.replace(" Indexbuffer,", " IndexBuffer,") - out_lines = out_lines.replace(" N2d,", " Plane,") - out_lines = out_lines.replace(" N3d,", " Volume,") - out_lines = out_lines.replace(" Vs,", " Vertex,") - out_lines = out_lines.replace(" Fs,", " Fragment,") - - ## include extensions in generated code - l("# Nim-specific API extensions") - l(f"include nim/{ir['module']}") - with open(output_path, 'w', newline='\n') as f_outp: f_outp.write(out_lines) diff --git a/3rdparty/sokol/bindgen/gen_zig.py b/3rdparty/sokol/bindgen/gen_zig.py index 00d1f6b..a8768ca 100644 --- a/3rdparty/sokol/bindgen/gen_zig.py +++ b/3rdparty/sokol/bindgen/gen_zig.py @@ -1,5 +1,5 @@ #------------------------------------------------------------------------------- -# Read output of gen_json.py and generate Zig language bindings. +# Generate Zig bindings. # # Zig coding style: # - types are PascalCase @@ -7,9 +7,12 @@ # - otherwise snake_case #------------------------------------------------------------------------------- import gen_ir -import json, re, os, shutil +import os, shutil, sys + +import gen_util as util module_names = { + 'slog_': 'log', 'sg_': 'gfx', 'sapp_': 'app', 'stm_': 'time', @@ -20,6 +23,7 @@ module_names = { } c_source_paths = { + 'slog_': 'sokol-zig/src/sokol/c/sokol_log.c', 'sg_': 'sokol-zig/src/sokol/c/sokol_gfx.c', 'sapp_': 'sokol-zig/src/sokol/c/sokol_app.c', 'stm_': 'sokol-zig/src/sokol/c/sokol_time.c', @@ -29,21 +33,23 @@ c_source_paths = { 'sshape_': 'sokol-zig/src/sokol/c/sokol_shape.c', } -name_ignores = [ +ignores = [ 'sdtx_printf', 'sdtx_vprintf', 'sg_install_trace_hooks', 'sg_trace_hooks', ] -name_overrides = { - 'sgl_error': 'sgl_get_error', # 'error' is reserved in Zig - 'sgl_deg': 'sgl_as_degrees', - 'sgl_rad': 'sgl_as_radians' -} +# functions that need to be exposed as 'raw' C callbacks without a Zig wrapper function +c_callbacks = [ + 'slog_func' +] # NOTE: syntax for function results: "func_name.RESULT" -type_overrides = { +overrides = { + 'sgl_error': 'sgl_get_error', # 'error' is reserved in Zig + 'sgl_deg': 'sgl_as_degrees', + 'sgl_rad': 'sgl_as_radians', 'sg_context_desc.color_format': 'int', 'sg_context_desc.depth_format': 'int', 'sg_apply_uniforms.ub_index': 'uint32_t', @@ -53,6 +59,7 @@ type_overrides = { 'sshape_element_range_t.base_element': 'uint32_t', 'sshape_element_range_t.num_elements': 'uint32_t', 'sdtx_font.font_index': 'uint32_t', + 'SGL_NO_ERROR': 'SGL_ERROR_NO_ERROR', } prim_types = { @@ -92,6 +99,7 @@ prim_defaults = { 'size_t': '0' } + struct_types = [] enum_types = [] enum_items = {} @@ -107,9 +115,6 @@ def reset_globals(): enum_items = {} out_lines = '' -re_1d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]$") -re_2d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]\[\d*\]$") - def l(s): global out_lines out_lines += s + '\n' @@ -122,6 +127,7 @@ def as_zig_struct_type(s, prefix): parts = s.lower().split('_') outp = '' if s.startswith(prefix) else f'{parts[0]}.' for part in parts[1:]: + # ignore '_t' type postfix if (part != 't'): outp += part.capitalize() return outp @@ -135,42 +141,20 @@ def as_zig_enum_type(s, prefix): outp += part.capitalize() return outp -def check_type_override(func_or_struct_name, field_or_arg_name, orig_type): - s = f"{func_or_struct_name}.{field_or_arg_name}" - if s in type_overrides: - return type_overrides[s] - else: - return orig_type - -def check_name_override(name): - if name in name_overrides: - return name_overrides[name] - else: +def check_override(name, default=None): + if name in overrides: + return overrides[name] + elif default is None: return name + else: + return default -def check_name_ignore(name): - return name in name_ignores - -# PREFIX_BLA_BLUB to bla_blub -def as_snake_case(s, prefix): - outp = s.lower() - if outp.startswith(prefix): - outp = outp[len(prefix):] - return outp - -# prefix_bla_blub => blaBlub -def as_camel_case(s): - parts = s.lower().split('_')[1:] - outp = parts[0] - for part in parts[1:]: - outp += part.capitalize() - return outp +def check_ignore(name): + return name in ignores # PREFIX_ENUM_BLA => Bla, _PREFIX_ENUM_BLA => Bla def as_enum_item_name(s): - outp = s - if outp.startswith('_'): - outp = outp[1:] + outp = s.lstrip('_') parts = outp.split('_')[2:] outp = '_'.join(parts) if outp[0].isdigit(): @@ -189,15 +173,6 @@ def is_struct_type(s): def is_enum_type(s): return s in enum_types -def is_string_ptr(s): - return s == "const char *" - -def is_const_void_ptr(s): - return s == "const void *" - -def is_void_ptr(s): - return s == "void *" - def is_const_prim_ptr(s): for prim_type in prim_types: if s == f"const {prim_type} *": @@ -216,32 +191,10 @@ def is_const_struct_ptr(s): return True return False -def is_func_ptr(s): - return '(*)' in s - -def is_1d_array_type(s): - return re_1d_array.match(s) - -def is_2d_array_type(s): - return re_2d_array.match(s) - def type_default_value(s): return prim_defaults[s] -def extract_array_type(s): - return s[:s.index('[')].strip() - -def extract_array_nums(s): - return s[s.index('['):].replace('[', ' ').replace(']', ' ').split() - -def extract_ptr_type(s): - tokens = s.split() - if tokens[0] == 'const': - return tokens[1] - else: - return tokens[0] - -def as_extern_c_arg_type(arg_type, prefix): +def as_c_arg_type(arg_type, prefix): if arg_type == "void": return "void" elif is_prim_type(arg_type): @@ -250,20 +203,20 @@ def as_extern_c_arg_type(arg_type, prefix): return as_zig_struct_type(arg_type, prefix) elif is_enum_type(arg_type): return as_zig_enum_type(arg_type, prefix) - elif is_void_ptr(arg_type): - return "?*c_void" - elif is_const_void_ptr(arg_type): - return "?*const c_void" - elif is_string_ptr(arg_type): + elif util.is_void_ptr(arg_type): + return "?*anyopaque" + elif util.is_const_void_ptr(arg_type): + return "?*const anyopaque" + elif util.is_string_ptr(arg_type): return "[*c]const u8" elif is_const_struct_ptr(arg_type): - return f"[*c]const {as_zig_struct_type(extract_ptr_type(arg_type), prefix)}" + return f"[*c]const {as_zig_struct_type(util.extract_ptr_type(arg_type), prefix)}" elif is_prim_ptr(arg_type): - return f"[*c] {as_zig_prim_type(extract_ptr_type(arg_type))}" + return f"[*c] {as_zig_prim_type(util.extract_ptr_type(arg_type))}" elif is_const_prim_ptr(arg_type): - return f"[*c]const {as_zig_prim_type(extract_ptr_type(arg_type))}" + return f"[*c]const {as_zig_prim_type(util.extract_ptr_type(arg_type))}" else: - return '??? (as_extern_c_arg_type)' + sys.exit(f"Error as_c_arg_type(): {arg_type}") def as_zig_arg_type(arg_prefix, arg_type, prefix): # NOTE: if arg_prefix is None, the result is used as return value @@ -279,21 +232,24 @@ def as_zig_arg_type(arg_prefix, arg_type, prefix): return pre + as_zig_struct_type(arg_type, prefix) elif is_enum_type(arg_type): return pre + as_zig_enum_type(arg_type, prefix) - elif is_void_ptr(arg_type): - return pre + "?*c_void" - elif is_const_void_ptr(arg_type): - return pre + "?*const c_void" - elif is_string_ptr(arg_type): + elif util.is_void_ptr(arg_type): + return pre + "?*anyopaque" + elif util.is_const_void_ptr(arg_type): + return pre + "?*const anyopaque" + elif util.is_string_ptr(arg_type): return pre + "[:0]const u8" elif is_const_struct_ptr(arg_type): # not a bug, pass const structs by value - return pre + f"{as_zig_struct_type(extract_ptr_type(arg_type), prefix)}" + return pre + f"{as_zig_struct_type(util.extract_ptr_type(arg_type), prefix)}" elif is_prim_ptr(arg_type): - return pre + f"* {as_zig_prim_type(extract_ptr_type(arg_type))}" + return pre + f"* {as_zig_prim_type(util.extract_ptr_type(arg_type))}" elif is_const_prim_ptr(arg_type): - return pre + f"*const {as_zig_prim_type(extract_ptr_type(arg_type))}" + return pre + f"*const {as_zig_prim_type(util.extract_ptr_type(arg_type))}" else: - return arg_prefix + "??? (as_zig_arg_type)" + sys.exit(f"ERROR as_zig_arg_type(): {arg_type}") + +def is_zig_string(zig_type): + return zig_type == "[:0]const u8" # get C-style arguments of a function pointer as string def funcptr_args_c(field_type, prefix): @@ -303,22 +259,24 @@ def funcptr_args_c(field_type, prefix): arg_type = token.strip() if s != "": s += ", " - c_arg = as_extern_c_arg_type(arg_type, prefix) - if (c_arg == "void"): + c_arg = as_c_arg_type(arg_type, prefix) + if c_arg == "void": return "" else: s += c_arg return s # get C-style result of a function pointer as string -def funcptr_res_c(field_type): +def funcptr_result_c(field_type): res_type = field_type[:field_type.index('(*)')].strip() if res_type == 'void': return 'void' - elif is_const_void_ptr(res_type): - return '?*const c_void' + elif util.is_const_void_ptr(res_type): + return '?*const anyopaque' + elif util.is_void_ptr(res_type): + return '?*anyopaque' else: - return '???' + sys.exit(f"ERROR funcptr_result_c(): {field_type}") def funcdecl_args_c(decl, prefix): s = "" @@ -327,8 +285,8 @@ def funcdecl_args_c(decl, prefix): if s != "": s += ", " param_name = param_decl['name'] - param_type = check_type_override(func_name, param_name, param_decl['type']) - s += as_extern_c_arg_type(param_type, prefix) + param_type = check_override(f'{func_name}.{param_name}', default=param_decl['type']) + s += as_c_arg_type(param_type, prefix) return s def funcdecl_args_zig(decl, prefix): @@ -338,55 +296,49 @@ def funcdecl_args_zig(decl, prefix): if s != "": s += ", " param_name = param_decl['name'] - param_type = check_type_override(func_name, param_name, param_decl['type']) + param_type = check_override(f'{func_name}.{param_name}', default=param_decl['type']) s += f"{as_zig_arg_type(f'{param_name}: ', param_type, prefix)}" return s def funcdecl_result_c(decl, prefix): func_name = decl['name'] decl_type = decl['type'] - result_type = check_type_override(func_name, 'RESULT', decl_type[:decl_type.index('(')].strip()) - return as_extern_c_arg_type(result_type, prefix) + result_type = check_override(f'{func_name}.RESULT', default=decl_type[:decl_type.index('(')].strip()) + return as_c_arg_type(result_type, prefix) def funcdecl_result_zig(decl, prefix): func_name = decl['name'] decl_type = decl['type'] - result_type = check_type_override(func_name, 'RESULT', decl_type[:decl_type.index('(')].strip()) + result_type = check_override(f'{func_name}.RESULT', default=decl_type[:decl_type.index('(')].strip()) zig_res_type = as_zig_arg_type(None, result_type, prefix) - if zig_res_type == "": - zig_res_type = "void" return zig_res_type -def gen_struct(decl, prefix, callconvc_funcptrs = True, use_raw_name=False, use_extern=True): - struct_name = decl['name'] - zig_type = struct_name if use_raw_name else as_zig_struct_type(struct_name, prefix) - l(f"pub const {zig_type} = {'extern ' if use_extern else ''}struct {{") +def gen_struct(decl, prefix): + struct_name = check_override(decl['name']) + zig_type = as_zig_struct_type(struct_name, prefix) + l(f"pub const {zig_type} = extern struct {{") for field in decl['fields']: - field_name = field['name'] - field_type = field['type'] - field_type = check_type_override(struct_name, field_name, field_type) + field_name = check_override(field['name']) + field_type = check_override(f'{struct_name}.{field_name}', default=field['type']) if is_prim_type(field_type): l(f" {field_name}: {as_zig_prim_type(field_type)} = {type_default_value(field_type)},") elif is_struct_type(field_type): l(f" {field_name}: {as_zig_struct_type(field_type, prefix)} = .{{ }},") elif is_enum_type(field_type): l(f" {field_name}: {as_zig_enum_type(field_type, prefix)} = .{enum_default_item(field_type)},") - elif is_string_ptr(field_type): + elif util.is_string_ptr(field_type): l(f" {field_name}: [*c]const u8 = null,") - elif is_const_void_ptr(field_type): - l(f" {field_name}: ?*const c_void = null,") - elif is_void_ptr(field_type): - l(f" {field_name}: ?*c_void = null,") + elif util.is_const_void_ptr(field_type): + l(f" {field_name}: ?*const anyopaque = null,") + elif util.is_void_ptr(field_type): + l(f" {field_name}: ?*anyopaque = null,") elif is_const_prim_ptr(field_type): - l(f" {field_name}: ?[*]const {as_zig_prim_type(extract_ptr_type(field_type))} = null,") - elif is_func_ptr(field_type): - if callconvc_funcptrs: - l(f" {field_name}: ?fn({funcptr_args_c(field_type, prefix)}) callconv(.C) {funcptr_res_c(field_type)} = null,") - else: - l(f" {field_name}: ?fn({funcptr_args_c(field_type, prefix)}) {funcptr_res_c(field_type)} = null,") - elif is_1d_array_type(field_type): - array_type = extract_array_type(field_type) - array_nums = extract_array_nums(field_type) + l(f" {field_name}: ?[*]const {as_zig_prim_type(util.extract_ptr_type(field_type))} = null,") + elif util.is_func_ptr(field_type): + l(f" {field_name}: ?*const fn({funcptr_args_c(field_type, prefix)}) callconv(.C) {funcptr_result_c(field_type)} = null,") + elif util.is_1d_array_type(field_type): + array_type = util.extract_array_type(field_type) + array_sizes = util.extract_array_sizes(field_type) if is_prim_type(array_type) or is_struct_type(array_type): if is_prim_type(array_type): zig_type = as_zig_prim_type(array_type) @@ -398,19 +350,17 @@ def gen_struct(decl, prefix, callconvc_funcptrs = True, use_raw_name=False, use_ zig_type = as_zig_enum_type(array_type, prefix) def_val = '.{}' else: - zig_type = '??? (array type)' - def_val = '???' - t0 = f"[{array_nums[0]}]{zig_type}" - t0_slice = f"[]const {zig_type}" + sys.exit(f"ERROR gen_struct is_1d_array_type: {array_type}") + t0 = f"[{array_sizes[0]}]{zig_type}" t1 = f"[_]{zig_type}" - l(f" {field_name}: {t0} = {t1}{{{def_val}}} ** {array_nums[0]},") - elif is_const_void_ptr(array_type): - l(f" {field_name}: [{array_nums[0]}]?*const c_void = [_]?*const c_void {{ null }} ** {array_nums[0]},") + l(f" {field_name}: {t0} = {t1}{{{def_val}}} ** {array_sizes[0]},") + elif util.is_const_void_ptr(array_type): + l(f" {field_name}: [{array_sizes[0]}]?*const anyopaque = [_]?*const anyopaque {{ null }} ** {array_sizes[0]},") else: - l(f"// FIXME: ??? array {field_name}: {field_type} => {array_type} [{array_nums[0]}]") - elif is_2d_array_type(field_type): - array_type = extract_array_type(field_type) - array_nums = extract_array_nums(field_type) + sys.exit(f"ERROR gen_struct: array {field_name}: {field_type} => {array_type} [{array_sizes[0]}]") + elif util.is_2d_array_type(field_type): + array_type = util.extract_array_type(field_type) + array_sizes = util.extract_array_sizes(field_type) if is_prim_type(array_type): zig_type = as_zig_prim_type(array_type) def_val = type_default_value(array_type) @@ -418,22 +368,23 @@ def gen_struct(decl, prefix, callconvc_funcptrs = True, use_raw_name=False, use_ zig_type = as_zig_struct_type(array_type, prefix) def_val = ".{ }" else: - zig_type = "???" - def_val = "???" - t0 = f"[{array_nums[0]}][{array_nums[1]}]{zig_type}" - l(f" {field_name}: {t0} = [_][{array_nums[1]}]{zig_type}{{[_]{zig_type}{{ {def_val} }}**{array_nums[1]}}}**{array_nums[0]},") + sys.exit(f"ERROR gen_struct is_2d_array_type: {array_type}") + t0 = f"[{array_sizes[0]}][{array_sizes[1]}]{zig_type}" + l(f" {field_name}: {t0} = [_][{array_sizes[1]}]{zig_type}{{[_]{zig_type}{{ {def_val} }}**{array_sizes[1]}}}**{array_sizes[0]},") else: - l(f"// FIXME: {field_name}: {field_type};") + sys.exit(f"ERROR gen_struct: {field_name}: {field_type};") l("};") def gen_consts(decl, prefix): for item in decl['items']: - l(f"pub const {as_snake_case(item['name'], prefix)} = {item['value']};") + item_name = check_override(item['name']) + l(f"pub const {util.as_lower_snake_case(item_name, prefix)} = {item['value']};") def gen_enum(decl, prefix): - l(f"pub const {as_zig_enum_type(decl['name'], prefix)} = enum(i32) {{") + enum_name = check_override(decl['name']) + l(f"pub const {as_zig_enum_type(enum_name, prefix)} = enum(i32) {{") for item in decl['items']: - item_name = as_enum_item_name(item['name']) + item_name = as_enum_item_name(check_override(item['name'])) if item_name != "FORCE_U32": if 'value' in item: l(f" {item_name} = {item['value']},") @@ -446,27 +397,36 @@ def gen_func_c(decl, prefix): def gen_func_zig(decl, prefix): c_func_name = decl['name'] - zig_func_name = as_camel_case(check_name_override(decl['name'])) - zig_res_type = funcdecl_result_zig(decl, prefix) - l(f"pub fn {zig_func_name}({funcdecl_args_zig(decl, prefix)}) {zig_res_type} {{") - if zig_res_type != 'void': - s = f" return {c_func_name}(" + zig_func_name = util.as_lower_camel_case(check_override(decl['name']), prefix) + if c_func_name in c_callbacks: + # a simple forwarded C callback function + l(f"pub const {zig_func_name} = {c_func_name};") else: - s = f" {c_func_name}(" - for i, param_decl in enumerate(decl['params']): - if i > 0: - s += ", " - arg_name = param_decl['name'] - arg_type = param_decl['type'] - if is_const_struct_ptr(arg_type): - s += f"&{arg_name}" - elif is_string_ptr(arg_type): - s += f"@ptrCast([*c]const u8,{arg_name})" + zig_res_type = funcdecl_result_zig(decl, prefix) + l(f"pub fn {zig_func_name}({funcdecl_args_zig(decl, prefix)}) {zig_res_type} {{") + if is_zig_string(zig_res_type): + # special case: convert C string to Zig string slice + s = f" return cStrToZig({c_func_name}(" + elif zig_res_type != 'void': + s = f" return {c_func_name}(" else: - s += arg_name - s += ");" - l(s) - l("}") + s = f" {c_func_name}(" + for i, param_decl in enumerate(decl['params']): + if i > 0: + s += ", " + arg_name = param_decl['name'] + arg_type = param_decl['type'] + if is_const_struct_ptr(arg_type): + s += f"&{arg_name}" + elif util.is_string_ptr(arg_type): + s += f"@ptrCast([*c]const u8,{arg_name})" + else: + s += arg_name + if is_zig_string(zig_res_type): + s += ")" + s += ");" + l(s) + l("}") def pre_parse(inp): global struct_types @@ -483,12 +443,17 @@ def pre_parse(inp): enum_items[enum_name].append(as_enum_item_name(item['name'])) def gen_imports(inp, dep_prefixes): + l('const builtin = @import("builtin");') for dep_prefix in dep_prefixes: dep_module_name = module_names[dep_prefix] l(f'const {dep_prefix[:-1]} = @import("{dep_module_name}.zig");') - l('') + l('') def gen_helpers(inp): + l('// helper function to convert a C string to a Zig string slice') + l('fn cStrToZig(c_str: [*c]const u8) [:0]const u8 {') + l(' return @import("std").mem.span(c_str);') + l('}') if inp['prefix'] in ['sg_', 'sdtx_', 'sshape_']: l('// helper function to convert "anything" to a Range struct') l('pub fn asRange(val: anytype) Range {') @@ -502,7 +467,7 @@ def gen_helpers(inp): l(' }') l(' },') l(' .Struct, .Array => {') - l(' return .{ .ptr = &val, .size = @sizeOf(@TypeOf(val)) };') + l(' @compileError("Structs and arrays must be passed as pointers to asRange");') l(' },') l(' else => {') l(' @compileError("Cannot convert to range!");') @@ -547,7 +512,7 @@ def gen_module(inp, dep_prefixes): kind = decl['kind'] if kind == 'consts': gen_consts(decl, prefix) - elif not check_name_ignore(decl['name']): + elif not check_ignore(decl['name']): if kind == 'struct': gen_struct(decl, prefix) elif kind == 'enum': @@ -557,13 +522,16 @@ def gen_module(inp, dep_prefixes): gen_func_zig(decl, prefix) def prepare(): - print('Generating zig bindings:') + print('=== Generating Zig bindings:') if not os.path.isdir('sokol-zig/src/sokol'): os.makedirs('sokol-zig/src/sokol') if not os.path.isdir('sokol-zig/src/sokol/c'): os.makedirs('sokol-zig/src/sokol/c') def gen(c_header_path, c_prefix, dep_c_prefixes): + if not c_prefix in module_names: + print(f' >> warning: skipping generation for {c_prefix} prefix...') + return module_name = module_names[c_prefix] c_source_path = c_source_paths[c_prefix] print(f' {c_header_path} => {module_name}') diff --git a/src/main.cc b/src/main.cc index 3a069e4..a3f753e 100644 --- a/src/main.cc +++ b/src/main.cc @@ -9,6 +9,7 @@ #define SOKOL_IMPL #define SOKOL_GLCORE33 #include "sokol_gfx.h" +#include "sokol_log.h" #include "sokol_time.h" #define SOKOL_GL_IMPL #include "util/sokol_gl.h" @@ -28,6 +29,7 @@ const int MaxVertices = (1 << 16); const int MaxIndices = MaxVertices * 3; uint64_t last_time = 0; +const int cMSAASampleCount = 8; sg_pass_action pass_action; sg_pipeline pip; @@ -42,7 +44,6 @@ static void draw_imgui(ImDrawData*); #define HANDMADE_MATH_IMPLEMENTATION #define HANDMADE_MATH_NO_SSE #include "HandmadeMath.h" -#include "camera.h" // ozz-animation headers #include // fmodf @@ -77,7 +78,7 @@ static struct { struct { double frame; double anim_update_time; - float absolute; + double absolute; uint64_t laptime; float factor; float anim_ratio; @@ -107,6 +108,7 @@ struct Viewport { sg_pass_action pass_action = {}; sg_pass pass = {}; sg_pipeline pip = {}; + sgl_pipeline glpip = {}; sg_bindings bind = {}; sg_image color_image = {}; sg_image depth_image = {}; @@ -138,7 +140,7 @@ struct Viewport { .width = this->size[0], .height = this->size[1], .pixel_format = SG_PIXELFORMAT_RGBA8, - .sample_count = 4, + .sample_count = cMSAASampleCount, .min_filter = SG_FILTER_LINEAR, .mag_filter = SG_FILTER_LINEAR, .wrap_u = SG_WRAP_REPEAT, @@ -155,6 +157,14 @@ struct Viewport { offscreen_pass_desc.label = "offscreen-pass"; this->pass = sg_make_pass(&offscreen_pass_desc); + + sg_pipeline_desc gl_pipeline_desc = { + .depth = { + .compare = SG_COMPAREFUNC_LESS_EQUAL, + .write_enabled = true + }, + .cull_mode = SG_CULLMODE_BACK + }; } }; @@ -350,11 +360,8 @@ ApplicationConfig gApplicationConfig; static uint8_t skel_data_buffer[4 * 1024]; static uint8_t anim_data_buffer[32 * 1024]; -static void draw_grid(void); -static void draw_ui(void); -// static void skeleton_data_loaded(const sfetch_response_t* response); -// static void animation_data_loaded(const sfetch_response_t* response); -static void frame(void); +static void draw_grid(); +static void frame(); void handle_mouse(GLFWwindow* w, GuiInputState* io_input_state) { if (!glfwGetWindowAttrib(w, GLFW_FOCUSED)) { @@ -414,6 +421,19 @@ void save_application_config(const char* filename) { output_file.close(); } +void sokol_logger( + const char* tag, + uint32_t log_level, + uint32_t log_item_id, + const char* + message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_gl.h + const char* + filename_or_null, // source filename, may be nullptr in release mode + void* user_data) { + fprintf(stderr, "%s\n", message_or_null); +} + int main() { // window and GL context via GLFW and flextGL glfwInit(); @@ -423,7 +443,8 @@ int main() { glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE); glfwWindowHint(GLFW_SAMPLES, 16); - GLFWwindow* w = glfwCreateWindow(Width, Height, "ATP Editor", 0, 0); + GLFWwindow* w = + glfwCreateWindow(Width, Height, "ATP Editor", nullptr, nullptr); glfwMakeContextCurrent(w); glfwSwapInterval(1); @@ -476,16 +497,19 @@ int main() { // setup sokol_gfx and sokol_time stm_setup(); - sg_desc desc = {}; + sg_desc desc = {.logger = {.func = slog_func}}; sg_setup(&desc); assert(sg_isvalid()); // setup sokol-gl sgl_desc_t sgldesc = { - .sample_count = 4 - }; + .sample_count = cMSAASampleCount, + .logger = sokol_logger}; sgl_setup(&sgldesc); + // sgl_context_desc_t sgl_context_desc = {}; + // sgl_context ctx = sgl_make_context(&sgl_context_desc); + SkinnedMeshResource skinned_mesh_resource; skinned_mesh_resource.loadFromFile("../media/SampleSkinnedMesh.json"); @@ -602,6 +626,7 @@ int main() { pip_desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA; pip_desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; pip_desc.colors[0].write_mask = SG_COLORMASK_RGB; + pip_desc.label = "imgui-rendering"; pip = sg_make_pipeline(&pip_desc); // initial clear color @@ -625,7 +650,7 @@ int main() { if (state.ozz.animation != nullptr) { state.time.absolute = - fmodf(state.time.absolute, state.ozz.animation->duration()); + fmod(state.time.absolute, state.ozz.animation->duration()); } // Update window state @@ -643,7 +668,6 @@ int main() { glfwGetFramebufferSize(w, &cur_width, &cur_height); // this is standard ImGui demo code - ImGuiIO& io = ImGui::GetIO(); io.DisplaySize = ImVec2(float(cur_width), float(cur_height)); io.DeltaTime = (float)stm_sec(stm_laptime(&last_time)); ImGui::NewFrame(); @@ -664,7 +688,7 @@ int main() { &state.camera, offscreen_viewport.size[0], offscreen_viewport.size[1], - state.time.frame, + static_cast(state.time.frame), 0, 0, nullptr); @@ -703,7 +727,7 @@ int main() { &state.camera, cur_width, cur_height, - state.time.frame, + static_cast(state.time.frame), gGuiInputState.mousedX, gGuiInputState.mousedY, camera_accel); @@ -769,22 +793,22 @@ int main() { if (gApplicationConfig.viewport_widget.visible) { ImGui::SetNextWindowPos( ImVec2( - gApplicationConfig.viewport_widget.position[0], - gApplicationConfig.viewport_widget.position[1]), + static_cast( + gApplicationConfig.viewport_widget.position[0]), + static_cast( + gApplicationConfig.viewport_widget.position[1])), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize( ImVec2( - gApplicationConfig.viewport_widget.size[0], - gApplicationConfig.viewport_widget.size[1]), + static_cast(gApplicationConfig.viewport_widget.size[0]), + static_cast(gApplicationConfig.viewport_widget.size[1])), ImGuiCond_FirstUseEver); ImGui::Begin("Viewport", &gApplicationConfig.viewport_widget.visible); ImVec2 viewport_widget_size = ImGui::GetWindowSize(); - gApplicationConfig.viewport_widget.size[0] = - viewport_widget_size.x; - gApplicationConfig.viewport_widget.size[1] = - viewport_widget_size.y; + gApplicationConfig.viewport_widget.size[0] = viewport_widget_size.x; + gApplicationConfig.viewport_widget.size[1] = viewport_widget_size.y; ImGui::Text( "Viewport size: %d, %d", @@ -795,15 +819,15 @@ int main() { int* current_size = offscreen_viewport.size; - if (current_size[0] != content_size[0] || current_size[1] != content_size[1]) { + if (current_size[0] != content_size[0] + || current_size[1] != content_size[1] + || offscreen_viewport.pass.id == 0) { offscreen_viewport.Resize(content_size[0], content_size[1]); } ImGui::Image( (ImTextureID)(uintptr_t)offscreen_viewport.color_image.id, - ImVec2( - offscreen_viewport.size[0], - offscreen_viewport.size[1]), + ImVec2(offscreen_viewport.size[0], offscreen_viewport.size[1]), ImVec2(0.0f, 1.0f), ImVec2(1.0f, 0.0f)); @@ -927,13 +951,16 @@ int main() { if (state.ozz.animation != nullptr) { ImGui::SameLine(); - ImGui::SliderFloat( - "Time", - &state.time.absolute, - 0, - state.ozz.animation->duration(), - "%.3f", - 0); + float time_absolute_float = static_cast(state.time.absolute); + if (ImGui::SliderFloat( + "Time", + &time_absolute_float, + 0, + state.ozz.animation->duration(), + "%.3f", + 0)) { + state.time.absolute = time_absolute_float; + } } ImGui::End(); @@ -1012,8 +1039,9 @@ int main() { ImGui::ShowDemoWindow(); } - // Rendering of the offscreen scene + sgl_defaults(); sg_begin_pass(offscreen_viewport.pass, &offscreen_viewport.pass_action); + sgl_load_pipeline(offscreen_viewport.glpip); sgl_draw(); sg_end_pass(); @@ -1023,6 +1051,9 @@ int main() { draw_imgui(ImGui::GetDrawData()); sg_end_pass(); + + + sg_commit(); glfwSwapBuffers(w); glfwPollEvents();