@threenative/core 0.3.2 → 0.3.4

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.
@@ -1,4 +1,4 @@
1
- import { Texture, Object3D, AnimationClip } from 'three';
1
+ import { Texture, Wrapping, Object3D, AnimationClip } from 'three';
2
2
 
3
3
  interface IAssetLoaderOptions {
4
4
  readonly basePath?: string;
@@ -49,10 +49,41 @@ interface ICompressedTextureSupport {
49
49
  readonly loader: Promise<IKTX2LoaderLike | undefined>;
50
50
  readonly ready: Promise<void>;
51
51
  }
52
+ /** Which pipeline served a logical path: the compile step's manifest, or the project's own files. */
53
+ type AssetSource = "manifest" | "source";
54
+ /** Where one settled load's bytes actually came from. */
55
+ interface IResolvedAsset {
56
+ readonly url: string;
57
+ readonly via: AssetSource;
58
+ }
59
+ interface ITextureOptions {
60
+ /**
61
+ * The pixels are data, not colour: a normal map, a roughness map, a mask. Data textures are
62
+ * sampled without a colour-space conversion, which is wrong for an albedo and right for these.
63
+ * Left out, the copy is sRGB — the space an image file is authored in. A loader leaves a plain
64
+ * image linear, which washes out every albedo, so an options call never inherits that.
65
+ */
66
+ readonly data?: boolean;
67
+ /** Both axes at once. Absent leaves the loaded texture's own wrapping. */
68
+ readonly wrap?: Wrapping;
69
+ /** Tiling counts, one for both axes or one per axis. Absent leaves the loaded texture's own. */
70
+ readonly repeat?: number | readonly [number, number];
71
+ /** Samples to take at grazing angles. Absent leaves the loaded texture's own. */
72
+ readonly anisotropy?: number;
73
+ }
52
74
  interface IAssetLoader {
53
75
  readonly compressedTextures?: ICompressedTextureSupport;
54
76
  model<T = unknown>(path: string): Promise<T>;
55
- texture(path: string): Promise<Texture>;
77
+ /**
78
+ * Load a texture, optionally configured in one call.
79
+ *
80
+ * @situation set color space, wrap, repeat or anisotropy on a loaded texture
81
+ *
82
+ * With no options this is the shared cached instance, exactly as before. With options it is a
83
+ * copy of it: the cached texture is shared by every other caller of that path, and a wrap or a
84
+ * colour-space write on it would silently change how another material draws.
85
+ */
86
+ texture(path: string, options?: ITextureOptions): Promise<Texture>;
56
87
  audio(path: string): Promise<AudioBuffer>;
57
88
  release(kind: "audio" | "model" | "texture", path: string): boolean;
58
89
  /**
@@ -74,11 +105,29 @@ interface IAssetLoader {
74
105
  * stay 0 for a game with no manifest, where no size is knowable before the bytes arrive.
75
106
  */
76
107
  readonly progress: {
108
+ /**
109
+ * The logical paths asked for and not yet settled, in request order. A loading screen that
110
+ * only shows a ratio cannot say *what* it is waiting for, which is the difference between
111
+ * "still loading" and "stuck on `akagi.glb`" — and a stall report that names nothing is a
112
+ * bug report nobody can act on.
113
+ */
114
+ readonly pending: readonly string[];
77
115
  readonly requested: number;
78
116
  readonly requestedBytes: number;
79
117
  readonly settled: number;
80
118
  readonly settledBytes: number;
81
119
  };
120
+ /**
121
+ * Where each settled load was actually served from, keyed by the logical path asked for.
122
+ *
123
+ * `progress` counts loads and cannot say which of a path's candidate urls answered, so a game
124
+ * whose manifest 404s and a game whose manifest named the output look identical from the game's
125
+ * own side — which is how an unreadable manifest became a silent uncompiled fallback on the
126
+ * native hosts, where a failed read arrives as a rejected fetch rather than a 404. `via` is
127
+ * `"manifest"` only for the compiled output the manifest named; `"source"` is the verbatim or
128
+ * source-directory path, and an external url, which no manifest governs.
129
+ */
130
+ readonly resolved: ReadonlyMap<string, IResolvedAsset>;
82
131
  clear(): void;
83
132
  }
84
133
  /**
@@ -100,4 +149,4 @@ interface IAssetLoader {
100
149
  declare function reconcileMirroredClips(root: Object3D, clips: readonly AnimationClip[]): boolean;
101
150
  declare function createAssetLoader(options?: IAssetLoaderOptions): IAssetLoader;
102
151
 
103
- export { type IAssetLoader as I, type IAssetLoaderOptions as a, createAssetLoader as c, reconcileMirroredClips as r };
152
+ export { type IAssetLoader as I, type IAssetLoaderOptions as a, type ITextureOptions as b, createAssetLoader as c, reconcileMirroredClips as r };
@@ -14,6 +14,15 @@ interface IAudioBusOptions {
14
14
  readonly maxVoices?: number;
15
15
  }
16
16
  interface IAudioPlayOptions {
17
+ /**
18
+ * What this cue is, for anything reading back what the game played — a playtest above all.
19
+ *
20
+ * Nothing about the sound changes. Every other audio check answers "is the file right": that it
21
+ * exists, decodes, and is inside its byte budget. None of them can answer "did the game say the
22
+ * general-quarters line twice", which is the class of defect players actually report, so the bus
23
+ * keeps a ledger of the labels it was given.
24
+ */
25
+ readonly cue?: string;
17
26
  readonly fade?: number;
18
27
  readonly loop?: boolean;
19
28
  readonly volume?: number;
@@ -49,6 +58,21 @@ interface IAudioPlayOptions {
49
58
  readonly lowpassHz?: number;
50
59
  }
51
60
  interface IAudioRuntimeSnapshot {
61
+ /**
62
+ * How many times each labelled cue has sounded, across every live bus.
63
+ *
64
+ * This is the only observation that can answer "what did the game actually say, and how often".
65
+ * Every other audio check in the repository is about the *file* — that it exists, decodes and is
66
+ * inside its budget — and all of them stay green while a one-shot line plays a second time
67
+ * halfway through a match, which is the defect players report. A game opts a cue in by passing
68
+ * `cue` to `play`/`playAt`; unlabelled sounds never appear here.
69
+ */
70
+ readonly cues: Readonly<Record<string, number>>;
71
+ /** The most recent labelled cues in the order they sounded, bounded per bus. */
72
+ readonly recentCues: ReadonlyArray<{
73
+ readonly atMs: number;
74
+ readonly cue: string;
75
+ }>;
52
76
  readonly queued: number;
53
77
  readonly voices: number;
54
78
  /** Retired voices held for reuse. Bounded by peak concurrency, never by session length. */
@@ -67,6 +91,13 @@ interface IAudioRuntimeSnapshot {
67
91
  */
68
92
  readonly unsupported: readonly string[];
69
93
  }
94
+ /**
95
+ * Forgets every recorded cue.
96
+ *
97
+ * @situation clear the recorded audio cue counts between tests so one test cannot read another's plays
98
+ * @example resetAudioCueLedger();
99
+ */
100
+ declare function resetAudioCueLedger(): void;
70
101
  declare class AudioBus {
71
102
  #private;
72
103
  readonly listener: AudioListener;
@@ -153,4 +184,4 @@ declare class AudioBus {
153
184
  }
154
185
  declare function audioRuntimeSnapshot(): IAudioRuntimeSnapshot;
155
186
 
156
- export { AudioBus as A, type IAudioBusOptions as I, audioRuntimeSnapshot as a, type IAudioPlayOptions as b };
187
+ export { AudioBus as A, type IAudioBusOptions as I, audioRuntimeSnapshot as a, type IAudioPlayOptions as b, resetAudioCueLedger as r };
@@ -1,6 +1,6 @@
1
1
  import * as three from 'three';
2
2
  import { Camera, Vector2, Vector3, Scene, OrthographicCamera } from 'three';
3
- import { I as IRendererLike } from './renderer-C6hqZpoG.js';
3
+ import { I as IRendererLike } from './renderer-CfsS2hxi.js';
4
4
 
5
5
  interface IViewportSize {
6
6
  readonly aspect: number;