lecodes-sdk 0.19.1 → 0.20.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/dist/global.d.ts +31 -0
- package/dist/host.d.ts +3 -0
- package/dist/inject.js +260 -361
- package/dist/types/gl/Locomotion.d.ts +3 -1
- package/dist/types/gl/animation/Animator.d.ts +51 -183
- package/dist/types/gl/animation/Feet.d.ts +85 -0
- package/dist/types/gl/animation/Warp.d.ts +53 -0
- package/dist/types/gl/animation/core.d.ts +46 -7
- package/dist/types/inject.d.ts +7 -2
- package/dist/types/plugins/map.d.ts +174 -0
- package/dist/types.json +1 -1
- package/package.json +1 -1
- package/prompts/dist/2d-game.md +197 -408
- package/prompts/dist/3d-app.md +166 -491
- package/prompts/dist/ar-app.md +163 -373
- package/prompts/dist/design.md +87 -83
- package/prompts/dist/ui-app.md +136 -325
- package/src/bridges.d.ts +13 -6
- package/src/compile/__tests__/compile.test.ts +12 -0
- package/src/compile/compileProject.ts +5 -0
- package/src/core/Aspect.ts +1 -1
- package/src/gl/CameraPlace.ts +52 -52
- package/src/gl/Locomotion.ts +7 -5
- package/src/gl/Particles.ts +3 -3
- package/src/gl/Ragdoll.ts +270 -270
- package/src/gl/Scene.ts +1 -1
- package/src/gl/Trigger.ts +45 -45
- package/src/gl/animation/Animator.ts +137 -329
- package/src/gl/animation/Feet.ts +134 -0
- package/src/gl/animation/Loop.ts +3 -1
- package/src/gl/animation/Warp.ts +96 -0
- package/src/gl/animation/core.ts +736 -670
- package/src/gl/scenarios.ts +291 -291
- package/src/gl/state.ts +6 -6
- package/src/host.d.ts +3 -0
- package/src/inject.ts +11 -2
- package/src/plugins/map.ts +396 -0
- package/src/scene/gizmos.ts +148 -148
package/dist/inject.js
CHANGED
|
@@ -1235,88 +1235,6 @@ var Color = {
|
|
|
1235
1235
|
}
|
|
1236
1236
|
};
|
|
1237
1237
|
|
|
1238
|
-
// src/core/events.ts
|
|
1239
|
-
var Emitter = class {
|
|
1240
|
-
_listeners = {};
|
|
1241
|
-
addEventListener(channel, callback) {
|
|
1242
|
-
const list = this._listeners[channel];
|
|
1243
|
-
if (list) list.push(callback);
|
|
1244
|
-
else this._listeners[channel] = [callback];
|
|
1245
|
-
}
|
|
1246
|
-
removeEventListener(channel, callback) {
|
|
1247
|
-
const list = this._listeners[channel];
|
|
1248
|
-
if (!list) return;
|
|
1249
|
-
const i = list.indexOf(callback);
|
|
1250
|
-
if (i >= 0) list.splice(i, 1);
|
|
1251
|
-
}
|
|
1252
|
-
/** Fire all listeners on a channel. Protected: only the owning class emits. */
|
|
1253
|
-
dispatch(channel, ...args) {
|
|
1254
|
-
const list = this._listeners[channel];
|
|
1255
|
-
if (!list) return;
|
|
1256
|
-
for (const fn of list.slice()) fn(...args);
|
|
1257
|
-
}
|
|
1258
|
-
hasListeners(channel) {
|
|
1259
|
-
const list = this._listeners[channel];
|
|
1260
|
-
return !!list && list.length > 0;
|
|
1261
|
-
}
|
|
1262
|
-
/** Drop every listener on every channel (used when the owning object is disposed). */
|
|
1263
|
-
clearListeners() {
|
|
1264
|
-
this._listeners = {};
|
|
1265
|
-
}
|
|
1266
|
-
};
|
|
1267
|
-
|
|
1268
|
-
// src/core/Aspect.ts
|
|
1269
|
-
var AspectHost = class extends Emitter {
|
|
1270
|
-
/** @internal class → instance, the authoritative store (named accessors mirror this). */
|
|
1271
|
-
_aspects = /* @__PURE__ */ new Map();
|
|
1272
|
-
/** Attach (and configure) an aspect, or reconfigure it if already present. Returns the node typed
|
|
1273
|
-
* as now-having that aspect (so no guard/`?.` is needed afterwards). Rejects a wrong target node. */
|
|
1274
|
-
aspect(ctor, opts) {
|
|
1275
|
-
const host = this;
|
|
1276
|
-
let inst = host._aspects.get(ctor);
|
|
1277
|
-
const fresh = inst === void 0;
|
|
1278
|
-
if (!inst) {
|
|
1279
|
-
inst = new ctor();
|
|
1280
|
-
inst.node = this;
|
|
1281
|
-
host._aspects.set(ctor, inst);
|
|
1282
|
-
const name = ctor.aspect;
|
|
1283
|
-
if (name) host[name] = inst;
|
|
1284
|
-
}
|
|
1285
|
-
if (opts) Object.assign(inst, opts);
|
|
1286
|
-
if (fresh) inst.onAttach?.();
|
|
1287
|
-
return this;
|
|
1288
|
-
}
|
|
1289
|
-
/** Safe access — undefined if the aspect isn't attached. */
|
|
1290
|
-
get(ctor) {
|
|
1291
|
-
return this._aspects.get(ctor);
|
|
1292
|
-
}
|
|
1293
|
-
/** Existence check AND type guard: inside `if (node.has(Physics))`, `node.physics` is present. */
|
|
1294
|
-
has(ctor) {
|
|
1295
|
-
return this._aspects.has(ctor);
|
|
1296
|
-
}
|
|
1297
|
-
/** Detach an aspect (runs its onDetach). Named `removeAspect` so it never collides with the
|
|
1298
|
-
* child-management `add`/`remove` semantics some node kinds expose. */
|
|
1299
|
-
removeAspect(ctor) {
|
|
1300
|
-
const inst = this._aspects.get(ctor);
|
|
1301
|
-
if (inst) {
|
|
1302
|
-
inst.onDetach?.();
|
|
1303
|
-
this._aspects.delete(ctor);
|
|
1304
|
-
const name = ctor.aspect;
|
|
1305
|
-
if (name) delete this[name];
|
|
1306
|
-
}
|
|
1307
|
-
return this;
|
|
1308
|
-
}
|
|
1309
|
-
};
|
|
1310
|
-
var Aspect = class {
|
|
1311
|
-
// NOTE: P has no `extends AspectHost` constraint on purpose — Emitter<E>'s generic
|
|
1312
|
-
// addEventListener makes a specific-event node (AspectHost<NodeEvents>) not assignable to
|
|
1313
|
-
// AspectHost<any>, which would reject every real node. P only types `this.node`; target
|
|
1314
|
-
// enforcement happens in aspect() via `this: Self extends TargetOf<A>`. Defaults to Node
|
|
1315
|
-
// (3D is the default engine) — a 2D aspect declares its own P, e.g. Aspect<'x', Sprite>.
|
|
1316
|
-
/** The node this aspect is attached to (set at attach time). */
|
|
1317
|
-
node;
|
|
1318
|
-
};
|
|
1319
|
-
|
|
1320
1238
|
// src/runtime/misc.ts
|
|
1321
1239
|
var toast = (msg) => _creatorUI.toast(msg);
|
|
1322
1240
|
var SvgSource = (svg) => ({ svg, tintColor: null });
|
|
@@ -1418,6 +1336,36 @@ var Input = {
|
|
|
1418
1336
|
}
|
|
1419
1337
|
};
|
|
1420
1338
|
|
|
1339
|
+
// src/core/events.ts
|
|
1340
|
+
var Emitter = class {
|
|
1341
|
+
_listeners = {};
|
|
1342
|
+
addEventListener(channel, callback) {
|
|
1343
|
+
const list = this._listeners[channel];
|
|
1344
|
+
if (list) list.push(callback);
|
|
1345
|
+
else this._listeners[channel] = [callback];
|
|
1346
|
+
}
|
|
1347
|
+
removeEventListener(channel, callback) {
|
|
1348
|
+
const list = this._listeners[channel];
|
|
1349
|
+
if (!list) return;
|
|
1350
|
+
const i = list.indexOf(callback);
|
|
1351
|
+
if (i >= 0) list.splice(i, 1);
|
|
1352
|
+
}
|
|
1353
|
+
/** Fire all listeners on a channel. Protected: only the owning class emits. */
|
|
1354
|
+
dispatch(channel, ...args) {
|
|
1355
|
+
const list = this._listeners[channel];
|
|
1356
|
+
if (!list) return;
|
|
1357
|
+
for (const fn of list.slice()) fn(...args);
|
|
1358
|
+
}
|
|
1359
|
+
hasListeners(channel) {
|
|
1360
|
+
const list = this._listeners[channel];
|
|
1361
|
+
return !!list && list.length > 0;
|
|
1362
|
+
}
|
|
1363
|
+
/** Drop every listener on every channel (used when the owning object is disposed). */
|
|
1364
|
+
clearListeners() {
|
|
1365
|
+
this._listeners = {};
|
|
1366
|
+
}
|
|
1367
|
+
};
|
|
1368
|
+
|
|
1421
1369
|
// src/runtime/net.ts
|
|
1422
1370
|
var WebSocket = class extends Emitter {
|
|
1423
1371
|
_id = null;
|
|
@@ -1637,7 +1585,7 @@ var Registry = class {
|
|
|
1637
1585
|
|
|
1638
1586
|
// src/g2/Node2D.ts
|
|
1639
1587
|
var node2dRegistry = new Registry();
|
|
1640
|
-
var Node2D = class extends
|
|
1588
|
+
var Node2D = class extends Emitter {
|
|
1641
1589
|
/** Native entity handle. */
|
|
1642
1590
|
id;
|
|
1643
1591
|
_x = 0;
|
|
@@ -1717,14 +1665,16 @@ var Node2D = class extends AspectHost {
|
|
|
1717
1665
|
this._visible = v;
|
|
1718
1666
|
_creator2d.setVisible(this.id, v);
|
|
1719
1667
|
}
|
|
1720
|
-
/**
|
|
1721
|
-
*
|
|
1722
|
-
|
|
1668
|
+
/**
|
|
1669
|
+
* Attach a collider for overlap detection. DEFERRED: native 2D physics/triggers are not built yet,
|
|
1670
|
+
* so this records intent and is a no-op until the engine lands — the 'enter'/'exit' events won't
|
|
1671
|
+
* fire. The API is here so game code that uses it compiles and stays stable.
|
|
1672
|
+
*/
|
|
1673
|
+
collider(_options = {}) {
|
|
1674
|
+
return this;
|
|
1723
1675
|
}
|
|
1724
|
-
/** @internal —
|
|
1725
|
-
|
|
1726
|
-
_emitAnim(channel, clip) {
|
|
1727
|
-
this.dispatch(channel, clip);
|
|
1676
|
+
/** @internal — overridden by Sprite to translate native clip ids into named animation events. */
|
|
1677
|
+
_dispatchAnimEvent(_clipId, _type) {
|
|
1728
1678
|
}
|
|
1729
1679
|
destroy() {
|
|
1730
1680
|
node2dRegistry.delete(this.id);
|
|
@@ -1887,6 +1837,9 @@ var Sprite = class extends Node2D {
|
|
|
1887
1837
|
_flipX = false;
|
|
1888
1838
|
_flipY = false;
|
|
1889
1839
|
_size = null;
|
|
1840
|
+
_clipIds = {};
|
|
1841
|
+
_clipNames = {};
|
|
1842
|
+
_current = null;
|
|
1890
1843
|
constructor(options = {}) {
|
|
1891
1844
|
super();
|
|
1892
1845
|
if (options.texture) this.texture = options.texture;
|
|
@@ -1960,92 +1913,69 @@ var Sprite = class extends Node2D {
|
|
|
1960
1913
|
this._flipY = v;
|
|
1961
1914
|
_creator2d.setSpriteFlip(this.id, this._flipX, v);
|
|
1962
1915
|
}
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
/** Named clips; frame indices are row-major into the grid. Set via the attach opts. */
|
|
1975
|
-
clips = {};
|
|
1976
|
-
_clipIds = {};
|
|
1977
|
-
_clipNames = {};
|
|
1978
|
-
_current = null;
|
|
1979
|
-
onAttach() {
|
|
1980
|
-
this._define();
|
|
1981
|
-
this.node._dispatchAnimEvent = (clipId, type) => this.node._emitAnim(type === 1 ? "completed" : "loopReached", this._clipNames[clipId] ?? "");
|
|
1982
|
-
ensureAnimEvents();
|
|
1983
|
-
}
|
|
1984
|
-
/** (Re)build native clips from `this.clips`. Called on attach; call again after changing clips. */
|
|
1985
|
-
define() {
|
|
1986
|
-
this._define();
|
|
1987
|
-
return this;
|
|
1988
|
-
}
|
|
1989
|
-
_define() {
|
|
1990
|
-
const node = this.node;
|
|
1991
|
-
const tex = node.texture;
|
|
1992
|
-
if (!tex) throw new Error("SpriteAnimation: set a texture on the Sprite before adding the aspect");
|
|
1993
|
-
const [fw, fh] = this.size ?? [tex.width, tex.height];
|
|
1916
|
+
// --- animation (chainable) ---
|
|
1917
|
+
/**
|
|
1918
|
+
* Define named sprite-sheet clips from a uniform grid. Frame indices are row-major into the grid.
|
|
1919
|
+
* Sets the sprite's display size to one cell. Once defined, clips advance entirely in native code.
|
|
1920
|
+
*/
|
|
1921
|
+
clips(def) {
|
|
1922
|
+
const tex = this._texture;
|
|
1923
|
+
if (!tex) throw new Error("Sprite.clips(): set a texture before defining clips");
|
|
1924
|
+
const [fw, fh] = def.size;
|
|
1925
|
+
const defFps = typeof def.fps === "number" ? def.fps : 12;
|
|
1926
|
+
const defLoop = typeof def.loop === "boolean" ? def.loop : true;
|
|
1994
1927
|
const cols = Math.max(1, Math.floor(tex.width / fw));
|
|
1995
|
-
for (const name of Object.keys(
|
|
1996
|
-
|
|
1928
|
+
for (const name of Object.keys(def)) {
|
|
1929
|
+
if (name === "size" || name === "fps" || name === "loop") continue;
|
|
1930
|
+
const value = def[name];
|
|
1997
1931
|
const indices = Array.isArray(value) ? value : value.frames;
|
|
1998
|
-
const fps = Array.isArray(value) ?
|
|
1999
|
-
const loop = Array.isArray(value) ?
|
|
1932
|
+
const fps = Array.isArray(value) ? defFps : value.fps ?? defFps;
|
|
1933
|
+
const loop = Array.isArray(value) ? defLoop : value.loop ?? defLoop;
|
|
2000
1934
|
const frames = new Float32Array(indices.length * 4);
|
|
2001
1935
|
indices.forEach((idx, i) => {
|
|
2002
|
-
const
|
|
2003
|
-
const
|
|
2004
|
-
frames[i * 4 + 0] =
|
|
2005
|
-
frames[i * 4 + 1] =
|
|
2006
|
-
frames[i * 4 + 2] = (
|
|
2007
|
-
frames[i * 4 + 3] = (
|
|
1936
|
+
const cx2 = idx % cols * fw;
|
|
1937
|
+
const cy2 = Math.floor(idx / cols) * fh;
|
|
1938
|
+
frames[i * 4 + 0] = cx2 / tex.width;
|
|
1939
|
+
frames[i * 4 + 1] = cy2 / tex.height;
|
|
1940
|
+
frames[i * 4 + 2] = (cx2 + fw) / tex.width;
|
|
1941
|
+
frames[i * 4 + 3] = (cy2 + fh) / tex.height;
|
|
2008
1942
|
});
|
|
2009
|
-
const clipId = _creator2d.defineAnimation(
|
|
1943
|
+
const clipId = _creator2d.defineAnimation(this.id, frames, fps, loop);
|
|
2010
1944
|
this._clipIds[name] = clipId;
|
|
2011
1945
|
this._clipNames[clipId] = name;
|
|
2012
1946
|
}
|
|
2013
|
-
if (this.
|
|
1947
|
+
if (!this._size) _creator2d.setSpriteSize(this.id, fw, fh);
|
|
1948
|
+
ensureAnimEvents();
|
|
1949
|
+
return this;
|
|
2014
1950
|
}
|
|
2015
1951
|
/** Play a clip by name. Re-playing the active clip is a no-op (keeps it running smoothly). */
|
|
2016
1952
|
play(name) {
|
|
2017
1953
|
const id = this._clipIds[name];
|
|
2018
|
-
if (id === void 0) throw new Error(`
|
|
1954
|
+
if (id === void 0) throw new Error(`Sprite.play(): unknown clip "${name}"`);
|
|
2019
1955
|
if (this._current === name) return this;
|
|
2020
1956
|
this._current = name;
|
|
2021
|
-
_creator2d.playAnimation(this.
|
|
1957
|
+
_creator2d.playAnimation(this.id, id);
|
|
2022
1958
|
return this;
|
|
2023
1959
|
}
|
|
2024
1960
|
stop() {
|
|
2025
1961
|
this._current = null;
|
|
2026
|
-
_creator2d.stopAnimation(this.
|
|
1962
|
+
_creator2d.stopAnimation(this.id);
|
|
2027
1963
|
return this;
|
|
2028
1964
|
}
|
|
2029
|
-
set
|
|
2030
|
-
_creator2d.setAnimationSpeed(this.
|
|
1965
|
+
set animationSpeed(value) {
|
|
1966
|
+
_creator2d.setAnimationSpeed(this.id, value);
|
|
2031
1967
|
}
|
|
2032
|
-
get
|
|
1968
|
+
get currentClip() {
|
|
2033
1969
|
return this._current;
|
|
2034
1970
|
}
|
|
2035
|
-
get
|
|
2036
|
-
return _creator2d.getAnimationFrame(this.
|
|
1971
|
+
get animationFrame() {
|
|
1972
|
+
return _creator2d.getAnimationFrame(this.id);
|
|
1973
|
+
}
|
|
1974
|
+
/** @internal — translate a native clip id back to its name and emit the animation event. */
|
|
1975
|
+
_dispatchAnimEvent(clipId, type) {
|
|
1976
|
+
const name = this._clipNames[clipId] ?? "";
|
|
1977
|
+
this.dispatch(type === 1 ? "completed" : "loopReached", name);
|
|
2037
1978
|
}
|
|
2038
|
-
};
|
|
2039
|
-
|
|
2040
|
-
// src/g2/Collider2D.ts
|
|
2041
|
-
var Collider2D = class extends Aspect {
|
|
2042
|
-
static aspect = "collider";
|
|
2043
|
-
/** Box half-extents (world units). Defaults to the sprite size when omitted. */
|
|
2044
|
-
box;
|
|
2045
|
-
/** Center offset from the node. */
|
|
2046
|
-
offset;
|
|
2047
|
-
/** Report overlaps via the node's 'enter'/'exit' events instead of blocking (always true for now). */
|
|
2048
|
-
trigger = true;
|
|
2049
1979
|
};
|
|
2050
1980
|
|
|
2051
1981
|
// src/g2/Tilemap.ts
|
|
@@ -3226,6 +3156,82 @@ var QRScanner = class _QRScanner {
|
|
|
3226
3156
|
}
|
|
3227
3157
|
};
|
|
3228
3158
|
|
|
3159
|
+
// src/gl/Body.ts
|
|
3160
|
+
var MOTION = { static: 0, kinematic: 1, dynamic: 2 };
|
|
3161
|
+
var Body = class {
|
|
3162
|
+
_bodyId = 0;
|
|
3163
|
+
_motion;
|
|
3164
|
+
constructor(node, options = {}) {
|
|
3165
|
+
this._motion = options.motion ?? "dynamic";
|
|
3166
|
+
const mass = options.mass ?? 1;
|
|
3167
|
+
if (!_creator.physicsHasSupport || !_creator.physicsHasSupport()) return;
|
|
3168
|
+
const motion = MOTION[this._motion];
|
|
3169
|
+
const shape = resolveShape(node, options.shape ?? { type: "auto" });
|
|
3170
|
+
if (shape.type === "sphere") {
|
|
3171
|
+
this._bodyId = _creator.physicsCreateSphere(node.id, shape.radius, motion, mass);
|
|
3172
|
+
} else if (shape.type === "cylinder") {
|
|
3173
|
+
this._bodyId = _creator.physicsCreateCylinder(node.id, shape.halfHeight, shape.radius, motion, mass);
|
|
3174
|
+
} else {
|
|
3175
|
+
this._bodyId = _creator.physicsCreateBox(node.id, cx(shape.halfExtents), cy(shape.halfExtents), cz(shape.halfExtents), motion, mass);
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3178
|
+
/** Native Jolt body id. 0 until assigned, or if the build has no physics support. */
|
|
3179
|
+
get id() {
|
|
3180
|
+
return this._bodyId;
|
|
3181
|
+
}
|
|
3182
|
+
get motion() {
|
|
3183
|
+
return this._motion;
|
|
3184
|
+
}
|
|
3185
|
+
/** Linear velocity in world units/second (fresh Vec3 on read). */
|
|
3186
|
+
get velocity() {
|
|
3187
|
+
const out = new Float32Array(3);
|
|
3188
|
+
if (this._bodyId) _creator.physicsGetLinearVelocity(this._bodyId, out);
|
|
3189
|
+
return new Vec3(out[0], out[1], out[2]);
|
|
3190
|
+
}
|
|
3191
|
+
set velocity(v) {
|
|
3192
|
+
if (this._bodyId) _creator.physicsSetLinearVelocity(this._bodyId, cx(v), cy(v), cz(v));
|
|
3193
|
+
}
|
|
3194
|
+
/** Apply an instantaneous impulse (kg·m/s) and wake the body. */
|
|
3195
|
+
applyImpulse(v) {
|
|
3196
|
+
if (this._bodyId) _creator.physicsApplyImpulse(this._bodyId, cx(v), cy(v), cz(v));
|
|
3197
|
+
return this;
|
|
3198
|
+
}
|
|
3199
|
+
/** Teleport / drive a kinematic (or dynamic) body to a world position. */
|
|
3200
|
+
moveTo(p) {
|
|
3201
|
+
if (this._bodyId) _creator.physicsSetBodyPosition(this._bodyId, cx(p), cy(p), cz(p));
|
|
3202
|
+
return this;
|
|
3203
|
+
}
|
|
3204
|
+
/** Remove the body from the simulation. */
|
|
3205
|
+
remove() {
|
|
3206
|
+
if (this._bodyId) {
|
|
3207
|
+
_creator.physicsRemoveBody(this._bodyId);
|
|
3208
|
+
this._bodyId = 0;
|
|
3209
|
+
}
|
|
3210
|
+
}
|
|
3211
|
+
};
|
|
3212
|
+
var resolveShape = (node, shape) => {
|
|
3213
|
+
if (shape.type !== "auto") return shape;
|
|
3214
|
+
const scale = node.worldScale;
|
|
3215
|
+
let hx = 0.5, hy = 0.5, hz = 0.5;
|
|
3216
|
+
const geom = node.geometry;
|
|
3217
|
+
if (geom) {
|
|
3218
|
+
const v = geom.vertices;
|
|
3219
|
+
let minX = Infinity, minY = Infinity, minZ = Infinity, maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
|
|
3220
|
+
for (let i = 0; i + 2 < v.length; i += 3) {
|
|
3221
|
+
if (v[i] < minX) minX = v[i];
|
|
3222
|
+
if (v[i] > maxX) maxX = v[i];
|
|
3223
|
+
if (v[i + 1] < minY) minY = v[i + 1];
|
|
3224
|
+
if (v[i + 1] > maxY) maxY = v[i + 1];
|
|
3225
|
+
if (v[i + 2] < minZ) minZ = v[i + 2];
|
|
3226
|
+
if (v[i + 2] > maxZ) maxZ = v[i + 2];
|
|
3227
|
+
}
|
|
3228
|
+
hx = (maxX - minX) * 0.5;
|
|
3229
|
+
hy = (maxY - minY) * 0.5;
|
|
3230
|
+
hz = (maxZ - minZ) * 0.5;
|
|
3231
|
+
}
|
|
3232
|
+
return { type: "box", halfExtents: [hx * scale.x, hy * scale.y, hz * scale.z] };
|
|
3233
|
+
};
|
|
3234
|
+
|
|
3229
3235
|
// src/gl/state.ts
|
|
3230
3236
|
var glState = { activeScene: null };
|
|
3231
3237
|
|
|
@@ -3279,9 +3285,17 @@ var registerTouchEndEvent = () => {
|
|
|
3279
3285
|
|
|
3280
3286
|
// src/gl/Node.ts
|
|
3281
3287
|
var nodeRegistry = new Registry();
|
|
3282
|
-
var
|
|
3288
|
+
var animatorRegistered = false;
|
|
3289
|
+
var ensureAnimator = () => {
|
|
3290
|
+
if (animatorRegistered) return;
|
|
3291
|
+
animatorRegistered = true;
|
|
3292
|
+
_creator.setOnAnimationComplete((entityId, clipIndex) => nodeRegistry.get(entityId)?._onAnimationComplete(clipIndex) ?? true);
|
|
3293
|
+
};
|
|
3294
|
+
var Node = class _Node extends Emitter {
|
|
3283
3295
|
/** Native entity handle. */
|
|
3284
3296
|
id;
|
|
3297
|
+
/** Physics handle once .physics() is called. */
|
|
3298
|
+
body;
|
|
3285
3299
|
/** AR anchors only — whether the anchor is currently being tracked. Kept in step with the
|
|
3286
3300
|
* 'track' / 'untrack' events; `false` for any non-anchor node. */
|
|
3287
3301
|
isTracked = false;
|
|
@@ -3289,8 +3303,10 @@ var Node = class _Node extends AspectHost {
|
|
|
3289
3303
|
_lastSync = 0;
|
|
3290
3304
|
_worldMatrix;
|
|
3291
3305
|
_scaleCache;
|
|
3292
|
-
|
|
3293
|
-
|
|
3306
|
+
_animPlaying = false;
|
|
3307
|
+
_animClip = 0;
|
|
3308
|
+
_animSpeed = 1;
|
|
3309
|
+
_animLoop = false;
|
|
3294
3310
|
constructor(internalId) {
|
|
3295
3311
|
super();
|
|
3296
3312
|
this.id = internalId ?? _creator.createEntity();
|
|
@@ -3460,7 +3476,24 @@ var Node = class _Node extends AspectHost {
|
|
|
3460
3476
|
callback(this);
|
|
3461
3477
|
_creator.traverse(this.id, (id) => callback(nodeRegistry.get(id) ?? new _Node(id)));
|
|
3462
3478
|
}
|
|
3463
|
-
// --- behaviors
|
|
3479
|
+
// --- behaviors ---
|
|
3480
|
+
/** Add a physics body (reach it at node.body). Chainable. */
|
|
3481
|
+
physics(options = {}) {
|
|
3482
|
+
this.body = new Body(this, options);
|
|
3483
|
+
return this;
|
|
3484
|
+
}
|
|
3485
|
+
/** Add a collider for click/touch hit-testing. Derives from the mesh by default. Chainable. */
|
|
3486
|
+
collider(options = {}) {
|
|
3487
|
+
const form = options.shape ?? "box";
|
|
3488
|
+
if (form === "box" && options.center && options.size) {
|
|
3489
|
+
_creator.setColliderBox(this.id, cx(options.center), cy(options.center), cz(options.center), cx(options.size), cy(options.size), cz(options.size));
|
|
3490
|
+
} else if (form === "sphere" && options.center && options.radius !== void 0) {
|
|
3491
|
+
_creator.setColliderSphere(this.id, cx(options.center), cy(options.center), cz(options.center), options.radius);
|
|
3492
|
+
} else {
|
|
3493
|
+
_creator.setColliderFromMesh(this.id, this.id, form === "sphere" ? 1 : 0);
|
|
3494
|
+
}
|
|
3495
|
+
return this;
|
|
3496
|
+
}
|
|
3464
3497
|
/** Orient the node so the given axis (default "-z", "forward") points at a world point. */
|
|
3465
3498
|
lookAt(point, mode = "-z", ortho = [0, 1, 0]) {
|
|
3466
3499
|
const world = this.worldMatrix;
|
|
@@ -3509,6 +3542,52 @@ var Node = class _Node extends AspectHost {
|
|
|
3509
3542
|
);
|
|
3510
3543
|
return this;
|
|
3511
3544
|
}
|
|
3545
|
+
// --- GLB animation (no-op until a GLB is loaded as this node) ---
|
|
3546
|
+
get clips() {
|
|
3547
|
+
return _creator.getGlbAnimations(this.id);
|
|
3548
|
+
}
|
|
3549
|
+
get animationPlaying() {
|
|
3550
|
+
return this._animPlaying;
|
|
3551
|
+
}
|
|
3552
|
+
set animationPlaying(value) {
|
|
3553
|
+
this._animPlaying = value;
|
|
3554
|
+
_creator.setAnimation(this.id, value, this._animClip, this._animSpeed);
|
|
3555
|
+
}
|
|
3556
|
+
/** Play a GLB clip by name or index. Chainable. */
|
|
3557
|
+
play(clip, options = {}) {
|
|
3558
|
+
if (typeof clip === "number") this._animClip = clip;
|
|
3559
|
+
else if (typeof clip === "string") {
|
|
3560
|
+
const i = this.clips.findIndex((c2) => c2.name === clip);
|
|
3561
|
+
if (i >= 0) this._animClip = i;
|
|
3562
|
+
}
|
|
3563
|
+
if (options.loop !== void 0) this._animLoop = options.loop;
|
|
3564
|
+
this.animationPlaying = true;
|
|
3565
|
+
ensureAnimator();
|
|
3566
|
+
return this;
|
|
3567
|
+
}
|
|
3568
|
+
stop() {
|
|
3569
|
+
this.animationPlaying = false;
|
|
3570
|
+
return this;
|
|
3571
|
+
}
|
|
3572
|
+
get animationLoop() {
|
|
3573
|
+
return this._animLoop;
|
|
3574
|
+
}
|
|
3575
|
+
set animationLoop(v) {
|
|
3576
|
+
this._animLoop = v;
|
|
3577
|
+
}
|
|
3578
|
+
get animationSpeed() {
|
|
3579
|
+
return this._animSpeed;
|
|
3580
|
+
}
|
|
3581
|
+
set animationSpeed(v) {
|
|
3582
|
+
this._animSpeed = v;
|
|
3583
|
+
_creator.setAnimation(this.id, this._animPlaying, this._animClip, v);
|
|
3584
|
+
}
|
|
3585
|
+
get animationTime() {
|
|
3586
|
+
return _creator.getAnimationTime(this.id);
|
|
3587
|
+
}
|
|
3588
|
+
set animationTime(v) {
|
|
3589
|
+
_creator.setAnimationTime(this.id, this._animClip, v);
|
|
3590
|
+
}
|
|
3512
3591
|
// --- events ---
|
|
3513
3592
|
addEventListener(channel, callback) {
|
|
3514
3593
|
if (channel === "click") registerTouchEndEvent();
|
|
@@ -3519,14 +3598,14 @@ var Node = class _Node extends AspectHost {
|
|
|
3519
3598
|
_dispatchEvent(channel, ...args) {
|
|
3520
3599
|
this.dispatch(channel, ...args);
|
|
3521
3600
|
}
|
|
3522
|
-
/** @internal — host animation-complete callback;
|
|
3523
|
-
* Returns whether the clip should keep playing. */
|
|
3601
|
+
/** @internal — host animation-complete callback; returns whether the clip should keep playing. */
|
|
3524
3602
|
_onAnimationComplete(clipIndex) {
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
this.dispatch(
|
|
3603
|
+
if (clipIndex !== this._animClip) return true;
|
|
3604
|
+
if (!this._animLoop) {
|
|
3605
|
+
this._animPlaying = false;
|
|
3606
|
+
this.dispatch("completed", clipIndex);
|
|
3607
|
+
} else this.dispatch("loopReached", clipIndex);
|
|
3608
|
+
return this._animPlaying;
|
|
3530
3609
|
}
|
|
3531
3610
|
destroy() {
|
|
3532
3611
|
nodeRegistry.delete(this.id);
|
|
@@ -4087,10 +4166,10 @@ var createPlane = (options = {}) => {
|
|
|
4087
4166
|
let upVector, rightVec;
|
|
4088
4167
|
if (normal[1] === 0 && normal[2] === 0) {
|
|
4089
4168
|
upVector = [0, 1, 0];
|
|
4090
|
-
rightVec = [0, 0,
|
|
4169
|
+
rightVec = [0, 0, normal[0]];
|
|
4091
4170
|
} else if (normal[0] === 0 && normal[2] === 0) {
|
|
4092
4171
|
upVector = [0, 0, 1];
|
|
4093
|
-
rightVec = [
|
|
4172
|
+
rightVec = [normal[1], 0, 0];
|
|
4094
4173
|
} else if (normal[0] === 0 && normal[1] === 0) {
|
|
4095
4174
|
upVector = [0, 1, 0];
|
|
4096
4175
|
rightVec = [normal[2], 0, 0];
|
|
@@ -4180,101 +4259,15 @@ var Mesh = class _Mesh extends Node {
|
|
|
4180
4259
|
static from(geometry, options = {}) {
|
|
4181
4260
|
return apply(new _Mesh(geometry, options.material), options);
|
|
4182
4261
|
}
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
if (o.eulerAngles) mesh.eulerAngles = o.eulerAngles;
|
|
4188
|
-
if (o.scale !== void 0) mesh.scale = typeof o.scale === "number" ? [o.scale, o.scale, o.scale] : o.scale;
|
|
4189
|
-
if (o.name !== void 0) mesh.name = o.name;
|
|
4190
|
-
if (o.castShadows !== void 0) mesh.castShadows = o.castShadows;
|
|
4191
|
-
if (o.receiveShadows !== void 0) mesh.receiveShadows = o.receiveShadows;
|
|
4192
|
-
return mesh;
|
|
4193
|
-
};
|
|
4194
|
-
|
|
4195
|
-
// src/gl/ModelAnimation.ts
|
|
4196
|
-
var animatorRegistered = false;
|
|
4197
|
-
var ensureAnimator = () => {
|
|
4198
|
-
if (animatorRegistered) return;
|
|
4199
|
-
animatorRegistered = true;
|
|
4200
|
-
_creator.setOnAnimationComplete((entityId, clipIndex) => nodeRegistry.get(entityId)?._onAnimationComplete(clipIndex) ?? true);
|
|
4201
|
-
};
|
|
4202
|
-
var ModelAnimation = class extends Aspect {
|
|
4203
|
-
static aspect = "anim";
|
|
4204
|
-
_playing = false;
|
|
4205
|
-
_clip = 0;
|
|
4206
|
-
_speed = 1;
|
|
4207
|
-
_loop = false;
|
|
4208
|
-
onAttach() {
|
|
4209
|
-
this.node._animComplete = (clipIndex) => {
|
|
4210
|
-
if (clipIndex !== this._clip) return true;
|
|
4211
|
-
if (!this._loop) {
|
|
4212
|
-
this._playing = false;
|
|
4213
|
-
this.node._emitAnim("completed", clipIndex);
|
|
4214
|
-
} else this.node._emitAnim("loopReached", clipIndex);
|
|
4215
|
-
return this._playing;
|
|
4216
|
-
};
|
|
4217
|
-
}
|
|
4218
|
-
/** The clips baked into the glb. */
|
|
4219
|
-
get clips() {
|
|
4220
|
-
return _creator.getGlbAnimations(this.node.id);
|
|
4221
|
-
}
|
|
4222
|
-
get playing() {
|
|
4223
|
-
return this._playing;
|
|
4224
|
-
}
|
|
4225
|
-
set playing(value) {
|
|
4226
|
-
this._playing = value;
|
|
4227
|
-
_creator.setAnimation(this.node.id, value, this._clip, this._speed);
|
|
4228
|
-
}
|
|
4229
|
-
/** Play a clip by name or index. Chainable. */
|
|
4230
|
-
play(clip, options = {}) {
|
|
4231
|
-
if (typeof clip === "number") this._clip = clip;
|
|
4232
|
-
else if (typeof clip === "string") {
|
|
4233
|
-
const i = this.clips.findIndex((c2) => c2.name === clip);
|
|
4234
|
-
if (i >= 0) this._clip = i;
|
|
4235
|
-
}
|
|
4236
|
-
if (options.loop !== void 0) this._loop = options.loop;
|
|
4237
|
-
this.playing = true;
|
|
4238
|
-
ensureAnimator();
|
|
4239
|
-
return this;
|
|
4240
|
-
}
|
|
4241
|
-
stop() {
|
|
4242
|
-
this.playing = false;
|
|
4243
|
-
return this;
|
|
4244
|
-
}
|
|
4245
|
-
get loop() {
|
|
4246
|
-
return this._loop;
|
|
4247
|
-
}
|
|
4248
|
-
set loop(v) {
|
|
4249
|
-
this._loop = v;
|
|
4250
|
-
}
|
|
4251
|
-
get speed() {
|
|
4252
|
-
return this._speed;
|
|
4253
|
-
}
|
|
4254
|
-
set speed(v) {
|
|
4255
|
-
this._speed = v;
|
|
4256
|
-
_creator.setAnimation(this.node.id, this._playing, this._clip, v);
|
|
4257
|
-
}
|
|
4258
|
-
get time() {
|
|
4259
|
-
return _creator.getAnimationTime(this.node.id);
|
|
4260
|
-
}
|
|
4261
|
-
set time(v) {
|
|
4262
|
-
_creator.setAnimationTime(this.node.id, this._clip, v);
|
|
4263
|
-
}
|
|
4264
|
-
};
|
|
4265
|
-
|
|
4266
|
-
// src/gl/Model.ts
|
|
4267
|
-
var Model = class _Model extends Node {
|
|
4268
|
-
constructor(internalId) {
|
|
4269
|
-
super(internalId);
|
|
4270
|
-
this.aspect(ModelAnimation);
|
|
4271
|
-
}
|
|
4272
|
-
/** Load a GLB model. Returns its root as a Model; play its baked clips via model.anim. */
|
|
4262
|
+
/**
|
|
4263
|
+
* Load a GLB model. Returns the model's root Node (a GLB is a node hierarchy); its animation clips
|
|
4264
|
+
* are available via node.clips and driven with node.play(name|index).
|
|
4265
|
+
*/
|
|
4273
4266
|
static load(source, options = {}) {
|
|
4274
4267
|
if (typeof source === "string") {
|
|
4275
4268
|
return fetch(source, { useOnce: true, onProgress: options.onProgress }).then((resp) => {
|
|
4276
4269
|
if (resp.status >= 400) return Promise.reject(new Error(`Failed to load GLB from ${source}. HTTP ${resp.status}`));
|
|
4277
|
-
return
|
|
4270
|
+
return _Mesh.load(resp, options);
|
|
4278
4271
|
});
|
|
4279
4272
|
}
|
|
4280
4273
|
return new Promise((resolve, reject) => {
|
|
@@ -4283,109 +4276,22 @@ var Model = class _Model extends Node {
|
|
|
4283
4276
|
reject(new Error("Failed to load GLB"));
|
|
4284
4277
|
return;
|
|
4285
4278
|
}
|
|
4286
|
-
const
|
|
4279
|
+
const node = new Node(entityId);
|
|
4287
4280
|
if (options.culling !== true) _creator.setGlbCulling(entityId, false);
|
|
4288
|
-
resolve(
|
|
4281
|
+
resolve(node);
|
|
4289
4282
|
}, reject);
|
|
4290
4283
|
});
|
|
4291
4284
|
}
|
|
4292
4285
|
};
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
/** Default { type: "auto" } — a box from the mesh AABB × world scale. */
|
|
4303
|
-
shape = { type: "auto" };
|
|
4304
|
-
_bodyId = 0;
|
|
4305
|
-
onAttach() {
|
|
4306
|
-
if (!_creator.physicsHasSupport || !_creator.physicsHasSupport()) return;
|
|
4307
|
-
const motion = MOTION[this.motion];
|
|
4308
|
-
const shape = resolveShape(this.node, this.shape);
|
|
4309
|
-
if (shape.type === "sphere") {
|
|
4310
|
-
this._bodyId = _creator.physicsCreateSphere(this.node.id, shape.radius, motion, this.mass);
|
|
4311
|
-
} else if (shape.type === "cylinder") {
|
|
4312
|
-
this._bodyId = _creator.physicsCreateCylinder(this.node.id, shape.halfHeight, shape.radius, motion, this.mass);
|
|
4313
|
-
} else {
|
|
4314
|
-
this._bodyId = _creator.physicsCreateBox(this.node.id, cx(shape.halfExtents), cy(shape.halfExtents), cz(shape.halfExtents), motion, this.mass);
|
|
4315
|
-
}
|
|
4316
|
-
}
|
|
4317
|
-
/** Native Jolt body id. 0 until attached, or if the build has no physics support. */
|
|
4318
|
-
get id() {
|
|
4319
|
-
return this._bodyId;
|
|
4320
|
-
}
|
|
4321
|
-
/** Linear velocity in world units/second (fresh Vec3 on read). */
|
|
4322
|
-
get velocity() {
|
|
4323
|
-
const out = new Float32Array(3);
|
|
4324
|
-
if (this._bodyId) _creator.physicsGetLinearVelocity(this._bodyId, out);
|
|
4325
|
-
return new Vec3(out[0], out[1], out[2]);
|
|
4326
|
-
}
|
|
4327
|
-
set velocity(v) {
|
|
4328
|
-
if (this._bodyId) _creator.physicsSetLinearVelocity(this._bodyId, cx(v), cy(v), cz(v));
|
|
4329
|
-
}
|
|
4330
|
-
/** Apply an instantaneous impulse (kg·m/s) and wake the body. */
|
|
4331
|
-
applyImpulse(v) {
|
|
4332
|
-
if (this._bodyId) _creator.physicsApplyImpulse(this._bodyId, cx(v), cy(v), cz(v));
|
|
4333
|
-
return this;
|
|
4334
|
-
}
|
|
4335
|
-
/** Teleport / drive a kinematic (or dynamic) body to a world position. */
|
|
4336
|
-
moveTo(p) {
|
|
4337
|
-
if (this._bodyId) _creator.physicsSetBodyPosition(this._bodyId, cx(p), cy(p), cz(p));
|
|
4338
|
-
return this;
|
|
4339
|
-
}
|
|
4340
|
-
onDetach() {
|
|
4341
|
-
if (this._bodyId) {
|
|
4342
|
-
_creator.physicsRemoveBody(this._bodyId);
|
|
4343
|
-
this._bodyId = 0;
|
|
4344
|
-
}
|
|
4345
|
-
}
|
|
4346
|
-
};
|
|
4347
|
-
var resolveShape = (node, shape) => {
|
|
4348
|
-
if (shape.type !== "auto") return shape;
|
|
4349
|
-
const scale = node.worldScale;
|
|
4350
|
-
let hx = 0.5, hy = 0.5, hz = 0.5;
|
|
4351
|
-
const geom = node.geometry;
|
|
4352
|
-
if (geom) {
|
|
4353
|
-
const v = geom.vertices;
|
|
4354
|
-
let minX = Infinity, minY = Infinity, minZ = Infinity, maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
|
|
4355
|
-
for (let i = 0; i + 2 < v.length; i += 3) {
|
|
4356
|
-
if (v[i] < minX) minX = v[i];
|
|
4357
|
-
if (v[i] > maxX) maxX = v[i];
|
|
4358
|
-
if (v[i + 1] < minY) minY = v[i + 1];
|
|
4359
|
-
if (v[i + 1] > maxY) maxY = v[i + 1];
|
|
4360
|
-
if (v[i + 2] < minZ) minZ = v[i + 2];
|
|
4361
|
-
if (v[i + 2] > maxZ) maxZ = v[i + 2];
|
|
4362
|
-
}
|
|
4363
|
-
hx = (maxX - minX) * 0.5;
|
|
4364
|
-
hy = (maxY - minY) * 0.5;
|
|
4365
|
-
hz = (maxZ - minZ) * 0.5;
|
|
4366
|
-
}
|
|
4367
|
-
return { type: "box", halfExtents: [hx * scale.x, hy * scale.y, hz * scale.z] };
|
|
4368
|
-
};
|
|
4369
|
-
|
|
4370
|
-
// src/gl/Collider.ts
|
|
4371
|
-
var Collider = class extends Aspect {
|
|
4372
|
-
static aspect = "collider";
|
|
4373
|
-
shape = "box";
|
|
4374
|
-
/** Explicit box: center + full size. */
|
|
4375
|
-
center;
|
|
4376
|
-
size;
|
|
4377
|
-
/** Explicit sphere: center + radius. */
|
|
4378
|
-
radius;
|
|
4379
|
-
onAttach() {
|
|
4380
|
-
const id = this.node.id;
|
|
4381
|
-
if (this.shape === "box" && this.center && this.size) {
|
|
4382
|
-
_creator.setColliderBox(id, cx(this.center), cy(this.center), cz(this.center), cx(this.size), cy(this.size), cz(this.size));
|
|
4383
|
-
} else if (this.shape === "sphere" && this.center && this.radius !== void 0) {
|
|
4384
|
-
_creator.setColliderSphere(id, cx(this.center), cy(this.center), cz(this.center), this.radius);
|
|
4385
|
-
} else {
|
|
4386
|
-
_creator.setColliderFromMesh(id, id, this.shape === "sphere" ? 1 : 0);
|
|
4387
|
-
}
|
|
4388
|
-
}
|
|
4286
|
+
var sizeToVec = (s) => typeof s === "number" ? [s, s, s] : [cx(s), cy(s), cz(s)];
|
|
4287
|
+
var apply = (mesh, o) => {
|
|
4288
|
+
if (o.position) mesh.position = o.position;
|
|
4289
|
+
if (o.eulerAngles) mesh.eulerAngles = o.eulerAngles;
|
|
4290
|
+
if (o.scale !== void 0) mesh.scale = typeof o.scale === "number" ? [o.scale, o.scale, o.scale] : o.scale;
|
|
4291
|
+
if (o.name !== void 0) mesh.name = o.name;
|
|
4292
|
+
if (o.castShadows !== void 0) mesh.castShadows = o.castShadows;
|
|
4293
|
+
if (o.receiveShadows !== void 0) mesh.receiveShadows = o.receiveShadows;
|
|
4294
|
+
return mesh;
|
|
4389
4295
|
};
|
|
4390
4296
|
|
|
4391
4297
|
// src/gl/Light.ts
|
|
@@ -4654,12 +4560,9 @@ var dynamic = (baseValue, type, ...args) => {
|
|
|
4654
4560
|
};
|
|
4655
4561
|
export {
|
|
4656
4562
|
ARScene,
|
|
4657
|
-
Aspect,
|
|
4658
4563
|
AudioPlayer,
|
|
4659
4564
|
Camera,
|
|
4660
4565
|
ClickEvent,
|
|
4661
|
-
Collider,
|
|
4662
|
-
Collider2D,
|
|
4663
4566
|
Color,
|
|
4664
4567
|
File,
|
|
4665
4568
|
FormData,
|
|
@@ -4670,13 +4573,10 @@ export {
|
|
|
4670
4573
|
Material,
|
|
4671
4574
|
Mathf,
|
|
4672
4575
|
Mesh,
|
|
4673
|
-
Model,
|
|
4674
|
-
ModelAnimation,
|
|
4675
4576
|
Node,
|
|
4676
4577
|
Node2D,
|
|
4677
4578
|
Noise,
|
|
4678
4579
|
Particles,
|
|
4679
|
-
Physics,
|
|
4680
4580
|
Plane,
|
|
4681
4581
|
QRScanner,
|
|
4682
4582
|
Quat,
|
|
@@ -4685,7 +4585,6 @@ export {
|
|
|
4685
4585
|
Scene,
|
|
4686
4586
|
Scene2D,
|
|
4687
4587
|
Sprite,
|
|
4688
|
-
SpriteAnimation,
|
|
4689
4588
|
SvgSource,
|
|
4690
4589
|
Texture,
|
|
4691
4590
|
Texture2D,
|