feat: integrate Draco compression for 3D models in backoffice

- Added support for uploading and compressing GLB models using Draco in the backoffice.
- Implemented commands for importing and re-compressing 3D models.
- Updated model resource to handle 3D model uploads and validations.
- Introduced a new script for compressing GLB files with Draco.
- Modified Vite configuration to remove GLB files from the build output.
- Updated package dependencies to include GLTF-transform libraries for handling 3D models.
- Added tests to ensure proper functionality of 3D model handling and compression.
This commit is contained in:
2026-11-01 10:35:50 +01:00
parent 24f9e59654
commit 1208187a17
32 changed files with 895 additions and 52 deletions
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/env node
// Comprime con Draco un modello GLB: node scripts/comprimi-glb.mjs <sorgente.glb> <destinazione.glb>
//
// Usato dal backoffice al caricamento di un modello 3D (App\Support\Configuratore\CompressoreGlb):
// l'originale resta salvato non compresso, nel configuratore va solo questa copia. Nel browser la
// decomprime il DRACOLoader di three.js (vedi src/js/starter.js).
//
// In caso di successo stampa su stdout una riga JSON con le dimensioni prima/dopo.
import fs from 'node:fs/promises';
import { NodeIO } from '@gltf-transform/core';
import { ALL_EXTENSIONS } from '@gltf-transform/extensions';
import { draco, weld } from '@gltf-transform/functions';
import draco3d from 'draco3dgltf';
// Bit di quantizzazione: valori più alti = più fedeltà e file più grandi.
// Le posizioni a 14 bit sono sotto il decimo di millimetro sulle dimensioni di un infisso.
export const DRACO_OPTIONS = {
method: 'edgebreaker',
encodeSpeed: 5,
decodeSpeed: 5,
quantizePosition: 14,
quantizeNormal: 10,
quantizeTexcoord: 12,
quantizeColor: 8,
quantizeGeneric: 12,
};
const [src, dest] = process.argv.slice(2);
if (!src || !dest) {
console.error('Uso: comprimi-glb.mjs <sorgente.glb> <destinazione.glb>');
process.exit(2);
}
try {
const io = new NodeIO().registerExtensions(ALL_EXTENSIONS).registerDependencies({
'draco3d.encoder': await draco3d.createEncoderModule(),
'draco3d.decoder': await draco3d.createDecoderModule(),
});
const input = await fs.readFile(src);
const doc = await io.readBinary(new Uint8Array(input));
if (!doc.getRoot().listMeshes().length) throw new Error('Il file non contiene geometrie.');
// Draco lavora su primitive indicizzate: weld unisce solo i vertici identici in tutti gli
// attributi (posizione, normale, uv...), quindi non cambia l'aspetto del modello.
await doc.transform(weld(), draco(DRACO_OPTIONS));
const output = await io.writeBinary(doc);
await fs.writeFile(dest, output);
console.log(JSON.stringify({ originale: input.length, compresso: output.length }));
} catch (err) {
console.error(err?.message ?? String(err));
process.exit(1);
}