feat: 添加跨平台运行时、资产系统和UI适配功能 (#256)

* feat(platform-common): 添加WASM加载器和环境检测API

* feat(rapier2d): 新增Rapier2D WASM绑定包

* feat(physics-rapier2d): 添加跨平台WASM加载器

* feat(asset-system): 添加运行时资产目录和bundle格式

* feat(asset-system-editor): 新增编辑器资产管理包

* feat(editor-core): 添加构建系统和模块管理

* feat(editor-app): 重构浏览器预览使用import maps

* feat(platform-web): 添加BrowserRuntime和资产读取

* feat(engine): 添加材质系统和着色器管理

* feat(material): 新增材质系统和着色器编辑器

* feat(tilemap): 增强tilemap编辑器和动画系统

* feat(modules): 添加module.json配置

* feat(core): 添加module.json和类型定义更新

* chore: 更新依赖和构建配置

* refactor(plugins): 更新插件模板使用ModuleManifest

* chore: 添加第三方依赖库

* chore: 移除BehaviourTree-ai和ecs-astar子模块

* docs: 更新README和文档主题样式

* fix: 修复Rust文档测试和添加rapier2d WASM绑定

* fix(tilemap-editor): 修复画布高DPI屏幕分辨率适配问题

* feat(ui): 添加UI屏幕适配系统(CanvasScaler/SafeArea)

* fix(ecs-engine-bindgen): 添加缺失的ecs-framework-math依赖

* fix: 添加缺失的包依赖修复CI构建

* fix: 修复CodeQL检测到的代码问题

* fix: 修复构建错误和缺失依赖

* fix: 修复类型检查错误

* fix(material-system): 修复tsconfig配置支持TypeScript项目引用

* fix(editor-core): 修复Rollup构建配置添加tauri external

* fix: 修复CodeQL检测到的代码问题

* fix: 修复CodeQL检测到的代码问题
This commit is contained in:
YHH
2025-12-03 22:15:22 +08:00
committed by GitHub
parent caf7622aa0
commit 63f006ab62
496 changed files with 77601 additions and 4067 deletions

View File

@@ -0,0 +1,32 @@
{
"id": "rapier2d",
"name": "@esengine/rapier2d",
"displayName": "Rapier2D",
"description": "Rapier2D physics engine WASM bindings | Rapier2D 物理引擎 WASM 绑定",
"version": "0.14.0",
"category": "Physics",
"icon": "Atom",
"tags": [
"physics",
"wasm",
"rapier"
],
"isCore": false,
"defaultEnabled": false,
"isEngineModule": true,
"canContainContent": false,
"platforms": [
"web",
"desktop"
],
"dependencies": [],
"exports": {
"other": ["RAPIER"]
},
"requiresWasm": true,
"wasmPaths": [
"pkg/rapier_wasm2d_bg.wasm"
],
"outputPath": "dist/index.js",
"isExternalDependency": true
}

View File

@@ -0,0 +1,31 @@
{
"name": "@esengine/rapier2d",
"version": "0.14.0",
"description": "Rapier2D physics engine with dynamic WASM loading support",
"type": "module",
"main": "dist/index.js",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./pkg/*": "./pkg/*"
},
"files": [
"dist",
"pkg/*.wasm"
],
"scripts": {
"gen:src": "node scripts/gen-src.mjs",
"build": "pnpm gen:src && tsup",
"clean": "rimraf dist src"
},
"license": "Apache-2.0",
"devDependencies": {
"rimraf": "^5.0.0",
"tsup": "^8.0.0",
"typescript": "^5.0.0"
}
}

1
packages/rapier2d/pkg/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2020 Dimforge EURL
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,70 @@
<p align="center">
<img src="https://www.rapier.rs/img/rapier_logo_color_textpath_dark.svg" alt="crates.io">
</p>
<p align="center">
<a href="https://discord.gg/vt9DJSW">
<img src="https://img.shields.io/discord/507548572338880513.svg?logo=discord&colorB=7289DA">
</a>
<a href="https://github.com/dimforge/rapier.js/actions">
<img src="https://github.com/dimforge/rapier.js/workflows/main/badge.svg" alt="Build status">
</a>
<a href="https://crates.io/crates/rapier2d">
<img src="https://img.shields.io/crates/v/rapier2d.svg?style=flat-square" alt="crates.io">
</a>
<a href="https://www.npmjs.com/package/@dimforge/rapier2d">
<img src="https://badge.fury.io/js/%40dimforge%2Frapier2d.svg" alt="npm version">
</a>
<a href="https://opensource.org/licenses/Apache-2.0">
<img src="https://img.shields.io/badge/License-Apache%202.0-blue.svg">
</a>
</p>
<p align = "center">
<strong>
<a href="https://rapier.rs">Website</a> | <a href="https://rapier.rs/docs/">Documentation</a>
</strong>
</p>
---
<p align = "center">
<b>2D physics engine</b>
<i>for the JavaScript programming language (official bindings).</i>
</p>
---
## Feature selection
Multiple NPM packages exist for Rapier, depending on your needs:
- [`@dimforge/rapier2d`](https://www.npmjs.com/package/@dimforge/rapier2d) or
[`@dimforge/rapier3d`](https://www.npmjs.com/package/@dimforge/rapier3d):
The main build of the Rapier physics engine for 2D or 3D physics simulation. This should have wide browser
support while offering great performances. This does **not** guarantee cross-platform determinism of the physics
simulation (but it is still locally deterministic, on the same machine).
- [`@dimforge/rapier2d-simd`](https://www.npmjs.com/package/@dimforge/rapier2d-simd) or
[`@dimforge/rapier3d-simd`](https://www.npmjs.com/package/@dimforge/rapier3d-simd):
A build with internal SIMD optimizations enabled. More limited browser support (requires support for [simd128](https://caniuse.com/?search=simd)).
- [`@dimforge/rapier2d-deterministic`](https://www.npmjs.com/package/@dimforge/rapier2d-deterministic) or
[`@dimforge/rapier3d-deterministic`](https://www.npmjs.com/package/@dimforge/rapier3d-deterministic):
A less optimized build but with a guarantee of a cross-platform deterministic execution of the physics simulation.
## Bundler support
Some bundlers will struggle with the `.wasm` file package into the builds above. Alternative `-compat` versions exist
which embed the `.wasm` file into the `.js` sources encoded with base64. This results in a bigger package size, but
much wider bundler support.
Just append `-compat` to the build you are interested in:
[`rapier2d-compat`](https://www.npmjs.com/package/@dimforge/rapier2d-compat),
[`rapier2d-simd-compat`](https://www.npmjs.com/package/@dimforge/rapier2d-simd-compat),
[`rapier2d-deterministic-compat`](https://www.npmjs.com/package/@dimforge/rapier2d-deterministic-compat),
[`rapier3d-compat`](https://www.npmjs.com/package/@dimforge/rapier3d-compat),
[`rapier3d-simd-compat`](https://www.npmjs.com/package/@dimforge/rapier3d-simd-compat),
[`rapier3d-deterministic-compat`](https://www.npmjs.com/package/@dimforge/rapier3d-deterministic-compat).
## Nightly builds
Each time a new Pull Request is merged to the `main` branch of the [`rapier.js` repository](https://github.com/dimforge/rapier.js),
an automatic _canary_ build is triggered. Builds published to npmjs under the _canary_ tag does not come with any
stability guarantee and does not follow semver versioning. But it can be a useful solution to try out the latest
features until a proper release is cut.

View File

@@ -0,0 +1,32 @@
{
"name": "dimforge_rapier2d-deterministic",
"type": "module",
"collaborators": [
"Sébastien Crozet <developer@crozet.re>"
],
"description": "2-dimensional physics engine in Rust - official JS bindings.",
"version": "0.19.3",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/dimforge/rapier.js"
},
"files": [
"rapier_wasm2d_bg.wasm",
"rapier_wasm2d.js",
"rapier_wasm2d.d.ts"
],
"main": "rapier_wasm2d.js",
"homepage": "https://rapier.rs",
"types": "rapier_wasm2d.d.ts",
"sideEffects": [
"./snippets/*"
],
"keywords": [
"physics",
"dynamics",
"rigid",
"real-time",
"joints"
]
}

1666
packages/rapier2d/pkg/rapier_wasm2d.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -0,0 +1,468 @@
/* tslint:disable */
/* eslint-disable */
export const memory: WebAssembly.Memory;
export const __wbg_rawbroadphase_free: (a: number, b: number) => void;
export const __wbg_rawccdsolver_free: (a: number, b: number) => void;
export const __wbg_rawcharactercollision_free: (a: number, b: number) => void;
export const __wbg_rawcolliderset_free: (a: number, b: number) => void;
export const __wbg_rawcollidershapecasthit_free: (a: number, b: number) => void;
export const __wbg_rawcontactforceevent_free: (a: number, b: number) => void;
export const __wbg_rawcontactmanifold_free: (a: number, b: number) => void;
export const __wbg_rawdebugrenderpipeline_free: (a: number, b: number) => void;
export const __wbg_rawdeserializedworld_free: (a: number, b: number) => void;
export const __wbg_raweventqueue_free: (a: number, b: number) => void;
export const __wbg_rawgenericjoint_free: (a: number, b: number) => void;
export const __wbg_rawimpulsejointset_free: (a: number, b: number) => void;
export const __wbg_rawintegrationparameters_free: (a: number, b: number) => void;
export const __wbg_rawislandmanager_free: (a: number, b: number) => void;
export const __wbg_rawkinematiccharactercontroller_free: (a: number, b: number) => void;
export const __wbg_rawmultibodyjointset_free: (a: number, b: number) => void;
export const __wbg_rawnarrowphase_free: (a: number, b: number) => void;
export const __wbg_rawphysicspipeline_free: (a: number, b: number) => void;
export const __wbg_rawpidcontroller_free: (a: number, b: number) => void;
export const __wbg_rawpointcolliderprojection_free: (a: number, b: number) => void;
export const __wbg_rawpointprojection_free: (a: number, b: number) => void;
export const __wbg_rawrayintersection_free: (a: number, b: number) => void;
export const __wbg_rawrigidbodyset_free: (a: number, b: number) => void;
export const __wbg_rawrotation_free: (a: number, b: number) => void;
export const __wbg_rawshape_free: (a: number, b: number) => void;
export const __wbg_rawshapecontact_free: (a: number, b: number) => void;
export const __wbg_rawvector_free: (a: number, b: number) => void;
export const rawbroadphase_castRay: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number, n: number, o: number) => number;
export const rawbroadphase_castRayAndGetNormal: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number, n: number, o: number) => number;
export const rawbroadphase_castShape: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number, n: number, o: number, p: number, q: number, r: number) => number;
export const rawbroadphase_collidersWithAabbIntersectingAabb: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
export const rawbroadphase_intersectionWithShape: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number, n: number, o: number) => void;
export const rawbroadphase_intersectionsWithPoint: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number) => void;
export const rawbroadphase_intersectionsWithRay: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number, n: number, o: number, p: number) => void;
export const rawbroadphase_intersectionsWithShape: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number, n: number, o: number) => void;
export const rawbroadphase_new: () => number;
export const rawbroadphase_projectPoint: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number) => number;
export const rawbroadphase_projectPointAndGetFeature: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number) => number;
export const rawcharactercollision_handle: (a: number) => number;
export const rawcharactercollision_new: () => number;
export const rawcharactercollision_toi: (a: number) => number;
export const rawcharactercollision_translationDeltaApplied: (a: number) => number;
export const rawcharactercollision_translationDeltaRemaining: (a: number) => number;
export const rawcharactercollision_worldNormal1: (a: number) => number;
export const rawcharactercollision_worldNormal2: (a: number) => number;
export const rawcharactercollision_worldWitness1: (a: number) => number;
export const rawcharactercollision_worldWitness2: (a: number) => number;
export const rawcolliderset_coActiveCollisionTypes: (a: number, b: number) => number;
export const rawcolliderset_coActiveEvents: (a: number, b: number) => number;
export const rawcolliderset_coActiveHooks: (a: number, b: number) => number;
export const rawcolliderset_coCastCollider: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => number;
export const rawcolliderset_coCastRay: (a: number, b: number, c: number, d: number, e: number, f: number) => number;
export const rawcolliderset_coCastRayAndGetNormal: (a: number, b: number, c: number, d: number, e: number, f: number) => number;
export const rawcolliderset_coCastShape: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number) => number;
export const rawcolliderset_coCollisionGroups: (a: number, b: number) => number;
export const rawcolliderset_coCombineVoxelStates: (a: number, b: number, c: number, d: number, e: number) => void;
export const rawcolliderset_coContactCollider: (a: number, b: number, c: number, d: number) => number;
export const rawcolliderset_coContactForceEventThreshold: (a: number, b: number) => number;
export const rawcolliderset_coContactShape: (a: number, b: number, c: number, d: number, e: number, f: number) => number;
export const rawcolliderset_coContactSkin: (a: number, b: number) => number;
export const rawcolliderset_coContainsPoint: (a: number, b: number, c: number) => number;
export const rawcolliderset_coDensity: (a: number, b: number) => number;
export const rawcolliderset_coFriction: (a: number, b: number) => number;
export const rawcolliderset_coFrictionCombineRule: (a: number, b: number) => number;
export const rawcolliderset_coHalfExtents: (a: number, b: number) => number;
export const rawcolliderset_coHalfHeight: (a: number, b: number) => number;
export const rawcolliderset_coHalfspaceNormal: (a: number, b: number) => number;
export const rawcolliderset_coHeightfieldHeights: (a: number, b: number, c: number) => void;
export const rawcolliderset_coHeightfieldScale: (a: number, b: number) => number;
export const rawcolliderset_coIndices: (a: number, b: number, c: number) => void;
export const rawcolliderset_coIntersectsRay: (a: number, b: number, c: number, d: number, e: number) => number;
export const rawcolliderset_coIntersectsShape: (a: number, b: number, c: number, d: number, e: number) => number;
export const rawcolliderset_coIsEnabled: (a: number, b: number) => number;
export const rawcolliderset_coIsSensor: (a: number, b: number) => number;
export const rawcolliderset_coMass: (a: number, b: number) => number;
export const rawcolliderset_coParent: (a: number, b: number, c: number) => void;
export const rawcolliderset_coProjectPoint: (a: number, b: number, c: number, d: number) => number;
export const rawcolliderset_coPropagateVoxelChange: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
export const rawcolliderset_coRadius: (a: number, b: number) => number;
export const rawcolliderset_coRestitution: (a: number, b: number) => number;
export const rawcolliderset_coRestitutionCombineRule: (a: number, b: number) => number;
export const rawcolliderset_coRotation: (a: number, b: number) => number;
export const rawcolliderset_coRotationWrtParent: (a: number, b: number) => number;
export const rawcolliderset_coRoundRadius: (a: number, b: number) => number;
export const rawcolliderset_coSetActiveCollisionTypes: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetActiveEvents: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetActiveHooks: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetCollisionGroups: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetContactForceEventThreshold: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetContactSkin: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetDensity: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetEnabled: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetFriction: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetFrictionCombineRule: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetHalfExtents: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetHalfHeight: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetMass: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetMassProperties: (a: number, b: number, c: number, d: number, e: number) => void;
export const rawcolliderset_coSetRadius: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetRestitution: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetRestitutionCombineRule: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetRotation: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetRotationWrtParent: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetRoundRadius: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetSensor: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetShape: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetSolverGroups: (a: number, b: number, c: number) => void;
export const rawcolliderset_coSetTranslation: (a: number, b: number, c: number, d: number) => void;
export const rawcolliderset_coSetTranslationWrtParent: (a: number, b: number, c: number, d: number) => void;
export const rawcolliderset_coSetVoxel: (a: number, b: number, c: number, d: number, e: number) => void;
export const rawcolliderset_coShapeType: (a: number, b: number) => number;
export const rawcolliderset_coSolverGroups: (a: number, b: number) => number;
export const rawcolliderset_coTranslation: (a: number, b: number) => number;
export const rawcolliderset_coTranslationWrtParent: (a: number, b: number) => number;
export const rawcolliderset_coTriMeshFlags: (a: number, b: number) => number;
export const rawcolliderset_coVertices: (a: number, b: number, c: number) => void;
export const rawcolliderset_coVolume: (a: number, b: number) => number;
export const rawcolliderset_coVoxelData: (a: number, b: number, c: number) => void;
export const rawcolliderset_coVoxelSize: (a: number, b: number) => number;
export const rawcolliderset_contains: (a: number, b: number) => number;
export const rawcolliderset_createCollider: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number, n: number, o: number, p: number, q: number, r: number, s: number, t: number, u: number, v: number, w: number, x: number, y: number, z: number) => void;
export const rawcolliderset_forEachColliderHandle: (a: number, b: number) => void;
export const rawcolliderset_len: (a: number) => number;
export const rawcolliderset_new: () => number;
export const rawcolliderset_remove: (a: number, b: number, c: number, d: number, e: number) => void;
export const rawcollidershapecasthit_colliderHandle: (a: number) => number;
export const rawcollidershapecasthit_normal1: (a: number) => number;
export const rawcollidershapecasthit_normal2: (a: number) => number;
export const rawcollidershapecasthit_time_of_impact: (a: number) => number;
export const rawcollidershapecasthit_witness1: (a: number) => number;
export const rawcollidershapecasthit_witness2: (a: number) => number;
export const rawcontactforceevent_collider2: (a: number) => number;
export const rawcontactforceevent_max_force_magnitude: (a: number) => number;
export const rawcontactforceevent_total_force: (a: number) => number;
export const rawcontactforceevent_total_force_magnitude: (a: number) => number;
export const rawcontactmanifold_contact_dist: (a: number, b: number) => number;
export const rawcontactmanifold_contact_fid1: (a: number, b: number) => number;
export const rawcontactmanifold_contact_fid2: (a: number, b: number) => number;
export const rawcontactmanifold_contact_impulse: (a: number, b: number) => number;
export const rawcontactmanifold_contact_local_p1: (a: number, b: number) => number;
export const rawcontactmanifold_contact_local_p2: (a: number, b: number) => number;
export const rawcontactmanifold_contact_tangent_impulse: (a: number, b: number) => number;
export const rawcontactmanifold_local_n1: (a: number) => number;
export const rawcontactmanifold_local_n2: (a: number) => number;
export const rawcontactmanifold_normal: (a: number) => number;
export const rawcontactmanifold_num_contacts: (a: number) => number;
export const rawcontactmanifold_num_solver_contacts: (a: number) => number;
export const rawcontactmanifold_solver_contact_dist: (a: number, b: number) => number;
export const rawcontactmanifold_solver_contact_friction: (a: number, b: number) => number;
export const rawcontactmanifold_solver_contact_point: (a: number, b: number) => number;
export const rawcontactmanifold_solver_contact_restitution: (a: number, b: number) => number;
export const rawcontactmanifold_solver_contact_tangent_velocity: (a: number, b: number) => number;
export const rawcontactmanifold_subshape1: (a: number) => number;
export const rawcontactmanifold_subshape2: (a: number) => number;
export const rawcontactpair_collider1: (a: number) => number;
export const rawcontactpair_collider2: (a: number) => number;
export const rawcontactpair_contactManifold: (a: number, b: number) => number;
export const rawcontactpair_numContactManifolds: (a: number) => number;
export const rawdebugrenderpipeline_colors: (a: number) => number;
export const rawdebugrenderpipeline_new: () => number;
export const rawdebugrenderpipeline_render: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void;
export const rawdebugrenderpipeline_vertices: (a: number) => number;
export const rawdeserializedworld_takeBodies: (a: number) => number;
export const rawdeserializedworld_takeBroadPhase: (a: number) => number;
export const rawdeserializedworld_takeColliders: (a: number) => number;
export const rawdeserializedworld_takeGravity: (a: number) => number;
export const rawdeserializedworld_takeImpulseJoints: (a: number) => number;
export const rawdeserializedworld_takeIntegrationParameters: (a: number) => number;
export const rawdeserializedworld_takeIslandManager: (a: number) => number;
export const rawdeserializedworld_takeMultibodyJoints: (a: number) => number;
export const rawdeserializedworld_takeNarrowPhase: (a: number) => number;
export const raweventqueue_clear: (a: number) => void;
export const raweventqueue_drainCollisionEvents: (a: number, b: number) => void;
export const raweventqueue_drainContactForceEvents: (a: number, b: number) => void;
export const raweventqueue_new: (a: number) => number;
export const rawgenericjoint_fixed: (a: number, b: number, c: number, d: number) => number;
export const rawgenericjoint_prismatic: (a: number, b: number, c: number, d: number, e: number, f: number) => number;
export const rawgenericjoint_revolute: (a: number, b: number) => number;
export const rawgenericjoint_rope: (a: number, b: number, c: number) => number;
export const rawgenericjoint_spring: (a: number, b: number, c: number, d: number, e: number) => number;
export const rawimpulsejointset_contains: (a: number, b: number) => number;
export const rawimpulsejointset_createJoint: (a: number, b: number, c: number, d: number, e: number) => number;
export const rawimpulsejointset_forEachJointAttachedToRigidBody: (a: number, b: number, c: number) => void;
export const rawimpulsejointset_forEachJointHandle: (a: number, b: number) => void;
export const rawimpulsejointset_jointAnchor1: (a: number, b: number) => number;
export const rawimpulsejointset_jointAnchor2: (a: number, b: number) => number;
export const rawimpulsejointset_jointBodyHandle1: (a: number, b: number) => number;
export const rawimpulsejointset_jointBodyHandle2: (a: number, b: number) => number;
export const rawimpulsejointset_jointConfigureMotor: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
export const rawimpulsejointset_jointConfigureMotorModel: (a: number, b: number, c: number, d: number) => void;
export const rawimpulsejointset_jointConfigureMotorPosition: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
export const rawimpulsejointset_jointConfigureMotorVelocity: (a: number, b: number, c: number, d: number, e: number) => void;
export const rawimpulsejointset_jointContactsEnabled: (a: number, b: number) => number;
export const rawimpulsejointset_jointFrameX1: (a: number, b: number) => number;
export const rawimpulsejointset_jointFrameX2: (a: number, b: number) => number;
export const rawimpulsejointset_jointLimitsEnabled: (a: number, b: number, c: number) => number;
export const rawimpulsejointset_jointLimitsMax: (a: number, b: number, c: number) => number;
export const rawimpulsejointset_jointLimitsMin: (a: number, b: number, c: number) => number;
export const rawimpulsejointset_jointSetAnchor1: (a: number, b: number, c: number) => void;
export const rawimpulsejointset_jointSetAnchor2: (a: number, b: number, c: number) => void;
export const rawimpulsejointset_jointSetContactsEnabled: (a: number, b: number, c: number) => void;
export const rawimpulsejointset_jointSetLimits: (a: number, b: number, c: number, d: number, e: number) => void;
export const rawimpulsejointset_jointType: (a: number, b: number) => number;
export const rawimpulsejointset_len: (a: number) => number;
export const rawimpulsejointset_new: () => number;
export const rawimpulsejointset_remove: (a: number, b: number, c: number) => void;
export const rawintegrationparameters_contact_erp: (a: number) => number;
export const rawintegrationparameters_dt: (a: number) => number;
export const rawintegrationparameters_lengthUnit: (a: number) => number;
export const rawintegrationparameters_maxCcdSubsteps: (a: number) => number;
export const rawintegrationparameters_minIslandSize: (a: number) => number;
export const rawintegrationparameters_new: () => number;
export const rawintegrationparameters_numInternalPgsIterations: (a: number) => number;
export const rawintegrationparameters_numSolverIterations: (a: number) => number;
export const rawintegrationparameters_set_contact_natural_frequency: (a: number, b: number) => void;
export const rawintegrationparameters_set_dt: (a: number, b: number) => void;
export const rawintegrationparameters_set_lengthUnit: (a: number, b: number) => void;
export const rawintegrationparameters_set_maxCcdSubsteps: (a: number, b: number) => void;
export const rawintegrationparameters_set_minIslandSize: (a: number, b: number) => void;
export const rawintegrationparameters_set_normalizedAllowedLinearError: (a: number, b: number) => void;
export const rawintegrationparameters_set_normalizedPredictionDistance: (a: number, b: number) => void;
export const rawintegrationparameters_set_numInternalPgsIterations: (a: number, b: number) => void;
export const rawintegrationparameters_set_numSolverIterations: (a: number, b: number) => void;
export const rawislandmanager_forEachActiveRigidBodyHandle: (a: number, b: number) => void;
export const rawislandmanager_new: () => number;
export const rawkinematiccharactercontroller_autostepEnabled: (a: number) => number;
export const rawkinematiccharactercontroller_autostepIncludesDynamicBodies: (a: number) => number;
export const rawkinematiccharactercontroller_autostepMaxHeight: (a: number) => number;
export const rawkinematiccharactercontroller_autostepMinWidth: (a: number) => number;
export const rawkinematiccharactercontroller_computeColliderMovement: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number) => void;
export const rawkinematiccharactercontroller_computedCollision: (a: number, b: number, c: number) => number;
export const rawkinematiccharactercontroller_computedGrounded: (a: number) => number;
export const rawkinematiccharactercontroller_computedMovement: (a: number) => number;
export const rawkinematiccharactercontroller_disableAutostep: (a: number) => void;
export const rawkinematiccharactercontroller_disableSnapToGround: (a: number) => void;
export const rawkinematiccharactercontroller_enableAutostep: (a: number, b: number, c: number, d: number) => void;
export const rawkinematiccharactercontroller_enableSnapToGround: (a: number, b: number) => void;
export const rawkinematiccharactercontroller_maxSlopeClimbAngle: (a: number) => number;
export const rawkinematiccharactercontroller_minSlopeSlideAngle: (a: number) => number;
export const rawkinematiccharactercontroller_new: (a: number) => number;
export const rawkinematiccharactercontroller_normalNudgeFactor: (a: number) => number;
export const rawkinematiccharactercontroller_numComputedCollisions: (a: number) => number;
export const rawkinematiccharactercontroller_offset: (a: number) => number;
export const rawkinematiccharactercontroller_setMaxSlopeClimbAngle: (a: number, b: number) => void;
export const rawkinematiccharactercontroller_setMinSlopeSlideAngle: (a: number, b: number) => void;
export const rawkinematiccharactercontroller_setNormalNudgeFactor: (a: number, b: number) => void;
export const rawkinematiccharactercontroller_setOffset: (a: number, b: number) => void;
export const rawkinematiccharactercontroller_setSlideEnabled: (a: number, b: number) => void;
export const rawkinematiccharactercontroller_setUp: (a: number, b: number) => void;
export const rawkinematiccharactercontroller_slideEnabled: (a: number) => number;
export const rawkinematiccharactercontroller_snapToGroundDistance: (a: number) => number;
export const rawkinematiccharactercontroller_snapToGroundEnabled: (a: number) => number;
export const rawmultibodyjointset_contains: (a: number, b: number) => number;
export const rawmultibodyjointset_createJoint: (a: number, b: number, c: number, d: number, e: number) => number;
export const rawmultibodyjointset_forEachJointAttachedToRigidBody: (a: number, b: number, c: number) => void;
export const rawmultibodyjointset_forEachJointHandle: (a: number, b: number) => void;
export const rawmultibodyjointset_jointAnchor1: (a: number, b: number) => number;
export const rawmultibodyjointset_jointAnchor2: (a: number, b: number) => number;
export const rawmultibodyjointset_jointContactsEnabled: (a: number, b: number) => number;
export const rawmultibodyjointset_jointFrameX1: (a: number, b: number) => number;
export const rawmultibodyjointset_jointFrameX2: (a: number, b: number) => number;
export const rawmultibodyjointset_jointLimitsEnabled: (a: number, b: number, c: number) => number;
export const rawmultibodyjointset_jointLimitsMax: (a: number, b: number, c: number) => number;
export const rawmultibodyjointset_jointLimitsMin: (a: number, b: number, c: number) => number;
export const rawmultibodyjointset_jointSetContactsEnabled: (a: number, b: number, c: number) => void;
export const rawmultibodyjointset_jointType: (a: number, b: number) => number;
export const rawmultibodyjointset_new: () => number;
export const rawmultibodyjointset_remove: (a: number, b: number, c: number) => void;
export const rawnarrowphase_contact_pair: (a: number, b: number, c: number) => number;
export const rawnarrowphase_contact_pairs_with: (a: number, b: number, c: number) => void;
export const rawnarrowphase_intersection_pair: (a: number, b: number, c: number) => number;
export const rawnarrowphase_intersection_pairs_with: (a: number, b: number, c: number) => void;
export const rawnarrowphase_new: () => number;
export const rawphysicspipeline_is_profiler_enabled: (a: number) => number;
export const rawphysicspipeline_new: () => number;
export const rawphysicspipeline_set_profiler_enabled: (a: number, b: number) => void;
export const rawphysicspipeline_step: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number) => void;
export const rawphysicspipeline_stepWithEvents: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number, n: number, o: number) => void;
export const rawphysicspipeline_timing_broad_phase: (a: number) => number;
export const rawphysicspipeline_timing_ccd: (a: number) => number;
export const rawphysicspipeline_timing_ccd_broad_phase: (a: number) => number;
export const rawphysicspipeline_timing_ccd_narrow_phase: (a: number) => number;
export const rawphysicspipeline_timing_ccd_solver: (a: number) => number;
export const rawphysicspipeline_timing_ccd_toi_computation: (a: number) => number;
export const rawphysicspipeline_timing_collision_detection: (a: number) => number;
export const rawphysicspipeline_timing_island_construction: (a: number) => number;
export const rawphysicspipeline_timing_narrow_phase: (a: number) => number;
export const rawphysicspipeline_timing_solver: (a: number) => number;
export const rawphysicspipeline_timing_step: (a: number) => number;
export const rawphysicspipeline_timing_user_changes: (a: number) => number;
export const rawphysicspipeline_timing_velocity_assembly: (a: number) => number;
export const rawphysicspipeline_timing_velocity_resolution: (a: number) => number;
export const rawphysicspipeline_timing_velocity_update: (a: number) => number;
export const rawphysicspipeline_timing_velocity_writeback: (a: number) => number;
export const rawpidcontroller_angular_correction: (a: number, b: number, c: number, d: number, e: number, f: number) => number;
export const rawpidcontroller_apply_angular_correction: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
export const rawpidcontroller_apply_linear_correction: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
export const rawpidcontroller_linear_correction: (a: number, b: number, c: number, d: number, e: number, f: number) => number;
export const rawpidcontroller_new: (a: number, b: number, c: number, d: number) => number;
export const rawpidcontroller_reset_integrals: (a: number) => void;
export const rawpidcontroller_set_axes_mask: (a: number, b: number) => void;
export const rawpidcontroller_set_kd: (a: number, b: number, c: number) => void;
export const rawpidcontroller_set_ki: (a: number, b: number, c: number) => void;
export const rawpidcontroller_set_kp: (a: number, b: number, c: number) => void;
export const rawpointcolliderprojection_colliderHandle: (a: number) => number;
export const rawpointcolliderprojection_featureId: (a: number) => number;
export const rawpointcolliderprojection_featureType: (a: number) => number;
export const rawpointcolliderprojection_isInside: (a: number) => number;
export const rawpointcolliderprojection_point: (a: number) => number;
export const rawpointprojection_isInside: (a: number) => number;
export const rawpointprojection_point: (a: number) => number;
export const rawrigidbodyset_contains: (a: number, b: number) => number;
export const rawrigidbodyset_createRigidBody: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number, n: number, o: number, p: number, q: number, r: number, s: number, t: number, u: number, v: number, w: number) => number;
export const rawrigidbodyset_forEachRigidBodyHandle: (a: number, b: number) => void;
export const rawrigidbodyset_len: (a: number) => number;
export const rawrigidbodyset_new: () => number;
export const rawrigidbodyset_propagateModifiedBodyPositionsToColliders: (a: number, b: number) => void;
export const rawrigidbodyset_rbAddForce: (a: number, b: number, c: number, d: number) => void;
export const rawrigidbodyset_rbAddForceAtPoint: (a: number, b: number, c: number, d: number, e: number) => void;
export const rawrigidbodyset_rbAddTorque: (a: number, b: number, c: number, d: number) => void;
export const rawrigidbodyset_rbAdditionalSolverIterations: (a: number, b: number) => number;
export const rawrigidbodyset_rbAngularDamping: (a: number, b: number) => number;
export const rawrigidbodyset_rbAngvel: (a: number, b: number) => number;
export const rawrigidbodyset_rbApplyImpulse: (a: number, b: number, c: number, d: number) => void;
export const rawrigidbodyset_rbApplyImpulseAtPoint: (a: number, b: number, c: number, d: number, e: number) => void;
export const rawrigidbodyset_rbApplyTorqueImpulse: (a: number, b: number, c: number, d: number) => void;
export const rawrigidbodyset_rbBodyType: (a: number, b: number) => number;
export const rawrigidbodyset_rbCollider: (a: number, b: number, c: number) => number;
export const rawrigidbodyset_rbDominanceGroup: (a: number, b: number) => number;
export const rawrigidbodyset_rbEffectiveAngularInertia: (a: number, b: number) => number;
export const rawrigidbodyset_rbEffectiveInvMass: (a: number, b: number) => number;
export const rawrigidbodyset_rbEffectiveWorldInvInertia: (a: number, b: number) => number;
export const rawrigidbodyset_rbEnableCcd: (a: number, b: number, c: number) => void;
export const rawrigidbodyset_rbGravityScale: (a: number, b: number) => number;
export const rawrigidbodyset_rbInvMass: (a: number, b: number) => number;
export const rawrigidbodyset_rbInvPrincipalInertia: (a: number, b: number) => number;
export const rawrigidbodyset_rbIsCcdEnabled: (a: number, b: number) => number;
export const rawrigidbodyset_rbIsDynamic: (a: number, b: number) => number;
export const rawrigidbodyset_rbIsEnabled: (a: number, b: number) => number;
export const rawrigidbodyset_rbIsFixed: (a: number, b: number) => number;
export const rawrigidbodyset_rbIsKinematic: (a: number, b: number) => number;
export const rawrigidbodyset_rbIsMoving: (a: number, b: number) => number;
export const rawrigidbodyset_rbIsSleeping: (a: number, b: number) => number;
export const rawrigidbodyset_rbLinearDamping: (a: number, b: number) => number;
export const rawrigidbodyset_rbLinvel: (a: number, b: number) => number;
export const rawrigidbodyset_rbLocalCom: (a: number, b: number) => number;
export const rawrigidbodyset_rbLockRotations: (a: number, b: number, c: number, d: number) => void;
export const rawrigidbodyset_rbLockTranslations: (a: number, b: number, c: number, d: number) => void;
export const rawrigidbodyset_rbMass: (a: number, b: number) => number;
export const rawrigidbodyset_rbNextRotation: (a: number, b: number) => number;
export const rawrigidbodyset_rbNextTranslation: (a: number, b: number) => number;
export const rawrigidbodyset_rbNumColliders: (a: number, b: number) => number;
export const rawrigidbodyset_rbPrincipalInertia: (a: number, b: number) => number;
export const rawrigidbodyset_rbRecomputeMassPropertiesFromColliders: (a: number, b: number, c: number) => void;
export const rawrigidbodyset_rbResetForces: (a: number, b: number, c: number) => void;
export const rawrigidbodyset_rbResetTorques: (a: number, b: number, c: number) => void;
export const rawrigidbodyset_rbRotation: (a: number, b: number) => number;
export const rawrigidbodyset_rbSetAdditionalMass: (a: number, b: number, c: number, d: number) => void;
export const rawrigidbodyset_rbSetAdditionalMassProperties: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
export const rawrigidbodyset_rbSetAdditionalSolverIterations: (a: number, b: number, c: number) => void;
export const rawrigidbodyset_rbSetAngularDamping: (a: number, b: number, c: number) => void;
export const rawrigidbodyset_rbSetAngvel: (a: number, b: number, c: number, d: number) => void;
export const rawrigidbodyset_rbSetBodyType: (a: number, b: number, c: number, d: number) => void;
export const rawrigidbodyset_rbSetDominanceGroup: (a: number, b: number, c: number) => void;
export const rawrigidbodyset_rbSetEnabled: (a: number, b: number, c: number) => void;
export const rawrigidbodyset_rbSetEnabledTranslations: (a: number, b: number, c: number, d: number, e: number) => void;
export const rawrigidbodyset_rbSetGravityScale: (a: number, b: number, c: number, d: number) => void;
export const rawrigidbodyset_rbSetLinearDamping: (a: number, b: number, c: number) => void;
export const rawrigidbodyset_rbSetLinvel: (a: number, b: number, c: number, d: number) => void;
export const rawrigidbodyset_rbSetNextKinematicRotation: (a: number, b: number, c: number) => void;
export const rawrigidbodyset_rbSetNextKinematicTranslation: (a: number, b: number, c: number, d: number) => void;
export const rawrigidbodyset_rbSetRotation: (a: number, b: number, c: number, d: number) => void;
export const rawrigidbodyset_rbSetSoftCcdPrediction: (a: number, b: number, c: number) => void;
export const rawrigidbodyset_rbSetTranslation: (a: number, b: number, c: number, d: number, e: number) => void;
export const rawrigidbodyset_rbSetUserData: (a: number, b: number, c: number) => void;
export const rawrigidbodyset_rbSleep: (a: number, b: number) => void;
export const rawrigidbodyset_rbSoftCcdPrediction: (a: number, b: number) => number;
export const rawrigidbodyset_rbTranslation: (a: number, b: number) => number;
export const rawrigidbodyset_rbUserData: (a: number, b: number) => number;
export const rawrigidbodyset_rbUserForce: (a: number, b: number) => number;
export const rawrigidbodyset_rbUserTorque: (a: number, b: number) => number;
export const rawrigidbodyset_rbVelocityAtPoint: (a: number, b: number, c: number) => number;
export const rawrigidbodyset_rbWakeUp: (a: number, b: number) => void;
export const rawrigidbodyset_rbWorldCom: (a: number, b: number) => number;
export const rawrigidbodyset_remove: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
export const rawrotation_angle: (a: number) => number;
export const rawrotation_fromAngle: (a: number) => number;
export const rawrotation_identity: () => number;
export const rawserializationpipeline_deserializeAll: (a: number, b: number) => number;
export const rawserializationpipeline_serializeAll: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number) => number;
export const rawshape_ball: (a: number) => number;
export const rawshape_capsule: (a: number, b: number) => number;
export const rawshape_castRay: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => number;
export const rawshape_castRayAndGetNormal: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => number;
export const rawshape_castShape: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number) => number;
export const rawshape_contactShape: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => number;
export const rawshape_containsPoint: (a: number, b: number, c: number, d: number) => number;
export const rawshape_convexHull: (a: number, b: number) => number;
export const rawshape_convexPolyline: (a: number, b: number) => number;
export const rawshape_cuboid: (a: number, b: number) => number;
export const rawshape_halfspace: (a: number) => number;
export const rawshape_heightfield: (a: number, b: number, c: number) => number;
export const rawshape_intersectsRay: (a: number, b: number, c: number, d: number, e: number, f: number) => number;
export const rawshape_intersectsShape: (a: number, b: number, c: number, d: number, e: number, f: number) => number;
export const rawshape_polyline: (a: number, b: number, c: number, d: number) => number;
export const rawshape_projectPoint: (a: number, b: number, c: number, d: number, e: number) => number;
export const rawshape_roundConvexHull: (a: number, b: number, c: number) => number;
export const rawshape_roundConvexPolyline: (a: number, b: number, c: number) => number;
export const rawshape_roundCuboid: (a: number, b: number, c: number) => number;
export const rawshape_roundTriangle: (a: number, b: number, c: number, d: number) => number;
export const rawshape_segment: (a: number, b: number) => number;
export const rawshape_triangle: (a: number, b: number, c: number) => number;
export const rawshape_trimesh: (a: number, b: number, c: number, d: number, e: number) => number;
export const rawshape_voxels: (a: number, b: number, c: number) => number;
export const rawshape_voxelsFromPoints: (a: number, b: number, c: number) => number;
export const rawshapecasthit_witness1: (a: number) => number;
export const rawvector_new: (a: number, b: number) => number;
export const rawvector_set_y: (a: number, b: number) => void;
export const rawvector_xy: (a: number) => number;
export const rawvector_yx: (a: number) => number;
export const rawvector_zero: () => number;
export const version: (a: number) => void;
export const rawcolliderset_isHandleValid: (a: number, b: number) => number;
export const rawvector_set_x: (a: number, b: number) => void;
export const reserve_memory: (a: number) => void;
export const rawraycolliderintersection_featureId: (a: number) => number;
export const rawrayintersection_featureId: (a: number) => number;
export const rawcontactforceevent_collider1: (a: number) => number;
export const rawintegrationparameters_normalizedAllowedLinearError: (a: number) => number;
export const rawintegrationparameters_normalizedPredictionDistance: (a: number) => number;
export const rawraycolliderhit_colliderHandle: (a: number) => number;
export const rawraycolliderhit_timeOfImpact: (a: number) => number;
export const rawraycolliderintersection_colliderHandle: (a: number) => number;
export const rawraycolliderintersection_featureType: (a: number) => number;
export const rawraycolliderintersection_time_of_impact: (a: number) => number;
export const rawrayintersection_featureType: (a: number) => number;
export const rawrayintersection_time_of_impact: (a: number) => number;
export const rawrotation_im: (a: number) => number;
export const rawrotation_re: (a: number) => number;
export const rawshapecasthit_time_of_impact: (a: number) => number;
export const rawshapecontact_distance: (a: number) => number;
export const rawvector_x: (a: number) => number;
export const rawvector_y: (a: number) => number;
export const __wbg_rawcontactpair_free: (a: number, b: number) => void;
export const __wbg_rawraycolliderhit_free: (a: number, b: number) => void;
export const __wbg_rawraycolliderintersection_free: (a: number, b: number) => void;
export const __wbg_rawserializationpipeline_free: (a: number, b: number) => void;
export const __wbg_rawshapecasthit_free: (a: number, b: number) => void;
export const rawcontactforceevent_max_force_direction: (a: number) => number;
export const rawkinematiccharactercontroller_up: (a: number) => number;
export const rawraycolliderintersection_normal: (a: number) => number;
export const rawrayintersection_normal: (a: number) => number;
export const rawshapecasthit_normal1: (a: number) => number;
export const rawshapecasthit_normal2: (a: number) => number;
export const rawshapecasthit_witness2: (a: number) => number;
export const rawshapecontact_normal1: (a: number) => number;
export const rawshapecontact_normal2: (a: number) => number;
export const rawshapecontact_point1: (a: number) => number;
export const rawshapecontact_point2: (a: number) => number;
export const rawccdsolver_new: () => number;
export const rawserializationpipeline_new: () => number;
export const __wbindgen_export_0: (a: number) => void;
export const __wbindgen_add_to_stack_pointer: (a: number) => number;
export const __wbindgen_export_1: (a: number, b: number, c: number) => void;
export const __wbindgen_export_2: (a: number, b: number) => number;

