import { parseDFF } from './rw/dff.js'; import { parseTXD } from './rw/txd.js'; import { Viewer } from './viewer.js'; const canvas = document.getElementById('view'); const statusEl = document.getElementById('status'); const viewer = new Viewer(canvas); // The page shows nothing but the model, so a failure is the only thing worth // putting on screen. Everything else goes to the console. function setError(text) { statusEl.textContent = text || ''; statusEl.style.display = text ? 'block' : 'none'; } async function fetchBuffer(url) { const res = await fetch(url); if (!res.ok) throw new Error(`${res.status} fetching ${url}`); return res.arrayBuffer(); } function render(name, dffBuffer, txdBuffer) { const dff = parseDFF(dffBuffer); const txd = txdBuffer ? parseTXD(txdBuffer) : null; const report = viewer.load(dff, txd); console.log( `${name}: rw ${dff.versionText}, ${report.meshes} mesh(es), ` + `${report.vertices} verts, ${report.triangles} tris, ` + `${txd ? txd.textures.size : 0} textures` ); const warnings = [...dff.errors, ...(txd ? txd.errors : [])]; if (report.missingTextures.size) { warnings.push('not in txd: ' + [...report.missingTextures].join(', ')); } warnings.forEach((warning) => console.warn(warning)); setError(''); } async function loadSkin(id) { try { const res = await fetch(`/api/skin/${encodeURIComponent(id)}`); const meta = await res.json(); if (!res.ok) { setError(meta.error || `${res.status} loading ${id}`); return; } const [dffBuffer, txdBuffer] = await Promise.all([ fetchBuffer(meta.dff), meta.txd ? fetchBuffer(meta.txd) : Promise.resolve(null), ]); render(id, dffBuffer, txdBuffer); } catch (err) { console.error(err); setError(err.message); } } function setupDragAndDrop() { document.body.addEventListener('dragover', (e) => { e.preventDefault(); document.body.classList.add('dragging'); }); document.body.addEventListener('dragleave', () => document.body.classList.remove('dragging')); document.body.addEventListener('drop', async (e) => { e.preventDefault(); document.body.classList.remove('dragging'); const files = [...e.dataTransfer.files]; const dffFile = files.find((f) => f.name.toLowerCase().endsWith('.dff')); const txdFile = files.find((f) => f.name.toLowerCase().endsWith('.txd')); if (!dffFile) { setError('drop a .dff (and its .txd) together'); return; } try { render( dffFile.name.replace(/\.dff$/i, ''), await dffFile.arrayBuffer(), txdFile ? await txdFile.arrayBuffer() : null ); } catch (err) { console.error(err); setError(err.message); } }); } setupDragAndDrop(); const match = location.pathname.match(/^\/samp\/skin\/(.+)$/); if (match) loadSkin(decodeURIComponent(match[1]));