Implement material signature and texture set management
This commit is contained in:
223
src/materials/material-rendering.ts
Normal file
223
src/materials/material-rendering.ts
Normal file
@@ -0,0 +1,223 @@
|
||||
import {
|
||||
NoColorSpace,
|
||||
RepeatWrapping,
|
||||
SRGBColorSpace,
|
||||
Texture,
|
||||
TextureLoader,
|
||||
UVMapping
|
||||
} from "three";
|
||||
|
||||
import type { LoadedImageAsset } from "../assets/image-assets";
|
||||
|
||||
import {
|
||||
createStarterMaterialSignature,
|
||||
createStarterMaterialTextureSet,
|
||||
disposeStarterMaterialTextureSet,
|
||||
type StarterMaterialTextureSet
|
||||
} from "./starter-material-textures";
|
||||
import type {
|
||||
CustomMaterialDef,
|
||||
MaterialDef,
|
||||
MaterialTextureSlot,
|
||||
StarterMaterialDef
|
||||
} from "./starter-material-library";
|
||||
|
||||
export interface MaterialTextureSet {
|
||||
baseColor: Texture | null;
|
||||
normal: Texture | null;
|
||||
roughness: Texture | null;
|
||||
metallic: Texture | null;
|
||||
specular: Texture | null;
|
||||
albedoHasAlpha: boolean;
|
||||
}
|
||||
|
||||
export interface CachedMaterialTextureSet {
|
||||
signature: string;
|
||||
textureSet: MaterialTextureSet;
|
||||
}
|
||||
|
||||
function createStarterTextureSet(
|
||||
material: StarterMaterialDef,
|
||||
loader: TextureLoader
|
||||
): MaterialTextureSet {
|
||||
const starterTextureSet = createStarterMaterialTextureSet(material, loader);
|
||||
|
||||
return {
|
||||
baseColor: starterTextureSet.baseColor,
|
||||
normal: starterTextureSet.normal,
|
||||
roughness: starterTextureSet.roughness,
|
||||
metallic: starterTextureSet.metallic,
|
||||
specular: starterTextureSet.specular,
|
||||
albedoHasAlpha: false
|
||||
};
|
||||
}
|
||||
|
||||
function configureCustomTexture(
|
||||
texture: Texture,
|
||||
slot: MaterialTextureSlot
|
||||
): Texture {
|
||||
texture.mapping = UVMapping;
|
||||
texture.wrapS = RepeatWrapping;
|
||||
texture.wrapT = RepeatWrapping;
|
||||
texture.repeat.set(1, 1);
|
||||
texture.colorSpace = slot === "albedo" ? SRGBColorSpace : NoColorSpace;
|
||||
texture.needsUpdate = true;
|
||||
return texture;
|
||||
}
|
||||
|
||||
function cloneCustomTexture(
|
||||
source: LoadedImageAsset | undefined,
|
||||
slot: MaterialTextureSlot
|
||||
): Texture | null {
|
||||
if (source === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return configureCustomTexture(source.texture.clone(), slot);
|
||||
}
|
||||
|
||||
function createCustomTextureSet(
|
||||
material: CustomMaterialDef,
|
||||
loadedImageAssets: Record<string, LoadedImageAsset>
|
||||
): MaterialTextureSet {
|
||||
const albedoAsset =
|
||||
material.textures.albedo === null
|
||||
? undefined
|
||||
: loadedImageAssets[material.textures.albedo.assetId];
|
||||
|
||||
return {
|
||||
baseColor: cloneCustomTexture(albedoAsset, "albedo"),
|
||||
normal:
|
||||
material.textures.normal === null
|
||||
? null
|
||||
: cloneCustomTexture(
|
||||
loadedImageAssets[material.textures.normal.assetId],
|
||||
"normal"
|
||||
),
|
||||
roughness:
|
||||
material.textures.roughness === null
|
||||
? null
|
||||
: cloneCustomTexture(
|
||||
loadedImageAssets[material.textures.roughness.assetId],
|
||||
"roughness"
|
||||
),
|
||||
metallic:
|
||||
material.textures.metallic === null
|
||||
? null
|
||||
: cloneCustomTexture(
|
||||
loadedImageAssets[material.textures.metallic.assetId],
|
||||
"metallic"
|
||||
),
|
||||
specular: null,
|
||||
albedoHasAlpha: albedoAsset?.metadata.hasAlpha ?? false
|
||||
};
|
||||
}
|
||||
|
||||
function getLoadedTextureSignature(
|
||||
loadedImageAssets: Record<string, LoadedImageAsset>,
|
||||
assetId: string
|
||||
): string {
|
||||
const loadedAsset = loadedImageAssets[assetId];
|
||||
|
||||
return loadedAsset === undefined
|
||||
? `${assetId}:missing`
|
||||
: `${assetId}:${loadedAsset.texture.uuid}`;
|
||||
}
|
||||
|
||||
function createCustomMaterialSignature(
|
||||
material: CustomMaterialDef,
|
||||
loadedImageAssets: Record<string, LoadedImageAsset>
|
||||
): string {
|
||||
return [
|
||||
material.kind,
|
||||
material.name,
|
||||
material.swatchColorHex,
|
||||
material.albedoColorHex,
|
||||
material.opacity,
|
||||
material.roughness,
|
||||
material.metallic,
|
||||
material.normalStrength,
|
||||
material.textures.albedo === null
|
||||
? "albedo:none"
|
||||
: `albedo:${getLoadedTextureSignature(
|
||||
loadedImageAssets,
|
||||
material.textures.albedo.assetId
|
||||
)}`,
|
||||
material.textures.normal === null
|
||||
? "normal:none"
|
||||
: `normal:${getLoadedTextureSignature(
|
||||
loadedImageAssets,
|
||||
material.textures.normal.assetId
|
||||
)}`,
|
||||
material.textures.roughness === null
|
||||
? "roughness:none"
|
||||
: `roughness:${getLoadedTextureSignature(
|
||||
loadedImageAssets,
|
||||
material.textures.roughness.assetId
|
||||
)}`,
|
||||
material.textures.metallic === null
|
||||
? "metallic:none"
|
||||
: `metallic:${getLoadedTextureSignature(
|
||||
loadedImageAssets,
|
||||
material.textures.metallic.assetId
|
||||
)}`
|
||||
].join("|");
|
||||
}
|
||||
|
||||
export function createMaterialSignature(
|
||||
material: MaterialDef,
|
||||
loadedImageAssets: Record<string, LoadedImageAsset>
|
||||
): string {
|
||||
return material.kind === "starter"
|
||||
? createStarterMaterialSignature(material)
|
||||
: createCustomMaterialSignature(material, loadedImageAssets);
|
||||
}
|
||||
|
||||
export function createMaterialTextureSet(
|
||||
material: MaterialDef,
|
||||
loader: TextureLoader,
|
||||
loadedImageAssets: Record<string, LoadedImageAsset>
|
||||
): MaterialTextureSet {
|
||||
return material.kind === "starter"
|
||||
? createStarterTextureSet(material, loader)
|
||||
: createCustomTextureSet(material, loadedImageAssets);
|
||||
}
|
||||
|
||||
export function disposeMaterialTextureSet(textureSet: MaterialTextureSet): void {
|
||||
if (textureSet.specular !== null || textureSet.metallic !== null) {
|
||||
const starterCandidate: StarterMaterialTextureSet = {
|
||||
baseColor: textureSet.baseColor as Texture,
|
||||
normal: textureSet.normal as Texture,
|
||||
roughness: textureSet.roughness as Texture,
|
||||
metallic: textureSet.metallic,
|
||||
specular: textureSet.specular
|
||||
};
|
||||
|
||||
if (
|
||||
starterCandidate.baseColor !== null &&
|
||||
starterCandidate.normal !== null &&
|
||||
starterCandidate.roughness !== null
|
||||
) {
|
||||
disposeStarterMaterialTextureSet(starterCandidate);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const textures = new Set<Texture>();
|
||||
|
||||
for (const texture of [
|
||||
textureSet.baseColor,
|
||||
textureSet.normal,
|
||||
textureSet.roughness,
|
||||
textureSet.metallic,
|
||||
textureSet.specular
|
||||
]) {
|
||||
if (texture !== null) {
|
||||
textures.add(texture);
|
||||
}
|
||||
}
|
||||
|
||||
for (const texture of textures) {
|
||||
texture.dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user