@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/pause.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
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 function pause() {
|
|
7
|
+
const scheduler = Reflect.get(globalThis, "scheduler");
|
|
8
|
+
const yielding = typeof scheduler === "object" && scheduler !== null ? Reflect.get(scheduler, "yield") : undefined;
|
|
9
|
+
if (typeof yielding === "function")
|
|
10
|
+
return Promise.resolve(Reflect.apply(yielding, scheduler, [])).then(() => { });
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
const channel = new MessageChannel();
|
|
13
|
+
channel.port1.onmessage = () => {
|
|
14
|
+
channel.port1.close();
|
|
15
|
+
resolve();
|
|
16
|
+
};
|
|
17
|
+
channel.port2.postMessage(null);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=pause.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pause.js","sourceRoot":"","sources":["../src/pause.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,KAAK;IACnB,MAAM,SAAS,GAAY,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAChE,MAAM,QAAQ,GAAG,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACnH,IAAI,OAAO,QAAQ,KAAK,UAAU;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAClH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,OAAO,GAAG,IAAI,cAAc,EAAE,CAAC;QACrC,OAAO,CAAC,KAAK,CAAC,SAAS,GAAG,GAAG,EAAE;YAC7B,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACtB,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;QACF,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { WebGPURenderer, type RenderTarget, type Renderer } from "three/webgpu";
|
|
2
|
+
/**
|
|
3
|
+
* The package's own renderer for bakes, made once and initialised. Its canvas is never
|
|
4
|
+
* drawn to: three applies a render target's viewport only when it differs from the
|
|
5
|
+
* canvas's size, and the bakes place their frames by viewport.
|
|
6
|
+
*/
|
|
7
|
+
export declare function sharedRenderer(): Promise<WebGPURenderer>;
|
|
8
|
+
/**
|
|
9
|
+
* Point `renderer` at `target`, without the app's XR camera, MRT or clear colour, and
|
|
10
|
+
* return a function that puts the app's state back. A bake holds a renderer for one
|
|
11
|
+
* synchronous run of renders: the app keeps rendering while the bake awaits.
|
|
12
|
+
*/
|
|
13
|
+
export declare function borrow(renderer: Renderer, target: RenderTarget): () => void;
|
|
14
|
+
/**
|
|
15
|
+
* Texture `index` of `target` as floats, four channels a texel, rows packed and bottom
|
|
16
|
+
* row first: the layout the bakes write their images in. For float and half-float
|
|
17
|
+
* targets.
|
|
18
|
+
*/
|
|
19
|
+
export declare function readFloats(renderer: Renderer, target: RenderTarget, index: number): Promise<Float32Array>;
|
|
20
|
+
/** Texture `index` of an 8-bit `target`, laid out as `readFloats` lays it out. */
|
|
21
|
+
export declare function readBytes(renderer: Renderer, target: RenderTarget, index: number): Promise<Uint8Array>;
|
package/dist/renderer.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { Color, DataUtils, WebGPURenderer } from "three/webgpu";
|
|
2
|
+
// The GPU side of bakes: impostor frames and interior rooms render here.
|
|
3
|
+
let shared;
|
|
4
|
+
/**
|
|
5
|
+
* The package's own renderer for bakes, made once and initialised. Its canvas is never
|
|
6
|
+
* drawn to: three applies a render target's viewport only when it differs from the
|
|
7
|
+
* canvas's size, and the bakes place their frames by viewport.
|
|
8
|
+
*/
|
|
9
|
+
export function sharedRenderer() {
|
|
10
|
+
shared ??= (async () => {
|
|
11
|
+
const renderer = new WebGPURenderer({ antialias: false });
|
|
12
|
+
await renderer.init();
|
|
13
|
+
return renderer;
|
|
14
|
+
})();
|
|
15
|
+
return shared;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Point `renderer` at `target`, without the app's XR camera, MRT or clear colour, and
|
|
19
|
+
* return a function that puts the app's state back. A bake holds a renderer for one
|
|
20
|
+
* synchronous run of renders: the app keeps rendering while the bake awaits.
|
|
21
|
+
*/
|
|
22
|
+
export function borrow(renderer, target) {
|
|
23
|
+
const previous = {
|
|
24
|
+
target: renderer.getRenderTarget(),
|
|
25
|
+
clear: renderer.getClearColor(new Color()),
|
|
26
|
+
alpha: renderer.getClearAlpha(),
|
|
27
|
+
autoClear: renderer.autoClear,
|
|
28
|
+
xr: renderer.xr.enabled,
|
|
29
|
+
mrt: renderer.getMRT(),
|
|
30
|
+
};
|
|
31
|
+
renderer.setRenderTarget(target);
|
|
32
|
+
renderer.setClearColor(0x000000, 0);
|
|
33
|
+
renderer.autoClear = false;
|
|
34
|
+
renderer.xr.enabled = false;
|
|
35
|
+
renderer.setMRT(null);
|
|
36
|
+
return () => {
|
|
37
|
+
renderer.setRenderTarget(previous.target);
|
|
38
|
+
renderer.setClearColor(previous.clear, previous.alpha);
|
|
39
|
+
renderer.autoClear = previous.autoClear;
|
|
40
|
+
renderer.xr.enabled = previous.xr;
|
|
41
|
+
renderer.setMRT(previous.mrt);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Texture `index` of `target` as floats, four channels a texel, rows packed and bottom
|
|
46
|
+
* row first: the layout the bakes write their images in. For float and half-float
|
|
47
|
+
* targets.
|
|
48
|
+
*/
|
|
49
|
+
export async function readFloats(renderer, target, index) {
|
|
50
|
+
const read = await renderer.readRenderTargetPixelsAsync(target, 0, 0, target.width, target.height, index);
|
|
51
|
+
if (read instanceof Float32Array)
|
|
52
|
+
return packed(read, target, (n) => new Float32Array(n));
|
|
53
|
+
// Half floats come back as their bits.
|
|
54
|
+
if (read instanceof Uint16Array)
|
|
55
|
+
return fromHalves(packed(read, target, (n) => new Uint16Array(n)));
|
|
56
|
+
throw new Error(`@drawcall/lod: read ${read.constructor.name} from a float render target`);
|
|
57
|
+
}
|
|
58
|
+
/** Texture `index` of an 8-bit `target`, laid out as `readFloats` lays it out. */
|
|
59
|
+
export async function readBytes(renderer, target, index) {
|
|
60
|
+
const read = await renderer.readRenderTargetPixelsAsync(target, 0, 0, target.width, target.height, index);
|
|
61
|
+
if (!(read instanceof Uint8Array))
|
|
62
|
+
throw new Error(`@drawcall/lod: read ${read.constructor.name} from an 8-bit render target`);
|
|
63
|
+
return packed(read, target, (n) => new Uint8Array(n));
|
|
64
|
+
}
|
|
65
|
+
/** Every half float by its bits. */
|
|
66
|
+
const HALF = Float32Array.from({ length: 65536 }, (_, bits) => DataUtils.fromHalfFloat(bits));
|
|
67
|
+
function fromHalves(bits) {
|
|
68
|
+
const out = new Float32Array(bits.length);
|
|
69
|
+
// Typed-array reads below are in bounds; `?? 0` only answers noUncheckedIndexedAccess.
|
|
70
|
+
for (let i = 0; i < bits.length; i++)
|
|
71
|
+
out[i] = HALF[bits[i] ?? 0] ?? 0;
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
/** `read`'s rows, each padded to 256 bytes and top first, packed and bottom first. */
|
|
75
|
+
function packed(read, { width, height }, make) {
|
|
76
|
+
const row = width * 4;
|
|
77
|
+
const stride = (Math.ceil((row * read.BYTES_PER_ELEMENT) / 256) * 256) / read.BYTES_PER_ELEMENT;
|
|
78
|
+
const out = make(row * height);
|
|
79
|
+
for (let y = 0; y < height; y++)
|
|
80
|
+
out.set(read.subarray(y * stride, y * stride + row), (height - 1 - y) * row);
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer.js","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type Object3D } from "three/webgpu";
|
|
2
|
+
export interface SimplifyOptions {
|
|
3
|
+
/** share of each mesh's triangles to keep, in (0, 1]; default 0.5 */
|
|
4
|
+
ratio?: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* A copy of `object` with the geometry of every mesh drawn at full detail decimated by
|
|
8
|
+
* meshoptimizer to about `ratio` of its triangles: of a nested LOD only level 0 (its other
|
|
9
|
+
* levels are kept), of an InstancedMesh the geometry its instances share. Materials (and
|
|
10
|
+
* so textures) are the originals, shared with `object`. Meshes that share a geometry
|
|
11
|
+
* share the simplified one. Morph targets are dropped. ImpostorMeshes and hidden meshes
|
|
12
|
+
* are kept as they are. Skinned meshes and InstancedLods are refused: simplify an
|
|
13
|
+
* InstancedLod's source and build a new InstancedLod of it.
|
|
14
|
+
*
|
|
15
|
+
* Per material group it keeps UV and normal seams; if that cannot reach the target it
|
|
16
|
+
* lets seams collapse, and as a last resort ignores topology (meshopt's sloppy mode).
|
|
17
|
+
*
|
|
18
|
+
* How far the copy's surface is from `object`'s (the 99.9th percentile of the distance
|
|
19
|
+
* between the two, by area, over the parts seen from outside; ImpostorMeshes left out)
|
|
20
|
+
* sets its default maxPixels in a Lod.
|
|
21
|
+
*/
|
|
22
|
+
export declare function simplify(object: Object3D, options?: SimplifyOptions): Promise<Object3D>;
|
|
23
|
+
/** A copy of `object` decimated to `ratio`, as `simplify` makes it, without measuring it. */
|
|
24
|
+
export declare function decimate(object: Object3D, ratio: number): Promise<Object3D>;
|
package/dist/simplify.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { BufferAttribute, BufferGeometry } from "three/webgpu";
|
|
2
|
+
import { mergeVertices } from "three/addons/utils/BufferGeometryUtils.js";
|
|
3
|
+
import { MeshoptSimplifier } from "meshoptimizer";
|
|
4
|
+
import { detailed } from "./bounds.js";
|
|
5
|
+
import { recordError } from "./levels.js";
|
|
6
|
+
import { measureError } from "./source/error.js";
|
|
7
|
+
import { readSource } from "./source/source.js";
|
|
8
|
+
import { merge } from "./source/surface.js";
|
|
9
|
+
import { isImpostorMesh, isInstancedLod, isMesh, isSkinnedMesh } from "./three.js";
|
|
10
|
+
/**
|
|
11
|
+
* A copy of `object` with the geometry of every mesh drawn at full detail decimated by
|
|
12
|
+
* meshoptimizer to about `ratio` of its triangles: of a nested LOD only level 0 (its other
|
|
13
|
+
* levels are kept), of an InstancedMesh the geometry its instances share. Materials (and
|
|
14
|
+
* so textures) are the originals, shared with `object`. Meshes that share a geometry
|
|
15
|
+
* share the simplified one. Morph targets are dropped. ImpostorMeshes and hidden meshes
|
|
16
|
+
* are kept as they are. Skinned meshes and InstancedLods are refused: simplify an
|
|
17
|
+
* InstancedLod's source and build a new InstancedLod of it.
|
|
18
|
+
*
|
|
19
|
+
* Per material group it keeps UV and normal seams; if that cannot reach the target it
|
|
20
|
+
* lets seams collapse, and as a last resort ignores topology (meshopt's sloppy mode).
|
|
21
|
+
*
|
|
22
|
+
* How far the copy's surface is from `object`'s (the 99.9th percentile of the distance
|
|
23
|
+
* between the two, by area, over the parts seen from outside; ImpostorMeshes left out)
|
|
24
|
+
* sets its default maxPixels in a Lod.
|
|
25
|
+
*/
|
|
26
|
+
export async function simplify(object, options = {}) {
|
|
27
|
+
const copy = await decimate(object, options.ratio ?? 0.5);
|
|
28
|
+
recordError(copy, measureError(readSource(object, "simplify", "measure"), merge(copy, "simplify")).error);
|
|
29
|
+
return copy;
|
|
30
|
+
}
|
|
31
|
+
/** A copy of `object` decimated to `ratio`, as `simplify` makes it, without measuring it. */
|
|
32
|
+
export async function decimate(object, ratio) {
|
|
33
|
+
if (!(ratio > 0 && ratio <= 1))
|
|
34
|
+
throw new Error(`simplify: ratio must be in (0, 1], got ${ratio}`);
|
|
35
|
+
await MeshoptSimplifier.ready;
|
|
36
|
+
// clone() also visits hidden children, whose crowd allocations must never be
|
|
37
|
+
// created just to reject them (or skipped by the visible-detail walk below).
|
|
38
|
+
object.traverse((node) => {
|
|
39
|
+
if (isInstancedLod(node))
|
|
40
|
+
throw new Error(`simplify: cannot simplify an InstancedLod ("${node.name}"); simplify its source and build a new one`);
|
|
41
|
+
if (isSkinnedMesh(node))
|
|
42
|
+
throw new Error(`simplify: skinned meshes are not supported ("${node.name}")`);
|
|
43
|
+
});
|
|
44
|
+
const copy = object.clone(true);
|
|
45
|
+
decimateTree(copy, ratio, new Map());
|
|
46
|
+
return copy;
|
|
47
|
+
}
|
|
48
|
+
/** Decimate the meshes of `node` drawn at full detail; `done` maps each geometry to its simplified one. */
|
|
49
|
+
function decimateTree(node, ratio, done) {
|
|
50
|
+
if (isMesh(node) && !isImpostorMesh(node)) {
|
|
51
|
+
let geometry = done.get(node.geometry);
|
|
52
|
+
if (!geometry)
|
|
53
|
+
done.set(node.geometry, (geometry = simplifyGeometry(node.geometry, ratio)));
|
|
54
|
+
node.geometry = geometry;
|
|
55
|
+
}
|
|
56
|
+
for (const child of detailed(node))
|
|
57
|
+
decimateTree(child, ratio, done);
|
|
58
|
+
}
|
|
59
|
+
function simplifyGeometry(source, ratio) {
|
|
60
|
+
const geometry = source.index ? source : mergeVertices(source);
|
|
61
|
+
const index = geometry.index;
|
|
62
|
+
if (!index)
|
|
63
|
+
throw new Error("simplify: geometry could not be indexed");
|
|
64
|
+
const positions = toFloat32(geometry.getAttribute("position"));
|
|
65
|
+
const { attributes, stride, weights } = simplifierAttributes(geometry);
|
|
66
|
+
const groups = geometry.groups.length > 0 ? geometry.groups : [{ start: 0, count: index.count, materialIndex: 0 }];
|
|
67
|
+
const parts = [];
|
|
68
|
+
for (const group of groups) {
|
|
69
|
+
const indices = new Uint32Array(Math.min(group.count, index.count - group.start));
|
|
70
|
+
for (let i = 0; i < indices.length; i++)
|
|
71
|
+
indices[i] = index.getX(group.start + i);
|
|
72
|
+
const [simplified] = simplifyIndices(indices, positions, attributes, stride, weights, Math.floor((indices.length / 3) * ratio) * 3);
|
|
73
|
+
parts.push({ indices: simplified, materialIndex: group.materialIndex ?? 0 });
|
|
74
|
+
}
|
|
75
|
+
const all = new Uint32Array(parts.reduce((n, p) => n + p.indices.length, 0));
|
|
76
|
+
const out = new BufferGeometry();
|
|
77
|
+
let offset = 0;
|
|
78
|
+
for (const part of parts) {
|
|
79
|
+
all.set(part.indices, offset);
|
|
80
|
+
if (source.groups.length > 0)
|
|
81
|
+
out.addGroup(offset, part.indices.length, part.materialIndex);
|
|
82
|
+
offset += part.indices.length;
|
|
83
|
+
}
|
|
84
|
+
const [remap, unique] = MeshoptSimplifier.compactMesh(all); // rewrites `all` in place
|
|
85
|
+
for (const [name, attribute] of Object.entries(geometry.attributes))
|
|
86
|
+
out.setAttribute(name, compactAttribute(attribute, remap, unique));
|
|
87
|
+
out.setIndex(new BufferAttribute(all, 1));
|
|
88
|
+
out.computeBoundingBox();
|
|
89
|
+
out.computeBoundingSphere();
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
function simplifyIndices(indices, positions, attributes, stride, weights, target) {
|
|
93
|
+
const reached = (r) => r.length <= target * 1.25 + 3;
|
|
94
|
+
const strict = MeshoptSimplifier.simplifyWithAttributes(indices, positions, 3, attributes, stride, weights, null, target, 1);
|
|
95
|
+
if (reached(strict[0]))
|
|
96
|
+
return strict;
|
|
97
|
+
const permissive = MeshoptSimplifier.simplifyWithAttributes(indices, positions, 3, attributes, stride, weights, null, target, 1, ["Permissive"]);
|
|
98
|
+
if (reached(permissive[0]))
|
|
99
|
+
return permissive;
|
|
100
|
+
const sloppy = MeshoptSimplifier.simplifySloppy(indices, positions, 3, null, target, 1);
|
|
101
|
+
// Sloppy can collapse a tiny target to nothing; then keep the permissive result.
|
|
102
|
+
return sloppy[0].length > 0 ? sloppy : permissive;
|
|
103
|
+
}
|
|
104
|
+
/** Normal (weight 0.5) and UV (weight 1) packed per vertex for simplifyWithAttributes. */
|
|
105
|
+
function simplifierAttributes(geometry) {
|
|
106
|
+
const normal = geometry.getAttribute("normal");
|
|
107
|
+
const uv = geometry.getAttribute("uv");
|
|
108
|
+
const count = geometry.getAttribute("position").count;
|
|
109
|
+
const weights = [...(normal ? [0.5, 0.5, 0.5] : []), ...(uv ? [1, 1] : [])];
|
|
110
|
+
const stride = Math.max(weights.length, 1);
|
|
111
|
+
const attributes = new Float32Array(count * stride);
|
|
112
|
+
for (let i = 0; i < count; i++) {
|
|
113
|
+
let o = i * stride;
|
|
114
|
+
if (normal)
|
|
115
|
+
for (let c = 0; c < 3; c++)
|
|
116
|
+
attributes[o++] = normal.getComponent(i, c);
|
|
117
|
+
if (uv)
|
|
118
|
+
for (let c = 0; c < 2; c++)
|
|
119
|
+
attributes[o++] = uv.getComponent(i, c);
|
|
120
|
+
}
|
|
121
|
+
if (weights.length === 0)
|
|
122
|
+
weights.push(0);
|
|
123
|
+
return { attributes, stride, weights };
|
|
124
|
+
}
|
|
125
|
+
function compactAttribute(attribute, remap, unique) {
|
|
126
|
+
const size = attribute.itemSize;
|
|
127
|
+
// A slice keeps the array type, so normalized and integer attributes keep their encoding;
|
|
128
|
+
// every element is written below (unique <= count, so it is long enough).
|
|
129
|
+
const out = new BufferAttribute(attribute.array.slice(0, unique * size), size, attribute.normalized);
|
|
130
|
+
for (let i = 0; i < remap.length; i++) {
|
|
131
|
+
const j = remap[i];
|
|
132
|
+
if (j === undefined || j === 0xffffffff)
|
|
133
|
+
continue;
|
|
134
|
+
for (let c = 0; c < size; c++)
|
|
135
|
+
out.setComponent(j, c, attribute.getComponent(i, c));
|
|
136
|
+
}
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
139
|
+
function toFloat32(attribute) {
|
|
140
|
+
const out = new Float32Array(attribute.count * 3);
|
|
141
|
+
for (let i = 0; i < attribute.count; i++) {
|
|
142
|
+
out[i * 3] = attribute.getX(i);
|
|
143
|
+
out[i * 3 + 1] = attribute.getY(i);
|
|
144
|
+
out[i * 3 + 2] = attribute.getZ(i);
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=simplify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"simplify.js","sourceRoot":"","sources":["../src/simplify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Source } from "./source.js";
|
|
2
|
+
import { type Surface } from "./surface.js";
|
|
3
|
+
/**
|
|
4
|
+
* How far a level's surface `low` is from `source`'s: the distances from points spread
|
|
5
|
+
* evenly by area over each surface to the other, measured with a BVH. Source points
|
|
6
|
+
* nobody sees from outside (a floor inside, the inner side of a partition) are left out:
|
|
7
|
+
* they need no counterpart on the level. `error` is the 99.9th percentile of the
|
|
8
|
+
* distances, `widest` the largest. A high percentile, as what a level moves (a part
|
|
9
|
+
* collapsed or cut off) is often a sliver of the area.
|
|
10
|
+
*/
|
|
11
|
+
export declare function measureError(source: Source, low: Surface): {
|
|
12
|
+
error: number;
|
|
13
|
+
widest: number;
|
|
14
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { BufferAttribute, BufferGeometry, Vector3 } from "three/webgpu";
|
|
2
|
+
import { MeshBVH } from "three-mesh-bvh";
|
|
3
|
+
import { Shading } from "./shading.js";
|
|
4
|
+
import { triangleArea } from "./surface.js";
|
|
5
|
+
import { Tracer } from "./trace.js";
|
|
6
|
+
/** Points sampled per surface to measure the error. */
|
|
7
|
+
const SAMPLES = 20000;
|
|
8
|
+
/**
|
|
9
|
+
* How far a level's surface `low` is from `source`'s: the distances from points spread
|
|
10
|
+
* evenly by area over each surface to the other, measured with a BVH. Source points
|
|
11
|
+
* nobody sees from outside (a floor inside, the inner side of a partition) are left out:
|
|
12
|
+
* they need no counterpart on the level. `error` is the 99.9th percentile of the
|
|
13
|
+
* distances, `widest` the largest. A high percentile, as what a level moves (a part
|
|
14
|
+
* collapsed or cut off) is often a sliver of the area.
|
|
15
|
+
*/
|
|
16
|
+
export function measureError(source, low) {
|
|
17
|
+
const geometry = new BufferGeometry();
|
|
18
|
+
geometry.setAttribute("position", new BufferAttribute(low.positions, 3));
|
|
19
|
+
// The BVH reorders the index it is built on.
|
|
20
|
+
geometry.setIndex(new BufferAttribute(low.index.slice(), 1));
|
|
21
|
+
const toLow = new MeshBVH(geometry);
|
|
22
|
+
const distances = [];
|
|
23
|
+
measure(low, (point) => distances.push(closest(source.bvh, point)));
|
|
24
|
+
const visible = visibility(source);
|
|
25
|
+
measure(source, (point, triangle) => {
|
|
26
|
+
if (visible(point, triangle))
|
|
27
|
+
distances.push(closest(toLow, point));
|
|
28
|
+
});
|
|
29
|
+
const sorted = Float64Array.from(distances).sort();
|
|
30
|
+
return { error: sorted[Math.floor(0.999 * (sorted.length - 1))] ?? 0, widest: sorted[sorted.length - 1] ?? 0 };
|
|
31
|
+
}
|
|
32
|
+
const hit = { point: new Vector3(), distance: 0, faceIndex: 0 };
|
|
33
|
+
function closest(bvh, point) {
|
|
34
|
+
if (!bvh.closestPointToPoint(point, hit))
|
|
35
|
+
throw new Error("@drawcall/lod: no closest point to measure the error by");
|
|
36
|
+
return hit.distance;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Whether a point of a source triangle is seen from outside: a ray from it along the
|
|
40
|
+
* triangle's normal (either way, for a double-sided material) leaves the source's
|
|
41
|
+
* bounds without meeting an opaque surface, from either side.
|
|
42
|
+
*/
|
|
43
|
+
function visibility(source) {
|
|
44
|
+
const geometry = source.bvh.geometry;
|
|
45
|
+
geometry.computeBoundingSphere();
|
|
46
|
+
const bounds = geometry.boundingSphere;
|
|
47
|
+
if (!bounds)
|
|
48
|
+
throw new Error("@drawcall/lod: the source has no bounds");
|
|
49
|
+
const shading = new Shading(source, 0);
|
|
50
|
+
const tracer = new Tracer(source, shading);
|
|
51
|
+
const normal = new Vector3();
|
|
52
|
+
const corner = new Vector3();
|
|
53
|
+
const origin = new Vector3();
|
|
54
|
+
const epsilon = bounds.radius * 1e-4;
|
|
55
|
+
const far = bounds.radius * 4;
|
|
56
|
+
const escapes = (point, direction) => tracer.cast(origin.copy(point).addScaledVector(direction, epsilon), direction, far, null) !== "opaque";
|
|
57
|
+
return (point, triangle) => {
|
|
58
|
+
const { positions, index } = source;
|
|
59
|
+
const a = (index[triangle * 3] ?? 0) * 3;
|
|
60
|
+
normal.fromArray(positions, (index[triangle * 3 + 1] ?? 0) * 3).sub(corner.fromArray(positions, a));
|
|
61
|
+
corner.fromArray(positions, (index[triangle * 3 + 2] ?? 0) * 3).sub(origin.fromArray(positions, a));
|
|
62
|
+
normal.cross(corner).normalize();
|
|
63
|
+
if (escapes(point, normal))
|
|
64
|
+
return true;
|
|
65
|
+
return shading.material(triangle).doubleSided && escapes(point, normal.negate());
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** Call `visit` with points spread evenly by area over `from`, and the triangle of each. */
|
|
69
|
+
function measure(from, visit) {
|
|
70
|
+
const { positions, index } = from;
|
|
71
|
+
const count = index.length / 3;
|
|
72
|
+
const cumulative = new Float64Array(count);
|
|
73
|
+
let total = 0;
|
|
74
|
+
for (let t = 0; t < count; t++)
|
|
75
|
+
cumulative[t] = total += triangleArea(positions, 3, index, t * 3);
|
|
76
|
+
const point = new Vector3();
|
|
77
|
+
const corner = new Vector3();
|
|
78
|
+
let t = 0;
|
|
79
|
+
for (let i = 0; i < SAMPLES; i++) {
|
|
80
|
+
// Stratified by area; within a triangle, a low-discrepancy (R2) point.
|
|
81
|
+
const target = ((i + 0.5) / SAMPLES) * total;
|
|
82
|
+
while (t < count - 1 && (cumulative[t] ?? 0) < target)
|
|
83
|
+
t++;
|
|
84
|
+
let r1 = (i * 0.7548776662466927) % 1;
|
|
85
|
+
let r2 = (i * 0.5698402909980532) % 1;
|
|
86
|
+
if (r1 + r2 > 1)
|
|
87
|
+
[r1, r2] = [1 - r1, 1 - r2];
|
|
88
|
+
point.fromArray(positions, (index[t * 3] ?? 0) * 3).multiplyScalar(1 - r1 - r2);
|
|
89
|
+
point.addScaledVector(corner.fromArray(positions, (index[t * 3 + 1] ?? 0) * 3), r1);
|
|
90
|
+
point.addScaledVector(corner.fromArray(positions, (index[t * 3 + 2] ?? 0) * 3), r2);
|
|
91
|
+
visit(point, t);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error.js","sourceRoot":"","sources":["../../src/source/error.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Vector3 } from "three/webgpu";
|
|
2
|
+
/**
|
|
3
|
+
* The tangent frame three derives for a normal map when a mesh has no tangents
|
|
4
|
+
* (getTangentFrame in normalmap_pars_fragment), from a triangle instead of screen
|
|
5
|
+
* derivatives: `e1`, `e2` its edges from the first corner, (`du1`, `dv1`) and
|
|
6
|
+
* (`du2`, `dv2`) their UV deltas, `n` the unit normal there. Writes `t` and `b` scaled as
|
|
7
|
+
* three scales them; the frame is (t, b, n), not orthonormal. For a triangle three sees
|
|
8
|
+
* from the front, the result is three's exactly: both are the UV gradients along the
|
|
9
|
+
* surface, up to the same positive factor, which the scaling cancels.
|
|
10
|
+
*/
|
|
11
|
+
export declare function tangentFrame(e1: Vector3, e2: Vector3, du1: number, dv1: number, du2: number, dv2: number, n: Vector3, t: Vector3, b: Vector3): void;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tangent frame three derives for a normal map when a mesh has no tangents
|
|
3
|
+
* (getTangentFrame in normalmap_pars_fragment), from a triangle instead of screen
|
|
4
|
+
* derivatives: `e1`, `e2` its edges from the first corner, (`du1`, `dv1`) and
|
|
5
|
+
* (`du2`, `dv2`) their UV deltas, `n` the unit normal there. Writes `t` and `b` scaled as
|
|
6
|
+
* three scales them; the frame is (t, b, n), not orthonormal. For a triangle three sees
|
|
7
|
+
* from the front, the result is three's exactly: both are the UV gradients along the
|
|
8
|
+
* surface, up to the same positive factor, which the scaling cancels.
|
|
9
|
+
*/
|
|
10
|
+
export function tangentFrame(e1, e2, du1, dv1, du2, dv2, n, t, b) {
|
|
11
|
+
// q1perp = e2 × n, q0perp = n × e1
|
|
12
|
+
const px = e2.y * n.z - e2.z * n.y;
|
|
13
|
+
const py = e2.z * n.x - e2.x * n.z;
|
|
14
|
+
const pz = e2.x * n.y - e2.y * n.x;
|
|
15
|
+
const qx = n.y * e1.z - n.z * e1.y;
|
|
16
|
+
const qy = n.z * e1.x - n.x * e1.z;
|
|
17
|
+
const qz = n.x * e1.y - n.y * e1.x;
|
|
18
|
+
t.set(px * du1 + qx * du2, py * du1 + qy * du2, pz * du1 + qz * du2);
|
|
19
|
+
b.set(px * dv1 + qx * dv2, py * dv1 + qy * dv2, pz * dv1 + qz * dv2);
|
|
20
|
+
const det = Math.max(t.lengthSq(), b.lengthSq());
|
|
21
|
+
const scale = det === 0 ? 0 : 1 / Math.sqrt(det);
|
|
22
|
+
t.multiplyScalar(scale);
|
|
23
|
+
b.multiplyScalar(scale);
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=frame.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame.js","sourceRoot":"","sources":["../../src/source/frame.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,EAAW,EAAE,EAAW,EAAE,GAAW,EAAE,GAAW,EAAE,GAAW,EAAE,GAAW,EAAE,CAAU,EAAE,CAAU,EAAE,CAAU;IAC3I,mCAAmC;IACnC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACnC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACnC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACnC,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,GAAG,EAAE,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,GAAG,EAAE,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC;IACrE,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,GAAG,EAAE,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,GAAG,EAAE,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC;IACrE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjD,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Color, Vector2, Vector3, type Material, type Texture } from "three/webgpu";
|
|
2
|
+
import type { Source } from "./source.js";
|
|
3
|
+
import { Texels } from "./texels.js";
|
|
4
|
+
/**
|
|
5
|
+
* What the bake reads of a material, read as a MeshStandardMaterial: color, map, opacity,
|
|
6
|
+
* alphaMap, alphaTest, vertexColors, roughness (+ map G), metalness (+ map B), normalMap
|
|
7
|
+
* (tangent space, + normalScale) and aoMap R. Other materials bake as rough dielectrics
|
|
8
|
+
* with their color and map. Emissive, clearcoat, transmission, sheen etc. are not baked.
|
|
9
|
+
*/
|
|
10
|
+
export interface MaterialInputs {
|
|
11
|
+
color: Color;
|
|
12
|
+
map: Texels | null;
|
|
13
|
+
alphaMap: Texels | null;
|
|
14
|
+
opacity: number;
|
|
15
|
+
/**
|
|
16
|
+
* Whether alpha cuts holes: an alphaTest, or alpha to coverage without blending.
|
|
17
|
+
* Blended transparency (glass) bakes opaque, at its own colour.
|
|
18
|
+
*/
|
|
19
|
+
cutout: boolean;
|
|
20
|
+
alphaTest: number;
|
|
21
|
+
vertexColors: boolean;
|
|
22
|
+
roughness: number;
|
|
23
|
+
roughnessMap: Texels | null;
|
|
24
|
+
metalness: number;
|
|
25
|
+
metalnessMap: Texels | null;
|
|
26
|
+
normalMap: Texels | null;
|
|
27
|
+
normalScale: Vector2;
|
|
28
|
+
aoMap: Texels | null;
|
|
29
|
+
aoMapIntensity: number;
|
|
30
|
+
doubleSided: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Share of what is behind the surface that shows through it: blended opacity and
|
|
33
|
+
* transmission, 0 for an opaque surface. A surface with any is glass. Texture alpha is
|
|
34
|
+
* not read: it cuts out (leaves), where opacity and transmission make panes.
|
|
35
|
+
*/
|
|
36
|
+
transmittance: number;
|
|
37
|
+
}
|
|
38
|
+
/** `material` as the CPU reads it, to `bake` or to `measure` (see `readSource`). */
|
|
39
|
+
export declare function readMaterial(material: Material, cache: Map<Texture, Texels>, purpose: "bake" | "measure"): MaterialInputs;
|
|
40
|
+
/**
|
|
41
|
+
* What a ray met: the back of a single-sided surface (which three does not draw), a
|
|
42
|
+
* surface cut out at that point, or an opaque one.
|
|
43
|
+
*/
|
|
44
|
+
export type Hit = "back" | "cutout" | "opaque";
|
|
45
|
+
/** The source's material values at surface points: `test` where a ray meets one, `shade` the one it keeps. */
|
|
46
|
+
export declare class Shading {
|
|
47
|
+
private readonly source;
|
|
48
|
+
/** size of one baked texel in the source's units: mip levels are picked to match it */
|
|
49
|
+
private readonly texelSize;
|
|
50
|
+
/** linear base colour */
|
|
51
|
+
r: number;
|
|
52
|
+
g: number;
|
|
53
|
+
b: number;
|
|
54
|
+
ao: number;
|
|
55
|
+
roughness: number;
|
|
56
|
+
metalness: number;
|
|
57
|
+
/** shading normal in the source's space */
|
|
58
|
+
readonly normal: Vector3;
|
|
59
|
+
/** per source triangle: unit face normal */
|
|
60
|
+
private readonly faces;
|
|
61
|
+
/** per source triangle: UV area per unit of surface area */
|
|
62
|
+
private readonly density;
|
|
63
|
+
private readonly e1;
|
|
64
|
+
private readonly e2;
|
|
65
|
+
private readonly tangent;
|
|
66
|
+
private readonly bitangent;
|
|
67
|
+
private readonly texel;
|
|
68
|
+
private triangle;
|
|
69
|
+
private w0;
|
|
70
|
+
private w1;
|
|
71
|
+
private w2;
|
|
72
|
+
constructor(source: Source,
|
|
73
|
+
/** size of one baked texel in the source's units: mip levels are picked to match it */
|
|
74
|
+
texelSize: number);
|
|
75
|
+
/**
|
|
76
|
+
* What a ray along -`facing` meets at weights (`w1`, `w2`) of triangle `triangle`;
|
|
77
|
+
* with `facing` null, either side of a surface counts as its front.
|
|
78
|
+
*/
|
|
79
|
+
test(triangle: number, w1: number, w2: number, facing: Vector3 | null): Hit;
|
|
80
|
+
/** Whether triangle `triangle` faces away from a viewer looking along -`facing`. */
|
|
81
|
+
facesAway(triangle: number, facing: Vector3): boolean;
|
|
82
|
+
/** Evaluate the material at weights (`w1`, `w2`) of triangle `triangle`, seen from its back if `back`. */
|
|
83
|
+
shade(triangle: number, w1: number, w2: number, back: boolean): void;
|
|
84
|
+
/** Barycentric weights (into `out`, x = w1, y = w2) of `point` on triangle `triangle`. */
|
|
85
|
+
weights(triangle: number, point: Vector3, out: Vector3): Vector3;
|
|
86
|
+
material(triangle: number): MaterialInputs;
|
|
87
|
+
/** Vertex `k` of triangle `triangle`. */
|
|
88
|
+
private corner;
|
|
89
|
+
/** Edges e1, e2 of triangle `triangle` from its first corner. */
|
|
90
|
+
private corners;
|
|
91
|
+
private at;
|
|
92
|
+
private lerp;
|
|
93
|
+
private alpha;
|
|
94
|
+
/** `texels` at the current point, at the mip level where one of its texels covers one baked texel. */
|
|
95
|
+
private sample;
|
|
96
|
+
}
|