castle-web-cli 0.4.129 → 0.4.130
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/dist/agent-prompts.js +4 -4
- package/dist/filesChanged.d.ts +2 -0
- package/dist/filesChanged.js +6 -2
- package/dist/ide.js +15 -4
- package/dist/importBrowse.js +1 -0
- package/dist/index.js +10 -4
- package/dist/init.js +63 -15
- package/dist/native/loop.js +1 -1
- package/dist/native/tools.js +1 -1
- package/dist/serve.d.ts +1 -0
- package/dist/serve.js +20 -1
- package/dist/shell/assets/index-BpOLUyVO.js +441 -0
- package/dist/shell/assets/{index-CAq6f9B5.css → index-D6K-0YDB.css} +1 -1
- package/dist/shell/index.html +2 -2
- package/kits/physics-2d/CLAUDE.md +9 -10
- package/kits/physics-2d/castle.json +1 -1
- package/kits/physics-2d/editors/PlayOnly.jsx +15 -6
- package/kits/physics-2d/editors/PxArtEditor.jsx +95 -31
- package/kits/physics-2d/editors/SceneEditor.jsx +5 -5
- package/kits/physics-2d/editors/SingleEditor.jsx +37 -8
- package/kits/physics-2d/editors/StyleEditor.jsx +7 -4
- package/kits/physics-2d/editors/pixelEditorChrome.jsx +7 -7
- package/kits/physics-2d/editors/pixelInspector.jsx +13 -7
- package/kits/physics-2d/editors/pxArtTimeline.jsx +29 -11
- package/kits/physics-2d/editors/pxArtTimeline.module.css +11 -0
- package/kits/physics-2d/engine/blueprint.js +15 -3
- package/kits/physics-2d/engine/liveReload.js +33 -21
- package/kits/physics-2d/engine/systemRegistry.js +1 -1
- package/kits/physics-2d/engine/ui.jsx +2 -2
- package/kits/physics-2d/engine/ui.module.css +42 -0
- package/kits/physics-3d/CLAUDE.md +109 -0
- package/kits/physics-3d/behaviors/Body.jsx +51 -0
- package/kits/physics-3d/behaviors/Door.jsx +31 -0
- package/kits/physics-3d/behaviors/Lookable.jsx +11 -0
- package/kits/physics-3d/behaviors/Model.jsx +14 -0
- package/kits/physics-3d/behaviors/Pickup.jsx +39 -0
- package/kits/physics-3d/behaviors/Player.jsx +172 -0
- package/kits/physics-3d/behaviors/Portal.jsx +19 -0
- package/kits/physics-3d/behaviors/Shape.jsx +75 -0
- package/kits/physics-3d/behaviors/Solid.jsx +11 -0
- package/kits/physics-3d/behaviors/Transform.jsx +24 -0
- package/kits/physics-3d/blueprints/barrel.scene +15 -0
- package/kits/physics-3d/blueprints/crate.scene +29 -0
- package/kits/physics-3d/blueprints/door.scene +35 -0
- package/kits/physics-3d/blueprints/gem.scene +26 -0
- package/kits/physics-3d/blueprints/pillar.scene +25 -0
- package/kits/physics-3d/blueprints/platform.scene +23 -0
- package/kits/physics-3d/blueprints/player.scene +29 -0
- package/kits/physics-3d/blueprints/portal.scene +21 -0
- package/kits/physics-3d/blueprints/rock.scene +14 -0
- package/kits/physics-3d/blueprints/statue.scene +26 -0
- package/kits/physics-3d/blueprints/tree.scene +14 -0
- package/kits/physics-3d/blueprints/wall.scene +26 -0
- package/kits/{basic-2d → physics-3d}/castle.json +23 -5
- package/kits/physics-3d/docs/pxmodel-format.md +111 -0
- package/kits/physics-3d/drawings/crate.pxart +26 -0
- package/kits/physics-3d/drawings/door-edge.pxart +26 -0
- package/kits/physics-3d/drawings/door.pxart +27 -0
- package/kits/physics-3d/drawings/face.pxart +26 -0
- package/kits/physics-3d/drawings/floor.pxart +27 -0
- package/kits/physics-3d/drawings/platform.pxart +27 -0
- package/kits/physics-3d/drawings/statue.pxart +27 -0
- package/kits/physics-3d/drawings/wall.pxart +27 -0
- package/kits/physics-3d/engine3d/PlayOnly3D.jsx +32 -0
- package/kits/physics-3d/engine3d/PxModelEditor.jsx +619 -0
- package/kits/physics-3d/engine3d/Scene3DEditor.jsx +618 -0
- package/kits/physics-3d/engine3d/Scene3DPlayer.jsx +309 -0
- package/kits/physics-3d/engine3d/editor3dData.js +114 -0
- package/kits/physics-3d/engine3d/editor3dInspector.jsx +404 -0
- package/kits/physics-3d/engine3d/editorChrome.jsx +218 -0
- package/kits/physics-3d/engine3d/editorWorld.js +249 -0
- package/kits/physics-3d/engine3d/materials.js +174 -0
- package/kits/physics-3d/engine3d/meshops.js +123 -0
- package/kits/physics-3d/engine3d/modelEditorWorld.js +439 -0
- package/kits/physics-3d/engine3d/modelFiles.js +139 -0
- package/kits/physics-3d/engine3d/pxmodel.js +265 -0
- package/kits/physics-3d/engine3d/thumbnails.js +137 -0
- package/kits/physics-3d/engine3d/world3d.js +216 -0
- package/kits/{basic-2d → physics-3d}/eslint.config.js +10 -25
- package/kits/physics-3d/index.html +20 -0
- package/kits/physics-3d/main.jsx +30 -0
- package/kits/physics-3d/models/barrel.pxmodel +10 -0
- package/kits/physics-3d/models/player.pxmodel +222 -0
- package/kits/physics-3d/models/rock.pxmodel +30 -0
- package/kits/physics-3d/models/tree.pxmodel +10 -0
- package/kits/{basic-2d → physics-3d}/package-lock.json +140 -119
- package/kits/{basic-2d → physics-3d}/package.json +11 -9
- package/kits/{basic-2d → physics-3d}/pnpm-lock.yaml +162 -137
- package/kits/physics-3d/scenes/main.scene +414 -0
- package/kits/physics-3d/scenes/model-lab.scene +127 -0
- package/kits/physics-3d/systems/physics3d.js +356 -0
- package/package.json +5 -2
- package/dist/shell/assets/index-DKu9ejyh.js +0 -436
- package/kits/basic-2d/CLAUDE.md +0 -221
- package/kits/basic-2d/behaviors/Camera.jsx +0 -43
- package/kits/basic-2d/behaviors/Collider.jsx +0 -213
- package/kits/basic-2d/behaviors/Layout.jsx +0 -53
- package/kits/basic-2d/behaviors/Sprite.jsx +0 -357
- package/kits/basic-2d/behaviors/tint.js +0 -47
- package/kits/basic-2d/blueprints/cauldron.scene +0 -20
- package/kits/basic-2d/docs/pxart-format.md +0 -377
- package/kits/basic-2d/drawings/cauldron.pxart +0 -113
- package/kits/basic-2d/editors/BlueprintLibrary.jsx +0 -270
- package/kits/basic-2d/editors/ErrorBoundary.jsx +0 -59
- package/kits/basic-2d/editors/PlayOnly.jsx +0 -31
- package/kits/basic-2d/editors/PxArtEditor.jsx +0 -1092
- package/kits/basic-2d/editors/SceneEditor.jsx +0 -1780
- package/kits/basic-2d/editors/SelectionOverlay.jsx +0 -909
- package/kits/basic-2d/editors/SingleEditor.jsx +0 -122
- package/kits/basic-2d/editors/behaviorRegistry.js +0 -34
- package/kits/basic-2d/editors/editorHistory.js +0 -157
- package/kits/basic-2d/editors/inspectorSheet.js +0 -13
- package/kits/basic-2d/editors/pixelCanvas.js +0 -11
- package/kits/basic-2d/editors/pixelEditorChrome.jsx +0 -74
- package/kits/basic-2d/editors/pixelGeometry.js +0 -140
- package/kits/basic-2d/editors/pixelInspector.jsx +0 -633
- package/kits/basic-2d/editors/pxArtEditorModel.js +0 -732
- package/kits/basic-2d/editors/pxArtPlayback.js +0 -92
- package/kits/basic-2d/editors/pxArtTimeline.jsx +0 -752
- package/kits/basic-2d/editors/pxArtTimeline.module.css +0 -506
- package/kits/basic-2d/editors/pxArtTools.js +0 -232
- package/kits/basic-2d/editors/useArtboardFit.js +0 -105
- package/kits/basic-2d/engine/ScenePlayer.jsx +0 -209
- package/kits/basic-2d/engine/SceneUI.jsx +0 -59
- package/kits/basic-2d/engine/assets.js +0 -15
- package/kits/basic-2d/engine/autoInspector.jsx +0 -70
- package/kits/basic-2d/engine/behaviorExtensions.js +0 -32
- package/kits/basic-2d/engine/blueprint.js +0 -557
- package/kits/basic-2d/engine/collider.js +0 -200
- package/kits/basic-2d/engine/files.js +0 -141
- package/kits/basic-2d/engine/liveReload.js +0 -88
- package/kits/basic-2d/engine/pxart.js +0 -1032
- package/kits/basic-2d/engine/pxartSmooth.js +0 -222
- package/kits/basic-2d/engine/scene.js +0 -696
- package/kits/basic-2d/engine/spriteGeometry.js +0 -32
- package/kits/basic-2d/engine/systemRegistry.js +0 -16
- package/kits/basic-2d/engine/ui.jsx +0 -695
- package/kits/basic-2d/engine/ui.module.css +0 -2287
- package/kits/basic-2d/index.html +0 -24
- package/kits/basic-2d/main.jsx +0 -24
- package/kits/basic-2d/scenes/main.scene +0 -16
- package/kits/basic-2d/scripts/draw.mjs +0 -121
- /package/kits/{basic-2d → physics-3d}/.prettierrc +0 -0
- /package/kits/{basic-2d → physics-3d}/vite.config.js +0 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
// The 3d physics system: one rapier world per scene runtime, registered as a
|
|
2
|
+
// runtime system (auto-discovered from `systems/*.js` by the imported engine's
|
|
3
|
+
// systemRegistry). Every actor with a Transform gets a rapier body -- fixed by
|
|
4
|
+
// default, dynamic or kinematic via a `Body` behavior. Colliders come from the
|
|
5
|
+
// actor's `Shape` and are sensors unless the actor is solid (a `Solid`
|
|
6
|
+
// behavior, or `Shape.solid`), so pickups and trigger zones still get overlap
|
|
7
|
+
// events. Behaviors get `onCollisionEnter` / `onCollisionExit` callbacks and
|
|
8
|
+
// drive motion through `scene.physics3d`.
|
|
9
|
+
//
|
|
10
|
+
// rapier's wasm loads async; the system installs immediately and starts
|
|
11
|
+
// simulating on the first frame after init resolves.
|
|
12
|
+
import * as THREE from 'three';
|
|
13
|
+
import * as RAPIER from '@dimforge/rapier3d-compat';
|
|
14
|
+
|
|
15
|
+
const FIXED_STEP = 1 / 60;
|
|
16
|
+
const MAX_STEPS_PER_FRAME = 4;
|
|
17
|
+
const DEG = Math.PI / 180;
|
|
18
|
+
|
|
19
|
+
let rapierReady = false;
|
|
20
|
+
let rapierInitPromise = null;
|
|
21
|
+
|
|
22
|
+
function ensureRapierInit() {
|
|
23
|
+
if (!rapierInitPromise) {
|
|
24
|
+
rapierInitPromise = RAPIER.init().then(() => {
|
|
25
|
+
rapierReady = true;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return rapierInitPromise;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function installSystem(runtime) {
|
|
32
|
+
const system = new Physics3DSystem();
|
|
33
|
+
runtime.registerSystem(system);
|
|
34
|
+
system.attach(runtime);
|
|
35
|
+
return system;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class Physics3DSystem {
|
|
39
|
+
constructor() {
|
|
40
|
+
this.world = null;
|
|
41
|
+
this.eventQueue = null;
|
|
42
|
+
this.bodies = new Map(); // actorId -> { body, collider, sig, pushedKey }
|
|
43
|
+
this.byCollider = new Map(); // collider handle -> actorId
|
|
44
|
+
this.acc = 0;
|
|
45
|
+
this.touching = new Set(); // "aId|bId" sorted pair keys
|
|
46
|
+
this.enter = [];
|
|
47
|
+
this.exit = [];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
attach(runtime) {
|
|
51
|
+
runtime.physics3d = makeApi(this);
|
|
52
|
+
void ensureRapierInit();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
reset() {
|
|
56
|
+
// Rapier objects live in wasm -- dropping the JS reference leaks them, so
|
|
57
|
+
// both the world and its event queue are freed explicitly. Runs on every
|
|
58
|
+
// scene load (the runtime resets registered systems) and on dispose.
|
|
59
|
+
if (this.world) this.world.free();
|
|
60
|
+
if (this.eventQueue) this.eventQueue.free();
|
|
61
|
+
this.world = null;
|
|
62
|
+
this.eventQueue = null;
|
|
63
|
+
this.bodies.clear();
|
|
64
|
+
this.byCollider.clear();
|
|
65
|
+
this.touching.clear();
|
|
66
|
+
this.enter.length = 0;
|
|
67
|
+
this.exit.length = 0;
|
|
68
|
+
this.acc = 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
dispose() {
|
|
72
|
+
this.reset();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
afterBehaviors(scene, dt) {
|
|
76
|
+
if (!rapierReady) return;
|
|
77
|
+
this.ensureWorld();
|
|
78
|
+
this.reconcileBodies(scene);
|
|
79
|
+
this.pushTransforms(scene);
|
|
80
|
+
this.step(dt);
|
|
81
|
+
this.writeBack(scene);
|
|
82
|
+
this.dispatchCallbacks(scene);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
ensureWorld() {
|
|
86
|
+
if (this.world) return;
|
|
87
|
+
this.world = new RAPIER.World({ x: 0, y: -9.81, z: 0 });
|
|
88
|
+
this.eventQueue = new RAPIER.EventQueue(true);
|
|
89
|
+
// The ground everything stands on, matching the visual ground plane.
|
|
90
|
+
const groundBody = this.world.createRigidBody(
|
|
91
|
+
RAPIER.RigidBodyDesc.fixed().setTranslation(0, -0.5, 0)
|
|
92
|
+
);
|
|
93
|
+
this.world.createCollider(RAPIER.ColliderDesc.cuboid(100, 0.5, 100), groundBody);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
reconcileBodies(scene) {
|
|
97
|
+
const seen = new Set();
|
|
98
|
+
for (const actor of scene.getActors()) {
|
|
99
|
+
if (!actor.components.Transform) continue;
|
|
100
|
+
seen.add(actor.id);
|
|
101
|
+
const sig = bodySignature(actor);
|
|
102
|
+
const existing = this.bodies.get(actor.id);
|
|
103
|
+
if (existing && existing.sig === sig) continue;
|
|
104
|
+
if (existing) this.removeBody(actor.id);
|
|
105
|
+
this.createBody(actor, sig);
|
|
106
|
+
}
|
|
107
|
+
for (const id of [...this.bodies.keys()]) {
|
|
108
|
+
if (!seen.has(id)) this.removeBody(id);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
createBody(actor, sig) {
|
|
113
|
+
const t = actor.components.Transform;
|
|
114
|
+
const body = this.world.createRigidBody(bodyDescFor(actor, t));
|
|
115
|
+
body.userData = actor.id;
|
|
116
|
+
let collider = null;
|
|
117
|
+
const shape = actor.components.Shape;
|
|
118
|
+
if (shape) {
|
|
119
|
+
collider = this.world.createCollider(colliderDescFor(actor, shape), body);
|
|
120
|
+
this.byCollider.set(collider.handle, actor.id);
|
|
121
|
+
}
|
|
122
|
+
this.bodies.set(actor.id, { body, collider, sig, pushedKey: transformKey(t) });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
removeBody(actorId) {
|
|
126
|
+
const entry = this.bodies.get(actorId);
|
|
127
|
+
if (!entry) return;
|
|
128
|
+
if (entry.collider) this.byCollider.delete(entry.collider.handle);
|
|
129
|
+
this.world.removeRigidBody(entry.body);
|
|
130
|
+
this.bodies.delete(actorId);
|
|
131
|
+
for (const key of [...this.touching]) {
|
|
132
|
+
if (key.startsWith(`${actorId}|`) || key.endsWith(`|${actorId}`)) this.touching.delete(key);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Fixed bodies follow Transform edits (teleport); kinematic bodies follow
|
|
137
|
+
// with contact resolution. Dynamic bodies are the sim's to move.
|
|
138
|
+
pushTransforms(scene) {
|
|
139
|
+
for (const actor of scene.getActors()) {
|
|
140
|
+
const entry = this.bodies.get(actor.id);
|
|
141
|
+
const t = actor.components.Transform;
|
|
142
|
+
if (!entry || !t) continue;
|
|
143
|
+
const type = entry.body.bodyType();
|
|
144
|
+
if (type === RAPIER.RigidBodyType.KinematicPositionBased) {
|
|
145
|
+
entry.body.setNextKinematicTranslation({ x: t.x, y: t.y, z: t.z });
|
|
146
|
+
entry.body.setNextKinematicRotation(quatFromEuler(t));
|
|
147
|
+
} else if (type === RAPIER.RigidBodyType.Fixed) {
|
|
148
|
+
const key = transformKey(t);
|
|
149
|
+
if (key !== entry.pushedKey) {
|
|
150
|
+
entry.body.setTranslation({ x: t.x, y: t.y, z: t.z }, false);
|
|
151
|
+
entry.body.setRotation(quatFromEuler(t), false);
|
|
152
|
+
entry.pushedKey = key;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
step(dt) {
|
|
159
|
+
this.enter.length = 0;
|
|
160
|
+
this.exit.length = 0;
|
|
161
|
+
this.acc += dt;
|
|
162
|
+
let steps = 0;
|
|
163
|
+
while (this.acc >= FIXED_STEP && steps < MAX_STEPS_PER_FRAME) {
|
|
164
|
+
// Snapshot dynamic bodies before each step so rendering can interpolate
|
|
165
|
+
// between the last two physics states -- without this, bodies advance
|
|
166
|
+
// only on step frames and stutter on displays faster than 60Hz.
|
|
167
|
+
for (const entry of this.bodies.values()) {
|
|
168
|
+
if (entry.body.bodyType() !== RAPIER.RigidBodyType.Dynamic) continue;
|
|
169
|
+
const p = entry.body.translation();
|
|
170
|
+
const q = entry.body.rotation();
|
|
171
|
+
entry.prev = { x: p.x, y: p.y, z: p.z, qx: q.x, qy: q.y, qz: q.z, qw: q.w };
|
|
172
|
+
}
|
|
173
|
+
this.world.timestep = FIXED_STEP;
|
|
174
|
+
this.world.step(this.eventQueue);
|
|
175
|
+
this.eventQueue.drainCollisionEvents((h1, h2, started) => this.recordPair(h1, h2, started));
|
|
176
|
+
this.acc -= FIXED_STEP;
|
|
177
|
+
steps += 1;
|
|
178
|
+
}
|
|
179
|
+
if (steps === MAX_STEPS_PER_FRAME) this.acc = 0;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
recordPair(handle1, handle2, started) {
|
|
183
|
+
const a = this.byCollider.get(handle1);
|
|
184
|
+
const b = this.byCollider.get(handle2);
|
|
185
|
+
if (!a || !b) return; // the ground, or an already-removed collider
|
|
186
|
+
const key = a < b ? `${a}|${b}` : `${b}|${a}`;
|
|
187
|
+
if (started && !this.touching.has(key)) {
|
|
188
|
+
this.touching.add(key);
|
|
189
|
+
this.enter.push([a, b]);
|
|
190
|
+
} else if (!started && this.touching.has(key)) {
|
|
191
|
+
this.touching.delete(key);
|
|
192
|
+
this.exit.push([a, b]);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
writeBack(scene) {
|
|
197
|
+
// Blend factor between the last two physics states: how far into the next
|
|
198
|
+
// fixed step this render frame sits.
|
|
199
|
+
const alpha = Math.max(0, Math.min(1, this.acc / FIXED_STEP));
|
|
200
|
+
for (const actor of scene.getActors()) {
|
|
201
|
+
const entry = this.bodies.get(actor.id);
|
|
202
|
+
if (!entry || entry.body.bodyType() !== RAPIER.RigidBodyType.Dynamic) continue;
|
|
203
|
+
const t = actor.components.Transform;
|
|
204
|
+
const p = entry.body.translation();
|
|
205
|
+
const prev = entry.prev;
|
|
206
|
+
if (prev) {
|
|
207
|
+
t.x = prev.x + (p.x - prev.x) * alpha;
|
|
208
|
+
t.y = prev.y + (p.y - prev.y) * alpha;
|
|
209
|
+
t.z = prev.z + (p.z - prev.z) * alpha;
|
|
210
|
+
} else {
|
|
211
|
+
t.x = p.x;
|
|
212
|
+
t.y = p.y;
|
|
213
|
+
t.z = p.z;
|
|
214
|
+
}
|
|
215
|
+
// Locked-rotation bodies (characters) keep Transform rotation under
|
|
216
|
+
// behavior control so they can face where they're going.
|
|
217
|
+
if (!actor.components.Body?.lockRotations) {
|
|
218
|
+
const q = entry.body.rotation();
|
|
219
|
+
if (prev) {
|
|
220
|
+
tmpQuat.set(prev.qx, prev.qy, prev.qz, prev.qw);
|
|
221
|
+
tmpQuat2.set(q.x, q.y, q.z, q.w);
|
|
222
|
+
tmpQuat.slerp(tmpQuat2, alpha);
|
|
223
|
+
actor.runtime.quat = { x: tmpQuat.x, y: tmpQuat.y, z: tmpQuat.z, w: tmpQuat.w };
|
|
224
|
+
setEulerFromQuat(t, actor.runtime.quat);
|
|
225
|
+
} else {
|
|
226
|
+
actor.runtime.quat = { x: q.x, y: q.y, z: q.z, w: q.w };
|
|
227
|
+
setEulerFromQuat(t, q);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
dispatchCallbacks(scene) {
|
|
234
|
+
for (const [a, b] of this.enter) emitPair(scene, a, b, 'onCollisionEnter');
|
|
235
|
+
for (const [a, b] of this.exit) emitPair(scene, a, b, 'onCollisionExit');
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function emitPair(scene, aId, bId, method) {
|
|
240
|
+
const a = scene.getActor(aId);
|
|
241
|
+
const b = scene.getActor(bId);
|
|
242
|
+
if (!a || !b) return;
|
|
243
|
+
scene.forEachBehavior(a, (instance) => instance[method]?.(a, scene, b));
|
|
244
|
+
scene.forEachBehavior(b, (instance) => instance[method]?.(b, scene, a));
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function isSolid(actor) {
|
|
248
|
+
return Boolean(actor.components.Solid) || Boolean(actor.components.Shape?.solid);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function bodySignature(actor) {
|
|
252
|
+
return JSON.stringify([
|
|
253
|
+
actor.components.Body ?? null,
|
|
254
|
+
actor.components.Shape ?? null,
|
|
255
|
+
isSolid(actor),
|
|
256
|
+
]);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function transformKey(t) {
|
|
260
|
+
return `${t.x},${t.y},${t.z},${t.rotationX},${t.rotationY},${t.rotationZ}`;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function bodyDescFor(actor, t) {
|
|
264
|
+
const opts = actor.components.Body;
|
|
265
|
+
let desc;
|
|
266
|
+
if (opts?.type === 'dynamic') desc = RAPIER.RigidBodyDesc.dynamic();
|
|
267
|
+
else if (opts?.type === 'kinematic') desc = RAPIER.RigidBodyDesc.kinematicPositionBased();
|
|
268
|
+
else desc = RAPIER.RigidBodyDesc.fixed();
|
|
269
|
+
desc.setTranslation(t.x, t.y, t.z);
|
|
270
|
+
desc.setRotation(quatFromEuler(t));
|
|
271
|
+
if (opts) {
|
|
272
|
+
desc.setGravityScale(opts.gravityScale);
|
|
273
|
+
if (opts.lockRotations) desc.lockRotations();
|
|
274
|
+
}
|
|
275
|
+
return desc;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function colliderDescFor(actor, shape) {
|
|
279
|
+
let desc;
|
|
280
|
+
if (shape.kind === 'sphere') desc = RAPIER.ColliderDesc.ball(shape.radius);
|
|
281
|
+
else if (shape.kind === 'capsule') {
|
|
282
|
+
desc = RAPIER.ColliderDesc.capsule(
|
|
283
|
+
Math.max(0.01, shape.height / 2 - shape.radius),
|
|
284
|
+
shape.radius
|
|
285
|
+
);
|
|
286
|
+
} else if (shape.kind === 'cylinder') {
|
|
287
|
+
desc = RAPIER.ColliderDesc.cylinder(shape.height / 2, shape.radius);
|
|
288
|
+
} else {
|
|
289
|
+
desc = RAPIER.ColliderDesc.cuboid(shape.width / 2, shape.height / 2, shape.depth / 2);
|
|
290
|
+
}
|
|
291
|
+
const opts = actor.components.Body;
|
|
292
|
+
desc.setFriction(opts?.friction ?? 0.6);
|
|
293
|
+
desc.setRestitution(opts?.restitution ?? 0);
|
|
294
|
+
if (!isSolid(actor)) desc.setSensor(true);
|
|
295
|
+
desc.setActiveEvents(RAPIER.ActiveEvents.COLLISION_EVENTS);
|
|
296
|
+
return desc;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const tmpEuler = new THREE.Euler();
|
|
300
|
+
const tmpQuat = new THREE.Quaternion();
|
|
301
|
+
const tmpQuat2 = new THREE.Quaternion();
|
|
302
|
+
|
|
303
|
+
function quatFromEuler(t) {
|
|
304
|
+
tmpEuler.set(t.rotationX * DEG, t.rotationY * DEG, t.rotationZ * DEG);
|
|
305
|
+
tmpQuat.setFromEuler(tmpEuler);
|
|
306
|
+
return { x: tmpQuat.x, y: tmpQuat.y, z: tmpQuat.z, w: tmpQuat.w };
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function setEulerFromQuat(t, q) {
|
|
310
|
+
tmpQuat.set(q.x, q.y, q.z, q.w);
|
|
311
|
+
tmpEuler.setFromQuaternion(tmpQuat);
|
|
312
|
+
t.rotationX = tmpEuler.x / DEG;
|
|
313
|
+
t.rotationY = tmpEuler.y / DEG;
|
|
314
|
+
t.rotationZ = tmpEuler.z / DEG;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// The author-facing API behaviors reach through `scene.physics3d`. Motion goes
|
|
318
|
+
// through the body (never by writing a dynamic actor's Transform); rays
|
|
319
|
+
// exclude sensors so ground checks don't land on pickups.
|
|
320
|
+
function makeApi(system) {
|
|
321
|
+
const bodyOf = (actorOrId) => {
|
|
322
|
+
const id = typeof actorOrId === 'string' ? actorOrId : actorOrId?.id;
|
|
323
|
+
return system.bodies.get(id)?.body ?? null;
|
|
324
|
+
};
|
|
325
|
+
return {
|
|
326
|
+
ready: () => rapierReady && Boolean(system.world),
|
|
327
|
+
debugRender: () => (system.world ? system.world.debugRender() : null),
|
|
328
|
+
bodyOf,
|
|
329
|
+
mass: (actor) => bodyOf(actor)?.mass() ?? 0,
|
|
330
|
+
getVelocity: (actor) => {
|
|
331
|
+
const v = bodyOf(actor)?.linvel();
|
|
332
|
+
return v ? { x: v.x, y: v.y, z: v.z } : { x: 0, y: 0, z: 0 };
|
|
333
|
+
},
|
|
334
|
+
setVelocity: (actor, v) => bodyOf(actor)?.setLinvel(v, true),
|
|
335
|
+
applyImpulse: (actor, v) => bodyOf(actor)?.applyImpulse(v, true),
|
|
336
|
+
castRay: (origin, dir, maxToi, excludeActor) => {
|
|
337
|
+
if (!system.world) return null;
|
|
338
|
+
const ray = new RAPIER.Ray(origin, dir);
|
|
339
|
+
const exclude = excludeActor ? bodyOf(excludeActor) : undefined;
|
|
340
|
+
const hit = system.world.castRay(
|
|
341
|
+
ray,
|
|
342
|
+
maxToi,
|
|
343
|
+
true,
|
|
344
|
+
RAPIER.QueryFilterFlags.EXCLUDE_SENSORS,
|
|
345
|
+
undefined,
|
|
346
|
+
undefined,
|
|
347
|
+
exclude ?? undefined
|
|
348
|
+
);
|
|
349
|
+
if (!hit) return null;
|
|
350
|
+
return {
|
|
351
|
+
distance: hit.timeOfImpact,
|
|
352
|
+
actorId: system.byCollider.get(hit.collider.handle) ?? null,
|
|
353
|
+
};
|
|
354
|
+
},
|
|
355
|
+
};
|
|
356
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "castle-web-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.130",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"castle-web": "./dist/index.js"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
|
-
"build": "rm -rf dist && tsc && chmod +x dist/index.js && vite build && cp -R src/castle-host dist/castle-host && rm -rf kits && cp -R ../kits kits",
|
|
9
|
+
"build": "rm -rf dist && tsc && chmod +x dist/index.js && vite build && cp -R src/castle-host dist/castle-host && chmod -R u+w kits 2>/dev/null; rm -rf kits && cp -R ../kits kits && chmod -R u+w kits && rm -rf kits/*/imports",
|
|
10
10
|
"dev": "tsc --watch",
|
|
11
11
|
"check": "eslint . && jscpd && tsc --noEmit && tsc --noEmit -p src/shell/tsconfig.json"
|
|
12
12
|
},
|
|
@@ -24,7 +24,10 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@codemirror/commands": "^6.10.4",
|
|
27
|
+
"@codemirror/lang-css": "^6.3.1",
|
|
28
|
+
"@codemirror/lang-html": "^6.4.12",
|
|
27
29
|
"@codemirror/lang-javascript": "^6.2.5",
|
|
30
|
+
"@codemirror/lang-markdown": "^6.5.2",
|
|
28
31
|
"@codemirror/language": "^6.12.3",
|
|
29
32
|
"@codemirror/state": "^6.6.0",
|
|
30
33
|
"@codemirror/view": "^6.41.1",
|