@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.
- package/LICENSE +21 -0
- package/README.md +83 -2
- package/dist/baked/codec.d.ts +10 -0
- package/dist/baked/codec.js +291 -0
- package/dist/baked/codec.js.map +1 -0
- package/dist/baked/index.d.ts +51 -0
- package/dist/baked/index.js +183 -0
- package/dist/baked/index.js.map +1 -0
- package/dist/baked/interior/faces.d.ts +26 -0
- package/dist/baked/interior/faces.js +105 -0
- package/dist/baked/interior/faces.js.map +1 -0
- package/dist/baked/interior/material.d.ts +31 -0
- package/dist/baked/interior/material.js +157 -0
- package/dist/baked/interior/material.js.map +1 -0
- package/dist/baked/interior/rooms.d.ts +22 -0
- package/dist/baked/interior/rooms.js +78 -0
- package/dist/baked/interior/rooms.js.map +1 -0
- package/dist/baked/low.d.ts +20 -0
- package/dist/baked/low.js +75 -0
- package/dist/baked/low.js.map +1 -0
- package/dist/baked/raster.d.ts +20 -0
- package/dist/baked/raster.js +71 -0
- package/dist/baked/raster.js.map +1 -0
- package/dist/baked/transfer.d.ts +36 -0
- package/dist/baked/transfer.js +243 -0
- package/dist/baked/transfer.js.map +1 -0
- package/dist/baked/unwrap.d.ts +9 -0
- package/dist/baked/unwrap.js +167 -0
- package/dist/baked/unwrap.js.map +1 -0
- package/dist/baked/xatlas.d.ts +2 -0
- package/dist/baked/xatlas.js +31 -0
- package/dist/baked/xatlas.js.map +1 -0
- package/dist/bounds.d.ts +33 -0
- package/dist/bounds.js +127 -0
- package/dist/bounds.js.map +1 -0
- package/dist/dispose.d.ts +5 -0
- package/dist/dispose.js +28 -0
- package/dist/dispose.js.map +1 -0
- package/dist/fade.d.ts +44 -0
- package/dist/fade.js +78 -0
- package/dist/fade.js.map +1 -0
- package/dist/impostor/bake/atlas.d.ts +51 -0
- package/dist/impostor/bake/atlas.js +190 -0
- package/dist/impostor/bake/atlas.js.map +1 -0
- package/dist/impostor/bake/index.d.ts +50 -0
- package/dist/impostor/bake/index.js +221 -0
- package/dist/impostor/bake/index.js.map +1 -0
- package/dist/impostor/bake/materials.d.ts +29 -0
- package/dist/impostor/bake/materials.js +156 -0
- package/dist/impostor/bake/materials.js.map +1 -0
- package/dist/impostor/bake/source.d.ts +3 -0
- package/dist/impostor/bake/source.js +23 -0
- package/dist/impostor/bake/source.js.map +1 -0
- package/dist/impostor/cache.d.ts +15 -0
- package/dist/impostor/cache.js +44 -0
- package/dist/impostor/cache.js.map +1 -0
- package/dist/impostor/codec.d.ts +14 -0
- package/dist/impostor/codec.js +137 -0
- package/dist/impostor/codec.js.map +1 -0
- package/dist/impostor/impostor.d.ts +67 -0
- package/dist/impostor/impostor.js +79 -0
- package/dist/impostor/impostor.js.map +1 -0
- package/dist/impostor/mesh/card.d.ts +38 -0
- package/dist/impostor/mesh/card.js +79 -0
- package/dist/impostor/mesh/card.js.map +1 -0
- package/dist/impostor/mesh/index.d.ts +66 -0
- package/dist/impostor/mesh/index.js +174 -0
- package/dist/impostor/mesh/index.js.map +1 -0
- package/dist/impostor/mesh/material.d.ts +63 -0
- package/dist/impostor/mesh/material.js +199 -0
- package/dist/impostor/mesh/material.js.map +1 -0
- package/dist/impostor/mesh/surface.d.ts +63 -0
- package/dist/impostor/mesh/surface.js +162 -0
- package/dist/impostor/mesh/surface.js.map +1 -0
- package/dist/impostor/mesh/textures.d.ts +4 -0
- package/dist/impostor/mesh/textures.js +37 -0
- package/dist/impostor/mesh/textures.js.map +1 -0
- package/dist/impostor/octahedral.d.ts +29 -0
- package/dist/impostor/octahedral.js +83 -0
- package/dist/impostor/octahedral.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/instanced/args.d.ts +20 -0
- package/dist/instanced/args.js +77 -0
- package/dist/instanced/args.js.map +1 -0
- package/dist/instanced/bounds.d.ts +50 -0
- package/dist/instanced/bounds.js +271 -0
- package/dist/instanced/bounds.js.map +1 -0
- package/dist/instanced/dirty.d.ts +12 -0
- package/dist/instanced/dirty.js +61 -0
- package/dist/instanced/dirty.js.map +1 -0
- package/dist/instanced/index.d.ts +116 -0
- package/dist/instanced/index.js +573 -0
- package/dist/instanced/index.js.map +1 -0
- package/dist/instanced/meshes.d.ts +14 -0
- package/dist/instanced/meshes.js +54 -0
- package/dist/instanced/meshes.js.map +1 -0
- package/dist/instanced/select.d.ts +84 -0
- package/dist/instanced/select.js +264 -0
- package/dist/instanced/select.js.map +1 -0
- package/dist/lazy.d.ts +64 -0
- package/dist/lazy.js +187 -0
- package/dist/lazy.js.map +1 -0
- package/dist/levels.d.ts +72 -0
- package/dist/levels.js +146 -0
- package/dist/levels.js.map +1 -0
- package/dist/loader/cache.d.ts +15 -0
- package/dist/loader/cache.js +37 -0
- package/dist/loader/cache.js.map +1 -0
- package/dist/loader/dispose.d.ts +3 -0
- package/dist/loader/dispose.js +25 -0
- package/dist/loader/dispose.js.map +1 -0
- package/dist/loader.d.ts +99 -0
- package/dist/loader.js +234 -0
- package/dist/loader.js.map +1 -0
- package/dist/lod.d.ts +78 -0
- package/dist/lod.js +211 -0
- package/dist/lod.js.map +1 -0
- package/dist/material.d.ts +12 -0
- package/dist/material.js +52 -0
- package/dist/material.js.map +1 -0
- package/dist/pause.d.ts +6 -0
- package/dist/pause.js +20 -0
- package/dist/pause.js.map +1 -0
- package/dist/renderer.d.ts +21 -0
- package/dist/renderer.js +83 -0
- package/dist/renderer.js.map +1 -0
- package/dist/simplify.d.ts +24 -0
- package/dist/simplify.js +148 -0
- package/dist/simplify.js.map +1 -0
- package/dist/source/error.d.ts +14 -0
- package/dist/source/error.js +94 -0
- package/dist/source/error.js.map +1 -0
- package/dist/source/frame.d.ts +11 -0
- package/dist/source/frame.js +25 -0
- package/dist/source/frame.js.map +1 -0
- package/dist/source/shading.d.ts +96 -0
- package/dist/source/shading.js +224 -0
- package/dist/source/shading.js.map +1 -0
- package/dist/source/source.d.ts +23 -0
- package/dist/source/source.js +50 -0
- package/dist/source/source.js.map +1 -0
- package/dist/source/surface.d.ts +31 -0
- package/dist/source/surface.js +110 -0
- package/dist/source/surface.js.map +1 -0
- package/dist/source/texels.d.ts +45 -0
- package/dist/source/texels.js +191 -0
- package/dist/source/texels.js.map +1 -0
- package/dist/source/trace.d.ts +42 -0
- package/dist/source/trace.js +153 -0
- package/dist/source/trace.js.map +1 -0
- package/dist/three.d.ts +55 -0
- package/dist/three.js +103 -0
- package/dist/three.js.map +1 -0
- package/dist/tsl.d.ts +45 -0
- package/dist/tsl.js +99 -0
- package/dist/tsl.js.map +1 -0
- package/dist/view.d.ts +30 -0
- package/dist/view.js +75 -0
- package/dist/view.js.map +1 -0
- package/dist/wind/bounds.d.ts +3 -0
- package/dist/wind/bounds.js +47 -0
- package/dist/wind/bounds.js.map +1 -0
- package/dist/wind/hooks.d.ts +5 -0
- package/dist/wind/hooks.js +3 -0
- package/dist/wind/hooks.js.map +1 -0
- package/dist/wind/index.d.ts +57 -0
- package/dist/wind/index.js +196 -0
- package/dist/wind/index.js.map +1 -0
- package/dist/wind/sway.d.ts +29 -0
- package/dist/wind/sway.js +71 -0
- package/dist/wind/sway.js.map +1 -0
- package/package.json +69 -3
- 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
|
package/dist/lod.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lod.js","sourceRoot":"","sources":["../src/lod.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAkD,MAAM,cAAc,CAAC;AAChG,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAa,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAuB,MAAM,WAAW,CAAC;AAC9E,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAiB,MAAM,aAAa,CAAC;AAC7I,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACxG,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAExD,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;AAIxB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,OAAO,GAAI,SAAQ,QAAwB;IAC/C,kFAAkF;IAClF,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC;IACnB,qEAAqE;IACrE,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB;;;OAGG;IACH,MAAM,CAAU,IAAI,GAAG,IAAI,CAAC;IAEnB,KAAK,GAAG,IAAI,CAAC;IACtB,kEAAkE;IAClE,UAAU,GAAG,IAAI,CAAC;IAClB,2CAA2C;IAC3C,UAAU,GAAkB,IAAI,CAAC;IACxB,MAAM,CAAsB;IAEpB,OAAO,GAAY,EAAE,CAAC;IACvC,sCAAsC;IACrB,KAAK,GAAiB,EAAE,CAAC;IAC1C,kFAAkF;IAC1E,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;IACtB,YAAY,GAAG,CAAC,CAAC;IACjB,QAAQ,GAAG,KAAK,CAAC;IACR,SAAS,GAAG,IAAI,GAAG,EAAY,CAAC;IACzC,QAAQ,GAAa,EAAE,CAAC;IAEhC;;;;OAIG;IACH,YAAY,SAAqB,EAAE;QACjC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC;QAC3B,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;YAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC5C,CAAC;IAEO,KAAK,CAAC,OAAmB;QAC/B,MAAM,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC;QACzB,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QACrE,IAAI,cAAc,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACnF,KAAK,MAAM,MAAM,IAAI,OAAO;YAC1B,IAAI,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC3B,MAAM,CAAC,WAAW,EAAE,CAAC;YACvB,CAAC;QACH,KAAK,MAAM,MAAM,IAAI,OAAO;YAAE,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC3D,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACtD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC;QAClC,IAAI,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC;QACrB,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YAC5B,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YAChE,MAAM,IAAI,GAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;YACpF,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACzB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtB,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAC9C,IAAI,SAAS,CAAC,MAAM,CAAC;gBAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QACH,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;QACnC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,KAAK,MAAM,MAAM,IAAI,OAAO;YAAE,IAAI,cAAc,CAAC,MAAM,CAAC;gBAAE,MAAM,CAAC,MAAM,EAAE,CAAC;IAC5E,CAAC;IAED,kHAAkH;IAC1G,IAAI,CAAC,IAAc,EAAE,IAAgB,EAAE,MAAe,EAAE,KAAe;QAC7E,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,KAAK,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QACxD,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YACjB,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBACpB,yCAAyC;gBACzC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC;oBAAE,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACtJ,CAAC;YACD,aAAa,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ;YAAE,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IAC1E,CAAC;IAED,CAAC,WAAW,CAAC,CAAC,OAAe;QAC3B,IAAI,CAAC,YAAY,IAAI,OAAO,CAAC;IAC/B,CAAC;IAED,gGAAgG;IAChG,MAAM,CAAC,MAAc;QACnB,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;QAChC,IAAI,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC;YAAE,OAAO;QACxE,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,GAAG,OAAO,CAAC,MAAM;YACtC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;gBAC3B,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC;oBAAE,OAAO;gBAC9E,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;gBAC3B,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC;oBAAE,OAAO;gBACrC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;gBACtB,IAAI,CAAC,IAAI;oBAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;gBACrD,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;gBACnD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBACjC,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC,CAAC,CAAC;QACL,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;YAChE,WAAW,CAAC,IAAI,CAAC,CAAC;YAClB,IAAI,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACnC,IAAI,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;QAC/C,CAAC;QACD,QAAQ,CAAC,OAAO,CAAC,CAAC;QAClB,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,IAAI,cAAc,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAC9E,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;QAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC;QACxE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAC1D,IAAI,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,KAAK,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC;YAAE,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QAClJ,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QACpD,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChE,MAAM,SAAS,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;QACzE,IAAI,SAAS,IAAI,cAAc,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,MAAM,KAAK,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC;YAAE,SAAS,CAAC,OAAO,EAAE,CAAC;QAChJ,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC5E,IAAI,KAAK,KAAK,KAAK;YAAE,CAAC,GAAG,CAAC,CAAC;QAC3B,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,WAAW,CAAC,iBAAiB,EAAE,CAAC;QAC9E,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;YAC1B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACtB,IAAI,CAAC,IAAI;gBAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;YACvD,IAAI,CAAC,IAAI,GAAG,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACzD,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;YACzB,IAAI,CAAC,MAAM,CAAC,OAAO,GAAG,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,IAAI,KAAK,KAAK,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;IACL,CAAC;IAEQ,OAAO,CAAC,SAAoB,EAAE,UAA0B;QAC/D,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,MAAM;YAAE,SAAS,CAAC,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;QACvE,4DAA4D;QAC5D,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;OAIG;IACM,IAAI,CAAC,MAAW;QACvB,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACjF,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC1B,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;QACvE,cAAc,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;QACpC,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;QACpC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;OAGG;IACH,OAAO;QACL,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,KAAK,MAAM,EAAE,MAAM,EAAE,IAAI,IAAI,CAAC,OAAO;YAAE,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IACzG,CAAC"}
|
|
@@ -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;
|
package/dist/material.js
ADDED
|
@@ -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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"}
|
package/dist/pause.d.ts
ADDED
|
@@ -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>;
|