Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
81 changes: 81 additions & 0 deletions .github/workflows/c-bindings.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: C bindings
on:
push:
branches: [master]
paths: ['c/**', 'src/**', 'crates/rapier*/**', 'Cargo.toml', '.github/workflows/c-bindings.yml']
pull_request:
branches: [master]
paths: ['c/**', 'src/**', 'crates/rapier*/**', 'Cargo.toml', '.github/workflows/c-bindings.yml']
workflow_dispatch:
jobs:
native:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
variant:
- {dimension: 2, parallel: "OFF"}
- {dimension: 3, parallel: "ON"}
precision: [32, 64]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Linux graphics build prerequisites
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libgl1-mesa-dev
- name: Configure
run: cmake -S c -B build/c -DRAPIER_DIMENSION=${{ matrix.variant.dimension }} -DRAPIER_PRECISION=${{ matrix.precision }} -DRAPIER_ENABLE_PARALLEL=${{ matrix.variant.parallel }} -DRAPIER_BUILD_TESTBED=ON -DRAPIER_PROFILE=debug -DRAPIER_CARGO_TARGET_DIR=${{ github.workspace }}/target
- name: Build
run: cmake --build build/c --config Release --parallel 2
- name: Test
run: ctest --test-dir build/c -C Release --output-on-failure
- name: Install
run: cmake --install build/c --config Release --prefix build/install
- name: Consume installed package
run: |
cmake -S c/tests/consumer -B build/consumer -DCMAKE_PREFIX_PATH=${{ github.workspace }}/build/install
cmake --build build/consumer --config Release
ctest --test-dir build/consumer -C Release --output-on-failure
abi-and-features:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
- run: cargo install cbindgen --version 0.29.4 --locked
- name: Header matches source
run: |
sh c/tools/generate-header.sh
git diff --exit-code -- c/include/rapier.h
- run: cargo fmt -p rapier-c-macros -p rapier3d-ffi -- --check
- run: cargo test -p rapier-c-macros -p rapier2d-ffi -p rapier3d-ffi -p rapier2d-f64-ffi -p rapier3d-f64-ffi
- run: cargo clippy -p rapier-c-macros -p rapier2d-ffi -p rapier3d-ffi -p rapier2d-f64-ffi -p rapier3d-f64-ffi --no-deps -- -D warnings
- name: Every ABI symbol and both linkage modes
run: python3 c/tools/test-native.py
- name: Optional feature combinations
run: cargo check -p rapier2d-ffi -p rapier3d-ffi -p rapier2d-f64-ffi -p rapier3d-f64-ffi --features parallel,fem,enhanced-determinism
- name: Eight-lane SIMD with parallel execution
run: |
cmake -S c -B build/simd8 -DRAPIER_BUILD_TESTBED=ON -DRAPIER_TESTBED_GRAPHICS=OFF -DRAPIER_PROFILE=debug -DRAPIER_SIMD_LANES=8 -DRAPIER_ENABLE_PARALLEL=ON -DRAPIER_CARGO_TARGET_DIR=${{ github.workspace }}/target
cmake --build build/simd8 --parallel 2
ctest --test-dir build/simd8 --output-on-failure

