Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Export KHR extensions: IOR, Transmission and Volume #12389

Merged
merged 18 commits into from
Apr 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}

bghgary marked this conversation as resolved.
Show resolved Hide resolved
/**
* [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);
},
bghgary marked this conversation as resolved.
Show resolved Hide resolved
};
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