From b3f64293079aa378446464eaabde3d2e32c061c0 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Fri, 24 Jul 2026 13:31:08 +0200 Subject: [PATCH] feat(optimize): optimize Falukant 3D models during build and update deployment scripts --- backend/services/modelsProxyService.js | 8 +++++ deploy-frontend.sh | 3 ++ frontend/scripts/optimize-glb.mjs | 42 ++++++++++++++++--------- frontend/src/components/Character3D.vue | 13 +++++--- package.json | 2 +- update-frontend.sh | 3 ++ 6 files changed, 51 insertions(+), 20 deletions(-) diff --git a/backend/services/modelsProxyService.js b/backend/services/modelsProxyService.js index 78daeea..68ceb76 100755 --- a/backend/services/modelsProxyService.js +++ b/backend/services/modelsProxyService.js @@ -95,6 +95,14 @@ export async function getOptimizedModelPath(filename) { const sourceDir = getSourceDir(); const sourcePath = path.join(sourceDir, filename); + + if (filename.endsWith('_opt.glb')) { + if (!fs.existsSync(sourcePath)) { + throw new Error(`Source model not found: ${filename} (looked in ${sourceDir})`); + } + return sourcePath; + } + const cacheFilename = filename.replace(/\.glb$/, '_opt.glb'); const cachePath = path.join(CACHE_DIR, cacheFilename); diff --git a/deploy-frontend.sh b/deploy-frontend.sh index 940258c..8287515 100755 --- a/deploy-frontend.sh +++ b/deploy-frontend.sh @@ -44,6 +44,9 @@ if [ $? -ne 0 ]; then fi # 5b. Frontend neu bauen +echo "Optimiere Falukant-3D-Modelle..." +npm run optimize-models + npm run build if [ $? -ne 0 ]; then diff --git a/frontend/scripts/optimize-glb.mjs b/frontend/scripts/optimize-glb.mjs index a9f3e6a..c646087 100755 --- a/frontend/scripts/optimize-glb.mjs +++ b/frontend/scripts/optimize-glb.mjs @@ -1,41 +1,53 @@ #!/usr/bin/env node /** - * Optimiert Falukant-Charakter-GLBs mit Draco + Textur-Optimierung. + * Optimiert alle Falukant-Charakter-GLBs mit Draco + Textur-Optimierung. * Ausgabe: *_opt.glb im selben Verzeichnis. * Voraussetzung: npm install (@gltf-transform/cli als Dev-Dep) * * Aufruf: npm run optimize-models */ -import { execSync } from 'child_process'; +import fs from 'fs'; +import { spawnSync } from 'child_process'; import { fileURLToPath } from 'url'; import path from 'path'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const charsDir = path.join(__dirname, '..', 'public', 'models', '3d', 'falukant', 'characters'); - -const models = [ - 'male.glb', 'female.glb', - 'male_adult.glb', 'male_child.glb', 'male_preteen.glb', 'male_teen.glb', 'male_toddler.glb', - 'female_adult.glb', 'female_child.glb', 'female_preteen.glb', 'female_teen.glb', 'female_toddler.glb', -]; - const cli = path.join(__dirname, '..', 'node_modules', '.bin', 'gltf-transform'); -const cmd = (input, output) => - `"${cli}" optimize "${input}" "${output}" --compress draco --texture-size 1024`; + +function listSourceModels() { + return fs.readdirSync(charsDir) + .filter((file) => file.endsWith('.glb')) + .filter((file) => !file.endsWith('_opt.glb')) + .sort((a, b) => a.localeCompare(b)); +} console.log('Optimize Falukant character GLBs (Draco + texture 1024)\n'); +const models = listSourceModels(); +if (models.length === 0) { + console.log('No source GLBs found.'); + process.exit(0); +} + for (const f of models) { const input = path.join(charsDir, f); const out = f.replace(/\.glb$/, '_opt.glb'); const output = path.join(charsDir, out); try { - execSync(`node "${cli}" optimize "${input}" "${output}" --compress draco --texture-size 1024`, { - stdio: 'inherit', - cwd: path.join(__dirname, '..'), - }); + const result = spawnSync( + 'node', + [cli, 'optimize', input, output, '--compress', 'draco', '--texture-size', '1024'], + { + stdio: 'inherit', + cwd: path.join(__dirname, '..'), + } + ); + if (result.status !== 0) { + throw new Error(`gltf-transform exited with ${result.status}`); + } } catch (e) { console.error(`Failed: ${f}`); process.exitCode = 1; diff --git a/frontend/src/components/Character3D.vue b/frontend/src/components/Character3D.vue index 5bccbc1..ee222ce 100755 --- a/frontend/src/components/Character3D.vue +++ b/frontend/src/components/Character3D.vue @@ -26,6 +26,7 @@ import { getApiBaseURL } from '@/utils/axios.js'; /** Backend-Route: GET /api/models/3d/falukant/characters/:filename (Proxy mit Draco-Optimierung) */ const MODELS_API_PATH = '/api/models/3d/falukant/characters'; +const OPTIMIZED_MODEL_SUFFIX = '_opt.glb'; // Optional: zusätzliche Namenskonventionen, die vor dem Standardpfad ausprobiert werden. // Hier werden mögliche Alternativ-Dateinamen aufgeführt, die für bestimmte Altersstufen // (z.B. weibliche Modelle für 1-4 Jahre) existieren könnten. Trage hier die tatsächlichen @@ -273,6 +274,10 @@ export default { this.cleanup(); }, methods: { + toOptimizedModelPath(url) { + return String(url || '').replace(/\.glb$/i, OPTIMIZED_MODEL_SUFFIX); + }, + async ensureThreeRuntime() { if (!this.threeRuntime) { this.threeRuntime = markRaw(await loadThreeRuntime()); @@ -457,9 +462,9 @@ export default { // Lightweight: // 1. Altersbereich // 2. Basis-Modell - const exactAgePath = this.exactAgeModelPath; - const ageGroupPath = this.modelPath; - const fallbackPath = `${prefix}/${this.actualGender}.glb`; + const exactAgePath = this.toOptimizedModelPath(this.exactAgeModelPath); + const ageGroupPath = this.toOptimizedModelPath(this.modelPath); + const fallbackPath = `${prefix}/${this.actualGender}${OPTIMIZED_MODEL_SUFFIX}`; // Build a list of candidate paths. For every age and gender we first try // possible alternate filenames for the exact-age model (e.g. female_1y_alt.glb), @@ -471,7 +476,7 @@ export default { // Try exact-age variants with configured suffixes first (works for any gender/age) for (const sfx of AGE_ALTERNATIVE_SUFFIXES) { - candidates.push(`${prefix}/${this.actualGender}_${modelAge}y${sfx}`); + candidates.push(this.toOptimizedModelPath(`${prefix}/${this.actualGender}_${modelAge}y${sfx}`)); } // Standard preference order: exact age, age group, fallback diff --git a/package.json b/package.json index f024354..78cc46d 100755 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "3.0.0-beta-1.0.0", "scripts": { "start": "npm-run-all --parallel build start:backend", - "build": "cd frontend && npm run build", + "build": "cd frontend && npm run optimize-models && npm run build", "start:backend": "cd backend && node server.js", "dev": "concurrently \"npm run dev:backend\" \"npm run dev:frontend\"", "dev:backend": "cd backend && nodemon server.js", diff --git a/update-frontend.sh b/update-frontend.sh index ecaf05e..ceffcb5 100755 --- a/update-frontend.sh +++ b/update-frontend.sh @@ -54,6 +54,9 @@ echo "VITE_CHAT_WS_URL=$VITE_CHAT_WS_URL" echo "Installiere Dependencies..." npm install +echo "Optimiere Falukant-3D-Modelle..." +npm run optimize-models + echo "Baue Frontend..." npm run build