Skip to content

Commit

Permalink
Improve glTF material export code (#13229)
Browse files Browse the repository at this point in the history
  • Loading branch information
bghgary authored Nov 14, 2022
1 parent 0049371 commit cac7d14
Show file tree
Hide file tree
Showing 5 changed files with 249 additions and 395 deletions.
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
import type { ITextureInfo, IKHRTextureTransform } from "babylonjs-gltf2interface";
import { Tools } from "core/Misc/tools";
import type { Texture } from "core/Materials/Textures/texture";
import { ProceduralTexture } from "core/Materials/Textures/Procedurals/proceduralTexture";
import type { Scene } from "core/scene";

import type { Nullable } from "core/types";
import type { IGLTFExporterExtensionV2 } from "../glTFExporterExtension";
import { _Exporter } from "../glTFExporter";

const NAME = "KHR_texture_transform";

import "../shaders/textureTransform.fragment";

/**
* @internal
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
export class KHR_texture_transform implements IGLTFExporterExtensionV2 {
private _recordedTextures: ProceduralTexture[] = [];

/** Name of this extension */
public readonly name = NAME;

Expand All @@ -32,11 +26,7 @@ export class KHR_texture_transform implements IGLTFExporterExtensionV2 {

constructor() {}

public dispose() {
for (const texture of this._recordedTextures) {
texture.dispose();
}
}
public dispose() {}

/** @internal */
public get wasUsed() {
Expand Down Expand Up @@ -85,76 +75,26 @@ export class KHR_texture_transform implements IGLTFExporterExtensionV2 {
}
}

public preExportTextureAsync(context: string, babylonTexture: Texture): Promise<Texture> {
public preExportTextureAsync(context: string, babylonTexture: Texture): Promise<Nullable<Texture>> {
return new Promise((resolve, reject) => {
const scene = babylonTexture.getScene();
if (!scene) {
reject(`${context}: "scene" is not defined for Babylon texture ${babylonTexture.name}!`);
return;
}

let bakeTextureTransform = false;

/*
* The KHR_texture_transform schema only supports rotation around the origin.
* the texture must be baked to preserve appearance.
* see: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_texture_transform#gltf-schema-updates
* See https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_texture_transform#gltf-schema-updates.
*/
if (
(babylonTexture.uAng !== 0 || babylonTexture.wAng !== 0 || babylonTexture.vAng !== 0) &&
(babylonTexture.uRotationCenter !== 0 || babylonTexture.vRotationCenter !== 0)
) {
bakeTextureTransform = true;
}

if (!bakeTextureTransform) {
resolve(babylonTexture);
return;
}

return this._textureTransformTextureAsync(babylonTexture, scene)
.then((proceduralTexture) => {
resolve(proceduralTexture);
})
.catch((e) => {
reject(e);
});
});
}

/**
* Transform the babylon texture by the offset, rotation and scale parameters using a procedural texture
* @param babylonTexture
* @param scene
*/
private _textureTransformTextureAsync(babylonTexture: Texture, scene: Scene): Promise<Texture> {
return new Promise((resolve) => {
const proceduralTexture = new ProceduralTexture(`${babylonTexture.name}`, babylonTexture.getSize(), "textureTransform", scene);
if (!proceduralTexture) {
Tools.Log(`Cannot create procedural texture for ${babylonTexture.name}!`);
resolve(babylonTexture);
}

proceduralTexture.reservedDataStore = {
hidden: true,
source: babylonTexture,
};

this._recordedTextures.push(proceduralTexture);

proceduralTexture.coordinatesIndex = babylonTexture.coordinatesIndex;
proceduralTexture.setTexture("textureSampler", babylonTexture);
proceduralTexture.setMatrix("textureTransformMat", babylonTexture.getTextureMatrix());

// isReady trigger creation of effect if it doesnt exist yet
if (proceduralTexture.isReady()) {
proceduralTexture.render();
resolve(proceduralTexture);
Tools.Warn(`${context}: Texture ${babylonTexture.name} with rotation not centered at the origin cannot be exported with ${NAME}`);
resolve(null);
} else {
proceduralTexture.getEffect().executeWhenCompiled(() => {
proceduralTexture.render();
resolve(proceduralTexture);
});
resolve(babylonTexture);
}
});
}
Expand Down
12 changes: 6 additions & 6 deletions packages/dev/serializers/src/glTF/2.0/glTFExporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,9 @@ export class _Exporter {
* Stores a map of the image data, where the key is the file name and the value
* is the image data
*/
public _imageData: { [fileName: string]: { data: Uint8Array; mimeType: ImageMimeType } };
public _imageData: { [fileName: string]: { data: ArrayBuffer; mimeType: ImageMimeType } };

protected _orderedImageData: Array<{ data: Uint8Array; mimeType: ImageMimeType }>;
protected _orderedImageData: Array<{ data: ArrayBuffer; mimeType: ImageMimeType }>;

/**
* Stores a map of the unique id of a node to its index in the node array
Expand Down Expand Up @@ -1054,7 +1054,7 @@ export class _Exporter {
private _generateJSON(shouldUseGlb: boolean, glTFPrefix?: string, prettyPrint?: boolean): string {
const buffer: IBuffer = { byteLength: this._totalByteLength };
let imageName: string;
let imageData: { data: Uint8Array; mimeType: ImageMimeType };
let imageData: { data: ArrayBuffer; mimeType: ImageMimeType };
let bufferView: IBufferView;
let byteOffset: number = this._totalByteLength;

Expand Down Expand Up @@ -1106,8 +1106,8 @@ export class _Exporter {
imageData = this._imageData[image.uri];
this._orderedImageData.push(imageData);
imageName = image.uri.split(".")[0] + " image";
bufferView = _GLTFUtilities._CreateBufferView(0, byteOffset, imageData.data.length, undefined, imageName);
byteOffset += imageData.data.buffer.byteLength;
bufferView = _GLTFUtilities._CreateBufferView(0, byteOffset, imageData.data.byteLength, undefined, imageName);
byteOffset += imageData.data.byteLength;
this._bufferViews.push(bufferView);
image.bufferView = this._bufferViews.length - 1;
image.name = imageName;
Expand Down Expand Up @@ -1281,7 +1281,7 @@ export class _Exporter {

// binary data
for (let i = 0; i < this._orderedImageData.length; ++i) {
glbData.push(this._orderedImageData[i].data.buffer);
glbData.push(this._orderedImageData[i].data);
}

glbData.push(binPaddingBuffer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export interface IGLTFExporterExtensionV2 extends IGLTFExporterExtension, IDispo
* @param mimeType The mime-type of the generated image
* @returns A promise that resolves with the exported texture
*/
preExportTextureAsync?(context: string, babylonTexture: Nullable<Texture>, mimeType: ImageMimeType): Promise<Texture>;
preExportTextureAsync?(context: string, babylonTexture: Nullable<Texture>, mimeType: ImageMimeType): Promise<Nullable<Texture>>;

/**
* Define this method to get notified when a texture info is created
Expand Down
Loading

0 comments on commit cac7d14

Please sign in to comment.