@drawcall/lod 0.0.0-stage → 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.
Files changed (175) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +83 -2
  3. package/dist/baked/codec.d.ts +10 -0
  4. package/dist/baked/codec.js +291 -0
  5. package/dist/baked/codec.js.map +1 -0
  6. package/dist/baked/index.d.ts +51 -0
  7. package/dist/baked/index.js +183 -0
  8. package/dist/baked/index.js.map +1 -0
  9. package/dist/baked/interior/faces.d.ts +26 -0
  10. package/dist/baked/interior/faces.js +105 -0
  11. package/dist/baked/interior/faces.js.map +1 -0
  12. package/dist/baked/interior/material.d.ts +31 -0
  13. package/dist/baked/interior/material.js +157 -0
  14. package/dist/baked/interior/material.js.map +1 -0
  15. package/dist/baked/interior/rooms.d.ts +22 -0
  16. package/dist/baked/interior/rooms.js +78 -0
  17. package/dist/baked/interior/rooms.js.map +1 -0
  18. package/dist/baked/low.d.ts +20 -0
  19. package/dist/baked/low.js +75 -0
  20. package/dist/baked/low.js.map +1 -0
  21. package/dist/baked/raster.d.ts +20 -0
  22. package/dist/baked/raster.js +71 -0
  23. package/dist/baked/raster.js.map +1 -0
  24. package/dist/baked/transfer.d.ts +36 -0
  25. package/dist/baked/transfer.js +243 -0
  26. package/dist/baked/transfer.js.map +1 -0
  27. package/dist/baked/unwrap.d.ts +9 -0
  28. package/dist/baked/unwrap.js +167 -0
  29. package/dist/baked/unwrap.js.map +1 -0
  30. package/dist/baked/xatlas.d.ts +2 -0
  31. package/dist/baked/xatlas.js +31 -0
  32. package/dist/baked/xatlas.js.map +1 -0
  33. package/dist/bounds.d.ts +33 -0
  34. package/dist/bounds.js +127 -0
  35. package/dist/bounds.js.map +1 -0
  36. package/dist/dispose.d.ts +5 -0
  37. package/dist/dispose.js +28 -0
  38. package/dist/dispose.js.map +1 -0
  39. package/dist/fade.d.ts +44 -0
  40. package/dist/fade.js +78 -0
  41. package/dist/fade.js.map +1 -0
  42. package/dist/impostor/bake/atlas.d.ts +51 -0
  43. package/dist/impostor/bake/atlas.js +190 -0
  44. package/dist/impostor/bake/atlas.js.map +1 -0
  45. package/dist/impostor/bake/index.d.ts +50 -0
  46. package/dist/impostor/bake/index.js +221 -0
  47. package/dist/impostor/bake/index.js.map +1 -0
  48. package/dist/impostor/bake/materials.d.ts +29 -0
  49. package/dist/impostor/bake/materials.js +156 -0
  50. package/dist/impostor/bake/materials.js.map +1 -0
  51. package/dist/impostor/bake/source.d.ts +3 -0
  52. package/dist/impostor/bake/source.js +23 -0
  53. package/dist/impostor/bake/source.js.map +1 -0
  54. package/dist/impostor/cache.d.ts +15 -0
  55. package/dist/impostor/cache.js +44 -0
  56. package/dist/impostor/cache.js.map +1 -0
  57. package/dist/impostor/codec.d.ts +14 -0
  58. package/dist/impostor/codec.js +137 -0
  59. package/dist/impostor/codec.js.map +1 -0
  60. package/dist/impostor/impostor.d.ts +67 -0
  61. package/dist/impostor/impostor.js +79 -0
  62. package/dist/impostor/impostor.js.map +1 -0
  63. package/dist/impostor/mesh/card.d.ts +38 -0
  64. package/dist/impostor/mesh/card.js +79 -0
  65. package/dist/impostor/mesh/card.js.map +1 -0
  66. package/dist/impostor/mesh/index.d.ts +66 -0
  67. package/dist/impostor/mesh/index.js +174 -0
  68. package/dist/impostor/mesh/index.js.map +1 -0
  69. package/dist/impostor/mesh/material.d.ts +63 -0
  70. package/dist/impostor/mesh/material.js +199 -0
  71. package/dist/impostor/mesh/material.js.map +1 -0
  72. package/dist/impostor/mesh/surface.d.ts +63 -0
  73. package/dist/impostor/mesh/surface.js +162 -0
  74. package/dist/impostor/mesh/surface.js.map +1 -0
  75. package/dist/impostor/mesh/textures.d.ts +4 -0
  76. package/dist/impostor/mesh/textures.js +37 -0
  77. package/dist/impostor/mesh/textures.js.map +1 -0
  78. package/dist/impostor/octahedral.d.ts +29 -0
  79. package/dist/impostor/octahedral.js +83 -0
  80. package/dist/impostor/octahedral.js.map +1 -0
  81. package/dist/index.d.ts +15 -0
  82. package/dist/index.js +16 -0
  83. package/dist/index.js.map +1 -0
  84. package/dist/instanced/args.d.ts +20 -0
  85. package/dist/instanced/args.js +77 -0
  86. package/dist/instanced/args.js.map +1 -0
  87. package/dist/instanced/bounds.d.ts +50 -0
  88. package/dist/instanced/bounds.js +271 -0
  89. package/dist/instanced/bounds.js.map +1 -0
  90. package/dist/instanced/dirty.d.ts +12 -0
  91. package/dist/instanced/dirty.js +61 -0
  92. package/dist/instanced/dirty.js.map +1 -0
  93. package/dist/instanced/index.d.ts +116 -0
  94. package/dist/instanced/index.js +573 -0
  95. package/dist/instanced/index.js.map +1 -0
  96. package/dist/instanced/meshes.d.ts +14 -0
  97. package/dist/instanced/meshes.js +54 -0
  98. package/dist/instanced/meshes.js.map +1 -0
  99. package/dist/instanced/select.d.ts +84 -0
  100. package/dist/instanced/select.js +264 -0
  101. package/dist/instanced/select.js.map +1 -0
  102. package/dist/lazy.d.ts +64 -0
  103. package/dist/lazy.js +187 -0
  104. package/dist/lazy.js.map +1 -0
  105. package/dist/levels.d.ts +72 -0
  106. package/dist/levels.js +146 -0
  107. package/dist/levels.js.map +1 -0
  108. package/dist/loader/cache.d.ts +15 -0
  109. package/dist/loader/cache.js +37 -0
  110. package/dist/loader/cache.js.map +1 -0
  111. package/dist/loader/dispose.d.ts +3 -0
  112. package/dist/loader/dispose.js +25 -0
  113. package/dist/loader/dispose.js.map +1 -0
  114. package/dist/loader.d.ts +99 -0
  115. package/dist/loader.js +234 -0
  116. package/dist/loader.js.map +1 -0
  117. package/dist/lod.d.ts +78 -0
  118. package/dist/lod.js +211 -0
  119. package/dist/lod.js.map +1 -0
  120. package/dist/material.d.ts +12 -0
  121. package/dist/material.js +52 -0
  122. package/dist/material.js.map +1 -0
  123. package/dist/pause.d.ts +6 -0
  124. package/dist/pause.js +20 -0
  125. package/dist/pause.js.map +1 -0
  126. package/dist/renderer.d.ts +21 -0
  127. package/dist/renderer.js +83 -0
  128. package/dist/renderer.js.map +1 -0
  129. package/dist/simplify.d.ts +24 -0
  130. package/dist/simplify.js +148 -0
  131. package/dist/simplify.js.map +1 -0
  132. package/dist/source/error.d.ts +14 -0
  133. package/dist/source/error.js +94 -0
  134. package/dist/source/error.js.map +1 -0
  135. package/dist/source/frame.d.ts +11 -0
  136. package/dist/source/frame.js +25 -0
  137. package/dist/source/frame.js.map +1 -0
  138. package/dist/source/shading.d.ts +96 -0
  139. package/dist/source/shading.js +224 -0
  140. package/dist/source/shading.js.map +1 -0
  141. package/dist/source/source.d.ts +23 -0
  142. package/dist/source/source.js +50 -0
  143. package/dist/source/source.js.map +1 -0
  144. package/dist/source/surface.d.ts +31 -0
  145. package/dist/source/surface.js +110 -0
  146. package/dist/source/surface.js.map +1 -0
  147. package/dist/source/texels.d.ts +45 -0
  148. package/dist/source/texels.js +191 -0
  149. package/dist/source/texels.js.map +1 -0
  150. package/dist/source/trace.d.ts +42 -0
  151. package/dist/source/trace.js +153 -0
  152. package/dist/source/trace.js.map +1 -0
  153. package/dist/three.d.ts +55 -0
  154. package/dist/three.js +103 -0
  155. package/dist/three.js.map +1 -0
  156. package/dist/tsl.d.ts +45 -0
  157. package/dist/tsl.js +99 -0
  158. package/dist/tsl.js.map +1 -0
  159. package/dist/view.d.ts +30 -0
  160. package/dist/view.js +75 -0
  161. package/dist/view.js.map +1 -0
  162. package/dist/wind/bounds.d.ts +3 -0
  163. package/dist/wind/bounds.js +47 -0
  164. package/dist/wind/bounds.js.map +1 -0
  165. package/dist/wind/hooks.d.ts +5 -0
  166. package/dist/wind/hooks.js +3 -0
  167. package/dist/wind/hooks.js.map +1 -0
  168. package/dist/wind/index.d.ts +57 -0
  169. package/dist/wind/index.js +196 -0
  170. package/dist/wind/index.js.map +1 -0
  171. package/dist/wind/sway.d.ts +29 -0
  172. package/dist/wind/sway.js +71 -0
  173. package/dist/wind/sway.js.map +1 -0
  174. package/package.json +69 -3
  175. package/skills/lod/SKILL.md +137 -0
