From 31c05a35fc985998b66e60a9ca36904170306979 Mon Sep 17 00:00:00 2001 From: Tom Boullay Date: Mon, 27 Apr 2026 23:35:43 +0200 Subject: [PATCH] fix: canonicalize asset families --- README.md | 6 +++--- lib/asset-classification.ts | 8 ++++---- lib/asset-naming.ts | 18 ++++++++++++++---- lib/validate-folder.ts | 20 ++++++++++++++++++-- 4 files changed, 39 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index fa60782..2d48196 100644 --- a/README.md +++ b/README.md @@ -76,11 +76,11 @@ Access the app at `http://localhost:3000` Texture filenames must start with a known asset family. Use `asset.png` to apply a texture to the whole model, or `asset_object.png` to target a specific object. -Allowed families are defined in `lib/asset-naming.ts`: `baseColor`, `color`, `roughness`, `normal`, `normalOpengl`, `metallic`, `metalness`, `occlusionRoughnessMetallic`, `height`, `opacity`. +Allowed families are defined in `lib/asset-naming.ts`: `color`, `diffuse`, `roughness`, `normal`, `metalness`, `height`, `opacity`. -Valid examples: `color.png`, `baseColor_lampe.png`, `normalOpengl_cable1.png`, `opacity_lampe.png`. +Valid examples: `color.png`, `diffuse_lampe.png`, `normal_cable1.png`, `opacity_lampe.png`. -Invalid examples: `lampe_opacity.png`, `cable1_base_color.png`, `normal_opengl_cable1.png`. Invalid or unknown asset names block the upload. +Invalid examples: `lampe_opacity.png`, `cable1_base_color.png`, `normal_opengl_cable1.png`, `metallic_pied.png`. Invalid or unknown asset names block the upload. ### Production (Coolify / Docker) diff --git a/lib/asset-classification.ts b/lib/asset-classification.ts index ca084b8..162491d 100644 --- a/lib/asset-classification.ts +++ b/lib/asset-classification.ts @@ -6,19 +6,19 @@ export function classifyAssetCategory(filename: string): AssetCategory { const name = filename.replace(/\.[^.]+$/, '') const family = getAssetFamily(name.split('_')[0]) - if (family === 'baseColor' || family === 'color') { + if (family === 'color' || family === 'diffuse') { return 'color' } - if (family === 'roughness' || family === 'occlusionRoughnessMetallic') { + if (family === 'roughness') { return 'roughness' } - if (family === 'normal' || family === 'normalOpengl') { + if (family === 'normal') { return 'normal' } - if (family === 'metallic' || family === 'metalness') { + if (family === 'metalness') { return 'metalness' } diff --git a/lib/asset-naming.ts b/lib/asset-naming.ts index 4026152..c0ff7aa 100644 --- a/lib/asset-naming.ts +++ b/lib/asset-naming.ts @@ -1,12 +1,9 @@ export const ASSET_FAMILIES = [ - 'baseColor', 'color', + 'diffuse', 'roughness', 'normal', - 'normalOpengl', - 'metallic', 'metalness', - 'occlusionRoughnessMetallic', 'height', 'opacity', ] as const @@ -14,11 +11,24 @@ export const ASSET_FAMILIES = [ export type AssetFamily = typeof ASSET_FAMILIES[number] const ASSET_FAMILY_BY_KEY = new Map(ASSET_FAMILIES.map((family) => [family.toLowerCase(), family])) +const FORBIDDEN_ASSET_FAMILY_ALIASES: ReadonlyMap = new Map([ + ['basecolor', 'color'], + ['base_color', 'color'], + ['normalopengl', 'normal'], + ['normal_opengl', 'normal'], + ['metallic', 'metalness'], + ['occlusionroughnessmetallic', 'roughness'], + ['occlusion_roughness_metallic', 'roughness'], +]) export function getAssetFamily(value: string): AssetFamily | undefined { return ASSET_FAMILY_BY_KEY.get(value.toLowerCase()) } +export function getForbiddenAssetFamilyAlias(value: string): AssetFamily | undefined { + return FORBIDDEN_ASSET_FAMILY_ALIASES.get(value.toLowerCase()) +} + export function formatAssetFamilies() { return ASSET_FAMILIES.join(', ') } diff --git a/lib/validate-folder.ts b/lib/validate-folder.ts index 822fbd3..45dd94f 100644 --- a/lib/validate-folder.ts +++ b/lib/validate-folder.ts @@ -3,7 +3,7 @@ // --------------------------------------------------------------------------- import { ASSET_EXTENSIONS, TEXTURE_EXTENSIONS } from '@/lib/constants' -import { formatAssetFamilies, getAssetFamily } from '@/lib/asset-naming' +import { formatAssetFamilies, getAssetFamily, getForbiddenAssetFamilyAlias } from '@/lib/asset-naming' import type { TextureFile } from '@/lib/client-types' const SUPPORT_FILE_EXT_ARRAY = [...TEXTURE_EXTENSIONS, ...ASSET_EXTENSIONS] @@ -46,15 +46,31 @@ function getTextureNamingError(file: File) { const stem = getFileStem(file.name) const [prefix, ...targetParts] = stem.split('_') const family = getAssetFamily(prefix) + const extension = file.name.split('.').pop() if (family && targetParts.every(Boolean)) return null + const aliasSuggestion = getForbiddenAssetFamilyAlias(prefix) + + if (aliasSuggestion && targetParts.every(Boolean)) { + const target = targetParts.join('_') + return `Convention invalide : ${file.name}. Utilisez ${aliasSuggestion}_${target}.${extension} pour cibler "${target}", ou ${aliasSuggestion}.${extension} pour tout le modele.` + } + const reversedParts = stem.split('_') const reversedFamily = reversedParts.length > 1 ? getAssetFamily(reversedParts[reversedParts.length - 1]) : undefined + const reversedAliasSuggestion = reversedParts.length > 1 + ? getForbiddenAssetFamilyAlias(reversedParts[reversedParts.length - 1]) + : undefined if (reversedFamily) { const target = reversedParts.slice(0, -1).join('_') - return `Convention invalide : ${file.name}. Utilisez ${reversedFamily}_${target}.${file.name.split('.').pop()} pour cibler "${target}", ou ${reversedFamily}.${file.name.split('.').pop()} pour tout le modele.` + return `Convention invalide : ${file.name}. Utilisez ${reversedFamily}_${target}.${extension} pour cibler "${target}", ou ${reversedFamily}.${extension} pour tout le modele.` + } + + if (reversedAliasSuggestion) { + const target = reversedParts.slice(0, -1).join('_') + return `Convention invalide : ${file.name}. Utilisez ${reversedAliasSuggestion}_${target}.${extension} pour cibler "${target}", ou ${reversedAliasSuggestion}.${extension} pour tout le modele.` } return `Asset inconnu : ${file.name}. Familles autorisees : ${formatAssetFamilies()}. Utilisez asset.png pour tout le modele ou asset_objet.png pour cibler un objet.`