Files
editor/packages/viewer/src/lib/ktx2-loader.ts
T
Wassim SAMADandClaude Opus 4.8 042f855586 feat(paint-slots): KTX2 finish library — fabric/leather/concrete/metal (phase 4)
Wire KTX2 into the catalog finish path and add 15 textured finishes across four
new families, all as GPU-compressed KTX2 (512px) + a webp picker thumbnail.

- Shared `ktx2-loader.ts`: one KTX2Loader (Basis transcoder) used by both the
  GLB loader and catalog textures; `ensureKtx2Support(renderer)` runs once at
  viewer init (GPUDeviceWatcher) so `.ktx2` finishes load even with no GLB in
  the scene. `use-gltf-ktx2` now reuses the shared instance.
- `materials.ts`: texture loaders pick the KTX2 loader for `.ktx2` urls, the
  image loader otherwise (all three load paths).
- `material-library.ts`: 15 entries (fabric ×6, leather ×2, concrete ×4,
  metal ×3). Neutral albedo for tinting, `flipY: false` (compressed textures
  can't flip), `repeat` per real-world tile size, metals `metalness: 1`.
  Normals encoded UASTC, data maps ETC1S/linear.

Assets generated from raw sources via the new community
`scripts/build-material-textures.ts`. 1024/256 tiers + raws kept out of git.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 14:37:23 -04:00

41 lines
1.7 KiB
TypeScript

import { KTX2Loader } from 'three/examples/jsm/Addons.js'
/**
* Single shared KTX2 loader for the whole viewer — used both by the GLB loader
* (`use-gltf-ktx2`) and by catalog finish textures (`materials.ts`). KTX2 must
* be transcoded at load via the Basis WASM, and `detectSupport(renderer)` has to
* run once before any `.ktx2` is loaded so the loader picks a GPU format the
* device supports. `ensureKtx2Support` is idempotent per renderer and is called
* from the viewer root the moment the renderer is ready (even when no GLB is in
* the scene, so catalog `.ktx2` finishes still load).
*/
export const ktx2Loader = new KTX2Loader()
ktx2Loader.setTranscoderPath('https://cdn.jsdelivr.net/gh/pmndrs/drei-assets@master/basis/')
const configuredRenderers = new WeakSet<object>()
const warnedRenderers = new WeakSet<object>()
/** Returns true once support has been detected for this renderer (KTX2 safe to load). */
export function ensureKtx2Support(renderer: unknown): boolean {
const key = renderer as object | null
if (!key) return false
if (configuredRenderers.has(key)) return true
try {
;(ktx2Loader as unknown as { detectSupport: (r: unknown) => void }).detectSupport(renderer)
configuredRenderers.add(key)
return true
} catch (error) {
// Some WebGPU flows can transiently call this before backend init; don't
// crash the scene — a later call (or the next render) retries.
if (!warnedRenderers.has(key)) {
console.warn('[viewer] Skipping KTX2 support detection for now.', error)
warnedRenderers.add(key)
}
return false
}
}
export function isKtx2Url(url: string): boolean {
return url.toLowerCase().endsWith('.ktx2')
}