@drawcall/lod 0.1.2 → 0.3.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/README.md +13 -70
- package/dist/at.d.ts +2 -0
- package/dist/at.js +8 -0
- package/dist/at.js.map +1 -0
- package/dist/bake.d.ts +22 -0
- package/dist/bake.js +88 -0
- package/dist/bake.js.map +1 -0
- package/dist/baked/codec.d.ts +5 -5
- package/dist/baked/codec.js +58 -158
- package/dist/baked/codec.js.map +1 -1
- package/dist/baked/coverage.d.ts +6 -3
- package/dist/baked/coverage.js +6 -3
- package/dist/baked/coverage.js.map +1 -1
- package/dist/baked/fields.d.ts +9 -0
- package/dist/baked/fields.js +22 -0
- package/dist/baked/fields.js.map +1 -0
- package/dist/baked/index.d.ts +16 -6
- package/dist/baked/index.js +23 -21
- package/dist/baked/index.js.map +1 -1
- package/dist/baked/interior/faces.d.ts +4 -3
- package/dist/baked/interior/faces.js +13 -10
- package/dist/baked/interior/faces.js.map +1 -1
- package/dist/baked/interior/material.d.ts +7 -10
- package/dist/baked/interior/material.js +29 -36
- package/dist/baked/interior/material.js.map +1 -1
- package/dist/baked/interior/rooms.js +1 -1
- package/dist/baked/interior/rooms.js.map +1 -1
- package/dist/baked/low.d.ts +2 -2
- package/dist/baked/low.js +9 -8
- package/dist/baked/low.js.map +1 -1
- package/dist/baked/raster.js +6 -6
- package/dist/baked/raster.js.map +1 -1
- package/dist/baked/textures.d.ts +17 -0
- package/dist/baked/textures.js +95 -0
- package/dist/baked/textures.js.map +1 -0
- package/dist/baked/transfer.js +19 -18
- package/dist/baked/transfer.js.map +1 -1
- package/dist/baked/unwrap.js +4 -3
- package/dist/baked/unwrap.js.map +1 -1
- package/dist/baked/xatlas.js.map +1 -1
- package/dist/codec/container.d.ts +8 -0
- package/dist/codec/container.js +63 -0
- package/dist/codec/container.js.map +1 -0
- package/dist/codec/mesh.d.ts +8 -0
- package/dist/codec/mesh.js +220 -0
- package/dist/codec/mesh.js.map +1 -0
- package/dist/dispose.d.ts +11 -4
- package/dist/dispose.js +39 -12
- package/dist/dispose.js.map +1 -1
- package/dist/gpu.d.ts +39 -0
- package/dist/gpu.js +41 -0
- package/dist/gpu.js.map +1 -0
- package/dist/impostor/bake/atlas.d.ts +4 -4
- package/dist/impostor/bake/atlas.js +12 -12
- package/dist/impostor/bake/atlas.js.map +1 -1
- package/dist/impostor/bake/fit.d.ts +8 -0
- package/dist/impostor/bake/fit.js +28 -0
- package/dist/impostor/bake/fit.js.map +1 -0
- package/dist/impostor/bake/index.d.ts +23 -14
- package/dist/impostor/bake/index.js +42 -134
- package/dist/impostor/bake/index.js.map +1 -1
- package/dist/impostor/bake/materials.d.ts +3 -3
- package/dist/impostor/bake/materials.js +60 -12
- package/dist/impostor/bake/materials.js.map +1 -1
- package/dist/impostor/bake/sheet.d.ts +8 -0
- package/dist/impostor/bake/sheet.js +90 -0
- package/dist/impostor/bake/sheet.js.map +1 -0
- package/dist/impostor/bake/source.d.ts +2 -2
- package/dist/impostor/bake/source.js +8 -19
- package/dist/impostor/bake/source.js.map +1 -1
- package/dist/impostor/codec.d.ts +5 -11
- package/dist/impostor/codec.js +37 -121
- package/dist/impostor/codec.js.map +1 -1
- package/dist/impostor/impostor.d.ts +31 -7
- package/dist/impostor/impostor.js +21 -6
- package/dist/impostor/impostor.js.map +1 -1
- package/dist/impostor/ktx2/encode.d.ts +26 -0
- package/dist/impostor/ktx2/encode.js +117 -0
- package/dist/impostor/ktx2/encode.js.map +1 -0
- package/dist/impostor/ktx2/transcode.d.ts +3 -0
- package/dist/impostor/ktx2/transcode.js +99 -0
- package/dist/impostor/ktx2/transcode.js.map +1 -0
- package/dist/impostor/ktx2/uastc.d.ts +7 -0
- package/dist/impostor/ktx2/uastc.js +64 -0
- package/dist/impostor/ktx2/uastc.js.map +1 -0
- package/dist/impostor/ktx2/wasm/encoder.d.ts +4 -0
- package/dist/impostor/ktx2/wasm/encoder.js +10 -0
- package/dist/impostor/ktx2/wasm/encoder.js.map +1 -0
- package/dist/impostor/ktx2/wasm/transcoder.d.ts +3 -0
- package/dist/impostor/ktx2/wasm/transcoder.js +9 -0
- package/dist/impostor/ktx2/wasm/transcoder.js.map +1 -0
- package/dist/impostor/ktx2/worker.d.ts +1 -0
- package/dist/impostor/ktx2/worker.js +12 -0
- package/dist/impostor/ktx2/worker.js.map +1 -0
- package/dist/impostor/mesh/card.d.ts +3 -3
- package/dist/impostor/mesh/card.js +11 -8
- package/dist/impostor/mesh/card.js.map +1 -1
- package/dist/impostor/mesh/index.d.ts +10 -7
- package/dist/impostor/mesh/index.js +15 -18
- package/dist/impostor/mesh/index.js.map +1 -1
- package/dist/impostor/mesh/material.d.ts +24 -13
- package/dist/impostor/mesh/material.js +67 -51
- package/dist/impostor/mesh/material.js.map +1 -1
- package/dist/impostor/mesh/surface.d.ts +20 -16
- package/dist/impostor/mesh/surface.js +81 -58
- package/dist/impostor/mesh/surface.js.map +1 -1
- package/dist/impostor/mesh/textures.d.ts +3 -3
- package/dist/impostor/mesh/textures.js +24 -34
- package/dist/impostor/mesh/textures.js.map +1 -1
- package/dist/impostor/octahedral.d.ts +1 -8
- package/dist/impostor/octahedral.js +3 -18
- package/dist/impostor/octahedral.js.map +1 -1
- package/dist/index.d.ts +16 -10
- package/dist/index.js +14 -8
- package/dist/index.js.map +1 -1
- package/dist/instanced/camera.d.ts +46 -0
- package/dist/instanced/camera.js +70 -0
- package/dist/instanced/camera.js.map +1 -0
- package/dist/instanced/chunk.d.ts +97 -0
- package/dist/instanced/chunk.js +156 -0
- package/dist/instanced/chunk.js.map +1 -0
- package/dist/instanced/chunks.d.ts +60 -0
- package/dist/instanced/chunks.js +208 -0
- package/dist/instanced/chunks.js.map +1 -0
- package/dist/instanced/cull.d.ts +28 -0
- package/dist/instanced/cull.js +22 -0
- package/dist/instanced/cull.js.map +1 -0
- package/dist/instanced/index.d.ts +96 -92
- package/dist/instanced/index.js +229 -512
- package/dist/instanced/index.js.map +1 -1
- package/dist/instanced/key.d.ts +19 -0
- package/dist/instanced/key.js +45 -0
- package/dist/instanced/key.js.map +1 -0
- package/dist/instanced/levels.d.ts +60 -0
- package/dist/instanced/levels.js +130 -0
- package/dist/instanced/levels.js.map +1 -0
- package/dist/instanced/lists.d.ts +70 -0
- package/dist/instanced/lists.js +133 -0
- package/dist/instanced/lists.js.map +1 -0
- package/dist/instanced/measure.d.ts +28 -0
- package/dist/instanced/measure.js +203 -0
- package/dist/instanced/measure.js.map +1 -0
- package/dist/instanced/meshes.d.ts +46 -14
- package/dist/instanced/meshes.js +137 -22
- package/dist/instanced/meshes.js.map +1 -1
- package/dist/instanced/mirror.d.ts +20 -0
- package/dist/instanced/mirror.js +37 -0
- package/dist/instanced/mirror.js.map +1 -0
- package/dist/instanced/pages/buffers.d.ts +37 -0
- package/dist/instanced/pages/buffers.js +15 -0
- package/dist/instanced/pages/buffers.js.map +1 -0
- package/dist/instanced/pages/chunk.d.ts +51 -0
- package/dist/instanced/pages/chunk.js +81 -0
- package/dist/instanced/pages/chunk.js.map +1 -0
- package/dist/instanced/pages/index.d.ts +74 -0
- package/dist/instanced/pages/index.js +269 -0
- package/dist/instanced/pages/index.js.map +1 -0
- package/dist/instanced/pages/passes.d.ts +21 -0
- package/dist/instanced/pages/passes.js +52 -0
- package/dist/instanced/pages/passes.js.map +1 -0
- package/dist/instanced/pages/slots.d.ts +11 -0
- package/dist/instanced/pages/slots.js +90 -0
- package/dist/instanced/pages/slots.js.map +1 -0
- package/dist/instanced/pages/workgroup.d.ts +14 -0
- package/dist/instanced/pages/workgroup.js +38 -0
- package/dist/instanced/pages/workgroup.js.map +1 -0
- package/dist/instanced/raycast.d.ts +15 -0
- package/dist/instanced/raycast.js +46 -0
- package/dist/instanced/raycast.js.map +1 -0
- package/dist/instanced/rows.d.ts +108 -0
- package/dist/instanced/rows.js +209 -0
- package/dist/instanced/rows.js.map +1 -0
- package/dist/instanced/shape.d.ts +25 -0
- package/dist/instanced/shape.js +51 -0
- package/dist/instanced/shape.js.map +1 -0
- package/dist/instanced/sort.d.ts +63 -0
- package/dist/instanced/sort.js +162 -0
- package/dist/instanced/sort.js.map +1 -0
- package/dist/instanced/update.d.ts +30 -0
- package/dist/instanced/update.js +207 -0
- package/dist/instanced/update.js.map +1 -0
- package/dist/levels/bounds.d.ts +21 -0
- package/dist/levels/bounds.js +42 -0
- package/dist/levels/bounds.js.map +1 -0
- package/dist/levels/cameras.d.ts +48 -0
- package/dist/levels/cameras.js +122 -0
- package/dist/levels/cameras.js.map +1 -0
- package/dist/levels/compile/contexts.d.ts +30 -0
- package/dist/levels/compile/contexts.js +18 -0
- package/dist/levels/compile/contexts.js.map +1 -0
- package/dist/levels/compile/index.d.ts +39 -0
- package/dist/levels/compile/index.js +185 -0
- package/dist/levels/compile/index.js.map +1 -0
- package/dist/levels/compile/pipelines.d.ts +26 -0
- package/dist/levels/compile/pipelines.js +177 -0
- package/dist/levels/compile/pipelines.js.map +1 -0
- package/dist/levels/compiles.d.ts +29 -0
- package/dist/levels/compiles.js +50 -0
- package/dist/levels/compiles.js.map +1 -0
- package/dist/levels/draw.d.ts +102 -0
- package/dist/levels/draw.js +401 -0
- package/dist/levels/draw.js.map +1 -0
- package/dist/levels/fade.d.ts +69 -0
- package/dist/levels/fade.js +150 -0
- package/dist/levels/fade.js.map +1 -0
- package/dist/levels/index.d.ts +99 -0
- package/dist/levels/index.js +155 -0
- package/dist/levels/index.js.map +1 -0
- package/dist/levels/item.d.ts +38 -0
- package/dist/levels/item.js +97 -0
- package/dist/levels/item.js.map +1 -0
- package/dist/levels/load.d.ts +31 -0
- package/dist/levels/load.js +142 -0
- package/dist/levels/load.js.map +1 -0
- package/dist/levels/manager.d.ts +159 -0
- package/dist/levels/manager.js +339 -0
- package/dist/levels/manager.js.map +1 -0
- package/dist/{material.js → levels/material.js} +13 -7
- package/dist/levels/material.js.map +1 -0
- package/dist/levels/renderer.d.ts +29 -0
- package/dist/levels/renderer.js +41 -0
- package/dist/levels/renderer.js.map +1 -0
- package/dist/levels/requests.d.ts +49 -0
- package/dist/levels/requests.js +95 -0
- package/dist/levels/requests.js.map +1 -0
- package/dist/levels/screen.d.ts +14 -0
- package/dist/levels/screen.js +21 -0
- package/dist/levels/screen.js.map +1 -0
- package/dist/levels/wait.d.ts +27 -0
- package/dist/levels/wait.js +61 -0
- package/dist/levels/wait.js.map +1 -0
- package/dist/loader/build.d.ts +10 -0
- package/dist/loader/build.js +28 -0
- package/dist/loader/build.js.map +1 -0
- package/dist/loader/cache.d.ts +12 -15
- package/dist/loader/cache.js +64 -33
- package/dist/loader/cache.js.map +1 -1
- package/dist/loader/context.d.ts +46 -0
- package/dist/loader/context.js +91 -0
- package/dist/loader/context.js.map +1 -0
- package/dist/loader/dependencies.d.ts +32 -0
- package/dist/loader/dependencies.js +113 -0
- package/dist/loader/dependencies.js.map +1 -0
- package/dist/loader/index.d.ts +62 -0
- package/dist/loader/index.js +284 -0
- package/dist/loader/index.js.map +1 -0
- package/dist/loader/level.d.ts +41 -0
- package/dist/loader/level.js +140 -0
- package/dist/loader/level.js.map +1 -0
- package/dist/loader/request.d.ts +87 -0
- package/dist/loader/request.js +59 -0
- package/dist/loader/request.js.map +1 -0
- package/dist/loader/url.d.ts +16 -0
- package/dist/loader/url.js +35 -0
- package/dist/loader/url.js.map +1 -0
- package/dist/lod.d.ts +79 -49
- package/dist/lod.js +209 -153
- package/dist/lod.js.map +1 -1
- package/dist/meshopt.d.ts +6 -0
- package/dist/meshopt.js +19 -0
- package/dist/meshopt.js.map +1 -0
- package/dist/simplify/cards.d.ts +14 -0
- package/dist/simplify/cards.js +128 -0
- package/dist/simplify/cards.js.map +1 -0
- package/dist/{simplify.d.ts → simplify/index.d.ts} +7 -4
- package/dist/{simplify.js → simplify/index.js} +42 -22
- package/dist/simplify/index.js.map +1 -0
- package/dist/{bounds.d.ts → source/detail.d.ts} +4 -13
- package/dist/{bounds.js → source/detail.js} +62 -32
- package/dist/source/detail.js.map +1 -0
- package/dist/source/{error.d.ts → deviation.d.ts} +3 -3
- package/dist/source/{error.js → deviation.js} +12 -12
- package/dist/source/deviation.js.map +1 -0
- package/dist/source/{shading.d.ts → evaluator.d.ts} +10 -10
- package/dist/source/{shading.js → evaluator.js} +5 -5
- package/dist/source/evaluator.js.map +1 -0
- package/dist/source/{trace.d.ts → raycaster.d.ts} +6 -6
- package/dist/source/{trace.js → raycaster.js} +8 -8
- package/dist/source/raycaster.js.map +1 -0
- package/dist/source/{texels.d.ts → sampler.d.ts} +1 -1
- package/dist/source/{texels.js → sampler.js} +3 -3
- package/dist/source/sampler.js.map +1 -0
- package/dist/source/source.d.ts +2 -2
- package/dist/source/source.js +2 -2
- package/dist/source/source.js.map +1 -1
- package/dist/source/surface.js +1 -1
- package/dist/source/surface.js.map +1 -1
- package/dist/task.d.ts +6 -0
- package/dist/{pause.js → task.js} +5 -5
- package/dist/task.js.map +1 -0
- package/dist/three.d.ts +20 -19
- package/dist/three.js +29 -35
- package/dist/three.js.map +1 -1
- package/dist/tsl.d.ts +10 -14
- package/dist/tsl.js +12 -47
- package/dist/tsl.js.map +1 -1
- package/dist/typegpu.d.ts +37 -0
- package/dist/typegpu.js +37 -0
- package/dist/typegpu.js.map +1 -0
- package/dist/wind/bounds.js +2 -2
- package/dist/wind/bounds.js.map +1 -1
- package/dist/wind/hooks.d.ts +7 -4
- package/dist/wind/hooks.js +5 -2
- package/dist/wind/hooks.js.map +1 -1
- package/dist/wind/index.d.ts +4 -4
- package/dist/wind/index.js +43 -40
- package/dist/wind/index.js.map +1 -1
- package/dist/wind/sway.d.ts +4 -1
- package/dist/wind/sway.js +4 -1
- package/dist/wind/sway.js.map +1 -1
- package/package.json +24 -8
- package/skills/lod/SKILL.md +147 -108
- package/dist/bounds.js.map +0 -1
- package/dist/fade.d.ts +0 -44
- package/dist/fade.js +0 -78
- package/dist/fade.js.map +0 -1
- package/dist/impostor/cache.d.ts +0 -15
- package/dist/impostor/cache.js +0 -44
- package/dist/impostor/cache.js.map +0 -1
- package/dist/instanced/args.d.ts +0 -20
- package/dist/instanced/args.js +0 -77
- package/dist/instanced/args.js.map +0 -1
- package/dist/instanced/bounds.d.ts +0 -50
- package/dist/instanced/bounds.js +0 -271
- package/dist/instanced/bounds.js.map +0 -1
- package/dist/instanced/dirty.d.ts +0 -12
- package/dist/instanced/dirty.js +0 -61
- package/dist/instanced/dirty.js.map +0 -1
- package/dist/instanced/select.d.ts +0 -84
- package/dist/instanced/select.js +0 -264
- package/dist/instanced/select.js.map +0 -1
- package/dist/lazy.d.ts +0 -64
- package/dist/lazy.js +0 -187
- package/dist/lazy.js.map +0 -1
- package/dist/levels.d.ts +0 -72
- package/dist/levels.js +0 -146
- package/dist/levels.js.map +0 -1
- package/dist/loader/dispose.d.ts +0 -3
- package/dist/loader/dispose.js +0 -25
- package/dist/loader/dispose.js.map +0 -1
- package/dist/loader.d.ts +0 -99
- package/dist/loader.js +0 -234
- package/dist/loader.js.map +0 -1
- package/dist/material.js.map +0 -1
- package/dist/pause.d.ts +0 -6
- package/dist/pause.js.map +0 -1
- package/dist/renderer.d.ts +0 -21
- package/dist/renderer.js +0 -83
- package/dist/renderer.js.map +0 -1
- package/dist/simplify.js.map +0 -1
- package/dist/source/error.js.map +0 -1
- package/dist/source/shading.js.map +0 -1
- package/dist/source/texels.js.map +0 -1
- package/dist/source/trace.js.map +0 -1
- package/dist/view.d.ts +0 -30
- package/dist/view.js +0 -75
- package/dist/view.js.map +0 -1
- /package/dist/{material.d.ts → levels/material.d.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drawcall/lod",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Level of detail for three.js: octahedral impostors, simplified and baked meshes, with dithered switching by on-screen size. WebGPURenderer only.",
|
|
6
6
|
"keywords": [
|
|
@@ -29,24 +29,34 @@
|
|
|
29
29
|
"LICENSE"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@drawcall/glts": "^2.0.0",
|
|
33
32
|
"fflate": "^0.8.2",
|
|
34
33
|
"ktx-parse": "^2.0.0",
|
|
35
34
|
"meshoptimizer": "^1.3.0",
|
|
36
35
|
"three-mesh-bvh": "^0.9.15",
|
|
37
|
-
"vitecache": "^0.1
|
|
36
|
+
"vitecache": "^0.2.1"
|
|
38
37
|
},
|
|
39
38
|
"peerDependencies": {
|
|
40
|
-
"
|
|
39
|
+
"@drawcall/glts": "^4.1.0",
|
|
40
|
+
"three": ">=0.186.0",
|
|
41
|
+
"typegpu": "^0.12.9"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
44
|
+
"@babel/core": "^7.29.13",
|
|
45
|
+
"@drawcall/glts": "^4.1.0",
|
|
46
|
+
"@drawcall/lod": "link:",
|
|
47
|
+
"@typegpu/gl": "^0.12.9",
|
|
48
|
+
"@typegpu/three": "^0.12.2",
|
|
49
|
+
"@types/babel__core": "^7.20.5",
|
|
43
50
|
"@types/node": "^24.13.6",
|
|
44
|
-
"@types/three": "^0.
|
|
51
|
+
"@types/three": "^0.186.0",
|
|
45
52
|
"@vitest/browser-playwright": "4.1.11",
|
|
46
53
|
"@webgpu/types": "^0.1.74",
|
|
54
|
+
"ktx2-encoder": "0.6.0",
|
|
47
55
|
"playwright": "1.63.0",
|
|
48
|
-
"three": "^0.
|
|
56
|
+
"three": "^0.186.1",
|
|
57
|
+
"typegpu": "0.12.9",
|
|
49
58
|
"typescript": "^5.9.3",
|
|
59
|
+
"unplugin-typegpu": "^0.12.8",
|
|
50
60
|
"vitest": "^4.0.17",
|
|
51
61
|
"xatlas-wasm": "0.1.3"
|
|
52
62
|
},
|
|
@@ -63,10 +73,16 @@
|
|
|
63
73
|
"publishConfig": {
|
|
64
74
|
"access": "public"
|
|
65
75
|
},
|
|
76
|
+
"peerDependenciesMeta": {
|
|
77
|
+
"@drawcall/glts": {
|
|
78
|
+
"optional": true
|
|
79
|
+
}
|
|
80
|
+
},
|
|
66
81
|
"scripts": {
|
|
67
|
-
"build": "rm -rf dist && tsc -p tsconfig.json",
|
|
82
|
+
"build": "rm -rf dist && tsc -p tsconfig.json && node --experimental-strip-types --no-warnings scripts/typegpu.ts",
|
|
68
83
|
"typecheck": "tsc -p tsconfig.test.json",
|
|
69
84
|
"test": "vitest run",
|
|
70
|
-
"xatlas": "node --experimental-strip-types --no-warnings scripts/xatlas.ts"
|
|
85
|
+
"xatlas": "node --experimental-strip-types --no-warnings scripts/xatlas.ts",
|
|
86
|
+
"basis": "node --experimental-strip-types --no-warnings scripts/basis.ts"
|
|
71
87
|
}
|
|
72
88
|
}
|
package/skills/lod/SKILL.md
CHANGED
|
@@ -1,137 +1,176 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lod
|
|
3
|
-
description: Use @drawcall/lod
|
|
3
|
+
description: Use @drawcall/lod for cached, progressively loaded levels of detail in three.js WebGPURenderer scenes. Covers model and procedural assets, mesh simplification, baked meshes, impostors, instanced copies, wind, interior mapping, asset preparation and capturing fully loaded frames; not WebGLRenderer or skinned animation.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Level of detail with @drawcall/lod
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## Start with the loader
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
- **Many copies of one object → `InstancedLod`** (`loadInstancesAsync`, or built from levels): every copy switches and fades on its own, with one draw per level and mesh.
|
|
12
|
-
- **Levels you already have → `new Lod([source, ...levels])`**: any `Object3D` per level, another `Lod` included, detailed to cheap. Levels made by this package know how far off they are and pick their own switch size; hand-made ones need a `maxPixels`.
|
|
13
|
-
- **Only ever far away, never switching → an `ImpostorMesh`** from `bakeImpostor`: one instanced draw for thousands of copies.
|
|
10
|
+
Give the host's `LodLoader` an asset URL and the representations to derive from it. Use `.glb` or `.gltf` for stored models and `.glts` for procedural assets. Put generation parameters in the asset URL's query: each distinct query is a distinct cache identity. Build a `Lod` from the returned levels for an independently placed object, or an `InstancedLod` for repeated objects, whose instances arrive and leave in chunks of matrices and colours, from the CPU or from a compute pass of yours.
|
|
14
11
|
|
|
15
|
-
|
|
12
|
+
Make a `LodGpu` once the renderer is initialised and give it to every `Lod` and `InstancedLod` drawing with that renderer. Every InstancedLod of one LodGpu sorts for a camera in one compute call. A camera that draws only a few InstancedLods, such as a minimap's, can give those a LodGpu of their own, which bounds the per-camera list memory. `LodLoader` needs no renderer.
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
`loadAsync` resolves with the ladder before its levels load; each level loads when a camera first needs it. On a cold cache a derived level is built when it first loads, so a cold far camera builds only the coarse levels it needs. Give simplified and baked levels an explicit `maxPixels`: without one, a level is built with the ladder to measure its default. A simplified level also loads the source for its original materials; a source-only ladder loads its source eagerly. Prefer baked or impostor middle levels for an expensive `.glts`: a simplified level brings the script run forward to that viewing range.
|
|
15
|
+
|
|
16
|
+
Choose levels from the asset's silhouette, materials and expected viewing range. Alpha-tested leaf cards often work best as source → impostor; a simplified level has fewer of them, whole and enlarged to the same coverage, so at a low ratio its foliage reads coarser than the source up close. Hard-surface props often suit source → simplified → impostor. Detailed structures can use source → baked shell → impostor, with interiors supplied separately for windows. A very heavy source, such as a two-million-triangle scan, needs a simplified level between source and impostor, or every copy in mid range draws the full mesh. Add an empty final level when the object should disappear at distance.
|
|
17
|
+
|
|
18
|
+
Register the source loaders on the loading manager before loading: a `GLTSLoader` for `.glts` URLs, and a `GLTFLoader` with the decoders for models compressed with Draco or KTX2. Follow the [GLTS skill](https://github.com/drawcall-ai/glts/blob/main/packages/glts/skills/glts/SKILL.md) for the Vite plugin, script authoring and resource lifetimes. The package carries its own workers and WebAssembly; it needs no Vite configuration beyond the vitecache plugin.
|
|
19
|
+
|
|
20
|
+
This example draws many copies of one procedural asset. `positions` contains their placements in the InstancedLod's space, `renderer` is the application's WebGPURenderer and `scene` its scene.
|
|
22
21
|
|
|
23
22
|
```ts
|
|
24
|
-
import
|
|
25
|
-
import {
|
|
23
|
+
import { InstancedBufferAttribute, LoadingManager, Matrix4 } from "three/webgpu";
|
|
24
|
+
import { GLTSLoader } from "@drawcall/glts";
|
|
25
|
+
import { InstancedLod, LodGpu, LodLoader } from "@drawcall/lod";
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const loader = new LodLoader(manager);
|
|
27
|
+
await renderer.init();
|
|
28
|
+
const gpu = new LodGpu(renderer);
|
|
30
29
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
const manager = new LoadingManager();
|
|
31
|
+
manager.addHandler(/\.glts$/, new GLTSLoader(manager));
|
|
32
|
+
const loader = new LodLoader(manager);
|
|
33
|
+
const levels = await loader.loadAsync({
|
|
34
|
+
url: "/trees/oak.glts?seed=2",
|
|
34
35
|
levels: [
|
|
35
|
-
{ type: "
|
|
36
|
-
{ type: "
|
|
36
|
+
{ type: "impostor", layout: "hemi", maxPixels: 120, draw: { parallax: 2 } },
|
|
37
|
+
{ type: "empty", maxPixels: 4 },
|
|
37
38
|
],
|
|
38
39
|
});
|
|
39
|
-
|
|
40
|
+
const trees = new InstancedLod(levels, gpu);
|
|
41
|
+
|
|
42
|
+
const matrix = new Matrix4();
|
|
43
|
+
const matrices = new Float32Array(positions.flatMap((position) => matrix.makeTranslation(position).toArray()));
|
|
44
|
+
trees.addChunk({ instanceMatrix: new InstancedBufferAttribute(matrices, 16) });
|
|
45
|
+
scene.add(trees);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Supply authored low shells and baking interiors as asset URLs on a baked level; both resolve relative to the source asset as ordinary URLs, so a query-only value replaces the source's whole query. For a variant of the same script, repeat the source's generation parameters, for example the source URL plus `&part=low`. The source asset should be complete at full detail; a baking interior supplies room structure for the distant windows. Author all three in the same coordinate system.
|
|
49
|
+
|
|
50
|
+
## Put each cache with its computation
|
|
51
|
+
|
|
52
|
+
Follow the [vitecache skill](https://github.com/drawcall-ai/vitecache/blob/main/packages/vitecache/skills/vitecache/SKILL.md) to configure the host. `LodLoader` writes each derived level to the cache directory when the app first loads that level under the Vite dev server with the vitecache plugin. A deployed miss builds the missing level in the browser each time it loads, without persisting it. When the app is deployed under a sub-path, pass that location as `new LodLoader(manager, { basePath })`.
|
|
53
|
+
|
|
54
|
+
To deploy every level prebuilt, load the ladders in a public `.glts` script, which `vite build` runs in Node as the [GLTS skill](https://github.com/drawcall-ai/glts/blob/main/packages/glts/skills/glts/SKILL.md#precomputing-into-the-build) describes. There `LodLoader` builds and saves every level of each ladder it loads, deferred ones included. Request the same URLs and levels as the app, from a helper beside the script that the app imports too; `onLevel` changes nothing cached, so the app adds its effects to the shared requests. Register the contextual `gltsLoader` for `.glts` sources, as the app registers its `GLTSLoader`, and add every level to `scene`: a level can hold a `.glts` source the script loaded, which GLTS requires beneath `scene`.
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
// public/precompute.glts
|
|
58
|
+
import { gltsLoader, loadingManager, scene } from "@drawcall/glts";
|
|
59
|
+
import { LodLoader } from "@drawcall/lod";
|
|
60
|
+
import { ladders } from "./ladders.ts";
|
|
61
|
+
|
|
62
|
+
loadingManager.addHandler(/\.glts$/i, gltsLoader);
|
|
63
|
+
const loader = new LodLoader(loadingManager);
|
|
64
|
+
for (const ladder of ladders) {
|
|
65
|
+
for (const level of await loader.loadAsync(ladder)) scene.add(level.object ?? (await level.load()));
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Edits to the script, its helpers, packages and loaded files invalidate the derived levels; no dependency list is needed. A level rebuilds on every load when its source or anything the script loads cannot be tied to a project file: packages imported from a CDN at runtime or not installed by the app, resources from another origin, and same-origin URLs without a file, such as URLs a server answers dynamically. Precomputing such a level fails the build, so install every package the precomputed sources import.
|
|
70
|
+
|
|
71
|
+
Cache expensive generation inside a `.glts` asset, such as geometry, rooms or generated texture data that would otherwise stall the first approach, as the GLTS skill describes; leave cheap generation uncached. Use the `encodeMeshAsync` / `decodeMeshAsync` codec for geometry an asset caches itself.
|
|
72
|
+
|
|
73
|
+
## Place, share and dispose levels
|
|
74
|
+
|
|
75
|
+
Load an asset once and build an `InstancedLod` per spatial region from its levels, each with its own chunks of placements and colours; clone a `Lod` for further independently placed copies. Reuse the loaded levels across Lods; adjust each Lod's resolved thresholds through its `levels`. Clones and InstancedLods share geometry, materials, textures and deferred loads. Disposing a `Lod` or `InstancedLod` frees only what it made of its levels: objects you give or load are yours to dispose once every Lod drawing them is disposed, while `LodLoader` frees what it loaded when the last Lod holding its levels is disposed.
|
|
76
|
+
|
|
77
|
+
Use `onLevel` for application effects and shadow settings, `castShadow` and `shadowSide` included. Put source generation and its materials in the asset so that the same URL also loads correctly outside the LOD host.
|
|
78
|
+
|
|
79
|
+
Levels draw with copies of the source's materials, made once through three's `clone`. A custom material class must carry its own state in `copy`, and changes meant to reach drawn levels belong in shared TSL uniforms rather than in properties set on the original afterwards.
|
|
80
|
+
|
|
81
|
+
Apply a shared `Wind` after assembling a LOD and before its first render; arriving levels inherit the effect. A `Lod` or `InstancedLod` that has rendered refuses wind: its shaders would build in a frame. Apply it separately to nested LODs and independently managed batches.
|
|
82
|
+
|
|
83
|
+
Levels are chosen by on-screen size in CSS pixels on the actual render target, so a lower renderer pixel ratio saves shading, not geometry. Compile the initial scene with the actual camera and renderer before revealing it. Render every frame while finer levels load: they upload and compile only inside renders.
|
|
84
|
+
|
|
85
|
+
## Add instances in chunks and thin them with distance
|
|
40
86
|
|
|
41
|
-
|
|
42
|
-
spots.forEach((spot, i) => forest.setMatrixAt(i, new THREE.Matrix4().setPosition(spot)));
|
|
43
|
-
scene.add(forest);
|
|
87
|
+
A fixed set of copies is one chunk. For copies that exist only near the camera, such as plants on patches of ground, add a chunk when a patch comes into reach and remove it when the camera moves on; a few chunks a frame cost about what they hold. Prefer whole chunks over parking unused instances at scale 0. When a compute pass of your own writes the copies, give `addChunk` its storage attributes and the bounds the CPU cannot measure (see `InstancedLodChunkParameters`).
|
|
44
88
|
|
|
45
|
-
|
|
89
|
+
Give an InstancedLod a `cullDistance` to thin it out towards a distance instead of cutting it off. `scaleSurvivors` enlarges the copies that remain so that small things like grass cover about as much; leave it off for objects whose size reads, such as trees. Thinning does not change which levels load: let the ladder's last drawn level end near `far`, or add an empty level there.
|
|
90
|
+
|
|
91
|
+
In this example `grass` is an InstancedLod, `camera` the camera it thins from, and `matrices` a patch's instance matrices, 16 floats each.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
grass.cullDistance = { camera, near: 35, far: 60, scaleSurvivors: true };
|
|
95
|
+
const chunk = grass.addChunk({ instanceMatrix: new InstancedBufferAttribute(matrices, 16) });
|
|
96
|
+
// Later, when the patch leaves reach:
|
|
97
|
+
grass.removeChunk(chunk);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Assemble levels you already have
|
|
101
|
+
|
|
102
|
+
Pass levels in finest-to-coarsest order. An empty object can be a deliberate culled level. A ladder needs measurable bounds.
|
|
103
|
+
|
|
104
|
+
Describe a deferred representation with a load function, bounds and a transition height. The function returns the object already placed in the Lod's space; the bounds cover it in that same space, including its children. For a cached impostor, decode the atlas and return an `ImpostorMesh` with the intended draw settings.
|
|
105
|
+
|
|
106
|
+
Here, `loadDetailedMesh` loads the detailed object, `sourceBox` describes its bounds in the Lod's space, `coarseMesh` is already available, and `gpu` is the renderer's LodGpu.
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import { Lod } from "@drawcall/lod";
|
|
110
|
+
|
|
111
|
+
const object = new Lod([{ load: loadDetailedMesh, maxPixels: Infinity, box: sourceBox }, { object: coarseMesh }], gpu);
|
|
112
|
+
scene.add(object);
|
|
46
113
|
```
|
|
47
114
|
|
|
48
|
-
|
|
115
|
+
A level fades in over the coarser one drawn in its place once it arrives. A `Lod` loads the levels its on-screen height needs; an `InstancedLod` loads every level the heights of its chunks' instances can need for a camera, judged from each chunk's bounds, so give chunks tight bounds: a chunk reaching from the camera to the horizon loads every level. The resources a deferred load uses must stay alive until it finishes.
|
|
116
|
+
|
|
117
|
+
For direct preparation and storage outside `LodLoader`, combine `simplify`, `bakeMesh`, or `bakeImpostor` with the corresponding codecs and drawing objects. Bakes that draw take a renderer made with `createBakeRenderer`, never the application's, whose canvas a bake would draw over. Use `ImpostorMesh` directly when copies stay distant throughout their lifetime. For external detail systems, combine an empty near level with a coarse representation and ask `lod.getLevel(camera)` or `chunk.getLevelAt(index, camera)` where to supply detail.
|
|
49
118
|
|
|
50
|
-
|
|
119
|
+
## Prepare assets that survive transitions
|
|
51
120
|
|
|
52
|
-
|
|
53
|
-
- **Near a switch both levels draw** and split the pixels with a dither, so nothing pops. Elsewhere only one level is visible: hidden levels submit no draws or shadows, but their geometry and textures stay resident. Each level's switch size must stay far enough below the previous one's for one fade to end before the next begins; the `Lod` throws otherwise.
|
|
54
|
-
- **Nested `Lod`s** switch their own levels; the parent hands over to and from them without the dither, and a hidden parent level hides everything inside.
|
|
55
|
-
- **Clone instead of loading twice:** `clone()` shares geometry, textures and deferred atlas requests. A persistent cache avoids rebaking, but each separate load still reconstructs the model and its other levels. For hundreds of copies use `InstancedLod`, as a `Lod` per copy costs draws per copy.
|
|
56
|
-
- **Split large crowds into regions:** use one `InstancedLod` per region so regions outside the view or a shadow map can skip their draws. Keep nearby instances together in the batch; one scattered world-sized crowd is hard to cull, while many tiny crowds spend the frame submitting draws. Cheap far cards can use larger batches than detailed source models; compare their draw overhead and culling cost separately. Check that your target devices offer the required WebGPU feature `indirect-first-instance`. Keep the batch's own transform uniformly scaled (instances may scale any way). Build its levels as templates without nested `Lod`s or InstancedMeshes; use `Lod` for hierarchies.
|
|
57
|
-
- **Hierarchies on top** (a field whose plants exist only near the camera): level 0 may be an empty `Object3D`, and the `Lod` measures by the first level that draws. Draw the detail yourself where level 0 shows: `levelOf` on an `InstancedLod`, `visible` on a `Lod`'s level. `Lod.fade` is the share of a level's `maxPixels` its fade spans.
|
|
58
|
-
- **Saving results:** persist impostors with `encodeImpostor` and baked meshes with `encodeBakedMesh`, before applying runtime material changes or adding them to a `Lod`. Decode cached results instead of repeating their bakes; the asynchronous impostor decoder avoids long synchronous JavaScript decompression where native streams are available. Simplified levels still need construction. An incompatible encoding change requires rebaking old files, and results need regeneration when their inputs or baking algorithm change.
|
|
59
|
-
- **`dispose()` frees everything,** the source included. Clones share geometry and textures: dispose only once no clone draws any more.
|
|
121
|
+
Ship source models with deduplicated textures, sized to their on-screen use and GPU-compressed as KTX2, using established tools such as gltf-transform. Uploads are paced per frame, so variants that each carry their own uncompressed 2048² textures add up to gigabytes of uploads and long waits before a close-up is complete. Use instance colour variation when it can replace separately textured variants.
|
|
60
122
|
|
|
61
|
-
|
|
123
|
+
Author sources, simplified geometry, low shells and interiors in one coordinate system. A ground-level pivot helps upright repeated assets.
|
|
62
124
|
|
|
63
|
-
|
|
125
|
+
Author foliage with alpha-tested cut-outs and colour beyond the visible texture edges. The package supplies no foliage material: give cut-out source materials alpha to coverage and an alpha that holds coverage in smaller mips, for the camera and the shadow maps, or distant leaves thin out and vanish. Fill a cut-out's transparent texels with the mean colour of its opaque texels, as the last step, in a format that preserves colour under zero alpha: lossy webp discards it, PNG preserves it, and a KTX2 encoder needs checking. Mips average those texels in, so left dark they darken the distant source while an impostor, baked from full detail, holds its colour and turns brighter where the two switch. Dilating from the edges instead spreads their often dark fringe. Match visual effects across representations so foliage does not change lighting at a transition.
|
|
64
126
|
|
|
65
|
-
|
|
127
|
+
Custom shading on an impostor has little room: an impostor uses close to WebGPU's 16 varyings, so a colour node of your own may find none left; read per pixel what a mesh would carry. An impostor's `normalView` is one unit normal per texel, however many leaves that texel averages. For a lighting term of your own, read `cosine(impostor.material.spread, lightDirection)`, the mean of the clamped cosine over those leaves. Here `impostor` is the level's `ImpostorMesh` and `lightDirection` the view-space direction towards the light.
|
|
128
|
+
|
|
129
|
+
Match a low shell to the source's occupied shape and fit it within the source's bounds: `LodLoader` rejects a level that reaches beyond them. Preserve important silhouette features and remove faces that will never be seen. Generate shell and source from the same parameters when possible. Author rooms separately, with their boundaries just behind windows and furnishings near visible walls; include rooms in the detailed source when they should appear up close too.
|
|
130
|
+
|
|
131
|
+
For dense cover, combine near individuals with a baked distant patch. Jitter position and rotation to avoid aligned gaps, preserve continuity across tile borders, and use side walls only on exposed boundaries. An empty far level can stop drawing individuals once the patch carries their appearance.
|
|
132
|
+
|
|
133
|
+
## Wait for a complete frame before a screenshot
|
|
134
|
+
|
|
135
|
+
Levels load as the camera comes to need them, so a frame drawn soon after loading can lack them. Give the Lods and InstancedLods a `manager` of their own, so that it tracks only them: each holds an item there while the levels its cameras need load, compile or fade in, and ends it once they are complete. Render the `Scene` that holds them each frame, since a Lod loads nothing before such a render.
|
|
136
|
+
|
|
137
|
+
To capture one frame, even with nothing cached, place the camera and render, then wait a frame; while the manager is loading, compile with `renderer.compileAsync(scene, camera)`, render again and wait a frame; capture after a quiet frame, one in which no item started or ended. After moving the camera, wait again. In this example `levels` and `treeLevels` are ladders from `LodLoader`, `gpu` the renderer's LodGpu, `instanceMatrix` an attribute of tree matrices, and `viewpoint` the camera position to capture.
|
|
66
138
|
|
|
67
139
|
```ts
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
);
|
|
140
|
+
import { LoadingManager } from "three/webgpu";
|
|
141
|
+
import { InstancedLod, Lod } from "@drawcall/lod";
|
|
142
|
+
|
|
143
|
+
const manager = new LoadingManager();
|
|
144
|
+
const lod = new Lod(levels, gpu, { manager });
|
|
145
|
+
const trees = new InstancedLod(treeLevels, gpu, { manager });
|
|
146
|
+
trees.addChunk({ instanceMatrix });
|
|
147
|
+
scene.add(lod, trees);
|
|
148
|
+
|
|
149
|
+
camera.position.copy(viewpoint);
|
|
150
|
+
let loading = false;
|
|
151
|
+
manager.onStart = () => (loading = true);
|
|
152
|
+
manager.onLoad = () => (loading = false);
|
|
153
|
+
const frame = () => new Promise((resolve) => requestAnimationFrame(resolve));
|
|
154
|
+
renderer.render(scene, camera);
|
|
155
|
+
await frame();
|
|
156
|
+
while (loading) {
|
|
157
|
+
await renderer.compileAsync(scene, camera);
|
|
158
|
+
renderer.render(scene, camera);
|
|
159
|
+
await frame();
|
|
160
|
+
}
|
|
161
|
+
// The last render drew the complete frame: capture it.
|
|
75
162
|
```
|
|
76
163
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
- **A canopy for a patch of plants** sits at the leaf tops, with side walls only on the outer edge of the whole field: inner walls show as a cliff where plants hand over. Rays that find no leaf at the top bake the leaves further down, so the canopy needs nothing under it. Whatever stands above the leaves (tassels, flowers) covers far more of a top-down bake than of any view across the field: bake the canopy from plants with fewer of them. Lay out the patch so rows run on across its borders, then copies tile without seams. Switch the plants on top per plant, by their own distance, not per patch: the hand-over then follows a smooth contour around the camera instead of patch edges.
|
|
91
|
-
|
|
92
|
-
## Performance
|
|
93
|
-
|
|
94
|
-
- **Tune switch sizes on a standard screen.** They are in CSS pixels, so the choice holds on a high-density one; size anything of your own that reacts to on-screen size (how far plants exist, how many) in CSS pixels too, or a high-density screen multiplies it.
|
|
95
|
-
- **When source geometry dominates,** lighten the source first, then let its impostor take over earlier — a larger `maxPixels` on the impostor level: lowering it keeps more copies at the source. Bake that impostor with the same `maxPixels`: above its default it shows its top mip magnified, which otherwise keeps the look of the source at the bake's size — foliage denser and darker than the leaves it replaces up close.
|
|
96
|
-
- **Impostors cost fill.** Few triangles can still cover the screen many times: atlas sampling, lighting and overlapping cards can dominate even with parallax off. Compare dense individual plants with a patch's baked far level, not just triangle counts. Keep parallax for impostors seen large — without it they ghost (doubled trunks) — and turn it off only for impostors that only ever show small or densely packed, such as a field's plants.
|
|
97
|
-
- **Count material groups as well as triangles.** Instancing combines copies of a model, but its separate materials still require separate draws in each pass. Merge compatible parts before creating LOD batches; packing textures alone does not combine draws. Preserve material and shadow behavior, and keep overlapping or thin parts separate if batching changes their appearance. For `bakeMesh`, bake from the authored materials before replacing them with GPU-only atlas materials.
|
|
98
|
-
- **Keep the rendering conditions fixed when comparing performance:** viewport, pixel ratio, multisampling, shadows, wind and switch sizes. Disabling an effect can identify its cost, but is not an equivalent-looking optimization. Check moving transitions as well as stationary images; multisampling matters especially for thin twigs and grass.
|
|
99
|
-
- **Judge changes by completed frame time.** Fewer draws, triangles or buffer bindings can still cost more if culling becomes coarser or preparation, vertex work or shading becomes heavier. Merged shadow casters can process parts that were previously skipped. Atlases can increase texture memory; preserve resolution and mip quality in the comparison. Measure CPU work and GPU completion separately; overlapping GPU pass durations do not add up to frame time. Recheck the original setup after each experiment to catch background-load drift. Compare images after the same movement history: streamed instance slots and cached shadows can differ even when the final camera pose is identical.
|
|
100
|
-
- **Stream into existing batches:** reuse nearby regions' slots and change only the matrices or colours that actually changed. Rebuilding batches or rewriting the whole crowd during flight adds preparation and upload work even when few instances draw.
|
|
101
|
-
- **Check the first flight as well as a warmed repeat.** Preparing the starting view does not prepare every later level and shadow variant. Measure first-use shader preparation and streaming separately from steady-state throughput. Any startup warmup needs its own loading-time and appearance checks, including simulation and streaming state, the first visible frame and later transitions. Free flight can still reveal new variants. Measure rendering separately from an overlay that counts every instance; count queries and temporary objects can cause periodic CPU and garbage-collection stalls.
|
|
102
|
-
|
|
103
|
-
## Shadows
|
|
104
|
-
|
|
105
|
-
- **Levels are chosen from the viewing camera,** and every shadow map draws those same levels, including a finer fallback while an impostor loads. During a fade both levels cast, dithered as they draw, so the object casts one shadow.
|
|
106
|
-
- **Casters narrower than a texel** of a directional light's shadow map draw nothing into it, so coarse cascades skip small casters by themselves.
|
|
107
|
-
- **Shadow setups on the light or renderer reach every level**; impostors cast and receive at the surface they resolve. A per-material setup must run on the level meshes' materials after the `Lod` is built: the material you passed in is no longer drawn.
|
|
108
|
-
- **Open models leak light** (eaves, window reveals, single walls): three casts with back faces only. Set `shadowSide = DoubleSide` on their materials, with about a texel of shadow `bias` against acne.
|
|
109
|
-
|
|
110
|
-
## Pitfalls
|
|
111
|
-
|
|
112
|
-
- **Materials are replaced.** A `Lod` draws each mesh with a faded node-material copy of its material; the material you passed in is no longer drawn. Edit the material on the level's mesh after building. `ShaderMaterial`s are refused: write them as node materials.
|
|
113
|
-
- **Authored levels are read once,** when the `Lod` is built: arbitrary meshes added or moved later are neither faded nor measured right. Build a new `Lod` instead. `LazyImpostor` is the supported way to defer an impostor level.
|
|
114
|
-
- **Skinned meshes are refused,** and animation is ignored.
|
|
115
|
-
- **Orthographic cameras** switch by on-screen size too, but swap levels at once.
|
|
116
|
-
- **Several cameras:** each is measured at the viewport height it last drew the `Lod` at; a camera's first frame assumes a typical screen. An `InstancedLod` holds one selection at a time: a render nested in another (a reflection, a `CubeCamera`) that sees it replaces the selection the outer render draws, so keep it off the layers such cameras see. Renders one after the other are fine.
|
|
117
|
-
- **Many `Lod`s appearing at once hitch** while three sets up each mesh's material; draw crowds with `InstancedLod`.
|
|
118
|
-
- **Lines, points and sprites** in a level draw but do not fade. Raycasts hit the source only, whatever level shows.
|
|
119
|
-
- **Canvas alpha:** impostor edges use alpha-to-coverage, which leaves fractional alpha in the frame, so the page shows through: create the renderer with `alpha: false`.
|
|
120
|
-
|
|
121
|
-
- **Impostor layout `"hemi"`** covers views from the horizon up only: right for upright trees, rocks and buildings, wrong for anything seen from below.
|
|
122
|
-
- **`.glts` URLs need a registered loader:** `manager.addHandler(/\.glts(?:[?#]|$)/i, new GLTSLoader(manager))`. Inside a `.glts` script, register the contextual `gltsLoader` on the contextual `loadingManager` instead. Share the host's package through GLTS when using `instanceof`; separately loaded package copies have different constructors.
|
|
123
|
-
- **Baking a mesh is slow and CPU-bound** on the loading thread (it yields between chunks), and each baked level has its own textures in GPU memory. Bake once per model and clone; time and memory grow with `textureSize` squared, so small or only-far objects bake faster at a smaller one.
|
|
124
|
-
- **Baked levels read textures on the CPU:** compressed textures (KTX2) fail the bake. Emissive, clearcoat and transmission are not baked; glass bakes opaque in its own colour unless it looks into an interior. Only alpha-tested surfaces cut holes.
|
|
125
|
-
- **Specular level** (`ior`, `specularIntensity`, `specularColor`, glTF `KHR_materials_specular`) reaches an impostor only as one average for the whole object, so parts or maps that differ in it reflect alike; baked levels reflect like a plain dielectric, so a source with its specular turned down shows brighter at them.
|
|
126
|
-
- **Clones of a built level** made by hand lose the measured error their switch size comes from; clone the whole `Lod` instead.
|
|
127
|
-
- **Large worlds** (kilometres to millimetres) z-fight with the default depth buffer. Every level works with the renderer's `reversedDepthBuffer`.
|
|
128
|
-
|
|
129
|
-
## Interiors
|
|
130
|
-
|
|
131
|
-
- **Rooms are their own model,** never part of the building's: only rooms given as the interior are left out of the baked exterior and shown through its windows. One baked level per model takes an interior.
|
|
132
|
-
- **One direct child per room, filling its box:** walls, floor and ceiling on the box's faces, facing in. Rooms must not overlap; doorways between them, stairwells and several storeys are fine. A room's box should begin at the inside of the wall its windows are in; glass with no room just behind it stays plain glass.
|
|
133
|
-
- **The glass must be see-through in the source:** transparent with opacity below 1, or transmissive. Dark but opaque glass gives no windows, and the bake fails when no glass looks into a room.
|
|
134
|
-
- **Rooms are lit by the light reaching the window** — ambient, light probes and the environment, not the sun, which the walls would block — so they darken with the scene. For a dimmer room, darken its materials.
|
|
135
|
-
- **Lamps glow through emissive (or unlit) materials.** Lights in an interior are refused: wherever the source draws they would be scene lights, recompiling every lit material at each switch.
|
|
136
|
-
- **Furniture against a wall looks right;** things standing free in the room flatten onto the walls behind them.
|
|
137
|
-
- **Calling `bakeMesh` yourself:** pass the interior and put it inside the source, so the source shows the real rooms; the bake leaves it out of the exterior either way.
|
|
164
|
+
Loaded levels stay until their Lod is disposed, which nothing does for you: in a world larger than memory, dispose the Lods the camera has left far behind.
|
|
165
|
+
|
|
166
|
+
## Tune the complete scene
|
|
167
|
+
|
|
168
|
+
Tune transitions in the intended viewport with `Lod.debug`, then inspect motion through them. Use `Lod.quality` for a scene-wide adjustment and individual thresholds for particular assets. Give an impostor bake the intended screen size at its switch, then inspect its coverage and colour from several angles. Compare both sides of a switch under one camera, drawing each with the Lod's `forceLevel`, looking toward the sun as well as away: a mismatch can show in one light direction only.
|
|
169
|
+
|
|
170
|
+
Group nearby copies into spatial regions. Broad batches can lose useful culling, while tiny batches spend too much time submitting draws. Add and remove chunks and move changed instances instead of rebuilding whole InstancedLods. Count material groups as well as triangles; an atlas alone does not combine draws. Each distinct material structure costs a pipeline compile on first use, so variants should share material structure.
|
|
171
|
+
|
|
172
|
+
Avoid custom shadow nodes on lights that LOD objects should cast into. An `InstancedLod` draws for each camera, shadow maps' included, only the instances inside its frustum, at the level the map's texels resolve, so shadow casting costs about what the map can show. When it still dominates, turn `castShadow` off in `onLevel` on the levels that matter least to the shadow, such as small plants.
|
|
173
|
+
|
|
174
|
+
Judge impostors by covered screen area and overlap as well as triangle count. Compare individual cards with baked patches for dense cover, and inspect parallax and mip quality during transitions.
|
|
175
|
+
|
|
176
|
+
Compare cold startup, warm startup and first traversal under the same camera movement, viewport, shadows, wind and thresholds. Leave expensive diagnostic overlays out of measurements. A warm repeat does not reveal first-use work, and fewer draws can still lose to worse culling, shading or memory costs.
|
package/dist/bounds.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bounds.js","sourceRoot":"","sources":["../src/bounds.ts"],"names":[],"mappings":"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"}
|
package/dist/fade.d.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { Color, type Node, type Object3D, type StorageBufferAttribute } from "three/webgpu";
|
|
2
|
-
/** How a mesh of a level is drawn, which says where its fade and shadow size come from. */
|
|
3
|
-
export type Draw = {
|
|
4
|
-
kind: "object";
|
|
5
|
-
/** the signed fade */
|
|
6
|
-
fade: number;
|
|
7
|
-
/** the whole object's bounding diameter, world units */
|
|
8
|
-
diameter: number;
|
|
9
|
-
/** debug tint, white when off */
|
|
10
|
-
tint: Color;
|
|
11
|
-
} | {
|
|
12
|
-
kind: "instances";
|
|
13
|
-
/** one signed fade per instance slot; null for none (an ImpostorMesh of its own) */
|
|
14
|
-
fades: StorageBufferAttribute | null;
|
|
15
|
-
/** one instance's bounding radius in its own space */
|
|
16
|
-
radius: number;
|
|
17
|
-
tint: Color;
|
|
18
|
-
};
|
|
19
|
-
/** Draw `mesh` as `draw` says: its materials read it while it renders. */
|
|
20
|
-
export declare const setDraw: (mesh: Object3D, draw: Draw) => void;
|
|
21
|
-
export declare const drawOf: (mesh: Object3D) => Draw | undefined;
|
|
22
|
-
/** This draw's debug tint (see `Lod.debug`). */
|
|
23
|
-
export declare const tint: import("three/webgpu").UniformNode<"color", Color>;
|
|
24
|
-
/** Interleaved gradient noise over the pixels of the target being drawn. */
|
|
25
|
-
export declare const dither: (pixel: Node<"vec2">) => Node<"float">;
|
|
26
|
-
/**
|
|
27
|
-
* Whether `object` is an ImpostorMesh of one instance, which reads it per object: more,
|
|
28
|
-
* or slots the GPU writes (an InstancedLod's indirect draws), come from its storage buffer.
|
|
29
|
-
*/
|
|
30
|
-
export declare const isSingle: (object: Object3D) => boolean;
|
|
31
|
-
/** The instance matrix of this draw, for an InstancedMesh or ImpostorMesh with its matrices in a storage buffer; else null. */
|
|
32
|
-
export declare function instanceMatrixOf(object: Object3D): Node<"mat4"> | null;
|
|
33
|
-
/** This draw's signed fade. */
|
|
34
|
-
export declare const fade: import("three/src/nodes/TSL.js").FnNode<[], Node<"float">>;
|
|
35
|
-
/** Whether this pixel is this draw's, by its fade: false on the other level's pixels. */
|
|
36
|
-
export declare const keepsPixel: import("three/src/nodes/TSL.js").FnNode<[], Node<"bool">>;
|
|
37
|
-
/**
|
|
38
|
-
* Whether this draw is too small for the shadow map being drawn: narrower than a texel
|
|
39
|
-
* of an orthographic (directional light's) map, it would cover a texel centre only by
|
|
40
|
-
* chance, flickering as it or the map moves. Only valid in shadow passes.
|
|
41
|
-
*/
|
|
42
|
-
export declare const tooSmallToCast: import("three/src/nodes/TSL.js").FnNode<[], Node<"bool">>;
|
|
43
|
-
/** `position` in shadow passes, or nothing (a point) when the draw is too small to cast. */
|
|
44
|
-
export declare const castPosition: (position: Node<"vec3">) => Node<"vec3">;
|
package/dist/fade.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { Color, Matrix4 } from "three/webgpu";
|
|
2
|
-
import { Fn, cameraProjectionMatrix, dot, float, fract, instanceIndex, modelWorldMatrix, screenCoordinate, screenSize, select, uniform, varying, vec2, vec3, } from "three/tsl";
|
|
3
|
-
import { isImpostorMesh, storageMatrices } from "./three.js";
|
|
4
|
-
import { column, instanceFades, instanceMatrices, isOrthographic, maxScale } from "./tsl.js";
|
|
5
|
-
const draws = new WeakMap();
|
|
6
|
-
/** Draw `mesh` as `draw` says: its materials read it while it renders. */
|
|
7
|
-
export const setDraw = (mesh, draw) => void draws.set(mesh, draw);
|
|
8
|
-
export const drawOf = (mesh) => draws.get(mesh);
|
|
9
|
-
const WHITE = new Color(1, 1, 1);
|
|
10
|
-
const of = (object) => (object ? draws.get(object) : undefined);
|
|
11
|
-
// Per-object values, shared by every material: each draw reads its own.
|
|
12
|
-
const objectFade = uniform(0).onObjectUpdate(({ object }) => {
|
|
13
|
-
const draw = of(object);
|
|
14
|
-
return draw?.kind === "object" ? draw.fade : 0;
|
|
15
|
-
});
|
|
16
|
-
const placement = new Matrix4();
|
|
17
|
-
// A mesh no level draws is never too small to cast. An ImpostorMesh of one instance
|
|
18
|
-
// reads its own here too, however it is drawn: such meshes share one shader.
|
|
19
|
-
const objectDiameter = uniform(0).onObjectUpdate(({ object }) => {
|
|
20
|
-
const draw = of(object);
|
|
21
|
-
if (draw?.kind === "object")
|
|
22
|
-
return draw.diameter;
|
|
23
|
-
if (draw?.kind !== "instances" || !object || !isImpostorMesh(object) || !isSingle(object))
|
|
24
|
-
return 3.4e38;
|
|
25
|
-
return 2 * draw.radius * placement.fromArray(object.instanceMatrix.array).premultiply(object.matrixWorld).getMaxScaleOnAxis();
|
|
26
|
-
});
|
|
27
|
-
const instanceRadius = uniform(0).onObjectUpdate(({ object }) => {
|
|
28
|
-
const draw = of(object);
|
|
29
|
-
return draw?.kind === "instances" ? draw.radius : 0;
|
|
30
|
-
});
|
|
31
|
-
/** This draw's debug tint (see `Lod.debug`). */
|
|
32
|
-
export const tint = uniform(new Color(1, 1, 1)).onObjectUpdate(({ object }) => of(object)?.tint ?? WHITE);
|
|
33
|
-
/** Interleaved gradient noise over the pixels of the target being drawn. */
|
|
34
|
-
export const dither = (pixel) => fract(float(52.9829189).mul(fract(dot(pixel, vec2(0.06711056, 0.00583715)))));
|
|
35
|
-
/** The matrix of an ImpostorMesh of one instance, read per object: such meshes share one shader. */
|
|
36
|
-
const singleMatrix = uniform(new Matrix4()).onObjectUpdate(({ object }, self) => object && isImpostorMesh(object) ? self.value.fromArray(object.instanceMatrix.array) : undefined);
|
|
37
|
-
/**
|
|
38
|
-
* Whether `object` is an ImpostorMesh of one instance, which reads it per object: more,
|
|
39
|
-
* or slots the GPU writes (an InstancedLod's indirect draws), come from its storage buffer.
|
|
40
|
-
*/
|
|
41
|
-
export const isSingle = (object) => isImpostorMesh(object) && object.count === 1 && object.geometry.getIndirect() === null;
|
|
42
|
-
/** The instance matrix of this draw, for an InstancedMesh or ImpostorMesh with its matrices in a storage buffer; else null. */
|
|
43
|
-
export function instanceMatrixOf(object) {
|
|
44
|
-
if (isSingle(object))
|
|
45
|
-
return singleMatrix;
|
|
46
|
-
const attribute = storageMatrices(object);
|
|
47
|
-
return attribute && instanceMatrices(attribute).element(instanceIndex);
|
|
48
|
-
}
|
|
49
|
-
/** This draw's signed fade. */
|
|
50
|
-
export const fade = Fn((builder) => {
|
|
51
|
-
const draw = draws.get(builder.object);
|
|
52
|
-
if (draw?.kind !== "instances" || !draw.fades)
|
|
53
|
-
return objectFade;
|
|
54
|
-
// Constant across the primitive: keep compute-written slots out of fragment bindings.
|
|
55
|
-
return varying(instanceFades(draw.fades).element(instanceIndex)).setInterpolation("flat");
|
|
56
|
-
});
|
|
57
|
-
/** Whether this pixel is this draw's, by its fade: false on the other level's pixels. */
|
|
58
|
-
export const keepsPixel = Fn(() => {
|
|
59
|
-
const f = fade();
|
|
60
|
-
const d = dither(screenCoordinate.xy);
|
|
61
|
-
return select(f.greaterThanEqual(0), d.greaterThanEqual(f), d.lessThan(f.negate()));
|
|
62
|
-
});
|
|
63
|
-
/**
|
|
64
|
-
* Whether this draw is too small for the shadow map being drawn: narrower than a texel
|
|
65
|
-
* of an orthographic (directional light's) map, it would cover a texel centre only by
|
|
66
|
-
* chance, flickering as it or the map moves. Only valid in shadow passes.
|
|
67
|
-
*/
|
|
68
|
-
export const tooSmallToCast = Fn((builder) => {
|
|
69
|
-
const object = builder.object;
|
|
70
|
-
const instance = draws.get(object)?.kind === "instances" && !isSingle(object) ? instanceMatrixOf(object) : null;
|
|
71
|
-
const diameter = instance ? maxScale(modelWorldMatrix.mul(instance)).mul(instanceRadius).mul(2) : objectDiameter;
|
|
72
|
-
// projection[1][1] is 2 × zoom / (top - bottom)
|
|
73
|
-
const texel = float(2).div(column(cameraProjectionMatrix, 1).y.mul(screenSize.y));
|
|
74
|
-
return isOrthographic(cameraProjectionMatrix).and(diameter.lessThan(texel));
|
|
75
|
-
});
|
|
76
|
-
/** `position` in shadow passes, or nothing (a point) when the draw is too small to cast. */
|
|
77
|
-
export const castPosition = (position) => select(tooSmallToCast(), vec3(0), position);
|
|
78
|
-
//# sourceMappingURL=fade.js.map
|
package/dist/fade.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fade.js","sourceRoot":"","sources":["../src/fade.ts"],"names":[],"mappings":"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"}
|
package/dist/impostor/cache.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { Object3D } from "three/webgpu";
|
|
2
|
-
import { type CacheEntryOptions, type Dependencies } from "vitecache";
|
|
3
|
-
import { type BakeImpostorOptions } from "./bake/index.js";
|
|
4
|
-
import type { Impostor } from "./impostor.js";
|
|
5
|
-
/** Cache location; browser basePath and Node root follow vitecache's conventions. */
|
|
6
|
-
export type ImpostorCacheOptions = Pick<CacheEntryOptions, "basePath" | "root">;
|
|
7
|
-
/**
|
|
8
|
-
* Read or bake an impostor with vitecache. Declare every source input as a dependency;
|
|
9
|
-
* bake options and the lod/three package versions are added automatically. Dependency
|
|
10
|
-
* names beginning with `lod:` are reserved. URL dependencies need the vitecache Vite
|
|
11
|
-
* plugin during development and build. Cache errors reject instead of hiding stale data.
|
|
12
|
-
*/
|
|
13
|
-
export declare function cachedImpostor(source: Object3D, options: BakeImpostorOptions, dependencies: Dependencies, cacheOptions?: ImpostorCacheOptions): Promise<Impostor>;
|
|
14
|
-
/** Shared persistence for the public bake and the loader's source-excluding bake. */
|
|
15
|
-
export declare function cached(options: BakeImpostorOptions, dependencies: Dependencies, make: (options: BakeImpostorOptions) => Promise<Impostor>, cacheOptions?: ImpostorCacheOptions): Promise<Impostor>;
|