View File

@@ -0,0 +1,125 @@
/**
* Generate 2D-specific source code from rapier.js source.
* 从 rapier.js 源码生成 2D 专用代码。
*
* This script:
* 1. Copies TypeScript source from rapier.js/src.ts
* 2. Removes #if DIM3 ... #endif blocks (keeps only 2D code)
* 3. Overwrites raw.ts and init.ts with 2D-specific versions
*/
import { readFileSync, writeFileSync, readdirSync, mkdirSync, cpSync, existsSync, renameSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const packageRoot = join(__dirname, '..');
const rapierRoot = join(packageRoot, '..', '..', 'thirdparty', 'rapier.js');
const srcTsDir = join(rapierRoot, 'src.ts');
const src2dDir = join(rapierRoot, 'rapier-compat', 'src2d');
const outputDir = join(packageRoot, 'src');
// Check if rapier.js exists
if (!existsSync(srcTsDir)) {
console.error(`Error: rapier.js source not found at ${rapierRoot}`);
console.error('Please clone https://github.com/esengine/rapier.js.git to thirdparty/rapier.js');
process.exit(1);
}
/**
* Remove #if DIM3 ... #endif blocks from source code
*/
function removeDim3Blocks(content) {
// Remove lines between #if DIM3 and #endif (inclusive)
const lines = content.split('\n');
const result = [];
let skipDepth = 0;
for (const line of lines) {
const trimmed = line.trim();
if (trimmed.startsWith('//#if DIM3') || trimmed.startsWith('// #if DIM3')) {
skipDepth++;
continue;
}
if (skipDepth > 0 && (trimmed.startsWith('//#endif') || trimmed.startsWith('// #endif'))) {
skipDepth--;
continue;
}
if (skipDepth === 0) {
// Also remove #if DIM2 and its #endif (but keep the content)
if (trimmed.startsWith('//#if DIM2') || trimmed.startsWith('// #if DIM2')) {
continue;
}
if (trimmed.startsWith('//#endif') || trimmed.startsWith('// #endif')) {
continue;
}
result.push(line);
}
}
return result.join('\n');
}
/**
* Process a single TypeScript file
*/
function processFile(srcPath, destPath) {
const content = readFileSync(srcPath, 'utf-8');
const processed = removeDim3Blocks(content);
writeFileSync(destPath, processed);
}
/**
* Recursively copy and process directory
*/
function processDirectory(srcDir, destDir) {
mkdirSync(destDir, { recursive: true });
const entries = readdirSync(srcDir, { withFileTypes: true });
for (const entry of entries) {
const srcPath = join(srcDir, entry.name);
const destPath = join(destDir, entry.name);
if (entry.isDirectory()) {
processDirectory(srcPath, destPath);
} else if (entry.name.endsWith('.ts')) {
processFile(srcPath, destPath);
console.log(`Processed: ${entry.name}`);
}
}
}
// Main
console.log('Generating 2D source code...');
console.log(`Source: ${srcTsDir}`);
console.log(`Output: ${outputDir}`);
// Step 1: Copy and process src.ts directory
processDirectory(srcTsDir, outputDir);
// Step 2: Overwrite with 2D-specific files (raw.ts, init.ts)
if (existsSync(src2dDir)) {
const entries = readdirSync(src2dDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isFile() && entry.name.endsWith('.ts')) {
const srcPath = join(src2dDir, entry.name);
const destPath = join(outputDir, entry.name);
cpSync(srcPath, destPath);
console.log(`Overwrote: ${entry.name} (2D-specific)`);
}
}
}
// Step 3: Rename rapier.ts to index.ts
const rapierTs = join(outputDir, 'rapier.ts');
const indexTs = join(outputDir, 'index.ts');
if (existsSync(rapierTs)) {
renameSync(rapierTs, indexTs);
console.log('Renamed: rapier.ts -> index.ts');
}
console.log('Done!');

