Skip to content

Commit

Permalink
Export KHR extensions: IOR, Transmission and Volume (#12389)
Browse files Browse the repository at this point in the history
* initial support for KHR_maetrials_specular

* update factors test to keep 0 significativ

* update with prettier

* link to specification

* collapse,unecessary test block

* Add support for KHR_materials ior

* add ref to KHR_materials_ior

* finalize extensions

* IKHRLightsPunctual_LightType name change

* Revert "IKHRLightsPunctual_LightType name change"

This reverts commit 0b87226.

* Update KHR_lights_punctual.ts

* formatting

* update test

Co-authored-by: Gary Hsu <[email protected]>

* add test if refraction enabled

Co-authored-by: Gary Hsu <[email protected]>
  • Loading branch information
pandaGaume and bghgary authored Apr 21, 2022
1 parent 20b8ab5 commit 145041a
Show file tree
Hide file tree
Showing 10 changed files with 321 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { VertexBuffer } from "core/Buffers/buffer";
import { Geometry } from "core/Meshes/geometry";
import type { Mesh } from "core/Meshes/mesh";

import type { IKHRDracoMeshCompression } from "babylonjs-gltf2interface";
import { MeshPrimitiveMode, AccessorComponentType } from "babylonjs-gltf2interface";
import type { IKHRDracoMeshCompression } from "babylonjs-gltf2interface";
import type { IMeshPrimitive, IBufferView } from "../glTFLoaderInterfaces";
import type { IGLTFLoaderExtension } from "../glTFLoaderExtension";
import { GLTFLoader, ArrayItem } from "../glTFLoader";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Light } from "core/Lights/light";
import type { TransformNode } from "core/Meshes/transformNode";

