@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
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { cameraPosition, cameraProjectionMatrix, cameraViewMatrix, cameraWorldMatrix, clamp, dot, float, floor, length, max, min, modelWorldMatrix, normalize, positionGeometry, select, varyingProperty, vec2, vec3, vec4, } from "three/tsl";
|
|
2
|
+
import { affine, column, isOrthographic } from "../../tsl.js";
|
|
3
|
+
import { basisNode, encodeNode } from "../octahedral.js";
|
|
4
|
+
// What the surface reads of the card, interpolated across it: varyings the vertex stage
|
|
5
|
+
// writes (see `buildCard`). They are named once, so every stage and context that reads
|
|
6
|
+
// them shares them, whichever stage three builds first.
|
|
7
|
+
const written = {
|
|
8
|
+
/** the first frame, and which neighbour (1: along x) the second is */
|
|
9
|
+
frames: varyingProperty("vec3", "vImpostorFrames"),
|
|
10
|
+
weights: varyingProperty("vec3", "vImpostorWeights"),
|
|
11
|
+
point: varyingProperty("vec3", "vImpostorPoint"),
|
|
12
|
+
ray: varyingProperty("vec3", "vImpostorRay"),
|
|
13
|
+
worldPoint: varyingProperty("vec3", "vImpostorWorldPoint"),
|
|
14
|
+
worldRay: varyingProperty("vec3", "vImpostorWorldRay"),
|
|
15
|
+
normals: [varyingProperty("vec3", "vImpostorNormal0"), varyingProperty("vec3", "vImpostorNormal1"), varyingProperty("vec3", "vImpostorNormal2")],
|
|
16
|
+
};
|
|
17
|
+
const first = written.frames.xy;
|
|
18
|
+
const neighbour = written.frames.z;
|
|
19
|
+
/** The card as the surface reads it. */
|
|
20
|
+
export const card = {
|
|
21
|
+
/** the three frames around the view direction and their barycentric weights */
|
|
22
|
+
frames: [first, first.add(vec2(neighbour, neighbour.oneMinus())), first.add(1)],
|
|
23
|
+
weights: written.weights,
|
|
24
|
+
/** the view ray through the fragment, in the object's space: from `point`, along `ray` */
|
|
25
|
+
point: written.point,
|
|
26
|
+
ray: written.ray,
|
|
27
|
+
/** the same ray in world space: `ray` × s in object space is `worldRay` × s in the world */
|
|
28
|
+
worldPoint: written.worldPoint,
|
|
29
|
+
worldRay: written.worldRay,
|
|
30
|
+
/** an object-space normal in view space, as three's normal matrix turns a mesh's */
|
|
31
|
+
toView(normal) {
|
|
32
|
+
const [n0, n1, n2] = written.normals;
|
|
33
|
+
return normalize(cameraViewMatrix.mul(vec4(n0.mul(normal.x).add(n1.mul(normal.y)).add(n2.mul(normal.z)), 0)).xyz);
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* The card's vertex, in the mesh's space (after the instance matrix), for the instance
|
|
38
|
+
* placed by `instance`, `deform` (wind) applied in its space. Call it while the vertex
|
|
39
|
+
* stage builds the position: it writes the varyings `card` reads.
|
|
40
|
+
*/
|
|
41
|
+
export function buildCard(inputs, instance, deform) {
|
|
42
|
+
const { layout, centre, extent, radius, halfSize, frames, placement } = inputs;
|
|
43
|
+
const local = (deform ? instance.mul(deform) : instance).mul(placement).toVar();
|
|
44
|
+
const model = modelWorldMatrix.mul(local).toVar();
|
|
45
|
+
const toWorld = affine(model);
|
|
46
|
+
const eye = toWorld.unproject(cameraPosition).toVar();
|
|
47
|
+
// Orthographic, read off the projection: shadow passes of directional lights land
|
|
48
|
+
// here too, and the card faces the light.
|
|
49
|
+
const ortho = isOrthographic(cameraProjectionMatrix);
|
|
50
|
+
const back = column(cameraWorldMatrix, 2).xyz;
|
|
51
|
+
const toEye = eye.sub(centre);
|
|
52
|
+
const distance = length(toEye);
|
|
53
|
+
const view = select(ortho, normalize(toWorld.solve(back)), toEye.div(distance)).toVar();
|
|
54
|
+
// Up close, perspective spreads the near side of the object past the ortho frame.
|
|
55
|
+
const grow = select(ortho, float(1), min(distance.div(max(distance.sub(radius), 1e-4)), 4));
|
|
56
|
+
const { tangent, bitangent } = basisNode(view);
|
|
57
|
+
// The card covers the box as seen from here, plus the frame's empty border (the bake's
|
|
58
|
+
// PAD) for the filtered edge, and no more than the frame: every pixel of it runs the
|
|
59
|
+
// whole surface, and a square frame is mostly empty for a tall plant.
|
|
60
|
+
const box = vec2(dot(tangent.abs(), extent), dot(bitangent.abs(), extent)).add(halfSize.mul(0.06));
|
|
61
|
+
const size = min(box, vec2(halfSize, halfSize)).mul(grow.mul(2));
|
|
62
|
+
const point = centre.add(tangent.mul(positionGeometry.x.mul(size.x))).add(bitangent.mul(positionGeometry.y.mul(size.y))).toVar();
|
|
63
|
+
const last = frames.sub(1);
|
|
64
|
+
// Clamped: rounding can put a horizon view a hair outside the grid.
|
|
65
|
+
const grid = clamp(encodeNode(layout, view).mul(last), vec2(0, 0), vec2(last, last));
|
|
66
|
+
const cell = min(floor(grid), vec2(last.sub(1), last.sub(1)));
|
|
67
|
+
const f = grid.sub(cell);
|
|
68
|
+
const ray = select(ortho, view.negate(), point.sub(eye));
|
|
69
|
+
// Every vertex of a card holds the same frames and weights, so they interpolate to themselves.
|
|
70
|
+
written.frames.assign(vec3(cell, select(f.x.greaterThan(f.y), float(1), float(0))));
|
|
71
|
+
written.weights.assign(vec3(min(f.x.oneMinus(), f.y.oneMinus()), f.x.sub(f.y).abs(), min(f.x, f.y)));
|
|
72
|
+
written.point.assign(point);
|
|
73
|
+
written.ray.assign(ray);
|
|
74
|
+
written.worldPoint.assign(model.mul(vec4(point, 1)).xyz);
|
|
75
|
+
written.worldRay.assign(toWorld.apply(ray));
|
|
76
|
+
toWorld.normalColumns.forEach((n, i) => written.normals[i]?.assign(n));
|
|
77
|
+
return local.mul(vec4(point, 1)).xyz;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=card.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"card.js","sourceRoot":"","sources":["../../../src/impostor/mesh/card.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,EACL,GAAG,EACH,KAAK,EACL,KAAK,EACL,MAAM,EACN,GAAG,EACH,GAAG,EACH,gBAAgB,EAChB,SAAS,EACT,gBAAgB,EAChB,MAAM,EACN,eAAe,EACf,IAAI,EACJ,IAAI,EACJ,IAAI,GACL,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9D,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAwBzD,wFAAwF;AACxF,uFAAuF;AACvF,wDAAwD;AACxD,MAAM,OAAO,GAAG;IACd,sEAAsE;IACtE,MAAM,EAAE,eAAe,CAAC,MAAM,EAAE,iBAAiB,CAAC;IAClD,OAAO,EAAE,eAAe,CAAC,MAAM,EAAE,kBAAkB,CAAC;IACpD,KAAK,EAAE,eAAe,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAChD,GAAG,EAAE,eAAe,CAAC,MAAM,EAAE,cAAc,CAAC;IAC5C,UAAU,EAAE,eAAe,CAAC,MAAM,EAAE,qBAAqB,CAAC;IAC1D,QAAQ,EAAE,eAAe,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACtD,OAAO,EAAE,CAAC,eAAe,CAAC,MAAM,EAAE,kBAAkB,CAAC,EAAE,eAAe,CAAC,MAAM,EAAE,kBAAkB,CAAC,EAAE,eAAe,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAU;CAC1J,CAAC;AAEF,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;AAChC,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AAEnC,wCAAwC;AACxC,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB,+EAA+E;IAC/E,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC/E,OAAO,EAAE,OAAO,CAAC,OAAO;IACxB,0FAA0F;IAC1F,KAAK,EAAE,OAAO,CAAC,KAAK;IACpB,GAAG,EAAE,OAAO,CAAC,GAAG;IAChB,4FAA4F;IAC5F,UAAU,EAAE,OAAO,CAAC,UAAU;IAC9B,QAAQ,EAAE,OAAO,CAAC,QAAQ;IAC1B,oFAAoF;IACpF,MAAM,CAAC,MAAoB;QACzB,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;QACrC,OAAO,SAAS,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACpH,CAAC;CACO,CAAC;AAIX;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,MAAkB,EAAE,QAAsB,EAAE,MAA2B;IAC/F,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IAC/E,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,CAAC;IAChF,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC;IAClD,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9B,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,KAAK,EAAE,CAAC;IACtD,kFAAkF;IAClF,0CAA0C;IAC1C,MAAM,KAAK,GAAG,cAAc,CAAC,sBAAsB,CAAC,CAAC;IACrD,MAAM,IAAI,GAAG,MAAM,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;IAC9C,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;IACxF,kFAAkF;IAClF,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5F,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC/C,uFAAuF;IACvF,qFAAqF;IACrF,sEAAsE;IACtE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IACnG,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;IAEjI,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC3B,oEAAoE;IACpE,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IACrF,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9D,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzB,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAEzD,+FAA+F;IAC/F,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpF,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5B,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACxB,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5C,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Color, Matrix4, Mesh, PlaneGeometry, Sphere, StorageInstancedBufferAttribute, type Intersection, type Raycaster } from "three/webgpu";
|
|
2
|
+
import { type Impostor } from "../impostor.js";
|
|
3
|
+
import { ImpostorMaterial } from "./material.js";
|
|
4
|
+
export interface ImpostorMeshOptions {
|
|
5
|
+
/** coverage below which a pixel is empty, in (0, 1); default 0.5 */
|
|
6
|
+
alphaTest?: number;
|
|
7
|
+
/** depth-parallax refinements per frame before blending (0 = off); default 2 */
|
|
8
|
+
parallax?: number;
|
|
9
|
+
/**
|
|
10
|
+
* Depth disagreement (in depth-channel units, > 0) at which a frame's sample counts
|
|
11
|
+
* half in the blend; smaller suppresses ghosting harder. Default 0.03.
|
|
12
|
+
*/
|
|
13
|
+
tolerance?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Mip bias for atlas reads. Negative keeps thin features crisp at a distance; the
|
|
16
|
+
* dithered coverage absorbs the extra aliasing. Default -0.5.
|
|
17
|
+
*/
|
|
18
|
+
mipBias?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Draws an impostor once per instance, as an InstancedMesh draws a mesh: instance
|
|
22
|
+
* matrices are ordinary model transforms of the source object (identity = where the
|
|
23
|
+
* source was baked), set with `setMatrixAt`, and instance colours with `setColorAt`.
|
|
24
|
+
* Lighting, fog, shadows and colour go through three's MeshStandardNodeMaterial; meshes
|
|
25
|
+
* of the same impostor share its textures, and a clone shares its material: clones of
|
|
26
|
+
* one instance draw with one shader.
|
|
27
|
+
*
|
|
28
|
+
* It draws nothing of an instance into a directional light's shadow map whose texels are
|
|
29
|
+
* wider than the instance. Like an InstancedMesh, it is culled by `boundingSphere`,
|
|
30
|
+
* which `computeBoundingSphere` makes from the instances.
|
|
31
|
+
*/
|
|
32
|
+
export declare class ImpostorMesh extends Mesh<PlaneGeometry, ImpostorMaterial> {
|
|
33
|
+
readonly isImpostorMesh = true;
|
|
34
|
+
readonly impostor: Impostor;
|
|
35
|
+
/** The options it draws with. */
|
|
36
|
+
options: ImpostorMeshOptions;
|
|
37
|
+
/** Instances drawn. */
|
|
38
|
+
count: number;
|
|
39
|
+
/** 16 floats per instance; drawn from a storage buffer when there is more than one. */
|
|
40
|
+
instanceMatrix: StorageInstancedBufferAttribute;
|
|
41
|
+
/** RGB (and a pad) per instance, once `setColorAt` was called. */
|
|
42
|
+
instanceColor: StorageInstancedBufferAttribute | null;
|
|
43
|
+
boundingSphere: Sphere | null;
|
|
44
|
+
private disposed;
|
|
45
|
+
constructor(impostor: Impostor, count?: number, options?: ImpostorMeshOptions, material?: ImpostorMaterial);
|
|
46
|
+
/** Where the card sits within an instance's space (InstancedLod places it). */
|
|
47
|
+
get placement(): Matrix4;
|
|
48
|
+
setMatrixAt(index: number, matrix: Matrix4): void;
|
|
49
|
+
getMatrixAt(index: number, matrix: Matrix4): Matrix4;
|
|
50
|
+
setColorAt(index: number, color: Color): void;
|
|
51
|
+
getColorAt(index: number, color: Color): Color;
|
|
52
|
+
/** Bound every instance's bounding box of the source object. */
|
|
53
|
+
computeBoundingSphere(): void;
|
|
54
|
+
copy(source: ImpostorMesh, recursive?: boolean): this;
|
|
55
|
+
clone(recursive?: boolean): this;
|
|
56
|
+
/** Hits each instance's bounding box of the source object. */
|
|
57
|
+
raycast(raycaster: Raycaster, intersects: Intersection[]): void;
|
|
58
|
+
/** Frees its geometry and instance buffers; the last mesh sharing its material frees that too. */
|
|
59
|
+
dispose(): this;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* An ImpostorMesh like `template` for `count` instances, its card placed by `placement`
|
|
63
|
+
* within each instance's space (instance matrix × placement), as InstancedLod draws it.
|
|
64
|
+
* It has a material of its own: its placement is its own.
|
|
65
|
+
*/
|
|
66
|
+
export declare function impostorInstances(template: ImpostorMesh, count: number, placement: Matrix4): ImpostorMesh;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { Box3, Color, Matrix4, Mesh, PlaneGeometry, Ray, Sphere, StorageInstancedBufferAttribute, Vector3, } from "three/webgpu";
|
|
2
|
+
import { setDraw } from "../../fade.js";
|
|
3
|
+
import { sameClass } from "../../three.js";
|
|
4
|
+
import { copyWind } from "../../wind/index.js";
|
|
5
|
+
import { boundingRadius, validateImpostor } from "../impostor.js";
|
|
6
|
+
import { ImpostorMaterial } from "./material.js";
|
|
7
|
+
/** The options with defaults, validated. */
|
|
8
|
+
function shadingOf(options) {
|
|
9
|
+
const { alphaTest = 0.5, parallax = 2, tolerance = 0.03, mipBias = -0.5 } = options;
|
|
10
|
+
if (!(alphaTest > 0 && alphaTest < 1))
|
|
11
|
+
throw new Error(`ImpostorMesh: alphaTest must be in (0, 1), got ${alphaTest}`);
|
|
12
|
+
if (!Number.isInteger(parallax) || parallax < 0)
|
|
13
|
+
throw new Error(`ImpostorMesh: parallax must be an integer >= 0, got ${parallax}`);
|
|
14
|
+
if (!(tolerance > 0))
|
|
15
|
+
throw new Error(`ImpostorMesh: tolerance must be > 0, got ${tolerance}`);
|
|
16
|
+
if (!Number.isFinite(mipBias))
|
|
17
|
+
throw new Error(`ImpostorMesh: mipBias must be a number, got ${mipBias}`);
|
|
18
|
+
return { cutoff: alphaTest, parallax, tolerance, mipBias };
|
|
19
|
+
}
|
|
20
|
+
const scratch = new Matrix4();
|
|
21
|
+
const box = new Box3();
|
|
22
|
+
const sphere = new Sphere();
|
|
23
|
+
/**
|
|
24
|
+
* Draws an impostor once per instance, as an InstancedMesh draws a mesh: instance
|
|
25
|
+
* matrices are ordinary model transforms of the source object (identity = where the
|
|
26
|
+
* source was baked), set with `setMatrixAt`, and instance colours with `setColorAt`.
|
|
27
|
+
* Lighting, fog, shadows and colour go through three's MeshStandardNodeMaterial; meshes
|
|
28
|
+
* of the same impostor share its textures, and a clone shares its material: clones of
|
|
29
|
+
* one instance draw with one shader.
|
|
30
|
+
*
|
|
31
|
+
* It draws nothing of an instance into a directional light's shadow map whose texels are
|
|
32
|
+
* wider than the instance. Like an InstancedMesh, it is culled by `boundingSphere`,
|
|
33
|
+
* which `computeBoundingSphere` makes from the instances.
|
|
34
|
+
*/
|
|
35
|
+
export class ImpostorMesh extends Mesh {
|
|
36
|
+
isImpostorMesh = true;
|
|
37
|
+
impostor;
|
|
38
|
+
/** The options it draws with. */
|
|
39
|
+
options;
|
|
40
|
+
/** Instances drawn. */
|
|
41
|
+
count;
|
|
42
|
+
/** 16 floats per instance; drawn from a storage buffer when there is more than one. */
|
|
43
|
+
instanceMatrix;
|
|
44
|
+
/** RGB (and a pad) per instance, once `setColorAt` was called. */
|
|
45
|
+
instanceColor = null;
|
|
46
|
+
boundingSphere = null;
|
|
47
|
+
disposed = false;
|
|
48
|
+
constructor(impostor, count = 1, options = {}, material) {
|
|
49
|
+
validateImpostor(impostor);
|
|
50
|
+
if (material && material.impostor !== impostor)
|
|
51
|
+
throw new Error("ImpostorMesh: the material draws another impostor");
|
|
52
|
+
// The card is built in the shader; the geometry carries the source's bounds so
|
|
53
|
+
// culling and raycasting see the real object.
|
|
54
|
+
const geometry = new PlaneGeometry(1, 1);
|
|
55
|
+
geometry.boundingBox = new Box3().setFromArray(impostor.box);
|
|
56
|
+
geometry.boundingSphere = geometry.boundingBox.getBoundingSphere(new Sphere());
|
|
57
|
+
super(geometry, (material ?? new ImpostorMaterial(impostor, shadingOf(options))).acquire());
|
|
58
|
+
this.impostor = impostor;
|
|
59
|
+
this.options = options;
|
|
60
|
+
this.count = count;
|
|
61
|
+
this.instanceMatrix = new StorageInstancedBufferAttribute(count, 16);
|
|
62
|
+
for (let i = 0; i < count; i++)
|
|
63
|
+
scratch.identity().toArray(this.instanceMatrix.array, i * 16);
|
|
64
|
+
setDraw(this, { kind: "instances", fades: null, radius: boundingRadius(impostor.box), tint: new Color(1, 1, 1) });
|
|
65
|
+
}
|
|
66
|
+
/** Where the card sits within an instance's space (InstancedLod places it). */
|
|
67
|
+
get placement() {
|
|
68
|
+
return this.material.placement;
|
|
69
|
+
}
|
|
70
|
+
setMatrixAt(index, matrix) {
|
|
71
|
+
matrix.toArray(this.instanceMatrix.array, index * 16);
|
|
72
|
+
this.instanceMatrix.needsUpdate = true;
|
|
73
|
+
}
|
|
74
|
+
getMatrixAt(index, matrix) {
|
|
75
|
+
return matrix.fromArray(this.instanceMatrix.array, index * 16);
|
|
76
|
+
}
|
|
77
|
+
setColorAt(index, color) {
|
|
78
|
+
if (!this.instanceColor) {
|
|
79
|
+
this.instanceColor = new StorageInstancedBufferAttribute(new Float32Array(this.count * 4).fill(1), 4);
|
|
80
|
+
// Instance colours change the shader.
|
|
81
|
+
this.material.needsUpdate = true;
|
|
82
|
+
}
|
|
83
|
+
color.toArray(this.instanceColor.array, index * 4);
|
|
84
|
+
this.instanceColor.needsUpdate = true;
|
|
85
|
+
}
|
|
86
|
+
getColorAt(index, color) {
|
|
87
|
+
return this.instanceColor ? color.fromArray(this.instanceColor.array, index * 4) : color.setRGB(1, 1, 1);
|
|
88
|
+
}
|
|
89
|
+
/** Bound every instance's bounding box of the source object. */
|
|
90
|
+
computeBoundingSphere() {
|
|
91
|
+
this.boundingSphere ??= new Sphere();
|
|
92
|
+
this.boundingSphere.makeEmpty();
|
|
93
|
+
const own = box.setFromArray(this.impostor.box).getBoundingSphere(sphere);
|
|
94
|
+
for (let i = 0; i < this.count; i++)
|
|
95
|
+
this.boundingSphere.union(own.clone().applyMatrix4(this.getMatrixAt(i, scratch)));
|
|
96
|
+
}
|
|
97
|
+
copy(source, recursive) {
|
|
98
|
+
if (source.impostor !== this.impostor)
|
|
99
|
+
throw new Error("ImpostorMesh.copy: the source draws a different impostor");
|
|
100
|
+
if (source === this)
|
|
101
|
+
return this;
|
|
102
|
+
// Mesh.copy adopts the source's material and geometry; each card owns its geometry.
|
|
103
|
+
const { geometry, material } = this;
|
|
104
|
+
super.copy(source, recursive);
|
|
105
|
+
this.geometry = geometry;
|
|
106
|
+
if (material !== this.material) {
|
|
107
|
+
material.release();
|
|
108
|
+
this.material.acquire();
|
|
109
|
+
}
|
|
110
|
+
this.count = source.count;
|
|
111
|
+
this.options = source.options;
|
|
112
|
+
this.instanceMatrix.dispose();
|
|
113
|
+
this.instanceColor?.dispose();
|
|
114
|
+
this.instanceMatrix = new StorageInstancedBufferAttribute(source.instanceMatrix.array.slice(), 16);
|
|
115
|
+
this.instanceColor = source.instanceColor ? new StorageInstancedBufferAttribute(source.instanceColor.array.slice(), 4) : null;
|
|
116
|
+
this.material.needsUpdate = true;
|
|
117
|
+
this.boundingSphere = source.boundingSphere?.clone() ?? null;
|
|
118
|
+
copyWind(source, this);
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
121
|
+
clone(recursive) {
|
|
122
|
+
const clone = new ImpostorMesh(this.impostor, this.count, this.options, this.material).copy(this, recursive);
|
|
123
|
+
// three's clone builds `new this.constructor()`; a subclass takes other arguments.
|
|
124
|
+
if (!sameClass(this, clone))
|
|
125
|
+
throw new Error("ImpostorMesh.clone: a subclass must override clone");
|
|
126
|
+
return clone;
|
|
127
|
+
}
|
|
128
|
+
/** Hits each instance's bounding box of the source object. */
|
|
129
|
+
raycast(raycaster, intersects) {
|
|
130
|
+
box.setFromArray(this.impostor.box);
|
|
131
|
+
for (let i = 0; i < this.count; i++) {
|
|
132
|
+
this.getMatrixAt(i, matrix).multiply(this.placement).premultiply(this.matrixWorld);
|
|
133
|
+
inverse.copy(matrix).invert();
|
|
134
|
+
ray.copy(raycaster.ray).applyMatrix4(inverse);
|
|
135
|
+
if (!ray.intersectBox(box, point))
|
|
136
|
+
continue;
|
|
137
|
+
point.applyMatrix4(matrix);
|
|
138
|
+
const distance = raycaster.ray.origin.distanceTo(point);
|
|
139
|
+
if (distance < raycaster.near || distance > raycaster.far)
|
|
140
|
+
continue;
|
|
141
|
+
intersects.push({ distance, point: point.clone(), object: this, instanceId: i });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/** Frees its geometry and instance buffers; the last mesh sharing its material frees that too. */
|
|
145
|
+
dispose() {
|
|
146
|
+
if (this.disposed)
|
|
147
|
+
return this;
|
|
148
|
+
this.disposed = true;
|
|
149
|
+
this.geometry.dispose();
|
|
150
|
+
this.instanceMatrix.dispose();
|
|
151
|
+
this.instanceColor?.dispose();
|
|
152
|
+
this.material.release();
|
|
153
|
+
return this;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Scratch for raycasts.
|
|
157
|
+
const matrix = new Matrix4();
|
|
158
|
+
const inverse = new Matrix4();
|
|
159
|
+
const ray = new Ray();
|
|
160
|
+
const point = new Vector3();
|
|
161
|
+
/**
|
|
162
|
+
* An ImpostorMesh like `template` for `count` instances, its card placed by `placement`
|
|
163
|
+
* within each instance's space (instance matrix × placement), as InstancedLod draws it.
|
|
164
|
+
* It has a material of its own: its placement is its own.
|
|
165
|
+
*/
|
|
166
|
+
export function impostorInstances(template, count, placement) {
|
|
167
|
+
const mesh = new ImpostorMesh(template.impostor, count, template.options);
|
|
168
|
+
mesh.placement.copy(placement);
|
|
169
|
+
// A tint and mip bias set on the template, as for a level drawn by a Lod.
|
|
170
|
+
mesh.material.color.copy(template.material.color);
|
|
171
|
+
mesh.material.mipBias.value = template.material.mipBias.value;
|
|
172
|
+
return mesh;
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/impostor/mesh/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Matrix4, MeshStandardNodeMaterial, PhysicalLightingModel, type Node, type NodeBuilder } from "three/webgpu";
|
|
2
|
+
import { type Impostor } from "../impostor.js";
|
|
3
|
+
/** How an ImpostorMesh draws: see `ImpostorMeshOptions`. */
|
|
4
|
+
export interface ImpostorShading {
|
|
5
|
+
cutoff: number;
|
|
6
|
+
parallax: number;
|
|
7
|
+
tolerance: number;
|
|
8
|
+
mipBias: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Draws an impostor: the card (see card.ts) in the vertex stage and the surface (see
|
|
12
|
+
* surface.ts) in the fragment stage, lit as a MeshStandardMaterial lights a mesh, with
|
|
13
|
+
* the baked normal, occlusion, roughness, metalness and emission. Every pass builds its
|
|
14
|
+
* own card and surface: the view with parallax and, on a multisampled target, coverage
|
|
15
|
+
* to alpha-to-coverage; shadow maps without either, at the resolved surface's depth.
|
|
16
|
+
*/
|
|
17
|
+
export declare class ImpostorMaterial extends MeshStandardNodeMaterial {
|
|
18
|
+
readonly impostor: Impostor;
|
|
19
|
+
readonly shading: ImpostorShading;
|
|
20
|
+
readonly isImpostorMaterial = true;
|
|
21
|
+
/** Where the card sits within an instance's space; identity except under InstancedLod. */
|
|
22
|
+
readonly placement: Matrix4;
|
|
23
|
+
/** An object-space deformation of every instance (wind), applied before `placement`; null for none. */
|
|
24
|
+
deformNode: Node<"mat4"> | null;
|
|
25
|
+
/** Mip bias of every atlas read: see `ImpostorMeshOptions.mipBias`, which it starts at. */
|
|
26
|
+
readonly mipBias: Node<"float"> & {
|
|
27
|
+
value: number;
|
|
28
|
+
};
|
|
29
|
+
private readonly inputs;
|
|
30
|
+
/**
|
|
31
|
+
* Per build, one surface for every slot that reads it, made when the fragment stage
|
|
32
|
+
* first reads it: TSL adds a node's variables to the stage being built when the node
|
|
33
|
+
* is made. Keyed by what the build keeps for `anchor`: each slot gets its own proxy of
|
|
34
|
+
* the builder.
|
|
35
|
+
*/
|
|
36
|
+
private readonly surfaces;
|
|
37
|
+
private readonly anchor;
|
|
38
|
+
private users;
|
|
39
|
+
private disposed;
|
|
40
|
+
constructor(impostor: Impostor, shading: ImpostorShading);
|
|
41
|
+
/** One mesh starts or stops sharing this material, including when `copy` replaces it. */
|
|
42
|
+
acquire(): this;
|
|
43
|
+
release(): void;
|
|
44
|
+
/** Explicit disposal, including by a caller: release its texture acquisition only once. */
|
|
45
|
+
dispose(): void;
|
|
46
|
+
/** The card's vertex as `builder` draws it, in the vertex stage. */
|
|
47
|
+
private card;
|
|
48
|
+
/**
|
|
49
|
+
* The surface `builder` draws, as its pass needs it. No parallax walk in shadow passes:
|
|
50
|
+
* shadow maps cover far more impostor pixels than the view, and shadows are too soft to
|
|
51
|
+
* show it.
|
|
52
|
+
*/
|
|
53
|
+
private surface;
|
|
54
|
+
private lit;
|
|
55
|
+
setupLightingModel(): PhysicalLightingModel;
|
|
56
|
+
/** The baked dielectric F0 (RGB) and F90 (A), when the source changes three's defaults. */
|
|
57
|
+
setupSpecular(): void;
|
|
58
|
+
/**
|
|
59
|
+
* The view keeps the card's depth, so early depth testing keeps rejecting hidden pixels
|
|
60
|
+
* (several times faster in crowds); shadow maps get the surface's (`depthNode`).
|
|
61
|
+
*/
|
|
62
|
+
setupDepth(builder: NodeBuilder): void;
|
|
63
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { DoubleSide, Matrix4, MeshStandardNodeMaterial, PhysicalLightingModel, TSL, Vector3 } from "three/webgpu";
|
|
2
|
+
import { Fn, diffuseColor, float, instanceIndex, materialColor, metalness, mix, specularColor, specularF90, uniform, varying, varyingProperty, vec3, vec4, } from "three/tsl";
|
|
3
|
+
import { castPosition, instanceMatrixOf, isSingle, tint } from "../../fade.js";
|
|
4
|
+
import { isImpostorMesh, isShadowPass } from "../../three.js";
|
|
5
|
+
import { buildKey, instanceColors, isNodeOf } from "../../tsl.js";
|
|
6
|
+
import { boundingRadius, boxCentre, boxExtent } from "../impostor.js";
|
|
7
|
+
import { buildCard, card } from "./card.js";
|
|
8
|
+
import { buildSurface, cosine } from "./surface.js";
|
|
9
|
+
import { acquireTextures, releaseTextures } from "./textures.js";
|
|
10
|
+
/** The colour of an ImpostorMesh of one instance, white unless set, read per object as its matrix is (see `isSingle`). */
|
|
11
|
+
const singleColor = uniform(new Vector3(1, 1, 1)).onObjectUpdate(({ object }, self) => object && isImpostorMesh(object) && object.instanceColor ? self.value.fromArray(object.instanceColor.array) : self.value.set(1, 1, 1));
|
|
12
|
+
/** How `object`, an ImpostorMesh, places and colours its instances. */
|
|
13
|
+
function instancesOf(object) {
|
|
14
|
+
const matrix = instanceMatrixOf(object);
|
|
15
|
+
if (!matrix || !isImpostorMesh(object))
|
|
16
|
+
throw new Error(`ImpostorMesh: "${object.name}" is drawn with an impostor's material but is no ImpostorMesh`);
|
|
17
|
+
if (isSingle(object))
|
|
18
|
+
return { matrix, color: singleColor };
|
|
19
|
+
const { instanceColor } = object;
|
|
20
|
+
return { matrix, color: instanceColor && varying(instanceColors(instanceColor).element(instanceIndex).xyz).setInterpolation("flat") };
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Draws an impostor: the card (see card.ts) in the vertex stage and the surface (see
|
|
24
|
+
* surface.ts) in the fragment stage, lit as a MeshStandardMaterial lights a mesh, with
|
|
25
|
+
* the baked normal, occlusion, roughness, metalness and emission. Every pass builds its
|
|
26
|
+
* own card and surface: the view with parallax and, on a multisampled target, coverage
|
|
27
|
+
* to alpha-to-coverage; shadow maps without either, at the resolved surface's depth.
|
|
28
|
+
*/
|
|
29
|
+
export class ImpostorMaterial extends MeshStandardNodeMaterial {
|
|
30
|
+
impostor;
|
|
31
|
+
shading;
|
|
32
|
+
isImpostorMaterial = true;
|
|
33
|
+
/** Where the card sits within an instance's space; identity except under InstancedLod. */
|
|
34
|
+
placement = new Matrix4();
|
|
35
|
+
/** An object-space deformation of every instance (wind), applied before `placement`; null for none. */
|
|
36
|
+
deformNode = null;
|
|
37
|
+
/** Mip bias of every atlas read: see `ImpostorMeshOptions.mipBias`, which it starts at. */
|
|
38
|
+
mipBias;
|
|
39
|
+
inputs;
|
|
40
|
+
/**
|
|
41
|
+
* Per build, one surface for every slot that reads it, made when the fragment stage
|
|
42
|
+
* first reads it: TSL adds a node's variables to the stage being built when the node
|
|
43
|
+
* is made. Keyed by what the build keeps for `anchor`: each slot gets its own proxy of
|
|
44
|
+
* the builder.
|
|
45
|
+
*/
|
|
46
|
+
surfaces = new WeakMap();
|
|
47
|
+
anchor = float(0);
|
|
48
|
+
users = 0;
|
|
49
|
+
disposed = false;
|
|
50
|
+
constructor(impostor, shading) {
|
|
51
|
+
super({ roughness: 1, metalness: 1, alphaToCoverage: true, side: DoubleSide });
|
|
52
|
+
this.impostor = impostor;
|
|
53
|
+
this.shading = shading;
|
|
54
|
+
this.name = "impostor";
|
|
55
|
+
const textures = acquireTextures(impostor);
|
|
56
|
+
this.mipBias = uniform(shading.mipBias);
|
|
57
|
+
const layer = (map) => textures[impostor.maps.indexOf(map)] ?? null;
|
|
58
|
+
const base = layer("base");
|
|
59
|
+
const normalDepth = layer("normalDepth");
|
|
60
|
+
const orm = layer("orm");
|
|
61
|
+
if (!base || !normalDepth || !orm)
|
|
62
|
+
throw new Error("ImpostorMesh: the impostor lacks a base, normalDepth or orm layer");
|
|
63
|
+
this.inputs = {
|
|
64
|
+
layout: impostor.layout,
|
|
65
|
+
centre: uniform(new Vector3(...boxCentre(impostor.box))),
|
|
66
|
+
extent: uniform(new Vector3(...boxExtent(impostor.box))),
|
|
67
|
+
radius: uniform(boundingRadius(impostor.box)),
|
|
68
|
+
halfSize: uniform(impostor.halfSize),
|
|
69
|
+
frames: uniform(impostor.frames),
|
|
70
|
+
placement: uniform(this.placement),
|
|
71
|
+
cell: impostor.size / impostor.frames,
|
|
72
|
+
maps: { base, normalDepth, orm, emissive: layer("emissive") },
|
|
73
|
+
cutoff: shading.cutoff,
|
|
74
|
+
tolerance: shading.tolerance,
|
|
75
|
+
mipBias: this.mipBias,
|
|
76
|
+
};
|
|
77
|
+
this.positionNode = Fn((builder) => this.card(builder))();
|
|
78
|
+
this.castShadowPositionNode = Fn((builder) => castPosition(this.card(builder)))();
|
|
79
|
+
this.colorNode = Fn((builder) => {
|
|
80
|
+
const { color } = this.surface(builder);
|
|
81
|
+
// A shadow pass reads only the coverage, and its material has no colour.
|
|
82
|
+
if (isShadowPass(builder.material))
|
|
83
|
+
return color;
|
|
84
|
+
const rgb = color.rgb.mul(materialColor.rgb).mul(tint);
|
|
85
|
+
const instance = instancesOf(builder.object).color;
|
|
86
|
+
return vec4(instance ? rgb.mul(instance) : rgb, color.a);
|
|
87
|
+
})();
|
|
88
|
+
this.maskNode = Fn((builder) => this.surface(builder).drawn)();
|
|
89
|
+
this.maskShadowNode = this.maskNode;
|
|
90
|
+
// Shadow passes only (see setupDepth): maps hold the resolved surface, not the card.
|
|
91
|
+
this.depthNode = Fn((builder) => this.surface(builder).depth)();
|
|
92
|
+
this.receivedShadowPositionNode = Fn((builder) => this.surface(builder).shadowPosition)();
|
|
93
|
+
this.normalNode = Fn((builder) => this.lit(builder).normal)();
|
|
94
|
+
this.roughnessNode = Fn((builder) => this.lit(builder).roughness)();
|
|
95
|
+
this.metalnessNode = Fn((builder) => this.lit(builder).metalness)();
|
|
96
|
+
this.aoNode = Fn((builder) => this.lit(builder).occlusion)();
|
|
97
|
+
if (layer("emissive"))
|
|
98
|
+
this.emissiveNode = Fn((builder) => this.lit(builder).emissive ?? vec3(0))();
|
|
99
|
+
}
|
|
100
|
+
/** One mesh starts or stops sharing this material, including when `copy` replaces it. */
|
|
101
|
+
acquire() {
|
|
102
|
+
this.users++;
|
|
103
|
+
return this;
|
|
104
|
+
}
|
|
105
|
+
release() {
|
|
106
|
+
if (--this.users === 0)
|
|
107
|
+
this.dispose();
|
|
108
|
+
}
|
|
109
|
+
/** Explicit disposal, including by a caller: release its texture acquisition only once. */
|
|
110
|
+
dispose() {
|
|
111
|
+
if (this.disposed)
|
|
112
|
+
return;
|
|
113
|
+
this.disposed = true;
|
|
114
|
+
super.dispose();
|
|
115
|
+
releaseTextures(this.impostor);
|
|
116
|
+
}
|
|
117
|
+
/** The card's vertex as `builder` draws it, in the vertex stage. */
|
|
118
|
+
card(builder) {
|
|
119
|
+
// three multiplies the colour of any object with instance colours by its varying
|
|
120
|
+
// vInstanceColor, which only its InstancedMesh writes: white here, as the colour node
|
|
121
|
+
// applies them (the same way for every clone sharing this material).
|
|
122
|
+
if (isImpostorMesh(builder.object) && builder.object.instanceColor)
|
|
123
|
+
varyingProperty("vec3", "vInstanceColor").assign(vec3(1, 1, 1));
|
|
124
|
+
return buildCard(this.inputs, instancesOf(builder.object).matrix, this.deformNode);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* The surface `builder` draws, as its pass needs it. No parallax walk in shadow passes:
|
|
128
|
+
* shadow maps cover far more impostor pixels than the view, and shadows are too soft to
|
|
129
|
+
* show it.
|
|
130
|
+
*/
|
|
131
|
+
surface(builder) {
|
|
132
|
+
const build = buildKey(builder, this.anchor);
|
|
133
|
+
let surface = this.surfaces.get(build);
|
|
134
|
+
if (!surface) {
|
|
135
|
+
const shadow = isShadowPass(builder.material);
|
|
136
|
+
surface = buildSurface(this.inputs, card, {
|
|
137
|
+
parallax: shadow ? 0 : this.shading.parallax,
|
|
138
|
+
multisampled: !shadow && builder.renderer.currentSamples > 1,
|
|
139
|
+
lit: !shadow,
|
|
140
|
+
});
|
|
141
|
+
this.surfaces.set(build, surface);
|
|
142
|
+
}
|
|
143
|
+
return surface;
|
|
144
|
+
}
|
|
145
|
+
lit(builder) {
|
|
146
|
+
const lit = this.surface(builder).lit;
|
|
147
|
+
if (!lit)
|
|
148
|
+
throw new Error("ImpostorMesh: lighting read in a shadow pass");
|
|
149
|
+
return lit;
|
|
150
|
+
}
|
|
151
|
+
setupLightingModel() {
|
|
152
|
+
return new SpreadLighting(Fn((builder) => this.lit(builder).spread)());
|
|
153
|
+
}
|
|
154
|
+
/** The baked dielectric F0 (RGB) and F90 (A), when the source changes three's defaults. */
|
|
155
|
+
setupSpecular() {
|
|
156
|
+
const { specular } = this.impostor;
|
|
157
|
+
if (!specular)
|
|
158
|
+
return super.setupSpecular();
|
|
159
|
+
const f0 = vec3(specular[0], specular[1], specular[2]);
|
|
160
|
+
specularColor.assign(f0);
|
|
161
|
+
TSL.specularColorBlended.assign(mix(f0, diffuseColor.rgb, metalness));
|
|
162
|
+
specularF90.assign(specular[3]);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* The view keeps the card's depth, so early depth testing keeps rejecting hidden pixels
|
|
166
|
+
* (several times faster in crowds); shadow maps get the surface's (`depthNode`).
|
|
167
|
+
*/
|
|
168
|
+
setupDepth(builder) {
|
|
169
|
+
const surface = this.depthNode;
|
|
170
|
+
this.depthNode = null;
|
|
171
|
+
super.setupDepth(builder);
|
|
172
|
+
this.depthNode = surface;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Direct diffuse light by `cosine` instead of the clamped cosine of the mean normal. That
|
|
177
|
+
* one leaves a canopy unlit wherever its mean normal is side-on to the sun, where the real
|
|
178
|
+
* leaves, tilted every which way, catch a good part of it; renormalised, the mean lights
|
|
179
|
+
* the canopy as one smooth surface. Specular keeps three's terms: its roughness widens
|
|
180
|
+
* with the spread instead.
|
|
181
|
+
*/
|
|
182
|
+
class SpreadLighting extends PhysicalLightingModel {
|
|
183
|
+
spread;
|
|
184
|
+
constructor(spread) {
|
|
185
|
+
super();
|
|
186
|
+
this.spread = spread;
|
|
187
|
+
}
|
|
188
|
+
direct(input, builder) {
|
|
189
|
+
const { lightDirection, lightColor, reflectedLight } = input;
|
|
190
|
+
const { directDiffuse } = reflectedLight;
|
|
191
|
+
if (!isNodeOf(lightDirection, "vec3", builder) || !isNodeOf(lightColor, "color", builder) || !isNodeOf(directDiffuse, "vec3", builder))
|
|
192
|
+
throw new Error("ImpostorMesh: three's direct light is not a direction, a colour and a sum");
|
|
193
|
+
// three's terms, its diffuse into a throwaway.
|
|
194
|
+
super.direct({ ...input, reflectedLight: { ...reflectedLight, directDiffuse: vec3(0).toVar() } }, builder);
|
|
195
|
+
// Lambert: the diffuse colour over π.
|
|
196
|
+
directDiffuse.addAssign(cosine(this.spread, lightDirection).mul(lightColor).mul(TSL.diffuseContribution).div(Math.PI));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=material.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"material.js","sourceRoot":"","sources":["../../../src/impostor/mesh/material.ts"],"names":[],"mappings":"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"}
|