View File

@@ -0,0 +1,70 @@
export class Coarena<T> {
fconv: Float64Array;
uconv: Uint32Array;
data: Array<T>;
size: number;
public constructor() {
this.fconv = new Float64Array(1);
this.uconv = new Uint32Array(this.fconv.buffer);
this.data = new Array<T>();
this.size = 0;
}
public set(handle: number, data: T) {
let i = this.index(handle);
while (this.data.length <= i) {
this.data.push(null);
}
if (this.data[i] == null) this.size += 1;
this.data[i] = data;
}
public len(): number {
return this.size;
}
public delete(handle: number) {
let i = this.index(handle);
if (i < this.data.length) {
if (this.data[i] != null) this.size -= 1;
this.data[i] = null;
}
}
public clear() {
this.data = new Array<T>();
}
public get(handle: number): T | null {
let i = this.index(handle);
if (i < this.data.length) {
return this.data[i];
} else {
return null;
}
}
public forEach(f: (elt: T) => void) {
for (const elt of this.data) {
if (elt != null) f(elt);
}
}
public getAll(): Array<T> {
return this.data.filter((elt) => elt != null);
}
private index(handle: number): number {
/// Extracts the index part of a handle (the lower 32 bits).
/// This is done by first injecting the handle into an Float64Array
/// which is itself injected into an Uint32Array (at construction time).
/// The 0-th value of the Uint32Array will become the `number` integer
/// representation of the lower 32 bits.
/// Also `this.uconv[1]` then contains the generation number as a `number`,
/// which we dont really need.
this.fconv[0] = handle;
return this.uconv[0];
}
}

View File

@@ -0,0 +1,387 @@
import {RawKinematicCharacterController, RawCharacterCollision} from "../raw";
import {Rotation, Vector, VectorOps} from "../math";
import {
BroadPhase,
Collider,
ColliderSet,
InteractionGroups,
NarrowPhase,
Shape,
} from "../geometry";
import {QueryFilterFlags, World} from "../pipeline";
import {IntegrationParameters, RigidBody, RigidBodySet} from "../dynamics";
/**
* A collision between the character and an obstacle hit on its path.
*/
export class CharacterCollision {
/** The collider involved in the collision. Null if the collider no longer exists in the physics world. */
public collider: Collider | null;
/** The translation delta applied to the character before this collision took place. */
public translationDeltaApplied: Vector;
/** The translation delta the character would move after this collision if there is no other obstacles. */
public translationDeltaRemaining: Vector;
/** The time-of-impact between the character and the obstacles. */
public toi: number;
/** The world-space contact point on the collider when the collision happens. */
public witness1: Vector;
/** The local-space contact point on the character when the collision happens. */
public witness2: Vector;
/** The world-space outward contact normal on the collider when the collision happens. */
public normal1: Vector;
/** The local-space outward contact normal on the character when the collision happens. */
public normal2: Vector;
}
/**
* A character controller for controlling kinematic bodies and parentless colliders by hitting
* and sliding against obstacles.
*/
export class KinematicCharacterController {
private raw: RawKinematicCharacterController;
private rawCharacterCollision: RawCharacterCollision;
private params: IntegrationParameters;
private broadPhase: BroadPhase;
private narrowPhase: NarrowPhase;
private bodies: RigidBodySet;
private colliders: ColliderSet;
private _applyImpulsesToDynamicBodies: boolean;
private _characterMass: number | null;
constructor(
offset: number,
params: IntegrationParameters,
broadPhase: BroadPhase,
narrowPhase: NarrowPhase,
bodies: RigidBodySet,
colliders: ColliderSet,
) {
this.params = params;
this.bodies = bodies;
this.colliders = colliders;
this.broadPhase = broadPhase;
this.narrowPhase = narrowPhase;
this.raw = new RawKinematicCharacterController(offset);
this.rawCharacterCollision = new RawCharacterCollision();
this._applyImpulsesToDynamicBodies = false;
this._characterMass = null;
}
/** @internal */
public free() {
if (!!this.raw) {
this.raw.free();
this.rawCharacterCollision.free();
}
this.raw = undefined;
this.rawCharacterCollision = undefined;
}
/**
* The direction that goes "up". Used to determine where the floor is, and the floors angle.
*/
public up(): Vector {
return this.raw.up();
}
/**
* Sets the direction that goes "up". Used to determine where the floor is, and the floors angle.
*/
public setUp(vector: Vector) {
let rawVect = VectorOps.intoRaw(vector);
const result = this.raw.setUp(rawVect);
rawVect.free();
return result;
}
public applyImpulsesToDynamicBodies(): boolean {
return this._applyImpulsesToDynamicBodies;
}
public setApplyImpulsesToDynamicBodies(enabled: boolean) {
this._applyImpulsesToDynamicBodies = enabled;
}
/**
* Returns the custom value of the character mass, if it was set by `this.setCharacterMass`.
*/
public characterMass(): number | null {
return this._characterMass;
}
/**
* Set the mass of the character to be used for impulse resolution if `self.applyImpulsesToDynamicBodies`
* is set to `true`.
*
* If no character mass is set explicitly (or if it is set to `null`) it is automatically assumed to be equal
* to the mass of the rigid-body the character collider is attached to; or equal to 0 if the character collider
* isnt attached to any rigid-body.
*
* @param mass - The mass to set.
*/
public setCharacterMass(mass: number | null) {
this._characterMass = mass;
}
/**
* A small gap to preserve between the character and its surroundings.
*
* This value should not be too large to avoid visual artifacts, but shouldnt be too small
* (must not be zero) to improve numerical stability of the character controller.
*/
public offset(): number {
return this.raw.offset();
}
/**
* Sets a small gap to preserve between the character and its surroundings.
*
* This value should not be too large to avoid visual artifacts, but shouldnt be too small
* (must not be zero) to improve numerical stability of the character controller.
*/
public setOffset(value: number) {
this.raw.setOffset(value);
}
/// Increase this number if your character appears to get stuck when sliding against surfaces.
///
/// This is a small distance applied to the movement toward the contact normals of shapes hit
/// by the character controller. This helps shape-casting not getting stuck in an always-penetrating
/// state during the sliding calculation.
///
/// This value should remain fairly small since it can introduce artificial "bumps" when sliding
/// along a flat surface.
public normalNudgeFactor(): number {
return this.raw.normalNudgeFactor();
}
/// Increase this number if your character appears to get stuck when sliding against surfaces.
///
/// This is a small distance applied to the movement toward the contact normals of shapes hit
/// by the character controller. This helps shape-casting not getting stuck in an always-penetrating
/// state during the sliding calculation.
///
/// This value should remain fairly small since it can introduce artificial "bumps" when sliding
/// along a flat surface.
public setNormalNudgeFactor(value: number) {
this.raw.setNormalNudgeFactor(value);
}
/**
* Is sliding against obstacles enabled?
*/
public slideEnabled(): boolean {
return this.raw.slideEnabled();
}
/**
* Enable or disable sliding against obstacles.
*/
public setSlideEnabled(enabled: boolean) {
this.raw.setSlideEnabled(enabled);
}
/**
* The maximum step height a character can automatically step over.
*/
public autostepMaxHeight(): number | null {
return this.raw.autostepMaxHeight();
}
/**
* The minimum width of free space that must be available after stepping on a stair.
*/
public autostepMinWidth(): number | null {
return this.raw.autostepMinWidth();
}
/**
* Can the character automatically step over dynamic bodies too?
*/
public autostepIncludesDynamicBodies(): boolean | null {
return this.raw.autostepIncludesDynamicBodies();
}
/**
* Is automatically stepping over small objects enabled?
*/
public autostepEnabled(): boolean {
return this.raw.autostepEnabled();
}
/**
* Enabled automatically stepping over small objects.
*
* @param maxHeight - The maximum step height a character can automatically step over.
* @param minWidth - The minimum width of free space that must be available after stepping on a stair.
* @param includeDynamicBodies - Can the character automatically step over dynamic bodies too?
*/
public enableAutostep(
maxHeight: number,
minWidth: number,
includeDynamicBodies: boolean,
) {
this.raw.enableAutostep(maxHeight, minWidth, includeDynamicBodies);
}
/**
* Disable automatically stepping over small objects.
*/
public disableAutostep() {
return this.raw.disableAutostep();
}
/**
* The maximum angle (radians) between the floors normal and the `up` vector that the
* character is able to climb.
*/
public maxSlopeClimbAngle(): number {
return this.raw.maxSlopeClimbAngle();
}
/**
* Sets the maximum angle (radians) between the floors normal and the `up` vector that the
* character is able to climb.
*/
public setMaxSlopeClimbAngle(angle: number) {
this.raw.setMaxSlopeClimbAngle(angle);
}
/**
* The minimum angle (radians) between the floors normal and the `up` vector before the
* character starts to slide down automatically.
*/
public minSlopeSlideAngle(): number {
return this.raw.minSlopeSlideAngle();
}
/**
* Sets the minimum angle (radians) between the floors normal and the `up` vector before the
* character starts to slide down automatically.
*/
public setMinSlopeSlideAngle(angle: number) {
this.raw.setMinSlopeSlideAngle(angle);
}
/**
* If snap-to-ground is enabled, should the character be automatically snapped to the ground if
* the distance between the ground and its feet are smaller than the specified threshold?
*/
public snapToGroundDistance(): number | null {
return this.raw.snapToGroundDistance();
}
/**
* Enables automatically snapping the character to the ground if the distance between
* the ground and its feet are smaller than the specified threshold.
*/
public enableSnapToGround(distance: number) {
this.raw.enableSnapToGround(distance);
}
/**
* Disables automatically snapping the character to the ground.
*/
public disableSnapToGround() {
this.raw.disableSnapToGround();
}
/**
* Is automatically snapping the character to the ground enabled?
*/
public snapToGroundEnabled(): boolean {
return this.raw.snapToGroundEnabled();
}
/**
* Computes the movement the given collider is able to execute after hitting and sliding on obstacles.
*
* @param collider - The collider to move.
* @param desiredTranslationDelta - The desired collider movement.
* @param filterFlags - Flags for excluding whole subsets of colliders from the obstacles taken into account.
* @param filterGroups - Groups for excluding colliders with incompatible collision groups from the obstacles
* taken into account.
* @param filterPredicate - Any collider for which this closure returns `false` will be excluded from the
* obstacles taken into account.
*/
public computeColliderMovement(
collider: Collider,
desiredTranslationDelta: Vector,
filterFlags?: QueryFilterFlags,
filterGroups?: InteractionGroups,
filterPredicate?: (collider: Collider) => boolean,
) {
let rawTranslationDelta = VectorOps.intoRaw(desiredTranslationDelta);
this.raw.computeColliderMovement(
this.params.dt,
this.broadPhase.raw,
this.narrowPhase.raw,
this.bodies.raw,
this.colliders.raw,
collider.handle,
rawTranslationDelta,
this._applyImpulsesToDynamicBodies,
this._characterMass,
filterFlags,
filterGroups,
this.colliders.castClosure(filterPredicate),
);
rawTranslationDelta.free();
}
/**
* The movement computed by the last call to `this.computeColliderMovement`.
*/
public computedMovement(): Vector {
return VectorOps.fromRaw(this.raw.computedMovement());
}
/**
* The result of ground detection computed by the last call to `this.computeColliderMovement`.
*/
public computedGrounded(): boolean {
return this.raw.computedGrounded();
}
/**
* The number of collisions against obstacles detected along the path of the last call
* to `this.computeColliderMovement`.
*/
public numComputedCollisions(): number {
return this.raw.numComputedCollisions();
}
/**
* Returns the collision against one of the obstacles detected along the path of the last
* call to `this.computeColliderMovement`.
*
* @param i - The i-th collision will be returned.
* @param out - If this argument is set, it will be filled with the collision information.
*/
public computedCollision(
i: number,
out?: CharacterCollision,
): CharacterCollision | null {
if (!this.raw.computedCollision(i, this.rawCharacterCollision)) {
return null;
} else {
let c = this.rawCharacterCollision;
out = out ?? new CharacterCollision();
out.translationDeltaApplied = VectorOps.fromRaw(
c.translationDeltaApplied(),
);
out.translationDeltaRemaining = VectorOps.fromRaw(
c.translationDeltaRemaining(),
);
out.toi = c.toi();
out.witness1 = VectorOps.fromRaw(c.worldWitness1());
out.witness2 = VectorOps.fromRaw(c.worldWitness2());
out.normal1 = VectorOps.fromRaw(c.worldNormal1());
out.normal2 = VectorOps.fromRaw(c.worldNormal2());
out.collider = this.colliders.get(c.handle());
return out;
}
}
}

View File

@@ -0,0 +1,3 @@
export * from "./character_controller";
export * from "./pid_controller";

View File

@@ -0,0 +1,153 @@
import {RawPidController} from "../raw";
import {Rotation, RotationOps, Vector, VectorOps} from "../math";
import {Collider, ColliderSet, InteractionGroups, Shape} from "../geometry";
import {QueryFilterFlags, World} from "../pipeline";
import {IntegrationParameters, RigidBody, RigidBodySet} from "../dynamics";
// TODO: unify with the JointAxesMask
/**
* An enum representing the possible joint axes controlled by a PidController.
* They can be ORed together, like:
* PidAxesMask.LinX || PidAxesMask.LinY
* to get a pid controller that only constraints the translational X and Y axes.
*
* Possible axes are:
*
* - `X`: X translation axis
* - `Y`: Y translation axis
* - `Z`: Z translation axis
* - `AngX`: X angular rotation axis (3D only)
* - `AngY`: Y angular rotation axis (3D only)
* - `AngZ`: Z angular rotation axis
*/
export enum PidAxesMask {
None = 0,
LinX = 1 << 0,
LinY = 1 << 1,
LinZ = 1 << 2,
AngZ = 1 << 5,
AllLin = PidAxesMask.LinX | PidAxesMask.LinY,
AllAng = PidAxesMask.AngZ,
All = PidAxesMask.AllLin | PidAxesMask.AllAng,
}
/**
* A controller for controlling dynamic bodies using the
* Proportional-Integral-Derivative correction model.
*/
export class PidController {
private raw: RawPidController;
private params: IntegrationParameters;
private bodies: RigidBodySet;
constructor(
params: IntegrationParameters,
bodies: RigidBodySet,
kp: number,
ki: number,
kd: number,
axes: PidAxesMask,
) {
this.params = params;
this.bodies = bodies;
this.raw = new RawPidController(kp, ki, kd, axes);
}
/** @internal */
public free() {
if (!!this.raw) {
this.raw.free();
}
this.raw = undefined;
}
public setKp(kp: number, axes: PidAxesMask) {
this.raw.set_kp(kp, axes);
}
public setKi(ki: number, axes: PidAxesMask) {
this.raw.set_kp(ki, axes);
}
public setKd(kd: number, axes: PidAxesMask) {
this.raw.set_kp(kd, axes);
}
public setAxes(axes: PidAxesMask) {
this.raw.set_axes_mask(axes);
}
public resetIntegrals() {
this.raw.reset_integrals();
}
public applyLinearCorrection(
body: RigidBody,
targetPosition: Vector,
targetLinvel: Vector,
) {
let rawPos = VectorOps.intoRaw(targetPosition);
let rawVel = VectorOps.intoRaw(targetLinvel);
this.raw.apply_linear_correction(
this.params.dt,
this.bodies.raw,
body.handle,
rawPos,
rawVel,
);
rawPos.free();
rawVel.free();
}
public applyAngularCorrection(
body: RigidBody,
targetRotation: number,
targetAngVel: number,
) {
this.raw.apply_angular_correction(
this.params.dt,
this.bodies.raw,
body.handle,
targetRotation,
targetAngVel,
);
}
public linearCorrection(
body: RigidBody,
targetPosition: Vector,
targetLinvel: Vector,
): Vector {
let rawPos = VectorOps.intoRaw(targetPosition);
let rawVel = VectorOps.intoRaw(targetLinvel);
let correction = this.raw.linear_correction(
this.params.dt,
this.bodies.raw,
body.handle,
rawPos,
rawVel,
);
rawPos.free();
rawVel.free();
return VectorOps.fromRaw(correction);
}
public angularCorrection(
body: RigidBody,
targetRotation: number,
targetAngVel: number,
): number {
return this.raw.angular_correction(
this.params.dt,
this.bodies.raw,
body.handle,
targetRotation,
targetAngVel,
);
}
}

View File

