@gltf-transform/extensions
Version:
Adds extension support to @gltf-transform/core
171 lines (150 loc) • 6.76 kB
text/typescript
import { Extension, type GLTF, PropertyType, type ReaderContext, type WriterContext } from '@gltf-transform/core';
import { KHR_MATERIALS_IRIDESCENCE } from '../constants.js';
import { Iridescence } from './iridescence.js';
interface IridescenceDef {
iridescenceFactor: number;
iridescenceTexture: GLTF.ITextureInfo;
iridescenceIor: number;
iridescenceThicknessMinimum: number;
iridescenceThicknessMaximum: number;
iridescenceThicknessTexture: GLTF.ITextureInfo;
}
/**
* [`KHR_materials_iridescence`](https://github.com/KhronosGroup/gltf/blob/main/extensions/2.0/Khronos/KHR_materials_iridescence/)
* defines iridescence (thin film interference) on a PBR material.
*
* 
*
* > _**Figure:** Varying levels of iridescence IOR values.
* > Source: [Khronos Group](https://github.com/KhronosGroup/gltf/tree/main/extensions/2.0/Khronos/KHR_materials_iridescence)._
*
* Iridescence describes an effect where hue varies depending on the viewing
* angle and illumination angle: A thin-film of a semi-transparent layer
* results in inter-reflections and due to thin-film interference, certain
* wavelengths get absorbed or amplified. Iridescence can be seen on soap
* bubbles, oil films, or on the wings of many insects. With this extension,
* thickness and index of refraction (IOR) of the thin-film can be specified,
* enabling iridescent materials.
*
* Properties:
* - {@link Iridescence}
*
* ### Example
*
* The `KHRMaterialsIridescence` class provides a single {@link ExtensionProperty} type, `Iridescence`,
* which may be attached to any {@link Material} instance. For example:
*
* ```typescript
* import { KHRMaterialsIridescence, Iridescence } from '@gltf-transform/extensions';
*
* // Create an Extension attached to the Document.
* const iridescenceExtension = document.createExtension(KHRMaterialsIridescence);
*
* // Create an Iridescence property.
* const iridescence = iridescenceExtension.createIridescence()
* .setIridescenceFactor(1.0)
* .setIridescenceIOR(1.8);
*
* // Attach the property to a Material.
* material.setExtension('KHR_materials_iridescence', iridescence);
* ```
*/
export class KHRMaterialsIridescence extends Extension {
public static readonly EXTENSION_NAME: typeof KHR_MATERIALS_IRIDESCENCE = KHR_MATERIALS_IRIDESCENCE;
public readonly extensionName: typeof KHR_MATERIALS_IRIDESCENCE = KHR_MATERIALS_IRIDESCENCE;
public readonly prereadTypes: PropertyType[] = [PropertyType.MESH];
public readonly prewriteTypes: PropertyType[] = [PropertyType.MESH];
/** Creates a new Iridescence property for use on a {@link Material}. */
public createIridescence(): Iridescence {
return new Iridescence(this.document.getGraph());
}
/** @hidden */
public read(_context: ReaderContext): this {
return this;
}
/** @hidden */
public write(_context: WriterContext): this {
return this;
}
/** @hidden */
public preread(context: ReaderContext): this {
const jsonDoc = context.jsonDoc;
const materialDefs = jsonDoc.json.materials || [];
const textureDefs = jsonDoc.json.textures || [];
materialDefs.forEach((materialDef, materialIndex) => {
if (materialDef.extensions && materialDef.extensions[KHR_MATERIALS_IRIDESCENCE]) {
const iridescence = this.createIridescence();
context.materials[materialIndex].setExtension(KHR_MATERIALS_IRIDESCENCE, iridescence);
const iridescenceDef = materialDef.extensions[KHR_MATERIALS_IRIDESCENCE] as IridescenceDef;
// Factors.
if (iridescenceDef.iridescenceFactor !== undefined) {
iridescence.setIridescenceFactor(iridescenceDef.iridescenceFactor);
}
if (iridescenceDef.iridescenceIor !== undefined) {
iridescence.setIridescenceIOR(iridescenceDef.iridescenceIor);
}
if (iridescenceDef.iridescenceThicknessMinimum !== undefined) {
iridescence.setIridescenceThicknessMinimum(iridescenceDef.iridescenceThicknessMinimum);
}
if (iridescenceDef.iridescenceThicknessMaximum !== undefined) {
iridescence.setIridescenceThicknessMaximum(iridescenceDef.iridescenceThicknessMaximum);
}
// Textures.
if (iridescenceDef.iridescenceTexture !== undefined) {
const textureInfoDef = iridescenceDef.iridescenceTexture;
const texture = context.textures[textureDefs[textureInfoDef.index].source!];
iridescence.setIridescenceTexture(texture);
context.setTextureInfo(iridescence.getIridescenceTextureInfo()!, textureInfoDef);
}
if (iridescenceDef.iridescenceThicknessTexture !== undefined) {
const textureInfoDef = iridescenceDef.iridescenceThicknessTexture;
const texture = context.textures[textureDefs[textureInfoDef.index].source!];
iridescence.setIridescenceThicknessTexture(texture);
context.setTextureInfo(iridescence.getIridescenceThicknessTextureInfo()!, textureInfoDef);
}
}
});
return this;
}
/** @hidden */
public prewrite(context: WriterContext): this {
const jsonDoc = context.jsonDoc;
this.document
.getRoot()
.listMaterials()
.forEach((material) => {
const iridescence = material.getExtension<Iridescence>(KHR_MATERIALS_IRIDESCENCE);
if (iridescence) {
const materialIndex = context.materialIndexMap.get(material)!;
const materialDef = jsonDoc.json.materials![materialIndex];
materialDef.extensions = materialDef.extensions || {};
// Factors.
const iridescenceDef = (materialDef.extensions[KHR_MATERIALS_IRIDESCENCE] = {} as IridescenceDef);
if (iridescence.getIridescenceFactor() > 0) {
iridescenceDef.iridescenceFactor = iridescence.getIridescenceFactor();
}
if (iridescence.getIridescenceIOR() !== 1.3) {
iridescenceDef.iridescenceIor = iridescence.getIridescenceIOR();
}
if (iridescence.getIridescenceThicknessMinimum() !== 100) {
iridescenceDef.iridescenceThicknessMinimum = iridescence.getIridescenceThicknessMinimum();
}
if (iridescence.getIridescenceThicknessMaximum() !== 400) {
iridescenceDef.iridescenceThicknessMaximum = iridescence.getIridescenceThicknessMaximum();
}
// Textures.
if (iridescence.getIridescenceTexture()) {
const texture = iridescence.getIridescenceTexture()!;
const textureInfo = iridescence.getIridescenceTextureInfo()!;
iridescenceDef.iridescenceTexture = context.createTextureInfoDef(texture, textureInfo);
}
if (iridescence.getIridescenceThicknessTexture()) {
const texture = iridescence.getIridescenceThicknessTexture()!;
const textureInfo = iridescence.getIridescenceThicknessTextureInfo()!;
iridescenceDef.iridescenceThicknessTexture = context.createTextureInfoDef(texture, textureInfo);
}
}
});
return this;
}
}