fix(falukant): use nearest lower age model fallback
Some checks failed
Deploy to production / deploy (push) Has been cancelled
Some checks failed
Deploy to production / deploy (push) Has been cancelled
This commit is contained in:
@@ -27,15 +27,12 @@ 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
|
||||
// Dateinamen ein, falls dein Backend solche Varianten enthält.
|
||||
const AGE_ALTERNATIVE_SUFFIXES = [
|
||||
'_alt.glb',
|
||||
'_special.glb',
|
||||
'_v2.glb'
|
||||
];
|
||||
// Only concrete age models are used. If an exact age is not available, choose
|
||||
// the nearest lower one instead of the generic gender/age-group fallback.
|
||||
const AVAILABLE_MODEL_AGES = Object.freeze({
|
||||
female: Object.freeze([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 25, 30, 40, 50, 60]),
|
||||
male: Object.freeze([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 25, 30, 40, 50, 60]),
|
||||
});
|
||||
|
||||
// Per-age camera offsets applied after auto-fit. Values are additive (meters).
|
||||
// Set `y` to raise/lower the camera, `z` to move it closer/further.
|
||||
@@ -196,18 +193,6 @@ export default {
|
||||
if (age <= 17) return 'teen';
|
||||
return 'adult';
|
||||
},
|
||||
modelPath() {
|
||||
const base = getApiBaseURL();
|
||||
const prefix = base ? `${base}${MODELS_API_PATH}` : MODELS_API_PATH;
|
||||
return `${prefix}/${this.actualGender}_${this.ageGroup}.glb`;
|
||||
},
|
||||
exactAgeModelPath() {
|
||||
// Pfad für genaues Alter (z.B. female_1y.glb für Alter 1)
|
||||
const age = this.actualAge;
|
||||
const base = getApiBaseURL();
|
||||
const prefix = base ? `${base}${MODELS_API_PATH}` : MODELS_API_PATH;
|
||||
return `${prefix}/${this.actualGender}_${age}y.glb`;
|
||||
},
|
||||
fallbackImageSrc() {
|
||||
return this.actualGender === 'female'
|
||||
? '/images/mascot/mascot_female.png'
|
||||
@@ -219,7 +204,7 @@ export default {
|
||||
if (this.is3DDisabled) return;
|
||||
await this.loadModel();
|
||||
},
|
||||
async ageGroup() {
|
||||
async actualAge() {
|
||||
if (this.is3DDisabled) return;
|
||||
await this.loadModel();
|
||||
}
|
||||
@@ -454,35 +439,14 @@ export default {
|
||||
const base = getApiBaseURL();
|
||||
const prefix = base ? `${base}${MODELS_API_PATH}` : MODELS_API_PATH;
|
||||
|
||||
// Fallback-Hierarchie:
|
||||
// Standard:
|
||||
// 1. Exaktes Altersmodell
|
||||
// 2. Altersbereich
|
||||
// 3. Basis-Modell
|
||||
// Lightweight:
|
||||
// 1. Altersbereich
|
||||
// 2. Basis-Modell
|
||||
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),
|
||||
// then the exact-age file, then the age-group file, and finally the base gender
|
||||
// fallback. This makes the age-specific model the primary source and the
|
||||
// age-group only a later fallback.
|
||||
const candidates = [];
|
||||
const modelAge = this.actualAge;
|
||||
|
||||
// Try exact-age variants with configured suffixes first (works for any gender/age)
|
||||
for (const sfx of AGE_ALTERNATIVE_SUFFIXES) {
|
||||
candidates.push(this.toOptimizedModelPath(`${prefix}/${this.actualGender}_${modelAge}y${sfx}`));
|
||||
}
|
||||
|
||||
// Standard preference order: exact age, age group, fallback
|
||||
candidates.push(exactAgePath);
|
||||
candidates.push(ageGroupPath);
|
||||
candidates.push(fallbackPath);
|
||||
const availableAges = AVAILABLE_MODEL_AGES[this.actualGender] || [];
|
||||
const candidateAges = availableAges
|
||||
.filter((age) => age <= modelAge)
|
||||
.sort((left, right) => right - left);
|
||||
const candidates = candidateAges.map((age) =>
|
||||
`${prefix}/${this.actualGender}_${age}y${OPTIMIZED_MODEL_SUFFIX}`
|
||||
);
|
||||
|
||||
let gltf;
|
||||
try {
|
||||
@@ -505,10 +469,8 @@ export default {
|
||||
// store for debug overlay
|
||||
this.debugInfo.modelPath = chosenPath;
|
||||
// determine modelType
|
||||
if (chosenPath === exactAgePath) this.debugInfo.modelType = 'exactAge';
|
||||
else if (chosenPath === ageGroupPath) this.debugInfo.modelType = 'ageGroup';
|
||||
else if (chosenPath === fallbackPath) this.debugInfo.modelType = 'fallback';
|
||||
else this.debugInfo.modelType = 'alternate';
|
||||
const chosenAge = candidateAges[candidates.indexOf(chosenPath)];
|
||||
this.debugInfo.modelType = chosenAge === modelAge ? 'exactAge' : 'nearestLowerAge';
|
||||
} finally {
|
||||
dracoLoader.dispose();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user