├── .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 | [](https://www.nuget.org/packages/MagicPhysX)
4 | [](https://github.com/Cysharp/MagicPhysX/actions)
5 | [](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 | 
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 | //! 
4 | //! [](https://crates.io/crates/physx-sys)
5 | //! [](https://docs.rs/physx-sys)
6 | //! [](../CODE_OF_CONDUCT.md)
7 | //! [](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://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 |
--------------------------------------------------------------------------------