@plasius/gpu-demo-viewer 0.1.4 → 0.1.6
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 +50 -0
- package/NFR.md +4 -0
- package/README.md +40 -0
- package/docs/adrs/adr-0002-hosted-oidc-package-publication.md +53 -0
- package/docs/adrs/adr-0003-reviewed-model-library.md +13 -0
- package/docs/adrs/adr-0004-model-quality-controls.md +13 -0
- package/docs/adrs/index.md +3 -0
- package/docs/model-inspection-uhd.md +11 -0
- package/docs/model-library-review.md +38 -0
- package/docs/model-review-release.md +11 -0
- package/model-library/acquire.js +54 -0
- package/model-library/catalogue.json +991 -0
- package/model-library/controller.js +45 -0
- package/model-library/index.html +35 -0
- package/model-library/main.js +76 -0
- package/model-library/styles.css +9 -0
- package/model-library/vite.config.js +12 -0
- package/package.json +14 -6
- package/scripts/acquire-model-library.mjs +8 -0
- package/viewer-manifest.js +15 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export function reviewEnabled(href) {
|
|
2
|
+
const url = new URL(href);
|
|
3
|
+
return ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname)
|
|
4
|
+
&& url.searchParams.get('review') === 'first-twelve';
|
|
5
|
+
}
|
|
6
|
+
export function fixtureRequest(asset, base) {
|
|
7
|
+
const safe = path => typeof path === 'string' && /^[\w./-]+$/u.test(path)
|
|
8
|
+
&& !path.startsWith('/') && !path.split('/').includes('..');
|
|
9
|
+
if (!safe(asset.key) || asset.key.includes('/') || !safe(asset.entry)
|
|
10
|
+
|| !asset.files.every(file => safe(file.path))) throw new Error('Unsafe fixture path.');
|
|
11
|
+
const root = new URL(`assets/${asset.key}/`, base);
|
|
12
|
+
return { modelUrl: new URL(asset.entry, root).href,
|
|
13
|
+
integrity: Object.fromEntries(asset.files.map(f => [new URL(f.path, root).href, f.integrity])) };
|
|
14
|
+
}
|
|
15
|
+
export function createReviewController({ load, onState }) {
|
|
16
|
+
let abort, active, generation = 0, disposed = false, paused = false;
|
|
17
|
+
return {
|
|
18
|
+
async select(asset) {
|
|
19
|
+
if (disposed) return;
|
|
20
|
+
const id = ++generation;
|
|
21
|
+
abort?.abort(); active?.destroy(); active = undefined;
|
|
22
|
+
abort = new AbortController();
|
|
23
|
+
if (!asset.entry) { onState({kind:'missing', asset, message:'Download deferred. This model is not installed.'}); return; }
|
|
24
|
+
onState({kind:'loading',asset});
|
|
25
|
+
try {
|
|
26
|
+
const runtime = await load(asset, abort.signal);
|
|
27
|
+
if (disposed || id !== generation) { runtime.destroy(); return; }
|
|
28
|
+
active = runtime; active.setPaused?.(paused); onState({kind:'ready',asset,runtime});
|
|
29
|
+
} catch (error) {
|
|
30
|
+
if (!disposed && id === generation) onState({kind:'error',asset,message:error.message});
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
async setQuality(quality) {
|
|
34
|
+
const runtime=active;
|
|
35
|
+
try { await runtime?.setQuality(quality); }
|
|
36
|
+
catch(error) { if(!disposed && active===runtime) throw error; }
|
|
37
|
+
},
|
|
38
|
+
setPaused(value) { paused=value;active?.setPaused?.(value); },
|
|
39
|
+
reset() { active?.reset(); },
|
|
40
|
+
destroy() {
|
|
41
|
+
if (disposed) return;
|
|
42
|
+
disposed = true; generation++; abort?.abort(); active?.destroy(); active = undefined;
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
3
|
+
<title>Plasius · Model studies</title><link rel="icon" href="data:,"><link rel="stylesheet" href="./styles.css">
|
|
4
|
+
<script type="importmap">
|
|
5
|
+
{
|
|
6
|
+
"imports": {
|
|
7
|
+
"@plasius/gpu-shared/model-inspection": "../node_modules/@plasius/gpu-shared/dist/model-inspection.js",
|
|
8
|
+
"@plasius/gpu-shared/feedback-diagnostics": "../node_modules/@plasius/gpu-shared/dist/feedback-diagnostics.js",
|
|
9
|
+
"@plasius/gpu-renderer": "../node_modules/@plasius/gpu-renderer/dist/index.js",
|
|
10
|
+
"@plasius/gpu-camera": "../node_modules/@plasius/gpu-camera/dist/index.js",
|
|
11
|
+
"@plasius/gpu-camera-controls": "../node_modules/@plasius/gpu-camera-controls/dist/index.js",
|
|
12
|
+
"@plasius/gpu-camera-controls/browser": "../node_modules/@plasius/gpu-camera-controls/dist/browser.js",
|
|
13
|
+
"@plasius/schema/feedback-diagnostics-vocabulary": "../node_modules/@plasius/schema/dist/feedback-diagnostics-vocabulary.js"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
</script></head>
|
|
17
|
+
<body><header><a href="https://plasius.co.uk/" target="_blank" rel="noopener">PLASIUS</a><span>Renderer workshop · Local review</span></header>
|
|
18
|
+
<main><div class="intro"><p class="eyebrow">From source to scene</p><h1>Model studies</h1><p>Eleven assets. One renderer. Explore the surfaces, silhouettes and details that will shape our next improvements.</p></div>
|
|
19
|
+
<div class="workspace"><aside><label for="model">Choose a model</label><select id="model" disabled></select><p id="category" class="muted"></p><h2 id="name"></h2><p id="target"></p><p id="note"></p><div id="metrics" class="metrics"></div>
|
|
20
|
+
<details><summary>Source and licence</summary><p id="credits"></p><p><a id="source" target="_blank" rel="noopener">Original model</a> · <a id="licence" target="_blank" rel="noopener"></a></p><p>Original files retained. Framing is normalized for inspection. Powered by Poly Haven for its asset metadata.</p></details>
|
|
21
|
+
<details><summary>Camera help</summary><p id="help">Drag to orbit. Shift-drag or right-drag to truck. Alt-drag to pan. Scroll to move closer; Shift-scroll changes the lens. Touch: one finger orbits; two fingers move and pinch. Keyboard: arrows orbit, Shift-arrows truck, + / − move closer or further, Home resets. Keys 1–5 choose orbit, truck, pan, dolly or zoom. Press ? for a brief reminder.</p></details>
|
|
22
|
+
<details open><summary>Review focus</summary><p id="review"></p><p>This is an inspection pass, not a visual-quality approval. Source animations are retained on disk; this view displays static geometry.</p></details></aside>
|
|
23
|
+
<section class="stage" aria-label="Model inspection">
|
|
24
|
+
<fieldset id="quality" class="quality" disabled><legend>Render quality</legend>
|
|
25
|
+
<div><label for="samples">Sample target</label><select id="samples" aria-describedby="quality-help"><option>1</option><option>8</option><option>16</option><option>32</option><option selected>64</option><option>128</option><option>256</option></select></div>
|
|
26
|
+
<div><label for="bounces">Light bounces</label><select id="bounces"><option>2</option><option>4</option><option selected>6</option><option>8</option><option>10</option><option>12</option></select></div>
|
|
27
|
+
<div><label for="resolution">Render size</label><select id="resolution"><option value="480">480 px · Quick</option><option value="800" selected>800 px · Balanced</option><option value="1280">1280 px · Detailed</option><option value="1920x1080">1920 × 1080 · Full HD</option><option value="2560x1440">2560 × 1440 · QHD</option><option value="3840x2160">3840 × 2160 · 4K UHD</option></select></div>
|
|
28
|
+
<label class="toggle" for="denoise"><input id="denoise" type="checkbox" checked> Denoising</label>
|
|
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
|
+
</fieldset>
|
|
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>
|
|
33
|
+
<p class="footnote">Gem is deferred pending its Sketchfab download. No replacement models are shown.</p></main>
|
|
34
|
+
<script type="module">import { mountLibrary } from './main.js'; mountLibrary().catch(error => { document.querySelector('#status').textContent = `Unable to open review: ${error.message}`; });</script>
|
|
35
|
+
</body></html>
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { createReviewController, fixtureRequest, reviewEnabled } from './controller.js';
|
|
2
|
+
|
|
3
|
+
export async function mountLibrary({document:doc=globalThis.document,window:win=globalThis.window,
|
|
4
|
+
fetch:request=globalThis.fetch,importRuntime=()=>import('@plasius/gpu-shared/model-inspection')}={}) {
|
|
5
|
+
const el=id=>doc.getElementById(id),status=el('status');
|
|
6
|
+
if (!reviewEnabled(win.location.href)) {
|
|
7
|
+
status.textContent='Local review is disabled. Open this page on localhost with ?review=first-twelve.';
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const response=await request(new URL('./catalogue.json',win.location.href));
|
|
11
|
+
if (!response.ok) throw new Error('Model catalogue could not be loaded.');
|
|
12
|
+
const {assets}=await response.json();
|
|
13
|
+
for(const asset of assets) {
|
|
14
|
+
const option=doc.createElement('option');option.value=asset.key;
|
|
15
|
+
option.textContent=asset.name+(asset.entry?'':' — deferred');el('model').append(option);
|
|
16
|
+
}
|
|
17
|
+
const {createModelInspection}=await importRuntime();
|
|
18
|
+
el('samples').value='64';el('bounces').value='6';el('resolution').value='800';el('denoise').checked=true;
|
|
19
|
+
const quality=()=>{
|
|
20
|
+
const [width,fixedHeight]=el('resolution').value.split('x').map(Number);
|
|
21
|
+
return {samplesPerPixel:Number(el('samples').value),maxDepth:Number(el('bounces').value),denoise:el('denoise').checked,
|
|
22
|
+
width,height:fixedHeight??Math.max(1,Math.min(1440,Math.round(width*el('canvas').clientHeight/el('canvas').clientWidth)))};
|
|
23
|
+
};
|
|
24
|
+
const refinement=p=>{
|
|
25
|
+
el('sample-progress').max=p.target;el('sample-progress').value=p.samples;
|
|
26
|
+
const phase={complete:'Complete',paused:'Paused',rendering:'Rendering',refining:'Refining'}[p.phase];
|
|
27
|
+
el('refinement').textContent=`${phase} · ${p.samples} / ${p.target} samples · ${p.width} × ${p.height}`;
|
|
28
|
+
};
|
|
29
|
+
const controller=createReviewController({
|
|
30
|
+
load:async(asset,signal)=>{
|
|
31
|
+
const request=fixtureRequest(asset,new URL('./',win.location.href));
|
|
32
|
+
el('canvas').hidden=false;
|
|
33
|
+
return createModelInspection({canvas:el('canvas'),...request,signal,enabled:true,
|
|
34
|
+
...quality(),quality:quality(),
|
|
35
|
+
onProgress:p=>{if(!signal.aborted)refinement(p);},
|
|
36
|
+
onHintChange:visible=>{el('hint').hidden=!visible;},
|
|
37
|
+
onError:error=>{if(signal.aborted)return;status.textContent=`Render failed: ${error.message}`;el('refinement').textContent='Refinement stopped';el('quality').disabled=true;}});
|
|
38
|
+
},
|
|
39
|
+
onState:state=>{
|
|
40
|
+
el('hint').hidden=true;el('quality').disabled=state.kind!=='ready';
|
|
41
|
+
if(state.kind==='loading'){el('sample-progress').value=0;el('refinement').textContent='Waiting for the first sample…';}
|
|
42
|
+
if(state.kind==='loading')status.textContent=`Loading ${state.asset.name} and checking its files…`;
|
|
43
|
+
else if(state.kind==='ready') {
|
|
44
|
+
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`;
|
|
45
|
+
el('metrics').textContent=`${state.runtime.triangleCount.toLocaleString()} triangles · ${state.asset.inspection.materials} source materials`;
|
|
46
|
+
} else {status.textContent=state.message;el('canvas').hidden=true;el('refinement').textContent='Refinement unavailable';}
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
const change=()=>{
|
|
50
|
+
const asset=assets.find(a=>a.key===el('model').value);
|
|
51
|
+
el('category').textContent=asset.category;el('name').textContent=asset.name;
|
|
52
|
+
el('target').textContent=asset.renderer_target;el('review').textContent=asset.observed_issue??asset.preparation;
|
|
53
|
+
el('credits').textContent=asset.authors;el('source').href=asset.source_url;
|
|
54
|
+
el('licence').href=asset.licence_url;el('licence').textContent=asset.licence;
|
|
55
|
+
el('metrics').textContent=asset.entry?`${(asset.files.reduce((n,f)=>n+f.bytes,0)/1048576).toFixed(1)} MiB on disk`:'';
|
|
56
|
+
const clips=asset.inspection?.animations??[];
|
|
57
|
+
el('note').textContent=clips.length?`Static pose only. ${clips.length} source clips are present: ${clips.join(', ')}. Animation playback needs importer work.`:
|
|
58
|
+
asset.inspection?.extensions.length?`Material features to inspect: ${asset.inspection.extensions.join(', ')}.`:'Source materials under the same inspection lighting.';
|
|
59
|
+
return controller.select(asset);
|
|
60
|
+
};
|
|
61
|
+
el('model').value=assets.find(a=>a.entry).key;
|
|
62
|
+
el('model').addEventListener('change',change);
|
|
63
|
+
el('model').disabled=false;
|
|
64
|
+
const qualityChange=async()=>{
|
|
65
|
+
try{await controller.setQuality(quality());}catch(error){status.textContent=`Render failed: ${error.message}`;}
|
|
66
|
+
};
|
|
67
|
+
for(const id of ['samples','bounces','resolution','denoise'])el(id).addEventListener('change',qualityChange);
|
|
68
|
+
const visibility=()=>controller.setPaused(doc.hidden===true);
|
|
69
|
+
doc.addEventListener('visibilitychange',visibility);visibility();
|
|
70
|
+
let resizeTimer;
|
|
71
|
+
const resize=()=>{clearTimeout(resizeTimer);resizeTimer=setTimeout(qualityChange,250);};
|
|
72
|
+
win.addEventListener('resize',resize);
|
|
73
|
+
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);};
|
|
74
|
+
win.addEventListener('pagehide',destroy);
|
|
75
|
+
await change();return {destroy};
|
|
76
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
:root{color-scheme:dark;font-family:system-ui,sans-serif;background:#10151c;color:#f2eee5;font-size:16px}*{box-sizing:border-box}body{margin:0}header{display:flex;justify-content:space-between;gap:1rem;padding:1.4rem 4vw;border-bottom:1px solid #39444d}header>a{letter-spacing:.22em;font-family:Georgia,serif;text-decoration:none}header span,.muted,.footnote{color:#b8c4cc}a{color:#dfc88d}main{max-width:1560px;margin:auto;padding:2rem 4vw}h1,h2{font-family:Georgia,serif;font-weight:400}h1{font-size:clamp(2rem,4vw,3rem);margin:.3rem 0}.intro>p:last-child{max-width:720px;color:#c1cbd1;line-height:1.6}.eyebrow{text-transform:uppercase;letter-spacing:.18em;color:#dfc88d;font-size:.75rem}.workspace{display:grid;grid-template-columns:minmax(240px,300px) minmax(0,1fr);gap:2rem;margin-top:2rem}aside{line-height:1.6}label{display:block;margin-bottom:.4rem}select{width:100%;padding:.8rem;background:#222d37;color:#fff;border:1px solid #64747f;border-radius:.35rem;font-size:1rem}h2{font-size:1.8rem;margin:1rem 0}.stage{min-width:0}.canvas-wrap{position:relative;background:#202e3c;border:1px solid #596775;border-radius:.75rem;overflow:hidden}canvas{display:block;width:100%;height:auto;aspect-ratio:4/3;touch-action:none;cursor:grab}canvas:active{cursor:grabbing}canvas:focus-visible{outline:3px solid #edd7a1;outline-offset:-5px}a:focus-visible,summary:focus-visible,select:focus-visible{outline:3px solid #edd7a1;outline-offset:3px}#hint{position:absolute;bottom:1rem;left:1rem;right:1rem;text-align:center;background:#10151cef;color:#f4ecdb;padding:1rem;border:1px solid #9d916d;border-radius:.5rem;pointer-events:none;font-size:.9rem}#status{padding:1rem;background:#1c2630;border-radius:.4rem;line-height:1.5;min-height:4rem;margin-top:1rem}.metrics{color:#dfc88d;margin:1rem 0}details{border-top:1px solid #3a4651;padding:.8rem 0}summary{cursor:pointer;min-height:28px}details p{font-size:.88rem;color:#c7d0d7;overflow-wrap:anywhere}.footnote{font-size:.86rem;line-height:1.5;margin-top:2rem}@media(max-width:780px){header{flex-wrap:wrap}.workspace{display:flex;flex-direction:column}.stage{order:-1}main{padding:1.3rem 5vw}h1{font-size:2.2rem}}@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto}}
|
|
2
|
+
|
|
3
|
+
@media(min-width:781px){.intro{display:grid;grid-template-columns:auto 1fr;column-gap:2rem;align-items:end}.intro .eyebrow{grid-column:1/-1;margin:0}.intro>p:last-child{max-width:660px;margin:.3rem 0}.workspace{margin-top:1.4rem}.canvas-wrap{height:clamp(320px,calc(100dvh - 285px),760px)}canvas{width:100%;height:100%;aspect-ratio:auto}}
|
|
4
|
+
|
|
5
|
+
.quality{display:grid;grid-template-columns:1fr 1fr 1.5fr auto;gap:.8rem;align-items:center;margin:0 0 1rem;padding:1rem;border:1px solid #596775;border-radius:.6rem;min-width:0}
|
|
6
|
+
.quality legend{padding:0 .5rem;color:#dfc88d}.quality label{font-size:.9rem}.quality select{min-width:0;padding:.6rem}.quality>div{min-width:0}.quality .toggle{display:flex;align-items:center;gap:.5rem;margin-top:1.2rem;min-height:44px;cursor:pointer}.toggle input{width:1.2rem;height:1.2rem;accent-color:#dfc88d}.quality p{grid-column:1/-1;font-size:.8rem;line-height:1.5;color:#c1cbd1;margin:.3rem 0 0}input:focus-visible{outline:3px solid #edd7a1;outline-offset:3px}.refinement{display:flex;align-items:center;justify-content:space-between;gap:1rem;margin-bottom:.8rem;font-size:.85rem;color:#dfc88d}.refinement label{margin:0}progress{width:120px;max-width:30%;height:8px;accent-color:#dfc88d}.quality:disabled{opacity:.65}@media(max-width:1100px){.quality{grid-template-columns:1fr 1fr}.quality .toggle{margin-top:1rem}}@media(min-width:781px){.canvas-wrap{height:clamp(300px,calc(100dvh - 505px),760px)}}
|
|
7
|
+
|
|
8
|
+
/* Preserve the traced camera aspect when fixed-size frames fit the viewport. */
|
|
9
|
+
canvas{object-fit:contain}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { isAbsolute } from 'node:path';
|
|
2
|
+
const source=process.env.PLASIUS_SHARED_INSPECTION_SOURCE;
|
|
3
|
+
const renderer=process.env.PLASIUS_RENDERER_SOURCE;
|
|
4
|
+
if(renderer && !isAbsolute(renderer))throw new Error('Renderer source override must be an absolute development path.');
|
|
5
|
+
if(source && !isAbsolute(source))throw new Error('Inspection source override must be an absolute development path.');
|
|
6
|
+
export default {
|
|
7
|
+
root: new URL('.', import.meta.url).pathname,
|
|
8
|
+
cacheDir: '../node_modules/.vite-model-library',
|
|
9
|
+
resolve: { alias: {...(source ? { '@plasius/gpu-shared/model-inspection':source } : {}),...(renderer ? {'@plasius/gpu-renderer':renderer} : {})} },
|
|
10
|
+
server: { host: '127.0.0.1', port: 5291, strictPort: true, watch: { usePolling: true },
|
|
11
|
+
fs:{allow:[new URL('..',import.meta.url).pathname,...(source?[source.replace(/\/src\/model-inspection\.js$/,'')]:[]),...(renderer?[renderer.replace(/\/(src|dist)\/index\.js$/,'')]:[])]} },
|
|
12
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plasius/gpu-demo-viewer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Root validation viewer for Plasius gpu-* demos.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
"index.js",
|
|
34
34
|
"main.js",
|
|
35
35
|
"styles.css",
|
|
36
|
-
"viewer-manifest.js"
|
|
36
|
+
"viewer-manifest.js",
|
|
37
|
+
"model-library/"
|
|
37
38
|
],
|
|
38
39
|
"engines": {
|
|
39
40
|
"node": ">=24"
|
|
@@ -45,11 +46,18 @@
|
|
|
45
46
|
"lint": "npm run typecheck",
|
|
46
47
|
"pack:check": "node scripts/verify-public-package.cjs",
|
|
47
48
|
"prepublishOnly": "npm run build && npm run pack:check",
|
|
48
|
-
"typecheck": "node --check index.js && node --check main.js && node --check viewer-manifest.js && node --check showcase/index.js && node --check showcase/main.js && node --check wavefront/main.js",
|
|
49
|
-
"test": "node --test"
|
|
49
|
+
"typecheck": "node --check index.js && node --check main.js && node --check viewer-manifest.js && node --check showcase/index.js && node --check showcase/main.js && node --check wavefront/main.js && node --check model-library/main.js && node --check model-library/controller.js && node --check model-library/acquire.js",
|
|
50
|
+
"test": "node --test",
|
|
51
|
+
"models:download": "node scripts/acquire-model-library.mjs",
|
|
52
|
+
"models:dev": "vite --config model-library/vite.config.js",
|
|
53
|
+
"test:coverage": "c8 --reporter=lcov --reporter=text node --test"
|
|
50
54
|
},
|
|
51
55
|
"dependencies": {
|
|
52
|
-
"@plasius/gpu-renderer": "^0.2.
|
|
53
|
-
"@plasius/gpu-shared": "^1.
|
|
56
|
+
"@plasius/gpu-renderer": "^0.2.49",
|
|
57
|
+
"@plasius/gpu-shared": "^1.1.7"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"c8": "^11.0.0",
|
|
61
|
+
"vite": "^8.1.4"
|
|
54
62
|
}
|
|
55
63
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { acquireFixtures } from '../model-library/acquire.js';
|
|
4
|
+
export async function run(acquire=acquireFixtures) {
|
|
5
|
+
const {assets}=JSON.parse(await readFile(new URL('../model-library/catalogue.json',import.meta.url),'utf8'));
|
|
6
|
+
return acquire({assets,root:fileURLToPath(new URL('../model-library/assets/',import.meta.url))});
|
|
7
|
+
}
|
|
8
|
+
if(process.argv[1]===fileURLToPath(import.meta.url))console.log(await run());
|
package/viewer-manifest.js
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
export const demos = [
|
|
2
|
+
{
|
|
3
|
+
id: "model-library",
|
|
4
|
+
packageName: "@plasius/gpu-demo-viewer",
|
|
5
|
+
type: "browser",
|
|
6
|
+
mode: "Reviewed model inspection",
|
|
7
|
+
runtime: "Shared WebGPU model inspection",
|
|
8
|
+
launchLabel: "Open model studies",
|
|
9
|
+
launchPath: "./model-library/?review=first-twelve",
|
|
10
|
+
sourcePath: "./model-library/main.js",
|
|
11
|
+
docsPath: "./docs/model-library-review.md",
|
|
12
|
+
command: "npm run models:download && npm run models:dev",
|
|
13
|
+
summary: "Inspect eleven acquired models with original materials, source credits and explicit rendering findings.",
|
|
14
|
+
notes: ["Requires the released shared model-inspection entrypoint, or the documented local source override.", "Gem remains deferred. Fox is static; amber has a recorded material defect."],
|
|
15
|
+
tags: ["browser", "3d", "experimental"]
|
|
16
|
+
},
|
|
2
17
|
{
|
|
3
18
|
id: "integration-showcase",
|
|
4
19
|
packageName: "@plasius/gpu-demo-viewer",
|