Refactor material definitions and update scene document versions to support custom PBR materials

This commit is contained in:
2026-05-18 15:57:45 +02:00
parent a029a99d80
commit 5b5ae00d68
4 changed files with 110 additions and 18 deletions

View File

@@ -1,5 +1,8 @@
import {
MATERIAL_TEXTURE_SLOTS,
createEmptyCustomMaterialTextureRefs,
createStarterMaterialRegistry,
type CustomMaterialTextureRefs,
type MaterialDef
} from "../materials/starter-material-library";
import type { Vec3 } from "../core/vector";
@@ -263,6 +266,7 @@ import {
SPLINE_PATH_SCENE_DOCUMENT_VERSION,
WHITEBOX_BOX_LIGHT_VOLUME_SCENE_DOCUMENT_VERSION,
CELESTIAL_BODY_OVERLAY_SCENE_DOCUMENT_VERSION,
CUSTOM_PBR_MATERIALS_SCENE_DOCUMENT_VERSION,
SCENE_DOCUMENT_VERSION,
STATIC_SIMPLE_MODEL_COLLIDERS_SCENE_DOCUMENT_VERSION,
TRIGGER_ACTION_TARGET_FOUNDATION_SCENE_DOCUMENT_VERSION,

View File

@@ -36,7 +36,8 @@ import {
type FoliagePrototypeRegistry
} from "../foliage/foliage";
export const SCENE_DOCUMENT_VERSION = 109 as const;
export const SCENE_DOCUMENT_VERSION = 110 as const;
export const CUSTOM_PBR_MATERIALS_SCENE_DOCUMENT_VERSION = 110 as const;
export const SPLINE_CORRIDOR_JUNCTION_SHAPE_SCENE_DOCUMENT_VERSION = 109 as const;
export const SPLINE_CORRIDOR_JUNCTION_EDGE_SCENE_DOCUMENT_VERSION = 108 as const;
export const SPLINE_CORRIDOR_JUNCTIONS_SCENE_DOCUMENT_VERSION = 107 as const;

View File

@@ -564,35 +564,46 @@ function cloneMaterialSizeCm(sizeCm: MaterialSizeCm): MaterialSizeCm {
};
}
export const STARTER_MATERIAL_LIBRARY: readonly MaterialDef[] =
export const STARTER_MATERIAL_LIBRARY: readonly StarterMaterialDef[] =
STARTER_MATERIAL_CATALOG.map((entry) => ({
...entry,
kind: "starter",
sizeCm: cloneMaterialSizeCm(entry.sizeCm),
tags: createMaterialTags(entry)
}));
export function getStarterMaterialAssetDirectory(material: MaterialDef): string {
export function getStarterMaterialAssetDirectory(
material: StarterMaterialDef
): string {
return `${STARTER_MATERIAL_ASSET_ROOT}/${material.assetFolder}`;
}
export function getStarterMaterialPreviewUrl(material: MaterialDef): string {
export function getStarterMaterialPreviewUrl(
material: StarterMaterialDef
): string {
return `${getStarterMaterialAssetDirectory(material)}/${material.previewImageName}`;
}
export function getStarterMaterialBaseColorUrl(material: MaterialDef): string {
export function getStarterMaterialBaseColorUrl(
material: StarterMaterialDef
): string {
return `${getStarterMaterialAssetDirectory(material)}/basecolor.webp`;
}
export function getStarterMaterialNormalUrl(material: MaterialDef): string {
export function getStarterMaterialNormalUrl(
material: StarterMaterialDef
): string {
return `${getStarterMaterialAssetDirectory(material)}/normal.webp`;
}
export function getStarterMaterialRoughnessUrl(material: MaterialDef): string {
export function getStarterMaterialRoughnessUrl(
material: StarterMaterialDef
): string {
return `${getStarterMaterialAssetDirectory(material)}/roughness.webp`;
}
export function getStarterMaterialMetallicUrl(
material: MaterialDef
material: StarterMaterialDef
): string | null {
return material.workflow === "metallic-roughness"
? `${getStarterMaterialAssetDirectory(material)}/metallic.webp`
@@ -600,14 +611,14 @@ export function getStarterMaterialMetallicUrl(
}
export function getStarterMaterialSpecularUrl(
material: MaterialDef
material: StarterMaterialDef
): string | null {
return material.workflow === "specular-roughness"
? `${getStarterMaterialAssetDirectory(material)}/specular.webp`
: null;
}
export function getStarterMaterialTileSizeMeters(material: MaterialDef): {
export function getStarterMaterialTileSizeMeters(material: StarterMaterialDef): {
x: number;
y: number;
} {
@@ -617,7 +628,7 @@ export function getStarterMaterialTileSizeMeters(material: MaterialDef): {
};
}
export function getStarterMaterialTextureRepeat(material: MaterialDef): {
export function getStarterMaterialTextureRepeat(material: StarterMaterialDef): {
x: number;
y: number;
} {
@@ -630,10 +641,18 @@ export function getStarterMaterialTextureRepeat(material: MaterialDef): {
}
export function cloneMaterialDef(material: MaterialDef): MaterialDef {
if (material.kind === "starter") {
return {
...material,
sizeCm: cloneMaterialSizeCm(material.sizeCm),
tags: [...material.tags]
};
}
return {
...material,
sizeCm: cloneMaterialSizeCm(material.sizeCm),
tags: [...material.tags]
tags: [...material.tags],
textures: cloneCustomMaterialTextureRefs(material.textures)
};
}
@@ -656,3 +675,68 @@ export function createStarterMaterialRegistry(): Record<string, MaterialDef> {
])
);
}
export function createEmptyCustomMaterialTextureRefs(): CustomMaterialTextureRefs {
return {
albedo: null,
normal: null,
roughness: null,
metallic: null
};
}
export function cloneCustomMaterialTextureRefs(
textures: CustomMaterialTextureRefs
): CustomMaterialTextureRefs {
return {
albedo:
textures.albedo === null ? null : { assetId: textures.albedo.assetId },
normal:
textures.normal === null ? null : { assetId: textures.normal.assetId },
roughness:
textures.roughness === null
? null
: { assetId: textures.roughness.assetId },
metallic:
textures.metallic === null
? null
: { assetId: textures.metallic.assetId }
};
}
export function createCustomMaterialDef(
overrides: Partial<
Pick<
CustomMaterialDef,
| "id"
| "name"
| "swatchColorHex"
| "tags"
| "albedoColorHex"
| "opacity"
| "roughness"
| "metallic"
| "normalStrength"
| "textures"
>
> = {}
): CustomMaterialDef {
const albedoColorHex = overrides.albedoColorHex ?? "#ffffff";
return {
id: overrides.id ?? createOpaqueId("material"),
kind: "custom",
name: overrides.name ?? "Custom Material",
swatchColorHex: overrides.swatchColorHex ?? albedoColorHex,
tags: overrides.tags ?? ["custom"],
albedoColorHex,
opacity: overrides.opacity ?? 1,
roughness: overrides.roughness ?? 0.8,
metallic: overrides.metallic ?? 0,
normalStrength: overrides.normalStrength ?? 1,
textures:
overrides.textures === undefined
? createEmptyCustomMaterialTextureRefs()
: cloneCustomMaterialTextureRefs(overrides.textures)
};
}

View File

@@ -12,7 +12,7 @@ import {
getStarterMaterialRoughnessUrl,
getStarterMaterialSpecularUrl,
getStarterMaterialTextureRepeat,
type MaterialDef
type StarterMaterialDef
} from "./starter-material-library";
export interface StarterMaterialTextureSet {
@@ -23,8 +23,11 @@ export interface StarterMaterialTextureSet {
specular: Texture | null;
}
export function createStarterMaterialSignature(material: MaterialDef): string {
export function createStarterMaterialSignature(
material: StarterMaterialDef
): string {
return [
material.kind,
material.assetFolder,
material.workflow,
material.previewImageName,
@@ -72,7 +75,7 @@ function createRepeatableTextureImage(
function configureTexture(
texture: Texture,
material: MaterialDef,
material: StarterMaterialDef,
options: { colorSpace?: typeof SRGBColorSpace | null }
) {
const repeat = getStarterMaterialTextureRepeat(material);
@@ -85,7 +88,7 @@ function configureTexture(
function loadMaterialTexture(
loader: TextureLoader,
url: string,
material: MaterialDef,
material: StarterMaterialDef,
options: { colorSpace?: typeof SRGBColorSpace | null }
): Texture {
const texture = loader.load(url, (loadedTexture) => {
@@ -102,7 +105,7 @@ function loadMaterialTexture(
}
export function createStarterMaterialTextureSet(
material: MaterialDef,
material: StarterMaterialDef,
loader: TextureLoader = new TextureLoader()
): StarterMaterialTextureSet {
const metallicUrl = getStarterMaterialMetallicUrl(material);