diff --git a/frontend/scripts/optimize-glb.mjs b/frontend/scripts/optimize-glb.mjs
index c862b11..ae7c0c5 100755
--- a/frontend/scripts/optimize-glb.mjs
+++ b/frontend/scripts/optimize-glb.mjs
@@ -1,12 +1,13 @@
#!/usr/bin/env node
/**
- * Optimiert alle Falukant-Charakter-GLBs mit Draco + Textur-Optimierung.
+ * Optimiert Falukant-Charakter- und Haus-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
* Optional können MODEL_SOURCE_DIR und MODEL_OUTPUT_DIR gesetzt werden, um
- * Modelle aus dem separaten YourPart3Assets-Repository zu verarbeiten.
+ * genau einen Modellordner aus dem separaten YourPart3Assets-Repository zu
+ * verarbeiten.
*/
import fs from 'fs';
@@ -15,24 +16,22 @@ import { fileURLToPath } from 'url';
import path from 'path';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
-const defaultSourceDir = path.join(__dirname, '..', 'models-src', '3d', 'falukant', 'characters');
-const defaultOutputDir = path.join(__dirname, '..', 'public', 'models', '3d', 'falukant', 'characters');
-const sourceDir = path.resolve(process.env.MODEL_SOURCE_DIR || defaultSourceDir);
-const outputDir = path.resolve(process.env.MODEL_OUTPUT_DIR || defaultOutputDir);
+const modelsRoot = path.join(__dirname, '..', 'models-src', '3d', 'falukant');
+const publicModelsRoot = path.join(__dirname, '..', 'public', 'models', '3d', 'falukant');
const cli = path.join(__dirname, '..', 'node_modules', '.bin', 'gltf-transform');
-function listSourceModels() {
+function listSourceModels(sourceDir) {
return fs.readdirSync(sourceDir)
.filter((file) => file.endsWith('.glb'))
.filter((file) => !file.endsWith('_opt.glb'))
.sort((a, b) => a.localeCompare(b));
}
-function ensureOutputDir() {
+function ensureOutputDir(outputDir) {
fs.mkdirSync(outputDir, { recursive: true });
}
-function removeStaleOptimizedModels() {
+function removeStaleOptimizedModels(outputDir) {
if (!fs.existsSync(outputDir)) {
return;
}
@@ -43,43 +42,74 @@ function removeStaleOptimizedModels() {
}
}
-console.log('Optimize Falukant character GLBs (Draco + texture 1024)\n');
-
-if (!fs.existsSync(sourceDir)) {
- console.error(`Source directory not found: ${sourceDir}`);
- process.exit(1);
-}
-
-ensureOutputDir();
-removeStaleOptimizedModels();
-
-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(sourceDir, f);
- const out = f.replace(/\.glb$/, '_opt.glb');
- const output = path.join(outputDir, out);
-
- try {
- 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}`);
+function optimizeModelDirectory({ name, sourceDir, outputDir, required = false }) {
+ if (!fs.existsSync(sourceDir)) {
+ if (required) {
+ console.error(`Source directory not found: ${sourceDir}`);
+ process.exitCode = 1;
+ }
+ return;
+ }
+
+ ensureOutputDir(outputDir);
+ removeStaleOptimizedModels(outputDir);
+
+ const models = listSourceModels(sourceDir);
+ if (models.length === 0) {
+ console.log(`No ${name} GLBs found.`);
+ return;
+ }
+
+ console.log(`Optimize Falukant ${name} GLBs (Draco + texture 1024)\n`);
+
+ for (const f of models) {
+ const input = path.join(sourceDir, f);
+ const out = f.replace(/\.glb$/, '_opt.glb');
+ const output = path.join(outputDir, out);
+
+ try {
+ 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;
}
- } catch (e) {
- console.error(`Failed: ${f}`);
- process.exitCode = 1;
}
}
-console.log(`\nDone. Optimized models written to ${outputDir}`);
+const customSourceDir = process.env.MODEL_SOURCE_DIR;
+const customOutputDir = process.env.MODEL_OUTPUT_DIR;
+if (Boolean(customSourceDir) !== Boolean(customOutputDir)) {
+ console.error('MODEL_SOURCE_DIR and MODEL_OUTPUT_DIR must be set together.');
+ process.exit(1);
+}
+
+if (customSourceDir) {
+ optimizeModelDirectory({
+ name: 'custom',
+ sourceDir: path.resolve(customSourceDir),
+ outputDir: path.resolve(customOutputDir),
+ required: true,
+ });
+} else {
+ optimizeModelDirectory({
+ name: 'character',
+ sourceDir: path.join(modelsRoot, 'characters'),
+ outputDir: path.join(publicModelsRoot, 'characters'),
+ required: true,
+ });
+ optimizeModelDirectory({
+ name: 'house',
+ sourceDir: path.join(modelsRoot, 'houses'),
+ outputDir: path.join(publicModelsRoot, 'houses'),
+ });
+}
diff --git a/frontend/src/components/falukant/House3D.vue b/frontend/src/components/falukant/House3D.vue
new file mode 100644
index 0000000..fcd8f24
--- /dev/null
+++ b/frontend/src/components/falukant/House3D.vue
@@ -0,0 +1,156 @@
+
+