@scenoco-three/rapier 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +32 -0
- package/dist/Collider.d.ts +22 -0
- package/dist/Collider.js +88 -0
- package/dist/Physics.d.ts +11 -0
- package/dist/Physics.js +25 -0
- package/dist/PhysicsSystem.d.ts +25 -0
- package/dist/PhysicsSystem.js +100 -0
- package/dist/RigidBody.d.ts +20 -0
- package/dist/RigidBody.js +45 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +27 -0
- package/package.json +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SceNoCo contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# @scenoco-three/rapier
|
|
2
|
+
|
|
3
|
+
Optional [Rapier](https://rapier.rs) physics for [SceNoCo](https://github.com/): a
|
|
4
|
+
`<Physics>` scene setting plus `<RigidBody>` / `<Collider>` components, built entirely on the
|
|
5
|
+
public `System` seam in `@scenoco-three/core`. Import it to get physics; don't, and no
|
|
6
|
+
physics (or Rapier WASM) ships.
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
import { initRapier } from '@scenoco-three/rapier'; // registers the tags + system
|
|
10
|
+
await initRapier(); // load the WASM once, before loadScene
|
|
11
|
+
engine.loadScene(bundle); // a scene that declares <Physics>/<RigidBody>/<Collider>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```xml
|
|
15
|
+
<Scene>
|
|
16
|
+
<Physics gravity="0 -9.81 0" />
|
|
17
|
+
<Mesh position="0 5 0"><BoxGeometry /><MeshStandardMaterial />
|
|
18
|
+
<Components><RigidBody /><Collider /></Components>
|
|
19
|
+
</Mesh>
|
|
20
|
+
</Scene>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This package is the reference example that the framework's add-on seams work: a
|
|
24
|
+
`@system(RigidBody)` reconciles a Rapier world from the live component set, with
|
|
25
|
+
`@dimforge/rapier3d-compat` isolated here and core staying physics-free.
|
|
26
|
+
|
|
27
|
+
`three` is a peer dependency. See the [repository](../../README.md) and
|
|
28
|
+
[docs/physics-plan.md](../../docs/physics-plan.md).
|
|
29
|
+
|
|
30
|
+
## License
|
|
31
|
+
|
|
32
|
+
[MIT](./LICENSE)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import RAPIER from '@dimforge/rapier3d-compat';
|
|
2
|
+
import { Component } from '@scenoco-three/core';
|
|
3
|
+
/**
|
|
4
|
+
* A collision shape on the node. The `PhysicsSystem` attaches it to the node's
|
|
5
|
+
* `<RigidBody>` when it builds that body (no manual registration). `shape="auto"`
|
|
6
|
+
* reads the host mesh's geometry parameters — a `<Mesh><SphereGeometry radius=
|
|
7
|
+
* "0.5"/></Mesh>` gets a 0.5 ball for free.
|
|
8
|
+
*/
|
|
9
|
+
export declare class Collider extends Component {
|
|
10
|
+
shape: string;
|
|
11
|
+
halfExtents: number[];
|
|
12
|
+
radius: number;
|
|
13
|
+
halfHeight: number;
|
|
14
|
+
restitution: number;
|
|
15
|
+
friction: number;
|
|
16
|
+
density: number;
|
|
17
|
+
/** @internal Build the Rapier collider descriptor with material properties. */
|
|
18
|
+
toDesc(): RAPIER.ColliderDesc;
|
|
19
|
+
private shapeDesc;
|
|
20
|
+
/** Derive a collider from the host mesh's geometry parameters. */
|
|
21
|
+
private autoDesc;
|
|
22
|
+
}
|
package/dist/Collider.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import RAPIER from '@dimforge/rapier3d-compat';
|
|
8
|
+
import { Component, component, property } from '@scenoco-three/core';
|
|
9
|
+
/**
|
|
10
|
+
* A collision shape on the node. The `PhysicsSystem` attaches it to the node's
|
|
11
|
+
* `<RigidBody>` when it builds that body (no manual registration). `shape="auto"`
|
|
12
|
+
* reads the host mesh's geometry parameters — a `<Mesh><SphereGeometry radius=
|
|
13
|
+
* "0.5"/></Mesh>` gets a 0.5 ball for free.
|
|
14
|
+
*/
|
|
15
|
+
let Collider = class Collider extends Component {
|
|
16
|
+
shape = 'auto';
|
|
17
|
+
halfExtents = [0.5, 0.5, 0.5];
|
|
18
|
+
radius = 0.5;
|
|
19
|
+
halfHeight = 0.5;
|
|
20
|
+
restitution = 0;
|
|
21
|
+
friction = 0.5;
|
|
22
|
+
density = 1;
|
|
23
|
+
/** @internal Build the Rapier collider descriptor with material properties. */
|
|
24
|
+
toDesc() {
|
|
25
|
+
return this.shapeDesc()
|
|
26
|
+
.setRestitution(this.restitution)
|
|
27
|
+
.setFriction(this.friction)
|
|
28
|
+
.setDensity(this.density);
|
|
29
|
+
}
|
|
30
|
+
shapeDesc() {
|
|
31
|
+
const [hx, hy, hz] = this.halfExtents;
|
|
32
|
+
switch (this.shape) {
|
|
33
|
+
case 'box':
|
|
34
|
+
return RAPIER.ColliderDesc.cuboid(hx, hy, hz);
|
|
35
|
+
case 'sphere':
|
|
36
|
+
return RAPIER.ColliderDesc.ball(this.radius);
|
|
37
|
+
case 'capsule':
|
|
38
|
+
return RAPIER.ColliderDesc.capsule(this.halfHeight, this.radius);
|
|
39
|
+
case 'cylinder':
|
|
40
|
+
return RAPIER.ColliderDesc.cylinder(this.halfHeight, this.radius);
|
|
41
|
+
default:
|
|
42
|
+
return this.autoDesc();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** Derive a collider from the host mesh's geometry parameters. */
|
|
46
|
+
autoDesc() {
|
|
47
|
+
const geom = this.node.geometry;
|
|
48
|
+
const p = geom?.parameters;
|
|
49
|
+
if (geom && p) {
|
|
50
|
+
switch (geom.type) {
|
|
51
|
+
case 'BoxGeometry':
|
|
52
|
+
return RAPIER.ColliderDesc.cuboid((p.width ?? 1) / 2, (p.height ?? 1) / 2, (p.depth ?? 1) / 2);
|
|
53
|
+
case 'SphereGeometry':
|
|
54
|
+
return RAPIER.ColliderDesc.ball(p.radius ?? 0.5);
|
|
55
|
+
case 'CylinderGeometry':
|
|
56
|
+
return RAPIER.ColliderDesc.cylinder((p.height ?? 1) / 2, p.radiusTop ?? p.radius ?? 0.5);
|
|
57
|
+
case 'PlaneGeometry':
|
|
58
|
+
return RAPIER.ColliderDesc.cuboid((p.width ?? 1) / 2, (p.height ?? 1) / 2, 0.01);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return RAPIER.ColliderDesc.cuboid(0.5, 0.5, 0.5); // fallback: unit box
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
__decorate([
|
|
65
|
+
property({ type: 'string', default: 'auto', description: 'auto | box | sphere | capsule | cylinder' })
|
|
66
|
+
], Collider.prototype, "shape", void 0);
|
|
67
|
+
__decorate([
|
|
68
|
+
property({ type: 'vec3', default: [0.5, 0.5, 0.5], description: 'Box half-extents (shape="box")' })
|
|
69
|
+
], Collider.prototype, "halfExtents", void 0);
|
|
70
|
+
__decorate([
|
|
71
|
+
property({ type: 'float', default: 0.5, description: 'Radius (sphere/capsule/cylinder)' })
|
|
72
|
+
], Collider.prototype, "radius", void 0);
|
|
73
|
+
__decorate([
|
|
74
|
+
property({ type: 'float', default: 0.5, description: 'Half-height (capsule/cylinder)' })
|
|
75
|
+
], Collider.prototype, "halfHeight", void 0);
|
|
76
|
+
__decorate([
|
|
77
|
+
property({ type: 'float', default: 0, description: 'Bounciness 0..1' })
|
|
78
|
+
], Collider.prototype, "restitution", void 0);
|
|
79
|
+
__decorate([
|
|
80
|
+
property({ type: 'float', default: 0.5 })
|
|
81
|
+
], Collider.prototype, "friction", void 0);
|
|
82
|
+
__decorate([
|
|
83
|
+
property({ type: 'float', default: 1 })
|
|
84
|
+
], Collider.prototype, "density", void 0);
|
|
85
|
+
Collider = __decorate([
|
|
86
|
+
component({ name: 'Collider', description: 'A Rapier collision shape; the PhysicsSystem attaches it to the node’s RigidBody' })
|
|
87
|
+
], Collider);
|
|
88
|
+
export { Collider };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Attachment } from '@scenoco-three/core';
|
|
2
|
+
/**
|
|
3
|
+
* Scene-level physics config: `<Physics gravity="0 -9.81 0" />` inside `<Scene>`.
|
|
4
|
+
* It's a pure config attachment — the `PhysicsSystem` (auto-run while the scene
|
|
5
|
+
* has rigid bodies) reads its `gravity` via `engine.findAttachment(Physics)`.
|
|
6
|
+
* Requires `await initRapier()` before `engine.loadScene()`.
|
|
7
|
+
*/
|
|
8
|
+
export declare class Physics extends Attachment {
|
|
9
|
+
gravity: number[];
|
|
10
|
+
attach(): void;
|
|
11
|
+
}
|
package/dist/Physics.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { Attachment, attachment, property } from '@scenoco-three/core';
|
|
8
|
+
/**
|
|
9
|
+
* Scene-level physics config: `<Physics gravity="0 -9.81 0" />` inside `<Scene>`.
|
|
10
|
+
* It's a pure config attachment — the `PhysicsSystem` (auto-run while the scene
|
|
11
|
+
* has rigid bodies) reads its `gravity` via `engine.findAttachment(Physics)`.
|
|
12
|
+
* Requires `await initRapier()` before `engine.loadScene()`.
|
|
13
|
+
*/
|
|
14
|
+
let Physics = class Physics extends Attachment {
|
|
15
|
+
gravity = [0, -9.81, 0];
|
|
16
|
+
// Config only: the value is read by PhysicsSystem.onAttach; nothing to apply.
|
|
17
|
+
attach() { }
|
|
18
|
+
};
|
|
19
|
+
__decorate([
|
|
20
|
+
property({ type: 'vec3', default: [0, -9.81, 0], description: 'Gravity vector' })
|
|
21
|
+
], Physics.prototype, "gravity", void 0);
|
|
22
|
+
Physics = __decorate([
|
|
23
|
+
attachment({ name: 'Physics', target: 'Scene', group: 'physics', description: 'Scene physics config (gravity)' })
|
|
24
|
+
], Physics);
|
|
25
|
+
export { Physics };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import RAPIER from '@dimforge/rapier3d-compat';
|
|
2
|
+
import { System } from '@scenoco-three/core';
|
|
3
|
+
import { RigidBody } from './RigidBody.js';
|
|
4
|
+
/**
|
|
5
|
+
* ECS-hybrid physics: registered for `RigidBody`, the engine auto-runs it while
|
|
6
|
+
* the scene contains rigid bodies and keeps `this.components` in sync. Each fixed
|
|
7
|
+
* step it reconciles the Rapier world with the live component set (creating a
|
|
8
|
+
* body for each new `RigidBody`, removing one whose component left), steps the
|
|
9
|
+
* world, then writes every body's pose back to its `Object3D`. Gravity comes from
|
|
10
|
+
* the `<Physics>` config attachment, read on attach.
|
|
11
|
+
*
|
|
12
|
+
* Requires `await initRapier()` before `loadScene`.
|
|
13
|
+
*/
|
|
14
|
+
export declare class PhysicsSystem extends System<RigidBody> {
|
|
15
|
+
world?: RAPIER.World;
|
|
16
|
+
private readonly bodies;
|
|
17
|
+
private readonly v;
|
|
18
|
+
private readonly q;
|
|
19
|
+
onAttach(): void;
|
|
20
|
+
fixedStep(dt: number): void;
|
|
21
|
+
onDetach(): void;
|
|
22
|
+
/** The live Rapier body for a RigidBody, once built (for RigidBody.applyImpulse, etc.). */
|
|
23
|
+
bodyFor(rb: RigidBody): RAPIER.RigidBody | undefined;
|
|
24
|
+
private createBody;
|
|
25
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import RAPIER from '@dimforge/rapier3d-compat';
|
|
8
|
+
import { Quaternion, Vector3 } from 'three';
|
|
9
|
+
import { System, system } from '@scenoco-three/core';
|
|
10
|
+
import { RigidBody } from './RigidBody.js';
|
|
11
|
+
import { Collider } from './Collider.js';
|
|
12
|
+
import { Physics } from './Physics.js';
|
|
13
|
+
/**
|
|
14
|
+
* ECS-hybrid physics: registered for `RigidBody`, the engine auto-runs it while
|
|
15
|
+
* the scene contains rigid bodies and keeps `this.components` in sync. Each fixed
|
|
16
|
+
* step it reconciles the Rapier world with the live component set (creating a
|
|
17
|
+
* body for each new `RigidBody`, removing one whose component left), steps the
|
|
18
|
+
* world, then writes every body's pose back to its `Object3D`. Gravity comes from
|
|
19
|
+
* the `<Physics>` config attachment, read on attach.
|
|
20
|
+
*
|
|
21
|
+
* Requires `await initRapier()` before `loadScene`.
|
|
22
|
+
*/
|
|
23
|
+
let PhysicsSystem = class PhysicsSystem extends System {
|
|
24
|
+
world;
|
|
25
|
+
bodies = new Map();
|
|
26
|
+
v = new Vector3();
|
|
27
|
+
q = new Quaternion();
|
|
28
|
+
onAttach() {
|
|
29
|
+
if (typeof RAPIER.World !== 'function') {
|
|
30
|
+
throw new Error('Rapier is not initialized — call `await initRapier()` before loadScene().');
|
|
31
|
+
}
|
|
32
|
+
const g = this.engine.findAttachment(Physics)?.gravity ?? [0, -9.81, 0];
|
|
33
|
+
this.world = new RAPIER.World({ x: g[0] ?? 0, y: g[1] ?? -9.81, z: g[2] ?? 0 });
|
|
34
|
+
}
|
|
35
|
+
fixedStep(dt) {
|
|
36
|
+
const world = this.world;
|
|
37
|
+
if (!world)
|
|
38
|
+
return;
|
|
39
|
+
// Reconcile bodies ↔ live components: create for new, remove for gone.
|
|
40
|
+
for (const rb of this.components)
|
|
41
|
+
if (!this.bodies.has(rb))
|
|
42
|
+
this.createBody(world, rb);
|
|
43
|
+
for (const [rb, body] of this.bodies) {
|
|
44
|
+
if (!this.components.includes(rb)) {
|
|
45
|
+
world.removeRigidBody(body);
|
|
46
|
+
this.bodies.delete(rb);
|
|
47
|
+
rb.body = undefined;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
world.timestep = dt;
|
|
51
|
+
world.step();
|
|
52
|
+
// World → scene graph.
|
|
53
|
+
for (const [rb, body] of this.bodies) {
|
|
54
|
+
const t = body.translation();
|
|
55
|
+
const r = body.rotation();
|
|
56
|
+
rb.node.position.set(t.x, t.y, t.z);
|
|
57
|
+
rb.node.quaternion.set(r.x, r.y, r.z, r.w);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
onDetach() {
|
|
61
|
+
for (const rb of this.bodies.keys())
|
|
62
|
+
rb.body = undefined;
|
|
63
|
+
this.world?.free();
|
|
64
|
+
this.world = undefined;
|
|
65
|
+
this.bodies.clear();
|
|
66
|
+
}
|
|
67
|
+
/** The live Rapier body for a RigidBody, once built (for RigidBody.applyImpulse, etc.). */
|
|
68
|
+
bodyFor(rb) {
|
|
69
|
+
return this.bodies.get(rb);
|
|
70
|
+
}
|
|
71
|
+
createBody(world, rb) {
|
|
72
|
+
rb.node.getWorldPosition(this.v);
|
|
73
|
+
rb.node.getWorldQuaternion(this.q);
|
|
74
|
+
const desc = descForKind(rb.kind)
|
|
75
|
+
.setTranslation(this.v.x, this.v.y, this.v.z)
|
|
76
|
+
.setRotation({ x: this.q.x, y: this.q.y, z: this.q.z, w: this.q.w })
|
|
77
|
+
.setLinearDamping(rb.linearDamping)
|
|
78
|
+
.setAngularDamping(rb.angularDamping)
|
|
79
|
+
.setGravityScale(rb.gravityScale);
|
|
80
|
+
const body = world.createRigidBody(desc);
|
|
81
|
+
this.bodies.set(rb, body);
|
|
82
|
+
rb.body = body;
|
|
83
|
+
// Attach the colliders declared on the same node.
|
|
84
|
+
for (const c of rb.engine.getComponents(rb.node)) {
|
|
85
|
+
if (c instanceof Collider)
|
|
86
|
+
world.createCollider(c.toDesc(), body);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
PhysicsSystem = __decorate([
|
|
91
|
+
system(RigidBody)
|
|
92
|
+
], PhysicsSystem);
|
|
93
|
+
export { PhysicsSystem };
|
|
94
|
+
function descForKind(kind) {
|
|
95
|
+
if (kind === 'fixed')
|
|
96
|
+
return RAPIER.RigidBodyDesc.fixed();
|
|
97
|
+
if (kind === 'kinematic')
|
|
98
|
+
return RAPIER.RigidBodyDesc.kinematicPositionBased();
|
|
99
|
+
return RAPIER.RigidBodyDesc.dynamic();
|
|
100
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type RAPIER from '@dimforge/rapier3d-compat';
|
|
2
|
+
import { Component } from '@scenoco-three/core';
|
|
3
|
+
/**
|
|
4
|
+
* Marks the host node as a Rapier rigid body — plain data the `PhysicsSystem`
|
|
5
|
+
* reconciles into the world (no manual registration). Pair it with one or more
|
|
6
|
+
* `<Collider>` on the same node to give it shape. The system stamps the live
|
|
7
|
+
* `body` onto this component once it's built.
|
|
8
|
+
*/
|
|
9
|
+
export declare class RigidBody extends Component {
|
|
10
|
+
kind: string;
|
|
11
|
+
linearDamping: number;
|
|
12
|
+
angularDamping: number;
|
|
13
|
+
gravityScale: number;
|
|
14
|
+
/** The live Rapier body, set by the PhysicsSystem after it builds it. */
|
|
15
|
+
body?: RAPIER.RigidBody;
|
|
16
|
+
/** Apply an instantaneous impulse (world space), waking the body. */
|
|
17
|
+
applyImpulse(x: number, y: number, z: number): void;
|
|
18
|
+
/** Set the linear velocity (world space), waking the body. */
|
|
19
|
+
setLinvel(x: number, y: number, z: number): void;
|
|
20
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { Component, component, property } from '@scenoco-three/core';
|
|
8
|
+
/**
|
|
9
|
+
* Marks the host node as a Rapier rigid body — plain data the `PhysicsSystem`
|
|
10
|
+
* reconciles into the world (no manual registration). Pair it with one or more
|
|
11
|
+
* `<Collider>` on the same node to give it shape. The system stamps the live
|
|
12
|
+
* `body` onto this component once it's built.
|
|
13
|
+
*/
|
|
14
|
+
let RigidBody = class RigidBody extends Component {
|
|
15
|
+
kind = 'dynamic';
|
|
16
|
+
linearDamping = 0;
|
|
17
|
+
angularDamping = 0;
|
|
18
|
+
gravityScale = 1;
|
|
19
|
+
/** The live Rapier body, set by the PhysicsSystem after it builds it. */
|
|
20
|
+
body;
|
|
21
|
+
/** Apply an instantaneous impulse (world space), waking the body. */
|
|
22
|
+
applyImpulse(x, y, z) {
|
|
23
|
+
this.body?.applyImpulse({ x, y, z }, true);
|
|
24
|
+
}
|
|
25
|
+
/** Set the linear velocity (world space), waking the body. */
|
|
26
|
+
setLinvel(x, y, z) {
|
|
27
|
+
this.body?.setLinvel({ x, y, z }, true);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
__decorate([
|
|
31
|
+
property({ type: 'string', default: 'dynamic', description: 'dynamic | fixed | kinematic' })
|
|
32
|
+
], RigidBody.prototype, "kind", void 0);
|
|
33
|
+
__decorate([
|
|
34
|
+
property({ type: 'float', default: 0 })
|
|
35
|
+
], RigidBody.prototype, "linearDamping", void 0);
|
|
36
|
+
__decorate([
|
|
37
|
+
property({ type: 'float', default: 0 })
|
|
38
|
+
], RigidBody.prototype, "angularDamping", void 0);
|
|
39
|
+
__decorate([
|
|
40
|
+
property({ type: 'float', default: 1, description: 'Multiplier on world gravity for this body' })
|
|
41
|
+
], RigidBody.prototype, "gravityScale", void 0);
|
|
42
|
+
RigidBody = __decorate([
|
|
43
|
+
component({ name: 'RigidBody', description: 'A physics body driven by the Rapier world (needs a <Physics> scene)' })
|
|
44
|
+
], RigidBody);
|
|
45
|
+
export { RigidBody };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { PhysicsSystem } from './PhysicsSystem.js';
|
|
2
|
+
export { RigidBody } from './RigidBody.js';
|
|
3
|
+
export { Collider } from './Collider.js';
|
|
4
|
+
export { Physics } from './Physics.js';
|
|
5
|
+
/**
|
|
6
|
+
* The XML tags this package registers — read by the compiler/Vite plugin
|
|
7
|
+
* (`registerPackages`) so a scene that uses them validates and imports the
|
|
8
|
+
* package. The convention for any SceNoCo tag package.
|
|
9
|
+
*/
|
|
10
|
+
export declare const sceneTags: readonly ["RigidBody", "Collider", "Physics"];
|
|
11
|
+
/**
|
|
12
|
+
* Load the Rapier WASM once. Await it before `engine.loadScene()` of any scene
|
|
13
|
+
* that uses `<Physics>` (a `World` cannot be built until the WASM is ready).
|
|
14
|
+
* Idempotent — repeated calls share one initialization.
|
|
15
|
+
*/
|
|
16
|
+
export declare function initRapier(): Promise<void>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// `@scenoco-three/rapier` — Rapier physics for SceNoCo, built entirely on the
|
|
2
|
+
// public seams: a `PhysicsSystem extends System`, `@component` `RigidBody`/
|
|
3
|
+
// `Collider`, and a `<Physics>` `@setting`. Core has no physics dependency; an
|
|
4
|
+
// app that doesn't import this package ships no physics (and no Rapier WASM).
|
|
5
|
+
//
|
|
6
|
+
// Importing this module registers the `RigidBody`, `Collider`, and `Physics`
|
|
7
|
+
// tags (decorator side effects) — that's why the package is marked `sideEffects`.
|
|
8
|
+
import RAPIER from '@dimforge/rapier3d-compat';
|
|
9
|
+
export { PhysicsSystem } from './PhysicsSystem.js';
|
|
10
|
+
export { RigidBody } from './RigidBody.js';
|
|
11
|
+
export { Collider } from './Collider.js';
|
|
12
|
+
export { Physics } from './Physics.js';
|
|
13
|
+
/**
|
|
14
|
+
* The XML tags this package registers — read by the compiler/Vite plugin
|
|
15
|
+
* (`registerPackages`) so a scene that uses them validates and imports the
|
|
16
|
+
* package. The convention for any SceNoCo tag package.
|
|
17
|
+
*/
|
|
18
|
+
export const sceneTags = ['RigidBody', 'Collider', 'Physics'];
|
|
19
|
+
let initPromise;
|
|
20
|
+
/**
|
|
21
|
+
* Load the Rapier WASM once. Await it before `engine.loadScene()` of any scene
|
|
22
|
+
* that uses `<Physics>` (a `World` cannot be built until the WASM is ready).
|
|
23
|
+
* Idempotent — repeated calls share one initialization.
|
|
24
|
+
*/
|
|
25
|
+
export function initRapier() {
|
|
26
|
+
return (initPromise ??= RAPIER.init());
|
|
27
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@scenoco-three/rapier",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Rapier physics for SceNoCo: <Physics> setting + RigidBody/Collider components on the EngineSystem seam",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"keywords": ["three", "threejs", "physics", "rapier", "scene", "components", "gamedev"],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"//sideEffects": "Importing the package registers its @component/@setting tags (decorator side effects), so it must not be tree-shaken away when imported for registration.",
|
|
23
|
+
"sideEffects": true,
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc -p tsconfig.build.json",
|
|
26
|
+
"typecheck": "npm --prefix ../core run build && tsc --noEmit -p tsconfig.json"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@scenoco-three/core": "^0.1.0",
|
|
30
|
+
"@dimforge/rapier3d-compat": "^0.19.0"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"three": ">=0.160.0"
|
|
34
|
+
}
|
|
35
|
+
}
|