@threenative/core 0.2.0 → 0.3.1

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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +55 -0
  3. package/capabilities.json +5292 -0
  4. package/dist/assets-kyoF7JlJ.d.ts +103 -0
  5. package/dist/audio-BFiGneTL.d.ts +156 -0
  6. package/dist/canvas-layer-BLVijiUJ.d.ts +62 -0
  7. package/dist/game-XGrTzapq.d.ts +1166 -0
  8. package/dist/gpu-readback-D2iRvoe9.d.ts +112 -0
  9. package/dist/hot.d.ts +20 -2
  10. package/dist/hot.js +14 -2
  11. package/dist/index.d.ts +2108 -55
  12. package/dist/index.js +15645 -2222
  13. package/dist/net.d.ts +65 -0
  14. package/dist/net.js +643 -0
  15. package/dist/playtest.d.ts +37 -4
  16. package/dist/playtest.js +246 -546
  17. package/dist/react.d.ts +177 -0
  18. package/dist/react.js +635 -0
  19. package/dist/renderer-C6hqZpoG.d.ts +770 -0
  20. package/dist/ui-layer.d.ts +306 -0
  21. package/dist/ui-layer.js +425 -0
  22. package/dist/world.d.ts +254 -0
  23. package/dist/world.js +2686 -0
  24. package/gpl/LICENSE.GPL +117 -0
  25. package/gpl/convert.py +192 -0
  26. package/gpl/recipes/_common.py +169 -0
  27. package/gpl/recipes/bake_ao.py +111 -0
  28. package/gpl/recipes/decimate.py +64 -0
  29. package/gpl/recipes/retarget.py +131 -0
  30. package/gpl/recipes/unwrap.py +71 -0
  31. package/mcp/assets.mjs +5 -0
  32. package/mcp/blender-server.mjs +632 -0
  33. package/mcp/blender.mjs +27 -0
  34. package/mcp/engine-server.mjs +501 -0
  35. package/mcp/engine.mjs +31 -0
  36. package/mcp/install.d.mts +37 -0
  37. package/mcp/install.mjs +145 -0
  38. package/mcp/launch.mjs +72 -0
  39. package/mcp/sculpt.mjs +5 -0
  40. package/mcp/servers.d.mts +34 -0
  41. package/mcp/servers.mjs +160 -0
  42. package/package.json +76 -6
  43. package/patches/three@0.185.1.patch +522 -0
  44. package/scripts/apply-three-patch.mjs +297 -0
  45. package/scripts/ensure-mcp.mjs +43 -0
  46. package/scripts/postinstall.mjs +6 -0
  47. package/dist/audio-CEAw0w5y.d.ts +0 -35
  48. package/dist/game-DRt1Qhq3.d.ts +0 -429
