From 7d16c5b3accf8569202ad8b8a234ac3c7dc5402f Mon Sep 17 00:00:00 2001 From: Branimir Karadzic Date: Wed, 10 Jun 2026 16:00:22 -0700 Subject: [PATCH 1/3] [Native] Load single-file .dds/.ktx/.ktx2 cubemaps on the native engine The native createCubeTexture override only handled a single .env file or six face files; a single self-contained cubemap container (.dds/.ktx/.ktx2, as produced by CubeTexture.CreateFromPrefilteredData) threw "Cannot load cubemap because 6 files were not defined". The generic WebGL loader route is unusable on native (its texture-upload and cube-readback entry points are unimplemented). Route a single-URL container cubemap to engine.loadCubeTexture with the raw buffer; the native engine decodes it (bimg) and, when polynomials are requested, returns spherical-harmonics coefficients computed from the top mip, which are set as the texture's spherical polynomial. loadCubeTexture's onSuccess now optionally carries those coefficients. The .env and six-file paths are unchanged. Pairs with a Babylon Native NativeEngine change implementing the single-buffer cube decode and spherical-harmonics computation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../nativeEngine.cubeTexture.pure.ts | 111 ++++++++++++++---- .../src/Engines/Native/nativeInterfaces.ts | 34 +++++- 2 files changed, 119 insertions(+), 26 deletions(-) diff --git a/packages/dev/core/src/Engines/Native/Extensions/nativeEngine.cubeTexture.pure.ts b/packages/dev/core/src/Engines/Native/Extensions/nativeEngine.cubeTexture.pure.ts index bdb35b2e32e..b37c0b44196 100644 --- a/packages/dev/core/src/Engines/Native/Extensions/nativeEngine.cubeTexture.pure.ts +++ b/packages/dev/core/src/Engines/Native/Extensions/nativeEngine.cubeTexture.pure.ts @@ -4,6 +4,7 @@ import { InternalTextureSource, InternalTexture } from "../../../Materials/Textu import { Texture } from "../../../Materials/Textures/texture.pure"; import { CreateRadianceImageDataArrayBufferViews, GetEnvInfo, UploadEnvSpherical } from "../../../Misc/environmentTextureTools.pure"; import { type IWebRequest } from "../../../Misc/interfaces/iWebRequest"; +import { SphericalPolynomial } from "../../../Maths/sphericalPolynomial"; import { type Scene } from "../../../scene.pure"; import { type Nullable } from "../../../types"; import { Constants } from "../../constants"; @@ -118,34 +119,94 @@ export function RegisterNativeEngineCubeTexture(): void { ); } } else { - if (!files || files.length !== 6) { - throw new Error("Cannot load cubemap because 6 files were not defined"); - } - - // Reorder from [+X, +Y, +Z, -X, -Y, -Z] to [+X, -X, +Y, -Y, +Z, -Z]. - const reorderedFiles = [files[0], files[3], files[1], files[4], files[2], files[5]]; - // eslint-disable-next-line github/no-then - Promise.all(reorderedFiles.map(async (file) => await this._loadFileAsync(file, undefined, true).then((data) => new Uint8Array(data, 0, data.byteLength)))) + if (files && files.length === 6) { + // Reorder from [+X, +Y, +Z, -X, -Y, -Z] to [+X, -X, +Y, -Y, +Z, -Z]. + const reorderedFiles = [files[0], files[3], files[1], files[4], files[2], files[5]]; // eslint-disable-next-line github/no-then - .then(async (data) => { - return await new Promise((resolve, reject) => { - this._engine.loadCubeTexture(texture._hardwareTexture!.underlyingResource, data, !noMipmap, true, texture._useSRGBBuffer, resolve, reject); - }); - }) - // eslint-disable-next-line github/no-then - .then( - () => { - texture.isReady = true; - if (onLoad) { - onLoad(); + Promise.all(reorderedFiles.map(async (file) => await this._loadFileAsync(file, undefined, true).then((data) => new Uint8Array(data, 0, data.byteLength)))) + // eslint-disable-next-line github/no-then + .then(async (data) => { + return await new Promise((resolve, reject) => { + this._engine.loadCubeTexture(texture._hardwareTexture!.underlyingResource, data, !noMipmap, true, texture._useSRGBBuffer, resolve, reject); + }); + }) + // eslint-disable-next-line github/no-then + .then( + () => { + texture.isReady = true; + if (onLoad) { + onLoad(); + } + }, + (error) => { + if (onError) { + onError(`Failed to load cubemap: ${error.message}`, error); + } } - }, - (error) => { - if (onError) { - onError(`Failed to load cubemap: ${error.message}`, error); + ); + } else if (files) { + throw new Error("Cannot load cubemap because 6 files were not defined"); + } else { + // Single self-contained cubemap container (.dds / .ktx / .ktx2) holding all six + // faces and their mip chain. The native engine decodes it with bimg and, when + // polynomials are requested, returns the spherical harmonics it computed from the + // top mip (native cannot read cube faces back from the GPU to derive them lazily). + const onContainerLoaded = (data: ArrayBufferView) => { + this._engine.loadCubeTexture( + texture._hardwareTexture!.underlyingResource, + [data], + !noMipmap, + true, + texture._useSRGBBuffer, + (sphericalPolynomialCoefficients?: Float32Array) => { + if (createPolynomials && sphericalPolynomialCoefficients && sphericalPolynomialCoefficients.length === 27) { + const c = sphericalPolynomialCoefficients; + texture._sphericalPolynomial = SphericalPolynomial.FromArray([ + [c[0], c[1], c[2]], + [c[3], c[4], c[5]], + [c[6], c[7], c[8]], + [c[9], c[10], c[11]], + [c[12], c[13], c[14]], + [c[15], c[16], c[17]], + [c[18], c[19], c[20]], + [c[21], c[22], c[23]], + [c[24], c[25], c[26]], + ]); + } + texture.isReady = true; + if (onLoad) { + onLoad(); + } + }, + () => { + if (onError) { + onError("Could not load a native cube texture."); + } } - } - ); + ); + }; + + if (buffer) { + onContainerLoaded(buffer); + } else { + const onInternalError = (request?: IWebRequest, exception?: any) => { + if (onError && request) { + onError(request.status + " " + request.statusText, exception); + } + }; + + this._loadFile( + rootUrl, + (data) => { + onContainerLoaded(new Uint8Array(data as ArrayBuffer, 0, (data as ArrayBuffer).byteLength)); + }, + undefined, + undefined, + true, + onInternalError + ); + } + } } this._internalTexturesCache.push(texture); diff --git a/packages/dev/core/src/Engines/Native/nativeInterfaces.ts b/packages/dev/core/src/Engines/Native/nativeInterfaces.ts index 90e077e9dec..59207947a2b 100644 --- a/packages/dev/core/src/Engines/Native/nativeInterfaces.ts +++ b/packages/dev/core/src/Engines/Native/nativeInterfaces.ts @@ -95,7 +95,15 @@ export interface INativeEngine { generateMipMaps: boolean, invertY: boolean ): void; - loadCubeTexture(texture: NativeTexture, data: Array, generateMips: boolean, invertY: boolean, srgb: boolean, onSuccess: () => void, onError: () => void): void; + loadCubeTexture( + texture: NativeTexture, + data: Array, + generateMips: boolean, + invertY: boolean, + srgb: boolean, + onSuccess: (sphericalPolynomial?: Float32Array) => void, + onError: () => void + ): void; loadCubeTextureWithMips(texture: NativeTexture, data: Array>, invertY: boolean, srgb: boolean, onSuccess: () => void, onError: () => void): void; getTextureWidth(texture: NativeTexture): number; getTextureHeight(texture: NativeTexture): number; @@ -456,21 +464,45 @@ export const enum NativeTraceLevel { /** @internal */ export interface INative { // NativeEngine plugin + /** + * + */ Engine: INativeEngineConstructor; + /** + * + */ NativeDataStream: INativeDataStreamConstructor; // NativeCamera plugin + /** + * + */ Camera?: INativeCameraConstructor; // NativeCanvas plugin + /** + * + */ Canvas?: INativeCanvasConstructor; + /** + * + */ Image?: INativeImageConstructor; + /** + * + */ Path2D?: INativePath2DConstructor; // Native XMLHttpRequest polyfill + /** + * + */ XMLHttpRequest?: typeof XMLHttpRequest; // NativeInput plugin + /** + * + */ DeviceInputSystem?: IDeviceInputSystemConstructor; // NativeTracing plugin From 4ea26398191ffbd89545512fcd93d19b8c8dc294 Mon Sep 17 00:00:00 2001 From: Branimir Karadzic Date: Wed, 10 Jun 2026 16:44:06 -0700 Subject: [PATCH 2/3] Fix native build: wrap loadCubeTexture resolve/reject for the SH-typed onSuccess The six-file path passed the Promise resolve directly, which is not assignable to the widened onSuccess signature (sphericalPolynomial?: Float32Array). Wrap with () => resolve() / () => reject(new Error(...)). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Native/Extensions/nativeEngine.cubeTexture.pure.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/dev/core/src/Engines/Native/Extensions/nativeEngine.cubeTexture.pure.ts b/packages/dev/core/src/Engines/Native/Extensions/nativeEngine.cubeTexture.pure.ts index b37c0b44196..9fa99a13fd1 100644 --- a/packages/dev/core/src/Engines/Native/Extensions/nativeEngine.cubeTexture.pure.ts +++ b/packages/dev/core/src/Engines/Native/Extensions/nativeEngine.cubeTexture.pure.ts @@ -127,7 +127,15 @@ export function RegisterNativeEngineCubeTexture(): void { // eslint-disable-next-line github/no-then .then(async (data) => { return await new Promise((resolve, reject) => { - this._engine.loadCubeTexture(texture._hardwareTexture!.underlyingResource, data, !noMipmap, true, texture._useSRGBBuffer, resolve, reject); + this._engine.loadCubeTexture( + texture._hardwareTexture!.underlyingResource, + data, + !noMipmap, + true, + texture._useSRGBBuffer, + () => resolve(), + () => reject(new Error("Failed to load native cubemap")) + ); }); }) // eslint-disable-next-line github/no-then From c746fdb739acd412d63dd7bd94c78882393ddf7e Mon Sep 17 00:00:00 2001 From: Branimir Karadzic Date: Thu, 11 Jun 2026 08:13:33 -0700 Subject: [PATCH 3/3] Strip eslint --fix doc-stub noise from INative (combined branch cleanup) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/Engines/Native/nativeInterfaces.ts | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/packages/dev/core/src/Engines/Native/nativeInterfaces.ts b/packages/dev/core/src/Engines/Native/nativeInterfaces.ts index 59207947a2b..b124a1cc7a9 100644 --- a/packages/dev/core/src/Engines/Native/nativeInterfaces.ts +++ b/packages/dev/core/src/Engines/Native/nativeInterfaces.ts @@ -464,45 +464,21 @@ export const enum NativeTraceLevel { /** @internal */ export interface INative { // NativeEngine plugin - /** - * - */ Engine: INativeEngineConstructor; - /** - * - */ NativeDataStream: INativeDataStreamConstructor; // NativeCamera plugin - /** - * - */ Camera?: INativeCameraConstructor; // NativeCanvas plugin - /** - * - */ Canvas?: INativeCanvasConstructor; - /** - * - */ Image?: INativeImageConstructor; - /** - * - */ Path2D?: INativePath2DConstructor; // Native XMLHttpRequest polyfill - /** - * - */ XMLHttpRequest?: typeof XMLHttpRequest; // NativeInput plugin - /** - * - */ DeviceInputSystem?: IDeviceInputSystemConstructor; // NativeTracing plugin