├── .editorconfig ├── .gitattirbutes ├── .github ├── dependabot.yaml └── workflows │ ├── build-debug.yaml │ ├── build-physx.yaml │ ├── build-release.yaml │ └── stale.yaml ├── .gitignore ├── .gitmodules ├── Directory.Build.props ├── Icon.png ├── LICENSE ├── MagicPhysX.sln ├── MagicPhysX.sln.DotSettings.user ├── README.md ├── opensource.snk ├── samples └── MagicPhysX.Toolkit │ ├── MagicPhysX.Toolkit.csproj │ ├── Program.cs │ └── Toolkit │ ├── ColliderType.cs │ ├── Colliders │ ├── BoxCollider.cs │ ├── CapsuleCollider.cs │ ├── Collider.cs │ ├── PlaneCollider.cs │ └── SphereCollider.cs │ ├── CollisionDetectionMode.cs │ ├── ForceMode.cs │ ├── Internal │ ├── Extensions.cs │ ├── PhysicsFoundation.cs │ ├── PxRigidDynamicLockFlagsExtensions.cs │ ├── RigidbodyConstraintsExtensions.cs │ └── UnorderedKeyedCollection.cs │ ├── PhysicsScene.RigidActorFactory.cs │ ├── PhysicsScene.RigidActorFactory.tt │ ├── PhysicsScene.cs │ ├── PhysicsSettingsEnums.cs │ ├── PhysicsSystem.cs │ ├── Rigidbody.cs │ ├── RigidbodyConstraints.cs │ └── Transform.cs ├── sandbox └── ConsoleSandbox │ ├── BricksDoubleDomino.cs │ ├── ConsoleSandbox.csproj │ ├── DistanceJoint.cs │ ├── FixedJoint.cs │ ├── Geometries.cs │ ├── Program.cs │ ├── RaycastSingle.cs │ ├── ReadMeSample.cs │ ├── RevoluteJoint.cs │ └── SphericalJoint.cs ├── src ├── MagicPhysX │ ├── ImplicitConvert.cs │ ├── MagicPhysX.csproj │ ├── NativeMethods.DllImportResolver.cs │ ├── NativeMethods.Grouping.cs │ ├── NativeMethods.g.cs │ └── runtimes │ │ ├── linux-arm64 │ │ └── native │ │ │ └── libmagicphysx.so │ │ ├── linux-x64 │ │ └── native │ │ │ └── libmagicphysx.so │ │ ├── osx-arm64 │ │ └── native │ │ │ └── libmagicphysx.dylib │ │ ├── osx-x64 │ │ └── native │ │ │ └── libmagicphysx.dylib │ │ └── win-x64 │ │ └── native │ │ └── libmagicphysx.dll └── libmagicphysx │ ├── .vscode │ ├── settings.json │ └── tasks.json │ ├── Cargo.toml │ ├── build.rs │ └── src │ ├── lib.rs │ ├── physx │ ├── lib.rs │ ├── physx_generated.rs │ ├── unix │ │ └── structgen.rs │ └── x86_64-pc-windows-msvc │ │ └── structgen.rs │ └── physx_ffi.rs └── tests └── MagicPhysX.Tests ├── InitializeTest.cs ├── MagicPhysX.Tests.csproj └── Usings.cs /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_style = space 8 | indent_size = 2 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | # Visual Studio Spell checker configs (https://learn.microsoft.com/en-us/visualstudio/ide/text-spell-checker?view=vs-2022#how-to-customize-the-spell-checker) 13 | spelling_exclusion_path = ./exclusion.dic 14 | 15 | [*.cs] 16 | indent_size = 4 17 | charset = utf-8-bom 18 | end_of_line = unset 19 | 20 | # Solution files 21 | [*.{sln,slnx}] 22 | end_of_line = unset 23 | 24 | # MSBuild project files 25 | [*.{csproj,props,targets}] 26 | end_of_line = unset 27 | 28 | # Xml config files 29 | [*.{ruleset,config,nuspec,resx,runsettings,DotSettings}] 30 | end_of_line = unset 31 | 32 | [*.{ts,js}] 33 | charset = utf-8 34 | indent_size = 2 35 | end_of_line = lf 36 | 37 | [*{_AssemblyInfo.cs,.notsupported.cs}] 38 | generated_code = true 39 | 40 | # C# code style settings 41 | [*.{cs}] 42 | csharp_style_namespace_declarations = file_scoped 43 | dotnet_style_require_accessibility_modifiers = never 44 | 45 | # VSTHRD101: Avoid async void 46 | # VSTHRD101: Avoid unsupported async delegates 47 | dotnet_diagnostic.VSTHRD100.severity = none 48 | dotnet_diagnostic.VSTHRD101.severity = none 49 | 50 | # VSTHRD003: Avoid awaiting foreign Tasks 51 | dotnet_diagnostic.VSTHRD003.severity = none 52 | 53 | # VSTHRD111: Use ConfigureAwait(bool) 54 | dotnet_diagnostic.VSTHRD111.severity = error 55 | -------------------------------------------------------------------------------- /.gitattirbutes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.ts text eol=lf 3 | *.js text eol=lf 4 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | # ref: https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot 2 | version: 2 3 | updates: 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" # Check for updates to GitHub Actions every week 8 | ignore: 9 | # I just want update action when major/minor version is updated. patch updates are too noisy. 10 | - dependency-name: '*' 11 | update-types: 12 | - version-update:semver-patch 13 | -------------------------------------------------------------------------------- /.github/workflows/build-debug.yaml: -------------------------------------------------------------------------------- 1 | name: Build-Debug 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - "main" 8 | pull_request: 9 | branches: 10 | - "main" 11 | 12 | jobs: 13 | win-x64: 14 | permissions: 15 | contents: read 16 | runs-on: windows-2025 17 | timeout-minutes: 10 18 | steps: 19 | - uses: Cysharp/Actions/.github/actions/checkout@main 20 | - uses: Cysharp/Actions/.github/actions/setup-dotnet@main 21 | with: 22 | dotnet-version: 7.0.x 23 | - run: dotnet build -c Debug 24 | - run: dotnet test -c Debug --no-build 25 | 26 | linux-x64: 27 | permissions: 28 | contents: read 29 | runs-on: ubuntu-24.04 30 | timeout-minutes: 10 31 | steps: 32 | - uses: Cysharp/Actions/.github/actions/checkout@main 33 | - uses: Cysharp/Actions/.github/actions/setup-dotnet@main 34 | with: 35 | dotnet-version: 7.0.x 36 | - run: dotnet build -c Debug 37 | - run: dotnet test -c Debug --no-build 38 | 39 | # linux-arm64: 40 | # permissions: 41 | # contents: read 42 | # runs-on: ubuntu-24.04 43 | # timeout-minutes: 10 44 | # container: 45 | # image: mcr.microsoft.com/dotnet/aspnet:7.0.8-jammy-arm64v8 46 | # timeout-minutes: 30 47 | # steps: 48 | # - uses: Cysharp/Actions/.github/actions/checkout@main 49 | # - uses: Cysharp/Actions/.github/actions/setup-dotnet@main 50 | # with: 51 | # dotnet-version: 7.0.x 52 | # - run: dotnet build -c Debug 53 | # - run: dotnet test -c Debug --no-build 54 | 55 | osx-x64: 56 | permissions: 57 | contents: read 58 | runs-on: macos-15 59 | timeout-minutes: 10 60 | steps: 61 | - uses: Cysharp/Actions/.github/actions/checkout@main 62 | - uses: Cysharp/Actions/.github/actions/setup-dotnet@main 63 | with: 64 | dotnet-version: 7.0.x 65 | - run: dotnet build -c Debug 66 | - run: dotnet test -c Debug --no-build 67 | -------------------------------------------------------------------------------- /.github/workflows/build-physx.yaml: -------------------------------------------------------------------------------- 1 | name: Build-PhysX 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | physxversion: 7 | description: "physx-sys version. (sample 0.11.1)" 8 | required: true 9 | 10 | jobs: 11 | update-package: 12 | permissions: 13 | contents: write 14 | runs-on: ubuntu-24.04 15 | timeout-minutes: 30 16 | defaults: 17 | run: 18 | working-directory: ./src/libmagicphysx 19 | steps: 20 | - uses: Cysharp/Actions/.github/actions/checkout@main 21 | - run: cargo test update_package_version -- ${{ inputs.physxversion }} --nocapture 22 | 23 | - name: Check update 24 | id: check_update 25 | run: git diff --exit-code || echo "changed=1" 26 | 27 | - name: Commit files 28 | id: commit 29 | if: ${{ steps.check_update.outputs.changed == '1' }} 30 | run: | 31 | git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" 32 | git config --local user.name "github-actions[bot]" 33 | git add --all 34 | git commit -m "Update cargo.toml physx-sys version to ${{ inputs.physxversion }}" -a 35 | 36 | - name: Push changes 37 | if: ${{ steps.check_update.outputs.changed == '1' }} 38 | uses: ad-m/github-push-action@d91a481090679876dfc4178fef17f286781251df 39 | with: 40 | github_token: ${{ secrets.GITHUB_TOKEN }} 41 | branch: ${{ github.ref }} 42 | 43 | # Rust Platform target https://doc.rust-lang.org/nightly/rustc/platform-support.html 44 | # TODO: use matrix 45 | 46 | win-x64: 47 | needs: [update-package] 48 | permissions: 49 | contents: write 50 | runs-on: windows-2019 51 | timeout-minutes: 30 52 | defaults: 53 | run: 54 | working-directory: ./src/libmagicphysx 55 | steps: 56 | - uses: Cysharp/Actions/.github/actions/checkout@main 57 | - run: rustup target add x86_64-pc-windows-msvc 58 | - run: cargo build --target x86_64-pc-windows-msvc --release 59 | - uses: Cysharp/Actions/.github/actions/upload-artifact@main 60 | with: 61 | name: win-x64 62 | path: ./src/libmagicphysx/target/x86_64-pc-windows-msvc/release/magicphysx.dll 63 | retention-days: 1 64 | 65 | linux-x64: 66 | needs: [update-package] 67 | permissions: 68 | contents: write 69 | runs-on: ubuntu-24.04 70 | timeout-minutes: 30 71 | defaults: 72 | run: 73 | working-directory: ./src/libmagicphysx 74 | steps: 75 | - uses: Cysharp/Actions/.github/actions/checkout@main 76 | - run: rustup target add x86_64-unknown-linux-gnu 77 | - run: cargo build --target x86_64-unknown-linux-gnu --release 78 | - uses: Cysharp/Actions/.github/actions/upload-artifact@main 79 | with: 80 | name: linux-x64 81 | path: ./src/libmagicphysx/target/x86_64-unknown-linux-gnu/release/libmagicphysx.so 82 | retention-days: 1 83 | 84 | linux-arm64: 85 | needs: [update-package] 86 | permissions: 87 | contents: write 88 | runs-on: ubuntu-24.04 89 | container: ghcr.io/cross-rs/aarch64-unknown-linux-gnu:edge 90 | timeout-minutes: 30 91 | env: 92 | TARGET_CXX: g++ 93 | defaults: 94 | run: 95 | working-directory: ./src/libmagicphysx 96 | steps: 97 | - uses: Cysharp/Actions/.github/actions/checkout@main 98 | - uses: dtolnay/rust-toolchain@4305c38b25d97ef35a8ad1f985ccf2d2242004f2 # stable 99 | with: 100 | target: aarch64-unknown-linux-gnu 101 | - run: cargo build --target aarch64-unknown-linux-gnu --release 102 | - uses: Cysharp/Actions/.github/actions/upload-artifact@main 103 | with: 104 | name: linux-arm64 105 | path: ./src/libmagicphysx/target/aarch64-unknown-linux-gnu/release/libmagicphysx.so 106 | retention-days: 1 107 | 108 | osx-x64: 109 | needs: [update-package] 110 | permissions: 111 | contents: write 112 | runs-on: macos-15 113 | timeout-minutes: 30 114 | defaults: 115 | run: 116 | working-directory: ./src/libmagicphysx 117 | steps: 118 | - uses: Cysharp/Actions/.github/actions/checkout@main 119 | - run: rustup target add x86_64-apple-darwin 120 | - run: cargo build --target x86_64-apple-darwin --release 121 | - uses: Cysharp/Actions/.github/actions/upload-artifact@main 122 | with: 123 | name: osx-x64 124 | path: ./src/libmagicphysx/target/x86_64-apple-darwin/release/libmagicphysx.dylib 125 | retention-days: 1 126 | 127 | osx-arm64: 128 | needs: [update-package] 129 | permissions: 130 | contents: write 131 | runs-on: macos-15 132 | timeout-minutes: 30 133 | defaults: 134 | run: 135 | working-directory: ./src/libmagicphysx 136 | steps: 137 | - uses: Cysharp/Actions/.github/actions/checkout@main 138 | - run: rustup target add aarch64-apple-darwin 139 | - run: cargo build --target aarch64-apple-darwin --release 140 | - uses: Cysharp/Actions/.github/actions/upload-artifact@main 141 | with: 142 | name: osx-arm64 143 | path: ./src/libmagicphysx/target/aarch64-apple-darwin/release/libmagicphysx.dylib 144 | retention-days: 1 145 | 146 | # download binary and git push 147 | 148 | git-push: 149 | needs: [win-x64, linux-x64, linux-arm64, osx-x64, osx-arm64] 150 | permissions: 151 | contents: write 152 | runs-on: ubuntu-24.04 153 | timeout-minutes: 10 154 | steps: 155 | - uses: Cysharp/Actions/.github/actions/checkout@main 156 | - uses: Cysharp/Actions/.github/actions/download-artifact@main 157 | with: 158 | name: win-x64 159 | path: src/MagicPhysX/runtimes/win-x64/native/ 160 | - run: mv ./src/MagicPhysX/runtimes/win-x64/native/magicphysx.dll ./src/MagicPhysX/runtimes/win-x64/native/libmagicphysx.dll 161 | - uses: Cysharp/Actions/.github/actions/download-artifact@main 162 | with: 163 | name: linux-x64 164 | path: src/MagicPhysX/runtimes/linux-x64/native 165 | - uses: Cysharp/Actions/.github/actions/download-artifact@main 166 | with: 167 | name: linux-arm64 168 | path: src/MagicPhysX/runtimes/linux-arm64/native 169 | - uses: Cysharp/Actions/.github/actions/download-artifact@main 170 | with: 171 | name: osx-x64 172 | path: src/MagicPhysX/runtimes/osx-x64/native 173 | - uses: Cysharp/Actions/.github/actions/download-artifact@main 174 | with: 175 | name: osx-arm64 176 | path: src/MagicPhysX/runtimes/osx-arm64/native 177 | - run: | 178 | git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" 179 | git config --local user.name "github-actions[bot]" 180 | git add --all 181 | git commit -m "Update physx lib runtime" -a 182 | - name: Push changes 183 | uses: ad-m/github-push-action@d91a481090679876dfc4178fef17f286781251df 184 | with: 185 | github_token: ${{ secrets.GITHUB_TOKEN }} 186 | branch: ${{ github.ref }} 187 | -------------------------------------------------------------------------------- /.github/workflows/build-release.yaml: -------------------------------------------------------------------------------- 1 | name: Build-Release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | tag: 7 | description: "tag: git tag you want create. (sample 1.0.0)" 8 | required: true 9 | dry-run: 10 | description: "dry-run: true will never create relase/nuget." 11 | required: true 12 | default: false 13 | type: boolean 14 | 15 | jobs: 16 | build-dotnet: 17 | permissions: 18 | contents: read 19 | runs-on: ubuntu-24.04 20 | timeout-minutes: 10 21 | steps: 22 | - uses: Cysharp/Actions/.github/actions/checkout@main 23 | - uses: Cysharp/Actions/.github/actions/setup-dotnet@main 24 | with: 25 | dotnet-version: 7.0.x 26 | # pack nuget 27 | - run: dotnet build -c Release -p:Version=${{ inputs.tag }} 28 | - run: dotnet test -c Release --no-build 29 | - run: dotnet pack -c Release --no-build -p:Version=${{ inputs.tag }} -o ./publish 30 | # Store artifacts. 31 | - uses: Cysharp/Actions/.github/actions/upload-artifact@main 32 | with: 33 | name: nuget 34 | path: ./publish/ 35 | retention-days: 1 36 | 37 | create-release: 38 | needs: [build-dotnet] 39 | permissions: 40 | contents: write 41 | uses: Cysharp/Actions/.github/workflows/create-release.yaml@main 42 | with: 43 | commit-id: '' 44 | tag: ${{ inputs.tag }} 45 | dry-run: ${{ inputs.dry-run }} 46 | nuget-push: true 47 | release-upload: false 48 | secrets: inherit 49 | -------------------------------------------------------------------------------- /.github/workflows/stale.yaml: -------------------------------------------------------------------------------- 1 | name: "Close stale issues" 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "0 0 * * *" 7 | 8 | jobs: 9 | stale: 10 | permissions: 11 | contents: read 12 | pull-requests: write 13 | issues: write 14 | uses: Cysharp/Actions/.github/workflows/stale-issue.yaml@main 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | src/libmagicphysx/target 2 | **/Cargo.lock 3 | 4 | .vs/ 5 | **/bin 6 | **/obj 7 | 8 | .DS_Store 9 | .idea/ 10 | 11 | !src/MagicPhysX/runtimes/** -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cysharp/MagicPhysX/c320362da782c9f5815d77b78bd7980acf40e7d3/.gitmodules -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | latest 4 | enable 5 | true 6 | $(NoWarn);CS1591 7 | true 8 | $(MSBuildThisFileDirectory)opensource.snk 9 | 10 | 11 | $(Version) 12 | Cysharp 13 | Cysharp 14 | © Cysharp, Inc. 15 | https://github.com/Cysharp/MagicPhysX 16 | $(PackageProjectUrl) 17 | git 18 | MIT 19 | Icon.png 20 | $(MSBuildThisFileDirectory)opensource.snk 21 | 22 | -------------------------------------------------------------------------------- /Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cysharp/MagicPhysX/c320362da782c9f5815d77b78bd7980acf40e7d3/Icon.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Cysharp, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MagicPhysX.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.33414.496 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MagicPhysX", "src\MagicPhysX\MagicPhysX.csproj", "{93929A68-DEC0-420A-A717-F9DEB8ADF654}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{AD75C86C-CCF6-4F9C-B692-9E13BF5EDBDC}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{A824F7B6-B908-40A6-98C7-856ED23AD38C}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sandbox", "sandbox", "{51742C40-9288-4F98-A6EE-FC655D5D7BFC}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleSandbox", "sandbox\ConsoleSandbox\ConsoleSandbox.csproj", "{6B0F24F7-AD2A-467C-B8AE-3A752599784B}" 15 | EndProject 16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{89E81403-A514-4F30-A02E-E8C157FAAF16}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MagicPhysX.Tests", "tests\MagicPhysX.Tests\MagicPhysX.Tests.csproj", "{BA67B868-408A-4F95-9669-D012F71BBC05}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MagicPhysX.Toolkit", "samples\MagicPhysX.Toolkit\MagicPhysX.Toolkit.csproj", "{56AFF15C-EABE-4C09-8386-FDE720287EB3}" 21 | EndProject 22 | Global 23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 24 | Debug|Any CPU = Debug|Any CPU 25 | Release|Any CPU = Release|Any CPU 26 | EndGlobalSection 27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 28 | {93929A68-DEC0-420A-A717-F9DEB8ADF654}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {93929A68-DEC0-420A-A717-F9DEB8ADF654}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {93929A68-DEC0-420A-A717-F9DEB8ADF654}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {93929A68-DEC0-420A-A717-F9DEB8ADF654}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {6B0F24F7-AD2A-467C-B8AE-3A752599784B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {6B0F24F7-AD2A-467C-B8AE-3A752599784B}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {6B0F24F7-AD2A-467C-B8AE-3A752599784B}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {6B0F24F7-AD2A-467C-B8AE-3A752599784B}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {BA67B868-408A-4F95-9669-D012F71BBC05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {BA67B868-408A-4F95-9669-D012F71BBC05}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {BA67B868-408A-4F95-9669-D012F71BBC05}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {BA67B868-408A-4F95-9669-D012F71BBC05}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {56AFF15C-EABE-4C09-8386-FDE720287EB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {56AFF15C-EABE-4C09-8386-FDE720287EB3}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {56AFF15C-EABE-4C09-8386-FDE720287EB3}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {56AFF15C-EABE-4C09-8386-FDE720287EB3}.Release|Any CPU.Build.0 = Release|Any CPU 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(NestedProjects) = preSolution 49 | {93929A68-DEC0-420A-A717-F9DEB8ADF654} = {AD75C86C-CCF6-4F9C-B692-9E13BF5EDBDC} 50 | {6B0F24F7-AD2A-467C-B8AE-3A752599784B} = {51742C40-9288-4F98-A6EE-FC655D5D7BFC} 51 | {BA67B868-408A-4F95-9669-D012F71BBC05} = {89E81403-A514-4F30-A02E-E8C157FAAF16} 52 | {56AFF15C-EABE-4C09-8386-FDE720287EB3} = {A824F7B6-B908-40A6-98C7-856ED23AD38C} 53 | EndGlobalSection 54 | GlobalSection(ExtensibilityGlobals) = postSolution 55 | SolutionGuid = {2C800469-2418-4B4A-84C6-A50184E71234} 56 | EndGlobalSection 57 | EndGlobal 58 | -------------------------------------------------------------------------------- /MagicPhysX.sln.DotSettings.user: -------------------------------------------------------------------------------- 1 |  2 | <SessionState ContinuousTestingMode="0" IsActive="True" Name="CreateScene" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> 3 | <TestAncestor> 4 | <TestId>xUnit::BA67B868-408A-4F95-9669-D012F71BBC05::net7.0::MagicPhysX.Tests.BasicTest.InitializeTest</TestId> 5 | </TestAncestor> 6 | </SessionState> -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MagicPhysX 2 | 3 | [![NuGet](https://img.shields.io/nuget/v/MagicPhysX.svg)](https://www.nuget.org/packages/MagicPhysX) 4 | [![GitHub Actions](https://github.com/Cysharp/MagicPhysX/workflows/Build-Debug/badge.svg)](https://github.com/Cysharp/MagicPhysX/actions) 5 | [![Releases](https://img.shields.io/github/release/Cysharp/MagicPhysX.svg)](https://github.com/Cysharp/MagicPhysX/releases) 6 | 7 | .NET PhysX 5 binding to all platforms(win-x64, osx-x64, osx-arm64, linux-x64, linux-arm64) for 3D engine, deep learning, dedicated server of gaming. This library is built on top of [NVIDIA PhysX 5](https://github.com/NVIDIA-Omniverse/PhysX) and [physx-rs](https://github.com/EmbarkStudios/physx-rs). 8 | 9 | Use case: 10 | * 3D View for MAUI, WPF, Avalonia 11 | * Physics for Your Own Game Engine 12 | * Simulate robotics for deep learning 13 | * Server side physics for dedicated server of gaming 14 | 15 | Getting Started 16 | --- 17 | PhysX Binding provides all of PhysX feature through C API. This library is distributed via NuGet. 18 | 19 | > PM> Install-Package [MagicPhysX](https://www.nuget.org/packages/MagicPhysX) 20 | 21 | C API is provided in `NativeMethods` in `MagicPhysX` namespace. Methods are almostly prefixed `phys_Px` or `Px`. In addition, extension methods are defined for contexts, just like object-oriented methods. For example, for `PxPhysics*`: 22 | 23 | ```csharp 24 | PxPhysics* physics = physx_create_physics(foundation); 25 | 26 | // C API 27 | PxScene* scene1 = PxPhysics_createScene_mut(physics, &sceneDesc); 28 | 29 | // Extension methods 30 | PxScene* scene2 = physics->CreateSceneMut(&sceneDesc); 31 | ``` 32 | 33 | Extension methods API is simpler and similar as original C++ PhysX API. 34 | 35 | Here is the simple bound sphere on plane sample. 36 | 37 | ```csharp 38 | using MagicPhysX; // for enable Extension Methods. 39 | using static MagicPhysX.NativeMethods; // recommend to use C API. 40 | 41 | // create foundation(allocator, logging, etc...) 42 | var foundation = physx_create_foundation(); 43 | 44 | // create physics system 45 | var physics = physx_create_physics(foundation); 46 | 47 | // create physics scene settings 48 | var sceneDesc = PxSceneDesc_new(PxPhysics_getTolerancesScale(physics)); 49 | 50 | // you can create PhysX primitive(PxVec3, etc...) by C# struct 51 | sceneDesc.gravity = new PxVec3 { x = 0.0f, y = -9.81f, z = 0.0f }; 52 | 53 | var dispatcher = phys_PxDefaultCpuDispatcherCreate(1, null, PxDefaultCpuDispatcherWaitForWorkMode.WaitForWork, 0); 54 | sceneDesc.cpuDispatcher = (PxCpuDispatcher*)dispatcher; 55 | sceneDesc.filterShader = get_default_simulation_filter_shader(); 56 | 57 | // create physics scene 58 | var scene = physics->CreateSceneMut(&sceneDesc); 59 | 60 | var material = physics->CreateMaterialMut(0.5f, 0.5f, 0.6f); 61 | 62 | // create plane and add to scene 63 | var plane = PxPlane_new_1(0.0f, 1.0f, 0.0f, 0.0f); 64 | var groundPlane = physics->PhysPxCreatePlane(&plane, material); 65 | scene->AddActorMut((PxActor*)groundPlane, null); 66 | 67 | // create sphere and add to scene 68 | var sphereGeo = PxSphereGeometry_new(10.0f); 69 | var vec3 = new PxVec3 { x = 0.0f, y = 40.0f, z = 100.0f }; 70 | var transform = PxTransform_new_1(&vec3); 71 | var identity = PxTransform_new_2(PxIDENTITY.PxIdentity); 72 | var sphere = physics->PhysPxCreateDynamic(&transform, (PxGeometry*)&sphereGeo, material, 10.0f, &identity); 73 | PxRigidBody_setAngularDamping_mut((PxRigidBody*)sphere, 0.5f); 74 | scene->AddActorMut((PxActor*)sphere, null); 75 | 76 | // simulate scene 77 | for (int i = 0; i < 200; i++) 78 | { 79 | // 30fps update 80 | scene->SimulateMut(1.0f / 30.0f, null, null, 0, true); 81 | uint error = 0; 82 | scene->FetchResultsMut(true, &error); 83 | 84 | // output to console(frame-count: position-y) 85 | var pose = PxRigidActor_getGlobalPose((PxRigidActor*)sphere); 86 | Console.WriteLine($"{i:000}: {pose.p.y}"); 87 | } 88 | 89 | // release resources 90 | PxScene_release_mut(scene); 91 | PxDefaultCpuDispatcher_release_mut(dispatcher); 92 | PxPhysics_release_mut(physics); 93 | ``` 94 | 95 | Other samples(`FixedJoint`, `DistanceJoint`, `SphericalJoint`, `RevoluteJoint`,`RaycastSingle`, `BricksDoubleDomino`, `Geometries`) are exist in [ConsoleSandbox](https://github.com/Cysharp/MagicPhysX/tree/main/sandbox/ConsoleSandbox). 96 | 97 | ### Document 98 | 99 | MagicPhysX uses [physx-rs](https://github.com/EmbarkStudios/physx-rs) C binding([physx-sys](https://github.com/EmbarkStudios/physx-rs/tree/main/physx-sys)). You can refer these document. 100 | 101 | * [physx-sys Changelog](https://github.com/EmbarkStudios/physx-rs/blob/main/physx-sys/CHANGELOG.md) 102 | * [NVIDIA PhysX](https://github.com/NVIDIA-Omniverse/PhysX) 103 | * [PhysX 5 Documantation](https://nvidia-omniverse.github.io/PhysX/physx/5.1.3/) 104 | 105 | ### PhysX Visual Debugger 106 | 107 | MagicPhysX can enable [PhysX Visual Debugger](https://developer.nvidia.com/physx-visual-debugger) to debug physcs scene. 108 | 109 | ![image](https://github.com/Cysharp/MagicPhysX/assets/46207/2018e821-41c4-44a2-aac6-f0705993ab9b) 110 | 111 | To use pvd, add this instruction on scene init. 112 | 113 | ```csharp 114 | var foundation = physx_create_foundation(); 115 | 116 | // create pvd 117 | var pvd = phys_PxCreatePvd(foundation); 118 | 119 | fixed (byte* bytePointer = "127.0.0.1"u8.ToArray()) 120 | { 121 | var transport = phys_PxDefaultPvdSocketTransportCreate(bytePointer, 5425, 10); 122 | pvd->ConnectMut(transport, PxPvdInstrumentationFlags.All); 123 | } 124 | 125 | // create physics 126 | uint PX_PHYSICS_VERSION_MAJOR = 5; 127 | uint PX_PHYSICS_VERSION_MINOR = 1; 128 | uint PX_PHYSICS_VERSION_BUGFIX = 3; 129 | uint versionNumber = (PX_PHYSICS_VERSION_MAJOR << 24) + (PX_PHYSICS_VERSION_MINOR << 16) + (PX_PHYSICS_VERSION_BUGFIX << 8); 130 | 131 | var tolerancesScale = new PxTolerancesScale { length = 1, speed = 10 }; 132 | var physics = phys_PxCreatePhysics(versionNumber, foundation, &tolerancesScale, true, pvd, null); 133 | 134 | phys_PxInitExtensions(physics, pvd); 135 | 136 | var sceneDesc = PxSceneDesc_new(PxPhysics_getTolerancesScale(physics)); 137 | sceneDesc.gravity = new PxVec3 { x = 0.0f, y = -9.81f, z = 0.0f }; 138 | 139 | var dispatcher = phys_PxDefaultCpuDispatcherCreate(1, null, PxDefaultCpuDispatcherWaitForWorkMode.WaitForWork, 0); 140 | sceneDesc.cpuDispatcher = (PxCpuDispatcher*)dispatcher; 141 | sceneDesc.filterShader = get_default_simulation_filter_shader(); 142 | 143 | var scene = PxPhysics_createScene_mut(physics, &sceneDesc); 144 | 145 | // pvd client 146 | var pvdClient = scene->GetScenePvdClientMut(); 147 | if (pvdClient != null) 148 | { 149 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitConstraints, true); 150 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitContacts, true); 151 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitScenequeries, true); 152 | } 153 | ``` 154 | 155 | Toolkit Sample 156 | --- 157 | C API is slightly complex in C# usage. Here is the sample of high level framework, PhysicsSystem. 158 | 159 | ```csharp 160 | using MagicPhysX.Toolkit; 161 | using System.Numerics; 162 | 163 | unsafe 164 | { 165 | using var physics = new PhysicsSystem(enablePvd: false); 166 | using var scene = physics.CreateScene(); 167 | 168 | var material = physics.CreateMaterial(0.5f, 0.5f, 0.6f); 169 | 170 | var plane = scene.AddStaticPlane(0.0f, 1.0f, 0.0f, 0.0f, new Vector3(0, 0, 0), Quaternion.Identity, material); 171 | var sphere = scene.AddDynamicSphere(1.0f, new Vector3(0.0f, 10.0f, 0.0f), Quaternion.Identity, 10.0f, material); 172 | 173 | for (var i = 0; i < 200; i++) 174 | { 175 | scene.Update(1.0f / 30.0f); 176 | 177 | var position = sphere.transform.position; 178 | Console.WriteLine($"{i:D2} : x={position.X:F6}, y={position.Y:F6}, z={position.Z:F6}"); 179 | } 180 | } 181 | ``` 182 | 183 | Code sample is available in [MagicPhysX.Toolkit](https://github.com/Cysharp/MagicPhysX/tree/main/samples/MagicPhysX.Toolkit). 184 | 185 | Native Build Instruction 186 | --- 187 | require [Rust](https://www.rust-lang.org/). 188 | 189 | Open directory `src\libmagicphysx`. 190 | Run `cargo build`. 191 | 192 | Native binaries in package is built on GitHub Actions [build-physx.yml](https://github.com/Cysharp/MagicPhysX/blob/main/.github/workflows/build-physx.yml). If we need to update physx-sys(PhysX) version, run this GitHub Actions to input physxversion. 193 | 194 | License 195 | --- 196 | This library is licensed under the MIT License. 197 | -------------------------------------------------------------------------------- /opensource.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cysharp/MagicPhysX/c320362da782c9f5815d77b78bd7980acf40e7d3/opensource.snk -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/MagicPhysX.Toolkit.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | enable 7 | enable 8 | MagicPhysX 9 | true 10 | false 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Program.cs: -------------------------------------------------------------------------------- 1 | using MagicPhysX.Toolkit; 2 | using System.Numerics; 3 | 4 | unsafe 5 | { 6 | using var physics = new PhysicsSystem(enablePvd: false); 7 | using var scene = physics.CreateScene(); 8 | 9 | var material = physics.CreateMaterial(0.5f, 0.5f, 0.6f); 10 | 11 | var plane = scene.AddStaticPlane(0.0f, 1.0f, 0.0f, 0.0f, new Vector3(0, 0, 0), Quaternion.Identity, material); 12 | var sphere = scene.AddDynamicSphere(1.0f, new Vector3(0.0f, 10.0f, 0.0f), Quaternion.Identity, 10.0f, material); 13 | 14 | for (var i = 0; i < 200; i++) 15 | { 16 | scene.Update(1.0f / 30.0f); 17 | 18 | var position = sphere.transform.position; 19 | Console.WriteLine($"{i:D2} : x={position.X:F6}, y={position.Y:F6}, z={position.Z:F6}"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Toolkit/ColliderType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MagicPhysX.Toolkit; 8 | 9 | public enum ColliderType 10 | { 11 | Box, 12 | Capsule, 13 | //CharacterController, 14 | //Mesh, 15 | Sphere, 16 | Plane 17 | } 18 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Toolkit/Colliders/BoxCollider.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace MagicPhysX.Toolkit.Colliders 5 | { 6 | /// 7 | /// A box-shaped primitive collider. 8 | /// 9 | public unsafe class BoxCollider : Collider 10 | { 11 | ref PxBoxGeometry GetGeometry() => ref Unsafe.AsRef(shape->GetGeometry()); 12 | 13 | internal BoxCollider(PxShape* shape, ColliderType type) : base(shape, type) 14 | { 15 | } 16 | 17 | /// 18 | /// The center of the box, measured in the object's local space. 19 | /// 20 | public Vector3 center 21 | { 22 | get => shape->GetLocalPose().p; 23 | set 24 | { 25 | var pose = shape->GetLocalPose(); 26 | pose.p = value; 27 | shape->SetLocalPoseMut(&pose); 28 | } 29 | } 30 | 31 | /// 32 | /// The size of the box, measured in the object's local space. 33 | /// 34 | public Vector3 size 35 | { 36 | get => GetGeometry().halfExtents; 37 | set => GetGeometry().halfExtents = value; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Toolkit/Colliders/CapsuleCollider.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace MagicPhysX.Toolkit.Colliders 5 | { 6 | /// 7 | /// A capsule-shaped primitive collider. 8 | /// 9 | public unsafe class CapsuleCollider : Collider 10 | { 11 | ref PxCapsuleGeometry GetGeometry() => ref Unsafe.AsRef(shape->GetGeometry()); 12 | 13 | internal CapsuleCollider(PxShape* shape, ColliderType type) : base(shape, type) 14 | { 15 | } 16 | 17 | /// 18 | /// The center of the capsule, measured in the object's local space. 19 | /// 20 | public Vector3 center 21 | { 22 | get => shape->GetLocalPose().p; 23 | set 24 | { 25 | var pose = shape->GetLocalPose(); 26 | pose.p = value; 27 | shape->SetLocalPoseMut(&pose); 28 | } 29 | } 30 | 31 | /// 32 | /// The radius of the sphere, measured in the object's local space. 33 | /// 34 | public float radius 35 | { 36 | get => GetGeometry().radius; 37 | set => GetGeometry().radius = value; 38 | } 39 | 40 | /// 41 | /// The height of the capsule measured in the object's local space. 42 | /// 43 | public float height 44 | { 45 | get => GetGeometry().halfHeight * 2f; 46 | set => GetGeometry().halfHeight = value / 2f; 47 | } 48 | 49 | ///// 50 | ///// The direction of the capsule. 51 | ///// 52 | //public int direction 53 | //{ 54 | // // TODO: 55 | // get => throw new NotImplementedException(); 56 | // // TODO: 57 | // set => throw new NotImplementedException(); 58 | //} 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Toolkit/Colliders/Collider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace MagicPhysX.Toolkit.Colliders 5 | { 6 | /// 7 | /// A base class of all colliders. 8 | /// 9 | public unsafe abstract class Collider 10 | { 11 | internal PxShape* shape; 12 | readonly ColliderType type; 13 | 14 | internal Collider(PxShape* shape, ColliderType type) 15 | { 16 | this.shape = shape; 17 | this.type = type; 18 | } 19 | 20 | public PxShape* GetShapeHandler() => shape; 21 | 22 | public static Collider Create(PxShape* shape, ColliderType type) 23 | { 24 | switch (type) 25 | { 26 | case ColliderType.Box: 27 | return new BoxCollider(shape, type); 28 | case ColliderType.Capsule: 29 | return new CapsuleCollider(shape, type); 30 | case ColliderType.Sphere: 31 | return new SphereCollider(shape, type); 32 | case ColliderType.Plane: 33 | return new PlaneCollider(shape, type); 34 | default: 35 | throw new ArgumentException(); 36 | } 37 | } 38 | 39 | public ColliderType Type => type; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Toolkit/Colliders/PlaneCollider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Numerics; 3 | using System.Runtime.CompilerServices; 4 | 5 | namespace MagicPhysX.Toolkit.Colliders 6 | { 7 | // PlaneCollider is not exists in Unity 8 | 9 | public unsafe class PlaneCollider : Collider 10 | { 11 | ref PxPlaneGeometry GetGeometry() => ref Unsafe.AsRef(shape->GetGeometry()); 12 | 13 | internal PlaneCollider(PxShape* shape, ColliderType type) : base(shape, type) 14 | { 15 | } 16 | 17 | /// 18 | /// The center of the plane in the object's local space. 19 | /// 20 | public Vector3 center 21 | { 22 | get => shape->GetLocalPose().p; 23 | set 24 | { 25 | var pose = shape->GetLocalPose(); 26 | pose.p = value; 27 | shape->SetLocalPoseMut(&pose); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Toolkit/Colliders/SphereCollider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Numerics; 3 | using System.Runtime.CompilerServices; 4 | 5 | namespace MagicPhysX.Toolkit.Colliders 6 | { 7 | /// 8 | /// A sphere-shaped primitive collider. 9 | /// 10 | public unsafe class SphereCollider : Collider 11 | { 12 | ref PxSphereGeometry GetGeometry() => ref Unsafe.AsRef(shape->GetGeometry()); 13 | 14 | internal SphereCollider(PxShape* shape, ColliderType type) : base(shape, type) 15 | { 16 | } 17 | 18 | /// 19 | /// The center of the sphere in the object's local space. 20 | /// 21 | public Vector3 center 22 | { 23 | get => shape->GetLocalPose().p; 24 | set 25 | { 26 | var pose = shape->GetLocalPose(); 27 | pose.p = value; 28 | shape->SetLocalPoseMut(&pose); 29 | } 30 | } 31 | 32 | /// 33 | /// The radius of the sphere measured in the object's local space. 34 | /// 35 | public float radius 36 | { 37 | get => GetGeometry().radius; 38 | set => GetGeometry().radius = value; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Toolkit/CollisionDetectionMode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MagicPhysX.Toolkit 4 | { 5 | /// 6 | /// The collision detection mode constants used for Rigidbody.collisionDetectionMode. 7 | /// 8 | public enum CollisionDetectionMode : int 9 | { 10 | /// 11 | /// Continuous collision detection is off for this Rigidbody. 12 | /// 13 | Discrete = 0, 14 | /// 15 | /// Continuous collision detection is on for colliding with static mesh geometry. 16 | /// 17 | Continuous = 1, 18 | /// 19 | /// Continuous collision detection is on for colliding with static and dynamic geometry. 20 | /// 21 | ContinuousDynamic = 2, 22 | /// 23 | /// Speculative continuous collision detection is on for static and dynamic geometries 24 | /// 25 | ContinuousSpeculative = 3, 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Toolkit/ForceMode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MagicPhysX.Toolkit 4 | { 5 | /// 6 | /// Use ForceMode to specify how to apply a force using Rigidbody.AddForce or ArticulationBody.AddForce. 7 | /// 8 | public enum ForceMode : int 9 | { 10 | /// 11 | /// Add a continuous force to the rigidbody, using its mass. 12 | /// 13 | Force = 0, 14 | /// 15 | /// Add an instant force impulse to the rigidbody, using its mass. 16 | /// 17 | Impulse = 1, 18 | /// 19 | /// Add an instant velocity change to the rigidbody, ignoring its mass. 20 | /// 21 | VelocityChange = 2, 22 | /// 23 | /// Add a continuous acceleration to the rigidbody, ignoring its mass. 24 | /// 25 | Acceleration = 5, 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Toolkit/Internal/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace MagicPhysX.Toolkit.Internal; 5 | 6 | internal static class Extensions 7 | { 8 | internal static unsafe PxVec3* AsPxPointer(this Vector3 v) => (PxVec3*)Unsafe.AsPointer(ref Unsafe.AsRef(v)); 9 | } 10 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Toolkit/Internal/PhysicsFoundation.cs: -------------------------------------------------------------------------------- 1 | using static MagicPhysX.NativeMethods; 2 | 3 | namespace MagicPhysX.Toolkit.Internal; 4 | 5 | public static unsafe class PhysicsFoundation 6 | { 7 | static readonly object gate = new object(); 8 | static PxFoundation* staticFoundation; 9 | 10 | public static PxFoundation* GetFoundation() 11 | { 12 | lock (gate) 13 | { 14 | if (staticFoundation == null) 15 | { 16 | staticFoundation = physx_create_foundation(); 17 | } 18 | return staticFoundation; 19 | } 20 | } 21 | 22 | // foundation always be single instance per application 23 | // sometimes doesn't match app-lifetime and native-lifetime(for example, Unity Editor) 24 | // you can release foundation manually but be careful to use 25 | public static void ReleaseFoundtaion() 26 | { 27 | lock (gate) 28 | { 29 | if (staticFoundation != null) 30 | { 31 | PxFoundation_release_mut(staticFoundation); 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Toolkit/Internal/PxRigidDynamicLockFlagsExtensions.cs: -------------------------------------------------------------------------------- 1 | using MagicPhysX; 2 | 3 | namespace MagicPhysX.Toolkit.Internal; 4 | 5 | internal static class PxRigidDynamicLockFlagsExtensions 6 | { 7 | public static RigidbodyConstraints AsRigidbodyConstraints(this PxRigidDynamicLockFlags flags) 8 | { 9 | var result = RigidbodyConstraints.None; 10 | if ((flags & PxRigidDynamicLockFlags.LockLinearX) == PxRigidDynamicLockFlags.LockLinearX) 11 | { 12 | result |= RigidbodyConstraints.FreezePositionX; 13 | } 14 | 15 | if ((flags & PxRigidDynamicLockFlags.LockLinearY) == PxRigidDynamicLockFlags.LockLinearY) 16 | { 17 | result |= RigidbodyConstraints.FreezePositionY; 18 | } 19 | 20 | if ((flags & PxRigidDynamicLockFlags.LockLinearZ) == PxRigidDynamicLockFlags.LockLinearZ) 21 | { 22 | result |= RigidbodyConstraints.FreezePositionZ; 23 | } 24 | 25 | if ((flags & PxRigidDynamicLockFlags.LockAngularX) == PxRigidDynamicLockFlags.LockAngularX) 26 | { 27 | result |= RigidbodyConstraints.FreezeRotationX; 28 | } 29 | 30 | if ((flags & PxRigidDynamicLockFlags.LockAngularY) == PxRigidDynamicLockFlags.LockAngularY) 31 | { 32 | result |= RigidbodyConstraints.FreezeRotationY; 33 | } 34 | 35 | if ((flags & PxRigidDynamicLockFlags.LockAngularZ) == PxRigidDynamicLockFlags.LockAngularZ) 36 | { 37 | result |= RigidbodyConstraints.FreezeRotationZ; 38 | } 39 | 40 | return result; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Toolkit/Internal/RigidbodyConstraintsExtensions.cs: -------------------------------------------------------------------------------- 1 | using MagicPhysX; 2 | 3 | namespace MagicPhysX.Toolkit.Internal; 4 | 5 | internal static class RigidbodyConstraintsExtensions 6 | { 7 | public static PxRigidDynamicLockFlags AsPxRigidDynamicLockFlags(this RigidbodyConstraints constraints) 8 | { 9 | PxRigidDynamicLockFlags result = 0; 10 | if ((constraints & RigidbodyConstraints.FreezePositionX) == RigidbodyConstraints.FreezePositionX) 11 | { 12 | result |= PxRigidDynamicLockFlags.LockLinearX; 13 | } 14 | 15 | if ((constraints & RigidbodyConstraints.FreezePositionY) == RigidbodyConstraints.FreezePositionY) 16 | { 17 | result |= PxRigidDynamicLockFlags.LockLinearY; 18 | } 19 | 20 | if ((constraints & RigidbodyConstraints.FreezePositionZ) == RigidbodyConstraints.FreezePositionZ) 21 | { 22 | result |= PxRigidDynamicLockFlags.LockLinearZ; 23 | } 24 | 25 | if ((constraints & RigidbodyConstraints.FreezeRotationX) == RigidbodyConstraints.FreezeRotationX) 26 | { 27 | result |= PxRigidDynamicLockFlags.LockAngularX; 28 | } 29 | 30 | if ((constraints & RigidbodyConstraints.FreezeRotationY) == RigidbodyConstraints.FreezeRotationY) 31 | { 32 | result |= PxRigidDynamicLockFlags.LockAngularY; 33 | } 34 | 35 | if ((constraints & RigidbodyConstraints.FreezeRotationZ) == RigidbodyConstraints.FreezeRotationZ) 36 | { 37 | result |= PxRigidDynamicLockFlags.LockAngularZ; 38 | } 39 | 40 | return result; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Toolkit/Internal/UnorderedKeyedCollection.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace MagicPhysX.Toolkit.Internal; 4 | 5 | internal sealed class UnorderedKeyedCollection 6 | where T : class 7 | { 8 | readonly Dictionary dict; // Value is index of list. 9 | readonly List list; 10 | 11 | public UnorderedKeyedCollection() 12 | : this(4) 13 | { 14 | } 15 | 16 | public UnorderedKeyedCollection(int capacity) 17 | { 18 | dict = new Dictionary(capacity, ReferenceEqualityComparer.Instance); 19 | list = new List(capacity); 20 | } 21 | 22 | public int Count => list.Count; 23 | 24 | public void Add(T value) 25 | { 26 | var index = list.Count; 27 | dict.Add(value, index); 28 | list.Add(value); 29 | } 30 | 31 | public void Clear() 32 | { 33 | dict.Clear(); 34 | list.Clear(); 35 | } 36 | 37 | public void Remove(T value) 38 | { 39 | if (dict.Remove(value, out var index)) 40 | { 41 | // swap remove and update new-index 42 | var span = CollectionsMarshal.AsSpan(list); 43 | (span[index], span[span.Length - 1]) = (span[span.Length - 1], span[index]); 44 | 45 | dict[span[index]] = index; 46 | list.RemoveAt(list.Count - 1); 47 | } 48 | } 49 | 50 | public ReadOnlySpan AsSpan() 51 | { 52 | return CollectionsMarshal.AsSpan(list); 53 | } 54 | 55 | public T[] ToArray() 56 | { 57 | return list.ToArray(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Toolkit/PhysicsScene.RigidActorFactory.cs: -------------------------------------------------------------------------------- 1 | using MagicPhysX; 2 | using System.Numerics; 3 | using static MagicPhysX.NativeMethods; 4 | 5 | namespace MagicPhysX.Toolkit; 6 | 7 | public sealed unsafe partial class PhysicsScene 8 | { 9 | public Rigidbody AddDynamicSphere(float radius, Vector3 position, Quaternion rotation, float density, PxMaterial* material = null) 10 | { 11 | var geometry = PxSphereGeometry_new(radius); 12 | return AddDynamicGeometry((PxGeometry*)&geometry, position, rotation, density, material, ColliderType.Sphere); 13 | } 14 | 15 | public Rigidbody AddKinematicSphere(float radius, Vector3 position, Quaternion rotation, float density, PxMaterial* material = null) 16 | { 17 | var geometry = PxSphereGeometry_new(radius); 18 | return AddKinematicGeometry((PxGeometry*)&geometry, position, rotation, density, material, ColliderType.Sphere); 19 | } 20 | 21 | public Rigidstatic AddStaticSphere(float radius, Vector3 position, Quaternion rotation, PxMaterial* material = null) 22 | { 23 | var geometry = PxSphereGeometry_new(radius); 24 | return AddStaticGeometry((PxGeometry*)&geometry, position, rotation, material, ColliderType.Sphere); 25 | } 26 | 27 | public Rigidbody AddDynamicBox(Vector3 halfExtent, Vector3 position, Quaternion rotation, float density, PxMaterial* material = null) 28 | { 29 | var geometry = PxBoxGeometry_new_1(halfExtent); 30 | return AddDynamicGeometry((PxGeometry*)&geometry, position, rotation, density, material, ColliderType.Box); 31 | } 32 | 33 | public Rigidbody AddKinematicBox(Vector3 halfExtent, Vector3 position, Quaternion rotation, float density, PxMaterial* material = null) 34 | { 35 | var geometry = PxBoxGeometry_new_1(halfExtent); 36 | return AddKinematicGeometry((PxGeometry*)&geometry, position, rotation, density, material, ColliderType.Box); 37 | } 38 | 39 | public Rigidstatic AddStaticBox(Vector3 halfExtent, Vector3 position, Quaternion rotation, PxMaterial* material = null) 40 | { 41 | var geometry = PxBoxGeometry_new_1(halfExtent); 42 | return AddStaticGeometry((PxGeometry*)&geometry, position, rotation, material, ColliderType.Box); 43 | } 44 | 45 | public Rigidbody AddDynamicCapsule(float radius, float halfHeight, Vector3 position, Quaternion rotation, float density, PxMaterial* material = null) 46 | { 47 | var geometry = PxCapsuleGeometry_new(radius, halfHeight); 48 | return AddDynamicGeometry((PxGeometry*)&geometry, position, rotation, density, material, ColliderType.Capsule); 49 | } 50 | 51 | public Rigidbody AddKinematicCapsule(float radius, float halfHeight, Vector3 position, Quaternion rotation, float density, PxMaterial* material = null) 52 | { 53 | var geometry = PxCapsuleGeometry_new(radius, halfHeight); 54 | return AddKinematicGeometry((PxGeometry*)&geometry, position, rotation, density, material, ColliderType.Capsule); 55 | } 56 | 57 | public Rigidstatic AddStaticCapsule(float radius, float halfHeight, Vector3 position, Quaternion rotation, PxMaterial* material = null) 58 | { 59 | var geometry = PxCapsuleGeometry_new(radius, halfHeight); 60 | return AddStaticGeometry((PxGeometry*)&geometry, position, rotation, material, ColliderType.Capsule); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Toolkit/PhysicsScene.RigidActorFactory.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="false" language="C#" #> 2 | <#@ assembly name="System.Core" #> 3 | <#@ import namespace="System.Linq" #> 4 | <#@ import namespace="System.Text" #> 5 | <#@ import namespace="System.Collections.Generic" #> 6 | <#@ output extension=".cs" #> 7 | <# 8 | var bodyTypes = new[]{ 9 | new { Name = "Sphere", New = "new", Parameters = new[] { ("float", "radius") } }, 10 | new { Name = "Box", New = "new_1", Parameters = new[] { ("Vector3", "halfExtent") } }, 11 | new { Name = "Capsule", New = "new", Parameters = new[] { ("float", "radius"), ("float", "halfHeight") } }, 12 | }; 13 | var joinParameter1 = ((string, string)[] xs) => string.Join(", ", xs.Select(x => x.Item1 + " " + x.Item2)); 14 | var joinParameter2 = ((string, string)[] xs) => string.Join(", ", xs.Select(x => x.Item2)); 15 | #> 16 | using MagicPhysX; 17 | using static MagicPhysX.NativeMethods; 18 | 19 | namespace MagicPhysX.Toolkit; 20 | 21 | public sealed unsafe partial class PhysicsScene 22 | { 23 | <# foreach(var item in bodyTypes) { #> 24 | public Rigidbody AddDynamic<#= item.Name #>(<#= joinParameter1(item.Parameters) #>, Vector3 position, Quaternion rotation, float density, PxMaterial* material = null) 25 | { 26 | var geometry = Px<#= item.Name #>Geometry_<#= item.New #>(<#= joinParameter2(item.Parameters) #>); 27 | return AddDynamicGeometry((PxGeometry*)&geometry, position, rotation, density, material, ColliderType.<#= item.Name #>); 28 | } 29 | 30 | public Rigidbody AddKinematic<#= item.Name #>(<#= joinParameter1(item.Parameters) #>, Vector3 position, Quaternion rotation, float density, PxMaterial* material = null) 31 | { 32 | var geometry = Px<#= item.Name #>Geometry_<#= item.New #>(<#= joinParameter2(item.Parameters) #>); 33 | return AddKinematicGeometry((PxGeometry*)&geometry, position, rotation, density, material, ColliderType.<#= item.Name #>); 34 | } 35 | 36 | public Rigidstatic AddStatic<#= item.Name #>(<#= joinParameter1(item.Parameters) #>, Vector3 position, Quaternion rotation, PxMaterial* material = null) 37 | { 38 | var geometry = Px<#= item.Name #>Geometry_<#= item.New #>(<#= joinParameter2(item.Parameters) #>); 39 | return AddStaticGeometry((PxGeometry*)&geometry, position, rotation, material, ColliderType.<#= item.Name #>); 40 | } 41 | 42 | <# } #> 43 | } 44 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Toolkit/PhysicsScene.cs: -------------------------------------------------------------------------------- 1 | using MagicPhysX.Toolkit.Internal; 2 | using MagicPhysX; 3 | using static MagicPhysX.NativeMethods; 4 | using System.Numerics; 5 | using MagicPhysX.Toolkit.Colliders; 6 | 7 | namespace MagicPhysX.Toolkit; 8 | 9 | public sealed unsafe partial class PhysicsScene : IDisposable 10 | { 11 | PhysicsSystem physicsSystem; 12 | PxScene* scene; 13 | bool disposedValue; 14 | int frameCount; 15 | 16 | UnorderedKeyedCollection activeActors = new UnorderedKeyedCollection(); 17 | 18 | public PhysicsScene(PhysicsSystem physicsSystem, PxScene* scene) 19 | { 20 | this.physicsSystem = physicsSystem; 21 | this.scene = scene; 22 | this.frameCount = 0; 23 | 24 | // NOTE: set contact manifold 25 | // https://docs.nvidia.com/gameworks/content/gameworkslibrary/physx/guide/Manual/AdvancedCollisionDetection.html#persistent-contact-manifold-pcm 26 | 27 | // NOTE: set broadphase type(see:Broad-phase Algorithms) 28 | // https://docs.nvidia.com/gameworks/content/gameworkslibrary/physx/guide/Manual/RigidBodyCollision.html 29 | } 30 | 31 | PxPhysics* physics => physicsSystem.physics; 32 | public int FrameCount => frameCount; 33 | 34 | public event Action? Updating; 35 | public event Action? Updated; 36 | public event Action? Disposing; 37 | 38 | public PhysicsSystem PhysicsSystem => physicsSystem; 39 | 40 | public RigidActor[] GetActiveActors() 41 | { 42 | lock (activeActors) 43 | { 44 | return activeActors.ToArray(); 45 | } 46 | } 47 | 48 | public void ForEachActiveActors(Action action) 49 | { 50 | lock (activeActors) 51 | { 52 | foreach (var item in activeActors.AsSpan()) 53 | { 54 | action(item); 55 | } 56 | } 57 | } 58 | 59 | public bool TryCopyActiveActors(Span dest) 60 | { 61 | lock (activeActors) 62 | { 63 | var span = activeActors.AsSpan(); 64 | return span.TryCopyTo(dest); 65 | } 66 | } 67 | 68 | public int GetActiveActorCount() 69 | { 70 | lock (activeActors) 71 | { 72 | return activeActors.Count; 73 | } 74 | } 75 | 76 | public PxMaterial* CreateMaterial(float staticFriction, float dynamicFriction, float restitution) 77 | { 78 | return physicsSystem.CreateMaterial(staticFriction, @dynamicFriction, restitution); 79 | } 80 | 81 | public void Destroy(RigidActor actor) 82 | { 83 | scene->RemoveActorMut((PxActor*)actor.handler, wakeOnLostTouch: true); 84 | actor.handler = null; 85 | lock (activeActors) 86 | { 87 | activeActors.Remove(actor); 88 | } 89 | } 90 | 91 | public Rigidstatic AddStaticPlane(float x, float y, float z, float distance, Vector3 position, Quaternion rotation, PxMaterial* material = null) 92 | { 93 | var plane = PxPlane_new_1(x, y, z, distance); 94 | var transform = phys_PxTransformFromPlaneEquation(&plane); 95 | var geometry = PxPlaneGeometry_new(); 96 | 97 | lock (activeActors) 98 | { 99 | var actor = AddStaticGeometry((PxGeometry*)&geometry, transform.p, transform.q, material, ColliderType.Plane); 100 | 101 | var shape = actor.GetComponent().shape; 102 | var shapeOffset = new Transform { position = position, rotation = rotation }; 103 | shape->SetLocalPoseMut(shapeOffset.AsPxPointer()); 104 | 105 | return actor; 106 | } 107 | } 108 | 109 | Rigidstatic AddStaticGeometry(PxGeometry* geometry, Vector3 position, Quaternion rotation, PxMaterial* material, ColliderType type) 110 | { 111 | var shape = physics->CreateShapeMut(geometry, material, isExclusive: true, PxShapeFlags.Visualization | PxShapeFlags.SceneQueryShape | PxShapeFlags.SimulationShape); 112 | 113 | var transform = new Transform { position = position, rotation = rotation }; 114 | 115 | var rigidStatic = physics->PhysPxCreateStatic1(transform.AsPxPointer(), shape); 116 | 117 | scene->AddActorMut((PxActor*)rigidStatic, bvh: null); 118 | 119 | var collider = Collider.Create(shape, type); 120 | var actor = new Rigidstatic(rigidStatic, collider); 121 | lock (activeActors) 122 | { 123 | activeActors.Add(actor); 124 | } 125 | return actor; 126 | } 127 | 128 | Rigidbody AddDynamicGeometry(PxGeometry* geometry, Vector3 position, Quaternion rotation, float density, PxMaterial* material, ColliderType type) 129 | { 130 | // use default option parameter in C++ 131 | var shape = physics->CreateShapeMut(geometry, material, isExclusive: false, PxShapeFlags.Visualization | PxShapeFlags.SceneQueryShape | PxShapeFlags.SimulationShape); 132 | 133 | var transform = new Transform { position = position, rotation = rotation }; 134 | var rigidDynamic = physics->PhysPxCreateDynamic1(transform.AsPxPointer(), shape, density); 135 | 136 | scene->AddActorMut((PxActor*)rigidDynamic, bvh: null); 137 | 138 | var collider = Collider.Create(shape, type); 139 | var actor = new Rigidbody(rigidDynamic, collider, scene); 140 | lock (activeActors) 141 | { 142 | activeActors.Add(actor); 143 | } 144 | return actor; 145 | } 146 | 147 | Rigidbody AddKinematicGeometry(PxGeometry* geometry, Vector3 position, Quaternion rotation, float density, PxMaterial* material, ColliderType type) 148 | { 149 | // use default option parameter in C++ 150 | var shape = physics->CreateShapeMut(geometry, material, isExclusive: false, PxShapeFlags.Visualization | PxShapeFlags.SceneQueryShape | PxShapeFlags.SimulationShape); 151 | 152 | var transform = new Transform { position = position, rotation = rotation }; 153 | var rigidDynamic = physics->PhysPxCreateKinematic1(transform.AsPxPointer(), shape, density); 154 | 155 | scene->AddActorMut((PxActor*)rigidDynamic, bvh: null); 156 | 157 | var collider = Collider.Create(shape, type); 158 | var actor = new Rigidbody(rigidDynamic, collider, scene); 159 | lock (activeActors) 160 | { 161 | activeActors.Add(actor); 162 | } 163 | return actor; 164 | } 165 | 166 | public void Update(float elapsedTime = 1.0f / 60.0f) 167 | { 168 | Updating?.Invoke(frameCount); 169 | 170 | scene->SimulateMut(elapsedTime, null, null, 0, controlSimulation: true); 171 | uint error = 0; 172 | PxScene_fetchResults_mut(scene, true, &error); 173 | this.frameCount++; 174 | 175 | Updated?.Invoke(frameCount); 176 | } 177 | 178 | private void Dispose(bool disposing) 179 | { 180 | if (!disposedValue) 181 | { 182 | Disposing?.Invoke(); 183 | 184 | if (disposing) 185 | { 186 | // cleanup managed code 187 | physicsSystem.RemoveScene(this); 188 | physicsSystem = null!; 189 | activeActors.Clear(); 190 | activeActors = null!; 191 | } 192 | 193 | // cleanup unmanaged resource 194 | PxScene_release_mut(scene); 195 | scene = null; 196 | disposedValue = true; 197 | } 198 | } 199 | 200 | ~PhysicsScene() 201 | { 202 | Dispose(disposing: false); 203 | } 204 | 205 | public void Dispose() 206 | { 207 | Dispose(disposing: true); 208 | GC.SuppressFinalize(this); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Toolkit/PhysicsSettingsEnums.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MagicPhysX.Toolkit; 8 | 9 | // https://docs.unity3d.com/Manual/class-PhysicsManager.html 10 | 11 | public enum ContactsGeneration 12 | { 13 | LegacyContactsGeneration, 14 | PersistentContactManifold, // default 15 | } 16 | 17 | public enum ContactPairsMode 18 | { 19 | /// 20 | /// Receive collision and trigger events from all contact pairs except kinematic-kinematic and kinematic-static pairs. 21 | /// 22 | DefaultContactPairs, 23 | /// 24 | /// Receive collision and trigger events from kinematic-kinematic contact pairs. 25 | /// 26 | EnableKinematicKinematicPairs, 27 | /// 28 | /// Receive collision and trigger events from kinematic-static contact pairs. 29 | /// 30 | EnableKinematicStaticPairs, 31 | /// 32 | /// Receive collision and trigger events from all contact pairs. 33 | /// 34 | EnableAllContactPairs, 35 | } 36 | 37 | // map for PxBroadPhaseType::Enum https://docs.nvidia.com/gameworks/content/gameworkslibrary/physx/apireference/files/structPxBroadPhaseType.html 38 | 39 | 40 | public enum BroadphaseType 41 | { 42 | SweepAndPruneBroadphase, // eSAP 43 | MultiboxPruningBroadphase, // MBP 44 | AutomaticBoxPruning, // ??? 45 | } 46 | 47 | 48 | // map for PxFrictionType 49 | public enum FrictionType : int 50 | { 51 | /// 52 | /// A basic strong friction algorithm which typically leads to the most stable results at low solver iteration counts. This method uses only up to four scalar solver constraints per pair of touching objects. 53 | /// 54 | PatchFrictionType = 0, 55 | 56 | /// 57 | /// A simplification of the Coulomb friction model, in which the friction for a given point of contact is applied in the alternating tangent directions of the contact’s normal. This requires more solver iterations than patch friction but is not as accurate as the two-directional model. For Articulation bodies to work with this friction type, set the Solver Type to Temporal Gauss Seidel. 58 | /// 59 | OneDirectionalFrictionType = 1, 60 | 61 | /// 62 | /// Like the one-directional model, but applies friction in both tangent directions simultaneously. This requires more solver iterations but is more accurate. More expensive than patch friction for scenarios with many contact points because it is applied at every contact point. For Articulation bodies to work with this friction type, set the Solver Type to Temporal Gauss Seidel. 63 | /// 64 | TwoDirectionalFrictionType = 2, 65 | } 66 | 67 | public enum SolverType 68 | { 69 | /// 70 | /// The default PhysX solver. 71 | /// 72 | ProjectedGaussSeidel, 73 | 74 | /// 75 | /// This solver offers a better convergence and a better handling of high-mass ratios, minimizes energy introduced when correcting penetrations and improves the resistance of joints 76 | /// to overstretch. It usually helps when you experience some erratic behavior during simulation with the default solver. 77 | /// 78 | TemporalGaussSeidel 79 | } 80 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Toolkit/PhysicsSystem.cs: -------------------------------------------------------------------------------- 1 | using MagicPhysX.Toolkit.Internal; 2 | using MagicPhysX; 3 | using System.Text; 4 | using static MagicPhysX.NativeMethods; 5 | using System.Numerics; 6 | 7 | namespace MagicPhysX.Toolkit; 8 | 9 | public sealed unsafe class PhysicsSystem : IDisposable 10 | { 11 | static readonly uint VersionNumber = (5 << 24) + (1 << 16) + (3 << 8); 12 | PxDefaultCpuDispatcher* dispatcher; 13 | internal PxPhysics* physics; 14 | 15 | bool disposedValue; 16 | bool enablePvd; 17 | 18 | UnorderedKeyedCollection scenes = new UnorderedKeyedCollection(); 19 | 20 | public PhysicsSystem() 21 | : this(false) 22 | { 23 | } 24 | 25 | public PhysicsSystem(bool enablePvd) 26 | : this(enablePvd, "127.0.0.1", 5425) 27 | { 28 | } 29 | 30 | public PhysicsSystem(string pvdIp, int pvdPort) 31 | : this(true, pvdIp, pvdPort) 32 | { 33 | } 34 | 35 | PhysicsSystem(bool enablePvd, string pvdIp, int pvdPort) 36 | { 37 | this.enablePvd = enablePvd; 38 | 39 | if (!enablePvd) 40 | { 41 | this.physics = physx_create_physics(PhysicsFoundation.GetFoundation()); 42 | this.dispatcher = phys_PxDefaultCpuDispatcherCreate(1, null, PxDefaultCpuDispatcherWaitForWorkMode.WaitForWork, 0); 43 | return; 44 | } 45 | 46 | PxPvd* pvd = default; 47 | 48 | // create pvd 49 | pvd = phys_PxCreatePvd(PhysicsFoundation.GetFoundation()); 50 | 51 | fixed (byte* bytePointer = Encoding.UTF8.GetBytes(pvdIp)) 52 | { 53 | var transport = phys_PxDefaultPvdSocketTransportCreate(bytePointer, pvdPort, 10); 54 | pvd->ConnectMut(transport, PxPvdInstrumentationFlags.All); 55 | } 56 | 57 | var tolerancesScale = new PxTolerancesScale 58 | { 59 | length = 1, 60 | speed = 10 61 | }; 62 | 63 | this.physics = phys_PxCreatePhysics( 64 | VersionNumber, 65 | PhysicsFoundation.GetFoundation(), 66 | &tolerancesScale, 67 | true, 68 | pvd, 69 | null); 70 | phys_PxInitExtensions(physics, pvd); 71 | 72 | this.dispatcher = phys_PxDefaultCpuDispatcherCreate(1, null, PxDefaultCpuDispatcherWaitForWorkMode.WaitForWork, 0); 73 | } 74 | 75 | public event Action? SceneCreated; 76 | 77 | public PhysicsScene CreateScene() 78 | { 79 | var scene = CreateScene(new Vector3(0.0f, -9.81f, 0.0f)); 80 | lock (scenes) 81 | { 82 | scenes.Add(scene); 83 | } 84 | 85 | SceneCreated?.Invoke(scene); 86 | return scene; 87 | } 88 | 89 | public PhysicsScene CreateScene(Vector3 gravity) 90 | { 91 | var sceneDesc = PxSceneDesc_new(PxPhysics_getTolerancesScale(physics)); 92 | sceneDesc.gravity = gravity; 93 | sceneDesc.cpuDispatcher = (PxCpuDispatcher*)dispatcher; 94 | sceneDesc.filterShader = get_default_simulation_filter_shader(); 95 | sceneDesc.solverType = PxSolverType.Pgs; 96 | 97 | var scene = physics->CreateSceneMut(&sceneDesc); 98 | 99 | if (enablePvd) 100 | { 101 | var pvdClient = scene->GetScenePvdClientMut(); 102 | 103 | if (pvdClient != null) 104 | { 105 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitConstraints, true); 106 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitContacts, true); 107 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitScenequeries, true); 108 | } 109 | } 110 | 111 | return new PhysicsScene(this, scene); 112 | } 113 | 114 | internal void RemoveScene(PhysicsScene scene) 115 | { 116 | lock (scenes) 117 | { 118 | scenes.Remove(scene); 119 | } 120 | } 121 | 122 | public PhysicsScene[] GetPhysicsScenes() 123 | { 124 | lock (scenes) 125 | { 126 | return scenes.ToArray(); 127 | } 128 | } 129 | 130 | public void ForEachPhysicsScenes(Action action) 131 | { 132 | lock (scenes) 133 | { 134 | foreach (var item in scenes.AsSpan()) 135 | { 136 | action(item); 137 | } 138 | } 139 | } 140 | 141 | public bool TryCopyPhysicsScenes(Span dest) 142 | { 143 | lock (scenes) 144 | { 145 | var span = scenes.AsSpan(); 146 | return span.TryCopyTo(dest); 147 | } 148 | } 149 | 150 | public PxMaterial* CreateMaterial(float staticFriction, float dynamicFriction, float restitution) 151 | { 152 | return physics->CreateMaterialMut(staticFriction, @dynamicFriction, restitution); 153 | } 154 | 155 | void Dispose(bool disposing) 156 | { 157 | if (!disposedValue) 158 | { 159 | if (disposing) 160 | { 161 | // cleanup managed code 162 | foreach (var item in scenes.AsSpan()) 163 | { 164 | item.Dispose(); 165 | } 166 | scenes.Clear(); 167 | scenes = null!; 168 | } 169 | 170 | // cleanup unmanaged resource 171 | PxDefaultCpuDispatcher_release_mut(dispatcher); 172 | PxPhysics_release_mut(physics); 173 | 174 | dispatcher = null; 175 | physics = null; 176 | 177 | disposedValue = true; 178 | } 179 | } 180 | 181 | ~PhysicsSystem() 182 | { 183 | Dispose(disposing: false); 184 | } 185 | 186 | public void Dispose() 187 | { 188 | Dispose(disposing: true); 189 | GC.SuppressFinalize(this); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Toolkit/Rigidbody.cs: -------------------------------------------------------------------------------- 1 | using MagicPhysX.Toolkit.Colliders; 2 | using MagicPhysX.Toolkit.Internal; 3 | using System.Numerics; 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace MagicPhysX.Toolkit; 7 | 8 | // PxBase <- PxActor <- PxRigidActor <- PxRigidStatic 9 | // <- PxRidigBody <- PxArticulationLink 10 | // <- PxRigidDynamic 11 | 12 | public unsafe abstract class RigidActor 13 | { 14 | internal void* handler; 15 | protected Collider collider; 16 | 17 | public ref PxRigidActor RigidActorHandler => ref Unsafe.AsRef(handler); 18 | 19 | protected unsafe RigidActor(void* handler, Collider collider) 20 | { 21 | this.handler = handler; 22 | this.collider = collider; 23 | } 24 | 25 | public Collider GetComponent() => collider; 26 | 27 | public T GetComponent() 28 | where T : Collider 29 | { 30 | return (T)collider; 31 | } 32 | 33 | public Transform transform 34 | { 35 | get => RigidActorHandler.GetGlobalPose(); 36 | } 37 | } 38 | 39 | public unsafe class Rigidstatic : RigidActor 40 | { 41 | public ref PxRigidStatic RigidStatic => ref Unsafe.AsRef(handler); 42 | 43 | internal Rigidstatic(PxRigidStatic* handler, Collider collider) 44 | : base(handler, collider) 45 | { 46 | } 47 | } 48 | 49 | /// 50 | /// Control of an object's position through physics simulation. 51 | /// 52 | public unsafe class Rigidbody : RigidActor 53 | { 54 | PxScene* scene; 55 | 56 | public ref PxRigidDynamic RigidDynamic => ref Unsafe.AsRef(handler); 57 | public ref PxRigidBody RigidBody => ref Unsafe.AsRef(handler); 58 | public ref PxRigidActor RigidActor => ref Unsafe.AsRef(handler); 59 | public ref PxActor Actor => ref Unsafe.AsRef(handler); 60 | 61 | static readonly PxRigidDynamicLockFlags PxRigidDynamicLockFlagsLockAngular = 62 | PxRigidDynamicLockFlags.LockAngularX | 63 | PxRigidDynamicLockFlags.LockAngularY | 64 | PxRigidDynamicLockFlags.LockAngularZ; 65 | 66 | internal Rigidbody(PxRigidDynamic* handler, Collider collider, PxScene* scene) 67 | : base(handler, collider) 68 | { 69 | this.scene = scene; 70 | } 71 | 72 | /// 73 | /// The velocity vector of the rigidbody. It represents the rate of change of Rigidbody position. 74 | /// 75 | public Vector3 velocity 76 | { 77 | get => RigidDynamic.GetLinearVelocity(); 78 | set => RigidDynamic.SetLinearVelocityMut(value.AsPxPointer(), autowake: true); 79 | } 80 | 81 | /// 82 | /// The angular velocity vector of the rigidbody measured in radians per second. 83 | /// 84 | public Vector3 angularVelocity 85 | { 86 | get => RigidDynamic.GetAngularVelocity(); 87 | set => RigidDynamic.SetAngularVelocityMut(value.AsPxPointer(), autowake: true); 88 | } 89 | 90 | /// 91 | /// The drag of the object. 92 | /// 93 | public float drag 94 | { 95 | get => RigidBody.GetLinearDamping(); 96 | set => RigidBody.SetLinearDampingMut(value); 97 | } 98 | 99 | /// 100 | /// The angular drag of the object. 101 | /// 102 | public float angularDrag 103 | { 104 | get => RigidBody.GetAngularDamping(); 105 | set => RigidBody.SetAngularDampingMut(value); 106 | } 107 | 108 | /// 109 | /// The mass of the rigidbody. 110 | /// 111 | public float mass 112 | { 113 | get => RigidBody.GetMass(); 114 | set => RigidBody.SetMassMut(value); 115 | } 116 | 117 | /// 118 | /// Controls whether gravity affects this rigidbody. 119 | /// 120 | public bool useGravity 121 | { 122 | get => (Actor.GetActorFlags() & PxActorFlags.DisableGravity) == 0; 123 | set => Actor.SetActorFlagMut(PxActorFlag.DisableGravity, !value); 124 | } 125 | 126 | /// 127 | /// Maximum velocity of a rigidbody when moving out of penetrating state. 128 | /// 129 | public float maxDepenetrationVelocity 130 | { 131 | get => RigidBody.GetMaxDepenetrationVelocity(); 132 | set => RigidBody.SetMaxDepenetrationVelocityMut(value); 133 | } 134 | 135 | /// 136 | /// Controls whether physics affects the rigidbody. 137 | /// 138 | public bool isKinematic 139 | { 140 | get => (RigidBody.GetRigidBodyFlags() & PxRigidBodyFlags.Kinematic) == PxRigidBodyFlags.Kinematic; 141 | set => RigidBody.SetRigidBodyFlagMut(PxRigidBodyFlag.Kinematic, value); 142 | } 143 | 144 | /// 145 | /// Controls whether physics will change the rotation of the object. 146 | /// 147 | public bool freezeRotation 148 | { 149 | get => (RigidDynamic.GetRigidDynamicLockFlags() & PxRigidDynamicLockFlagsLockAngular) == 150 | PxRigidDynamicLockFlagsLockAngular; 151 | set => RigidDynamic.SetRigidDynamicLockFlagsMut(value ? 152 | RigidDynamic.GetRigidDynamicLockFlags() | PxRigidDynamicLockFlagsLockAngular : 153 | RigidDynamic.GetRigidDynamicLockFlags() & ~PxRigidDynamicLockFlagsLockAngular); 154 | } 155 | 156 | /// 157 | /// Controls which degrees of freedom are allowed for the simulation of this Rigidbody. 158 | /// 159 | public RigidbodyConstraints constraints 160 | { 161 | get => RigidDynamic.GetRigidDynamicLockFlags().AsRigidbodyConstraints(); 162 | set => RigidDynamic.SetRigidDynamicLockFlagsMut(constraints.AsPxRigidDynamicLockFlags()); 163 | } 164 | 165 | /// 166 | /// The Rigidbody's collision detection mode. 167 | /// 168 | public CollisionDetectionMode collisionDetectionMode 169 | { 170 | get 171 | { 172 | var flags = RigidBody.GetRigidBodyFlags(); 173 | if ((flags & PxRigidBodyFlags.EnableSpeculativeCcd) == PxRigidBodyFlags.EnableSpeculativeCcd) 174 | { 175 | return CollisionDetectionMode.ContinuousSpeculative; 176 | } 177 | 178 | if ((flags & PxRigidBodyFlags.EnableCcd) == PxRigidBodyFlags.EnableCcd) 179 | { 180 | return CollisionDetectionMode.ContinuousDynamic; 181 | } 182 | 183 | return CollisionDetectionMode.Discrete; 184 | } 185 | set 186 | { 187 | switch (value) 188 | { 189 | case CollisionDetectionMode.ContinuousDynamic: 190 | RigidBody.SetRigidBodyFlagMut(PxRigidBodyFlag.EnableCcd, true); 191 | RigidBody.SetRigidBodyFlagMut(PxRigidBodyFlag.EnableSpeculativeCcd, false); 192 | break; 193 | case CollisionDetectionMode.ContinuousSpeculative: 194 | RigidBody.SetRigidBodyFlagMut(PxRigidBodyFlag.EnableCcd, false); 195 | RigidBody.SetRigidBodyFlagMut(PxRigidBodyFlag.EnableSpeculativeCcd, true); 196 | break; 197 | case CollisionDetectionMode.Continuous: 198 | // ??? 199 | break; 200 | case CollisionDetectionMode.Discrete: 201 | RigidBody.SetRigidBodyFlagMut(PxRigidBodyFlag.EnableCcd, false); 202 | RigidBody.SetRigidBodyFlagMut(PxRigidBodyFlag.EnableSpeculativeCcd, false); 203 | break; 204 | } 205 | } 206 | } 207 | 208 | /// 209 | /// The center of mass relative to the transform's origin. 210 | /// 211 | public Vector3 centerOfMass 212 | { 213 | get => RigidBody.GetCMassLocalPose().p; 214 | set 215 | { 216 | var pose = RigidBody.GetCMassLocalPose(); 217 | pose.p = value; 218 | RigidBody.SetCMassLocalPoseMut(&pose); 219 | } 220 | } 221 | 222 | /// 223 | /// The center of mass of the rigidbody in world space (Read Only). 224 | /// 225 | public Vector3 worldCenterOfMass 226 | { 227 | get 228 | { 229 | var globalPose = RigidActor.GetGlobalPose(); 230 | return globalPose.Transform(centerOfMass.AsPxPointer()); 231 | } 232 | } 233 | 234 | /// 235 | /// The inertia tensor of this body, defined as a diagonal matrix in a reference frame positioned at this body's center of mass and rotated by Rigidbody.inertiaTensorRotation. 236 | /// 237 | public Vector3 inertiaTensor 238 | { 239 | get => RigidBody.GetMassSpaceInertiaTensor(); 240 | set => RigidBody.SetMassSpaceInertiaTensorMut(value.AsPxPointer()); 241 | } 242 | 243 | /// 244 | /// Should collision detection be enabled? (By default always enabled). 245 | /// 246 | public bool detectCollisions 247 | { 248 | get 249 | { 250 | var num = RigidActor.GetNbShapes(); 251 | if (num > 0) 252 | { 253 | PxShape*[] shapes = new PxShape*[num]; 254 | fixed (PxShape** p = shapes) 255 | { 256 | RigidActor.GetShapes(p, num, 0); 257 | ref PxShape shape = ref Unsafe.AsRef(shapes[0]); 258 | return (shape.GetFlags() & PxShapeFlags.SimulationShape) == PxShapeFlags.SimulationShape; 259 | } 260 | } 261 | 262 | return false; 263 | } 264 | 265 | set 266 | { 267 | var num = RigidActor.GetNbShapes(); 268 | if (num > 0) 269 | { 270 | PxShape*[] shapes = new PxShape*[num]; 271 | fixed (PxShape** p = shapes) 272 | { 273 | RigidActor.GetShapes(p, num, 0); 274 | for (var i = 0; i < num; i++) 275 | { 276 | ref PxShape shape = ref Unsafe.AsRef(shapes[i]); 277 | shape.SetFlagMut(PxShapeFlag.SimulationShape, value); 278 | } 279 | } 280 | } 281 | } 282 | } 283 | 284 | /// 285 | /// The position of the rigidbody. 286 | /// 287 | public Vector3 position 288 | { 289 | get => RigidActor.GetGlobalPose().p; 290 | set 291 | { 292 | var pose = RigidActor.GetGlobalPose(); 293 | pose.p = value; 294 | RigidActor.SetGlobalPoseMut(&pose, autowake: true); 295 | } 296 | } 297 | 298 | /// 299 | /// The rotation of the Rigidbody. 300 | /// 301 | public Quaternion rotation 302 | { 303 | get => RigidActor.GetGlobalPose().q; 304 | set 305 | { 306 | var pose = RigidActor.GetGlobalPose(); 307 | pose.q = value; 308 | RigidActor.SetGlobalPoseMut(&pose, autowake: true); 309 | } 310 | } 311 | 312 | /// 313 | /// The solverIterations determines how accurately Rigidbody joints and collision contacts are resolved. Overrides Physics.defaultSolverIterations. Must be positive. 314 | /// 315 | public int solverIterations 316 | { 317 | get 318 | { 319 | uint minPositionIters; 320 | uint* p = &minPositionIters; 321 | 322 | uint minVelocityIters; 323 | uint* _ = &minVelocityIters; 324 | 325 | RigidDynamic.GetSolverIterationCounts(p, _); 326 | 327 | return (int)*p; 328 | } 329 | set => RigidDynamic.SetSolverIterationCountsMut((uint)value, (uint)solverVelocityIterations); 330 | } 331 | 332 | /// 333 | /// The mass-normalized energy threshold, below which objects start going to sleep. 334 | /// 335 | public float sleepThreshold 336 | { 337 | get => RigidDynamic.GetSleepThreshold(); 338 | set => RigidDynamic.SetSleepThresholdMut(value); 339 | } 340 | 341 | /// 342 | /// The maximimum angular velocity of the rigidbody measured in radians per second. (Default 7) range { 0, infinity }. 343 | /// 344 | public float maxAngularVelocity 345 | { 346 | get => RigidBody.GetMaxAngularVelocity(); 347 | set => RigidBody.SetMaxAngularVelocityMut(value); 348 | } 349 | 350 | /// 351 | /// The solverVelocityIterations affects how how accurately Rigidbody joints and collision contacts are resolved. Overrides Physics.defaultSolverVelocityIterations. Must be positive. 352 | /// 353 | public int solverVelocityIterations 354 | { 355 | get 356 | { 357 | uint minPositionIters; 358 | uint* _ = &minPositionIters; 359 | 360 | uint minVelocityIters; 361 | uint* v = &minVelocityIters; 362 | 363 | RigidDynamic.GetSolverIterationCounts(_, v); 364 | 365 | return (int)*v; 366 | } 367 | set => RigidDynamic.SetSolverIterationCountsMut((uint)solverIterations, (uint)value); 368 | } 369 | 370 | /// 371 | /// Sets the mass based on the attached colliders assuming a constant density. 372 | /// 373 | /// 374 | public void SetDensity(float density) 375 | { 376 | RigidBody.ExtUpdateMassAndInertia(&density, 1, null, false); 377 | } 378 | 379 | /// 380 | /// Forces a rigidbody to sleep at least one frame. 381 | /// 382 | public void Sleep() 383 | { 384 | RigidDynamic.PutToSleepMut(); 385 | } 386 | 387 | /// 388 | /// Is the rigidbody sleeping? 389 | /// 390 | public bool IsSleeping() 391 | { 392 | return RigidDynamic.IsSleeping(); 393 | } 394 | 395 | /// 396 | /// Forces a rigidbody to wake up. 397 | /// 398 | public void WakeUp() 399 | { 400 | RigidDynamic.WakeUpMut(); 401 | } 402 | 403 | /// 404 | /// Reset the center of mass of the rigidbody. 405 | /// 406 | public void ResetCenterOfMass() 407 | { 408 | centerOfMass = Vector3.Zero; 409 | } 410 | 411 | /// 412 | /// Reset the inertia tensor value and rotation. 413 | /// 414 | public void ResetInertiaTensor() 415 | { 416 | inertiaTensor = Vector3.Zero; 417 | } 418 | 419 | /// 420 | /// Adds a force to the Rigidbody. 421 | /// 422 | /// Force vector in world coordinates. 423 | /// Type of force to apply. 424 | public void AddForce(Vector3 force, ForceMode mode) 425 | { 426 | RigidBody.AddForceMut(force.AsPxPointer(), (PxForceMode)mode, autowake: true); 427 | } 428 | 429 | /// 430 | /// Adds a force to the Rigidbody. 431 | /// 432 | /// Force vector in world coordinates. 433 | public void AddForce(Vector3 force) 434 | { 435 | RigidBody.AddForceMut(force.AsPxPointer(), PxForceMode.Force, autowake: true); 436 | } 437 | 438 | 439 | public void AddForce(float x, float y, float z, ForceMode mode) 440 | { 441 | AddForce(new Vector3(x, y, z), mode); 442 | } 443 | 444 | 445 | public void AddForce(float x, float y, float z) 446 | { 447 | AddForce(new Vector3(x, y, z)); 448 | } 449 | 450 | /// 451 | /// Adds a force to the rigidbody relative to its coordinate system. 452 | /// 453 | /// Force vector in local coordinates. 454 | /// Type of force to apply. 455 | public void AddRelativeForce(Vector3 force, ForceMode mode) 456 | { 457 | var globalPose = RigidActor.GetGlobalPose(); 458 | var globalForce = globalPose.Transform(force.AsPxPointer()); 459 | 460 | RigidBody.AddForceMut(&globalForce, (PxForceMode)mode, autowake: true); 461 | } 462 | 463 | /// 464 | /// Adds a force to the rigidbody relative to its coordinate system. 465 | /// 466 | /// Force vector in local coordinates. 467 | public void AddRelativeForce(Vector3 force) 468 | { 469 | AddRelativeForce(force, ForceMode.Force); 470 | } 471 | 472 | 473 | public void AddRelativeForce(float x, float y, float z, ForceMode mode) 474 | { 475 | AddRelativeForce(new Vector3(x, y, z), mode); 476 | } 477 | 478 | 479 | public void AddRelativeForce(float x, float y, float z) 480 | { 481 | AddRelativeForce(new Vector3(x, y, z)); 482 | } 483 | 484 | /// 485 | /// Adds a torque to the rigidbody. 486 | /// 487 | /// Torque vector in world coordinates. 488 | /// The type of torque to apply. 489 | public void AddTorque(Vector3 torque, ForceMode mode) 490 | { 491 | RigidBody.AddTorqueMut(torque.AsPxPointer(), (PxForceMode)mode, autowake: true); 492 | } 493 | 494 | /// 495 | /// Adds a torque to the rigidbody. 496 | /// 497 | /// Torque vector in world coordinates. 498 | public void AddTorque(Vector3 torque) 499 | { 500 | RigidBody.AddTorqueMut(torque.AsPxPointer(), PxForceMode.Force, autowake: true); 501 | } 502 | 503 | 504 | public void AddTorque(float x, float y, float z, ForceMode mode) 505 | { 506 | AddTorque(new Vector3(x, y, z), mode); 507 | } 508 | 509 | 510 | public void AddTorque(float x, float y, float z) 511 | { 512 | AddTorque(new Vector3(x, y, z)); 513 | } 514 | 515 | /// 516 | /// Adds a torque to the rigidbody relative to its coordinate system. 517 | /// 518 | /// Torque vector in local coordinates. 519 | /// Type of force to apply. 520 | public void AddRelativeTorque(Vector3 torque, ForceMode mode) 521 | { 522 | var globalPose = RigidActor.GetGlobalPose(); 523 | var globalToque = globalPose.Transform(torque.AsPxPointer()); 524 | 525 | RigidBody.AddTorqueMut(&globalToque, (PxForceMode)mode, autowake: true); 526 | } 527 | 528 | /// 529 | /// Adds a torque to the rigidbody relative to its coordinate system. 530 | /// 531 | /// Torque vector in local coordinates. 532 | public void AddRelativeTorque(Vector3 torque) 533 | { 534 | AddRelativeTorque(torque, ForceMode.Force); 535 | } 536 | 537 | 538 | public void AddRelativeTorque(float x, float y, float z, ForceMode mode) 539 | { 540 | AddRelativeTorque(new Vector3(x, y, z), mode); 541 | } 542 | 543 | 544 | public void AddRelativeTorque(float x, float y, float z) 545 | { 546 | AddRelativeTorque(new Vector3(x, y, z)); 547 | } 548 | 549 | /// 550 | /// Applies force at position. As a result this will apply a torque and force on the object. 551 | /// 552 | /// Force vector in world coordinates. 553 | /// Position in world coordinates. 554 | /// Type of force to apply. 555 | public void AddForceAtPosition(Vector3 force, Vector3 position, ForceMode mode) 556 | { 557 | RigidBody.ExtAddForceAtPos(force.AsPxPointer(), position.AsPxPointer(), (PxForceMode)mode, wakeup: true); 558 | } 559 | 560 | /// 561 | /// Applies force at position. As a result this will apply a torque and force on the object. 562 | /// 563 | /// Force vector in world coordinates. 564 | /// Position in world coordinates. 565 | public void AddForceAtPosition(Vector3 force, Vector3 position) 566 | { 567 | RigidBody.ExtAddForceAtPos(force.AsPxPointer(), position.AsPxPointer(), PxForceMode.Force, wakeup: true); 568 | } 569 | } 570 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Toolkit/RigidbodyConstraints.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MagicPhysX.Toolkit 4 | { 5 | /// 6 | /// Use these flags to constrain motion of Rigidbodies. 7 | /// 8 | public enum RigidbodyConstraints : int 9 | { 10 | /// 11 | /// No constraints. 12 | /// 13 | None = 0, 14 | /// 15 | /// Freeze motion along the X-axis. 16 | /// 17 | FreezePositionX = 2, 18 | /// 19 | /// Freeze motion along the Y-axis. 20 | /// 21 | FreezePositionY = 4, 22 | /// 23 | /// Freeze motion along the Z-axis. 24 | /// 25 | FreezePositionZ = 8, 26 | /// 27 | /// Freeze motion along all axes. 28 | /// 29 | FreezePosition = 14, 30 | /// 31 | /// Freeze rotation along the X-axis. 32 | /// 33 | FreezeRotationX = 16, 34 | /// 35 | /// Freeze rotation along the Y-axis. 36 | /// 37 | FreezeRotationY = 32, 38 | /// 39 | /// Freeze rotation along the Z-axis. 40 | /// 41 | FreezeRotationZ = 64, 42 | /// 43 | /// Freeze rotation along all axes. 44 | /// 45 | FreezeRotation = 112, 46 | /// 47 | /// Freeze rotation and motion along all axes. 48 | /// 49 | FreezeAll = 126, 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /samples/MagicPhysX.Toolkit/Toolkit/Transform.cs: -------------------------------------------------------------------------------- 1 | // compatible for PxTransform 2 | using MagicPhysX; 3 | using System.Numerics; 4 | using System.Runtime.CompilerServices; 5 | using System.Runtime.InteropServices; 6 | 7 | namespace MagicPhysX.Toolkit; 8 | 9 | [StructLayout(LayoutKind.Sequential)] 10 | public partial struct Transform 11 | { 12 | public Quaternion rotation; 13 | public Vector3 position; 14 | 15 | public static implicit operator PxTransform(Transform v) => Unsafe.As(ref v); 16 | public static implicit operator Transform(PxTransform v) => Unsafe.As(ref v); 17 | 18 | internal unsafe PxTransform* AsPxPointer() => (PxTransform*)Unsafe.AsPointer(ref Unsafe.AsRef(this)); 19 | } 20 | -------------------------------------------------------------------------------- /sandbox/ConsoleSandbox/BricksDoubleDomino.cs: -------------------------------------------------------------------------------- 1 | using MagicPhysX; 2 | using static MagicPhysX.NativeMethods; 3 | 4 | namespace ConsoleSandbox; 5 | 6 | public static class BricksDoubleDomino 7 | { 8 | public static unsafe void Run() 9 | { 10 | var foundation = physx_create_foundation(); 11 | 12 | // create pvd 13 | var pvd = phys_PxCreatePvd(foundation); 14 | 15 | fixed (byte* bytePointer = "127.0.0.1"u8.ToArray()) 16 | { 17 | var transport = phys_PxDefaultPvdSocketTransportCreate(bytePointer, 5425, 10); 18 | pvd->ConnectMut(transport, PxPvdInstrumentationFlags.All); 19 | } 20 | 21 | // create physics 22 | uint PX_PHYSICS_VERSION_MAJOR = 5; 23 | uint PX_PHYSICS_VERSION_MINOR = 1; 24 | uint PX_PHYSICS_VERSION_BUGFIX = 3; 25 | uint versionNumber = (PX_PHYSICS_VERSION_MAJOR << 24) + (PX_PHYSICS_VERSION_MINOR << 16) + (PX_PHYSICS_VERSION_BUGFIX << 8); 26 | 27 | var tolerancesScale = new PxTolerancesScale { length = 1, speed = 10 }; 28 | var physics = phys_PxCreatePhysics(versionNumber, foundation, &tolerancesScale, true, pvd, null); 29 | 30 | phys_PxInitExtensions(physics, pvd); 31 | 32 | var sceneDesc = PxSceneDesc_new(PxPhysics_getTolerancesScale(physics)); 33 | sceneDesc.gravity = new PxVec3 { x = 0.0f, y = -9.81f, z = 0.0f }; 34 | 35 | var dispatcher = phys_PxDefaultCpuDispatcherCreate(1, null, PxDefaultCpuDispatcherWaitForWorkMode.WaitForWork, 0); 36 | sceneDesc.cpuDispatcher = (PxCpuDispatcher*)dispatcher; 37 | sceneDesc.filterShader = get_default_simulation_filter_shader(); 38 | 39 | var scene = PxPhysics_createScene_mut(physics, &sceneDesc); 40 | 41 | // pvd client 42 | var pvdClient = scene->GetScenePvdClientMut(); 43 | if (pvdClient != null) 44 | { 45 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitConstraints, true); 46 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitContacts, true); 47 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitScenequeries, true); 48 | } 49 | 50 | var material = PxPhysics_createMaterial_mut(physics, 0.5f, 0.5f, 0.6f); 51 | 52 | // plane 53 | var plane = PxPlane_new_1(0.0f, 1.0f, 0.0f, 0.0f); 54 | var groundPlane = phys_PxCreatePlane(physics, &plane, material); 55 | 56 | PxScene_addActor_mut(scene, (PxActor*)groundPlane, null); 57 | 58 | // sphere 59 | var sphereVec = new PxVec3 60 | { 61 | x = 0.0f, 62 | y = 10.0f, 63 | z = 0.0f 64 | }; 65 | 66 | var transform1 = PxTransform_new_1(&sphereVec); 67 | var transform2 = PxTransform_new_2(PxIDENTITY.PxIdentity); 68 | var sphereGeo = PxSphereGeometry_new(1.0f); 69 | 70 | physics->CreateShapeMut( 71 | (PxGeometry*)&sphereGeo, 72 | material, 73 | false, 74 | PxShapeFlags.Visualization | PxShapeFlags.SceneQueryShape | PxShapeFlags.SimulationShape); 75 | 76 | var sphere = phys_PxCreateDynamic( 77 | physics, 78 | &transform1, 79 | (PxGeometry*)&sphereGeo, 80 | material, 81 | 10.0f, 82 | &transform2); 83 | 84 | PxScene_addActor_mut(scene, (PxActor*)sphere, null); 85 | 86 | // boxes 87 | for (var i = 0; i < 30; i++) 88 | { 89 | var boxVec = new PxVec3 90 | { 91 | x = 1f + i * 3.05f, 92 | y = 1.5f, 93 | z = 0.0f 94 | }; 95 | 96 | var boxTransform1 = PxTransform_new_1(&boxVec); 97 | var boxTransform2 = PxTransform_new_2(PxIDENTITY.PxIdentity); 98 | var boxGeo = PxBoxGeometry_new(0.5f, 1.5f, 1.0f); 99 | 100 | physics->CreateShapeMut( 101 | (PxGeometry*)&boxGeo, 102 | material, 103 | false, 104 | PxShapeFlags.Visualization | PxShapeFlags.SceneQueryShape | PxShapeFlags.SimulationShape); 105 | 106 | var box = phys_PxCreateDynamic( 107 | physics, 108 | &boxTransform1, 109 | (PxGeometry*)&boxGeo, 110 | material, 111 | 10.0f, 112 | &boxTransform2); 113 | 114 | PxScene_addActor_mut(scene, (PxActor*)box, null); 115 | } 116 | 117 | // simulate 118 | Console.WriteLine("Start simulate"); 119 | 120 | for (var i = 0; i < 1400; i++) 121 | { 122 | PxScene_simulate_mut(scene, 1.0f / 30.0f, null, null, 0, true); 123 | uint error = 0; 124 | PxScene_fetchResults_mut(scene, true, &error); 125 | 126 | Console.SetCursorPosition(0, Console.CursorTop); 127 | Console.Write($"Frame: {i}"); 128 | } 129 | 130 | Console.WriteLine("\nDone"); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /sandbox/ConsoleSandbox/ConsoleSandbox.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | enable 7 | enable 8 | true 9 | false 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /sandbox/ConsoleSandbox/DistanceJoint.cs: -------------------------------------------------------------------------------- 1 | using MagicPhysX; 2 | using System.Runtime.CompilerServices; 3 | using static MagicPhysX.NativeMethods; 4 | 5 | namespace ConsoleSandbox; 6 | 7 | public static class DistanceJoint 8 | { 9 | public static unsafe void Run() 10 | { 11 | var foundation = physx_create_foundation(); 12 | 13 | // create pvd 14 | var pvd = phys_PxCreatePvd(foundation); 15 | 16 | fixed (byte* bytePointer = "127.0.0.1"u8.ToArray()) 17 | { 18 | var transport = phys_PxDefaultPvdSocketTransportCreate(bytePointer, 5425, 10); 19 | pvd->ConnectMut(transport, PxPvdInstrumentationFlags.All); 20 | } 21 | 22 | // create physics 23 | uint PX_PHYSICS_VERSION_MAJOR = 5; 24 | uint PX_PHYSICS_VERSION_MINOR = 1; 25 | uint PX_PHYSICS_VERSION_BUGFIX = 3; 26 | uint versionNumber = (PX_PHYSICS_VERSION_MAJOR << 24) + (PX_PHYSICS_VERSION_MINOR << 16) + (PX_PHYSICS_VERSION_BUGFIX << 8); 27 | 28 | var tolerancesScale = new PxTolerancesScale { length = 1, speed = 10 }; 29 | var physics = phys_PxCreatePhysics(versionNumber, foundation, &tolerancesScale, true, pvd, null); 30 | 31 | phys_PxInitExtensions(physics, pvd); 32 | 33 | var sceneDesc = PxSceneDesc_new(PxPhysics_getTolerancesScale(physics)); 34 | sceneDesc.gravity = new PxVec3 { x = 0.0f, y = -9.81f, z = 0.0f }; 35 | 36 | var dispatcher = phys_PxDefaultCpuDispatcherCreate(1, null, PxDefaultCpuDispatcherWaitForWorkMode.WaitForWork, 0); 37 | sceneDesc.cpuDispatcher = (PxCpuDispatcher*)dispatcher; 38 | sceneDesc.filterShader = get_default_simulation_filter_shader(); 39 | 40 | var scene = PxPhysics_createScene_mut(physics, &sceneDesc); 41 | 42 | // pvd client 43 | var pvdClient = scene->GetScenePvdClientMut(); 44 | if (pvdClient != null) 45 | { 46 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitConstraints, true); 47 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitContacts, true); 48 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitScenequeries, true); 49 | } 50 | 51 | var material = PxPhysics_createMaterial_mut(physics, 0.5f, 0.5f, 0.6f); 52 | 53 | // plane 54 | var plane = PxPlane_new_1(0.0f, 1.0f, 0.0f, 0.0f); 55 | var groundPlane = phys_PxCreatePlane(physics, &plane, material); 56 | 57 | PxScene_addActor_mut(scene, (PxActor*)groundPlane, null); 58 | 59 | // box1 60 | var box1Position = new PxVec3 61 | { 62 | x = 0.0f, 63 | y = 20.0f, 64 | z = 0.0f 65 | }; 66 | 67 | var box1Transform = PxTransform_new_1(&box1Position); 68 | var box1ShapeOffset = PxTransform_new_2(PxIDENTITY.PxIdentity); 69 | var box1Geometry = PxBoxGeometry_new(2.0f, 2.0f, 2.0f); 70 | 71 | physics->CreateShapeMut( 72 | (PxGeometry*)&box1Geometry, 73 | material, 74 | false, 75 | PxShapeFlags.Visualization | PxShapeFlags.SceneQueryShape | PxShapeFlags.SimulationShape); 76 | 77 | var box1 = phys_PxCreateDynamic( 78 | physics, 79 | &box1Transform, 80 | (PxGeometry*)&box1Geometry, 81 | material, 82 | 1.0f, 83 | &box1ShapeOffset); 84 | 85 | PxScene_addActor_mut(scene, (PxActor*)box1, null); 86 | 87 | // box2 88 | var box2Position = new PxVec3 89 | { 90 | x = 10.0f, 91 | y = 20.0f, 92 | z = 0.0f 93 | }; 94 | 95 | var box2Transform = PxTransform_new_1(&box2Position); 96 | var box2ShapeOffset = PxTransform_new_2(PxIDENTITY.PxIdentity); 97 | var box2Geometry = PxBoxGeometry_new(4.0f, 2.0f, 2.0f); 98 | 99 | physics->CreateShapeMut( 100 | (PxGeometry*)&box2Geometry, 101 | material, 102 | false, 103 | PxShapeFlags.Visualization | PxShapeFlags.SceneQueryShape | PxShapeFlags.SimulationShape); 104 | 105 | var box2 = phys_PxCreateDynamic( 106 | physics, 107 | &box2Transform, 108 | (PxGeometry*)&box2Geometry, 109 | material, 110 | 1.0f, 111 | &box2ShapeOffset); 112 | 113 | PxScene_addActor_mut(scene, (PxActor*)box2, null); 114 | 115 | // box3 116 | var box3Position = new PxVec3 117 | { 118 | x = 10.0f, 119 | y = 17.0f, 120 | z = 0.0f 121 | }; 122 | 123 | var box3Transform = PxTransform_new_1(&box3Position); 124 | var box3ShapeOffset = PxTransform_new_2(PxIDENTITY.PxIdentity); 125 | var box3Geometry = PxBoxGeometry_new(1.0f, 1.0f, 1.0f); 126 | 127 | physics->CreateShapeMut( 128 | (PxGeometry*)&box3Geometry, 129 | material, 130 | false, 131 | PxShapeFlags.Visualization | PxShapeFlags.SceneQueryShape | PxShapeFlags.SimulationShape); 132 | 133 | var box3 = phys_PxCreateStatic( 134 | physics, 135 | &box3Transform, 136 | (PxGeometry*)&box3Geometry, 137 | material, 138 | &box3ShapeOffset); 139 | 140 | PxScene_addActor_mut(scene, (PxActor*)box3, null); 141 | 142 | // joint1 143 | var anchor1 = new PxVec3 144 | { 145 | x = 6.0f, 146 | y = 0.0f, 147 | z = 0.0f 148 | }; 149 | 150 | var anchor2 = new PxVec3 151 | { 152 | x = -6.0f, 153 | y = 0.0f, 154 | z = 0.0f 155 | }; 156 | 157 | var transform1 = PxTransform_new_1((PxVec3*)Unsafe.AsPointer(ref anchor1)); 158 | var transform2 = PxTransform_new_1((PxVec3*)Unsafe.AsPointer(ref anchor2)); 159 | 160 | var joint1 = phys_PxDistanceJointCreate( 161 | physics, 162 | (PxRigidActor*)box1, 163 | &transform1, 164 | (PxRigidActor*)box2, 165 | &transform2); 166 | 167 | // simulate 168 | Console.WriteLine("Start simulate"); 169 | 170 | for (var i = 0; i < 300; i++) 171 | { 172 | PxScene_simulate_mut(scene, 1.0f / 30.0f, null, null, 0, true); 173 | uint error = 0; 174 | PxScene_fetchResults_mut(scene, true, &error); 175 | 176 | Console.SetCursorPosition(0, Console.CursorTop); 177 | Console.Write($"Frame: {i}"); 178 | } 179 | 180 | Console.WriteLine("\nDone"); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /sandbox/ConsoleSandbox/FixedJoint.cs: -------------------------------------------------------------------------------- 1 | using MagicPhysX; 2 | using System.Runtime.CompilerServices; 3 | using static MagicPhysX.NativeMethods; 4 | 5 | namespace ConsoleSandbox; 6 | 7 | public static class FixedJoint 8 | { 9 | public static unsafe void Run() 10 | { 11 | var foundation = physx_create_foundation(); 12 | 13 | // create pvd 14 | var pvd = phys_PxCreatePvd(foundation); 15 | 16 | fixed (byte* bytePointer = "127.0.0.1"u8.ToArray()) 17 | { 18 | var transport = phys_PxDefaultPvdSocketTransportCreate(bytePointer, 5425, 10); 19 | pvd->ConnectMut(transport, PxPvdInstrumentationFlags.All); 20 | } 21 | 22 | // create physics 23 | uint PX_PHYSICS_VERSION_MAJOR = 5; 24 | uint PX_PHYSICS_VERSION_MINOR = 1; 25 | uint PX_PHYSICS_VERSION_BUGFIX = 3; 26 | uint versionNumber = (PX_PHYSICS_VERSION_MAJOR << 24) + (PX_PHYSICS_VERSION_MINOR << 16) + (PX_PHYSICS_VERSION_BUGFIX << 8); 27 | 28 | var tolerancesScale = new PxTolerancesScale { length = 1, speed = 10 }; 29 | var physics = phys_PxCreatePhysics(versionNumber, foundation, &tolerancesScale, true, pvd, null); 30 | 31 | phys_PxInitExtensions(physics, pvd); 32 | 33 | var sceneDesc = PxSceneDesc_new(PxPhysics_getTolerancesScale(physics)); 34 | sceneDesc.gravity = new PxVec3 { x = 0.0f, y = -9.81f, z = 0.0f }; 35 | 36 | var dispatcher = phys_PxDefaultCpuDispatcherCreate(1, null, PxDefaultCpuDispatcherWaitForWorkMode.WaitForWork, 0); 37 | sceneDesc.cpuDispatcher = (PxCpuDispatcher*)dispatcher; 38 | sceneDesc.filterShader = get_default_simulation_filter_shader(); 39 | 40 | var scene = PxPhysics_createScene_mut(physics, &sceneDesc); 41 | 42 | // pvd client 43 | var pvdClient = scene->GetScenePvdClientMut(); 44 | if (pvdClient != null) 45 | { 46 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitConstraints, true); 47 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitContacts, true); 48 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitScenequeries, true); 49 | } 50 | 51 | var material = PxPhysics_createMaterial_mut(physics, 0.5f, 0.5f, 0.6f); 52 | 53 | // plane 54 | var plane = PxPlane_new_1(0.0f, 1.0f, 0.0f, 0.0f); 55 | var groundPlane = phys_PxCreatePlane(physics, &plane, material); 56 | 57 | PxScene_addActor_mut(scene, (PxActor*)groundPlane, null); 58 | 59 | var height = 10.0f; 60 | 61 | // box1 62 | var box1Position = new PxVec3 63 | { 64 | x = 0.0f, 65 | y = 1.1f + height, 66 | z = 0.0f 67 | }; 68 | 69 | var box1Transform = PxTransform_new_1(&box1Position); 70 | var box1ShapeOffset = PxTransform_new_2(PxIDENTITY.PxIdentity); 71 | var box1Geometry = PxBoxGeometry_new(1.0f, 0.1f, 0.2f); 72 | 73 | physics->CreateShapeMut( 74 | (PxGeometry*)&box1Geometry, 75 | material, 76 | false, 77 | PxShapeFlags.Visualization | PxShapeFlags.SceneQueryShape | PxShapeFlags.SimulationShape); 78 | 79 | var box1 = phys_PxCreateDynamic( 80 | physics, 81 | &box1Transform, 82 | (PxGeometry*)&box1Geometry, 83 | material, 84 | 1.0f, 85 | &box1ShapeOffset); 86 | 87 | PxScene_addActor_mut(scene, (PxActor*)box1, null); 88 | 89 | // box2 90 | var box2Position = new PxVec3 91 | { 92 | x = 0.9f, 93 | y = 0.6f + height, 94 | z = 0.0f 95 | }; 96 | 97 | var box2Transform = PxTransform_new_1(&box2Position); 98 | var box2ShapeOffset = PxTransform_new_2(PxIDENTITY.PxIdentity); 99 | var box2Geometry = PxBoxGeometry_new(0.1f, 0.4f, 0.2f); 100 | 101 | physics->CreateShapeMut( 102 | (PxGeometry*)&box2Geometry, 103 | material, 104 | false, 105 | PxShapeFlags.Visualization | PxShapeFlags.SceneQueryShape | PxShapeFlags.SimulationShape); 106 | 107 | var box2 = phys_PxCreateDynamic( 108 | physics, 109 | &box2Transform, 110 | (PxGeometry*)&box2Geometry, 111 | material, 112 | 1.0f, 113 | &box2ShapeOffset); 114 | 115 | PxScene_addActor_mut(scene, (PxActor*)box2, null); 116 | 117 | // joint1 118 | var anchor1 = new PxVec3 119 | { 120 | x = 1.05f, 121 | y = 0.1f, 122 | z = 0.0f 123 | }; 124 | 125 | var pos1 = new PxVec3 126 | { 127 | x = anchor1.x - box1Position.x, 128 | y = anchor1.y - box1Position.y, 129 | z = anchor1.z - box1Position.z 130 | }; 131 | 132 | var transform1 = PxTransform_new_1((PxVec3*)Unsafe.AsPointer(ref pos1)); 133 | 134 | var pos2 = new PxVec3 135 | { 136 | x = anchor1.x - box2Position.x, 137 | y = anchor1.y - box2Position.y, 138 | z = anchor1.z - box2Position.z 139 | }; 140 | 141 | var transform2 = PxTransform_new_1((PxVec3*)Unsafe.AsPointer(ref pos2)); 142 | 143 | var joint1 = phys_PxFixedJointCreate( 144 | physics, 145 | (PxRigidActor*)box1, 146 | &transform1, 147 | (PxRigidActor*)box2, 148 | &transform2); 149 | 150 | PxJoint_setBreakForce_mut((PxJoint*)joint1, 0.1f, 0.1f); 151 | 152 | // box3 153 | var box3Position = new PxVec3 154 | { 155 | x = 0.0f + 5.0f, 156 | y = 1.1f + height, 157 | z = 0.0f 158 | }; 159 | 160 | var box3Transform = PxTransform_new_1(&box3Position); 161 | var box3ShapeOffset = PxTransform_new_2(PxIDENTITY.PxIdentity); 162 | var box3Geometry = PxBoxGeometry_new(1.0f, 0.1f, 0.2f); 163 | 164 | physics->CreateShapeMut( 165 | (PxGeometry*)&box3Geometry, 166 | material, 167 | false, 168 | PxShapeFlags.Visualization | PxShapeFlags.SceneQueryShape | PxShapeFlags.SimulationShape); 169 | 170 | var box3 = phys_PxCreateDynamic( 171 | physics, 172 | &box3Transform, 173 | (PxGeometry*)&box3Geometry, 174 | material, 175 | 1.0f, 176 | &box3ShapeOffset); 177 | 178 | PxScene_addActor_mut(scene, (PxActor*)box3, null); 179 | 180 | // box4 181 | var box4Position = new PxVec3 182 | { 183 | x = 0.9f + 5.0f, 184 | y = 0.6f + height, 185 | z = 0.0f 186 | }; 187 | 188 | var box4Transform = PxTransform_new_1(&box4Position); 189 | var box4ShapeOffset = PxTransform_new_2(PxIDENTITY.PxIdentity); 190 | var box4Geometry = PxBoxGeometry_new(0.1f, 0.4f, 0.2f); 191 | 192 | physics->CreateShapeMut( 193 | (PxGeometry*)&box4Geometry, 194 | material, 195 | false, 196 | PxShapeFlags.Visualization | PxShapeFlags.SceneQueryShape | PxShapeFlags.SimulationShape); 197 | 198 | var box4 = phys_PxCreateDynamic( 199 | physics, 200 | &box4Transform, 201 | (PxGeometry*)&box4Geometry, 202 | material, 203 | 1.0f, 204 | &box4ShapeOffset); 205 | 206 | PxScene_addActor_mut(scene, (PxActor*)box4, null); 207 | 208 | // joint2 209 | var anchor2 = new PxVec3 210 | { 211 | x = 1.05f, 212 | y = 0.1f, 213 | z = 0.0f 214 | }; 215 | 216 | var pos3 = new PxVec3 217 | { 218 | x = anchor2.x - box3Position.x, 219 | y = anchor2.y - box3Position.y, 220 | z = anchor2.z - box3Position.z 221 | }; 222 | 223 | var transform3 = PxTransform_new_1((PxVec3*)Unsafe.AsPointer(ref pos3)); 224 | 225 | var pos4 = new PxVec3 226 | { 227 | x = anchor2.x - box4Position.x, 228 | y = anchor2.y - box4Position.y, 229 | z = anchor2.z - box4Position.z 230 | }; 231 | 232 | var transform4 = PxTransform_new_1((PxVec3*)Unsafe.AsPointer(ref pos4)); 233 | 234 | var joint2 = phys_PxFixedJointCreate( 235 | physics, 236 | (PxRigidActor*)box3, 237 | &transform3, 238 | (PxRigidActor*)box4, 239 | &transform4); 240 | 241 | // simulate 242 | Console.WriteLine("Start simulate"); 243 | 244 | for (var i = 0; i < 200; i++) 245 | { 246 | PxScene_simulate_mut(scene, 1.0f / 30.0f, null, null, 0, true); 247 | uint error = 0; 248 | PxScene_fetchResults_mut(scene, true, &error); 249 | 250 | Console.SetCursorPosition(0, Console.CursorTop); 251 | Console.Write($"Frame: {i}"); 252 | } 253 | 254 | Console.WriteLine("\nDone"); 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /sandbox/ConsoleSandbox/Geometries.cs: -------------------------------------------------------------------------------- 1 | using MagicPhysX; 2 | using System.Runtime.CompilerServices; 3 | using static MagicPhysX.NativeMethods; 4 | 5 | namespace ConsoleSandbox; 6 | 7 | public static class Geometries 8 | { 9 | public static unsafe void Run() 10 | { 11 | var foundation = physx_create_foundation(); 12 | 13 | // create pvd 14 | var pvd = phys_PxCreatePvd(foundation); 15 | 16 | fixed (byte* bytePointer = "127.0.0.1"u8.ToArray()) 17 | { 18 | var transport = phys_PxDefaultPvdSocketTransportCreate(bytePointer, 5425, 10); 19 | pvd->ConnectMut(transport, PxPvdInstrumentationFlags.All); 20 | } 21 | 22 | // create physics 23 | uint PX_PHYSICS_VERSION_MAJOR = 5; 24 | uint PX_PHYSICS_VERSION_MINOR = 1; 25 | uint PX_PHYSICS_VERSION_BUGFIX = 3; 26 | uint versionNumber = (PX_PHYSICS_VERSION_MAJOR << 24) + (PX_PHYSICS_VERSION_MINOR << 16) + (PX_PHYSICS_VERSION_BUGFIX << 8); 27 | 28 | var tolerancesScale = new PxTolerancesScale { length = 1, speed = 10 }; 29 | var physics = phys_PxCreatePhysics(versionNumber, foundation, &tolerancesScale, true, pvd, null); 30 | 31 | phys_PxInitExtensions(physics, pvd); 32 | 33 | var sceneDesc = PxSceneDesc_new(PxPhysics_getTolerancesScale(physics)); 34 | sceneDesc.gravity = new PxVec3 { x = 0.0f, y = -9.81f, z = 0.0f }; 35 | 36 | var dispatcher = phys_PxDefaultCpuDispatcherCreate(1, null, PxDefaultCpuDispatcherWaitForWorkMode.WaitForWork, 0); 37 | sceneDesc.cpuDispatcher = (PxCpuDispatcher*)dispatcher; 38 | sceneDesc.filterShader = get_default_simulation_filter_shader(); 39 | 40 | var scene = PxPhysics_createScene_mut(physics, &sceneDesc); 41 | 42 | // pvd client 43 | var pvdClient = scene->GetScenePvdClientMut(); 44 | if (pvdClient != null) 45 | { 46 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitConstraints, true); 47 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitContacts, true); 48 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitScenequeries, true); 49 | } 50 | 51 | var material = PxPhysics_createMaterial_mut(physics, 0.5f, 0.5f, 0.6f); 52 | 53 | // plane 54 | var plane = PxPlane_new_1(0.0f, 1.0f, 0.0f, 0.0f); 55 | var groundPlane = phys_PxCreatePlane(physics, &plane, material); 56 | 57 | PxScene_addActor_mut(scene, (PxActor*)groundPlane, null); 58 | 59 | // sphere 60 | var sphereVec = new PxVec3 61 | { 62 | x = 0.0f, 63 | y = 3.0f, 64 | z = 0.0f 65 | }; 66 | 67 | var sphereTransform = PxTransform_new_1(&sphereVec); 68 | var sphereShapeOffset = PxTransform_new_2(PxIDENTITY.PxIdentity); 69 | var sphereGeo = PxSphereGeometry_new(1.0f); 70 | 71 | physics->CreateShapeMut( 72 | (PxGeometry*)&sphereGeo, 73 | material, 74 | false, 75 | PxShapeFlags.Visualization | PxShapeFlags.SceneQueryShape | PxShapeFlags.SimulationShape); 76 | 77 | var sphere = phys_PxCreateDynamic( 78 | physics, 79 | &sphereTransform, 80 | (PxGeometry*)&sphereGeo, 81 | material, 82 | 10.0f, 83 | &sphereShapeOffset); 84 | 85 | PxScene_addActor_mut(scene, (PxActor*)sphere, null); 86 | 87 | // capsule 88 | var capsuleVec = new PxVec3 89 | { 90 | x = 5.0f, 91 | y = 5.0f, 92 | z = 0.0f 93 | }; 94 | 95 | var capsuleRotation = new PxVec3 { x = 0, y = 0, z = 1.0f }; 96 | var capsuleOrientation = PxQuat_new_4((float)Math.PI / 2, &capsuleRotation); 97 | 98 | var capsuleTransform = PxTransform_new_1(&capsuleVec); 99 | var capsuleShapeOffset = PxTransform_new_3(&capsuleOrientation); 100 | var capsuleGeo = PxCapsuleGeometry_new(1.0f, 2.0f); 101 | 102 | physics->CreateShapeMut( 103 | (PxGeometry*)&capsuleGeo, 104 | material, 105 | false, 106 | PxShapeFlags.Visualization | PxShapeFlags.SceneQueryShape | PxShapeFlags.SimulationShape); 107 | 108 | var capsule = phys_PxCreateDynamic( 109 | physics, 110 | &capsuleTransform, 111 | (PxGeometry*)&capsuleGeo, 112 | material, 113 | 10.0f, 114 | &capsuleShapeOffset); 115 | 116 | PxScene_addActor_mut(scene, (PxActor*)capsule, null); 117 | 118 | // box1 119 | var box1Position = new PxVec3 120 | { 121 | x = -5.0f, 122 | y = 5.0f, 123 | z = 0.0f 124 | }; 125 | 126 | var box1Transform = PxTransform_new_1(&box1Position); 127 | var box1ShapeOffset = PxTransform_new_2(PxIDENTITY.PxIdentity); 128 | var box1Geometry = PxBoxGeometry_new(1.0f, 1.0f, 1.0f); 129 | 130 | physics->CreateShapeMut( 131 | (PxGeometry*)&box1Geometry, 132 | material, 133 | false, 134 | PxShapeFlags.Visualization | PxShapeFlags.SceneQueryShape | PxShapeFlags.SimulationShape); 135 | 136 | var box1 = phys_PxCreateDynamic( 137 | physics, 138 | &box1Transform, 139 | (PxGeometry*)&box1Geometry, 140 | material, 141 | 1.0f, 142 | &box1ShapeOffset); 143 | 144 | PxScene_addActor_mut(scene, (PxActor*)box1, null); 145 | 146 | // box2 147 | var box2Position = new PxVec3 148 | { 149 | x = -5.0f, 150 | y = 5.0f, 151 | z = -5.0f 152 | }; 153 | 154 | var box2Transform = PxTransform_new_1(&box2Position); 155 | var box2ShapeOffset = PxTransform_new_2(PxIDENTITY.PxIdentity); 156 | var box2Geometry = PxBoxGeometry_new(1.0f, 1.0f, 1.0f); 157 | 158 | physics->CreateShapeMut( 159 | (PxGeometry*)&box2Geometry, 160 | material, 161 | false, 162 | PxShapeFlags.Visualization | PxShapeFlags.SceneQueryShape | PxShapeFlags.SimulationShape); 163 | 164 | var box2 = phys_PxCreateStatic( 165 | physics, 166 | &box2Transform, 167 | (PxGeometry*)&box2Geometry, 168 | material, 169 | &box2ShapeOffset); 170 | 171 | PxScene_addActor_mut(scene, (PxActor*)box2, null); 172 | 173 | // simulate 174 | Console.WriteLine("Start simulate"); 175 | 176 | for (var i = 0; i < 100; i++) 177 | { 178 | PxScene_simulate_mut(scene, 1.0f / 30.0f, null, null, 0, true); 179 | uint error = 0; 180 | PxScene_fetchResults_mut(scene, true, &error); 181 | 182 | Console.SetCursorPosition(0, Console.CursorTop); 183 | Console.Write($"Frame: {i}"); 184 | } 185 | 186 | Console.WriteLine("\nDone"); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /sandbox/ConsoleSandbox/Program.cs: -------------------------------------------------------------------------------- 1 | using ConsoleSandbox; 2 | 3 | //ReadMeSample.Run(); 4 | //BricksDoubleDomino.Run(); 5 | //FixedJoint.Run(); 6 | //RaycastSingle.Run(); 7 | //Geometries.Run(); 8 | //RevoluteJoint.Run(); 9 | //SphericalJoint.Run(); 10 | DistanceJoint.Run(); 11 | -------------------------------------------------------------------------------- /sandbox/ConsoleSandbox/RaycastSingle.cs: -------------------------------------------------------------------------------- 1 | using MagicPhysX; 2 | using System.Runtime.CompilerServices; 3 | using static MagicPhysX.NativeMethods; 4 | 5 | namespace ConsoleSandbox; 6 | 7 | public static class RaycastSingle 8 | { 9 | public static unsafe void Run() 10 | { 11 | var foundation = physx_create_foundation(); 12 | 13 | // create pvd 14 | var pvd = phys_PxCreatePvd(foundation); 15 | 16 | fixed (byte* bytePointer = "127.0.0.1"u8.ToArray()) 17 | { 18 | var transport = phys_PxDefaultPvdSocketTransportCreate(bytePointer, 5425, 10); 19 | pvd->ConnectMut(transport, PxPvdInstrumentationFlags.All); 20 | } 21 | 22 | // create physics 23 | uint PX_PHYSICS_VERSION_MAJOR = 5; 24 | uint PX_PHYSICS_VERSION_MINOR = 1; 25 | uint PX_PHYSICS_VERSION_BUGFIX = 3; 26 | uint versionNumber = (PX_PHYSICS_VERSION_MAJOR << 24) + (PX_PHYSICS_VERSION_MINOR << 16) + (PX_PHYSICS_VERSION_BUGFIX << 8); 27 | 28 | var tolerancesScale = new PxTolerancesScale { length = 1, speed = 10 }; 29 | var physics = phys_PxCreatePhysics(versionNumber, foundation, &tolerancesScale, true, pvd, null); 30 | 31 | phys_PxInitExtensions(physics, pvd); 32 | 33 | var sceneDesc = PxSceneDesc_new(PxPhysics_getTolerancesScale(physics)); 34 | sceneDesc.gravity = new PxVec3 { x = 0.0f, y = -9.81f, z = 0.0f }; 35 | 36 | var dispatcher = phys_PxDefaultCpuDispatcherCreate(1, null, PxDefaultCpuDispatcherWaitForWorkMode.WaitForWork, 0); 37 | sceneDesc.cpuDispatcher = (PxCpuDispatcher*)dispatcher; 38 | sceneDesc.filterShader = get_default_simulation_filter_shader(); 39 | 40 | var scene = PxPhysics_createScene_mut(physics, &sceneDesc); 41 | 42 | // pvd client 43 | var pvdClient = scene->GetScenePvdClientMut(); 44 | if (pvdClient != null) 45 | { 46 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitConstraints, true); 47 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitContacts, true); 48 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitScenequeries, true); 49 | } 50 | 51 | var material = PxPhysics_createMaterial_mut(physics, 0.5f, 0.5f, 0.6f); 52 | 53 | // plane 54 | var plane = PxPlane_new_1(0.0f, 1.0f, 0.0f, 0.0f); 55 | var groundPlane = phys_PxCreatePlane(physics, &plane, material); 56 | 57 | PxScene_addActor_mut(scene, (PxActor*)groundPlane, null); 58 | 59 | // box1 60 | var box1Position = new PxVec3 61 | { 62 | x = 0f, 63 | y = 0f, 64 | z = 0f 65 | }; 66 | 67 | var box1Transform = PxTransform_new_1(&box1Position); 68 | var box1ShapeOffset = PxTransform_new_2(PxIDENTITY.PxIdentity); 69 | var box1Geometry = PxBoxGeometry_new(1.0f, 1.0f, 1.0f); 70 | 71 | physics->CreateShapeMut( 72 | (PxGeometry*)&box1Geometry, 73 | material, 74 | false, 75 | PxShapeFlags.Visualization | PxShapeFlags.SceneQueryShape | PxShapeFlags.SimulationShape); 76 | 77 | var box1 = phys_PxCreateDynamic( 78 | physics, 79 | &box1Transform, 80 | (PxGeometry*)&box1Geometry, 81 | material, 82 | 1.0f, 83 | &box1ShapeOffset); 84 | 85 | PxScene_addActor_mut(scene, (PxActor*)box1, null); 86 | 87 | var origin = new PxVec3 88 | { 89 | x = 3.0f, 90 | y = 3.0f, 91 | z = 3.0f 92 | }; 93 | 94 | var direction1 = new PxVec3 95 | { 96 | x = -1.0f, 97 | y = -1.0f, 98 | z = -1.0f 99 | }; 100 | 101 | var normalizedDirection1 = direction1.GetNormalized(); 102 | 103 | var outputFlags1 = PxHitFlags.Default; 104 | var hit1 = new PxRaycastHit(); 105 | var filterData1 = PxQueryFilterData_new(); 106 | 107 | var result1 = scene->QueryExtRaycastSingle( 108 | (PxVec3*)Unsafe.AsPointer(ref origin), 109 | (PxVec3*)Unsafe.AsPointer(ref normalizedDirection1), 110 | 10.0f, 111 | outputFlags1, 112 | &hit1, 113 | &filterData1, 114 | null, 115 | null); 116 | 117 | Console.WriteLine($"Raycast result1: {result1}"); 118 | Console.WriteLine($" hit.position=x:{hit1.position.x},y:{hit1.position.y},z:{hit1.position.z}"); 119 | 120 | var direction2 = new PxVec3 121 | { 122 | x = 1.0f, 123 | y = 1.0f, 124 | z = 1.0f 125 | }; 126 | 127 | var normalizedDirection2 = direction2.GetNormalized(); 128 | 129 | var outputFlags2 = PxHitFlags.Default; 130 | var hit2 = new PxRaycastHit(); 131 | var filterData2 = PxQueryFilterData_new(); 132 | 133 | var result2 = scene->QueryExtRaycastSingle( 134 | (PxVec3*)Unsafe.AsPointer(ref origin), 135 | (PxVec3*)Unsafe.AsPointer(ref normalizedDirection2), 136 | 10.0f, 137 | outputFlags2, 138 | &hit2, 139 | &filterData2, 140 | null, 141 | null); 142 | 143 | Console.WriteLine($"Raycast result2: {result2}"); 144 | 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /sandbox/ConsoleSandbox/ReadMeSample.cs: -------------------------------------------------------------------------------- 1 | using MagicPhysX; // for enable Extension Methods. 2 | using static MagicPhysX.NativeMethods; // recommend to use C API. 3 | 4 | public static class ReadMeSample 5 | { 6 | public static unsafe void Run() 7 | { 8 | // create foundation(allocator, logging, etc...) 9 | var foundation = physx_create_foundation(); 10 | 11 | // create physics system 12 | var physics = physx_create_physics(foundation); 13 | 14 | // create physics scene settings 15 | var sceneDesc = PxSceneDesc_new(PxPhysics_getTolerancesScale(physics)); 16 | 17 | // you can create PhysX primitive(PxVec3, etc...) by C# struct 18 | sceneDesc.gravity = new PxVec3 { x = 0.0f, y = -9.81f, z = 0.0f }; 19 | 20 | var dispatcher = phys_PxDefaultCpuDispatcherCreate(1, null, PxDefaultCpuDispatcherWaitForWorkMode.WaitForWork, 0); 21 | sceneDesc.cpuDispatcher = (PxCpuDispatcher*)dispatcher; 22 | sceneDesc.filterShader = get_default_simulation_filter_shader(); 23 | 24 | // create physics scene 25 | var scene = physics->CreateSceneMut(&sceneDesc); 26 | 27 | var material = physics->CreateMaterialMut(0.5f, 0.5f, 0.6f); 28 | 29 | // create plane and add to scene 30 | var plane = PxPlane_new_1(0.0f, 1.0f, 0.0f, 0.0f); 31 | var groundPlane = physics->PhysPxCreatePlane(&plane, material); 32 | scene->AddActorMut((PxActor*)groundPlane, null); 33 | 34 | // create sphere and add to scene 35 | var sphereGeo = PxSphereGeometry_new(10.0f); 36 | var vec3 = new PxVec3 { x = 0.0f, y = 40.0f, z = 100.0f }; 37 | var transform = PxTransform_new_1(&vec3); 38 | var identity = PxTransform_new_2(PxIDENTITY.PxIdentity); 39 | var sphere = physics->PhysPxCreateDynamic(&transform, (PxGeometry*)&sphereGeo, material, 10.0f, &identity); 40 | PxRigidBody_setAngularDamping_mut((PxRigidBody*)sphere, 0.5f); 41 | scene->AddActorMut((PxActor*)sphere, null); 42 | 43 | // simulate scene 44 | for (int i = 0; i < 300; i++) 45 | { 46 | // 30fps update 47 | scene->SimulateMut(1.0f / 30.0f, null, null, 0, true); 48 | uint error = 0; 49 | scene->FetchResultsMut(true, &error); 50 | 51 | // output to console(frame-count: position-y) 52 | var pose = PxRigidActor_getGlobalPose((PxRigidActor*)sphere); 53 | Console.WriteLine($"{i:000}: {pose.p.y}"); 54 | } 55 | 56 | // release resources 57 | PxScene_release_mut(scene); 58 | PxDefaultCpuDispatcher_release_mut(dispatcher); 59 | PxPhysics_release_mut(physics); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /sandbox/ConsoleSandbox/RevoluteJoint.cs: -------------------------------------------------------------------------------- 1 | using MagicPhysX; 2 | using System.Runtime.CompilerServices; 3 | using static MagicPhysX.NativeMethods; 4 | 5 | namespace ConsoleSandbox; 6 | 7 | public static class RevoluteJoint 8 | { 9 | public static unsafe void Run() 10 | { 11 | var foundation = physx_create_foundation(); 12 | 13 | // create pvd 14 | var pvd = phys_PxCreatePvd(foundation); 15 | 16 | fixed (byte* bytePointer = "127.0.0.1"u8.ToArray()) 17 | { 18 | var transport = phys_PxDefaultPvdSocketTransportCreate(bytePointer, 5425, 10); 19 | pvd->ConnectMut(transport, PxPvdInstrumentationFlags.All); 20 | } 21 | 22 | // create physics 23 | uint PX_PHYSICS_VERSION_MAJOR = 5; 24 | uint PX_PHYSICS_VERSION_MINOR = 1; 25 | uint PX_PHYSICS_VERSION_BUGFIX = 3; 26 | uint versionNumber = (PX_PHYSICS_VERSION_MAJOR << 24) + (PX_PHYSICS_VERSION_MINOR << 16) + (PX_PHYSICS_VERSION_BUGFIX << 8); 27 | 28 | var tolerancesScale = new PxTolerancesScale { length = 1, speed = 10 }; 29 | var physics = phys_PxCreatePhysics(versionNumber, foundation, &tolerancesScale, true, pvd, null); 30 | 31 | phys_PxInitExtensions(physics, pvd); 32 | 33 | var sceneDesc = PxSceneDesc_new(PxPhysics_getTolerancesScale(physics)); 34 | sceneDesc.gravity = new PxVec3 { x = 0.0f, y = -9.81f, z = 0.0f }; 35 | 36 | var dispatcher = phys_PxDefaultCpuDispatcherCreate(1, null, PxDefaultCpuDispatcherWaitForWorkMode.WaitForWork, 0); 37 | sceneDesc.cpuDispatcher = (PxCpuDispatcher*)dispatcher; 38 | sceneDesc.filterShader = get_default_simulation_filter_shader(); 39 | 40 | var scene = PxPhysics_createScene_mut(physics, &sceneDesc); 41 | 42 | // pvd client 43 | var pvdClient = scene->GetScenePvdClientMut(); 44 | if (pvdClient != null) 45 | { 46 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitConstraints, true); 47 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitContacts, true); 48 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitScenequeries, true); 49 | } 50 | 51 | var material = PxPhysics_createMaterial_mut(physics, 0.5f, 0.5f, 0.6f); 52 | 53 | // plane 54 | var plane = PxPlane_new_1(0.0f, 1.0f, 0.0f, 0.0f); 55 | var groundPlane = phys_PxCreatePlane(physics, &plane, material); 56 | 57 | PxScene_addActor_mut(scene, (PxActor*)groundPlane, null); 58 | 59 | // box1 60 | var box1Position = new PxVec3 61 | { 62 | x = 0.0f, 63 | y = 10.0f, 64 | z = 0.0f 65 | }; 66 | 67 | var box1Transform = PxTransform_new_1(&box1Position); 68 | var box1ShapeOffset = PxTransform_new_2(PxIDENTITY.PxIdentity); 69 | var box1Geometry = PxBoxGeometry_new(2.0f, 12.0f, 2.0f); 70 | 71 | physics->CreateShapeMut( 72 | (PxGeometry*)&box1Geometry, 73 | material, 74 | false, 75 | PxShapeFlags.Visualization | PxShapeFlags.SceneQueryShape | PxShapeFlags.SimulationShape); 76 | 77 | var box1 = phys_PxCreateStatic( 78 | physics, 79 | &box1Transform, 80 | (PxGeometry*)&box1Geometry, 81 | material, 82 | &box1ShapeOffset); 83 | 84 | PxScene_addActor_mut(scene, (PxActor*)box1, null); 85 | 86 | // box2 87 | var box2Position = new PxVec3 88 | { 89 | x = 12.0f, 90 | y = 20.0f, 91 | z = 4.0f 92 | }; 93 | 94 | var box2Transform = PxTransform_new_1(&box2Position); 95 | var box2ShapeOffset = PxTransform_new_2(PxIDENTITY.PxIdentity); 96 | var box2Geometry = PxBoxGeometry_new(10.0f, 2.0f, 6.0f); 97 | 98 | physics->CreateShapeMut( 99 | (PxGeometry*)&box2Geometry, 100 | material, 101 | false, 102 | PxShapeFlags.Visualization | PxShapeFlags.SceneQueryShape | PxShapeFlags.SimulationShape); 103 | 104 | var box2 = phys_PxCreateDynamic( 105 | physics, 106 | &box2Transform, 107 | (PxGeometry*)&box2Geometry, 108 | material, 109 | 1.0f, 110 | &box2ShapeOffset); 111 | 112 | PxScene_addActor_mut(scene, (PxActor*)box2, null); 113 | 114 | // joint1 115 | var anchor1 = new PxVec3 116 | { 117 | x = 2.0f, 118 | y = 10.0f, 119 | z = 0.0f 120 | }; 121 | 122 | var anchor2 = new PxVec3 123 | { 124 | x = -10.0f, 125 | y = 0.0f, 126 | z = -4.0f 127 | }; 128 | 129 | var transform1 = PxTransform_new_1((PxVec3*)Unsafe.AsPointer(ref anchor1)); 130 | var transform2 = PxTransform_new_1((PxVec3*)Unsafe.AsPointer(ref anchor2)); 131 | 132 | var joint1 = phys_PxRevoluteJointCreate( 133 | physics, 134 | (PxRigidActor*)box1, 135 | &transform1, 136 | (PxRigidActor*)box2, 137 | &transform2); 138 | 139 | // simulate 140 | Console.WriteLine("Start simulate"); 141 | 142 | for (var i = 0; i < 300; i++) 143 | { 144 | PxScene_simulate_mut(scene, 1.0f / 30.0f, null, null, 0, true); 145 | uint error = 0; 146 | PxScene_fetchResults_mut(scene, true, &error); 147 | 148 | Console.SetCursorPosition(0, Console.CursorTop); 149 | Console.Write($"Frame: {i}"); 150 | } 151 | 152 | Console.WriteLine("\nDone"); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /sandbox/ConsoleSandbox/SphericalJoint.cs: -------------------------------------------------------------------------------- 1 | using MagicPhysX; 2 | using System.Runtime.CompilerServices; 3 | using static MagicPhysX.NativeMethods; 4 | 5 | namespace ConsoleSandbox; 6 | 7 | public static class SphericalJoint 8 | { 9 | public static unsafe void Run() 10 | { 11 | var foundation = physx_create_foundation(); 12 | 13 | // create pvd 14 | var pvd = phys_PxCreatePvd(foundation); 15 | 16 | fixed (byte* bytePointer = "127.0.0.1"u8.ToArray()) 17 | { 18 | var transport = phys_PxDefaultPvdSocketTransportCreate(bytePointer, 5425, 10); 19 | pvd->ConnectMut(transport, PxPvdInstrumentationFlags.All); 20 | } 21 | 22 | // create physics 23 | uint PX_PHYSICS_VERSION_MAJOR = 5; 24 | uint PX_PHYSICS_VERSION_MINOR = 1; 25 | uint PX_PHYSICS_VERSION_BUGFIX = 3; 26 | uint versionNumber = (PX_PHYSICS_VERSION_MAJOR << 24) + (PX_PHYSICS_VERSION_MINOR << 16) + (PX_PHYSICS_VERSION_BUGFIX << 8); 27 | 28 | var tolerancesScale = new PxTolerancesScale { length = 1, speed = 10 }; 29 | var physics = phys_PxCreatePhysics(versionNumber, foundation, &tolerancesScale, true, pvd, null); 30 | 31 | phys_PxInitExtensions(physics, pvd); 32 | 33 | var sceneDesc = PxSceneDesc_new(PxPhysics_getTolerancesScale(physics)); 34 | sceneDesc.gravity = new PxVec3 { x = 0.0f, y = -9.81f, z = 0.0f }; 35 | 36 | var dispatcher = phys_PxDefaultCpuDispatcherCreate(1, null, PxDefaultCpuDispatcherWaitForWorkMode.WaitForWork, 0); 37 | sceneDesc.cpuDispatcher = (PxCpuDispatcher*)dispatcher; 38 | sceneDesc.filterShader = get_default_simulation_filter_shader(); 39 | 40 | var scene = PxPhysics_createScene_mut(physics, &sceneDesc); 41 | 42 | // pvd client 43 | var pvdClient = scene->GetScenePvdClientMut(); 44 | if (pvdClient != null) 45 | { 46 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitConstraints, true); 47 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitContacts, true); 48 | pvdClient->SetScenePvdFlagMut(PxPvdSceneFlag.TransmitScenequeries, true); 49 | } 50 | 51 | var material = PxPhysics_createMaterial_mut(physics, 0.5f, 0.5f, 0.6f); 52 | 53 | // plane 54 | var plane = PxPlane_new_1(0.0f, 1.0f, 0.0f, 0.0f); 55 | var groundPlane = phys_PxCreatePlane(physics, &plane, material); 56 | 57 | PxScene_addActor_mut(scene, (PxActor*)groundPlane, null); 58 | 59 | // box1 60 | var box1Position = new PxVec3 61 | { 62 | x = 0.0f, 63 | y = 10.0f, 64 | z = 0.0f 65 | }; 66 | 67 | var box1Transform = PxTransform_new_1(&box1Position); 68 | var box1ShapeOffset = PxTransform_new_2(PxIDENTITY.PxIdentity); 69 | var box1Geometry = PxBoxGeometry_new(2.0f, 12.0f, 2.0f); 70 | 71 | physics->CreateShapeMut( 72 | (PxGeometry*)&box1Geometry, 73 | material, 74 | false, 75 | PxShapeFlags.Visualization | PxShapeFlags.SceneQueryShape | PxShapeFlags.SimulationShape); 76 | 77 | var box1 = phys_PxCreateStatic( 78 | physics, 79 | &box1Transform, 80 | (PxGeometry*)&box1Geometry, 81 | material, 82 | &box1ShapeOffset); 83 | 84 | PxScene_addActor_mut(scene, (PxActor*)box1, null); 85 | 86 | // box2 87 | var box2Position = new PxVec3 88 | { 89 | x = 10.0f, 90 | y = 20.0f, 91 | z = 0.0f 92 | }; 93 | 94 | var box2Transform = PxTransform_new_1(&box2Position); 95 | var box2ShapeOffset = PxTransform_new_2(PxIDENTITY.PxIdentity); 96 | var box2Geometry = PxBoxGeometry_new(8.0f, 2.0f, 2.0f); 97 | 98 | physics->CreateShapeMut( 99 | (PxGeometry*)&box2Geometry, 100 | material, 101 | false, 102 | PxShapeFlags.Visualization | PxShapeFlags.SceneQueryShape | PxShapeFlags.SimulationShape); 103 | 104 | var box2 = phys_PxCreateDynamic( 105 | physics, 106 | &box2Transform, 107 | (PxGeometry*)&box2Geometry, 108 | material, 109 | 1.0f, 110 | &box2ShapeOffset); 111 | 112 | PxScene_addActor_mut(scene, (PxActor*)box2, null); 113 | 114 | // joint1 115 | var anchor1 = new PxVec3 116 | { 117 | x = 2.0f, 118 | y = 10.0f, 119 | z = -1.0f 120 | }; 121 | 122 | var anchor2 = new PxVec3 123 | { 124 | x = -8.0f, 125 | y = 0.0f, 126 | z = -1.0f 127 | }; 128 | 129 | var transform1 = PxTransform_new_1((PxVec3*)Unsafe.AsPointer(ref anchor1)); 130 | var transform2 = PxTransform_new_1((PxVec3*)Unsafe.AsPointer(ref anchor2)); 131 | 132 | var joint1 = phys_PxSphericalJointCreate( 133 | physics, 134 | (PxRigidActor*)box1, 135 | &transform1, 136 | (PxRigidActor*)box2, 137 | &transform2); 138 | 139 | // simulate 140 | Console.WriteLine("Start simulate"); 141 | 142 | for (var i = 0; i < 300; i++) 143 | { 144 | PxScene_simulate_mut(scene, 1.0f / 30.0f, null, null, 0, true); 145 | uint error = 0; 146 | PxScene_fetchResults_mut(scene, true, &error); 147 | 148 | Console.SetCursorPosition(0, Console.CursorTop); 149 | Console.Write($"Frame: {i}"); 150 | } 151 | 152 | Console.WriteLine("\nDone"); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/MagicPhysX/ImplicitConvert.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace MagicPhysX; 5 | 6 | public partial struct PxVec2 7 | { 8 | public static implicit operator PxVec2(Vector2 v) => Unsafe.As(ref v); 9 | public static implicit operator Vector2(PxVec2 v) => Unsafe.As(ref v); 10 | } 11 | 12 | public partial struct PxVec3 13 | { 14 | public static implicit operator PxVec3(Vector3 v) => Unsafe.As(ref v); 15 | public static implicit operator Vector3(PxVec3 v) => Unsafe.As(ref v); 16 | } 17 | 18 | public partial struct PxVec4 19 | { 20 | public static implicit operator PxVec4(Vector4 v) => Unsafe.As(ref v); 21 | public static implicit operator Vector4(PxVec4 v) => Unsafe.As(ref v); 22 | } 23 | 24 | public partial struct PxQuat 25 | { 26 | public static implicit operator PxQuat(Quaternion v) => Unsafe.As(ref v); 27 | public static implicit operator Quaternion(PxQuat v) => Unsafe.As(ref v); 28 | } 29 | 30 | -------------------------------------------------------------------------------- /src/MagicPhysX/MagicPhysX.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | latest 8 | true 9 | 10 | physics 11 | .NET PhysX 5 binding to all platforms(win, osx, linux) for 3D engine, deep learning, dedicated server of gaming. 12 | 13 | 14 | 15 | 16 | 17 | all 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | 20 | 21 | 22 | 23 | 24 | Always 25 | 26 | 27 | Always 28 | 29 | 30 | Always 31 | 32 | 33 | Always 34 | 35 | 36 | Always 37 | 38 | 39 | 40 | 41 | 42 | 43 | Always 44 | libmagicphysx.dll 45 | 46 | 47 | Always 48 | libmagicphysx.pdb 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/MagicPhysX/NativeMethods.DllImportResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace MagicPhysX 7 | { 8 | public static unsafe partial class NativeMethods 9 | { 10 | // https://docs.microsoft.com/en-us/dotnet/standard/native-interop/cross-platform 11 | // Library path will search 12 | // win => __DllName, __DllName.dll 13 | // linux, osx => __DllName.so, __DllName.dylib 14 | 15 | static NativeMethods() 16 | { 17 | NativeLibrary.SetDllImportResolver(typeof(NativeMethods).Assembly, DllImportResolver); 18 | } 19 | 20 | static IntPtr DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) 21 | { 22 | if (libraryName == __DllName) 23 | { 24 | #if DEBUG 25 | var combinedPath = Path.Combine(AppContext.BaseDirectory, libraryName); 26 | if (File.Exists(combinedPath) || File.Exists(combinedPath + ".dll")) 27 | { 28 | return NativeLibrary.Load(combinedPath, assembly, searchPath); 29 | } 30 | #endif 31 | 32 | var path = "runtimes/"; 33 | var extension = ""; 34 | 35 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 36 | { 37 | path += "win-"; 38 | extension = ".dll"; 39 | } 40 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) 41 | { 42 | path += "osx-"; 43 | extension = ".dylib"; 44 | } 45 | else 46 | { 47 | path += "linux-"; 48 | extension = ".so"; 49 | } 50 | 51 | if (RuntimeInformation.OSArchitecture == Architecture.X86) 52 | { 53 | path += "x86"; 54 | } 55 | else if (RuntimeInformation.OSArchitecture == Architecture.X64) 56 | { 57 | path += "x64"; 58 | } 59 | else if (RuntimeInformation.OSArchitecture == Architecture.Arm64) 60 | { 61 | path += "arm64"; 62 | } 63 | 64 | path += "/native/" + __DllName + extension; 65 | 66 | return NativeLibrary.Load(Path.Combine(AppContext.BaseDirectory, path), assembly, searchPath); 67 | } 68 | 69 | return IntPtr.Zero; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/MagicPhysX/NativeMethods.Grouping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MagicPhysX 4 | { 5 | [GroupedNativeMethodsGenerator.GroupedNativeMethods("Px")] 6 | public static unsafe partial class NativeMethods 7 | { 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/MagicPhysX/runtimes/linux-arm64/native/libmagicphysx.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cysharp/MagicPhysX/c320362da782c9f5815d77b78bd7980acf40e7d3/src/MagicPhysX/runtimes/linux-arm64/native/libmagicphysx.so -------------------------------------------------------------------------------- /src/MagicPhysX/runtimes/linux-x64/native/libmagicphysx.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cysharp/MagicPhysX/c320362da782c9f5815d77b78bd7980acf40e7d3/src/MagicPhysX/runtimes/linux-x64/native/libmagicphysx.so -------------------------------------------------------------------------------- /src/MagicPhysX/runtimes/osx-arm64/native/libmagicphysx.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cysharp/MagicPhysX/c320362da782c9f5815d77b78bd7980acf40e7d3/src/MagicPhysX/runtimes/osx-arm64/native/libmagicphysx.dylib -------------------------------------------------------------------------------- /src/MagicPhysX/runtimes/osx-x64/native/libmagicphysx.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cysharp/MagicPhysX/c320362da782c9f5815d77b78bd7980acf40e7d3/src/MagicPhysX/runtimes/osx-x64/native/libmagicphysx.dylib -------------------------------------------------------------------------------- /src/MagicPhysX/runtimes/win-x64/native/libmagicphysx.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cysharp/MagicPhysX/c320362da782c9f5815d77b78bd7980acf40e7d3/src/MagicPhysX/runtimes/win-x64/native/libmagicphysx.dll -------------------------------------------------------------------------------- /src/libmagicphysx/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "type_traits": "cpp", 4 | "xtr1common": "cpp" 5 | }, 6 | "C_Cpp.errorSquiggles": "disabled", 7 | "rust-analyzer.linkedProjects": [ 8 | ".\\Cargo.toml" 9 | ] 10 | } -------------------------------------------------------------------------------- /src/libmagicphysx/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "cargo", 6 | "command": "build", 7 | "problemMatcher": [ 8 | "$rustc" 9 | ], 10 | "args": [ 11 | // "-vv" 12 | ], 13 | "group": { 14 | "kind": "build", 15 | "isDefault": true 16 | }, 17 | "label": "rust: cargo build" 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /src/libmagicphysx/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libmagicphysx" 3 | version = "1.0.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | name = "magicphysx" 8 | crate-type = ["cdylib"] 9 | 10 | [dependencies] 11 | physx-sys = "0.11.1" 12 | 13 | [dev-dependencies] 14 | regex = "1.7.3" 15 | 16 | [build-dependencies] 17 | csbindgen = "1.7.3" 18 | -------------------------------------------------------------------------------- /src/libmagicphysx/build.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | error::Error, 3 | }; 4 | 5 | fn main() -> Result<(), Box> { 6 | csbindgen::Builder::new() 7 | .input_bindgen_file("src/physx/lib.rs") 8 | .input_bindgen_file("src/physx/physx_generated.rs") 9 | .input_bindgen_file("src/physx/x86_64-pc-windows-msvc/structgen.rs") 10 | .method_filter(|x| !(x == "create_contact_callback" || x == "destroy_contact_callback")) 11 | .rust_file_header("use super::physx_sys::*;") 12 | .rust_method_prefix("magicphysx_") 13 | .csharp_entry_point_prefix("magicphysx_") 14 | .csharp_namespace("MagicPhysX") 15 | .csharp_class_name("NativeMethods") 16 | .csharp_dll_name("libmagicphysx") 17 | .csharp_class_accessibility("public") 18 | .generate_to_file( 19 | "./src/physx_ffi.rs", 20 | "../MagicPhysX/NativeMethods.g.cs", 21 | )?; 22 | 23 | Ok(()) 24 | } -------------------------------------------------------------------------------- /src/libmagicphysx/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[allow(dead_code)] 2 | #[allow(non_snake_case)] 3 | #[allow(non_camel_case_types)] 4 | #[allow(non_upper_case_globals)] 5 | #[allow(improper_ctypes)] 6 | use physx_sys; 7 | 8 | #[allow(dead_code)] 9 | #[allow(non_snake_case)] 10 | #[allow(non_camel_case_types)] 11 | #[allow(non_upper_case_globals)] 12 | #[allow(improper_ctypes)] 13 | mod physx_ffi; 14 | 15 | #[cfg(test)] 16 | mod tests { 17 | use std::{ 18 | env, 19 | fs::{self}, 20 | io::Write, 21 | }; 22 | 23 | use regex::Regex; 24 | 25 | // cargo test update_package_version -- 1.0.0 --nocapture 26 | #[test] 27 | fn update_package_version() { 28 | let args: Vec = env::args().collect(); 29 | // 0: exe path 30 | // 1: update_package_version 31 | // 2: 1.0.0 32 | // 3: --nocapture 33 | 34 | if args[1] != "update_package_version" { 35 | return; 36 | } 37 | 38 | println!("version: {}", args[2]); 39 | let mut path = std::env::current_dir().unwrap(); 40 | println!("current_dir: {}", path.display()); 41 | 42 | path.push("Cargo.toml"); 43 | let toml = fs::read_to_string(path.clone()).unwrap(); 44 | 45 | // replace only first-match 46 | let regex = Regex::new("physx-sys = \".+\"").unwrap(); 47 | 48 | let new_toml = regex.replace(toml.as_str(), format!("physx-sys = \"{}\"", args[2])); 49 | 50 | let mut file = fs::File::create(path.clone()).unwrap(); 51 | file.write_all(new_toml.as_bytes()).unwrap(); 52 | file.flush().unwrap(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/libmagicphysx/src/physx/lib.rs: -------------------------------------------------------------------------------- 1 | //! # 🎳 physx-sys 2 | //! 3 | //! ![Build Status](https://github.com/EmbarkStudios/physx-rs/workflows/CI/badge.svg) 4 | //! [![Crates.io](https://img.shields.io/crates/v/physx-sys.svg)](https://crates.io/crates/physx-sys) 5 | //! [![Docs](https://docs.rs/physx-sys/badge.svg)](https://docs.rs/physx-sys) 6 | //! [![Contributor Covenant](https://img.shields.io/badge/contributor%20covenant-v1.4%20adopted-ff69b4.svg)](../CODE_OF_CONDUCT.md) 7 | //! [![Embark](https://img.shields.io/badge/embark-open%20source-blueviolet.svg)](http://embark.games) 8 | //! 9 | //! Unsafe automatically-generated Rust bindings for [NVIDIA PhysX 4.1](https://github.com/NVIDIAGameWorks/PhysX) C++ API. 10 | //! 11 | //! Please also see the [repository](https://github.com/EmbarkStudios/physx-rs) containing a work-in-progress safe wrapper. 12 | //! 13 | //! ## Presentation 14 | //! 15 | //! [Tomasz Stachowiak](https://github.com/h3r2tic) did a presentation at the Stockholm Rust Meetup on October 2019 16 | //! about this project that goes through the tecnical details of how C++ to Rust bindings of `physx-sys` works: 17 | //! 18 | //! [![](http://img.youtube.com/vi/RxtXGeDHu0w/0.jpg)](http://www.youtube.com/watch?v=RxtXGeDHu0w "An unholy fusion of 19 | //! Rust and C++ in physx-rs (Stockholm Rust Meetup, October 2019)") 20 | //! 21 | //! 22 | //! ## Basic usage 23 | //! 24 | //! ```Rust 25 | //! unsafe { 26 | //! let foundation = physx_create_foundation(); 27 | //! let physics = physx_create_physics(foundation); 28 | //! 29 | //! let mut scene_desc = PxSceneDesc_new(PxPhysics_getTolerancesScale(physics)); 30 | //! scene_desc.gravity = PxVec3 { 31 | //! x: 0.0, 32 | //! y: -9.81, 33 | //! z: 0.0, 34 | //! }; 35 | //! 36 | //! let dispatcher = PxDefaultCpuDispatcherCreate(2, null_mut()); 37 | //! 38 | //! scene_desc.cpuDispatcher = dispatcher as *mut PxCpuDispatcher; 39 | //! scene_desc.filterShader = Some(PxDefaultSimulationFilterShader); 40 | //! 41 | //! let scene = PxPhysics_createScene_mut(physics, &scene_desc); 42 | //! 43 | //! // Your physics simulation goes here 44 | //! } 45 | //! ``` 46 | //! 47 | //! ## Examples 48 | //! 49 | //! ### [Ball](examples/ball.rs) 50 | //! 51 | //! A simple example to showcase how to use physx-sys. It can be run with `cargo run --examples ball`. 52 | //! 53 | //! ``` 54 | //! o 55 | //! 56 | //! o 57 | //! o 58 | //! 59 | //! o 60 | //! ooooooooo 61 | //! o oo oo 62 | //! o o 63 | //! o o o 64 | //! o oo 65 | //! o o o 66 | //! o ooooooo 67 | //! o o oo oo 68 | //! o o o oo oo 69 | //! o o o o ooooooooo 70 | //! o o o oo oooooooooo oo 71 | //! 72 | //! ``` 73 | //! 74 | //! ## How it works 75 | //! 76 | //! The binding is generated using a custom C++ app written against clang's 77 | //! [libtooling](https://clang.llvm.org/docs/LibTooling.html). It queries the compiler's abstract syntax tree, and maps 78 | //! the C++ PhysX functions and types to Rust using heuristics chosen specifically for this SDK. It is not a general 79 | //! C++ <-> Rust binding generator, and using it on other projects *will* likely crash and burn. 80 | //! 81 | //! Since C++ does not have a standardized and stable ABI, it's generally not safe to call it from Rust code; since 82 | //! PhysX exposes a C++ interface, we can't use it directly. That's why `physx-sys` generates both a Rust interface as 83 | //! well as a plain C wrapper. The C code is compiled into a static library at build time, and Rust then talks to C. 84 | //! 85 | //! In order to minimize the amount of work required to marshall data between the C wrapper and the original C++ API, we 86 | //! generate a **bespoke C wrapper for each build target**. The wrapper is based on metadata about structure layout 87 | //! extracted directly from compiling and running a tiny program against the PhysX SDK using the specific C++ compiler 88 | //! used in the build process. 89 | //! 90 | //! The build process comprises a few steps: 91 | //! 92 | //! 1. The `pxbind` utility uses `clang` to extract metadata about PhysX functions and types, and generates partial 93 | //! Rust and C bindings as `physx_generated.hpp` and `physx_generated.rs`. Those contain all function definitions, and 94 | //! a small subset of types. It also generates a C++ utility called `structgen` by emitting `structgen.cpp`. 95 | //! 2. `structgen` is compiled against the PhysX SDK, and generates all the remaining type wrappers. For each struct, it 96 | //! queries the size and offset of its members, and generates `structgen_out.hpp` and `structgen_out.rs`. The types are 97 | //! "plain old data" structs which will perfectly match the memory layout of the C++ types. 98 | //! 3. All the generated C types are compiled together to form `physx_api`, a static library for Rust to link with. 99 | //! 4. The Rust wrapper is compiled, and linked with PhysX and the C wrapper. 100 | //! 101 | //! Steps *2..4* are performed completely automatically from within `build.rs`, while step *1* is only necessary when 102 | //! upgrading the PhysX SDK or modifying the generator. As such, building and running `pxbind` is a manual task, and is 103 | //! currently only supported on \*nix systems. 104 | //! 105 | //! ## License 106 | //! 107 | //! Licensed under either of 108 | //! 109 | //! * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 110 | //! * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 111 | //! 112 | //! at your option. 113 | //! 114 | //! Note that the [PhysX C++ SDK](https://github.com/NVIDIAGameWorks/PhysX) has it's 115 | //! [own BSD 3 license](https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/Manual/License.html) and 116 | //! depends on [additional C++ third party libraries](https://github.com/NVIDIAGameWorks/PhysX/tree/4.1/externals). 117 | //! 118 | //! ### Contribution 119 | //! 120 | //! Unless you explicitly state otherwise, any contribution intentionally 121 | //! submitted for inclusion in the work by you, as defined in the Apache-2.0 122 | //! license, shall be dual licensed as above, without any additional terms or 123 | //! conditions. 124 | 125 | // crate-specific exceptions: 126 | #![allow( 127 | unsafe_code, 128 | non_upper_case_globals, 129 | non_camel_case_types, 130 | non_snake_case, 131 | clippy::doc_markdown, // TODO: fixup comments and docs (though annoyingly complains about "PhysX") 132 | clippy::unreadable_literal, 133 | clippy::unused_unit, 134 | clippy::upper_case_acronyms 135 | )] 136 | 137 | #[cfg(feature = "structgen")] 138 | include!(concat!(env!("OUT_DIR"), "/structgen_out.rs")); 139 | 140 | #[cfg(all( 141 | not(feature = "structgen"), 142 | target_os = "linux", 143 | target_arch = "x86_64", 144 | ))] 145 | include!("generated/unix/structgen.rs"); 146 | 147 | #[cfg(all( 148 | not(feature = "structgen"), 149 | target_os = "linux", 150 | target_arch = "aarch64", 151 | ))] 152 | include!("generated/unix/structgen.rs"); 153 | 154 | #[cfg(all( 155 | not(feature = "structgen"), 156 | target_os = "android", 157 | target_arch = "aarch64", 158 | ))] 159 | include!("generated/unix/structgen.rs"); 160 | 161 | #[cfg(all( 162 | not(feature = "structgen"), 163 | target_os = "macos", 164 | target_arch = "x86_64", 165 | ))] 166 | include!("generated/unix/structgen.rs"); 167 | 168 | #[cfg(all( 169 | not(feature = "structgen"), 170 | target_os = "macos", 171 | target_arch = "aarch64", 172 | ))] 173 | include!("generated/unix/structgen.rs"); 174 | 175 | #[cfg(all( 176 | not(feature = "structgen"), 177 | target_os = "windows", 178 | target_arch = "x86_64", 179 | target_env = "msvc", 180 | ))] 181 | include!("generated/x86_64-pc-windows-msvc/structgen.rs"); 182 | 183 | include!("physx_generated.rs"); 184 | 185 | use std::ffi::c_void; 186 | 187 | pub const fn version(major: u32, minor: u32, patch: u32) -> u32 { 188 | (major << 24) + (minor << 16) + (patch << 8) 189 | } 190 | 191 | pub type CollisionCallback = 192 | unsafe extern "C" fn(*mut c_void, *const PxContactPairHeader, *const PxContactPair, u32); 193 | 194 | pub type TriggerCallback = unsafe extern "C" fn(*mut c_void, *const PxTriggerPair, u32); 195 | 196 | pub type ConstraintBreakCallback = unsafe extern "C" fn(*mut c_void, *const PxConstraintInfo, u32); 197 | 198 | pub type WakeSleepCallback = unsafe extern "C" fn(*mut c_void, *const *const PxActor, u32, bool); 199 | 200 | pub type AdvanceCallback = 201 | unsafe extern "C" fn(*mut c_void, *const *const PxRigidBody, *const PxTransform, u32); 202 | 203 | // Function pointers in Rust are normally not nullable (which is why they don't require unsafe to call) 204 | // but we need them to be, so we simply wrap them in Option<>. An Option is luckily represented 205 | // by the compiler as a simple pointer with null representing None, so this is compatible with the C struct. 206 | #[repr(C)] 207 | pub struct SimulationEventCallbackInfo { 208 | // Callback for collision events. 209 | pub collision_callback: Option, 210 | pub collision_user_data: *mut c_void, 211 | // Callback for trigger shape events (an object entered or left a trigger shape). 212 | pub trigger_callback: Option, 213 | pub trigger_user_data: *mut c_void, 214 | // Callback for when a constraint breaks (such as a joint with a force limit) 215 | pub constraint_break_callback: Option, 216 | pub constraint_break_user_data: *mut c_void, 217 | // Callback for when an object falls asleep or is awoken. 218 | pub wake_sleep_callback: Option, 219 | pub wake_sleep_user_data: *mut c_void, 220 | // Callback to get the next pose early for objects (if flagged with eENABLE_POSE_INTEGRATION_PREVIEW). 221 | pub advance_callback: Option, 222 | pub advance_user_data: *mut c_void, 223 | } 224 | 225 | impl Default for SimulationEventCallbackInfo { 226 | fn default() -> Self { 227 | Self { 228 | collision_callback: None, 229 | collision_user_data: std::ptr::null_mut(), 230 | trigger_callback: None, 231 | trigger_user_data: std::ptr::null_mut(), 232 | constraint_break_callback: None, 233 | constraint_break_user_data: std::ptr::null_mut(), 234 | wake_sleep_callback: None, 235 | wake_sleep_user_data: std::ptr::null_mut(), 236 | advance_callback: None, 237 | advance_user_data: std::ptr::null_mut(), 238 | } 239 | } 240 | } 241 | 242 | /// return 0 = `PxQueryHitType::eNONE` 243 | /// return 1 = `PxQueryHitType::eTOUCH` 244 | /// return 2 = `PxQueryHitType::eBLOCK` 245 | pub type RaycastHitCallback = unsafe extern "C" fn( 246 | *const PxRigidActor, 247 | *const PxFilterData, 248 | *const PxShape, 249 | hit_flags: u32, 250 | *const c_void, 251 | ) -> PxQueryHitType; 252 | 253 | #[repr(C)] 254 | pub struct FilterShaderCallbackInfo { 255 | pub attributes0: u32, 256 | pub attributes1: u32, 257 | pub filterData0: PxFilterData, 258 | pub filterData1: PxFilterData, 259 | pub pairFlags: *mut PxPairFlags, 260 | pub constantBlock: *const std::ffi::c_void, 261 | pub constantBlockSize: u32, 262 | } 263 | 264 | pub type SimulationFilterShader = 265 | unsafe extern "C" fn(*mut FilterShaderCallbackInfo) -> PxFilterFlags; 266 | 267 | pub type RaycastProcessTouchesCallback = 268 | unsafe extern "C" fn(*const PxRaycastHit, u32, *mut c_void) -> bool; 269 | pub type SweepProcessTouchesCallback = 270 | unsafe extern "C" fn(*const PxSweepHit, u32, *mut c_void) -> bool; 271 | pub type OverlapProcessTouchesCallback = 272 | unsafe extern "C" fn(*const PxOverlapHit, u32, *mut c_void) -> bool; 273 | 274 | pub type FinalizeQueryCallback = unsafe extern "C" fn(*mut c_void); 275 | 276 | pub type AllocCallback = 277 | unsafe extern "C" fn(u64, *const c_void, *const c_void, u32, *const c_void) -> *mut c_void; 278 | 279 | pub type DeallocCallback = unsafe extern "C" fn(*const c_void, *const c_void); 280 | 281 | pub type ZoneStartCallback = 282 | unsafe extern "C" fn(*const i8, bool, u64, *const c_void) -> *mut c_void; 283 | 284 | pub type ZoneEndCallback = unsafe extern "C" fn(*const c_void, *const i8, bool, u64, *const c_void); 285 | 286 | pub type ErrorCallback = 287 | unsafe extern "C" fn(PxErrorCode, *const i8, *const i8, u32, *const c_void); 288 | 289 | pub type AssertHandler = unsafe extern "C" fn(*const i8, *const i8, u32, *mut bool, *const c_void); 290 | 291 | extern "C" { 292 | pub fn physx_create_foundation() -> *mut PxFoundation; 293 | pub fn physx_create_foundation_with_alloc( 294 | allocator: *mut PxDefaultAllocator, 295 | ) -> *mut PxFoundation; 296 | pub fn physx_create_physics(foundation: *mut PxFoundation) -> *mut PxPhysics; 297 | 298 | pub fn get_default_allocator() -> *mut PxDefaultAllocator; 299 | pub fn get_default_error_callback() -> *mut PxDefaultErrorCallback; 300 | 301 | /// Destroy the returned callback object using PxQueryFilterCallback_delete. 302 | pub fn create_raycast_filter_callback( 303 | actor_to_ignore: *const PxRigidActor, 304 | ) -> *mut PxQueryFilterCallback; 305 | 306 | /// Destroy the returned callback object using PxQueryFilterCallback_delete. 307 | pub fn create_raycast_filter_callback_func( 308 | callback: RaycastHitCallback, 309 | userdata: *mut c_void, 310 | ) -> *mut PxQueryFilterCallback; 311 | 312 | pub fn create_raycast_buffer() -> *mut PxRaycastCallback; 313 | pub fn create_sweep_buffer() -> *mut PxSweepCallback; 314 | pub fn create_overlap_buffer() -> *mut PxOverlapCallback; 315 | 316 | pub fn create_raycast_callback( 317 | process_touches_callback: RaycastProcessTouchesCallback, 318 | finalize_query_callback: FinalizeQueryCallback, 319 | touches_buffer: *mut PxRaycastHit, 320 | num_touches: u32, 321 | userdata: *mut c_void, 322 | ) -> *mut PxRaycastCallback; 323 | pub fn create_sweep_callback( 324 | process_touches_callback: SweepProcessTouchesCallback, 325 | finalize_query_callback: FinalizeQueryCallback, 326 | touches_buffer: *mut PxSweepHit, 327 | num_touches: u32, 328 | userdata: *mut c_void, 329 | ) -> *mut PxSweepCallback; 330 | pub fn create_overlap_callback( 331 | process_touches_callback: OverlapProcessTouchesCallback, 332 | finalize_query_callback: FinalizeQueryCallback, 333 | touches_buffer: *mut PxOverlapHit, 334 | num_touches: u32, 335 | userdata: *mut c_void, 336 | ) -> *mut PxOverlapCallback; 337 | 338 | pub fn delete_raycast_callback(callback: *mut PxRaycastCallback); 339 | pub fn delete_sweep_callback(callback: *mut PxSweepCallback); 340 | pub fn delete_overlap_callback(callback: *mut PxOverlapCallback); 341 | 342 | pub fn create_alloc_callback( 343 | alloc_callback: AllocCallback, 344 | dealloc_callback: DeallocCallback, 345 | userdata: *mut c_void, 346 | ) -> *mut PxAllocatorCallback; 347 | 348 | pub fn create_profiler_callback( 349 | zone_start_callback: ZoneStartCallback, 350 | zone_end_callback: ZoneEndCallback, 351 | userdata: *mut c_void, 352 | ) -> *mut PxProfilerCallback; 353 | 354 | pub fn get_alloc_callback_user_data(alloc_callback: *mut PxAllocatorCallback) -> *mut c_void; 355 | 356 | pub fn create_error_callback( 357 | error_callback: ErrorCallback, 358 | userdata: *mut c_void, 359 | ) -> *mut PxErrorCallback; 360 | 361 | pub fn create_assert_handler( 362 | error_callback: AssertHandler, 363 | userdata: *mut c_void, 364 | ) -> *mut PxAssertHandler; 365 | 366 | pub fn get_default_simulation_filter_shader() -> *mut c_void; 367 | 368 | /// Create a C++ proxy callback which will forward contact events to `Callback`. 369 | /// The returned pointer must be freed by calling `destroy_contact_callback` when done using. 370 | #[deprecated] 371 | pub fn create_contact_callback( 372 | callback: CollisionCallback, 373 | userdata: *mut c_void, 374 | ) -> *mut PxSimulationEventCallback; 375 | /// Deallocates the PxSimulationEventCallback that has previously been created 376 | #[deprecated()] 377 | pub fn destroy_contact_callback(callback: *mut PxSimulationEventCallback); 378 | 379 | /// New interface to handle simulation events, replacing create_contact_callback. 380 | pub fn create_simulation_event_callbacks( 381 | callbacks: *const SimulationEventCallbackInfo, 382 | ) -> *mut PxSimulationEventCallback; 383 | 384 | pub fn get_simulation_event_info( 385 | callback: *mut PxSimulationEventCallback, 386 | ) -> *mut SimulationEventCallbackInfo; 387 | 388 | pub fn destroy_simulation_event_callbacks(callback: *mut PxSimulationEventCallback); 389 | 390 | /// Override the default filter shader in the scene with a custom function. 391 | /// If call_default_filter_shader_first is set to true, this will first call the 392 | /// built-in PhysX filter (that matches Physx 2.8 behavior) before your callback. 393 | pub fn enable_custom_filter_shader( 394 | scene_desc: *mut PxSceneDesc, 395 | shader: SimulationFilterShader, 396 | call_default_filter_shader_first: u32, 397 | ); 398 | 399 | #[doc(hidden)] 400 | /// Should only be used in testing etc! This isn't generated as we don't generate op functions. 401 | pub fn PxAssertHandler_opCall_mut( 402 | self_: *mut PxAssertHandler, 403 | expr: *const i8, 404 | file: *const i8, 405 | line: i32, 406 | ignore: *mut bool, 407 | ) -> (); 408 | } 409 | -------------------------------------------------------------------------------- /tests/MagicPhysX.Tests/InitializeTest.cs: -------------------------------------------------------------------------------- 1 | using static MagicPhysX.NativeMethods; 2 | 3 | namespace MagicPhysX.Tests; 4 | 5 | public class BasicTest 6 | { 7 | [Fact] 8 | public unsafe void InitializeTest() 9 | { 10 | // create foundation 11 | var foundation = physx_create_foundation(); 12 | (foundation != (PxFoundation*)IntPtr.Zero).Should().BeTrue(); 13 | 14 | // create pvd 15 | var pvd = phys_PxCreatePvd(foundation); 16 | (pvd != (PxPvd*)IntPtr.Zero).Should().BeTrue(); 17 | 18 | fixed (byte* bytePointer = "127.0.0.1"u8.ToArray()) 19 | { 20 | var transport = phys_PxDefaultPvdSocketTransportCreate(bytePointer, 5425, 10); 21 | pvd->ConnectMut(transport, PxPvdInstrumentationFlags.All); 22 | } 23 | 24 | // create physics 25 | uint PX_PHYSICS_VERSION_MAJOR = 5; 26 | uint PX_PHYSICS_VERSION_MINOR = 1; 27 | uint PX_PHYSICS_VERSION_BUGFIX = 3; 28 | uint versionNumber = (PX_PHYSICS_VERSION_MAJOR << 24) + (PX_PHYSICS_VERSION_MINOR << 16) + (PX_PHYSICS_VERSION_BUGFIX << 8); 29 | 30 | var tolerancesScale = new PxTolerancesScale { length = 1, speed = 10 }; 31 | var physics = phys_PxCreatePhysics(versionNumber, foundation, &tolerancesScale, true, pvd, null); 32 | (physics != (PxPhysics*)IntPtr.Zero).Should().BeTrue(); 33 | 34 | phys_PxInitExtensions(physics, pvd); 35 | 36 | var sceneDesc = PxSceneDesc_new(PxPhysics_getTolerancesScale(physics)); 37 | sceneDesc.gravity = new PxVec3 { x = 0.0f, y = -9.81f, z = 0.0f }; 38 | 39 | var dispatcher = phys_PxDefaultCpuDispatcherCreate(1, null, PxDefaultCpuDispatcherWaitForWorkMode.WaitForWork, 0); 40 | sceneDesc.cpuDispatcher = (PxCpuDispatcher*)dispatcher; 41 | sceneDesc.filterShader = get_default_simulation_filter_shader(); 42 | 43 | var scene = PxPhysics_createScene_mut(physics, &sceneDesc); 44 | (scene != (PxScene*)IntPtr.Zero).Should().BeTrue(); 45 | 46 | // pvd client 47 | var pvdClient = scene->GetScenePvdClientMut(); 48 | (pvdClient != (PxPvdSceneClient*)IntPtr.Zero).Should().BeTrue(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/MagicPhysX.Tests/MagicPhysX.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | true 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | all 20 | 21 | 22 | runtime; build; native; contentfiles; analyzers; buildtransitive 23 | all 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /tests/MagicPhysX.Tests/Usings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; 2 | global using FluentAssertions; 3 | --------------------------------------------------------------------------------