Skip to content

Commit 7627150

Browse files
bkaradzicCopilot
authored andcommitted
[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>
1 parent a736e85 commit 7627150

2 files changed

Lines changed: 119 additions & 26 deletions

File tree

packages/dev/core/src/Engines/Native/Extensions/nativeEngine.cubeTexture.pure.ts

Lines changed: 86 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { InternalTextureSource, InternalTexture } from "../../../Materials/Textu
44
import { Texture } from "../../../Materials/Textures/texture.pure";
55
import { CreateRadianceImageDataArrayBufferViews, GetEnvInfo, UploadEnvSpherical } from "../../../Misc/environmentTextureTools.pure";
66
import { type IWebRequest } from "../../../Misc/interfaces/iWebRequest";
7+
import { SphericalPolynomial } from "../../../Maths/sphericalPolynomial";
78
import { type Scene } from "../../../scene.pure";
89
import { type Nullable } from "../../../types";
910
import { Constants } from "../../constants";
@@ -118,34 +119,94 @@ export function RegisterNativeEngineCubeTexture(): void {
118119
);
119120
}
120121
} else {
121-
if (!files || files.length !== 6) {
122-
throw new Error("Cannot load cubemap because 6 files were not defined");
123-
}
124-
125-
// Reorder from [+X, +Y, +Z, -X, -Y, -Z] to [+X, -X, +Y, -Y, +Z, -Z].
126-
const reorderedFiles = [files[0], files[3], files[1], files[4], files[2], files[5]];
127-
// eslint-disable-next-line github/no-then
128-
Promise.all(reorderedFiles.map(async (file) => await this._loadFileAsync(file, undefined, true).then((data) => new Uint8Array(data, 0, data.byteLength))))
122+
if (files && files.length === 6) {
123+
// Reorder from [+X, +Y, +Z, -X, -Y, -Z] to [+X, -X, +Y, -Y, +Z, -Z].
124+
const reorderedFiles = [files[0], files[3], files[1], files[4], files[2], files[5]];
129125
// eslint-disable-next-line github/no-then
130-
.then(async (data) => {
131-
return await new Promise<void>((resolve, reject) => {
132-
this._engine.loadCubeTexture(texture._hardwareTexture!.underlyingResource, data, !noMipmap, true, texture._useSRGBBuffer, resolve, reject);
133-
});
134-
})
135-
// eslint-disable-next-line github/no-then
136-
.then(
137-
() => {
138-
texture.isReady = true;
139-
if (onLoad) {
140-
onLoad();
126+
Promise.all(reorderedFiles.map(async (file) => await this._loadFileAsync(file, undefined, true).then((data) => new Uint8Array(data, 0, data.byteLength))))
127+
// eslint-disable-next-line github/no-then
128+
.then(async (data) => {
129+
return await new Promise<void>((resolve, reject) => {
130+
this._engine.loadCubeTexture(texture._hardwareTexture!.underlyingResource, data, !noMipmap, true, texture._useSRGBBuffer, resolve, reject);
131+
});
132+
})
133+
// eslint-disable-next-line github/no-then
134+
.then(
135+
() => {
136+
texture.isReady = true;
137+
if (onLoad) {
138+
onLoad();
139+
}
140+
},
141+
(error) => {
142+
if (onError) {
143+
onError(`Failed to load cubemap: ${error.message}`, error);
144+
}
141145
}
142-
},
143-
(error) => {
144-
if (onError) {
145-
onError(`Failed to load cubemap: ${error.message}`, error);
146+
);
147+
} else if (files) {
148+
throw new Error("Cannot load cubemap because 6 files were not defined");
149+
} else {
150+
// Single self-contained cubemap container (.dds / .ktx / .ktx2) holding all six
151+
// faces and their mip chain. The native engine decodes it with bimg and, when
152+
// polynomials are requested, returns the spherical harmonics it computed from the
153+
// top mip (native cannot read cube faces back from the GPU to derive them lazily).
154+
const onContainerLoaded = (data: ArrayBufferView) => {
155+
this._engine.loadCubeTexture(
156+
texture._hardwareTexture!.underlyingResource,
157+
[data],
158+
!noMipmap,
159+
true,
160+
texture._useSRGBBuffer,
161+
(sphericalPolynomialCoefficients?: Float32Array) => {
162+
if (createPolynomials && sphericalPolynomialCoefficients && sphericalPolynomialCoefficients.length === 27) {
163+
const c = sphericalPolynomialCoefficients;
164+
texture._sphericalPolynomial = SphericalPolynomial.FromArray([
165+
[c[0], c[1], c[2]],
166+
[c[3], c[4], c[5]],
167+
[c[6], c[7], c[8]],
168+
[c[9], c[10], c[11]],
169+
[c[12], c[13], c[14]],
170+
[c[15], c[16], c[17]],
171+
[c[18], c[19], c[20]],
172+
[c[21], c[22], c[23]],
173+
[c[24], c[25], c[26]],
174+
]);
175+
}
176+
texture.isReady = true;
177+
if (onLoad) {
178+
onLoad();
179+
}
180+
},
181+
() => {
182+
if (onError) {
183+
onError("Could not load a native cube texture.");
184+
}
146185
}
147-
}
148-
);
186+
);
187+
};
188+
189+
if (buffer) {
190+
onContainerLoaded(buffer);
191+
} else {
192+
const onInternalError = (request?: IWebRequest, exception?: any) => {
193+
if (onError && request) {
194+
onError(request.status + " " + request.statusText, exception);
195+
}
196+
};
197+
198+
this._loadFile(
199+
rootUrl,
200+
(data) => {
201+
onContainerLoaded(new Uint8Array(data as ArrayBuffer, 0, (data as ArrayBuffer).byteLength));
202+
},
203+
undefined,
204+
undefined,
205+
true,
206+
onInternalError
207+
);
208+
}
209+
}
149210
}
150211

151212
this._internalTexturesCache.push(texture);

packages/dev/core/src/Engines/Native/nativeInterfaces.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,15 @@ export interface INativeEngine {
7474
generateMipMaps: boolean,
7575
invertY: boolean
7676
): void;
77-
loadCubeTexture(texture: NativeTexture, data: Array<ArrayBufferView>, generateMips: boolean, invertY: boolean, srgb: boolean, onSuccess: () => void, onError: () => void): void;
77+
loadCubeTexture(
78+
texture: NativeTexture,
79+
data: Array<ArrayBufferView>,
80+
generateMips: boolean,
81+
invertY: boolean,
82+
srgb: boolean,
83+
onSuccess: (sphericalPolynomial?: Float32Array) => void,
84+
onError: () => void
85+
): void;
7886
loadCubeTextureWithMips(texture: NativeTexture, data: Array<Array<ArrayBufferView>>, invertY: boolean, srgb: boolean, onSuccess: () => void, onError: () => void): void;
7987
getTextureWidth(texture: NativeTexture): number;
8088
getTextureHeight(texture: NativeTexture): number;
@@ -433,21 +441,45 @@ export const enum NativeTraceLevel {
433441
/** @internal */
434442
export interface INative {
435443
// NativeEngine plugin
444+
/**
445+
*
446+
*/
436447
Engine: INativeEngineConstructor;
448+
/**
449+
*
450+
*/
437451
NativeDataStream: INativeDataStreamConstructor;
438452

439453
// NativeCamera plugin
454+
/**
455+
*
456+
*/
440457
Camera?: INativeCameraConstructor;
441458

442459
// NativeCanvas plugin
460+
/**
461+
*
462+
*/
443463
Canvas?: INativeCanvasConstructor;
464+
/**
465+
*
466+
*/
444467
Image?: INativeImageConstructor;
468+
/**
469+
*
470+
*/
445471
Path2D?: INativePath2DConstructor;
446472

447473
// Native XMLHttpRequest polyfill
474+
/**
475+
*
476+
*/
448477
XMLHttpRequest?: typeof XMLHttpRequest;
449478

450479
// NativeInput plugin
480+
/**
481+
*
482+
*/
451483
DeviceInputSystem?: IDeviceInputSystemConstructor;
452484

453485
// NativeTracing plugin

0 commit comments

Comments
 (0)