package/dist/loader.js ADDED
@@ -0,0 +1,234 @@
1
+ import { Loader, LoaderUtils } from "three/webgpu";
2
+ import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
3
+ import { getGLTSSource } from "@drawcall/glts";
4
+ import { bakeMesh } from "./baked/index.js";
5
+ import { measureLevels } from "./bounds.js";
6
+ import { disposeTree } from "./dispose.js";
7
+ import { ImpostorMesh } from "./impostor/mesh/index.js";
8
+ import { InstancedLod } from "./instanced/index.js";
9
+ import { estimate, Level, validate } from "./levels.js";
10
+ import { Lod } from "./lod.js";
11
+ import { LazyImpostor } from "./lazy.js";
12
+ import { impostor, originOf } from "./loader/cache.js";
13
+ import { disposeSimplified } from "./loader/dispose.js";
14
+ import { simplify } from "./simplify.js";
15
+ import { isLOD, isObject3D, placeLike, rejectSkinned } from "./three.js";
16
+ const DEFAULT_LEVELS = [{ type: "impostor" }];
17
+ /**
18
+ * Loads a model and builds a `Lod` of it: the model as level 0, then the requested
19
+ * levels (default: an impostor). Unless explicitly lazy, building runs at load time; impostors bake on a
20
+ * renderer this package keeps for that. Each level's maxPixels is its default (see
21
+ * `LodLevel.maxPixels`) unless the request sets it; a failed load frees what it built.
22
+ *
23
+ * Models load through the manager's handler for the URL if one is registered: a
24
+ * GLTFLoader with Draco or meshopt decoders, or for .glts a GLTSLoader from
25
+ * @drawcall/glts (inside a .glts script, the contextual `gltsLoader`, registered on the
26
+ * contextual `loadingManager`). Without one, .glb/.gltf load with a plain GLTFLoader and
27
+ * .glts fails. URLs resolve against `path` as GLTFLoader's do; a baked level's `low` and
28
+ * `interior` URLs load the same way, relative to the model's URL.
29
+ */
30
+ export class LodLoader extends Loader {
31
+ cacheOptions;
32
+ gltf;
33
+ constructor(manager, cacheOptions) {
34
+ super(manager);
35
+ this.cacheOptions = cacheOptions;
36
+ }
37
+ load(request, onLoad, _onProgress, onError) {
38
+ // As three's loaders: without onError, the failure goes to the console.
39
+ this.loadAsync(request).then(onLoad, onError ?? ((error) => console.error(error)));
40
+ }
41
+ async loadAsync(request) {
42
+ return this.loading(request, (object, levels, base, origin) => this.build(object, levels, base, (objects) => new Lod(objects), origin));
43
+ }
44
+ /** Load `request` as `count` instances: an `InstancedLod`. */
45
+ async loadInstancesAsync(request, count) {
46
+ return this.loading(request, (object, levels, base, origin) => this.build(object, levels, base, (objects) => new InstancedLod(objects, count), origin));
47
+ }
48
+ /** Build a `Lod` of the complete `object` inside GLTS. Relative low/interior URLs use the script's directory. */
49
+ async parseAsync(object, levels = DEFAULT_LEVELS) {
50
+ return this.loadAsync({ source: object, levels });
51
+ }
52
+ /** Build an `InstancedLod` of the complete `object` inside GLTS. */
53
+ async parseInstancesAsync(object, count, levels = DEFAULT_LEVELS) {
54
+ return this.loadInstancesAsync({ source: object, levels }, count);
55
+ }
56
+ /**
57
+ * `make` of `object` and its requested levels, with the maxPixels requested; `low` and
58
+ * `interior` URLs resolve against `base`. On failure, the levels built and interiors
59
+ * loaded are freed and `object` is left out of any Lod.
60
+ */
61
+ async build(object, requests, base, make, origin) {
62
+ rejectSkinned(object, "LodLoader");
63
+ if (requests.filter((request) => request.type === "baked" && request.interior !== undefined).length > 1)
64
+ throw new Error("LodLoader: give an interior to one baked level only: the source shows one");
65
+ const built = [];
66
+ const interiors = [];
67
+ try {
68
+ // One at a time: bakes share one renderer.
69
+ for (const request of requests) {
70
+ const { level, interior } = await this.buildLevel(object, request, base, origin, interiors);
71
+ built.push(level);
72
+ if (interior)
73
+ interiors.push(interior);
74
+ }
75
+ // Eager bakes have finished; deferred bakes exclude these attached children.
76
+ if (interiors.length > 0)
77
+ object.add(...interiors);
78
+ const objects = [object, ...built];
79
+ // Reject bad overrides before a constructor hooks the caller's source or
80
+ // allocates a crowd's pooled indirect arguments and selection buffers.
81
+ const preview = [];
82
+ objects.forEach((object, i) => preview.push(new Level(object, i, isLOD(object), preview)));
83
+ estimate(preview, 2 * measureLevels(objects, "LodLoader").own.radius);
84
+ requests.forEach(({ maxPixels }, i) => {
85
+ const level = preview[i + 1];
86
+ if (level && maxPixels !== undefined)
87
+ level.maxPixels = maxPixels;
88
+ });
89
+ validate(preview);
90
+ const lod = make(objects);
91
+ for (const [i, { maxPixels }] of requests.entries()) {
92
+ const level = lod.levels[i + 1];
93
+ if (!level)
94
+ throw new Error("LodLoader: a built level is missing");
95
+ if (maxPixels !== undefined)
96
+ level.maxPixels = maxPixels;
97
+ }
98
+ return lod;
99
+ }
100
+ catch (error) {
101
+ built.forEach((level, i) => {
102
+ if (requests[i]?.type === "simplified")
103
+ disposeSimplified(level, object);
104
+ else
105
+ disposeTree(level);
106
+ });
107
+ for (const interior of interiors) {
108
+ object.remove(interior);
109
+ disposeTree(interior);
110
+ }
111
+ throw error;
112
+ }
113
+ }
114
+ /** The level `request` asks for, and the interior a baked level loaded for the source. */
115
+ async buildLevel(source, request, base, origin, interiors = []) {
116
+ switch (request.type) {
117
+ case "simplified": {
118
+ const { type: _, maxPixels: __, ...options } = request;
119
+ return { level: await simplify(source, options) };
120
+ }
121
+ case "baked":
122
+ return this.bake(source, request, base);
123
+ case "impostor": {
124
+ const { type: _, lazy, ...options } = request;
125
+ if (lazy) {
126
+ const { maxPixels } = options;
127
+ if (maxPixels === undefined || !Number.isFinite(maxPixels) || maxPixels <= 0)
128
+ throw new Error("LodLoader: a lazy impostor needs an explicit positive maxPixels");
129
+ const level = new LazyImpostor(() => impostor(source, options, origin, interiors, this.cacheOptions), { maxPixels });
130
+ level.name = `${source.name || "object"} impostor`;
131
+ placeLike(level, source);
132
+ return { level };
133
+ }
134
+ return { level: impostorOf(source, await impostor(source, options, origin, [], this.cacheOptions)) };
135
+ }
136
+ default: {
137
+ const unknown = request;
138
+ throw new Error(`LodLoader: unknown level type in ${JSON.stringify(unknown)}`);
139
+ }
140
+ }
141
+ }
142
+ /**
143
+ * Bake `source` as `request` asks, loading its low mesh and interior relative to `base`.
144
+ * The low mesh is freed after: only the bake keeps it. The interior is returned for the
145
+ * source, or freed if the bake fails.
146
+ */
147
+ async bake(source, request, base) {
148
+ const { textureSize } = request;
149
+ const load = (url) => (url === undefined ? undefined : this.loadSource(LoaderUtils.resolveURL(url, base)));
150
+ const low = await load(request.low);
151
+ let interior;
152
+ try {
153
+ interior = await load(request.interior);
154
+ const level = await bakeMesh(source, low ? { low, textureSize, interior } : { ratio: request.ratio, textureSize, interior });
155
+ return { level, interior };
156
+ }
157
+ catch (error) {
158
+ if (interior)
159
+ disposeTree(interior);
160
+ throw error;
161
+ }
162
+ finally {
163
+ if (low)
164
+ disposeTree(low);
165
+ }
166
+ }
167
+ /**
168
+ * Load `request`'s model and `build` it, reporting to the manager as one item; `build`
169
+ * gets the model's directory. On failure, the model is freed.
170
+ */
171
+ async loading(request, build) {
172
+ const input = typeof request === "string" ? { url: request } : isObject3D(request) ? { source: request } : request;
173
+ const { levels = DEFAULT_LEVELS } = input;
174
+ if ("source" in input) {
175
+ // Native async code loses the GLTS execution after its first await.
176
+ const source = getGLTSSource();
177
+ return build(input.source, levels, LoaderUtils.extractUrlBase(source.url), source);
178
+ }
179
+ const { url } = input;
180
+ const full = LoaderUtils.resolveURL(url, this.path);
181
+ this.manager.itemStart(full);
182
+ let object;
183
+ try {
184
+ object = await this.loadSource(full);
185
+ const origin = originOf(object);
186
+ return await build(object, levels, LoaderUtils.extractUrlBase(origin?.url ?? full), origin);
187
+ }
188
+ catch (error) {
189
+ if (object)
190
+ disposeTree(object);
191
+ this.manager.itemError(full);
192
+ throw error;
193
+ }
194
+ finally {
195
+ this.manager.itemEnd(full);
196
+ }
197
+ }
198
+ async loadSource(url) {
199
+ const extension = /\.(glb|gltf|glts)$/i.exec(url.split(/[?#]/)[0] ?? "")?.[1]?.toLowerCase();
200
+ if (!extension)
201
+ throw new Error(`LodLoader: cannot load "${url}": expected a .glb, .gltf or .glts file`);
202
+ const handler = this.manager.getHandler(url);
203
+ if (handler)
204
+ return sceneOf(await handler.loadAsync(url), url);
205
+ // No GLTSLoader of our own: inside a .glts script only the contextual one may load,
206
+ // so the app registers the one to use.
207
+ if (extension === "glts")
208
+ throw new Error(`LodLoader: cannot load "${url}": register a GLTSLoader for .glts on the loading manager`);
209
+ this.gltf ??= new GLTFLoader(this.manager);
210
+ return (await this.configure(this.gltf).loadAsync(url)).scene;
211
+ }
212
+ /** `loader` with this loader's request settings. */
213
+ configure(loader) {
214
+ loader.setCrossOrigin(this.crossOrigin).setWithCredentials(this.withCredentials).setRequestHeader(this.requestHeader);
215
+ return loader;
216
+ }
217
+ }
218
+ /** An ImpostorMesh placed and shadowed like `source`. */
219
+ function impostorOf(source, impostor) {
220
+ const mesh = new ImpostorMesh(impostor);
221
+ mesh.name = `${source.name || "object"} impostor`;
222
+ placeLike(mesh, source);
223
+ return mesh;
224
+ }
225
+ /** The object a handler loaded: an Object3D, or a glTF result's scene. */
226
+ function sceneOf(result, url) {
227
+ if (isObject3D(result))
228
+ return result;
229
+ const scene = typeof result === "object" && result !== null ? Reflect.get(result, "scene") : undefined;
230
+ if (isObject3D(scene))
231
+ return scene;
232
+ throw new Error(`LodLoader: the handler for "${url}" did not load an Object3D or a glTF`);
233
+ }
234
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"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"}
package/dist/lod.d.ts ADDED
@@ -0,0 +1,78 @@
1
+ import { Object3D, type Camera, type Intersection, type Raycaster } from "three/webgpu";
2
+ import { type LevelsEventMap } from "./lazy.js";
3
+ import { type LodLevel } from "./levels.js";
4
+ import { WIND_BOUNDS } from "./wind/hooks.js";
5
+ /**
6
+ * Levels of detail of one object, switched by its height on screen. Level 0 is the
7
+ * source; each further level is used while the object is at most its `maxPixels` tall
8
+ * (see `LodLevel.maxPixels` for the defaults). Near a switch both neighbours draw and
9
+ * split the pixels with a dither, so nothing pops; elsewhere exactly one level is visible
10
+ * and the others are hidden. Shadows follow the camera's choice and fade with it; nothing
11
+ * of the object is drawn into a directional light's shadow map whose texels are wider
12
+ * than the whole object: it could cover a texel only by chance.
13
+ *
14
+ * three's renderer calls `update(camera)` while it lists the scene (as for its own LOD),
15
+ * before shadow maps render; the calls its shadow passes make are ignored. The height
16
+ * uses the viewport the camera last drew one of the levels' meshes in, in CSS pixels
17
+ * (1080 before that), so several views each get their own. An orthographic camera has
18
+ * no distance to dither by: levels switch by on-screen size there too, but at once.
19
+ *
20
+ * Levels are children of the Lod in its space, any Object3D: meshes, an ImpostorMesh, a
21
+ * group, another Lod. Each mesh draws with a faded copy of its material, which every Lod
22
+ * drawing that material shares. Lines, points and sprites are drawn but neither fade nor
23
+ * tell the Lod the viewport height. A nested Lod hands over to its neighbours at once.
24
+ * Raycasts hit the source only.
25
+ *
26
+ * Levels are read when the Lod is built: meshes added to a level later, or moved within
27
+ * it, are neither faded nor measured right. Build a new Lod instead.
28
+ */
29
+ export declare class Lod extends Object3D<LevelsEventMap> {
30
+ /** Multiplies every measured height: below 1, cheaper levels take over sooner. */
31
+ static quality: number;
32
+ /** Tint every level by its index, to see which level draws where. */
33
+ static debug: boolean;
34
+ /**
35
+ * Share of a level's range its fade spans: from `maxPixels × (1 - fade)` up to its
36
+ * `maxPixels`, the finer level draws too.
37
+ */
38
+ static readonly fade = 0.1;
39
+ readonly isLOD = true;
40
+ /** Let the renderer call `update` with its camera every frame. */
41
+ autoUpdate: boolean;
42
+ /** Draw only this level, without fades. */
43
+ forceLevel: number | null;
44
+ readonly levels: readonly LodLevel[];
45
+ private readonly entries;
46
+ /** Per level, how its meshes draw. */
47
+ private readonly draws;
48
+ /** Bounding sphere of the source (see the constructor) in the Lod's own space. */
49
+ private sphere;
50
+ private casterRadius;
51
+ private disposed;
52
+ private readonly installed;
53
+ private fallback;
54
+ /**
55
+ * `levels[0]` is the source. Empty only as the target of `copy`. A source that draws
56
+ * nothing (an empty Object3D, for detail another system draws) is measured by the
57
+ * first level that draws something.
58
+ */
59
+ constructor(levels?: Object3D[]);
60
+ private build;
61
+ /** Fade the meshes under `node` by `draw` and learn the viewport height from them; an inner Lod fades its own. */
62
+ private hook;
63
+ [WIND_BOUNDS](padding: number): void;
64
+ /** Pick and fade levels for `camera`; three's renderer calls this itself while `autoUpdate`. */
65
+ update(camera: Camera): void;
66
+ raycast(raycaster: Raycaster, intersects: Intersection[]): boolean;
67
+ /**
68
+ * Copies `source`'s levels into this empty Lod (it throws otherwise). The copies share
69
+ * geometry, materials and textures with `source`, as three's clone does; the Lod
70
+ * settings and each level's `maxPixels` come along.
71
+ */
72
+ copy(source: Lod): this;
73
+ /**
74
+ * Frees every level, the source included: geometries, materials and their textures.
75
+ * Clones share them, so dispose a Lod only once no clone draws any more.
76
+ */
77
+ dispose(): void;
78
+ }
package/dist/lod.js ADDED
@@ -0,0 +1,211 @@
1
+ import { Object3D, Sphere } from "three/webgpu";
2
+ import { measureLevels } from "./bounds.js";
3
+ import { disposeTree } from "./dispose.js";
4
+ import { setDraw } from "./fade.js";
5
+ import { levelMaterial } from "./material.js";
6
+ import { available, fallbacks, inView } from "./lazy.js";
7
+ import { FADE, Level, cloneLevels, copyThresholds, estimate, fadeProgress, levelAt, tintLevels, validate } from "./levels.js";
8
+ import { inShadowPass, isLazyImpostor, isImpostorMesh, isLOD, isMesh, rejectSkinned } from "./three.js";
9
+ import { TYPICAL_HEIGHT, View, viewportHeight, watchViewport } from "./view.js";
10
+ import { WIND_BOUNDS } from "./wind/hooks.js";
11
+ import { copyWind, refreshWind } from "./wind/index.js";
12
+ const view = new View();
13
+ /**
14
+ * Levels of detail of one object, switched by its height on screen. Level 0 is the
15
+ * source; each further level is used while the object is at most its `maxPixels` tall
16
+ * (see `LodLevel.maxPixels` for the defaults). Near a switch both neighbours draw and
17
+ * split the pixels with a dither, so nothing pops; elsewhere exactly one level is visible
18
+ * and the others are hidden. Shadows follow the camera's choice and fade with it; nothing
19
+ * of the object is drawn into a directional light's shadow map whose texels are wider
20
+ * than the whole object: it could cover a texel only by chance.
21
+ *
22
+ * three's renderer calls `update(camera)` while it lists the scene (as for its own LOD),
23
+ * before shadow maps render; the calls its shadow passes make are ignored. The height
24
+ * uses the viewport the camera last drew one of the levels' meshes in, in CSS pixels
25
+ * (1080 before that), so several views each get their own. An orthographic camera has
26
+ * no distance to dither by: levels switch by on-screen size there too, but at once.
27
+ *
28
+ * Levels are children of the Lod in its space, any Object3D: meshes, an ImpostorMesh, a
29
+ * group, another Lod. Each mesh draws with a faded copy of its material, which every Lod
30
+ * drawing that material shares. Lines, points and sprites are drawn but neither fade nor
31
+ * tell the Lod the viewport height. A nested Lod hands over to its neighbours at once.
32
+ * Raycasts hit the source only.
33
+ *
34
+ * Levels are read when the Lod is built: meshes added to a level later, or moved within
35
+ * it, are neither faded nor measured right. Build a new Lod instead.
36
+ */
37
+ export class Lod extends Object3D {
38
+ /** Multiplies every measured height: below 1, cheaper levels take over sooner. */
39
+ static quality = 1;
40
+ /** Tint every level by its index, to see which level draws where. */
41
+ static debug = false;
42
+ /**
43
+ * Share of a level's range its fade spans: from `maxPixels × (1 - fade)` up to its
44
+ * `maxPixels`, the finer level draws too.
45
+ */
46
+ static fade = FADE;
47
+ isLOD = true;
48
+ /** Let the renderer call `update` with its camera every frame. */
49
+ autoUpdate = true;
50
+ /** Draw only this level, without fades. */
51
+ forceLevel = null;
52
+ levels;
53
+ entries = [];
54
+ /** Per level, how its meshes draw. */
55
+ draws = [];
56
+ /** Bounding sphere of the source (see the constructor) in the Lod's own space. */
57
+ sphere = new Sphere();
58
+ casterRadius = 0;
59
+ disposed = false;
60
+ installed = new Set();
61
+ fallback = [];
62
+ /**
63
+ * `levels[0]` is the source. Empty only as the target of `copy`. A source that draws
64
+ * nothing (an empty Object3D, for detail another system draws) is measured by the
65
+ * first level that draws something.
66
+ */
67
+ constructor(levels = []) {
68
+ super();
69
+ this.levels = this.entries;
70
+ if (levels.length > 0)
71
+ this.build(levels);
72
+ }
73
+ build(objects) {
74
+ const [source] = objects;
75
+ if (!source)
76
+ throw new Error("Lod: needs at least the source level");
77
+ if (isLazyImpostor(source))
78
+ throw new Error("Lod: the source level must be eager");
79
+ for (const object of objects)
80
+ if (isLazyImpostor(object)) {
81
+ object.materialize();
82
+ }
83
+ for (const object of objects)
84
+ rejectSkinned(object, "Lod");
85
+ const { own, placed } = measureLevels(objects, "Lod");
86
+ this.sphere = placed;
87
+ this.casterRadius = placed.radius;
88
+ this.add(...objects);
89
+ objects.forEach((object, i) => {
90
+ const level = new Level(object, i, isLOD(object), this.entries);
91
+ const draw = { kind: "object", fade: 0, diameter: 0, tint: level.tint };
92
+ this.entries.push(level);
93
+ this.draws.push(draw);
94
+ this.hook(object, draw, level.nested, object);
95
+ if (available(object))
96
+ this.installed.add(object);
97
+ });
98
+ estimate(this.entries, 2 * own.radius);
99
+ this.fallback = fallbacks(objects);
100
+ validate(this.entries);
101
+ for (const object of objects)
102
+ if (isLazyImpostor(object))
103
+ object.retain();
104
+ }
105
+ /** Fade the meshes under `node` by `draw` and learn the viewport height from them; an inner Lod fades its own. */
106
+ hook(node, draw, nested, level) {
107
+ const inner = nested || (node !== level && isLOD(node));
108
+ if (isMesh(node)) {
109
+ if (!inner) {
110
+ setDraw(node, draw);
111
+ // An ImpostorMesh reads its draw itself.
112
+ if (!isImpostorMesh(node))
113
+ node.material = Array.isArray(node.material) ? node.material.map((m) => levelMaterial(m)) : levelMaterial(node.material);
114
+ }
115
+ watchViewport(node);
116
+ }
117
+ for (const child of node.children)
118
+ this.hook(child, draw, inner, level);
119
+ }
120
+ [WIND_BOUNDS](padding) {
121
+ this.casterRadius += padding;
122
+ }
123
+ /** Pick and fade levels for `camera`; three's renderer calls this itself while `autoUpdate`. */
124
+ update(camera) {
125
+ const { entries, draws } = this;
126
+ if (this.disposed || entries.length === 0 || inShadowPass(this))
127
+ return;
128
+ let changed = false;
129
+ if (this.installed.size < entries.length)
130
+ entries.forEach((entry, i) => {
131
+ if (!isLazyImpostor(entry.object) || this.installed.has(entry.object))
132
+ return;
133
+ entry.object.materialize();
134
+ if (!available(entry.object))
135
+ return;
136
+ const draw = draws[i];
137
+ if (!draw)
138
+ throw new Error("Lod: missing lazy draw");
139
+ this.hook(entry.object, draw, false, entry.object);
140
+ this.installed.add(entry.object);
141
+ changed = true;
142
+ });
143
+ if (changed) {
144
+ this.fallback = fallbacks(entries.map((entry) => entry.object));
145
+ refreshWind(this);
146
+ this.updateWorldMatrix(true, true);
147
+ this.dispatchEvent({ type: "levelschange" });
148
+ }
149
+ validate(entries);
150
+ view.begin(camera, viewportHeight(camera) ?? TYPICAL_HEIGHT, this.forceLevel);
151
+ tintLevels(entries, Lod.debug);
152
+ const height = view.pixels(this.matrixWorld, this.sphere) * Lod.quality;
153
+ const desired = levelAt(entries, height, this.forceLevel);
154
+ if (isLazyImpostor(desired.object) && desired.object.status === "idle" && inView(camera, this.matrixWorld, this.sphere))
155
+ desired.object.request();
156
+ const fallback = this.fallback;
157
+ const level = entries[fallback[desired.index] ?? 0];
158
+ let t = view.fades ? fadeProgress(entries, desired, height) : 0;
159
+ const neighbour = t > 0 ? entries[desired.index - 1]?.object : undefined;
160
+ if (neighbour && isLazyImpostor(neighbour) && neighbour.status === "idle" && inView(camera, this.matrixWorld, this.sphere))
161
+ neighbour.request();
162
+ const finer = t > 0 ? entries[fallback[desired.index - 1] ?? 0] : undefined;
163
+ if (level === finer)
164
+ t = 0;
165
+ const diameter = 2 * this.casterRadius * this.matrixWorld.getMaxScaleOnAxis();
166
+ entries.forEach((each, i) => {
167
+ const draw = draws[i];
168
+ if (!draw)
169
+ throw new Error("Lod: a level has no draw");
170
+ draw.fade = each === level ? t : each === finer ? -t : 0;
171
+ draw.diameter = diameter;
172
+ each.object.visible = each === level || (t > 0 && each === finer);
173
+ });
174
+ }
175
+ raycast(raycaster, intersects) {
176
+ const source = this.entries[0];
177
+ if (source)
178
+ raycaster.intersectObject(source.object, true, intersects);
179
+ // Stop the raycaster from descending into the other levels.
180
+ return false;
181
+ }
182
+ /**
183
+ * Copies `source`'s levels into this empty Lod (it throws otherwise). The copies share
184
+ * geometry, materials and textures with `source`, as three's clone does; the Lod
185
+ * settings and each level's `maxPixels` come along.
186
+ */
187
+ copy(source) {
188
+ if (this.entries.length > 0)
189
+ throw new Error("Lod.copy: copy into an empty Lod");
190
+ super.copy(source, false);
191
+ if (source.entries.length > 0)
192
+ this.build(cloneLevels(source.entries));
193
+ copyThresholds(source.entries, this.entries);
194
+ this.forceLevel = source.forceLevel;
195
+ this.autoUpdate = source.autoUpdate;
196
+ copyWind(source, this);
197
+ return this;
198
+ }
199
+ /**
200
+ * Frees every level, the source included: geometries, materials and their textures.
201
+ * Clones share them, so dispose a Lod only once no clone draws any more.
202
+ */
203
+ dispose() {
204
+ if (this.disposed)
205
+ return;
206
+ this.disposed = true;
207
+ for (const { object } of this.entries)
208
+ isLazyImpostor(object) ? object.release() : disposeTree(object);
209
+ }
210
+ }
211
+ //# sourceMappingURL=lod.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lod.js","sourceRoot":"","sources":["../src/lod.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { Material, NodeMaterial } from "three/webgpu";
2
+ /**
3
+ * The copy of `material` (or of the material it copies) a level of a Lod or
4
+ * InstancedLod draws with: it keeps only the pixels of its draw (see fade.ts), tints by
5
+ * its level, casts nothing into shadow maps too coarse for it and, `swaying`, sways in
6
+ * the wind of whatever object draws it (see Wind). Made once per original and kind, and
7
+ * shared by every level drawing it: what differs per object is read while it renders.
8
+ * Freed with the original.
9
+ */
10
+ export declare function levelMaterial(material: Material, swaying?: boolean): NodeMaterial;
11
+ /** The material a level copy was made from, or `material` itself. */
12
+ export declare const originalOf: (material: Material) => Material;
@@ -0,0 +1,52 @@
1
+ import { Fn, output, positionLocal, vec4 } from "three/tsl";
2
+ import { castPosition, keepsPixel, tint } from "./fade.js";
3
+ import { toNodeMaterial } from "./three.js";
4
+ import { isNodeOf } from "./tsl.js";
5
+ import { sway } from "./wind/sway.js";
6
+ /** Copy → the material it copies. */
7
+ const originals = new WeakMap();
8
+ /** Original → its copies, still and swaying. */
9
+ const copies = new WeakMap();
10
+ /** A slot the original set, as the type it builds to. */
11
+ function slot(node, type, builder) {
12
+ if (!isNodeOf(node, type, builder))
13
+ throw new Error(`@drawcall/lod: a material's ${type} slot builds to ${node.getNodeType(builder)}`);
14
+ return node;
15
+ }
16
+ /**
17
+ * The copy of `material` (or of the material it copies) a level of a Lod or
18
+ * InstancedLod draws with: it keeps only the pixels of its draw (see fade.ts), tints by
19
+ * its level, casts nothing into shadow maps too coarse for it and, `swaying`, sways in
20
+ * the wind of whatever object draws it (see Wind). Made once per original and kind, and
21
+ * shared by every level drawing it: what differs per object is read while it renders.
22
+ * Freed with the original.
23
+ */
24
+ export function levelMaterial(material, swaying = false) {
25
+ const original = originalOf(material);
26
+ let known = copies.get(original);
27
+ if (!known)
28
+ copies.set(original, (known = {}));
29
+ const kind = swaying ? "swaying" : "still";
30
+ const existing = known[kind];
31
+ if (existing)
32
+ return existing;
33
+ const copy = toNodeMaterial(original);
34
+ const { maskNode, outputNode, positionNode, castShadowPositionNode } = copy;
35
+ copy.maskNode = Fn((builder) => (maskNode ? slot(maskNode, "bool", builder).and(keepsPixel()) : keepsPixel()))();
36
+ // The tint goes on what the material outputs: its colour slot is left as it is.
37
+ copy.outputNode = Fn((builder) => (outputNode ? slot(outputNode, "vec4", builder) : output).mul(vec4(tint, 1)))();
38
+ const position = (builder) => {
39
+ const own = positionNode ? slot(positionNode, "vec3", builder) : positionLocal;
40
+ return swaying ? sway(own) : own;
41
+ };
42
+ if (swaying || positionNode)
43
+ copy.positionNode = Fn(position)();
44
+ copy.castShadowPositionNode = Fn((builder) => castPosition(castShadowPositionNode ? slot(castShadowPositionNode, "vec3", builder) : position(builder)))();
45
+ known[kind] = copy;
46
+ originals.set(copy, original);
47
+ original.addEventListener("dispose", () => copy.dispose());
48
+ return copy;
49
+ }
50
+ /** The material a level copy was made from, or `material` itself. */
51
+ export const originalOf = (material) => originals.get(material) ?? material;
52
+ //# sourceMappingURL=material.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"material.js","sourceRoot":"","sources":["../src/material.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Yield to the page, so that a long bake does not freeze it: `scheduler.yield()` where
3
+ * there is one, else a message round trip. Unlike setTimeout, neither is throttled to
4
+ * one call a second in a background tab.
5
+ */
6
+ export declare function pause(): Promise<void>;