@@ -0,0 +1,484 @@
import {RawDynamicRayCastVehicleController} from "../raw";
import {Vector, VectorOps} from "../math";
import {
BroadPhase,
Collider,
ColliderSet,
InteractionGroups,
NarrowPhase,
} from "../geometry";
import {QueryFilterFlags} from "../pipeline";
import {RigidBody, RigidBodyHandle, RigidBodySet} from "../dynamics";
/**
* A character controller to simulate vehicles using ray-casting for the wheels.
*/
export class DynamicRayCastVehicleController {
private raw: RawDynamicRayCastVehicleController;
private broadPhase: BroadPhase;
private narrowPhase: NarrowPhase;
private bodies: RigidBodySet;
private colliders: ColliderSet;
private _chassis: RigidBody;
constructor(
chassis: RigidBody,
broadPhase: BroadPhase,
narrowPhase: NarrowPhase,
bodies: RigidBodySet,
colliders: ColliderSet,
) {
if (typeof RawDynamicRayCastVehicleController === 'undefined') {
throw new Error('DynamicRayCastVehicleController is not available in 2D mode');
}
this.raw = new RawDynamicRayCastVehicleController(chassis.handle);
this.broadPhase = broadPhase;
this.narrowPhase = narrowPhase;
this.bodies = bodies;
this.colliders = colliders;
this._chassis = chassis;
}
/** @internal */
public free() {
if (!!this.raw) {
this.raw.free();
}
this.raw = undefined;
}
/**
* Updates the vehicles velocity based on its suspension, engine force, and brake.
*
* This directly updates the velocity of its chassis rigid-body.
*
* @param dt - Time increment used to integrate forces.
* @param filterFlags - Flag to exclude categories of objects from the wheels ray-cast.
* @param filterGroups - Only colliders compatible with these groups will be hit by the wheels ray-casts.
* @param filterPredicate - Callback to filter out which collider will be hit by the wheels ray-casts.
*/
public updateVehicle(
dt: number,
filterFlags?: QueryFilterFlags,
filterGroups?: InteractionGroups,
filterPredicate?: (collider: Collider) => boolean,
) {
this.raw.update_vehicle(
dt,
this.broadPhase.raw,
this.narrowPhase.raw,
this.bodies.raw,
this.colliders.raw,
filterFlags,
filterGroups,
this.colliders.castClosure(filterPredicate),
);
}
/**
* The current forward speed of the vehicle.
*/
public currentVehicleSpeed(): number {
return this.raw.current_vehicle_speed();
}
/**
* The rigid-body used as the chassis.
*/
public chassis(): RigidBody {
return this._chassis;
}
/**
* The chassis local _up_ direction (`0 = x, 1 = y, 2 = z`).
*/
get indexUpAxis(): number {
return this.raw.index_up_axis();
}
/**
* Sets the chassis local _up_ direction (`0 = x, 1 = y, 2 = z`).
*/
set indexUpAxis(axis: number) {
this.raw.set_index_up_axis(axis);
}
/**
* The chassis local _forward_ direction (`0 = x, 1 = y, 2 = z`).
*/
get indexForwardAxis(): number {
return this.raw.index_forward_axis();
}
/**
* Sets the chassis local _forward_ direction (`0 = x, 1 = y, 2 = z`).
*/
set setIndexForwardAxis(axis: number) {
this.raw.set_index_forward_axis(axis);
}
/**
* Adds a new wheel attached to this vehicle.
* @param chassisConnectionCs - The position of the wheel relative to the chassis.
* @param directionCs - The direction of the wheels suspension, relative to the chassis. The ray-casting will
* happen following this direction to detect the ground.
* @param axleCs - The wheels axle axis, relative to the chassis.
* @param suspensionRestLength - The rest length of the wheels suspension spring.
* @param radius - The wheels radius.
*/
public addWheel(
chassisConnectionCs: Vector,
directionCs: Vector,
axleCs: Vector,
suspensionRestLength: number,
radius: number,
) {
let rawChassisConnectionCs = VectorOps.intoRaw(chassisConnectionCs);
let rawDirectionCs = VectorOps.intoRaw(directionCs);
let rawAxleCs = VectorOps.intoRaw(axleCs);
this.raw.add_wheel(
rawChassisConnectionCs,
rawDirectionCs,
rawAxleCs,
suspensionRestLength,
radius,
);
rawChassisConnectionCs.free();
rawDirectionCs.free();
rawAxleCs.free();
}
/**
* The number of wheels attached to this vehicle.
*/
public numWheels(): number {
return this.raw.num_wheels();
}
/*
*
* Access to wheel properties.
*
*/
/*
* Getters + setters
*/
/**
* The position of the i-th wheel, relative to the chassis.
*/
public wheelChassisConnectionPointCs(i: number): Vector | null {
return VectorOps.fromRaw(this.raw.wheel_chassis_connection_point_cs(i));
}
/**
* Sets the position of the i-th wheel, relative to the chassis.
*/
public setWheelChassisConnectionPointCs(i: number, value: Vector) {
let rawValue = VectorOps.intoRaw(value);
this.raw.set_wheel_chassis_connection_point_cs(i, rawValue);
rawValue.free();
}
/**
* The rest length of the i-th wheels suspension spring.
*/
public wheelSuspensionRestLength(i: number): number | null {
return this.raw.wheel_suspension_rest_length(i);
}
/**
* Sets the rest length of the i-th wheels suspension spring.
*/
public setWheelSuspensionRestLength(i: number, value: number) {
this.raw.set_wheel_suspension_rest_length(i, value);
}
/**
* The maximum distance the i-th wheel suspension can travel before and after its resting length.
*/
public wheelMaxSuspensionTravel(i: number): number | null {
return this.raw.wheel_max_suspension_travel(i);
}
/**
* Sets the maximum distance the i-th wheel suspension can travel before and after its resting length.
*/
public setWheelMaxSuspensionTravel(i: number, value: number) {
this.raw.set_wheel_max_suspension_travel(i, value);
}
/**
* The i-th wheels radius.
*/
public wheelRadius(i: number): number | null {
return this.raw.wheel_radius(i);
}
/**
* Sets the i-th wheels radius.
*/
public setWheelRadius(i: number, value: number) {
this.raw.set_wheel_radius(i, value);
}
/**
* The i-th wheels suspension stiffness.
*
* Increase this value if the suspension appears to not push the vehicle strong enough.
*/
public wheelSuspensionStiffness(i: number): number | null {
return this.raw.wheel_suspension_stiffness(i);
}
/**
* Sets the i-th wheels suspension stiffness.
*
* Increase this value if the suspension appears to not push the vehicle strong enough.
*/
public setWheelSuspensionStiffness(i: number, value: number) {
this.raw.set_wheel_suspension_stiffness(i, value);
}
/**
* The i-th wheels suspensions damping when it is being compressed.
*/
public wheelSuspensionCompression(i: number): number | null {
return this.raw.wheel_suspension_compression(i);
}
/**
* The i-th wheels suspensions damping when it is being compressed.
*/
public setWheelSuspensionCompression(i: number, value: number) {
this.raw.set_wheel_suspension_compression(i, value);
}
/**
* The i-th wheels suspensions damping when it is being released.
*
* Increase this value if the suspension appears to overshoot.
*/
public wheelSuspensionRelaxation(i: number): number | null {
return this.raw.wheel_suspension_relaxation(i);
}
/**
* Sets the i-th wheels suspensions damping when it is being released.
*
* Increase this value if the suspension appears to overshoot.
*/
public setWheelSuspensionRelaxation(i: number, value: number) {
this.raw.set_wheel_suspension_relaxation(i, value);
}
/**
* The maximum force applied by the i-th wheels suspension.
*/
public wheelMaxSuspensionForce(i: number): number | null {
return this.raw.wheel_max_suspension_force(i);
}
/**
* Sets the maximum force applied by the i-th wheels suspension.
*/
public setWheelMaxSuspensionForce(i: number, value: number) {
this.raw.set_wheel_max_suspension_force(i, value);
}
/**
* The maximum amount of braking impulse applied on the i-th wheel to slow down the vehicle.
*/
public wheelBrake(i: number): number | null {
return this.raw.wheel_brake(i);
}
/**
* Set the maximum amount of braking impulse applied on the i-th wheel to slow down the vehicle.
*/
public setWheelBrake(i: number, value: number) {
this.raw.set_wheel_brake(i, value);
}
/**
* The steering angle (radians) for the i-th wheel.
*/
public wheelSteering(i: number): number | null {
return this.raw.wheel_steering(i);
}
/**
* Sets the steering angle (radians) for the i-th wheel.
*/
public setWheelSteering(i: number, value: number) {
this.raw.set_wheel_steering(i, value);
}
/**
* The forward force applied by the i-th wheel on the chassis.
*/
public wheelEngineForce(i: number): number | null {
return this.raw.wheel_engine_force(i);
}
/**
* Sets the forward force applied by the i-th wheel on the chassis.
*/
public setWheelEngineForce(i: number, value: number) {
this.raw.set_wheel_engine_force(i, value);
}
/**
* The direction of the i-th wheels suspension, relative to the chassis.
*
* The ray-casting will happen following this direction to detect the ground.
*/
public wheelDirectionCs(i: number): Vector | null {
return VectorOps.fromRaw(this.raw.wheel_direction_cs(i));
}
/**
* Sets the direction of the i-th wheels suspension, relative to the chassis.
*
* The ray-casting will happen following this direction to detect the ground.
*/
public setWheelDirectionCs(i: number, value: Vector) {
let rawValue = VectorOps.intoRaw(value);
this.raw.set_wheel_direction_cs(i, rawValue);
rawValue.free();
}
/**
* The i-th wheels axle axis, relative to the chassis.
*
* The axis index defined as 0 = X, 1 = Y, 2 = Z.
*/
public wheelAxleCs(i: number): Vector | null {
return VectorOps.fromRaw(this.raw.wheel_axle_cs(i));
}
/**
* Sets the i-th wheels axle axis, relative to the chassis.
*
* The axis index defined as 0 = X, 1 = Y, 2 = Z.
*/
public setWheelAxleCs(i: number, value: Vector) {
let rawValue = VectorOps.intoRaw(value);
this.raw.set_wheel_axle_cs(i, rawValue);
rawValue.free();
}
/**
* Parameter controlling how much traction the tire has.
*
* The larger the value, the more instantaneous braking will happen (with the risk of
* causing the vehicle to flip if its too strong).
*/
public wheelFrictionSlip(i: number): number | null {
return this.raw.wheel_friction_slip(i);
}
/**
* Sets the parameter controlling how much traction the tire has.
*
* The larger the value, the more instantaneous braking will happen (with the risk of
* causing the vehicle to flip if its too strong).
*/
public setWheelFrictionSlip(i: number, value: number) {
this.raw.set_wheel_friction_slip(i, value);
}
/**
* The multiplier of friction between a tire and the collider its on top of.
*
* The larger the value, the stronger side friction will be.
*/
public wheelSideFrictionStiffness(i: number): number | null {
return this.raw.wheel_side_friction_stiffness(i);
}
/**
* The multiplier of friction between a tire and the collider its on top of.
*
* The larger the value, the stronger side friction will be.
*/
public setWheelSideFrictionStiffness(i: number, value: number) {
this.raw.set_wheel_side_friction_stiffness(i, value);
}
/*
* Getters only.
*/
/**
* The i-th wheels current rotation angle (radians) on its axle.
*/
public wheelRotation(i: number): number | null {
return this.raw.wheel_rotation(i);
}
/**
* The forward impulses applied by the i-th wheel on the chassis.
*/
public wheelForwardImpulse(i: number): number | null {
return this.raw.wheel_forward_impulse(i);
}
/**
* The side impulses applied by the i-th wheel on the chassis.
*/
public wheelSideImpulse(i: number): number | null {
return this.raw.wheel_side_impulse(i);
}
/**
* The force applied by the i-th wheel suspension.
*/
public wheelSuspensionForce(i: number): number | null {
return this.raw.wheel_suspension_force(i);
}
/**
* The (world-space) contact normal between the i-th wheel and the floor.
*/
public wheelContactNormal(i: number): Vector | null {
return VectorOps.fromRaw(this.raw.wheel_contact_normal_ws(i));
}
/**
* The (world-space) point hit by the wheels ray-cast for the i-th wheel.
*/
public wheelContactPoint(i: number): Vector | null {
return VectorOps.fromRaw(this.raw.wheel_contact_point_ws(i));
}
/**
* The suspension length for the i-th wheel.
*/
public wheelSuspensionLength(i: number): number | null {
return this.raw.wheel_suspension_length(i);
}
/**
* The (world-space) starting point of the ray-cast for the i-th wheel.
*/
public wheelHardPoint(i: number): Vector | null {
return VectorOps.fromRaw(this.raw.wheel_hard_point_ws(i));
}
/**
* Is the i-th wheel in contact with the ground?
*/
public wheelIsInContact(i: number): boolean {
return this.raw.wheel_is_in_contact(i);
}
/**
* The collider hit by the ray-cast for the i-th wheel.
*/
public wheelGroundObject(i: number): Collider | null {
return this.colliders.get(this.raw.wheel_ground_object(i));
}
}

View File

@@ -0,0 +1,25 @@
import {RawCCDSolver} from "../raw";
/**
* The CCD solver responsible for resolving Continuous Collision Detection.
*
* To avoid leaking WASM resources, this MUST be freed manually with `ccdSolver.free()`
* once you are done using it.
*/
export class CCDSolver {
raw: RawCCDSolver;
/**
* Release the WASM memory occupied by this narrow-phase.
*/
public free() {
if (!!this.raw) {
this.raw.free();
}
this.raw = undefined;
}
constructor(raw?: RawCCDSolver) {
this.raw = raw || new RawCCDSolver();
}
}

View File

@@ -0,0 +1,13 @@
/**
* A rule applied to combine coefficients.
*
* Use this when configuring the `ColliderDesc` to specify
* how friction and restitution coefficient should be combined
* in a contact.
*/
export enum CoefficientCombineRule {
Average = 0,
Min = 1,
Multiply = 2,
Max = 3,
}

View File

@@ -0,0 +1,485 @@
import {Rotation, Vector, VectorOps, RotationOps} from "../math";
import {
RawGenericJoint,
RawImpulseJointSet,
RawRigidBodySet,
RawJointAxis,
RawJointType,
RawMotorModel,
} from "../raw";
import {RigidBody, RigidBodyHandle} from "./rigid_body";
import {RigidBodySet} from "./rigid_body_set";
/**
* The integer identifier of a collider added to a `ColliderSet`.
*/
export type ImpulseJointHandle = number;
/**
* An enum grouping all possible types of joints:
*
* - `Revolute`: A revolute joint that removes all degrees of freedom between the affected
* bodies except for the rotation along one axis.
* - `Fixed`: A fixed joint that removes all relative degrees of freedom between the affected bodies.
* - `Prismatic`: A prismatic joint that removes all degrees of freedom between the affected
* bodies except for the translation along one axis.
* - `Spherical`: (3D only) A spherical joint that removes all relative linear degrees of freedom between the affected bodies.
* - `Generic`: (3D only) A joint with customizable degrees of freedom, allowing any of the 6 axes to be locked.
*/
export enum JointType {
Revolute,
Fixed,
Prismatic,
Rope,
Spring,
}
export enum MotorModel {
AccelerationBased,
ForceBased,
}
/**
* An enum representing the possible joint axes of a generic joint.
* They can be ORed together, like:
* JointAxesMask.LinX || JointAxesMask.LinY
* to get a joint that is only free in the X and Y translational (positional) axes.
*
* Possible free axes are:
*
* - `X`: X translation axis
* - `Y`: Y translation axis
* - `Z`: Z translation axis
* - `AngX`: X angular rotation axis
* - `AngY`: Y angular rotations axis
* - `AngZ`: Z angular rotation axis
*/
export enum JointAxesMask {
LinX = 1 << 0,
LinY = 1 << 1,
LinZ = 1 << 2,
AngX = 1 << 3,
AngY = 1 << 4,
AngZ = 1 << 5,
}
export class ImpulseJoint {
protected rawSet: RawImpulseJointSet; // The ImpulseJoint won't need to free this.
protected bodySet: RigidBodySet; // The ImpulseJoint wont need to free this.
handle: ImpulseJointHandle;
constructor(
rawSet: RawImpulseJointSet,
bodySet: RigidBodySet,
handle: ImpulseJointHandle,
) {
this.rawSet = rawSet;
this.bodySet = bodySet;
this.handle = handle;
}
public static newTyped(
rawSet: RawImpulseJointSet,
bodySet: RigidBodySet,
handle: ImpulseJointHandle,
): ImpulseJoint {
switch (rawSet.jointType(handle)) {
case RawJointType.Revolute:
return new RevoluteImpulseJoint(rawSet, bodySet, handle);
case RawJointType.Prismatic:
return new PrismaticImpulseJoint(rawSet, bodySet, handle);
case RawJointType.Fixed:
return new FixedImpulseJoint(rawSet, bodySet, handle);
case RawJointType.Spring:
return new SpringImpulseJoint(rawSet, bodySet, handle);
case RawJointType.Rope:
return new RopeImpulseJoint(rawSet, bodySet, handle);
default:
return new ImpulseJoint(rawSet, bodySet, handle);
}
}
/** @internal */
public finalizeDeserialization(bodySet: RigidBodySet) {
this.bodySet = bodySet;
}
/**
* Checks if this joint is still valid (i.e. that it has
* not been deleted from the joint set yet).
*/
public isValid(): boolean {
return this.rawSet.contains(this.handle);
}
/**
* The first rigid-body this joint it attached to.
*/
public body1(): RigidBody {
return this.bodySet.get(this.rawSet.jointBodyHandle1(this.handle));
}
/**
* The second rigid-body this joint is attached to.
*/
public body2(): RigidBody {
return this.bodySet.get(this.rawSet.jointBodyHandle2(this.handle));
}
/**
* The type of this joint given as a string.
*/
public type(): JointType {
return this.rawSet.jointType(this.handle) as number as JointType;
}
/**
* The position of the first anchor of this joint.
*
* The first anchor gives the position of the application point on the
* local frame of the first rigid-body it is attached to.
*/
public anchor1(): Vector {
return VectorOps.fromRaw(this.rawSet.jointAnchor1(this.handle));
}
/**
* The position of the second anchor of this joint.
*
* The second anchor gives the position of the application point on the
* local frame of the second rigid-body it is attached to.
*/
public anchor2(): Vector {
return VectorOps.fromRaw(this.rawSet.jointAnchor2(this.handle));
}
/**
* Sets the position of the first anchor of this joint.
*
* The first anchor gives the position of the application point on the
* local frame of the first rigid-body it is attached to.
*/
public setAnchor1(newPos: Vector) {
const rawPoint = VectorOps.intoRaw(newPos);
this.rawSet.jointSetAnchor1(this.handle, rawPoint);
rawPoint.free();
}
/**
* Sets the position of the second anchor of this joint.
*
* The second anchor gives the position of the application point on the
* local frame of the second rigid-body it is attached to.
*/
public setAnchor2(newPos: Vector) {
const rawPoint = VectorOps.intoRaw(newPos);
this.rawSet.jointSetAnchor2(this.handle, rawPoint);
rawPoint.free();
}
/**
* Controls whether contacts are computed between colliders attached
* to the rigid-bodies linked by this joint.
*/
public setContactsEnabled(enabled: boolean) {
this.rawSet.jointSetContactsEnabled(this.handle, enabled);
}
/**
* Indicates if contacts are enabled between colliders attached
* to the rigid-bodies linked by this joint.
*/
public contactsEnabled(): boolean {
return this.rawSet.jointContactsEnabled(this.handle);
}
}
export class UnitImpulseJoint extends ImpulseJoint {
/**
* The axis left free by this joint.
*/
protected rawAxis?(): RawJointAxis;
/**
* Are the limits enabled for this joint?
*/
public limitsEnabled(): boolean {
return this.rawSet.jointLimitsEnabled(this.handle, this.rawAxis());
}
/**
* The min limit of this joint.
*/
public limitsMin(): number {
return this.rawSet.jointLimitsMin(this.handle, this.rawAxis());
}
/**
* The max limit of this joint.
*/
public limitsMax(): number {
return this.rawSet.jointLimitsMax(this.handle, this.rawAxis());
}
/**
* Sets the limits of this joint.
*
* @param min - The minimum bound of this joints free coordinate.
* @param max - The maximum bound of this joints free coordinate.
*/
public setLimits(min: number, max: number) {
this.rawSet.jointSetLimits(this.handle, this.rawAxis(), min, max);
}
public configureMotorModel(model: MotorModel) {
this.rawSet.jointConfigureMotorModel(
this.handle,
this.rawAxis(),
model as number as RawMotorModel,
);
}
public configureMotorVelocity(targetVel: number, factor: number) {
this.rawSet.jointConfigureMotorVelocity(
this.handle,
this.rawAxis(),
targetVel,
factor,
);
}
public configureMotorPosition(
targetPos: number,
stiffness: number,
damping: number,
) {
this.rawSet.jointConfigureMotorPosition(
this.handle,
this.rawAxis(),
targetPos,
stiffness,
damping,
);
}
public configureMotor(
targetPos: number,
targetVel: number,
stiffness: number,
damping: number,
) {
this.rawSet.jointConfigureMotor(
this.handle,
this.rawAxis(),
targetPos,
targetVel,
stiffness,
damping,
);
}
}
export class FixedImpulseJoint extends ImpulseJoint {}
export class RopeImpulseJoint extends ImpulseJoint {}
export class SpringImpulseJoint extends ImpulseJoint {}
export class PrismaticImpulseJoint extends UnitImpulseJoint {
public rawAxis(): RawJointAxis {
return RawJointAxis.LinX;
}
}
export class RevoluteImpulseJoint extends UnitImpulseJoint {
public rawAxis(): RawJointAxis {
return RawJointAxis.AngX;
}
}
export class JointData {
anchor1: Vector;
anchor2: Vector;
axis: Vector;
frame1: Rotation;
frame2: Rotation;
jointType: JointType;
limitsEnabled: boolean;
limits: Array<number>;
axesMask: JointAxesMask;
stiffness: number;
damping: number;
length: number;
private constructor() {}
/**
* Creates a new joint descriptor that builds a Fixed joint.
*
* A fixed joint removes all the degrees of freedom between the affected bodies, ensuring their
* anchor and local frames coincide in world-space.
*
* @param anchor1 - Point where the joint is attached on the first rigid-body affected by this joint. Expressed in the
* local-space of the rigid-body.
* @param frame1 - The reference orientation of the joint wrt. the first rigid-body.
* @param anchor2 - Point where the joint is attached on the second rigid-body affected by this joint. Expressed in the
* local-space of the rigid-body.
* @param frame2 - The reference orientation of the joint wrt. the second rigid-body.
*/
public static fixed(
anchor1: Vector,
frame1: Rotation,
anchor2: Vector,
frame2: Rotation,
): JointData {
let res = new JointData();
res.anchor1 = anchor1;
res.anchor2 = anchor2;
res.frame1 = frame1;
res.frame2 = frame2;
res.jointType = JointType.Fixed;
return res;
}
public static spring(
rest_length: number,
stiffness: number,
damping: number,
anchor1: Vector,
anchor2: Vector,
): JointData {
let res = new JointData();
res.anchor1 = anchor1;
res.anchor2 = anchor2;
res.length = rest_length;
res.stiffness = stiffness;
res.damping = damping;
res.jointType = JointType.Spring;
return res;
}
public static rope(
length: number,
anchor1: Vector,
anchor2: Vector,
): JointData {
let res = new JointData();
res.anchor1 = anchor1;
res.anchor2 = anchor2;
res.length = length;
res.jointType = JointType.Rope;
return res;
}
/**
* Create a new joint descriptor that builds revolute joints.
*
* A revolute joint allows three relative rotational degrees of freedom
* by preventing any relative translation between the anchors of the
* two attached rigid-bodies.
*
* @param anchor1 - Point where the joint is attached on the first rigid-body affected by this joint. Expressed in the
* local-space of the rigid-body.
* @param anchor2 - Point where the joint is attached on the second rigid-body affected by this joint. Expressed in the
* local-space of the rigid-body.
*/
public static revolute(anchor1: Vector, anchor2: Vector): JointData {
let res = new JointData();
res.anchor1 = anchor1;
res.anchor2 = anchor2;
res.jointType = JointType.Revolute;
return res;
}
/**
* Creates a new joint descriptor that builds a Prismatic joint.
*
* A prismatic joint removes all the degrees of freedom between the
* affected bodies, except for the translation along one axis.
*
* @param anchor1 - Point where the joint is attached on the first rigid-body affected by this joint. Expressed in the
* local-space of the rigid-body.
* @param anchor2 - Point where the joint is attached on the second rigid-body affected by this joint. Expressed in the
* local-space of the rigid-body.
* @param axis - Axis of the joint, expressed in the local-space of the rigid-bodies it is attached to.
*/
public static prismatic(
anchor1: Vector,
anchor2: Vector,
axis: Vector,
): JointData {
let res = new JointData();
res.anchor1 = anchor1;
res.anchor2 = anchor2;
res.axis = axis;
res.jointType = JointType.Prismatic;
return res;
}
public intoRaw(): RawGenericJoint {
let rawA1 = VectorOps.intoRaw(this.anchor1);
let rawA2 = VectorOps.intoRaw(this.anchor2);
let rawAx;
let result;
let limitsEnabled = false;
let limitsMin = 0.0;
let limitsMax = 0.0;
switch (this.jointType) {
case JointType.Fixed:
let rawFra1 = RotationOps.intoRaw(this.frame1);
let rawFra2 = RotationOps.intoRaw(this.frame2);
result = RawGenericJoint.fixed(rawA1, rawFra1, rawA2, rawFra2);
rawFra1.free();
rawFra2.free();
break;
case JointType.Spring:
result = RawGenericJoint.spring(
this.length,
this.stiffness,
this.damping,
rawA1,
rawA2,
);
break;
case JointType.Rope:
result = RawGenericJoint.rope(this.length, rawA1, rawA2);
break;
case JointType.Prismatic:
rawAx = VectorOps.intoRaw(this.axis);
if (!!this.limitsEnabled) {
limitsEnabled = true;
limitsMin = this.limits[0];
limitsMax = this.limits[1];
}
result = RawGenericJoint.prismatic(
rawA1,
rawA2,
rawAx,
limitsEnabled,
limitsMin,
limitsMax,
);
rawAx.free();
break;
case JointType.Revolute:
result = RawGenericJoint.revolute(rawA1, rawA2);
break;
}
rawA1.free();
rawA2.free();
return result;
}
}