documentation:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install documentation tools
run: sudo apt-get update && sudo apt-get install -y doxygen
- name: Generate and validate all API variants
run: |
cmake -S c/doxygen -B build/c-docs
cmake --build build/c-docs --target rapier_docs --parallel 2
- name: Upload HTML reference
uses: actions/upload-artifact@v4
with:
name: rapier-c-api-docs
path: build/c-docs/html
9 changes: 8 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
[workspace]
members = [
"c/testbed/tools/logo-mesh",
"c/rapier-c-macros",
"c/rapier2d-ffi",
"c/rapier3d-ffi",
"c/rapier2d-f64-ffi",
"c/rapier3d-f64-ffi",
"crates/rapier2d",
"crates/rapier2d-f64",
"crates/rapier_testbed2d",
Expand All @@ -21,7 +27,8 @@ members = [
# pure-Rust crates. The Python-binding crates need a Python build env
# (pyo3 `extension-module`) and are exercised by the dedicated
# `.github/workflows/python-bindings.yml` workflow via explicit
# `-p rapier-py-*` invocations.
# `-p rapier-py-*` invocations. C ABI crates are tested separately by
# `.github/workflows/c-bindings.yml`, including native C/C++ consumers.
default-members = [
"crates/rapier2d",
"crates/rapier2d-f64",
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ also enable:
rapier3d = { version = "*", features = ["parallel"] }
```

## C and C++ bindings

See [`c/README.md`](c/README.md) for the C ABI, C++ ownership helpers, native build instructions,
and Unity/Unreal integration guidance. The bindings cover 2D/3D and f32/f64, including soft bodies.

## Python bindings

Python bindings are under development. They ship as four PyPI packages —
Expand Down
206 changes: 206 additions & 0 deletions c/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
cmake_minimum_required(VERSION 3.20)
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" RAPIER_C_VERSION)
string(STRIP "${RAPIER_C_VERSION}" RAPIER_C_VERSION)
if(NOT RAPIER_C_VERSION MATCHES "^([0-9]+[.][0-9]+[.][0-9]+)[+]c[.](0|[1-9][0-9]*)$")
message(FATAL_ERROR "VERSION must be <Rust crate version>+c.<revision>")
endif()
# CMake package version comparisons use the numeric Rust version only.
project(RapierC VERSION ${CMAKE_MATCH_1} LANGUAGES C CXX)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION")
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
set(RAPIER_DIMENSION "3" CACHE STRING "2 or 3")
set_property(CACHE RAPIER_DIMENSION PROPERTY STRINGS 2 3)
set(RAPIER_PRECISION "32" CACHE STRING "32 or 64")
set_property(CACHE RAPIER_PRECISION PROPERTY STRINGS 32 64)
option(RAPIER_SHARED "Link the shared library (otherwise static)" ON)
option(RAPIER_BUILD_DOCS "Enable the optional Doxygen API reference target" OFF)
if(RAPIER_BUILD_DOCS)
add_subdirectory(doxygen)
endif()
option(RAPIER_BUILD_TESTS "Build C/C++ integration tests" ON)
option(RAPIER_BUILD_EXAMPLES "Build the falling-ball example" ON)
option(RAPIER_BUILD_TESTBED "Build the C example testbed" OFF)
option(RAPIER_TESTBED_GRAPHICS "Include the raylib viewer (fetches graphics dependencies)" ON)
set(RAPIER_PROFILE "release" CACHE STRING "Cargo release or debug profile")
set_property(CACHE RAPIER_PROFILE PROPERTY STRINGS release debug)
set(RAPIER_FEATURES "" CACHE STRING "Additional Cargo features: enhanced-determinism,fem,profiler,robotics")
# Accept the earlier parallel/simd8 feature spelling as defaults, while the
# explicit options below determine the final effective feature set.
set(RAPIER_DEFAULT_PARALLEL "${RAPIER_BUILD_TESTBED}")
set(RAPIER_DEFAULT_SIMD_LANES "4")
if(RAPIER_FEATURES MATCHES "(^|,)parallel(,|$)")
set(RAPIER_DEFAULT_PARALLEL ON)
endif()
if(RAPIER_FEATURES MATCHES "(^|,)simd8(,|$)")
set(RAPIER_DEFAULT_SIMD_LANES "8")
endif()
option(RAPIER_ENABLE_PARALLEL "Enable Rapier parallel execution and thread-pool control" ${RAPIER_DEFAULT_PARALLEL})
set(RAPIER_SIMD_LANES "${RAPIER_DEFAULT_SIMD_LANES}" CACHE STRING "Solver SIMD width: 4 or 8 (8 requires f32)")
set_property(CACHE RAPIER_SIMD_LANES PROPERTY STRINGS 4 8)
if(NOT RAPIER_SIMD_LANES MATCHES "^(4|8)$")
message(FATAL_ERROR "RAPIER_SIMD_LANES must be 4 or 8; this Rapier branch has no scalar solver build")
endif()
if(RAPIER_SIMD_LANES STREQUAL "8" AND RAPIER_PRECISION STREQUAL "64")
message(FATAL_ERROR "8-lane SIMD requires RAPIER_PRECISION=32; f64 uses 4 lanes")
endif()
if(RAPIER_SIMD_LANES STREQUAL "8" AND RAPIER_FEATURES MATCHES "(^|,)enhanced-determinism(,|$)")
message(FATAL_ERROR "8-lane SIMD is incompatible with enhanced-determinism; use RAPIER_SIMD_LANES=4")
endif()
string(REPLACE "," ";" RAPIER_FEATURE_LIST "${RAPIER_FEATURES}")
list(REMOVE_ITEM RAPIER_FEATURE_LIST parallel simd8)
if(RAPIER_ENABLE_PARALLEL)
list(APPEND RAPIER_FEATURE_LIST parallel)
endif()
if(RAPIER_SIMD_LANES STREQUAL "8")
list(APPEND RAPIER_FEATURE_LIST simd8)
endif()
if(RAPIER_BUILD_TESTBED)
list(APPEND RAPIER_FEATURE_LIST profiler)
endif()
list(REMOVE_DUPLICATES RAPIER_FEATURE_LIST)
list(JOIN RAPIER_FEATURE_LIST "," RAPIER_EFFECTIVE_FEATURES)
message(STATUS "Rapier physics: ${RAPIER_PROFILE}; SIMD ${RAPIER_SIMD_LANES} lanes; parallel ${RAPIER_ENABLE_PARALLEL}")
set(RAPIER_TARGET "" CACHE STRING "Optional Rust target triple for cross compilation")
if(NOT RAPIER_DIMENSION MATCHES "^[23]$" OR NOT RAPIER_PRECISION MATCHES "^(32|64)$")
message(FATAL_ERROR "RAPIER_DIMENSION must be 2/3 and RAPIER_PRECISION must be 32/64")
endif()
if(NOT RAPIER_PROFILE MATCHES "^(release|debug)$")
message(FATAL_ERROR "RAPIER_PROFILE must be release/debug")
endif()
find_program(CARGO_EXECUTABLE cargo REQUIRED)
get_filename_component(RAPIER_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/.." ABSOLUTE)
set(RAPIER_CRATE "rapier${RAPIER_DIMENSION}d")
if(RAPIER_PRECISION STREQUAL "64")
string(APPEND RAPIER_CRATE "-f64")
endif()
string(APPEND RAPIER_CRATE "-ffi")
string(REPLACE "-" "_" RAPIER_LIBNAME "${RAPIER_CRATE}")
set(RAPIER_CARGO_TARGET_DIR "${CMAKE_CURRENT_BINARY_DIR}/cargo" CACHE PATH "Cargo target directory")
set(RAPIER_CARGO_ARGS build --manifest-path "${RAPIER_ROOT}/Cargo.toml" -p "${RAPIER_CRATE}" --target-dir "${RAPIER_CARGO_TARGET_DIR}")
set(RAPIER_OUTPUT "${RAPIER_CARGO_TARGET_DIR}")
if(RAPIER_TARGET)
list(APPEND RAPIER_CARGO_ARGS --target "${RAPIER_TARGET}")
string(APPEND RAPIER_OUTPUT "/${RAPIER_TARGET}")
endif()
if(RAPIER_PROFILE STREQUAL "release")
list(APPEND RAPIER_CARGO_ARGS --release)
endif()
if(RAPIER_EFFECTIVE_FEATURES)
list(APPEND RAPIER_CARGO_ARGS --features "${RAPIER_EFFECTIVE_FEATURES}")
endif()
string(APPEND RAPIER_OUTPUT "/${RAPIER_PROFILE}")
set(RAPIER_DEFINITIONS "RAPIER_DIM${RAPIER_DIMENSION};RAPIER_F${RAPIER_PRECISION}")
if(RAPIER_EFFECTIVE_FEATURES MATCHES "(^|,)fem(,|$)")
list(APPEND RAPIER_DEFINITIONS RAPIER_FEM)
endif()
if(RAPIER_EFFECTIVE_FEATURES MATCHES "(^|,)robotics(,|$)")
if(NOT RAPIER_DIMENSION STREQUAL "3" OR NOT RAPIER_PRECISION STREQUAL "32")
message(FATAL_ERROR "The native URDF/MJCF importers currently support only 3D f32")
endif()
list(APPEND RAPIER_DEFINITIONS RAPIER_ROBOTICS)
endif()
if(RAPIER_ENABLE_PARALLEL)
list(APPEND RAPIER_DEFINITIONS RAPIER_PARALLEL)
endif()
set(RAPIER_SYSTEM_LIBS "")
if(UNIX)
list(APPEND RAPIER_SYSTEM_LIBS m) # Inline functions in rapier_math.h.
endif()
if(RAPIER_SHARED)
set(RAPIER_KIND SHARED)
if(WIN32)
set(RAPIER_FILENAME "${RAPIER_LIBNAME}.dll")
set(RAPIER_IMPLIB "${RAPIER_LIBNAME}.dll.lib")
elseif(APPLE)
set(RAPIER_FILENAME "lib${RAPIER_LIBNAME}.dylib")
else()
set(RAPIER_FILENAME "lib${RAPIER_LIBNAME}.so")
endif()
else()
set(RAPIER_KIND STATIC)
list(APPEND RAPIER_DEFINITIONS RAPIER_STATIC)
if(WIN32)
set(RAPIER_FILENAME "${RAPIER_LIBNAME}.lib")
set(RAPIER_SYSTEM_LIBS ws2_32 userenv bcrypt ntdll advapi32)
else()
set(RAPIER_FILENAME "lib${RAPIER_LIBNAME}.a")
find_package(Threads REQUIRED)
set(RAPIER_SYSTEM_LIBS Threads::Threads "${CMAKE_DL_LIBS}" m)
if(APPLE)
list(APPEND RAPIER_SYSTEM_LIBS "-framework Security" "-framework CoreFoundation")
endif()
endif()
endif()
set(RAPIER_LIBRARY "${RAPIER_OUTPUT}/${RAPIER_FILENAME}")
set(RAPIER_BYPRODUCTS "${RAPIER_LIBRARY}")
if(RAPIER_IMPLIB)
list(APPEND RAPIER_BYPRODUCTS "${RAPIER_OUTPUT}/${RAPIER_IMPLIB}")
endif()
add_custom_target(rapier_cargo_build
COMMAND "${CARGO_EXECUTABLE}" ${RAPIER_CARGO_ARGS}
WORKING_DIRECTORY "${RAPIER_ROOT}"
BYPRODUCTS ${RAPIER_BYPRODUCTS}
VERBATIM USES_TERMINAL)
add_library(rapier_native ${RAPIER_KIND} IMPORTED GLOBAL)
add_library(Rapier::rapier ALIAS rapier_native)
set_target_properties(rapier_native PROPERTIES
IMPORTED_LOCATION "${RAPIER_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/include"
INTERFACE_COMPILE_DEFINITIONS "${RAPIER_DEFINITIONS}"
INTERFACE_LINK_LIBRARIES "${RAPIER_SYSTEM_LIBS}")
if(RAPIER_IMPLIB)
set_property(TARGET rapier_native PROPERTY IMPORTED_IMPLIB "${RAPIER_OUTPUT}/${RAPIER_IMPLIB}")
endif()
if(APPLE AND RAPIER_SHARED)
set_property(TARGET rapier_native PROPERTY IMPORTED_SONAME "@rpath/${RAPIER_FILENAME}")
endif()
add_dependencies(rapier_native rapier_cargo_build)
function(rapier_executable name source)
add_executable(${name} ${source})
target_link_libraries(${name} PRIVATE Rapier::rapier)
target_compile_features(${name} PRIVATE c_std_11 cxx_std_17)
if(MSVC)
target_compile_options(${name} PRIVATE /W4 /WX /UNDEBUG)
else()
target_compile_options(${name} PRIVATE -Wall -Wextra -Werror -UNDEBUG)
endif()
if(WIN32 AND RAPIER_SHARED)
add_custom_command(TARGET ${name} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${RAPIER_LIBRARY}" "$<TARGET_FILE_DIR:${name}>" VERBATIM)
endif()
endfunction()
if(RAPIER_BUILD_TESTS)
enable_testing()
rapier_executable(rapier_c_integration tests/integration.c)
target_compile_definitions(rapier_c_integration PRIVATE RAPIER_EXPECTED_VERSION="${RAPIER_C_VERSION}" RAPIER_EXPECTED_PROFILE="${RAPIER_PROFILE}" RAPIER_EXPECTED_SIMD_LANES=${RAPIER_SIMD_LANES})
rapier_executable(rapier_cpp_integration tests/cpp.cpp)
rapier_executable(rapier_pod_integration tests/pod.c)
rapier_executable(rapier_handle_integration tests/handles.c)
rapier_executable(rapier_initializer_integration tests/initializers.c)
add_test(NAME initializer_abi COMMAND rapier_initializer_integration)
rapier_executable(rapier_array_view_integration tests/array_views.c)
add_test(NAME array_view_abi COMMAND rapier_array_view_integration)
add_test(NAME handle_abi COMMAND rapier_handle_integration)
add_test(NAME pod_abi COMMAND rapier_pod_integration)
add_test(NAME c_abi COMMAND rapier_c_integration)
add_test(NAME cpp_abi COMMAND rapier_cpp_integration)
endif()
if(RAPIER_BUILD_EXAMPLES)
rapier_executable(rapier_falling_ball examples/falling_ball.c)
endif()
if(WIN32 AND RAPIER_SHARED)
set(RAPIER_INSTALL_LIBRARY_DIR "${CMAKE_INSTALL_BINDIR}")
install(FILES "${RAPIER_OUTPUT}/${RAPIER_IMPLIB}" DESTINATION "${CMAKE_INSTALL_LIBDIR}")
else()
set(RAPIER_INSTALL_LIBRARY_DIR "${CMAKE_INSTALL_LIBDIR}")
endif()
install(FILES "${RAPIER_LIBRARY}" DESTINATION "${RAPIER_INSTALL_LIBRARY_DIR}")
install(FILES include/rapier.h include/rapier.hpp include/rapier_math.h include/rapier_helpers.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
set(RAPIER_INSTALL_NAME "@rpath/${RAPIER_FILENAME}")
configure_package_config_file(cmake/RapierConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/RapierConfig.cmake" INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Rapier")
write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/RapierConfigVersion.cmake" VERSION ${PROJECT_VERSION} COMPATIBILITY ExactVersion)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/RapierConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/RapierConfigVersion.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Rapier")

if(RAPIER_BUILD_TESTBED)
add_subdirectory(testbed)
endif()
Loading
Loading