perf(falukant): deploy prebuilt models and release WebGL contexts
All checks were successful
Deploy to production / deploy (push) Successful in 2m19s
All checks were successful
Deploy to production / deploy (push) Successful in 2m19s
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"optimize-models": "node scripts/optimize-glb.mjs",
|
"optimize-models": "node scripts/optimize-glb.mjs",
|
||||||
|
"build-runtime-models": "node scripts/build-falukant-runtime-models.mjs",
|
||||||
"i18n:check-parity": "node scripts/check-i18n-locale-parity.mjs de fr",
|
"i18n:check-parity": "node scripts/check-i18n-locale-parity.mjs de fr",
|
||||||
"i18n:translate-fr": "node scripts/generate-fr-locale-from-de.mjs",
|
"i18n:translate-fr": "node scripts/generate-fr-locale-from-de.mjs",
|
||||||
"i18n:smooth-falukant-fr": "node scripts/smooth-fr-falukant.mjs"
|
"i18n:smooth-falukant-fr": "node scripts/smooth-fr-falukant.mjs"
|
||||||
|
|||||||
45
frontend/scripts/build-falukant-runtime-models.mjs
Normal file
45
frontend/scripts/build-falukant-runtime-models.mjs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Creates deployment-ready Falukant GLBs in the asset repository.
|
||||||
|
* Sources remain in models-src; only optimized *_opt.glb files are written
|
||||||
|
* to public/models and are later fetched by update-frontend.sh.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* npm run build-runtime-models -- --assets-dir ../YourPart3Assets
|
||||||
|
*/
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { spawnSync } from 'node:child_process';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
const frontendDir = path.resolve(scriptDir, '..');
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
const optionIndex = args.indexOf('--assets-dir');
|
||||||
|
const assetsDir = path.resolve(optionIndex >= 0 && args[optionIndex + 1]
|
||||||
|
? args[optionIndex + 1]
|
||||||
|
: path.join(frontendDir, '..', '..', 'YourPart3Assets'));
|
||||||
|
const sourceRoot = path.join(assetsDir, 'models-src', '3d', 'falukant');
|
||||||
|
const outputRoot = path.join(assetsDir, 'public', 'models', '3d', 'falukant');
|
||||||
|
|
||||||
|
for (const category of ['characters', 'houses']) {
|
||||||
|
const sourceDir = path.join(sourceRoot, category);
|
||||||
|
const outputDir = path.join(outputRoot, category);
|
||||||
|
if (!fs.existsSync(sourceDir)) {
|
||||||
|
throw new Error(`Missing model source directory: ${sourceDir}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = spawnSync(process.execPath, [path.join(scriptDir, 'optimize-glb.mjs')], {
|
||||||
|
cwd: frontendDir,
|
||||||
|
stdio: 'inherit',
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
MODEL_SOURCE_DIR: sourceDir,
|
||||||
|
MODEL_OUTPUT_DIR: outputDir,
|
||||||
|
CLEAN_MODEL_OUTPUT: 'true',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (result.status !== 0) process.exit(result.status ?? 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Runtime models ready in ${outputRoot}`);
|
||||||
@@ -35,8 +35,9 @@ function removeStaleOptimizedModels(outputDir) {
|
|||||||
if (!fs.existsSync(outputDir)) {
|
if (!fs.existsSync(outputDir)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const cleanAllModels = process.env.CLEAN_MODEL_OUTPUT === 'true';
|
||||||
for (const file of fs.readdirSync(outputDir)) {
|
for (const file of fs.readdirSync(outputDir)) {
|
||||||
if (file.endsWith('_opt.glb')) {
|
if (file.endsWith('_opt.glb') || (cleanAllModels && file.endsWith('.glb'))) {
|
||||||
fs.unlinkSync(path.join(outputDir, file));
|
fs.unlinkSync(path.join(outputDir, file));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -332,7 +332,7 @@ export default {
|
|||||||
// Renderer erstellen
|
// Renderer erstellen
|
||||||
this.renderer = markRaw(new runtime.WebGLRenderer({ antialias: true, alpha: true }));
|
this.renderer = markRaw(new runtime.WebGLRenderer({ antialias: true, alpha: true }));
|
||||||
this.renderer.setSize(container.clientWidth, container.clientHeight);
|
this.renderer.setSize(container.clientWidth, container.clientHeight);
|
||||||
this.renderer.setPixelRatio(window.devicePixelRatio);
|
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
|
||||||
container.appendChild(this.renderer.domElement);
|
container.appendChild(this.renderer.domElement);
|
||||||
|
|
||||||
// Verbesserte Beleuchtung für kräftigere Farben
|
// Verbesserte Beleuchtung für kräftigere Farben
|
||||||
@@ -687,7 +687,12 @@ export default {
|
|||||||
if (container && this.renderer.domElement) {
|
if (container && this.renderer.domElement) {
|
||||||
container.removeChild(this.renderer.domElement);
|
container.removeChild(this.renderer.domElement);
|
||||||
}
|
}
|
||||||
|
// dispose() alone leaves the browser context alive until garbage
|
||||||
|
// collection. Route changes or long character lists can otherwise
|
||||||
|
// exceed Chromium's context limit and crash the tab.
|
||||||
|
this.renderer.forceContextLoss?.();
|
||||||
this.renderer.dispose();
|
this.renderer.dispose();
|
||||||
|
this.renderer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.scene) {
|
if (this.scene) {
|
||||||
|
|||||||
@@ -124,9 +124,15 @@ export default {
|
|||||||
this.removeModel();
|
this.removeModel();
|
||||||
if (this.renderer) {
|
if (this.renderer) {
|
||||||
this.$refs.container?.removeChild(this.renderer.domElement);
|
this.$refs.container?.removeChild(this.renderer.domElement);
|
||||||
|
// Release immediately instead of waiting for garbage collection.
|
||||||
|
// Several house previews otherwise accumulate WebGL contexts.
|
||||||
|
this.renderer.forceContextLoss?.();
|
||||||
this.renderer.dispose();
|
this.renderer.dispose();
|
||||||
|
this.renderer = null;
|
||||||
}
|
}
|
||||||
this.scene?.clear();
|
this.scene?.clear();
|
||||||
|
this.scene = null;
|
||||||
|
this.camera = null;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -60,8 +60,8 @@ if [ ! -f "$MODELS_MANIFEST" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Source GLBs are versioned in their own Git-LFS repository. Keep them outside
|
# Deployment-ready GLBs are versioned in their own Git-LFS repository. Source
|
||||||
# the application worktree, because update.sh removes untracked files there.
|
# GLBs stay out of the deployment entirely and are optimized before publishing.
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
source "$MODELS_MANIFEST"
|
source "$MODELS_MANIFEST"
|
||||||
: "${FALUKANT_MODELS_REPOSITORY:?Falukant models repository is missing}"
|
: "${FALUKANT_MODELS_REPOSITORY:?Falukant models repository is missing}"
|
||||||
@@ -79,30 +79,18 @@ echo "Lade Falukant-3D-Modelle aus ${FALUKANT_MODELS_REPOSITORY}@${FALUKANT_MODE
|
|||||||
rm -rf "$ASSETS_DIR"
|
rm -rf "$ASSETS_DIR"
|
||||||
GIT_LFS_SKIP_SMUDGE=1 git clone --depth 1 --branch "$FALUKANT_MODELS_REF" \
|
GIT_LFS_SKIP_SMUDGE=1 git clone --depth 1 --branch "$FALUKANT_MODELS_REF" \
|
||||||
"$FALUKANT_MODELS_REPOSITORY" "$ASSETS_DIR"
|
"$FALUKANT_MODELS_REPOSITORY" "$ASSETS_DIR"
|
||||||
git -C "$ASSETS_DIR" lfs pull
|
git -C "$ASSETS_DIR" lfs pull --include="public/models/3d/falukant/**"
|
||||||
|
|
||||||
ASSET_MODELS_DIR="$ASSETS_DIR/models-src/3d/falukant"
|
ASSET_RUNTIME_MODELS_DIR="$ASSETS_DIR/public/models/3d/falukant"
|
||||||
if [ ! -d "$ASSET_MODELS_DIR/characters" ]; then
|
if [ ! -f "$ASSET_RUNTIME_MODELS_DIR/characters/female_opt.glb" ] || [ ! -f "$ASSET_RUNTIME_MODELS_DIR/houses/under_bridge_opt.glb" ]; then
|
||||||
# Keep deployments available while the dedicated LFS repository is being
|
echo "ERROR: Optimized Falukant runtime models are missing from ${FALUKANT_MODELS_REPOSITORY}@${FALUKANT_MODELS_REF}. Run: npm --prefix frontend run build-runtime-models -- --assets-dir <asset-repository>." >&2
|
||||||
# populated. The active slot has already been built with these sources,
|
|
||||||
# so it is a safe, read-only fallback for the new inactive slot.
|
|
||||||
FALLBACK_MODELS_DIR="$CURRENT_FRONTEND/models-src/3d/falukant"
|
|
||||||
if [ -d "$FALLBACK_MODELS_DIR/characters" ]; then
|
|
||||||
echo "WARN: Falukant models are missing from ${FALUKANT_MODELS_REPOSITORY}@${FALUKANT_MODELS_REF}; nutze Modelle aus dem aktiven Slot." >&2
|
|
||||||
ASSET_MODELS_DIR="$FALLBACK_MODELS_DIR"
|
|
||||||
else
|
|
||||||
echo "ERROR: Falukant models are missing from ${FALUKANT_MODELS_REPOSITORY}@${FALUKANT_MODELS_REF} and from the active slot" >&2
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Übernehme Falukant-3D-Quellmodelle..."
|
echo "Übernehme voroptimierte Falukant-3D-Modelle..."
|
||||||
rm -rf "$FRONTEND_DIR/models-src/3d/falukant"
|
rm -rf "$FRONTEND_DIR/public/models/3d/falukant"
|
||||||
mkdir -p "$FRONTEND_DIR/models-src/3d"
|
mkdir -p "$FRONTEND_DIR/public/models/3d"
|
||||||
cp -a "$ASSET_MODELS_DIR" "$FRONTEND_DIR/models-src/3d/"
|
cp -a "$ASSET_RUNTIME_MODELS_DIR" "$FRONTEND_DIR/public/models/3d/"
|
||||||
|
|
||||||
echo "Optimiere Falukant-3D-Modelle..."
|
|
||||||
npm run optimize-models
|
|
||||||
|
|
||||||
echo "Baue Frontend..."
|
echo "Baue Frontend..."
|
||||||
npm run build
|
npm run build
|
||||||
|
|||||||
Reference in New Issue
Block a user