View File

@@ -0,0 +1,162 @@
import {RawImpulseJointSet} from "../raw";
import {Coarena} from "../coarena";
import {RigidBodySet} from "./rigid_body_set";
import {
RevoluteImpulseJoint,
FixedImpulseJoint,
ImpulseJoint,
ImpulseJointHandle,
JointData,
JointType,
PrismaticImpulseJoint,
} from "./impulse_joint";
import {IslandManager} from "./island_manager";
import {RigidBodyHandle} from "./rigid_body";
import {Collider, ColliderHandle} from "../geometry";
/**
* A set of joints.
*
* To avoid leaking WASM resources, this MUST be freed manually with `jointSet.free()`
* once you are done using it (and all the joints it created).
*/
export class ImpulseJointSet {
raw: RawImpulseJointSet;
private map: Coarena<ImpulseJoint>;
/**
* Release the WASM memory occupied by this joint set.
*/
public free() {
if (!!this.raw) {
this.raw.free();
}
this.raw = undefined;
if (!!this.map) {
this.map.clear();
}
this.map = undefined;
}
constructor(raw?: RawImpulseJointSet) {
this.raw = raw || new RawImpulseJointSet();
this.map = new Coarena<ImpulseJoint>();
// Initialize the map with the existing elements, if any.
if (raw) {
raw.forEachJointHandle((handle: ImpulseJointHandle) => {
this.map.set(handle, ImpulseJoint.newTyped(raw, null, handle));
});
}
}
/** @internal */
public finalizeDeserialization(bodies: RigidBodySet) {
this.map.forEach((joint) => joint.finalizeDeserialization(bodies));
}
/**
* Creates a new joint and return its integer handle.
*
* @param bodies - The set of rigid-bodies containing the bodies the joint is attached to.
* @param desc - The joint's parameters.
* @param parent1 - The handle of the first rigid-body this joint is attached to.
* @param parent2 - The handle of the second rigid-body this joint is attached to.
* @param wakeUp - Should the attached rigid-bodies be awakened?
*/
public createJoint(
bodies: RigidBodySet,
desc: JointData,
parent1: RigidBodyHandle,
parent2: RigidBodyHandle,
wakeUp: boolean,
): ImpulseJoint {
const rawParams = desc.intoRaw();
const handle = this.raw.createJoint(
rawParams,
parent1,
parent2,
wakeUp,
);
rawParams.free();
let joint = ImpulseJoint.newTyped(this.raw, bodies, handle);
this.map.set(handle, joint);
return joint;
}
/**
* Remove a joint from this set.
*
* @param handle - The integer handle of the joint.
* @param wakeUp - If `true`, the rigid-bodies attached by the removed joint will be woken-up automatically.
*/
public remove(handle: ImpulseJointHandle, wakeUp: boolean) {
this.raw.remove(handle, wakeUp);
this.unmap(handle);
}
/**
* Calls the given closure with the integer handle of each impulse joint attached to this rigid-body.
*
* @param f - The closure called with the integer handle of each impulse joint attached to the rigid-body.
*/
public forEachJointHandleAttachedToRigidBody(
handle: RigidBodyHandle,
f: (handle: ImpulseJointHandle) => void,
) {
this.raw.forEachJointAttachedToRigidBody(handle, f);
}
/**
* Internal function, do not call directly.
* @param handle
*/
public unmap(handle: ImpulseJointHandle) {
this.map.delete(handle);
}
/**
* The number of joints on this set.
*/
public len(): number {
return this.map.len();
}
/**
* Does this set contain a joint with the given handle?
*
* @param handle - The joint handle to check.
*/
public contains(handle: ImpulseJointHandle): boolean {
return this.get(handle) != null;
}
/**
* Gets the joint with the given handle.
*
* Returns `null` if no joint with the specified handle exists.
*
* @param handle - The integer handle of the joint to retrieve.
*/
public get(handle: ImpulseJointHandle): ImpulseJoint | null {
return this.map.get(handle);
}
/**
* Applies the given closure to each joint contained by this set.
*
* @param f - The closure to apply.
*/
public forEach(f: (joint: ImpulseJoint) => void) {
this.map.forEach(f);
}
/**
* Gets all joints in the list.
*
* @returns joint list.
*/
public getAll(): ImpulseJoint[] {
return this.map.getAll();
}
}

View File

@@ -0,0 +1,10 @@
export * from "./rigid_body";
export * from "./rigid_body_set";
export * from "./integration_parameters";
export * from "./impulse_joint";
export * from "./impulse_joint_set";
export * from "./multibody_joint";
export * from "./multibody_joint_set";
export * from "./coefficient_combine_rule";
export * from "./ccd_solver";
export * from "./island_manager";

View File

@@ -0,0 +1,126 @@
import {RawIntegrationParameters} from "../raw";
export class IntegrationParameters {
raw: RawIntegrationParameters;
constructor(raw?: RawIntegrationParameters) {
this.raw = raw || new RawIntegrationParameters();
}
/**
* Free the WASM memory used by these integration parameters.
*/
public free() {
if (!!this.raw) {
this.raw.free();
}
this.raw = undefined;
}
/**
* The timestep length (default: `1.0 / 60.0`)
*/
get dt(): number {
return this.raw.dt;
}
/**
* The Error Reduction Parameter in `[0, 1]` is the proportion of
* the positional error to be corrected at each time step (default: `0.2`).
*/
get contact_erp(): number {
return this.raw.contact_erp;
}
get lengthUnit(): number {
return this.raw.lengthUnit;
}
/**
* Normalized amount of penetration the engine wont attempt to correct (default: `0.001m`).
*
* This threshold considered by the physics engine is this value multiplied by the `lengthUnit`.
*/
get normalizedAllowedLinearError(): number {
return this.raw.normalizedAllowedLinearError;
}
/**
* The maximal normalized distance separating two objects that will generate predictive contacts (default: `0.002`).
*
* This threshold considered by the physics engine is this value multiplied by the `lengthUnit`.
*/
get normalizedPredictionDistance(): number {
return this.raw.normalizedPredictionDistance;
}
/**
* The number of solver iterations run by the constraints solver for calculating forces (default: `4`).
*/
get numSolverIterations(): number {
return this.raw.numSolverIterations;
}
/**
* Number of internal Project Gauss Seidel (PGS) iterations run at each solver iteration (default: `1`).
*/
get numInternalPgsIterations(): number {
return this.raw.numInternalPgsIterations;
}
/**
* Minimum number of dynamic bodies in each active island (default: `128`).
*/
get minIslandSize(): number {
return this.raw.minIslandSize;
}
/**
* Maximum number of substeps performed by the solver (default: `1`).
*/
get maxCcdSubsteps(): number {
return this.raw.maxCcdSubsteps;
}
set dt(value: number) {
this.raw.dt = value;
}
set contact_natural_frequency(value: number) {
this.raw.contact_natural_frequency = value;
}
set lengthUnit(value: number) {
this.raw.lengthUnit = value;
}
set normalizedAllowedLinearError(value: number) {
this.raw.normalizedAllowedLinearError = value;
}
set normalizedPredictionDistance(value: number) {
this.raw.normalizedPredictionDistance = value;
}
/**
* Sets the number of solver iterations run by the constraints solver for calculating forces (default: `4`).
*/
set numSolverIterations(value: number) {
this.raw.numSolverIterations = value;
}
/**
* Sets the number of internal Project Gauss Seidel (PGS) iterations run at each solver iteration (default: `1`).
*/
set numInternalPgsIterations(value: number) {
this.raw.numInternalPgsIterations = value;
}
set minIslandSize(value: number) {
this.raw.minIslandSize = value;
}
set maxCcdSubsteps(value: number) {
this.raw.maxCcdSubsteps = value;
}
}

View File

@@ -0,0 +1,37 @@
import {RawIslandManager} from "../raw";
import {RigidBodyHandle} from "./rigid_body";
/**
* The CCD solver responsible for resolving Continuous Collision Detection.
*
* To avoid leaking WASM resources, this MUST be freed manually with `ccdSolver.free()`
* once you are done using it.
*/
export class IslandManager {
raw: RawIslandManager;
/**
* Release the WASM memory occupied by this narrow-phase.
*/
public free() {
if (!!this.raw) {
this.raw.free();
}
this.raw = undefined;
}
constructor(raw?: RawIslandManager) {
this.raw = raw || new RawIslandManager();
}
/**
* Applies the given closure to the handle of each active rigid-bodies contained by this set.
*
* A rigid-body is active if it is not sleeping, i.e., if it moved recently.
*
* @param f - The closure to apply.
*/
public forEachActiveRigidBodyHandle(f: (handle: RigidBodyHandle) => void) {
this.raw.forEachActiveRigidBodyHandle(f);
}
}

View File

@@ -0,0 +1,190 @@
import {
RawImpulseJointSet,
RawJointAxis,
RawJointType,
RawMultibodyJointSet,
} from "../raw";
import {
FixedImpulseJoint,
ImpulseJointHandle,
JointType,
MotorModel,
PrismaticImpulseJoint,
RevoluteImpulseJoint,
} from "./impulse_joint";
/**
* The integer identifier of a collider added to a `ColliderSet`.
*/
export type MultibodyJointHandle = number;
export class MultibodyJoint {
protected rawSet: RawMultibodyJointSet; // The MultibodyJoint won't need to free this.
handle: MultibodyJointHandle;
constructor(rawSet: RawMultibodyJointSet, handle: MultibodyJointHandle) {
this.rawSet = rawSet;
this.handle = handle;
}
public static newTyped(
rawSet: RawMultibodyJointSet,
handle: MultibodyJointHandle,
): MultibodyJoint {
switch (rawSet.jointType(handle)) {
case RawJointType.Revolute:
return new RevoluteMultibodyJoint(rawSet, handle);
case RawJointType.Prismatic:
return new PrismaticMultibodyJoint(rawSet, handle);
case RawJointType.Fixed:
return new FixedMultibodyJoint(rawSet, handle);
default:
return new MultibodyJoint(rawSet, handle);
}
}
/**
* Checks if this joint is still valid (i.e. that it has
* not been deleted from the joint set yet).
*/
public isValid(): boolean {
return this.rawSet.contains(this.handle);
}
// /**
// * The unique integer identifier of the first rigid-body this joint it attached to.
// */
// public bodyHandle1(): RigidBodyHandle {
// return this.rawSet.jointBodyHandle1(this.handle);
// }
//
// /**
// * The unique integer identifier of the second rigid-body this joint is attached to.
// */
// public bodyHandle2(): RigidBodyHandle {
// return this.rawSet.jointBodyHandle2(this.handle);
// }
//
// /**
// * The type of this joint given as a string.
// */
// public type(): JointType {
// return this.rawSet.jointType(this.handle);
// }
//
// // #if DIM3
// /**
// * The rotation quaternion that aligns this joint's first local axis to the `x` axis.
// */
// public frameX1(): Rotation {
// return RotationOps.fromRaw(this.rawSet.jointFrameX1(this.handle));
// }
//
// // #endif
//
// // #if DIM3
// /**
// * The rotation matrix that aligns this joint's second local axis to the `x` axis.
// */
// public frameX2(): Rotation {
// return RotationOps.fromRaw(this.rawSet.jointFrameX2(this.handle));
// }
//
// // #endif
//
// /**
// * The position of the first anchor of this joint.
// *
// * The first anchor gives the position of the points application point on the
// * local frame of the first rigid-body it is attached to.
// */
// public anchor1(): Vector {
// return VectorOps.fromRaw(this.rawSet.jointAnchor1(this.handle));
// }
//
// /**
// * The position of the second anchor of this joint.
// *
// * The second anchor gives the position of the points application point on the
// * local frame of the second rigid-body it is attached to.
// */
// public anchor2(): Vector {
// return VectorOps.fromRaw(this.rawSet.jointAnchor2(this.handle));
// }
/**
* Controls whether contacts are computed between colliders attached
* to the rigid-bodies linked by this joint.
*/
public setContactsEnabled(enabled: boolean) {
this.rawSet.jointSetContactsEnabled(this.handle, enabled);
}
/**
* Indicates if contacts are enabled between colliders attached
* to the rigid-bodies linked by this joint.
*/
public contactsEnabled(): boolean {
return this.rawSet.jointContactsEnabled(this.handle);
}
}
export class UnitMultibodyJoint extends MultibodyJoint {
/**
* The axis left free by this joint.
*/
protected rawAxis?(): RawJointAxis;
// /**
// * Are the limits enabled for this joint?
// */
// public limitsEnabled(): boolean {
// return this.rawSet.jointLimitsEnabled(this.handle, this.rawAxis());
// }
//
// /**
// * The min limit of this joint.
// */
// public limitsMin(): number {
// return this.rawSet.jointLimitsMin(this.handle, this.rawAxis());
// }
//
// /**
// * The max limit of this joint.
// */
// public limitsMax(): number {
// return this.rawSet.jointLimitsMax(this.handle, this.rawAxis());
// }
//
// public configureMotorModel(model: MotorModel) {
// this.rawSet.jointConfigureMotorModel(this.handle, this.rawAxis(), model);
// }
//
// public configureMotorVelocity(targetVel: number, factor: number) {
// this.rawSet.jointConfigureMotorVelocity(this.handle, this.rawAxis(), targetVel, factor);
// }
//
// public configureMotorPosition(targetPos: number, stiffness: number, damping: number) {
// this.rawSet.jointConfigureMotorPosition(this.handle, this.rawAxis(), targetPos, stiffness, damping);
// }
//
// public configureMotor(targetPos: number, targetVel: number, stiffness: number, damping: number) {
// this.rawSet.jointConfigureMotor(this.handle, this.rawAxis(), targetPos, targetVel, stiffness, damping);
// }
}
export class FixedMultibodyJoint extends MultibodyJoint {}
export class PrismaticMultibodyJoint extends UnitMultibodyJoint {
public rawAxis(): RawJointAxis {
return RawJointAxis.LinX;
}
}
export class RevoluteMultibodyJoint extends UnitMultibodyJoint {
public rawAxis(): RawJointAxis {
return RawJointAxis.AngX;
}
}

View File

