fix: canonicalize asset families
This commit is contained in:
@@ -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.
|
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)
|
### Production (Coolify / Docker)
|
||||||
|
|
||||||
|
|||||||
@@ -6,19 +6,19 @@ export function classifyAssetCategory(filename: string): AssetCategory {
|
|||||||
const name = filename.replace(/\.[^.]+$/, '')
|
const name = filename.replace(/\.[^.]+$/, '')
|
||||||
const family = getAssetFamily(name.split('_')[0])
|
const family = getAssetFamily(name.split('_')[0])
|
||||||
|
|
||||||
if (family === 'baseColor' || family === 'color') {
|
if (family === 'color' || family === 'diffuse') {
|
||||||
return 'color'
|
return 'color'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (family === 'roughness' || family === 'occlusionRoughnessMetallic') {
|
if (family === 'roughness') {
|
||||||
return 'roughness'
|
return 'roughness'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (family === 'normal' || family === 'normalOpengl') {
|
if (family === 'normal') {
|
||||||
return 'normal'
|
return 'normal'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (family === 'metallic' || family === 'metalness') {
|
if (family === 'metalness') {
|
||||||
return 'metalness'
|
return 'metalness'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-4
@@ -1,12 +1,9 @@
|
|||||||
export const ASSET_FAMILIES = [
|
export const ASSET_FAMILIES = [
|
||||||
'baseColor',
|
|
||||||
'color',
|
'color',
|
||||||
|
'diffuse',
|
||||||
'roughness',
|
'roughness',
|
||||||
'normal',
|
'normal',
|
||||||
'normalOpengl',
|
|
||||||
'metallic',
|
|
||||||
'metalness',
|
'metalness',
|
||||||
'occlusionRoughnessMetallic',
|
|
||||||
'height',
|
'height',
|
||||||
'opacity',
|
'opacity',
|
||||||
] as const
|
] as const
|
||||||
@@ -14,11 +11,24 @@ export const ASSET_FAMILIES = [
|
|||||||
export type AssetFamily = typeof ASSET_FAMILIES[number]
|
export type AssetFamily = typeof ASSET_FAMILIES[number]
|
||||||
|
|
||||||
const ASSET_FAMILY_BY_KEY = new Map(ASSET_FAMILIES.map((family) => [family.toLowerCase(), family]))
|
const ASSET_FAMILY_BY_KEY = new Map(ASSET_FAMILIES.map((family) => [family.toLowerCase(), family]))
|
||||||
|
const FORBIDDEN_ASSET_FAMILY_ALIASES: ReadonlyMap<string, AssetFamily> = 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 {
|
export function getAssetFamily(value: string): AssetFamily | undefined {
|
||||||
return ASSET_FAMILY_BY_KEY.get(value.toLowerCase())
|
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() {
|
export function formatAssetFamilies() {
|
||||||
return ASSET_FAMILIES.join(', ')
|
return ASSET_FAMILIES.join(', ')
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-2
@@ -3,7 +3,7 @@
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
import { ASSET_EXTENSIONS, TEXTURE_EXTENSIONS } from '@/lib/constants'
|
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'
|
import type { TextureFile } from '@/lib/client-types'
|
||||||
|
|
||||||
const SUPPORT_FILE_EXT_ARRAY = [...TEXTURE_EXTENSIONS, ...ASSET_EXTENSIONS]
|
const SUPPORT_FILE_EXT_ARRAY = [...TEXTURE_EXTENSIONS, ...ASSET_EXTENSIONS]
|
||||||
@@ -46,15 +46,31 @@ function getTextureNamingError(file: File) {
|
|||||||
const stem = getFileStem(file.name)
|
const stem = getFileStem(file.name)
|
||||||
const [prefix, ...targetParts] = stem.split('_')
|
const [prefix, ...targetParts] = stem.split('_')
|
||||||
const family = getAssetFamily(prefix)
|
const family = getAssetFamily(prefix)
|
||||||
|
const extension = file.name.split('.').pop()
|
||||||
|
|
||||||
if (family && targetParts.every(Boolean)) return null
|
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 reversedParts = stem.split('_')
|
||||||
const reversedFamily = reversedParts.length > 1 ? getAssetFamily(reversedParts[reversedParts.length - 1]) : undefined
|
const reversedFamily = reversedParts.length > 1 ? getAssetFamily(reversedParts[reversedParts.length - 1]) : undefined
|
||||||
|
const reversedAliasSuggestion = reversedParts.length > 1
|
||||||
|
? getForbiddenAssetFamilyAlias(reversedParts[reversedParts.length - 1])
|
||||||
|
: undefined
|
||||||
|
|
||||||
if (reversedFamily) {
|
if (reversedFamily) {
|
||||||
const target = reversedParts.slice(0, -1).join('_')
|
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.`
|
return `Asset inconnu : ${file.name}. Familles autorisees : ${formatAssetFamilies()}. Utilisez asset.png pour tout le modele ou asset_objet.png pour cibler un objet.`
|
||||||
|
|||||||
Reference in New Issue
Block a user