@@ -0,0 +1,103 @@
1
+ import { Texture, Object3D, AnimationClip } from 'three';
2
+
3
+ interface IAssetLoaderOptions {
4
+ readonly basePath?: string;
5
+ /**
6
+ * URL of the manifest written by the asset compile step (`public/assets.manifest.json`).
7
+ * Defaults to `assets.manifest.json` resolved against `basePath`. A logical path is resolved
8
+ * to its compiled output through it; a manifest that is absent — 404 or unfetchable — falls
9
+ * back to loading every path verbatim, while one that is served malformed or with an unknown
10
+ * version throws.
11
+ */
12
+ readonly manifest?: string;
13
+ /**
14
+ * Directory the game's uncompiled assets live in, relative to `basePath`.
15
+ *
16
+ * Only consulted when there is no manifest, and only after the verbatim path has been tried. It
17
+ * is what makes the delete-test possible: remove everything the asset pipeline produced and the
18
+ * game still finds `assets/rock.png` where the author put it, just slower and uncompressed.
19
+ * Defaults to the compile step's own default source directory.
20
+ */
21
+ readonly sourcePath?: string;
22
+ readonly model?: (url: string) => Promise<unknown>;
23
+ readonly texture?: (url: string) => Promise<Texture>;
24
+ readonly audio?: (url: string) => Promise<AudioBuffer>;
25
+ /**
26
+ * The live renderer (`IRendererLike.raw`), handed to `KTX2Loader.detectSupport()` exactly
27
+ * once so compiled KTX2 textures transcode to a format this machine's GPU actually supports.
28
+ * Required for games whose textures compile to `.ktx2`; without it such a load throws rather
29
+ * than silently uploading decoded RGBA.
30
+ */
31
+ readonly renderer?: unknown;
32
+ }
33
+ /** The structural slice of three's `KTX2Loader` core depends on. */
34
+ interface IKTX2LoaderLike {
35
+ detectSupport(renderer: unknown): unknown;
36
+ load(url: string, onLoad: (texture: Texture) => void, onProgress?: (event: ProgressEvent<EventTarget>) => void, onError?: (error: unknown) => void): unknown;
37
+ setTranscoderPath(path: string): unknown;
38
+ }
39
+ /**
40
+ * Present only when a renderer was handed to the asset loader. `ready` settles once support
41
+ * detection ran; it rejects naming the renderer and platform when no compressed format is
42
+ * supported, and `defineGame` awaits it during boot so such a target fails at construction.
43
+ */
44
+ interface ICompressedTextureSupport {
45
+ /**
46
+ * The one shared instance, also handed to `GLTFLoader.setKTX2Loader()` for models. Resolves
47
+ * `undefined` when the renderer exposed no surface to probe (see `createKtx2Loader`).
48
+ */
49
+ readonly loader: Promise<IKTX2LoaderLike | undefined>;
50
+ readonly ready: Promise<void>;
51
+ }
52
+ interface IAssetLoader {
53
+ readonly compressedTextures?: ICompressedTextureSupport;
54
+ model<T = unknown>(path: string): Promise<T>;
55
+ texture(path: string): Promise<Texture>;
56
+ audio(path: string): Promise<AudioBuffer>;
57
+ release(kind: "audio" | "model" | "texture", path: string): boolean;
58
+ /**
59
+ * Where a logical path is served from, in the order worth trying — the manifest's
60
+ * content-addressed output when a manifest exists, otherwise the verbatim and source paths.
61
+ * For loaders this surface does not wrap (an HDR sky, a font, a data file) — a game must never
62
+ * hard-code a hashed output name, which changes on every rebuild.
63
+ */
64
+ resolve(path: string): Promise<readonly string[]>;
65
+ /**
66
+ * How many loads this loader has been asked for and how many have settled (resolved or
67
+ * rejected). A loading view reads the ratio; the runtime folds it into `ctx.startup.progress`
68
+ * while the start scene loads, so the bar moves with the bytes instead of jumping 0 to 1.
69
+ *
70
+ * `requestedBytes` and `settledBytes` are the same ledger weighed by the `bytes` the compile
71
+ * step already records for every manifest entry, and they are what a loading bar should read.
72
+ * A file count treats a 710 MB model and a 4 KB icon alike: one real game's bar reached 92%
73
+ * on eleven small assets and then stood still for the entire download of the twelfth. Both
74
+ * stay 0 for a game with no manifest, where no size is knowable before the bytes arrive.
75
+ */
76
+ readonly progress: {
77
+ readonly requested: number;
78
+ readonly requestedBytes: number;
79
+ readonly settled: number;
80
+ readonly settledBytes: number;
81
+ };
82
+ clear(): void;
83
+ }
84
+ /**
85
+ * Repair an exported rig whose clips are Z-mirrored against its own bind pose, in place.
86
+ *
87
+ * The signature (PRD-324, the whole Wildwood animal pack): the file's bind faces its own +Z, but
88
+ * every animation track is expressed in a Z-mirrored frame — position tracks hold `(x, y, −z)`
89
+ * where the bind holds `(x, y, z)`, and quaternion tracks hold `(−x, −y, z, w)`, the conjugation
90
+ * of the same mirror. Played as authored, every animal faces backwards with its spine folded:
91
+ * head behind pelvis, healthy bone lengths, zero errors anywhere.
92
+ *
93
+ * An exporter writes exactly this when it converts a rig's bind to one convention and forgets its
94
+ * animation tracks. The loader repairs it before the game ever sees it: detection votes per
95
+ * tracked bone on whether the clip's translation Z sits negated against the bind, and only an
96
+ * overwhelming vote (at least `MIRROR_VOTE_MINIMUM` bones, `MIRROR_VOTE_SHARE` of them) converts
97
+ * every track — positions negate Z, quaternions negate X and Y, once, across all clips. A file
98
+ * that does not carry the signature is left byte-identical.
99
+ */
100
+ declare function reconcileMirroredClips(root: Object3D, clips: readonly AnimationClip[]): boolean;
101
+ declare function createAssetLoader(options?: IAssetLoaderOptions): IAssetLoader;
102
+
103
+ export { type IAssetLoader as I, type IAssetLoaderOptions as a, createAssetLoader as c, reconcileMirroredClips as r };
@@ -0,0 +1,156 @@
1
+ import { AudioListener, Object3D, Audio, Vector3, PositionalAudio } from 'three';
2
+
3
+ interface IAudioBusOptions {
4
+ readonly camera: Object3D;
5
+ readonly gestureTarget?: EventTarget;
6
+ readonly listener?: AudioListener;
7
+ readonly source?: () => EventTarget | undefined;
8
+ /**
9
+ * Ceiling on simultaneously sounding one-shots. Past it the oldest voice is stopped and its
10
+ * slot reused: a firefight generates far more cues than a listener can resolve, and the newest
11
+ * event is always the one they need to hear. Looping voices from `music` are exempt.
12
+ * Defaults to 48.
13
+ */
14
+ readonly maxVoices?: number;
15
+ }
16
+ interface IAudioPlayOptions {
17
+ readonly fade?: number;
18
+ readonly loop?: boolean;
19
+ readonly volume?: number;
20
+ /**
21
+ * Metres from the source where positional attenuation begins; `playAt` only. Three's panner
22
+ * default of 1 m makes a shot 20 m away all but inaudible — raise this to keep mid-distance
23
+ * sounds loud. Must be finite and positive.
24
+ */
25
+ readonly refDistance?: number;
26
+ /**
27
+ * How fast volume falls off past `refDistance`; `playAt` only. 0 keeps the sound at full
28
+ * volume at any distance. Must be finite and non-negative.
29
+ */
30
+ readonly rolloffFactor?: number;
31
+ /**
32
+ * Pitch offset in cents; ±100 is a semitone. Applied before the first sample rather than
33
+ * through three's `setDetune`, which ramps over ~30 ms and turns a percussive attack into an
34
+ * audible sweep. A few dozen cents of per-shot spread is what stops a repeated sample
35
+ * comb-filtering with its own copies into a metallic buzz.
36
+ */
37
+ readonly detune?: number;
38
+ /**
39
+ * Seconds of the buffer to pass before an exponential cut-off. A 1.4 s gunshot fired ten times
40
+ * a second stacks fourteen overlapping tails into mush; truncating all but the last keeps the
41
+ * transient and drops the wash. Must be finite and positive.
42
+ */
43
+ readonly cutoffSeconds?: number;
44
+ /**
45
+ * Low-pass corner in Hz. Air and geometry eat the top of a sound as it crosses a space, and a
46
+ * sample played flat at every range is the loudest tell that a game's audio is not in a place.
47
+ * Must be finite and positive.
48
+ */
49
+ readonly lowpassHz?: number;
50
+ }
51
+ interface IAudioRuntimeSnapshot {
52
+ readonly queued: number;
53
+ readonly voices: number;
54
+ /** Retired voices held for reuse. Bounded by peak concurrency, never by session length. */
55
+ readonly pooled: number;
56
+ /**
57
+ * Voices stopped mid-cue by `pause` and holding their position. Nonzero here while a game
58
+ * claims to be running is a menu that muted the world and forgot to give it back.
59
+ */
60
+ readonly paused: number;
61
+ /**
62
+ * Cue-shaping options this runtime accepted and could not honour, sorted and de-duplicated.
63
+ *
64
+ * The native host binds neither a biquad filter nor a schedulable `detune`, so `lowpassHz` and
65
+ * `detune` are silently dropped there. A mix tuned on the web and shipped to a phone is flat in
66
+ * a way nothing else reports, and a build can be failed on this.
67
+ */
68
+ readonly unsupported: readonly string[];
69
+ }
70
+ declare class AudioBus {
71
+ #private;
72
+ readonly listener: AudioListener;
73
+ constructor(options: IAudioBusOptions);
74
+ get queued(): number;
75
+ get voices(): number;
76
+ /**
77
+ * Retired voices held for reuse. This is the number that used to climb without limit: it is
78
+ * now bounded by how many cues have ever sounded at once, so a gate can pin it.
79
+ */
80
+ get pooled(): number;
81
+ /** Voices holding their position across a `pause`. */
82
+ get pausedVoices(): number;
83
+ /** True between `pause` and `resume`. */
84
+ get paused(): boolean;
85
+ /** The master volume last asked for — the target, not a point some ramp is passing through. */
86
+ get volume(): number;
87
+ /** @see IAudioRuntimeSnapshot.unsupported */
88
+ get unsupported(): readonly string[];
89
+ /**
90
+ * The whole bus's level, which is what a volume slider and a duck both move.
91
+ *
92
+ * One bus per category — ambience, effects, music — makes this the mixer: ducking the wood
93
+ * under a discovery cue is `ambience.setVolume(0.35, 0.4)` and `setVolume(1, 0.8)` after. It
94
+ * rides the listener's gain, so it costs nothing per voice and applies to cues already sounding.
95
+ *
96
+ * A bus constructed with a shared `listener` shares that listener's master with every other bus
97
+ * on it; give each category its own bus (the default) to mix them apart.
98
+ *
99
+ * @param volume Linear gain, 0 or greater.
100
+ * @param fade Seconds to reach it. Defaults to a 15 ms settle, which is a level change rather
101
+ * than a fade — the shortest move that does not click.
102
+ */
103
+ setVolume(volume: number, fade?: number): void;
104
+ /**
105
+ * Stop every sounding voice where it stands and hold its position.
106
+ *
107
+ * The difference from `stop` is what happens next: a paused bed resumes mid-bar, a stopped one
108
+ * starts over. Cues asked for while paused stay queued and sound on `resume`, so a menu opened
109
+ * during a burst does not fire the backlog at the player when they close it.
110
+ *
111
+ * The audio context keeps running — suspending it would silence every other bus sharing it.
112
+ */
113
+ pause(): void;
114
+ /** Sound the held voices again from where they stopped, then release anything queued. */
115
+ resume(): void;
116
+ setCamera(camera: Object3D): void;
117
+ reparent(camera: Object3D): void;
118
+ unlock(): Promise<void>;
119
+ /**
120
+ * A cue with no place: the listener's own weapon, UI, narration.
121
+ *
122
+ * The returned voice is valid for as long as it is sounding. Once it ends the bus reclaims it
123
+ * and may hand the same object to a later cue, so a caller holding the reference past that
124
+ * point is addressing somebody else's sound. Read `isPlaying` before touching a voice you kept.
125
+ */
126
+ play(buffer: AudioBuffer, options?: IAudioPlayOptions): Audio;
127
+ /**
128
+ * A cue somewhere in the world, at a fixed point or riding a moving object.
129
+ *
130
+ * A `Vector3` source is read in the coordinate space of the camera's parent, which is the
131
+ * scene in the ordinary case. Pass an `Object3D` to weld the cue to something that moves.
132
+ *
133
+ * The same reclaim rule as `play` applies to the returned voice.
134
+ */
135
+ playAt(buffer: AudioBuffer, source: Object3D | Vector3, options?: IAudioPlayOptions): PositionalAudio;
136
+ music(buffer: AudioBuffer, options?: IAudioPlayOptions): Audio;
137
+ /**
138
+ * Stop one voice and give it back to the pool.
139
+ *
140
+ * The missing primitive: `stop()` silenced every voice or none, and stopping the handle
141
+ * directly does not work — three's `Audio.stop()` detaches `onended` before stopping the node,
142
+ * so the bus's reclaim hook never fires and the voice leaks out of the pool while staying in
143
+ * the scene. A game that wants one entity's loop to end needs exactly this, and `playAt` with
144
+ * `loop` is the obvious way to reach for it.
145
+ *
146
+ * Returns false for a voice this bus is not sounding — a stale handle from a voice already
147
+ * recycled, or one belonging to another bus. Reported rather than thrown: a caller stopping a
148
+ * sound that already ended has made no mistake.
149
+ */
150
+ stopVoice(voice: Audio<AudioNode>): boolean;
151
+ stop(): void;
152
+ dispose(): void;
153
+ }
154
+ declare function audioRuntimeSnapshot(): IAudioRuntimeSnapshot;
155
+
156
+ export { AudioBus as A, type IAudioBusOptions as I, audioRuntimeSnapshot as a, type IAudioPlayOptions as b };
@@ -0,0 +1,62 @@
1
+ import * as three from 'three';
2
+ import { Camera, Vector2, Vector3, Scene, OrthographicCamera } from 'three';
3
+ import { I as IRendererLike } from './renderer-C6hqZpoG.js';
4
+
5
+ interface IViewportSize {
6
+ readonly aspect: number;
7
+ readonly height: number;
8
+ readonly width: number;
9
+ }
10
+ interface IViewportInsets {
11
+ readonly bottom: number;
12
+ readonly left: number;
13
+ readonly right: number;
14
+ readonly top: number;
15
+ }
16
+ interface IViewportSafeArea extends IViewportInsets {
17
+ /** The safe rectangle in drawable pixel coordinates, with y measured from the top edge. */
18
+ readonly height: number;
19
+ readonly source: "full-drawable-fallback" | "measured";
20
+ readonly width: number;
21
+ readonly x: number;
22
+ readonly y: number;
23
+ }
24
+ interface IViewportOptions {
25
+ readonly camera: Camera;
26
+ readonly renderer: IRendererLike;
27
+ readonly source?: IViewportPlatformSource;
28
+ }
29
+ type ViewportResizeHandler = (size: IViewportSize) => void;
30
+ interface IViewportPlatformSource {
31
+ observeResize(canvas: HTMLCanvasElement, resize: () => void): () => void;
32
+ readSize(canvas: HTMLCanvasElement): IViewportSize;
33
+ readSafeArea?(canvas: HTMLCanvasElement, size: IViewportSize): IViewportInsets | undefined;
34
+ }
35
+ declare class Viewport {
36
+ #private;
37
+ readonly camera: Camera;
38
+ readonly renderer: IRendererLike;
39
+ constructor(options: IViewportOptions);
40
+ get size(): IViewportSize;
41
+ get safeArea(): IViewportSafeArea;
42
+ projectPosition(screen: Vector2, z?: number, target?: Vector3): Vector3;
43
+ unprojectPosition(world: Vector3, target?: Vector2): Vector2;
44
+ onResize(handler: ViewportResizeHandler): () => void;
45
+ resize(): void;
46
+ dispose(): void;
47
+ }
48
+
49
+ /** A Godot-shaped render surface that is independent of the world camera and post pipeline. */
50
+ declare class CanvasLayer {
51
+ #private;
52
+ readonly scene: Scene<three.Object3DEventMap>;
53
+ readonly camera: OrthographicCamera;
54
+ /** Declares that this layer covers the framebuffer, allowing the world pass to be skipped. */
55
+ opaque: boolean;
56
+ constructor(viewport: Pick<Viewport, "onResize" | "size">);
57
+ dispose(): void;
58
+ /** Subscribe to drawable-size changes after this layer has updated its camera. */
59
+ onResize(handler: (size: IViewportSize) => void): () => void;
60
+ }
61
+
62
+ export { CanvasLayer as C, type IViewportOptions as I, Viewport as V };