import type { IKHRLightsPunctual_LightReference, IKHRLightsPunctual_Light, IKHRLightsPunctual } from "babylonjs-gltf2interface";
import { IKHRLightsPunctual_LightType } from "babylonjs-gltf2interface";
import { KHRLightsPunctual_LightType } from "babylonjs-gltf2interface";
import type { INode } from "../glTFLoaderInterfaces";
import type { IGLTFLoaderExtension } from "../glTFLoaderExtension";
import { GLTFLoader, ArrayItem } from "../glTFLoader";
Expand Down Expand Up @@ -74,15 +74,15 @@ export class KHR_lights implements IGLTFLoaderExtension {
this._loader.babylonScene._blockEntityCollection = !!this._loader._assetContainer;

switch (light.type) {
case IKHRLightsPunctual_LightType.DIRECTIONAL: {
case KHRLightsPunctual_LightType.DIRECTIONAL: {
babylonLight = new DirectionalLight(name, Vector3.Backward(), this._loader.babylonScene);
break;
}
case IKHRLightsPunctual_LightType.POINT: {
case KHRLightsPunctual_LightType.POINT: {
babylonLight = new PointLight(name, Vector3.Zero(), this._loader.babylonScene);
break;
}
case IKHRLightsPunctual_LightType.SPOT: {
case KHRLightsPunctual_LightType.SPOT: {
const babylonSpotLight = new SpotLight(name, Vector3.Zero(), Vector3.Backward(), 0, 1, this._loader.babylonScene);
babylonSpotLight.angle = ((light.spot && light.spot.outerConeAngle) || Math.PI / 4) * 2;
babylonSpotLight.innerAngle = ((light.spot && light.spot.innerConeAngle) || 0) * 2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,10 @@ import type { BaseTexture } from "core/Materials/Textures/baseTexture";
import type { IMaterial, ITextureInfo } from "../glTFLoaderInterfaces";
import type { IGLTFLoaderExtension } from "../glTFLoaderExtension";
import { GLTFLoader } from "../glTFLoader";
import type { IKHRMaterialsVolume } from "babylonjs-gltf2interface";

const NAME = "KHR_materials_volume";

interface IMaterialsTransmission {
attenuationColor?: number[];
attenuationDistance?: number;
thicknessTexture?: ITextureInfo;
thicknessFactor?: number;
}

/**
* [Specification](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_volume)
* @since 5.0.0
Expand Down Expand Up @@ -66,7 +60,7 @@ export class KHR_materials_volume implements IGLTFLoaderExtension {
* @hidden
*/
public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable<Promise<void>> {
return GLTFLoader.LoadExtensionAsync<IMaterialsTransmission>(context, material, this.name, (extensionContext, extension) => {
return GLTFLoader.LoadExtensionAsync<IKHRMaterialsVolume>(context, material, this.name, (extensionContext, extension) => {
const promises = new Array<Promise<any>>();
promises.push(this._loader.loadMaterialBasePropertiesAsync(context, material, babylonMaterial));
promises.push(this._loader.loadMaterialPropertiesAsync(context, material, babylonMaterial));
Expand All @@ -75,7 +69,7 @@ export class KHR_materials_volume implements IGLTFLoaderExtension {
});
}

private _loadVolumePropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, extension: IMaterialsTransmission): Promise<void> {
private _loadVolumePropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, extension: IKHRMaterialsVolume): Promise<void> {
if (!(babylonMaterial instanceof PBRMaterial)) {
throw new Error(`${context}: Material type not supported`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { DirectionalLight } from "core/Lights/directionalLight";
import type { Node } from "core/node";
import { ShadowLight } from "core/Lights/shadowLight";
import type { INode, IKHRLightsPunctual_LightReference, IKHRLightsPunctual_Light, IKHRLightsPunctual } from "babylonjs-gltf2interface";
import { IKHRLightsPunctual_LightType } from "babylonjs-gltf2interface";
import { KHRLightsPunctual_LightType } from "babylonjs-gltf2interface";
import type { IGLTFExporterExtensionV2 } from "../glTFExporterExtension";
import { _Exporter } from "../glTFExporter";
import { Logger } from "core/Misc/logger";
Expand Down Expand Up @@ -72,11 +72,11 @@ export class KHR_lights_punctual implements IGLTFExporterExtensionV2 {

const lightType =
babylonLight.getTypeID() == Light.LIGHTTYPEID_POINTLIGHT
? IKHRLightsPunctual_LightType.POINT
? KHRLightsPunctual_LightType.POINT
: babylonLight.getTypeID() == Light.LIGHTTYPEID_DIRECTIONALLIGHT
? IKHRLightsPunctual_LightType.DIRECTIONAL
? KHRLightsPunctual_LightType.DIRECTIONAL
: babylonLight.getTypeID() == Light.LIGHTTYPEID_SPOTLIGHT
? IKHRLightsPunctual_LightType.SPOT
? KHRLightsPunctual_LightType.SPOT
: null;
if (lightType == null) {
Logger.Warn(`${context}: Light ${babylonLight.name} is not supported in ${NAME}`);
Expand All @@ -89,7 +89,7 @@ export class KHR_lights_punctual implements IGLTFExporterExtensionV2 {
}
node.translation = lightPosition.asArray();
}
if (lightType !== IKHRLightsPunctual_LightType.POINT) {
if (lightType !== KHRLightsPunctual_LightType.POINT) {
const localAxis = babylonLight.direction;
const yaw = -Math.atan2(localAxis.z * (this._exporter._babylonScene.useRightHandedSystem ? -1 : 1), localAxis.x) + Math.PI / 2;
const len = Math.sqrt(localAxis.x * localAxis.x + localAxis.z * localAxis.z);
Expand Down Expand Up @@ -119,7 +119,7 @@ export class KHR_lights_punctual implements IGLTFExporterExtensionV2 {
light.range = babylonLight.range;
}

if (lightType === IKHRLightsPunctual_LightType.SPOT) {
if (lightType === KHRLightsPunctual_LightType.SPOT) {
const babylonSpotLight = babylonLight as SpotLight;
if (babylonSpotLight.angle !== Math.PI / 2.0) {
if (light.spot == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import type { IMaterial, IKHRMaterialsIor } from "babylonjs-gltf2interface";
import type { IGLTFExporterExtensionV2 } from "../glTFExporterExtension";
import { _Exporter } from "../glTFExporter";
import type { Material } from "core/Materials/material";
import { PBRMaterial } from "core/Materials/PBR/pbrMaterial";

const NAME = "KHR_materials_ior";

/**
* [Specification](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_ior/README.md)
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
export class KHR_materials_ior implements IGLTFExporterExtensionV2 {
/** Name of this extension */
public readonly name = NAME;

/** Defines whether this extension is enabled */
public enabled = true;

/** Defines whether this extension is required */
public required = false;

private _wasUsed = false;

constructor() {}

public dispose() {}

/** @hidden */
public get wasUsed() {
return this._wasUsed;
}

private _isExtensionEnabled(mat: PBRMaterial): boolean {
// This extension must not be used on a material that also uses KHR_materials_unlit
if (mat.unlit) {
return false;
}
return mat.indexOfRefraction != undefined && mat.indexOfRefraction != 1.5; // 1.5 is normative default value.
}

public postExportMaterialAsync?(context: string, node: IMaterial, babylonMaterial: Material): Promise<IMaterial> {
return new Promise((resolve) => {
if (babylonMaterial instanceof PBRMaterial && this._isExtensionEnabled(babylonMaterial)) {
this._wasUsed = true;

const iorInfo: IKHRMaterialsIor = {
ior: babylonMaterial.indexOfRefraction,
};
node.extensions = node.extensions || {};
node.extensions[NAME] = iorInfo;
}
resolve(node);
});
}
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
_Exporter.RegisterExtension(NAME, (exporter) => new KHR_materials_ior());
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ export class KHR_materials_specular implements IGLTFExporterExtensionV2 {
}

private _isExtensionEnabled(mat: PBRMaterial): boolean {
// This extension must not be used on a material that also uses KHR_materials_unlit
if (mat.unlit) {
return false;
}
return (
(mat.metallicF0Factor != undefined && mat.metallicF0Factor != 1.0) ||
(mat.metallicReflectanceColor != undefined && !mat.metallicReflectanceColor.equalsFloats(1.0, 1.0, 1.0)) ||
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import type { IMaterial, IKHRMaterialsTransmission } from "babylonjs-gltf2interface";
import type { IGLTFExporterExtensionV2 } from "../glTFExporterExtension";
import { _Exporter } from "../glTFExporter";
import type { Material } from "core/Materials/material";
import { PBRMaterial } from "core/Materials/PBR/pbrMaterial";
import type { BaseTexture } from "core/Materials/Textures/baseTexture";

const NAME = "KHR_materials_transmission";

/**
* [Specification](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_transmission/README.md)
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
export class KHR_materials_transmission implements IGLTFExporterExtensionV2 {
/** Name of this extension */
public readonly name = NAME;

/** Defines whether this extension is enabled */
public enabled = true;

/** Defines whether this extension is required */
public required = false;

private _exporter: _Exporter;

private _wasUsed = false;

constructor(exporter: _Exporter) {
this._exporter = exporter;
}

public dispose() {}

/** @hidden */
public get wasUsed() {
return this._wasUsed;
}

public postExportMaterialAdditionalTextures?(context: string, node: IMaterial, babylonMaterial: Material): BaseTexture[] {
const additionalTextures: BaseTexture[] = [];

if (babylonMaterial instanceof PBRMaterial) {
if (this._isExtensionEnabled(babylonMaterial)) {
if (babylonMaterial.subSurface.thicknessTexture) {
additionalTextures.push(babylonMaterial.subSurface.thicknessTexture);
}
return additionalTextures;
}
}

return additionalTextures;
}

private _isExtensionEnabled(mat: PBRMaterial): boolean {
// This extension must not be used on a material that also uses KHR_materials_unlit
if (mat.unlit) {
return false;
}
const subs = mat.subSurface;
return (subs.isRefractionEnabled && subs.refractionIntensity != undefined && subs.refractionIntensity != 0) || this._hasTexturesExtension(mat);
}

private _hasTexturesExtension(mat: PBRMaterial): boolean {
return mat.subSurface.refractionIntensityTexture != null;
}

public postExportMaterialAsync?(context: string, node: IMaterial, babylonMaterial: Material): Promise<IMaterial> {
return new Promise((resolve) => {
if (babylonMaterial instanceof PBRMaterial && this._isExtensionEnabled(babylonMaterial)) {
this._wasUsed = true;

const subs = babylonMaterial.subSurface;
const transmissionFactor = subs.refractionIntensity === 0 ? undefined : subs.refractionIntensity;

const transmissionTexture = this._exporter._glTFMaterialExporter._getTextureInfo(subs.refractionIntensityTexture) ?? undefined;

const volumeInfo: IKHRMaterialsTransmission = {
transmissionFactor: transmissionFactor,
transmissionTexture: transmissionTexture,
hasTextures: () => {
return this._hasTexturesExtension(babylonMaterial);
},
};
node.extensions = node.extensions || {};
node.extensions[NAME] = volumeInfo;
}
resolve(node);
});
}
}

_Exporter.RegisterExtension(NAME, (exporter) => new KHR_materials_transmission(exporter));
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import type { IMaterial, IKHRMaterialsVolume } from "babylonjs-gltf2interface";
import type { IGLTFExporterExtensionV2 } from "../glTFExporterExtension";
import { _Exporter } from "../glTFExporter";
import type { Material } from "core/Materials/material";
import { PBRMaterial } from "core/Materials/PBR/pbrMaterial";
import type { BaseTexture } from "core/Materials/Textures/baseTexture";
import { Color3 } from "core/Maths/math.color";

const NAME = "KHR_materials_volume";

/**
* [Specification](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_volume/README.md)
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
export class KHR_materials_volume implements IGLTFExporterExtensionV2 {
/** Name of this extension */
public readonly name = NAME;

/** Defines whether this extension is enabled */
public enabled = true;

/** Defines whether this extension is required */
public required = false;

private _exporter: _Exporter;

private _wasUsed = false;

constructor(exporter: _Exporter) {
this._exporter = exporter;
}

public dispose() {}

/** @hidden */
public get wasUsed() {
return this._wasUsed;
}

public postExportMaterialAdditionalTextures?(context: string, node: IMaterial, babylonMaterial: Material): BaseTexture[] {
const additionalTextures: BaseTexture[] = [];

if (babylonMaterial instanceof PBRMaterial) {
if (this._isExtensionEnabled(babylonMaterial)) {
if (babylonMaterial.subSurface.thicknessTexture) {
additionalTextures.push(babylonMaterial.subSurface.thicknessTexture);
}
return additionalTextures;
}
}

return additionalTextures;
}

private _isExtensionEnabled(mat: PBRMaterial): boolean {
// This extension must not be used on a material that also uses KHR_materials_unlit
if (mat.unlit) {
return false;
}
const subs = mat.subSurface;
// this extension requires either the KHR_materials_transmission or KHR_materials_translucency extensions.
if (!subs.isRefractionEnabled && !subs.isTranslucencyEnabled) {
return false;
}
return (
(subs.maximumThickness != undefined && subs.maximumThickness != 0) ||
(subs.tintColorAtDistance != undefined && subs.tintColorAtDistance != Number.POSITIVE_INFINITY) ||
(subs.tintColor != undefined && subs.tintColor != Color3.White()) ||
this._hasTexturesExtension(mat)
);
}

private _hasTexturesExtension(mat: PBRMaterial): boolean {
return mat.subSurface.thicknessTexture != null;
}

public postExportMaterialAsync?(context: string, node: IMaterial, babylonMaterial: Material): Promise<IMaterial> {
return new Promise((resolve) => {
if (babylonMaterial instanceof PBRMaterial && this._isExtensionEnabled(babylonMaterial)) {
this._wasUsed = true;

const subs = babylonMaterial.subSurface;
const thicknessFactor = subs.maximumThickness == 0 ? undefined : subs.maximumThickness;
const thicknessTexture = this._exporter._glTFMaterialExporter._getTextureInfo(subs.thicknessTexture) ?? undefined;
const attenuationDistance = subs.tintColorAtDistance == Number.POSITIVE_INFINITY ? undefined : subs.tintColorAtDistance;
const attenuationColor = subs.tintColor.equalsFloats(1.0, 1.0, 1.0) ? undefined : subs.tintColor.asArray();

const volumeInfo: IKHRMaterialsVolume = {
thicknessFactor: thicknessFactor,
thicknessTexture: thicknessTexture,
attenuationDistance: attenuationDistance,
attenuationColor: attenuationColor,
hasTextures: () => {
return this._hasTexturesExtension(babylonMaterial);
},
};
node.extensions = node.extensions || {};
node.extensions[NAME] = volumeInfo;
}
resolve(node);
});
}
}

_Exporter.RegisterExtension(NAME, (exporter) => new KHR_materials_volume(exporter));
3 changes: 3 additions & 0 deletions packages/dev/serializers/src/glTF/2.0/Extensions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ export * from "./KHR_materials_clearcoat";
export * from "./KHR_materials_iridescence";
export * from "./KHR_materials_sheen";
export * from "./KHR_materials_unlit";
export * from "./KHR_materials_ior";
export * from "./KHR_materials_specular";
export * from "./KHR_materials_volume";
export * from "./KHR_materials_transmission";
Loading

0 comments on commit 145041a

Please sign in to comment.