@@ -0,0 +1,154 @@
import {RawMultibodyJointSet} from "../raw";
import {Coarena} from "../coarena";
import {RigidBodySet} from "./rigid_body_set";
import {
MultibodyJoint,
MultibodyJointHandle,
RevoluteMultibodyJoint,
FixedMultibodyJoint,
PrismaticMultibodyJoint,
} from "./multibody_joint";
import {ImpulseJointHandle, JointData, JointType} from "./impulse_joint";
import {IslandManager} from "./island_manager";
import {ColliderHandle} from "../geometry";
import {RigidBodyHandle} from "./rigid_body";
/**
* A set of joints.
*
* To avoid leaking WASM resources, this MUST be freed manually with `jointSet.free()`
* once you are done using it (and all the joints it created).
*/
export class MultibodyJointSet {
raw: RawMultibodyJointSet;
private map: Coarena<MultibodyJoint>;
/**
* Release the WASM memory occupied by this joint set.
*/
public free() {
if (!!this.raw) {
this.raw.free();
}
this.raw = undefined;
if (!!this.map) {
this.map.clear();
}
this.map = undefined;
}
constructor(raw?: RawMultibodyJointSet) {
this.raw = raw || new RawMultibodyJointSet();
this.map = new Coarena<MultibodyJoint>();
// Initialize the map with the existing elements, if any.
if (raw) {
raw.forEachJointHandle((handle: MultibodyJointHandle) => {
this.map.set(handle, MultibodyJoint.newTyped(this.raw, handle));
});
}
}
/**
* Creates a new joint and return its integer handle.
*
* @param desc - The joint's parameters.
* @param parent1 - The handle of the first rigid-body this joint is attached to.
* @param parent2 - The handle of the second rigid-body this joint is attached to.
* @param wakeUp - Should the attached rigid-bodies be awakened?
*/
public createJoint(
desc: JointData,
parent1: RigidBodyHandle,
parent2: RigidBodyHandle,
wakeUp: boolean,
): MultibodyJoint {
const rawParams = desc.intoRaw();
const handle = this.raw.createJoint(
rawParams,
parent1,
parent2,
wakeUp,
);
rawParams.free();
let joint = MultibodyJoint.newTyped(this.raw, handle);
this.map.set(handle, joint);
return joint;
}
/**
* Remove a joint from this set.
*
* @param handle - The integer handle of the joint.
* @param wake_up - If `true`, the rigid-bodies attached by the removed joint will be woken-up automatically.
*/
public remove(handle: MultibodyJointHandle, wake_up: boolean) {
this.raw.remove(handle, wake_up);
this.map.delete(handle);
}
/**
* Internal function, do not call directly.
* @param handle
*/
public unmap(handle: MultibodyJointHandle) {
this.map.delete(handle);
}
/**
* The number of joints on this set.
*/
public len(): number {
return this.map.len();
}
/**
* Does this set contain a joint with the given handle?
*
* @param handle - The joint handle to check.
*/
public contains(handle: MultibodyJointHandle): boolean {
return this.get(handle) != null;
}
/**
* Gets the joint with the given handle.
*
* Returns `null` if no joint with the specified handle exists.
*
* @param handle - The integer handle of the joint to retrieve.
*/
public get(handle: MultibodyJointHandle): MultibodyJoint | null {
return this.map.get(handle);
}
/**
* Applies the given closure to each joint contained by this set.
*
* @param f - The closure to apply.
*/
public forEach(f: (joint: MultibodyJoint) => void) {
this.map.forEach(f);
}
/**
* Calls the given closure with the integer handle of each multibody joint attached to this rigid-body.
*
* @param f - The closure called with the integer handle of each multibody joint attached to the rigid-body.
*/
public forEachJointHandleAttachedToRigidBody(
handle: RigidBodyHandle,
f: (handle: MultibodyJointHandle) => void,
) {
this.raw.forEachJointAttachedToRigidBody(handle, f);
}
/**
* Gets all joints in the list.
*
* @returns joint list.
*/
public getAll(): MultibodyJoint[] {
return this.map.getAll();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,211 @@
import {RawRigidBodySet, RawRigidBodyType} from "../raw";
import {Coarena} from "../coarena";
import {VectorOps, RotationOps} from "../math";
import {
RigidBody,
RigidBodyDesc,
RigidBodyHandle,
RigidBodyType,
} from "./rigid_body";
import {ColliderSet} from "../geometry";
import {ImpulseJointSet} from "./impulse_joint_set";
import {MultibodyJointSet} from "./multibody_joint_set";
import {IslandManager} from "./island_manager";
/**
* A set of rigid bodies that can be handled by a physics pipeline.
*
* To avoid leaking WASM resources, this MUST be freed manually with `rigidBodySet.free()`
* once you are done using it (and all the rigid-bodies it created).
*/
export class RigidBodySet {
raw: RawRigidBodySet;
private map: Coarena<RigidBody>;
/**
* Release the WASM memory occupied by this rigid-body set.
*/
public free() {
if (!!this.raw) {
this.raw.free();
}
this.raw = undefined;
if (!!this.map) {
this.map.clear();
}
this.map = undefined;
}
constructor(raw?: RawRigidBodySet) {
this.raw = raw || new RawRigidBodySet();
this.map = new Coarena<RigidBody>();
// deserialize
if (raw) {
raw.forEachRigidBodyHandle((handle: RigidBodyHandle) => {
this.map.set(handle, new RigidBody(raw, null, handle));
});
}
}
/**
* Internal method, do not call this explicitly.
*/
public finalizeDeserialization(colliderSet: ColliderSet) {
this.map.forEach((rb) => rb.finalizeDeserialization(colliderSet));
}
/**
* Creates a new rigid-body and return its integer handle.
*
* @param desc - The description of the rigid-body to create.
*/
public createRigidBody(
colliderSet: ColliderSet,
desc: RigidBodyDesc,
): RigidBody {
let rawTra = VectorOps.intoRaw(desc.translation);
let rawRot = RotationOps.intoRaw(desc.rotation);
let rawLv = VectorOps.intoRaw(desc.linvel);
let rawCom = VectorOps.intoRaw(desc.centerOfMass);
let handle = this.raw.createRigidBody(
desc.enabled,
rawTra,
rawRot,
desc.gravityScale,
desc.mass,
desc.massOnly,
rawCom,
rawLv,
desc.angvel,
desc.principalAngularInertia,
desc.translationsEnabledX,
desc.translationsEnabledY,
desc.rotationsEnabled,
desc.linearDamping,
desc.angularDamping,
desc.status as number as RawRigidBodyType,
desc.canSleep,
desc.sleeping,
desc.softCcdPrediction,
desc.ccdEnabled,
desc.dominanceGroup,
desc.additionalSolverIterations,
);
rawTra.free();
rawRot.free();
rawLv.free();
rawCom.free();
const body = new RigidBody(this.raw, colliderSet, handle);
body.userData = desc.userData;
this.map.set(handle, body);
return body;
}
/**
* Removes a rigid-body from this set.
*
* This will also remove all the colliders and joints attached to the rigid-body.
*
* @param handle - The integer handle of the rigid-body to remove.
* @param colliders - The set of colliders that may contain colliders attached to the removed rigid-body.
* @param impulseJoints - The set of impulse joints that may contain joints attached to the removed rigid-body.
* @param multibodyJoints - The set of multibody joints that may contain joints attached to the removed rigid-body.
*/
public remove(
handle: RigidBodyHandle,
islands: IslandManager,
colliders: ColliderSet,
impulseJoints: ImpulseJointSet,
multibodyJoints: MultibodyJointSet,
) {
// Unmap the entities that will be removed automatically because of the rigid-body removals.
for (let i = 0; i < this.raw.rbNumColliders(handle); i += 1) {
colliders.unmap(this.raw.rbCollider(handle, i));
}
impulseJoints.forEachJointHandleAttachedToRigidBody(handle, (handle) =>
impulseJoints.unmap(handle),
);
multibodyJoints.forEachJointHandleAttachedToRigidBody(
handle,
(handle) => multibodyJoints.unmap(handle),
);
// Remove the rigid-body.
this.raw.remove(
handle,
islands.raw,
colliders.raw,
impulseJoints.raw,
multibodyJoints.raw,
);
this.map.delete(handle);
}
/**
* The number of rigid-bodies on this set.
*/
public len(): number {
return this.map.len();
}
/**
* Does this set contain a rigid-body with the given handle?
*
* @param handle - The rigid-body handle to check.
*/
public contains(handle: RigidBodyHandle): boolean {
return this.get(handle) != null;
}
/**
* Gets the rigid-body with the given handle.
*
* @param handle - The handle of the rigid-body to retrieve.
*/
public get(handle: RigidBodyHandle): RigidBody | null {
return this.map.get(handle);
}
/**
* Applies the given closure to each rigid-body contained by this set.
*
* @param f - The closure to apply.
*/
public forEach(f: (body: RigidBody) => void) {
this.map.forEach(f);
}
/**
* Applies the given closure to each active rigid-bodies contained by this set.
*
* A rigid-body is active if it is not sleeping, i.e., if it moved recently.
*
* @param f - The closure to apply.
*/
public forEachActiveRigidBody(
islands: IslandManager,
f: (body: RigidBody) => void,
) {
islands.forEachActiveRigidBodyHandle((handle) => {
f(this.get(handle));
});
}
/**
* Gets all rigid-bodies in the list.
*
* @returns rigid-bodies list.
*/
public getAll(): RigidBody[] {
return this.map.getAll();
}
}

View File

@@ -0,0 +1,27 @@
import {version as vers, reserve_memory as reserve} from "./raw";
export function version(): string {
return vers();
}
/// Reserves additional memory in WASM land.
///
/// This will grow the internal WASM memory buffer so that it can fit at least
/// the specified amount of extra bytes. This can help reduce future runtime
/// overhead due to dynamic internal memory growth once the limit of the
/// pre-allocated memory is reached.
///
/// This feature is still experimental. Due to the nature of the internal
/// allocator, there can be situations where the allocator decides to perform
/// additional internal memory growth even though not all `extraBytesCount`
/// are occupied yet.
export function reserveMemory(extraBytesCount: number) {
reserve(extraBytesCount);
}
export * from "./math";
export * from "./dynamics";
export * from "./geometry";
export * from "./pipeline";
export * from "./init";
export * from "./control";

View File

@@ -0,0 +1,520 @@
import {RawBroadPhase, RawRayColliderIntersection} from "../raw";
import {RigidBodyHandle, RigidBodySet} from "../dynamics";
import {ColliderSet} from "./collider_set";
import {Ray, RayColliderHit, RayColliderIntersection} from "./ray";
import {InteractionGroups} from "./interaction_groups";
import {ColliderHandle} from "./collider";
import {Rotation, RotationOps, Vector, VectorOps} from "../math";
import {Shape} from "./shape";
import {PointColliderProjection} from "./point";
import {ColliderShapeCastHit} from "./toi";
import {QueryFilterFlags} from "../pipeline";
import {NarrowPhase} from "./narrow_phase";
/**
* The broad-phase used for coarse collision-detection.
*
* To avoid leaking WASM resources, this MUST be freed manually with `broadPhase.free()`
* once you are done using it.
*/
export class BroadPhase {
raw: RawBroadPhase;
/**
* Release the WASM memory occupied by this broad-phase.
*/
public free() {
if (!!this.raw) {
this.raw.free();
}
this.raw = undefined;
}
constructor(raw?: RawBroadPhase) {
this.raw = raw || new RawBroadPhase();
}
/**
* Find the closest intersection between a ray and a set of collider.
*
* @param colliders - The set of colliders taking part in this pipeline.
* @param ray - The ray to cast.
* @param maxToi - The maximum time-of-impact that can be reported by this cast. This effectively
* limits the length of the ray to `ray.dir.norm() * maxToi`.
* @param solid - If `false` then the ray will attempt to hit the boundary of a shape, even if its
* origin already lies inside of a shape. In other terms, `true` implies that all shapes are plain,
* whereas `false` implies that all shapes are hollow for this ray-cast.
* @param groups - Used to filter the colliders that can or cannot be hit by the ray.
* @param filter - The callback to filter out which collider will be hit.
*/
public castRay(
narrowPhase: NarrowPhase,
bodies: RigidBodySet,
colliders: ColliderSet,
ray: Ray,
maxToi: number,
solid: boolean,
filterFlags?: QueryFilterFlags,
filterGroups?: InteractionGroups,
filterExcludeCollider?: ColliderHandle,
filterExcludeRigidBody?: RigidBodyHandle,
filterPredicate?: (collider: ColliderHandle) => boolean,
): RayColliderHit | null {
let rawOrig = VectorOps.intoRaw(ray.origin);
let rawDir = VectorOps.intoRaw(ray.dir);
let result = RayColliderHit.fromRaw(
colliders,
this.raw.castRay(
narrowPhase.raw,
bodies.raw,
colliders.raw,
rawOrig,
rawDir,
maxToi,
solid,
filterFlags,
filterGroups,
filterExcludeCollider,
filterExcludeRigidBody,
filterPredicate,
),
);
rawOrig.free();
rawDir.free();
return result;
}
/**
* Find the closest intersection between a ray and a set of collider.
*
* This also computes the normal at the hit point.
* @param colliders - The set of colliders taking part in this pipeline.
* @param ray - The ray to cast.
* @param maxToi - The maximum time-of-impact that can be reported by this cast. This effectively
* limits the length of the ray to `ray.dir.norm() * maxToi`.
* @param solid - If `false` then the ray will attempt to hit the boundary of a shape, even if its
* origin already lies inside of a shape. In other terms, `true` implies that all shapes are plain,
* whereas `false` implies that all shapes are hollow for this ray-cast.
* @param groups - Used to filter the colliders that can or cannot be hit by the ray.
*/
public castRayAndGetNormal(
narrowPhase: NarrowPhase,
bodies: RigidBodySet,
colliders: ColliderSet,
ray: Ray,
maxToi: number,
solid: boolean,
filterFlags?: QueryFilterFlags,
filterGroups?: InteractionGroups,
filterExcludeCollider?: ColliderHandle,
filterExcludeRigidBody?: RigidBodyHandle,
filterPredicate?: (collider: ColliderHandle) => boolean,
): RayColliderIntersection | null {
let rawOrig = VectorOps.intoRaw(ray.origin);
let rawDir = VectorOps.intoRaw(ray.dir);
let result = RayColliderIntersection.fromRaw(
colliders,
this.raw.castRayAndGetNormal(
narrowPhase.raw,
bodies.raw,
colliders.raw,
rawOrig,
rawDir,
maxToi,
solid,
filterFlags,
filterGroups,
filterExcludeCollider,
filterExcludeRigidBody,
filterPredicate,
),
);
rawOrig.free();
rawDir.free();
return result;
}
/**
* Cast a ray and collects all the intersections between a ray and the scene.
*
* @param colliders - The set of colliders taking part in this pipeline.
* @param ray - The ray to cast.
* @param maxToi - The maximum time-of-impact that can be reported by this cast. This effectively
* limits the length of the ray to `ray.dir.norm() * maxToi`.
* @param solid - If `false` then the ray will attempt to hit the boundary of a shape, even if its
* origin already lies inside of a shape. In other terms, `true` implies that all shapes are plain,
* whereas `false` implies that all shapes are hollow for this ray-cast.
* @param groups - Used to filter the colliders that can or cannot be hit by the ray.
* @param callback - The callback called once per hit (in no particular order) between a ray and a collider.
* If this callback returns `false`, then the cast will stop and no further hits will be detected/reported.
*/
public intersectionsWithRay(
narrowPhase: NarrowPhase,
bodies: RigidBodySet,
colliders: ColliderSet,
ray: Ray,
maxToi: number,
solid: boolean,
callback: (intersect: RayColliderIntersection) => boolean,
filterFlags?: QueryFilterFlags,
filterGroups?: InteractionGroups,
filterExcludeCollider?: ColliderHandle,
filterExcludeRigidBody?: RigidBodyHandle,
filterPredicate?: (collider: ColliderHandle) => boolean,
) {
let rawOrig = VectorOps.intoRaw(ray.origin);
let rawDir = VectorOps.intoRaw(ray.dir);
let rawCallback = (rawInter: RawRayColliderIntersection) => {
return callback(
RayColliderIntersection.fromRaw(colliders, rawInter),
);
};
this.raw.intersectionsWithRay(
narrowPhase.raw,
bodies.raw,
colliders.raw,
rawOrig,
rawDir,
maxToi,
solid,
rawCallback,
filterFlags,
filterGroups,
filterExcludeCollider,
filterExcludeRigidBody,
filterPredicate,
);
rawOrig.free();
rawDir.free();
}
/**
* Gets the handle of up to one collider intersecting the given shape.
*
* @param colliders - The set of colliders taking part in this pipeline.
* @param shapePos - The position of the shape used for the intersection test.
* @param shapeRot - The orientation of the shape used for the intersection test.
* @param shape - The shape used for the intersection test.
* @param groups - The bit groups and filter associated to the ray, in order to only
* hit the colliders with collision groups compatible with the ray's group.
*/
public intersectionWithShape(
narrowPhase: NarrowPhase,
bodies: RigidBodySet,
colliders: ColliderSet,
shapePos: Vector,
shapeRot: Rotation,
shape: Shape,
filterFlags?: QueryFilterFlags,
filterGroups?: InteractionGroups,
filterExcludeCollider?: ColliderHandle,
filterExcludeRigidBody?: RigidBodyHandle,
filterPredicate?: (collider: ColliderHandle) => boolean,
): ColliderHandle | null {
let rawPos = VectorOps.intoRaw(shapePos);
let rawRot = RotationOps.intoRaw(shapeRot);
let rawShape = shape.intoRaw();
let result = this.raw.intersectionWithShape(
narrowPhase.raw,
bodies.raw,
colliders.raw,
rawPos,
rawRot,
rawShape,
filterFlags,
filterGroups,
filterExcludeCollider,
filterExcludeRigidBody,
filterPredicate,
);
rawPos.free();
rawRot.free();
rawShape.free();
return result;
}
/**
* Find the projection of a point on the closest collider.
*
* @param colliders - The set of colliders taking part in this pipeline.
* @param point - The point to project.
* @param solid - If this is set to `true` then the collider shapes are considered to
* be plain (if the point is located inside of a plain shape, its projection is the point
* itself). If it is set to `false` the collider shapes are considered to be hollow
* (if the point is located inside of an hollow shape, it is projected on the shape's
* boundary).
* @param groups - The bit groups and filter associated to the point to project, in order to only
* project on colliders with collision groups compatible with the ray's group.
*/
public projectPoint(
narrowPhase: NarrowPhase,
bodies: RigidBodySet,
colliders: ColliderSet,
point: Vector,
solid: boolean,
filterFlags?: QueryFilterFlags,
filterGroups?: InteractionGroups,
filterExcludeCollider?: ColliderHandle,
filterExcludeRigidBody?: RigidBodyHandle,
filterPredicate?: (collider: ColliderHandle) => boolean,
): PointColliderProjection | null {
let rawPoint = VectorOps.intoRaw(point);
let result = PointColliderProjection.fromRaw(
colliders,
this.raw.projectPoint(
narrowPhase.raw,
bodies.raw,
colliders.raw,
rawPoint,
solid,
filterFlags,
filterGroups,
filterExcludeCollider,
filterExcludeRigidBody,
filterPredicate,
),
);
rawPoint.free();
return result;
}
/**
* Find the projection of a point on the closest collider.
*
* @param colliders - The set of colliders taking part in this pipeline.
* @param point - The point to project.
* @param groups - The bit groups and filter associated to the point to project, in order to only
* project on colliders with collision groups compatible with the ray's group.
*/
public projectPointAndGetFeature(
narrowPhase: NarrowPhase,
bodies: RigidBodySet,
colliders: ColliderSet,
point: Vector,
filterFlags?: QueryFilterFlags,
filterGroups?: InteractionGroups,
filterExcludeCollider?: ColliderHandle,
filterExcludeRigidBody?: RigidBodyHandle,
filterPredicate?: (collider: ColliderHandle) => boolean,
): PointColliderProjection | null {
let rawPoint = VectorOps.intoRaw(point);
let result = PointColliderProjection.fromRaw(
colliders,
this.raw.projectPointAndGetFeature(
narrowPhase.raw,
bodies.raw,
colliders.raw,
rawPoint,
filterFlags,
filterGroups,
filterExcludeCollider,
filterExcludeRigidBody,
filterPredicate,
),
);
rawPoint.free();
return result;
}
/**
* Find all the colliders containing the given point.
*
* @param colliders - The set of colliders taking part in this pipeline.
* @param point - The point used for the containment test.
* @param groups - The bit groups and filter associated to the point to test, in order to only
* test on colliders with collision groups compatible with the ray's group.
* @param callback - A function called with the handles of each collider with a shape
* containing the `point`.
*/
public intersectionsWithPoint(
narrowPhase: NarrowPhase,
bodies: RigidBodySet,
colliders: ColliderSet,
point: Vector,
callback: (handle: ColliderHandle) => boolean,
filterFlags?: QueryFilterFlags,
filterGroups?: InteractionGroups,
filterExcludeCollider?: ColliderHandle,
filterExcludeRigidBody?: RigidBodyHandle,
filterPredicate?: (collider: ColliderHandle) => boolean,
) {
let rawPoint = VectorOps.intoRaw(point);
this.raw.intersectionsWithPoint(
narrowPhase.raw,
bodies.raw,
colliders.raw,
rawPoint,
callback,
filterFlags,
filterGroups,
filterExcludeCollider,
filterExcludeRigidBody,
filterPredicate,
);
rawPoint.free();
}
/**
* Casts a shape at a constant linear velocity and retrieve the first collider it hits.
* This is similar to ray-casting except that we are casting a whole shape instead of
* just a point (the ray origin).
*
* @param colliders - The set of colliders taking part in this pipeline.
* @param shapePos - The initial position of the shape to cast.
* @param shapeRot - The initial rotation of the shape to cast.
* @param shapeVel - The constant velocity of the shape to cast (i.e. the cast direction).
* @param shape - The shape to cast.
* @param targetDistance If the shape moves closer to this distance from a collider, a hit
* will be returned.
* @param maxToi - The maximum time-of-impact that can be reported by this cast. This effectively
* limits the distance traveled by the shape to `shapeVel.norm() * maxToi`.
* @param stopAtPenetration - If set to `false`, the linear shape-cast wont immediately stop if
* the shape is penetrating another shape at its starting point **and** its trajectory is such
* that its on a path to exit that penetration state.
* @param groups - The bit groups and filter associated to the shape to cast, in order to only
* test on colliders with collision groups compatible with this group.
*/
public castShape(
narrowPhase: NarrowPhase,
bodies: RigidBodySet,
colliders: ColliderSet,
shapePos: Vector,
shapeRot: Rotation,
shapeVel: Vector,
shape: Shape,
targetDistance: number,
maxToi: number,
stopAtPenetration: boolean,
filterFlags?: QueryFilterFlags,
filterGroups?: InteractionGroups,
filterExcludeCollider?: ColliderHandle,
filterExcludeRigidBody?: RigidBodyHandle,
filterPredicate?: (collider: ColliderHandle) => boolean,
): ColliderShapeCastHit | null {
let rawPos = VectorOps.intoRaw(shapePos);
let rawRot = RotationOps.intoRaw(shapeRot);
let rawVel = VectorOps.intoRaw(shapeVel);
let rawShape = shape.intoRaw();
let result = ColliderShapeCastHit.fromRaw(
colliders,
this.raw.castShape(
narrowPhase.raw,
bodies.raw,
colliders.raw,
rawPos,
rawRot,
rawVel,
rawShape,
targetDistance,
maxToi,
stopAtPenetration,
filterFlags,
filterGroups,
filterExcludeCollider,
filterExcludeRigidBody,
filterPredicate,
),
);
rawPos.free();
rawRot.free();
rawVel.free();
rawShape.free();
return result;
}
/**
* Retrieve all the colliders intersecting the given shape.
*
* @param colliders - The set of colliders taking part in this pipeline.
* @param shapePos - The position of the shape to test.
* @param shapeRot - The orientation of the shape to test.
* @param shape - The shape to test.
* @param groups - The bit groups and filter associated to the shape to test, in order to only
* test on colliders with collision groups compatible with this group.
* @param callback - A function called with the handles of each collider intersecting the `shape`.
*/
public intersectionsWithShape(
narrowPhase: NarrowPhase,
bodies: RigidBodySet,
colliders: ColliderSet,
shapePos: Vector,
shapeRot: Rotation,
shape: Shape,
callback: (handle: ColliderHandle) => boolean,
filterFlags?: QueryFilterFlags,
filterGroups?: InteractionGroups,
filterExcludeCollider?: ColliderHandle,
filterExcludeRigidBody?: RigidBodyHandle,
filterPredicate?: (collider: ColliderHandle) => boolean,
) {
let rawPos = VectorOps.intoRaw(shapePos);
let rawRot = RotationOps.intoRaw(shapeRot);
let rawShape = shape.intoRaw();
this.raw.intersectionsWithShape(
narrowPhase.raw,
bodies.raw,
colliders.raw,
rawPos,
rawRot,
rawShape,
callback,
filterFlags,
filterGroups,
filterExcludeCollider,
filterExcludeRigidBody,
filterPredicate,
);
rawPos.free();
rawRot.free();
rawShape.free();
}
/**
* Finds the handles of all the colliders with an AABB intersecting the given AABB.
*
* @param aabbCenter - The center of the AABB to test.
* @param aabbHalfExtents - The half-extents of the AABB to test.
* @param callback - The callback that will be called with the handles of all the colliders
* currently intersecting the given AABB.
*/
public collidersWithAabbIntersectingAabb(
narrowPhase: NarrowPhase,
bodies: RigidBodySet,
colliders: ColliderSet,
aabbCenter: Vector,
aabbHalfExtents: Vector,
callback: (handle: ColliderHandle) => boolean,
) {
let rawCenter = VectorOps.intoRaw(aabbCenter);
let rawHalfExtents = VectorOps.intoRaw(aabbHalfExtents);
this.raw.collidersWithAabbIntersectingAabb(
narrowPhase.raw,
bodies.raw,
colliders.raw,
rawCenter,
rawHalfExtents,
callback,
);
rawCenter.free();
rawHalfExtents.free();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,195 @@
import {RawColliderSet} from "../raw";
import {Coarena} from "../coarena";
import {RotationOps, VectorOps} from "../math";
import {Collider, ColliderDesc, ColliderHandle} from "./collider";
import {ImpulseJointHandle, IslandManager, RigidBodyHandle} from "../dynamics";
import {RigidBodySet} from "../dynamics";
/**
* A set of rigid bodies that can be handled by a physics pipeline.
*
* To avoid leaking WASM resources, this MUST be freed manually with `colliderSet.free()`
* once you are done using it (and all the rigid-bodies it created).
*/
export class ColliderSet {
raw: RawColliderSet;
private map: Coarena<Collider>;
/**
* Release the WASM memory occupied by this collider set.
*/
public free() {
if (!!this.raw) {
this.raw.free();
}
this.raw = undefined;
if (!!this.map) {
this.map.clear();
}
this.map = undefined;
}
constructor(raw?: RawColliderSet) {
this.raw = raw || new RawColliderSet();
this.map = new Coarena<Collider>();
// Initialize the map with the existing elements, if any.
if (raw) {
raw.forEachColliderHandle((handle: ColliderHandle) => {
this.map.set(handle, new Collider(this, handle, null));
});
}
}
/** @internal */
public castClosure<Res>(
f?: (collider: Collider) => Res,
): (handle: ColliderHandle) => Res | undefined {
return (handle) => {
if (!!f) {
return f(this.get(handle));
} else {
return undefined;
}
};
}
/** @internal */
public finalizeDeserialization(bodies: RigidBodySet) {
this.map.forEach((collider) =>
collider.finalizeDeserialization(bodies),
);
}
/**
* Creates a new collider and return its integer handle.
*
* @param bodies - The set of bodies where the collider's parent can be found.
* @param desc - The collider's description.
* @param parentHandle - The integer handle of the rigid-body this collider is attached to.
*/
public createCollider(
bodies: RigidBodySet,
desc: ColliderDesc,
parentHandle: RigidBodyHandle,
): Collider {
let hasParent = parentHandle != undefined && parentHandle != null;
if (hasParent && isNaN(parentHandle))
throw Error(
"Cannot create a collider with a parent rigid-body handle that is not a number.",
);
let rawShape = desc.shape.intoRaw();
let rawTra = VectorOps.intoRaw(desc.translation);
let rawRot = RotationOps.intoRaw(desc.rotation);
let rawCom = VectorOps.intoRaw(desc.centerOfMass);
let handle = this.raw.createCollider(
desc.enabled,
rawShape,
rawTra,
rawRot,
desc.massPropsMode,
desc.mass,
rawCom,
desc.principalAngularInertia,
desc.density,
desc.friction,
desc.restitution,
desc.frictionCombineRule,
desc.restitutionCombineRule,
desc.isSensor,
desc.collisionGroups,
desc.solverGroups,
desc.activeCollisionTypes,
desc.activeHooks,
desc.activeEvents,
desc.contactForceEventThreshold,
desc.contactSkin,
hasParent,
hasParent ? parentHandle : 0,
bodies.raw,
);
rawShape.free();
rawTra.free();
rawRot.free();
rawCom.free();
let parent = hasParent ? bodies.get(parentHandle) : null;
let collider = new Collider(this, handle, parent, desc.shape);
this.map.set(handle, collider);
return collider;
}
/**
* Remove a collider from this set.
*
* @param handle - The integer handle of the collider to remove.
* @param bodies - The set of rigid-body containing the rigid-body the collider is attached to.
* @param wakeUp - If `true`, the rigid-body the removed collider is attached to will be woken-up automatically.
*/
public remove(
handle: ColliderHandle,
islands: IslandManager,
bodies: RigidBodySet,
wakeUp: boolean,
) {
this.raw.remove(handle, islands.raw, bodies.raw, wakeUp);
this.unmap(handle);
}
/**
* Internal function, do not call directly.
* @param handle
*/
public unmap(handle: ImpulseJointHandle) {
this.map.delete(handle);
}
/**
* Gets the rigid-body with the given handle.
*
* @param handle - The handle of the rigid-body to retrieve.
*/
public get(handle: ColliderHandle): Collider | null {
return this.map.get(handle);
}
/**
* The number of colliders on this set.
*/
public len(): number {
return this.map.len();
}
/**
* Does this set contain a collider with the given handle?
*
* @param handle - The collider handle to check.
*/
public contains(handle: ColliderHandle): boolean {
return this.get(handle) != null;
}
/**
* Applies the given closure to each collider contained by this set.
*
* @param f - The closure to apply.
*/
public forEach(f: (collider: Collider) => void) {
this.map.forEach(f);
}
/**
* Gets all colliders in the list.
*
* @returns collider list.
*/
public getAll(): Collider[] {
return this.map.getAll();
}
}

View File

@@ -0,0 +1,62 @@
import {Vector, VectorOps} from "../math";
import {RawShapeContact} from "../raw";
/**
* The contact info between two shapes.
*/
export class ShapeContact {
/**
* Distance between the two contact points.
* If this is negative, this contact represents a penetration.
*/
distance: number;
/**
* Position of the contact on the first shape.
*/
point1: Vector;
/**
* Position of the contact on the second shape.
*/
point2: Vector;
/**
* Contact normal, pointing towards the exterior of the first shape.
*/
normal1: Vector;
/**
* Contact normal, pointing towards the exterior of the second shape.
* If these contact data are expressed in world-space, this normal is equal to -normal1.
*/
normal2: Vector;
constructor(
dist: number,
point1: Vector,
point2: Vector,
normal1: Vector,
normal2: Vector,
) {
this.distance = dist;
this.point1 = point1;
this.point2 = point2;
this.normal1 = normal1;
this.normal2 = normal2;
}
public static fromRaw(raw: RawShapeContact): ShapeContact {
if (!raw) return null;
const result = new ShapeContact(
raw.distance(),
VectorOps.fromRaw(raw.point1()),
VectorOps.fromRaw(raw.point2()),
VectorOps.fromRaw(raw.normal1()),
VectorOps.fromRaw(raw.normal2()),
);
raw.free();
return result;
}
}

View File

@@ -0,0 +1,6 @@
export enum FeatureType {
Vertex,
Face,
Unknown,
}

View File

@@ -0,0 +1,11 @@
export * from "./broad_phase";
export * from "./narrow_phase";
export * from "./shape";
export * from "./collider";
export * from "./collider_set";
export * from "./feature";
export * from "./ray";
export * from "./point";
export * from "./toi";
export * from "./interaction_groups";
export * from "./contact";

View File

@@ -0,0 +1,18 @@
/**
* Pairwise filtering using bit masks.
*
* This filtering method is based on two 16-bit values:
* - The interaction groups (the 16 left-most bits of `self.0`).
* - The interaction mask (the 16 right-most bits of `self.0`).
*
* An interaction is allowed between two filters `a` and `b` two conditions
* are met simultaneously:
* - The interaction groups of `a` has at least one bit set to `1` in common with the interaction mask of `b`.
* - The interaction groups of `b` has at least one bit set to `1` in common with the interaction mask of `a`.
* In other words, interactions are allowed between two filter iff. the following condition is met:
*
* ```
* ((a >> 16) & b) != 0 && ((b >> 16) & a) != 0
* ```
*/
export type InteractionGroups = number;

View File

@@ -0,0 +1,192 @@
import {RawNarrowPhase, RawContactManifold} from "../raw";
import {ColliderHandle} from "./collider";
import {Vector, VectorOps} from "../math";
/**
* The narrow-phase used for precise collision-detection.
*
* To avoid leaking WASM resources, this MUST be freed manually with `narrowPhase.free()`
* once you are done using it.
*/
export class NarrowPhase {
raw: RawNarrowPhase;
tempManifold: TempContactManifold;
/**
* Release the WASM memory occupied by this narrow-phase.
*/
public free() {
if (!!this.raw) {
this.raw.free();
}
this.raw = undefined;
}
constructor(raw?: RawNarrowPhase) {
this.raw = raw || new RawNarrowPhase();
this.tempManifold = new TempContactManifold(null);
}
/**
* Enumerates all the colliders potentially in contact with the given collider.
*
* @param collider1 - The second collider involved in the contact.
* @param f - Closure that will be called on each collider that is in contact with `collider1`.
*/
public contactPairsWith(
collider1: ColliderHandle,
f: (collider2: ColliderHandle) => void,
) {
this.raw.contact_pairs_with(collider1, f);
}
/**
* Enumerates all the colliders intersecting the given colliders, assuming one of them
* is a sensor.
*/
public intersectionPairsWith(
collider1: ColliderHandle,
f: (collider2: ColliderHandle) => void,
) {
this.raw.intersection_pairs_with(collider1, f);
}
/**
* Iterates through all the contact manifolds between the given pair of colliders.
*
* @param collider1 - The first collider involved in the contact.
* @param collider2 - The second collider involved in the contact.
* @param f - Closure that will be called on each contact manifold between the two colliders. If the second argument
* passed to this closure is `true`, then the contact manifold data is flipped, i.e., methods like `localNormal1`
* actually apply to the `collider2` and fields like `localNormal2` apply to the `collider1`.
*/
public contactPair(
collider1: ColliderHandle,
collider2: ColliderHandle,
f: (manifold: TempContactManifold, flipped: boolean) => void,
) {
const rawPair = this.raw.contact_pair(collider1, collider2);
if (!!rawPair) {
const flipped = rawPair.collider1() != collider1;
let i;
for (i = 0; i < rawPair.numContactManifolds(); ++i) {
this.tempManifold.raw = rawPair.contactManifold(i);
if (!!this.tempManifold.raw) {
f(this.tempManifold, flipped);
}
// SAFETY: The RawContactManifold stores a raw pointer that will be invalidated
// at the next timestep. So we must be sure to free the pair here
// to avoid unsoundness in the Rust code.
this.tempManifold.free();
}
rawPair.free();
}
}
/**
* Returns `true` if `collider1` and `collider2` intersect and at least one of them is a sensor.
* @param collider1 The first collider involved in the intersection.
* @param collider2 The second collider involved in the intersection.
*/
public intersectionPair(
collider1: ColliderHandle,
collider2: ColliderHandle,
): boolean {
return this.raw.intersection_pair(collider1, collider2);
}
}
export class TempContactManifold {
raw: RawContactManifold;
public free() {
if (!!this.raw) {
this.raw.free();
}
this.raw = undefined;
}
constructor(raw: RawContactManifold) {
this.raw = raw;
}
public normal(): Vector {
return VectorOps.fromRaw(this.raw.normal());
}
public localNormal1(): Vector {
return VectorOps.fromRaw(this.raw.local_n1());
}
public localNormal2(): Vector {
return VectorOps.fromRaw(this.raw.local_n2());
}
public subshape1(): number {
return this.raw.subshape1();
}
public subshape2(): number {
return this.raw.subshape2();
}
public numContacts(): number {
return this.raw.num_contacts();
}
public localContactPoint1(i: number): Vector | null {
return VectorOps.fromRaw(this.raw.contact_local_p1(i));
}
public localContactPoint2(i: number): Vector | null {
return VectorOps.fromRaw(this.raw.contact_local_p2(i));
}
public contactDist(i: number): number {
return this.raw.contact_dist(i);
}
public contactFid1(i: number): number {
return this.raw.contact_fid1(i);
}
public contactFid2(i: number): number {
return this.raw.contact_fid2(i);
}
public contactImpulse(i: number): number {
return this.raw.contact_impulse(i);
}
public contactTangentImpulse(i: number): number {
return this.raw.contact_tangent_impulse(i);
}
public numSolverContacts(): number {
return this.raw.num_solver_contacts();
}
public solverContactPoint(i: number): Vector {
return VectorOps.fromRaw(this.raw.solver_contact_point(i));
}
public solverContactDist(i: number): number {
return this.raw.solver_contact_dist(i);
}
public solverContactFriction(i: number): number {
return this.raw.solver_contact_friction(i);
}
public solverContactRestitution(i: number): number {
return this.raw.solver_contact_restitution(i);
}
public solverContactTangentVelocity(i: number): Vector {
return VectorOps.fromRaw(this.raw.solver_contact_tangent_velocity(i));
}
}

View File

@@ -0,0 +1,98 @@
import {Collider, ColliderHandle} from "./collider";
import {Vector, VectorOps} from "../math";
import {
RawFeatureType,
RawPointColliderProjection,
RawPointProjection,
} from "../raw";
import {FeatureType} from "./feature";
import {ColliderSet} from "./collider_set";
/**
* The projection of a point on a collider.
*/
export class PointProjection {
/**
* The projection of the point on the collider.
*/
point: Vector;
/**
* Is the point inside of the collider?
*/
isInside: boolean;
constructor(point: Vector, isInside: boolean) {
this.point = point;
this.isInside = isInside;
}
public static fromRaw(raw: RawPointProjection): PointProjection {
if (!raw) return null;
const result = new PointProjection(
VectorOps.fromRaw(raw.point()),
raw.isInside(),
);
raw.free();
return result;
}
}
/**
* The projection of a point on a collider (includes the collider handle).
*/
export class PointColliderProjection {
/**
* The collider hit by the ray.
*/
collider: Collider;
/**
* The projection of the point on the collider.
*/
point: Vector;
/**
* Is the point inside of the collider?
*/
isInside: boolean;
/**
* The type of the geometric feature the point was projected on.
*/
featureType = FeatureType.Unknown;
/**
* The id of the geometric feature the point was projected on.
*/
featureId: number | undefined = undefined;
constructor(
collider: Collider,
point: Vector,
isInside: boolean,
featureType?: FeatureType,
featureId?: number,
) {
this.collider = collider;
this.point = point;
this.isInside = isInside;
if (featureId !== undefined) this.featureId = featureId;
if (featureType !== undefined) this.featureType = featureType;
}
public static fromRaw(
colliderSet: ColliderSet,
raw: RawPointColliderProjection,
): PointColliderProjection {
if (!raw) return null;
const result = new PointColliderProjection(
colliderSet.get(raw.colliderHandle()),
VectorOps.fromRaw(raw.point()),
raw.isInside(),
raw.featureType() as number as FeatureType,
raw.featureId(),
);
raw.free();
return result;
}
}

View File

@@ -0,0 +1,189 @@
import {Vector, VectorOps} from "../math";
import {
RawFeatureType,
RawRayColliderIntersection,
RawRayColliderHit,
RawRayIntersection,
} from "../raw";
import {Collider} from "./collider";
import {FeatureType} from "./feature";
import {ColliderSet} from "./collider_set";
/**
* A ray. This is a directed half-line.
*/
export class Ray {
/**
* The starting point of the ray.
*/
public origin: Vector;
/**
* The direction of propagation of the ray.
*/
public dir: Vector;
/**
* Builds a ray from its origin and direction.
*
* @param origin - The ray's starting point.
* @param dir - The ray's direction of propagation.
*/
constructor(origin: Vector, dir: Vector) {
this.origin = origin;
this.dir = dir;
}
public pointAt(t: number): Vector {
return {
x: this.origin.x + this.dir.x * t,
y: this.origin.y + this.dir.y * t,
};
}
}
/**
* The intersection between a ray and a collider.
*/
export class RayIntersection {
/**
* The time-of-impact of the ray with the collider.
*
* The hit point is obtained from the ray's origin and direction: `origin + dir * timeOfImpact`.
*/
timeOfImpact: number;
/**
* The normal of the collider at the hit point.
*/
normal: Vector;
/**
* The type of the geometric feature the point was projected on.
*/
featureType = FeatureType.Unknown;
/**
* The id of the geometric feature the point was projected on.
*/
featureId: number | undefined = undefined;
constructor(
timeOfImpact: number,
normal: Vector,
featureType?: FeatureType,
featureId?: number,
) {
this.timeOfImpact = timeOfImpact;
this.normal = normal;
if (featureId !== undefined) this.featureId = featureId;
if (featureType !== undefined) this.featureType = featureType;
}
public static fromRaw(raw: RawRayIntersection): RayIntersection {
if (!raw) return null;
const result = new RayIntersection(
raw.time_of_impact(),
VectorOps.fromRaw(raw.normal()),
raw.featureType() as number as FeatureType,
raw.featureId(),
);
raw.free();
return result;
}
}
/**
* The intersection between a ray and a collider (includes the collider handle).
*/
export class RayColliderIntersection {
/**
* The collider hit by the ray.
*/
collider: Collider;
/**
* The time-of-impact of the ray with the collider.
*
* The hit point is obtained from the ray's origin and direction: `origin + dir * timeOfImpact`.
*/
timeOfImpact: number;
/**
* The normal of the collider at the hit point.
*/
normal: Vector;
/**
* The type of the geometric feature the point was projected on.
*/
featureType = FeatureType.Unknown;
/**
* The id of the geometric feature the point was projected on.
*/
featureId: number | undefined = undefined;
constructor(
collider: Collider,
timeOfImpact: number,
normal: Vector,
featureType?: FeatureType,
featureId?: number,
) {
this.collider = collider;
this.timeOfImpact = timeOfImpact;
this.normal = normal;
if (featureId !== undefined) this.featureId = featureId;
if (featureType !== undefined) this.featureType = featureType;
}
public static fromRaw(
colliderSet: ColliderSet,
raw: RawRayColliderIntersection,
): RayColliderIntersection {
if (!raw) return null;
const result = new RayColliderIntersection(
colliderSet.get(raw.colliderHandle()),
raw.time_of_impact(),
VectorOps.fromRaw(raw.normal()),
raw.featureType() as number as FeatureType,
raw.featureId(),
);
raw.free();
return result;
}
}
/**
* The time of impact between a ray and a collider.
*/
export class RayColliderHit {
/**
* The handle of the collider hit by the ray.
*/
collider: Collider;
/**
* The time-of-impact of the ray with the collider.
*
* The hit point is obtained from the ray's origin and direction: `origin + dir * timeOfImpact`.
*/
timeOfImpact: number;
constructor(collider: Collider, timeOfImpact: number) {
this.collider = collider;
this.timeOfImpact = timeOfImpact;
}
public static fromRaw(
colliderSet: ColliderSet,
raw: RawRayColliderHit,
): RayColliderHit {
if (!raw) return null;
const result = new RayColliderHit(
colliderSet.get(raw.colliderHandle()),
raw.timeOfImpact(),
);
raw.free();
return result;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,105 @@
import {Collider} from "./collider";
import {Vector, VectorOps} from "../math";
import {RawShapeCastHit, RawColliderShapeCastHit} from "../raw";
import {ColliderSet} from "./collider_set";
/**
* The intersection between a ray and a collider.
*/
export class ShapeCastHit {
/**
* The time of impact of the two shapes.
*/
time_of_impact: number;
/**
* The local-space contact point on the first shape, at
* the time of impact.
*/
witness1: Vector;
/**
* The local-space contact point on the second shape, at
* the time of impact.
*/
witness2: Vector;
/**
* The local-space normal on the first shape, at
* the time of impact.
*/
normal1: Vector;
/**
* The local-space normal on the second shape, at
* the time of impact.
*/
normal2: Vector;
constructor(
time_of_impact: number,
witness1: Vector,
witness2: Vector,
normal1: Vector,
normal2: Vector,
) {
this.time_of_impact = time_of_impact;
this.witness1 = witness1;
this.witness2 = witness2;
this.normal1 = normal1;
this.normal2 = normal2;
}
public static fromRaw(
colliderSet: ColliderSet,
raw: RawShapeCastHit,
): ShapeCastHit {
if (!raw) return null;
const result = new ShapeCastHit(
raw.time_of_impact(),
VectorOps.fromRaw(raw.witness1()),
VectorOps.fromRaw(raw.witness2()),
VectorOps.fromRaw(raw.normal1()),
VectorOps.fromRaw(raw.normal2()),
);
raw.free();
return result;
}
}
/**
* The intersection between a ray and a collider.
*/
export class ColliderShapeCastHit extends ShapeCastHit {
/**
* The handle of the collider hit by the ray.
*/
collider: Collider;
constructor(
collider: Collider,
time_of_impact: number,
witness1: Vector,
witness2: Vector,
normal1: Vector,
normal2: Vector,
) {
super(time_of_impact, witness1, witness2, normal1, normal2);
this.collider = collider;
}
public static fromRaw(
colliderSet: ColliderSet,
raw: RawColliderShapeCastHit,
): ColliderShapeCastHit {
if (!raw) return null;
const result = new ColliderShapeCastHit(
colliderSet.get(raw.colliderHandle()),
raw.time_of_impact(),
VectorOps.fromRaw(raw.witness1()),
VectorOps.fromRaw(raw.witness2()),
VectorOps.fromRaw(raw.normal1()),
VectorOps.fromRaw(raw.normal2()),
);
raw.free();
return result;
}
}

View File

@@ -0,0 +1,3 @@
import * as RAPIER from "./exports";
export * from "./exports";
export default RAPIER;

View File

@@ -0,0 +1,60 @@
/**
* RAPIER initialization module with dynamic WASM loading support.
* RAPIER 初始化模块,支持动态 WASM 加载。
*/
import wasmInit from "../pkg/rapier_wasm2d";
/**
* Input types for WASM initialization.
* WASM 初始化的输入类型。
*/
export type InitInput =
| RequestInfo // URL string or Request object
| URL // URL object
| Response // Fetch Response object
| BufferSource // ArrayBuffer or TypedArray
| WebAssembly.Module; // Pre-compiled module
let initialized = false;
/**
* Initializes RAPIER.
* Has to be called and awaited before using any library methods.
*
* 初始化 RAPIER。
* 必须在使用任何库方法之前调用并等待。
*
* @param input - WASM source (required). Can be URL, Response, ArrayBuffer, etc.
* WASM 源(必需)。可以是 URL、Response、ArrayBuffer 等。
*
* @example
* // Load from URL | 从 URL 加载
* await RAPIER.init('wasm/rapier_wasm2d_bg.wasm');
*
* @example
* // Load from fetch response | 从 fetch 响应加载
* const response = await fetch('wasm/rapier_wasm2d_bg.wasm');
* await RAPIER.init(response);
*
* @example
* // Load from ArrayBuffer | 从 ArrayBuffer 加载
* const buffer = await fetch('wasm/rapier_wasm2d_bg.wasm').then(r => r.arrayBuffer());
* await RAPIER.init(buffer);
*/
export async function init(input?: InitInput): Promise<void> {
if (initialized) {
return;
}
await wasmInit(input);
initialized = true;
}
/**
* Check if RAPIER is already initialized.
* 检查 RAPIER 是否已初始化。
*/
export function isInitialized(): boolean {
return initialized;
}

View File

@@ -0,0 +1,72 @@
import {RawVector, RawRotation} from "./raw";
export interface Vector {
x: number;
y: number;
}
/**
* A 2D vector.
*/
export class Vector2 implements Vector {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
export class VectorOps {
public static new(x: number, y: number): Vector {
return new Vector2(x, y);
}
public static zeros(): Vector {
return VectorOps.new(0.0, 0.0);
}
// FIXME: type ram: RawVector?
public static fromRaw(raw: RawVector): Vector | null {
if (!raw) return null;
let res = VectorOps.new(raw.x, raw.y);
raw.free();
return res;
}
public static intoRaw(v: Vector): RawVector {
return new RawVector(v.x, v.y);
}
public static copy(out: Vector, input: Vector) {
out.x = input.x;
out.y = input.y;
}
}
/**
* A rotation angle in radians.
*/
export type Rotation = number;
export class RotationOps {
public static identity(): number {
return 0.0;
}
public static fromRaw(raw: RawRotation): Rotation | null {
if (!raw) return null;
let res = raw.angle;
raw.free();
return res;
}
public static intoRaw(angle: Rotation): RawRotation {
return RawRotation.fromAngle(angle);
}
}

View File

@@ -0,0 +1,85 @@
import {RawDebugRenderPipeline} from "../raw";
import {Vector, VectorOps} from "../math";
import {
IntegrationParameters,
IslandManager,
ImpulseJointSet,
MultibodyJointSet,
RigidBodySet,
} from "../dynamics";
import {BroadPhase, Collider, ColliderSet, NarrowPhase} from "../geometry";
import {QueryFilterFlags} from "./query_pipeline";
/**
* The vertex and color buffers for debug-redering the physics scene.
*/
export class DebugRenderBuffers {
/**
* The lines to render. This is a flat array containing all the lines
* to render. Each line is described as two consecutive point. Each
* point is described as two (in 2D) or three (in 3D) consecutive
* floats. For example, in 2D, the array: `[1, 2, 3, 4, 5, 6, 7, 8]`
* describes the two segments `[[1, 2], [3, 4]]` and `[[5, 6], [7, 8]]`.
*/
public vertices: Float32Array;
/**
* The color buffer. There is one color per vertex, and each color
* has four consecutive components (in RGBA format).
*/
public colors: Float32Array;
constructor(vertices: Float32Array, colors: Float32Array) {
this.vertices = vertices;
this.colors = colors;
}
}
/**
* A pipeline for rendering the physics scene.
*
* To avoid leaking WASM resources, this MUST be freed manually with `debugRenderPipeline.free()`
* once you are done using it (and all the rigid-bodies it created).
*/
export class DebugRenderPipeline {
raw: RawDebugRenderPipeline;
public vertices: Float32Array;
public colors: Float32Array;
/**
* Release the WASM memory occupied by this serialization pipeline.
*/
free() {
if (!!this.raw) {
this.raw.free();
}
this.raw = undefined;
this.vertices = undefined;
this.colors = undefined;
}
constructor(raw?: RawDebugRenderPipeline) {
this.raw = raw || new RawDebugRenderPipeline();
}
public render(
bodies: RigidBodySet,
colliders: ColliderSet,
impulse_joints: ImpulseJointSet,
multibody_joints: MultibodyJointSet,
narrow_phase: NarrowPhase,
filterFlags?: QueryFilterFlags,
filterPredicate?: (collider: Collider) => boolean,
) {
this.raw.render(
bodies.raw,
colliders.raw,
impulse_joints.raw,
multibody_joints.raw,
narrow_phase.raw,
filterFlags,
colliders.castClosure(filterPredicate),
);
this.vertices = this.raw.vertices();
this.colors = this.raw.colors();
}
}

View File

@@ -0,0 +1,158 @@
import {RawContactForceEvent, RawEventQueue} from "../raw";
import {RigidBodyHandle} from "../dynamics";
import {Collider, ColliderHandle} from "../geometry";
import {Vector, VectorOps} from "../math";
/**
* Flags indicating what events are enabled for colliders.
*/
export enum ActiveEvents {
NONE = 0,
/**
* Enable collision events.
*/
COLLISION_EVENTS = 0b0001,
/**
* Enable contact force events.
*/
CONTACT_FORCE_EVENTS = 0b0010,
}
/**
* Event occurring when the sum of the magnitudes of the
* contact forces between two colliders exceed a threshold.
*
* This object should **not** be stored anywhere. Its properties can only be
* read from within the closure given to `EventHandler.drainContactForceEvents`.
*/
export class TempContactForceEvent {
raw: RawContactForceEvent;
public free() {
if (!!this.raw) {
this.raw.free();
}
this.raw = undefined;
}
/**
* The first collider involved in the contact.
*/
public collider1(): ColliderHandle {
return this.raw.collider1();
}
/**
* The second collider involved in the contact.
*/
public collider2(): ColliderHandle {
return this.raw.collider2();
}
/**
* The sum of all the forces between the two colliders.
*/
public totalForce(): Vector {
return VectorOps.fromRaw(this.raw.total_force());
}
/**
* The sum of the magnitudes of each force between the two colliders.
*
* Note that this is **not** the same as the magnitude of `self.total_force`.
* Here we are summing the magnitude of all the forces, instead of taking
* the magnitude of their sum.
*/
public totalForceMagnitude(): number {
return this.raw.total_force_magnitude();
}
/**
* The world-space (unit) direction of the force with strongest magnitude.
*/
public maxForceDirection(): Vector {
return VectorOps.fromRaw(this.raw.max_force_direction());
}
/**
* The magnitude of the largest force at a contact point of this contact pair.
*/
public maxForceMagnitude(): number {
return this.raw.max_force_magnitude();
}
}
/**
* A structure responsible for collecting events generated
* by the physics engine.
*
* To avoid leaking WASM resources, this MUST be freed manually with `eventQueue.free()`
* once you are done using it.
*/
export class EventQueue {
raw: RawEventQueue;
/**
* Creates a new event collector.
*
* @param autoDrain -setting this to `true` is strongly recommended. If true, the collector will
* be automatically drained before each `world.step(collector)`. If false, the collector will
* keep all events in memory unless it is manually drained/cleared; this may lead to unbounded use of
* RAM if no drain is performed.
*/
constructor(autoDrain: boolean, raw?: RawEventQueue) {
this.raw = raw || new RawEventQueue(autoDrain);
}
/**
* Release the WASM memory occupied by this event-queue.
*/
public free() {
if (!!this.raw) {
this.raw.free();
}
this.raw = undefined;
}
/**
* Applies the given javascript closure on each collision event of this collector, then clear
* the internal collision event buffer.
*
* @param f - JavaScript closure applied to each collision event. The
* closure must take three arguments: two integers representing the handles of the colliders
* involved in the collision, and a boolean indicating if the collision started (true) or stopped
* (false).
*/
public drainCollisionEvents(
f: (
handle1: ColliderHandle,
handle2: ColliderHandle,
started: boolean,
) => void,
) {
this.raw.drainCollisionEvents(f);
}
/**
* Applies the given javascript closure on each contact force event of this collector, then clear
* the internal collision event buffer.
*
* @param f - JavaScript closure applied to each collision event. The
* closure must take one `TempContactForceEvent` argument.
*/
public drainContactForceEvents(f: (event: TempContactForceEvent) => void) {
let event = new TempContactForceEvent();
this.raw.drainContactForceEvents((raw: RawContactForceEvent) => {
event.raw = raw;
f(event);
event.free();
});
}
/**
* Removes all events contained by this collector
*/
public clear() {
this.raw.clear();
}
}

View File

@@ -0,0 +1,7 @@
export * from "./world";
export * from "./physics_pipeline";
export * from "./serialization_pipeline";
export * from "./event_queue";
export * from "./physics_hooks";
export * from "./debug_render_pipeline";
export * from "./query_pipeline";

View File

@@ -0,0 +1,54 @@
import {RigidBodyHandle} from "../dynamics";
import {ColliderHandle} from "../geometry";
export enum ActiveHooks {
NONE = 0,
FILTER_CONTACT_PAIRS = 0b0001,
FILTER_INTERSECTION_PAIRS = 0b0010,
// MODIFY_SOLVER_CONTACTS = 0b0100, /* Not supported yet in JS. */
}
export enum SolverFlags {
EMPTY = 0b000,
COMPUTE_IMPULSE = 0b001,
}
export interface PhysicsHooks {
/**
* Function that determines if contacts computation should happen between two colliders, and how the
* constraints solver should behave for these contacts.
*
* This will only be executed and taken into account if at least one of the involved colliders contains the
* `ActiveHooks.FILTER_CONTACT_PAIR` flag in its active hooks.
*
* @param collider1 Handle of the first collider involved in the potential contact.
* @param collider2 Handle of the second collider involved in the potential contact.
* @param body1 Handle of the first body involved in the potential contact.
* @param body2 Handle of the second body involved in the potential contact.
*/
filterContactPair(
collider1: ColliderHandle,
collider2: ColliderHandle,
body1: RigidBodyHandle,
body2: RigidBodyHandle,
): SolverFlags | null;
/**
* Function that determines if intersection computation should happen between two colliders (where at least
* one is a sensor).
*
* This will only be executed and taken into account if `one of the involved colliders contains the
* `ActiveHooks.FILTER_INTERSECTION_PAIR` flag in its active hooks.
*
* @param collider1 Handle of the first collider involved in the potential contact.
* @param collider2 Handle of the second collider involved in the potential contact.
* @param body1 Handle of the first body involved in the potential contact.
* @param body2 Handle of the second body involved in the potential contact.
*/
filterIntersectionPair(
collider1: ColliderHandle,
collider2: ColliderHandle,
body1: RigidBodyHandle,
body2: RigidBodyHandle,
): boolean;
}

View File

@@ -0,0 +1,85 @@
import {RawPhysicsPipeline} from "../raw";
import {Vector, VectorOps} from "../math";
import {
IntegrationParameters,
ImpulseJointSet,
MultibodyJointSet,
RigidBodyHandle,
RigidBodySet,
CCDSolver,
IslandManager,
} from "../dynamics";
import {
BroadPhase,
ColliderHandle,
ColliderSet,
NarrowPhase,
} from "../geometry";
import {EventQueue} from "./event_queue";
import {PhysicsHooks} from "./physics_hooks";
export class PhysicsPipeline {
raw: RawPhysicsPipeline;
public free() {
if (!!this.raw) {
this.raw.free();
}
this.raw = undefined;
}
constructor(raw?: RawPhysicsPipeline) {
this.raw = raw || new RawPhysicsPipeline();
}
public step(
gravity: Vector,
integrationParameters: IntegrationParameters,
islands: IslandManager,
broadPhase: BroadPhase,
narrowPhase: NarrowPhase,
bodies: RigidBodySet,
colliders: ColliderSet,
impulseJoints: ImpulseJointSet,
multibodyJoints: MultibodyJointSet,
ccdSolver: CCDSolver,
eventQueue?: EventQueue,
hooks?: PhysicsHooks,
) {
let rawG = VectorOps.intoRaw(gravity);
if (!!eventQueue) {
this.raw.stepWithEvents(
rawG,
integrationParameters.raw,
islands.raw,
broadPhase.raw,
narrowPhase.raw,
bodies.raw,
colliders.raw,
impulseJoints.raw,
multibodyJoints.raw,
ccdSolver.raw,
eventQueue.raw,
hooks,
!!hooks ? hooks.filterContactPair : null,
!!hooks ? hooks.filterIntersectionPair : null,
);
} else {
this.raw.step(
rawG,
integrationParameters.raw,
islands.raw,
broadPhase.raw,
narrowPhase.raw,
bodies.raw,
colliders.raw,
impulseJoints.raw,
multibodyJoints.raw,
ccdSolver.raw,
);
}
rawG.free();
}
}

View File

@@ -0,0 +1,57 @@
import {RawRayColliderIntersection} from "../raw";
import {
ColliderHandle,
ColliderSet,
InteractionGroups,
PointColliderProjection,
Ray,
RayColliderIntersection,
RayColliderHit,
Shape,
ColliderShapeCastHit,
} from "../geometry";
import {IslandManager, RigidBodyHandle, RigidBodySet} from "../dynamics";
import {Rotation, RotationOps, Vector, VectorOps} from "../math";
// NOTE: must match the bits in the QueryFilterFlags on the Rust side.
/**
* Flags for excluding whole sets of colliders from a scene query.
*/
export enum QueryFilterFlags {
/**
* Exclude from the query any collider attached to a fixed rigid-body and colliders with no rigid-body attached.
*/
EXCLUDE_FIXED = 0b0000_0001,
/**
* Exclude from the query any collider attached to a dynamic rigid-body.
*/
EXCLUDE_KINEMATIC = 0b0000_0010,
/**
* Exclude from the query any collider attached to a kinematic rigid-body.
*/
EXCLUDE_DYNAMIC = 0b0000_0100,
/**
* Exclude from the query any collider that is a sensor.
*/
EXCLUDE_SENSORS = 0b0000_1000,
/**
* Exclude from the query any collider that is not a sensor.
*/
EXCLUDE_SOLIDS = 0b0001_0000,
/**
* Excludes all colliders not attached to a dynamic rigid-body.
*/
ONLY_DYNAMIC = QueryFilterFlags.EXCLUDE_FIXED |
QueryFilterFlags.EXCLUDE_KINEMATIC,
/**
* Excludes all colliders not attached to a kinematic rigid-body.
*/
ONLY_KINEMATIC = QueryFilterFlags.EXCLUDE_DYNAMIC |
QueryFilterFlags.EXCLUDE_FIXED,
/**
* Exclude all colliders attached to a non-fixed rigid-body
* (this will not exclude colliders not attached to any rigid-body).
*/
ONLY_FIXED = QueryFilterFlags.EXCLUDE_DYNAMIC |
QueryFilterFlags.EXCLUDE_KINEMATIC,
}

View File

@@ -0,0 +1,84 @@
import {RawSerializationPipeline} from "../raw";
import {Vector, VectorOps} from "../math";
import {
IntegrationParameters,
IslandManager,
ImpulseJointSet,
MultibodyJointSet,
RigidBodySet,
} from "../dynamics";
import {BroadPhase, ColliderSet, NarrowPhase} from "../geometry";
import {World} from "./world";
/**
* A pipeline for serializing the physics scene.
*
* To avoid leaking WASM resources, this MUST be freed manually with `serializationPipeline.free()`
* once you are done using it (and all the rigid-bodies it created).
*/
export class SerializationPipeline {
raw: RawSerializationPipeline;
/**
* Release the WASM memory occupied by this serialization pipeline.
*/
free() {
if (!!this.raw) {
this.raw.free();
}
this.raw = undefined;
}
constructor(raw?: RawSerializationPipeline) {
this.raw = raw || new RawSerializationPipeline();
}
/**
* Serialize a complete physics state into a single byte array.
* @param gravity - The current gravity affecting the simulation.
* @param integrationParameters - The integration parameters of the simulation.
* @param broadPhase - The broad-phase of the simulation.
* @param narrowPhase - The narrow-phase of the simulation.
* @param bodies - The rigid-bodies taking part into the simulation.
* @param colliders - The colliders taking part into the simulation.
* @param impulseJoints - The impulse joints taking part into the simulation.
* @param multibodyJoints - The multibody joints taking part into the simulation.
*/
public serializeAll(
gravity: Vector,
integrationParameters: IntegrationParameters,
islands: IslandManager,
broadPhase: BroadPhase,
narrowPhase: NarrowPhase,
bodies: RigidBodySet,
colliders: ColliderSet,
impulseJoints: ImpulseJointSet,
multibodyJoints: MultibodyJointSet,
): Uint8Array {
let rawGra = VectorOps.intoRaw(gravity);
const res = this.raw.serializeAll(
rawGra,
integrationParameters.raw,
islands.raw,
broadPhase.raw,
narrowPhase.raw,
bodies.raw,
colliders.raw,
impulseJoints.raw,
multibodyJoints.raw,
);
rawGra.free();
return res;
}
/**
* Deserialize the complete physics state from a single byte array.
*
* @param data - The byte array to deserialize.
*/
public deserializeAll(data: Uint8Array): World {
return World.fromRaw(this.raw.deserializeAll(data));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
export * from "../pkg/rapier_wasm2d";

View File

@@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"strict": false,
"strictNullChecks": false
},
"include": ["src/**/*", "pkg/*.d.ts"]
}

View File

@@ -0,0 +1,10 @@
import { defineConfig } from "tsup";
export default defineConfig({
entry: ["src/index.ts"],
format: ["esm"],
dts: true,
sourcemap: true,
clean: true,
external: ["../pkg/rapier_wasm2d.js"],
});