@plasius/gpu-demo-viewer 0.4.1 → 0.5.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/CHANGELOG.md +17 -0
- package/README.md +10 -0
- package/docs/adrs/adr-0007-consented-curated-showcase-diagnostics.md +4 -0
- package/docs/adrs/adr-0009-instanced-crowd-studies.md +31 -0
- package/docs/adrs/index.md +2 -0
- package/docs/catalogue-production-rollout.md +8 -0
- package/docs/consented-diagnostics.md +9 -0
- package/docs/dense-scene-studies.md +55 -0
- package/model-library/catalogue.json +40 -0
- package/model-library/index.html +1 -1
- package/model-library/main.js +20 -11
- package/model-library/scenes.js +19 -6
- package/model-library/studies.d.ts +4 -0
- package/model-library/studies.js +15 -0
- package/model-library/styles.css +2 -0
- package/package.json +3 -3
- package/scripts/acquire-model-library.mjs +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -18,6 +18,22 @@ The format is based on Keep a Changelog and this project adheres to SemVer.
|
|
|
18
18
|
- **Security**
|
|
19
19
|
- (placeholder)
|
|
20
20
|
|
|
21
|
+
## [0.5.0] - 2026-10-27
|
|
22
|
+
|
|
23
|
+
- Keep quality controls available after resource/device failures and add explicit scene retry with the latest settings.
|
|
24
|
+
|
|
25
|
+
- **Added**
|
|
26
|
+
- Static 50/100-astronaut grove studies using shared geometry, trees and complete shrub groups; typed dense layout definitions and a separate integrity-pinned, attributed benchmark source.
|
|
27
|
+
|
|
28
|
+
- **Changed**
|
|
29
|
+
- Report requested GPU resource storage and distinct instance/source counts; deduplicate repeated source credits while preserving placement counts and the original catalogue rollout.
|
|
30
|
+
|
|
31
|
+
- **Fixed**
|
|
32
|
+
- (placeholder)
|
|
33
|
+
|
|
34
|
+
- **Security**
|
|
35
|
+
- (placeholder)
|
|
36
|
+
|
|
21
37
|
## [0.4.1] - 2026-10-23
|
|
22
38
|
|
|
23
39
|
- **Added**
|
|
@@ -383,3 +399,4 @@ The format is based on Keep a Changelog and this project adheres to SemVer.
|
|
|
383
399
|
[0.3.0]: https://github.com/Plasius-LTD/gpu-demo-viewer/releases/tag/v0.3.0
|
|
384
400
|
[0.4.0]: https://github.com/Plasius-LTD/gpu-demo-viewer/releases/tag/v0.4.0
|
|
385
401
|
[0.4.1]: https://github.com/Plasius-LTD/gpu-demo-viewer/releases/tag/v0.4.1
|
|
402
|
+
[0.5.0]: https://github.com/Plasius-LTD/gpu-demo-viewer/releases/tag/v0.5.0
|
package/README.md
CHANGED
|
@@ -270,6 +270,16 @@ short-lived npm OIDC with provenance, and has no long-lived npm write-token
|
|
|
270
270
|
fallback. Rollback disables CD; it never rewrites published package history.
|
|
271
271
|
<!-- END PLASIUS RELEASE INTEGRITY -->
|
|
272
272
|
|
|
273
|
+
## Dense scene studies
|
|
274
|
+
|
|
275
|
+
Use `npm run models:download -- crowd` to acquire the separately tracked Poly
|
|
276
|
+
Astronaut benchmark and the two existing vegetation sources. The workshop adds
|
|
277
|
+
50- and 100-astronaut groves using shared geometry, six trees and twelve full shrub
|
|
278
|
+
groups. Credits, unique geometry counts and requested GPU allocation bytes remain
|
|
279
|
+
visible. These static studies do not alter the original model-release target.
|
|
280
|
+
See the [dense-scene guide](docs/dense-scene-studies.md) for controls, instancing
|
|
281
|
+
options, limits and qualification requirements.
|
|
282
|
+
|
|
273
283
|
## Reuse composite layouts
|
|
274
284
|
|
|
275
285
|
Import the small data-only subpath to offer the workshop's four arrangements in
|
|
@@ -40,3 +40,7 @@ cover explicit user flow, mobile/keyboard access and network/cache isolation.
|
|
|
40
40
|
Release uses PR CI followed by exact-main `cd.yml` in `production`; verify the
|
|
41
41
|
npm artifact before downstream site#1677 adopts it. No backend or secret mapping
|
|
42
42
|
changes occur in this static package release.
|
|
43
|
+
|
|
44
|
+
The 27 October 2026 dependency update in ADR 0009 preserves all four asset hashes
|
|
45
|
+
against published shared 1.4.0. The source-admission and consent decisions above
|
|
46
|
+
remain unchanged; model-library stress scenes are still excluded.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# ADR 0009: Instanced crowd studies
|
|
2
|
+
|
|
3
|
+
Status: Accepted. Date: 2026-10-27. Task #90; Story site#2336; Feature site#1170.
|
|
4
|
+
|
|
5
|
+
Add two explicit static stress studies: 50 and 100 astronauts, each with six
|
|
6
|
+
Quiver Tree placements and twelve complete Searsia Lucida shrub groups. Each
|
|
7
|
+
shrub source includes seven authored variants; preserve them and label groups
|
|
8
|
+
accurately. Layout definitions are fresh data returned by `denseStudyDefinitions`
|
|
9
|
+
in the existing typed studies export. Existing four composite definitions remain
|
|
10
|
+
stable. Hosts resolve only their admitted sources and own rollout/access decisions.
|
|
11
|
+
|
|
12
|
+
Use gpu-shared's explicit instancing option and renderer allocation reports. Do not
|
|
13
|
+
expand copies, duplicate shader logic or substitute lower-detail vegetation. Show
|
|
14
|
+
unique geometry, source/placement/mesh-instance counts and requested GPU resource
|
|
15
|
+
storage distinctly. The displayed byte figure is not physical VRAM or total browser
|
|
16
|
+
memory. Retain source credits once per unique asset with placement counts.
|
|
17
|
+
|
|
18
|
+
The Poly Astronaut GLB is a separately pinned, integrity-checked CC-BY-2.0 benchmark
|
|
19
|
+
source. Keep it in `benchmarkAssets` so the original 75 research identities and
|
|
20
|
+
74-model production target are not changed. `models:download -- crowd` acquires only
|
|
21
|
+
this asset plus the two existing vegetation sources through the bounded acquisition
|
|
22
|
+
helper. The original batch downloader and Gem exclusion retain their semantics.
|
|
23
|
+
|
|
24
|
+
No public access is enabled locally. The site continues to use
|
|
25
|
+
`gpu-demo.scene-fidelity.enabled` and `site.studio-coming-soon.view`. Complete
|
|
26
|
+
registry-package GPU qualification and approved production release before marking
|
|
27
|
+
this delivery complete. Static instances do not imply animation or voxel traversal.
|
|
28
|
+
|
|
29
|
+
Consume released shared 1.4.0 and renderer 0.2.57. The existing consented diagnostics
|
|
30
|
+
asset hashes are unchanged and verified against the new shared package; no wider
|
|
31
|
+
scene admission or diagnostics collection is introduced.
|
package/docs/adrs/index.md
CHANGED
|
@@ -12,3 +12,5 @@
|
|
|
12
12
|
- [ADR 0007: Consented curated showcase diagnostics](adr-0007-consented-curated-showcase-diagnostics.md)
|
|
13
13
|
|
|
14
14
|
- [ADR 0008: Public compliance artifact contract](adr-0008-public-compliance-artifact-contract.md)
|
|
15
|
+
|
|
16
|
+
- [ADR 0009: Instanced crowd studies](adr-0009-instanced-crowd-studies.md)
|
|
@@ -225,3 +225,11 @@ raise resource limits to make qualification pass.
|
|
|
225
225
|
|
|
226
226
|
The candidate contains 61 models. Production remains at the last independently
|
|
227
227
|
verified release until package publication, site CD and public checks complete.
|
|
228
|
+
|
|
229
|
+
## Instanced scene admission — 27 October 2026
|
|
230
|
+
|
|
231
|
+
Shared 1.4.0 replaces the historical 500,000-triangle guard with requested GPU
|
|
232
|
+
resource accounting and device limits. The four large tree sources above remain
|
|
233
|
+
unqualified: removing that guard does not establish their performance or memory
|
|
234
|
+
suitability. Dense studies reuse already reviewed vegetation and a separate pinned
|
|
235
|
+
Astronaut fixture; see [the dense-scene guide](dense-scene-studies.md).
|
|
@@ -50,6 +50,15 @@ signature/attestation verification cover the new locked graph. A future shared
|
|
|
50
50
|
version requires an explicit asset/hash compatibility review rather than
|
|
51
51
|
silently accepting changed assets.
|
|
52
52
|
|
|
53
|
+
## Instancing dependency qualification — 27 October 2026
|
|
54
|
+
|
|
55
|
+
The dense-scene release advances the exact shared dependency to 1.4.0 and the
|
|
56
|
+
renderer range/lock to ^0.2.57/0.2.57. The existing four SHA-384 asset pins match
|
|
57
|
+
the published 1.4.0 bytes without changes; tests also confirm their resources
|
|
58
|
+
remain self-contained. The diagnostics public exports, access/consent boundaries
|
|
59
|
+
and source-owned mount are unchanged. Instanced model studies do not become
|
|
60
|
+
eligible diagnostics sources. See [ADR 0009](adrs/adr-0009-instanced-crowd-studies.md).
|
|
61
|
+
|
|
53
62
|
## Browser qualification — 21 October 2026
|
|
54
63
|
|
|
55
64
|
An isolated in-app Chromium tab mounted the actual published shared/native
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Dense scene studies
|
|
2
|
+
|
|
3
|
+
The astronaut grove studies place 50 or 100 astronauts among six trees and twelve
|
|
4
|
+
shrub groups. Each group retains the complete seven-variant Searsia source. This
|
|
5
|
+
is a static instancing test: source meshes and acceleration structures are shared,
|
|
6
|
+
with distinct transforms per placement. Original geometry and textures are kept.
|
|
7
|
+
|
|
8
|
+
Acquire the three integrity-pinned sources with:
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
npm run models:download -- crowd
|
|
12
|
+
npm run models:dev
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Open the local workshop with `?review=first-twelve&model=study-astronaut-grove-100`
|
|
16
|
+
and choose the requested sample count, bounces, image size and denoising. Pause
|
|
17
|
+
when hidden, move/reset the camera to invalidate history, and compare the 50- and
|
|
18
|
+
100-placement study. An allocation or GPU failure must remain visible; choose a
|
|
19
|
+
smaller quality target or retry explicitly rather than silently changing the scene.
|
|
20
|
+
|
|
21
|
+
The `model-library/studies` export provides `denseStudyDefinitions()` alongside
|
|
22
|
+
the original `compositeStudyDefinitions()`. Dense definitions carry `instancing`
|
|
23
|
+
and `camera` as well as placement data. Resolve each source through your reviewed
|
|
24
|
+
catalogue, pass those options to `createSceneInspection`, and display the returned
|
|
25
|
+
source/instance counts and allocation report. Repeated models need one source
|
|
26
|
+
attribution, with a placement count, rather than a hundred duplicate credits.
|
|
27
|
+
|
|
28
|
+
The 100-astronaut scene has 118 model placements, 365 mesh instances including its
|
|
29
|
+
ground, 25 unique mesh primitives and 528,922 unique triangles. The expanded
|
|
30
|
+
geometry equivalent is 5,587,450 triangles. More instances still require TLAS and
|
|
31
|
+
transform storage and add ray traversal/shading work. Requested GPU storage also
|
|
32
|
+
depends on image size and denoising. It excludes driver overhead, other tabs and
|
|
33
|
+
physical-memory residency; it is not a measure of available VRAM.
|
|
34
|
+
|
|
35
|
+
Astronaut is credited to Poly under CC-BY-2.0, from the pinned Google model-viewer
|
|
36
|
+
source and attribution record in `catalogue.json`. Quiver Tree and Searsia Lucida
|
|
37
|
+
retain their existing Poly Haven CC0 source records and authors. Benchmark assets
|
|
38
|
+
are additional fixtures, separate from the original 74-model production target.
|
|
39
|
+
|
|
40
|
+
Qualification requirements: deterministic counts and fresh layout data; complete
|
|
41
|
+
integrity manifests; visible attribution; unchanged original composites; allocation
|
|
42
|
+
and device-loss errors; source cleanup; keyboard/touch/mouse and mobile layout;
|
|
43
|
+
registry-based 50/100 GPU review; approved release and public verification. Local
|
|
44
|
+
source-injected results alone do not establish production readiness. Track evidence
|
|
45
|
+
under Task #90 and Story site#2336.
|
|
46
|
+
|
|
47
|
+
After a resource allocation or device failure, adjust the quality settings and
|
|
48
|
+
choose **Retry selected scene**. Changes while failed do not submit further GPU
|
|
49
|
+
work; retry disposes the previous scene and uses the latest settings.
|
|
50
|
+
|
|
51
|
+
Registry consumer qualification on 27 October 2026 used renderer 0.2.57 and shared
|
|
52
|
+
1.4.0 with no source overrides. Both 50/100 studies completed 64 samples/six
|
|
53
|
+
bounces/denoising at the workshop's 800x283 viewport. The existing diagnostics
|
|
54
|
+
asset pins also match shared 1.4.0. This establishes local released-dependency
|
|
55
|
+
behavior; public deployment remains a separate gate.
|
|
@@ -6088,5 +6088,45 @@
|
|
|
6088
6088
|
]
|
|
6089
6089
|
}
|
|
6090
6090
|
}
|
|
6091
|
+
],
|
|
6092
|
+
"benchmarkAssets": [
|
|
6093
|
+
{
|
|
6094
|
+
"key": "Astronaut",
|
|
6095
|
+
"name": "Astronaut",
|
|
6096
|
+
"category": "Benchmark source",
|
|
6097
|
+
"priority": "Dense scene benchmark",
|
|
6098
|
+
"provider": "Poly / Google model-viewer",
|
|
6099
|
+
"source_url": "https://github.com/google/model-viewer/blob/626f7498f3b8bf1130d0ca0c7b7bb756d6fb89ee/packages/modelviewer.dev/ATTRIBUTIONS.md",
|
|
6100
|
+
"authors": "Poly",
|
|
6101
|
+
"licence": "CC-BY-2.0",
|
|
6102
|
+
"licence_url": "https://creativecommons.org/licenses/by/2.0/",
|
|
6103
|
+
"attribution_required": true,
|
|
6104
|
+
"original_source_url": "https://poly.google.com/view/dLHpzNdygsg",
|
|
6105
|
+
"source_revision": "626f7498f3b8bf1130d0ca0c7b7bb756d6fb89ee",
|
|
6106
|
+
"renderer_target": "Shared geometry, suit material and silhouette in dense scenes.",
|
|
6107
|
+
"preparation": "Static original model; source animation is not present.",
|
|
6108
|
+
"review_status": "local-review",
|
|
6109
|
+
"entry": "Astronaut.glb",
|
|
6110
|
+
"files": [
|
|
6111
|
+
{
|
|
6112
|
+
"path": "Astronaut.glb",
|
|
6113
|
+
"url": "https://raw.githubusercontent.com/google/model-viewer/626f7498f3b8bf1130d0ca0c7b7bb756d6fb89ee/packages/shared-assets/models/Astronaut.glb",
|
|
6114
|
+
"bytes": 2869044,
|
|
6115
|
+
"sha256": "313d48882e2ba7bb25a340dbfd3d6f9bbc7f012372d54f518dc52e4299e3a24e",
|
|
6116
|
+
"integrity": "sha256-MT1IiC4rp7slo0Db/T1vm7x/ASNy1U9RjcUuQpnjok4="
|
|
6117
|
+
}
|
|
6118
|
+
],
|
|
6119
|
+
"inspection": {
|
|
6120
|
+
"triangles": 1604,
|
|
6121
|
+
"primitives": 1,
|
|
6122
|
+
"materials": 1,
|
|
6123
|
+
"extensions": [],
|
|
6124
|
+
"animations": [],
|
|
6125
|
+
"alphaModes": [
|
|
6126
|
+
"OPAQUE"
|
|
6127
|
+
]
|
|
6128
|
+
},
|
|
6129
|
+
"checked": "2026-10-27"
|
|
6130
|
+
}
|
|
6091
6131
|
]
|
|
6092
6132
|
}
|
package/model-library/index.html
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
<p id="quality-help">Hold the camera still to refine. Higher targets reduce noise; more bounces reveal indirect light. HD, QHD and 4K trace at the selected size, even in a smaller window. Higher resolution costs more GPU time; use one sample for a quick inspection.</p>
|
|
30
30
|
</fieldset>
|
|
31
31
|
<div class="refinement"><label id="refinement" for="sample-progress">Waiting for a model…</label><progress id="sample-progress" max="64" value="0"></progress></div>
|
|
32
|
-
<div class="canvas-wrap"><canvas id="canvas" width="800" height="600" tabindex="0" aria-label="Interactive Plasius model view" aria-describedby="help"></canvas><p id="hint" hidden>Drag to orbit · Shift-drag to move · Scroll to approach<br>Touch: drag or pinch · Keyboard: arrows, + / −, Home</p></div><p id="status" role="status" aria-live="polite">Preparing the review…</p></section></div>
|
|
32
|
+
<div class="canvas-wrap"><canvas id="canvas" width="800" height="600" tabindex="0" aria-label="Interactive Plasius model view" aria-describedby="help"></canvas><p id="hint" hidden>Drag to orbit · Shift-drag to move · Scroll to approach<br>Touch: drag or pinch · Keyboard: arrows, + / −, Home</p></div><p id="status" role="status" aria-live="polite">Preparing the review…</p><button id="retry" type="button" hidden>Retry selected scene</button></section></div>
|
|
33
33
|
<p class="footnote">Source models join this workshop in review batches. Gem is excluded from this rollout; no substitute is shown.</p></main>
|
|
34
34
|
<script type="module">import { mountLibrary } from './main.js'; mountLibrary().catch(error => { document.querySelector('#status').textContent = `Unable to open review: ${error.message}`; });</script>
|
|
35
35
|
</body></html>
|
package/model-library/main.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createReviewController, fixtureRequest, reviewEnabled } from './controller.js';
|
|
2
|
-
import { compositeStudies, compositeRequest, renderCompositeCredits } from './scenes.js';
|
|
2
|
+
import { compositeStudies, denseStudies, compositeRequest, renderCompositeCredits } from './scenes.js';
|
|
3
3
|
|
|
4
4
|
export async function mountLibrary({document:doc=globalThis.document,window:win=globalThis.window,
|
|
5
5
|
fetch:request=globalThis.fetch,importRuntime=()=>import('@plasius/gpu-shared/model-inspection')}={}) {
|
|
@@ -10,8 +10,8 @@ export async function mountLibrary({document:doc=globalThis.document,window:win=
|
|
|
10
10
|
}
|
|
11
11
|
const response=await request(new URL('./catalogue.json',win.location.href));
|
|
12
12
|
if (!response.ok) throw new Error('Model catalogue could not be loaded.');
|
|
13
|
-
const
|
|
14
|
-
const studies=compositeStudies(assets), selections=[...assets,...studies];
|
|
13
|
+
const catalogue=await response.json(),assets=[...catalogue.assets,...(catalogue.benchmarkAssets??[])];
|
|
14
|
+
const studies=[...compositeStudies(assets),...denseStudies(assets)], selections=[...assets,...studies];
|
|
15
15
|
for(const asset of selections) {
|
|
16
16
|
const option=doc.createElement('option');option.value=asset.key;
|
|
17
17
|
option.textContent=asset.name+(asset.models?' — composite study':asset.entry?'':' — deferred');el('model').append(option);
|
|
@@ -26,7 +26,13 @@ export async function mountLibrary({document:doc=globalThis.document,window:win=
|
|
|
26
26
|
const refinement=p=>{
|
|
27
27
|
el('sample-progress').max=p.target;el('sample-progress').value=p.samples;
|
|
28
28
|
const phase={complete:'Complete',paused:'Paused',rendering:'Rendering',refining:'Refining'}[p.phase];
|
|
29
|
-
el('refinement').textContent=`${phase} · ${p.samples} / ${p.target} samples · ${p.width} × ${p.height}`;
|
|
29
|
+
el('refinement').textContent=`${phase} · ${p.samples} / ${p.target} samples · ${p.width} × ${p.height}${p.allocations?` · ${(p.allocations.allocatedBytes/1048576).toFixed(1)} MiB requested GPU storage${p.allocations.budgetBytes?` / ${(p.allocations.budgetBytes/1048576).toFixed(0)} MiB budget`:""}`:""}`;
|
|
30
|
+
};
|
|
31
|
+
let stateKind='loading';
|
|
32
|
+
const failed=error=>{
|
|
33
|
+
stateKind='error';status.textContent=`Render failed: ${error.message}`;
|
|
34
|
+
el('refinement').textContent='Refinement stopped. Adjust quality, then retry.';
|
|
35
|
+
el('quality').disabled=false;el('retry').hidden=false;
|
|
30
36
|
};
|
|
31
37
|
const controller=createReviewController({
|
|
32
38
|
load:async(asset,signal)=>{
|
|
@@ -37,16 +43,17 @@ export async function mountLibrary({document:doc=globalThis.document,window:win=
|
|
|
37
43
|
return inspect({canvas:el('canvas'),...request,signal,enabled:true,
|
|
38
44
|
...quality(),quality:quality(),
|
|
39
45
|
onProgress:p=>{if(!signal.aborted)refinement(p);},
|
|
40
|
-
onHintChange:visible=>{el('hint').hidden=!visible;},
|
|
41
|
-
onError:error=>{if(signal.aborted)
|
|
46
|
+
onHintChange:visible=>{if(!signal.aborted)el('hint').hidden=!visible;},
|
|
47
|
+
onError:error=>{if(!signal.aborted)failed(error);}});
|
|
42
48
|
},
|
|
43
49
|
onState:state=>{
|
|
44
|
-
|
|
50
|
+
stateKind=state.kind;el('retry').hidden=state.kind!=='error';
|
|
51
|
+
el('hint').hidden=true;el('quality').disabled=!['ready','error'].includes(state.kind);
|
|
45
52
|
if(state.kind==='loading'){el('sample-progress').value=0;el('refinement').textContent='Waiting for the first sample…';}
|
|
46
53
|
if(state.kind==='loading')status.textContent=`Loading ${state.asset.name} and checking its files…`;
|
|
47
54
|
else if(state.kind==='ready') {
|
|
48
55
|
status.textContent=`${state.asset.review_status==='needs-fix'?'Rendered — visual defect recorded':'Ready'} · ${state.asset.name} · ${state.runtime.triangleCount.toLocaleString()} triangles · ${state.runtime.primitiveCount} mesh primitives`;
|
|
49
|
-
el('metrics').textContent=`${state.runtime.triangleCount.toLocaleString()} triangles · ${state.asset.inspection.materials} source materials${state.asset.models?` · ${state.asset.models.length} models + shared ground`:''}`;
|
|
56
|
+
el('metrics').textContent=`${state.runtime.triangleCount.toLocaleString()} triangles · ${state.asset.inspection.materials} source materials${state.asset.models?` · ${state.asset.models.length} models + shared ground${state.runtime.geometryMode==='instanced'?` · ${state.runtime.sourceCount} unique sources · ${state.runtime.instanceCount} mesh instances · shared geometry`:""}`:''}`;
|
|
50
57
|
} else {status.textContent=state.message;el('canvas').hidden=true;el('refinement').textContent='Refinement unavailable';}
|
|
51
58
|
},
|
|
52
59
|
});
|
|
@@ -60,7 +67,7 @@ export async function mountLibrary({document:doc=globalThis.document,window:win=
|
|
|
60
67
|
if(!asset.models){el('source').href=asset.source_url;el('licence').href=asset.licence_url;el('licence').textContent=asset.licence;}
|
|
61
68
|
el('metrics').textContent=asset.entry?`${(asset.files.reduce((n,f)=>n+f.bytes,0)/1048576).toFixed(1)} MiB on disk`:'';
|
|
62
69
|
const clips=asset.inspection?.animations??[];
|
|
63
|
-
el('note').textContent=asset.models?`${asset.models.length} original models share one camera, ground and lighting. Geometry counts include the ground
|
|
70
|
+
el('note').textContent=asset.models?(asset.instancing?`${asset.models.length} model placements share three sources, one camera, ground and lighting. Geometry counts are unique; requested GPU storage excludes driver overhead and other tabs.`:`${asset.models.length} original models share one camera, ground and lighting. Geometry counts include the ground.`):
|
|
64
71
|
clips.length?`Static pose only. ${clips.length} source clips are present: ${clips.join(', ')}. Animation playback needs importer work.`:
|
|
65
72
|
asset.inspection?.extensions.length?`Material features to inspect: ${asset.inspection.extensions.join(', ')}.`:'Source materials under the same inspection lighting.';
|
|
66
73
|
return controller.select(asset);
|
|
@@ -68,9 +75,11 @@ export async function mountLibrary({document:doc=globalThis.document,window:win=
|
|
|
68
75
|
const requested=new URL(win.location.href).searchParams.get('model');
|
|
69
76
|
el('model').value=selections.find(a=>a.key===requested&&(a.entry||a.models))?.key??assets.find(a=>a.entry).key;
|
|
70
77
|
el('model').addEventListener('change',change);
|
|
78
|
+
el('retry').addEventListener('click',change);
|
|
71
79
|
el('model').disabled=false;
|
|
72
80
|
const qualityChange=async()=>{
|
|
73
|
-
|
|
81
|
+
if(stateKind!=='ready')return;
|
|
82
|
+
try{await controller.setQuality(quality());}catch(error){failed(error);}
|
|
74
83
|
};
|
|
75
84
|
for(const id of ['samples','bounces','resolution','denoise'])el(id).addEventListener('change',qualityChange);
|
|
76
85
|
const visibility=()=>controller.setPaused(doc.hidden===true);
|
|
@@ -78,7 +87,7 @@ export async function mountLibrary({document:doc=globalThis.document,window:win=
|
|
|
78
87
|
let resizeTimer;
|
|
79
88
|
const resize=()=>{clearTimeout(resizeTimer);resizeTimer=setTimeout(qualityChange,250);};
|
|
80
89
|
win.addEventListener('resize',resize);
|
|
81
|
-
const destroy=()=>{for(const id of ['samples','bounces','resolution','denoise'])el(id).removeEventListener('change',qualityChange);doc.removeEventListener('visibilitychange',visibility);clearTimeout(resizeTimer);controller.destroy();el('model').removeEventListener('change',change);win.removeEventListener('pagehide',destroy);win.removeEventListener('resize',resize);};
|
|
90
|
+
const destroy=()=>{for(const id of ['samples','bounces','resolution','denoise'])el(id).removeEventListener('change',qualityChange);doc.removeEventListener('visibilitychange',visibility);clearTimeout(resizeTimer);controller.destroy();el('model').removeEventListener('change',change);el('retry').removeEventListener('click',change);win.removeEventListener('pagehide',destroy);win.removeEventListener('resize',resize);};
|
|
82
91
|
win.addEventListener('pagehide',destroy);
|
|
83
92
|
await change();return {destroy};
|
|
84
93
|
}
|
package/model-library/scenes.js
CHANGED
|
@@ -1,31 +1,44 @@
|
|
|
1
1
|
import { fixtureRequest } from './controller.js';
|
|
2
|
-
import { compositeStudyDefinitions } from './studies.js';
|
|
2
|
+
import { compositeStudyDefinitions, denseStudyDefinitions } from './studies.js';
|
|
3
3
|
|
|
4
4
|
export function compositeStudies(assets) {
|
|
5
|
-
return compositeStudyDefinitions()
|
|
5
|
+
return resolveStudies(compositeStudyDefinitions(),assets);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function denseStudies(assets) {
|
|
9
|
+
return resolveStudies(denseStudyDefinitions(),assets);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function resolveStudies(definitions,assets) {
|
|
13
|
+
return definitions.flatMap(({ placements, ...definition }) => {
|
|
6
14
|
const models = placements.map(([key, anchor, size, yaw]) => ({
|
|
7
15
|
asset: assets.find(a => a.key === key), anchor: [...anchor], size, yaw,
|
|
8
16
|
}));
|
|
9
17
|
if (models.some(model => !model.asset?.entry)) return [];
|
|
10
18
|
return [{ ...definition, category: 'Composite study', models,
|
|
11
|
-
inspection: { materials: models.reduce((sum,
|
|
19
|
+
inspection: { materials: [...new Map(models.map(model=>[model.asset.key,model.asset])).values()].reduce((sum, asset) => sum + asset.inspection.materials, 0) } }];
|
|
12
20
|
});
|
|
13
21
|
}
|
|
14
22
|
|
|
15
23
|
export function compositeRequest(study, base) {
|
|
16
|
-
return { models: study.models.map(({ asset, anchor, size, yaw }) => ({
|
|
24
|
+
return { ...(study.instancing?{instancing:true,camera:study.camera}:{}), models: study.models.map(({ asset, anchor, size, yaw }) => ({
|
|
17
25
|
...fixtureRequest(asset, base), anchor, size, yaw,
|
|
18
26
|
})) };
|
|
19
27
|
}
|
|
20
28
|
|
|
21
29
|
export function renderCompositeCredits(doc, container, models) {
|
|
22
30
|
container.replaceChildren();
|
|
23
|
-
|
|
31
|
+
const sources=new Map();
|
|
32
|
+
for(const {asset,size} of models) {
|
|
33
|
+
const source=sources.get(asset.key)??{asset,sizes:new Set(),count:0};
|
|
34
|
+
source.sizes.add(size);source.count++;sources.set(asset.key,source);
|
|
35
|
+
}
|
|
36
|
+
for (const { asset, sizes, count } of sources.values()) {
|
|
24
37
|
const item = doc.createElement('li'), source = doc.createElement('a'), licence = doc.createElement('a');
|
|
25
38
|
source.textContent = `${asset.name} — ${asset.authors}`; source.href = asset.source_url;
|
|
26
39
|
licence.textContent = asset.licence; licence.href = asset.licence_url;
|
|
27
40
|
for (const link of [source, licence]) { link.target = '_blank'; link.rel = 'noopener'; }
|
|
28
|
-
item.append(source, ' · ', licence, ` · longest dimension ${
|
|
41
|
+
item.append(source, ' · ', licence, ` · ${count} placement${count===1?'':'s'} · longest dimension ${[...sizes].join(', ')} scene units`);
|
|
29
42
|
container.append(item);
|
|
30
43
|
}
|
|
31
44
|
}
|
|
@@ -4,7 +4,11 @@ export interface CompositeStudyDefinition {
|
|
|
4
4
|
name: string;
|
|
5
5
|
renderer_target: string;
|
|
6
6
|
preparation: string;
|
|
7
|
+
instancing?: boolean;
|
|
8
|
+
camera?: { eye: [number, number, number]; target: [number, number, number]; fov: number };
|
|
7
9
|
/** Source key, bottom-centre anchor, longest dimension in scene units, Y rotation in radians. */
|
|
8
10
|
placements: [string, [number, number, number], number, number][];
|
|
9
11
|
}
|
|
10
12
|
export function compositeStudyDefinitions(): CompositeStudyDefinition[];
|
|
13
|
+
|
|
14
|
+
export function denseStudyDefinitions(): CompositeStudyDefinition[];
|
package/model-library/studies.js
CHANGED
|
@@ -49,3 +49,18 @@ export function compositeStudyDefinitions() {
|
|
|
49
49
|
placements: placements.map(([key, anchor, size, yaw]) => [key, [...anchor], size, yaw]),
|
|
50
50
|
}));
|
|
51
51
|
}
|
|
52
|
+
|
|
53
|
+
/** Dense static scenes share three source models; shrub placements retain all authored variants. */
|
|
54
|
+
export function denseStudyDefinitions() {
|
|
55
|
+
return [50,100].map(count => ({
|
|
56
|
+
key: `study-astronaut-grove-${count}`, name: `Astronaut grove — ${count} astronauts`,
|
|
57
|
+
renderer_target: `Trace ${count} astronauts, six trees and twelve shrub groups with shared geometry.`,
|
|
58
|
+
preparation: 'Inspect overlapping silhouettes, vegetation transparency and contact shadows. Each shrub group retains seven authored variants. Static instances; dimensions are chosen scene units.',
|
|
59
|
+
instancing: true, camera: {eye:[25,22,33],target:[0,1,0],fov:42},
|
|
60
|
+
placements: [
|
|
61
|
+
...Array.from({length:count},(_,i)=>['Astronaut',[(i%10-4.5)*1.75,0,(Math.floor(i/10)-(count/10-1)/2)*1.75],1.8,(i*.618)%(Math.PI*2)]),
|
|
62
|
+
...Array.from({length:6},(_,i)=>['quiver_tree_01',[i%2?12:-12,0,Math.floor(i/2)*9-9],7,(i*.618)%(Math.PI*2)]),
|
|
63
|
+
...Array.from({length:12},(_,i)=>['searsia_lucida',[i%2?10.5:-10.5,0,Math.floor(i/2)*4-10],3,(i*.618)%(Math.PI*2)]),
|
|
64
|
+
],
|
|
65
|
+
}));
|
|
66
|
+
}
|
package/model-library/styles.css
CHANGED
|
@@ -7,3 +7,5 @@
|
|
|
7
7
|
|
|
8
8
|
/* Preserve the traced camera aspect when fixed-size frames fit the viewport. */
|
|
9
9
|
canvas{object-fit:contain}
|
|
10
|
+
|
|
11
|
+
#retry{min-height:44px;padding:.7rem 1rem;background:#222d37;color:#f2eee5;border:1px solid #dfc88d;border-radius:.35rem;font:inherit;cursor:pointer}#retry:focus-visible{outline:3px solid #edd7a1;outline-offset:3px}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plasius/gpu-demo-viewer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Root validation viewer for Plasius gpu-* demos.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
"test:coverage": "c8 --reporter=lcov --reporter=text node --test"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@plasius/gpu-renderer": "^0.2.
|
|
69
|
-
"@plasius/gpu-shared": "1.
|
|
68
|
+
"@plasius/gpu-renderer": "^0.2.57",
|
|
69
|
+
"@plasius/gpu-shared": "1.4.0"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"c8": "^11.0.0",
|
|
@@ -3,9 +3,10 @@ import { fileURLToPath } from 'node:url';
|
|
|
3
3
|
import { acquireFixtures } from '../model-library/acquire.js';
|
|
4
4
|
import { selectBatch } from '../model-library/rollout.js';
|
|
5
5
|
export async function run(acquire=acquireFixtures,batch=0) {
|
|
6
|
-
const {assets}=JSON.parse(await readFile(new URL('../model-library/catalogue.json',import.meta.url),'utf8'));
|
|
6
|
+
const {assets,benchmarkAssets=[]}=JSON.parse(await readFile(new URL('../model-library/catalogue.json',import.meta.url),'utf8'));
|
|
7
|
+
if(batch==='crowd')return acquire({assets:[...benchmarkAssets,...assets.filter(a=>['quiver_tree_01','searsia_lucida'].includes(a.key))],root:fileURLToPath(new URL('../model-library/assets/',import.meta.url))});
|
|
7
8
|
const {entries}=JSON.parse(await readFile(new URL('../model-library/rollout.json',import.meta.url),'utf8'));
|
|
8
9
|
const keys=new Set(selectBatch(entries,batch).map(entry=>entry.key));
|
|
9
10
|
return acquire({assets:assets.filter(asset=>keys.has(asset.key)),root:fileURLToPath(new URL('../model-library/assets/',import.meta.url))});
|
|
10
11
|
}
|
|
11
|
-
if(process.argv[1]===fileURLToPath(import.meta.url))console.log(await run(undefined,process.argv[2]===undefined?0:Number(process.argv[2])));
|
|
12
|
+
if(process.argv[1]===fileURLToPath(import.meta.url))console.log(await run(undefined,process.argv[2]===undefined?0:process.argv[2]==='crowd'?'crowd':Number(process.argv[2])));
|