feat: integrate Draco compression for GLB models and implement encryption for secure loading
- Added DRACOLoader to starter.js for handling compressed GLB models. - Created a createGltfLoader function to initialize GLTFLoader with DRACOLoader. - Implemented dracoModels plugin in vite.config.js to compress GLB models during build. - Introduced model_loader.js for loading encrypted GLB models with WebCrypto. - Developed encryptModels plugin to encrypt GLB files, ensuring no clear models are exposed. - Updated asset handling to remove uncompressed GLB files post-build.
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
// NON ATTIVO: per ora nessuno lo importa (vedi vite-plugins/encrypt-models.js).
|
||||
//
|
||||
// Carica un modello GLB cifrato in build (vedi vite-plugins/encrypt-models.js): scarica il .bin,
|
||||
// lo decifra in memoria con WebCrypto e lo passa al GLTFLoader. Nessun .glb in chiaro passa in rete.
|
||||
//
|
||||
// WebCrypto richiede un contesto sicuro: https, oppure http://localhost.
|
||||
|
||||
/* global __KREIOS_MK__, __KREIOS_MM__ */
|
||||
|
||||
let keyPromise = null;
|
||||
|
||||
function rawKey() {
|
||||
const masked = __KREIOS_MK__;
|
||||
const mask = __KREIOS_MM__;
|
||||
return Uint8Array.from(masked, (b, i) => b ^ mask[i]);
|
||||
}
|
||||
|
||||
function getKey() {
|
||||
keyPromise ??= (async () => {
|
||||
const raw = rawKey();
|
||||
const cryptoKey = await crypto.subtle.importKey('raw', raw, 'AES-GCM', false, ['decrypt']);
|
||||
return { raw, cryptoKey };
|
||||
})();
|
||||
return keyPromise;
|
||||
}
|
||||
|
||||
async function fileName(raw, relPath) {
|
||||
const data = new Uint8Array([...raw, ...new TextEncoder().encode(relPath)]);
|
||||
const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', data));
|
||||
return [...digest].map((b) => b.toString(16).padStart(2, '0')).join('').slice(0, 32) + '.bin';
|
||||
}
|
||||
|
||||
// Scarica con avanzamento, compatibile con il callback di progresso del GLTFLoader ({ loaded, total }).
|
||||
async function download(url, onProgress) {
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) throw new Error(`Modello non disponibile (${res.status})`);
|
||||
|
||||
const total = Number(res.headers.get('Content-Length')) || 0;
|
||||
if (!res.body || !total) {
|
||||
const buf = await res.arrayBuffer();
|
||||
onProgress?.({ loaded: buf.byteLength, total: buf.byteLength });
|
||||
return new Uint8Array(buf);
|
||||
}
|
||||
|
||||
const out = new Uint8Array(total);
|
||||
const reader = res.body.getReader();
|
||||
let loaded = 0;
|
||||
for (;;) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
out.set(value, loaded);
|
||||
loaded += value.length;
|
||||
onProgress?.({ loaded, total });
|
||||
}
|
||||
return out.subarray(0, loaded);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('three/examples/jsm/loaders/GLTFLoader').GLTFLoader} loader
|
||||
* @param {string} relPath percorso sotto assets/models, es. "pvc/finestra_1_anta.glb"
|
||||
*/
|
||||
export async function caricaModelloCifrato(loader, relPath, onProgress) {
|
||||
const { raw, cryptoKey } = await getKey();
|
||||
const data = await download(`./assets/m/${await fileName(raw, relPath)}`, onProgress);
|
||||
|
||||
const glb = await crypto.subtle.decrypt(
|
||||
{ name: 'AES-GCM', iv: data.subarray(0, 12), additionalData: new TextEncoder().encode(relPath) },
|
||||
cryptoKey,
|
||||
data.subarray(12),
|
||||
);
|
||||
|
||||
const dir = relPath.includes('/') ? relPath.slice(0, relPath.lastIndexOf('/') + 1) : '';
|
||||
return loader.parseAsync(glb, `./assets/models/${dir}`);
|
||||
}
|
||||
|
||||
/** Stessa firma di GLTFLoader.load, ma su modelli cifrati. */
|
||||
export function loadModello(loader, relPath, onLoad, onProgress, onError) {
|
||||
caricaModelloCifrato(loader, relPath, onProgress).then(onLoad, (err) => (onError ? onError(err) : console.error(err)));
|
||||
}
|
||||
+10
-3
@@ -2,6 +2,7 @@
|
||||
import * as THREE from 'three';
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
|
||||
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
||||
import { DRACOLoader, DRACO_GLTF_CONFIG } from 'three/examples/jsm/loaders/DRACOLoader';
|
||||
import $ from 'jquery';
|
||||
import { APP } from './app.js';
|
||||
import ParamsParser from './params_parser.js';
|
||||
@@ -20,7 +21,13 @@ import { API_BASE as PREVENTIVI_API_BASE } from './api_base.js';
|
||||
window.$ = $;
|
||||
window.THREE = THREE; // Used by APP Scripts.
|
||||
window.OrbitControls = OrbitControls; // Used by APP Scripts.
|
||||
window.GLTFLoader = GLTFLoader; // Used by APP Scripts.
|
||||
|
||||
// I modelli sono compressi con Draco in build (vite-plugins/draco-models.js). I decoder (versione
|
||||
// ridotta per glTF) li risolve three via import.meta.url e Vite li include nella build.
|
||||
const dracoLoader = new DRACOLoader().setDecoderPath(DRACO_GLTF_CONFIG);
|
||||
function createGltfLoader() {
|
||||
return new GLTFLoader().setDRACOLoader(dracoLoader);
|
||||
}
|
||||
|
||||
// --- Integrazione backend preventivi ---
|
||||
const CART_TOKEN_KEY = 'kreios_cart_token';
|
||||
@@ -239,7 +246,7 @@ const start = async () => {
|
||||
playerOff.rotateBackground(Math.PI);
|
||||
playerOff.setSize(playerOff.dom.clientWidth, playerOff.dom.clientHeight);
|
||||
|
||||
var gltfLoader = new GLTFLoader();
|
||||
var gltfLoader = createGltfLoader();
|
||||
|
||||
gltfLoader.load(`./assets/models/${params.sistema}/${modello.modello}`, function (gltf) {
|
||||
document.getElementById('loadingBarContainer').style.display = 'none';
|
||||
@@ -446,7 +453,7 @@ const start = async () => {
|
||||
document.getElementById('loadingBarProgressAlt').style.width = '0%';
|
||||
document.getElementById('loadingPercentageAlt').innerText = '0%';
|
||||
|
||||
var gltfLoader = new GLTFLoader();
|
||||
var gltfLoader = createGltfLoader();
|
||||
|
||||
gltfLoader.load(`./assets/models/${modello}`, function (gltf) {
|
||||
document.getElementById('loadingBarContainerAlt').style.display = 'none';
|
||||
|
||||
Reference in New Issue
Block a user