@babylonjs/core 7.47.3 → 7.48.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,8 +9,8 @@ import { Scene } from "../scene.js";
9
9
  import { PostProcess } from "../PostProcesses/postProcess.js";
10
10
  import { Logger } from "../Misc/logger.js";
11
11
  import { RGBDTextureTools } from "./rgbdTextureTools.js";
12
- import "../Materials/Textures/baseTexture.polynomial.js";
13
12
  import { DumpDataAsync } from "../Misc/dumpTools.js";
13
+ import "../Materials/Textures/baseTexture.polynomial.js";
14
14
  const DefaultEnvironmentTextureImageType = "image/png";
15
15
  const CurrentVersion = 2;
16
16
  /**
@@ -39,9 +39,9 @@ export function GetEnvInfo(data) {
39
39
  }
40
40
  let manifest = JSON.parse(manifestString);
41
41
  manifest = normalizeEnvInfo(manifest);
42
+ // Extend the header with the position of the payload.
43
+ manifest.binaryDataPosition = pos;
42
44
  if (manifest.specular) {
43
- // Extend the header with the position of the payload.
44
- manifest.specular.specularDataPosition = pos;
45
45
  // Fallback to 0.8 exactly if lodGenerationScale is not defined for backward compatibility.
46
46
  manifest.specular.lodGenerationScale = manifest.specular.lodGenerationScale || 0.8;
47
47
  }
@@ -77,7 +77,6 @@ export async function CreateEnvTextureAsync(texture, options = {}) {
77
77
  if (!internalTexture) {
78
78
  return Promise.reject("The cube texture is invalid.");
79
79
  }
80
- const imageType = options.imageType ?? DefaultEnvironmentTextureImageType;
81
80
  const engine = internalTexture.getEngine();
82
81
  if (texture.textureType !== 2 &&
83
82
  texture.textureType !== 1 &&
@@ -101,37 +100,26 @@ export async function CreateEnvTextureAsync(texture, options = {}) {
101
100
  const cubeWidth = internalTexture.width;
102
101
  const hostingScene = new Scene(engine);
103
102
  const specularTextures = {};
103
+ const diffuseTextures = {};
104
104
  // As we are going to readPixels the faces of the cube, make sure the drawing/update commands for the cube texture are fully sent to the GPU in case it is drawn for the first time in this very frame!
105
105
  engine.flushFramebuffer();
106
+ const imageType = options.imageType ?? DefaultEnvironmentTextureImageType;
106
107
  // Read and collect all mipmaps data from the cube.
107
108
  const mipmapsCount = ILog2(internalTexture.width);
108
109
  for (let i = 0; i <= mipmapsCount; i++) {
109
110
  const faceWidth = Math.pow(2, mipmapsCount - i);
110
111
  // All faces of the cube.
111
112
  for (let face = 0; face < 6; face++) {
112
- let faceData = await texture.readPixels(face, i, undefined, false);
113
- if (faceData && faceData.byteLength === faceData.length) {
114
- const faceDataFloat = new Float32Array(faceData.byteLength * 4);
115
- for (let i = 0; i < faceData.byteLength; i++) {
116
- faceDataFloat[i] = faceData[i] / 255;
117
- // Gamma to linear
118
- faceDataFloat[i] = Math.pow(faceDataFloat[i], 2.2);
119
- }
120
- faceData = faceDataFloat;
121
- }
122
- else if (faceData && texture.gammaSpace) {
123
- const floatData = faceData;
124
- for (let i = 0; i < floatData.length; i++) {
125
- // Gamma to linear
126
- floatData[i] = Math.pow(floatData[i], 2.2);
127
- }
128
- }
129
- const tempTexture = engine.createRawTexture(faceData, faceWidth, faceWidth, 5, false, true, 1, null, textureType);
130
- await RGBDTextureTools.EncodeTextureToRGBD(tempTexture, hostingScene, textureType);
131
- const rgbdEncodedData = await engine._readTexturePixels(tempTexture, faceWidth, faceWidth);
132
- const imageEncodedData = await DumpDataAsync(faceWidth, faceWidth, rgbdEncodedData, imageType, undefined, false, true, options.imageQuality);
133
- specularTextures[i * 6 + face] = imageEncodedData;
134
- tempTexture.dispose();
113
+ specularTextures[i * 6 + face] = await _getTextureEncodedData(hostingScene, texture, textureType, face, i, faceWidth, imageType, options.imageQuality);
114
+ }
115
+ }
116
+ // Read and collect all irradiance data from the cube.
117
+ const irradianceTexture = options.disableIrradianceTexture ? null : texture.irradianceTexture;
118
+ if (irradianceTexture) {
119
+ const faceWidth = irradianceTexture.getSize().width;
120
+ // All faces of the cube.
121
+ for (let face = 0; face < 6; face++) {
122
+ diffuseTextures[face] = await _getTextureEncodedData(hostingScene, irradianceTexture, textureType, face, 0, faceWidth, imageType, options.imageQuality);
135
123
  }
136
124
  }
137
125
  // We can delete the hosting scene keeping track of all the creation objects
@@ -163,6 +151,32 @@ export async function CreateEnvTextureAsync(texture, options = {}) {
163
151
  position += byteLength;
164
152
  }
165
153
  }
154
+ // Sets the irradiance image data information
155
+ if (irradianceTexture) {
156
+ info.irradiance = info.irradiance || {
157
+ x: [0, 0, 0],
158
+ xx: [0, 0, 0],
159
+ y: [0, 0, 0],
160
+ yy: [0, 0, 0],
161
+ z: [0, 0, 0],
162
+ zz: [0, 0, 0],
163
+ yz: [0, 0, 0],
164
+ zx: [0, 0, 0],
165
+ xy: [0, 0, 0],
166
+ };
167
+ info.irradiance.irradianceTexture = {
168
+ size: irradianceTexture.getSize().width,
169
+ faces: [],
170
+ };
171
+ for (let face = 0; face < 6; face++) {
172
+ const byteLength = diffuseTextures[face].byteLength;
173
+ info.irradiance.irradianceTexture.faces.push({
174
+ length: byteLength,
175
+ position: position,
176
+ });
177
+ position += byteLength;
178
+ }
179
+ }
166
180
  // Encode the JSON as an array buffer
167
181
  const infoString = JSON.stringify(info);
168
182
  const infoBuffer = new ArrayBuffer(infoString.length + 1);
@@ -185,7 +199,7 @@ export async function CreateEnvTextureAsync(texture, options = {}) {
185
199
  // Add the json info
186
200
  finalBufferView.set(new Uint8Array(infoBuffer), pos);
187
201
  pos += infoBuffer.byteLength;
188
- // Finally inserts the texture data
202
+ // Finally inserts the radiance texture data
189
203
  for (let i = 0; i <= mipmapsCount; i++) {
190
204
  for (let face = 0; face < 6; face++) {
191
205
  const dataBuffer = specularTextures[i * 6 + face];
@@ -193,9 +207,47 @@ export async function CreateEnvTextureAsync(texture, options = {}) {
193
207
  pos += dataBuffer.byteLength;
194
208
  }
195
209
  }
210
+ // Finally inserts the irradiance texture data
211
+ if (irradianceTexture) {
212
+ for (let face = 0; face < 6; face++) {
213
+ const dataBuffer = diffuseTextures[face];
214
+ finalBufferView.set(new Uint8Array(dataBuffer), pos);
215
+ pos += dataBuffer.byteLength;
216
+ }
217
+ }
196
218
  // Voila
197
219
  return finalBuffer;
198
220
  }
221
+ /**
222
+ * Get the texture encoded data from the current texture
223
+ * @internal
224
+ */
225
+ async function _getTextureEncodedData(hostingScene, texture, textureType, face, i, size, imageType, imageQuality) {
226
+ let faceData = await texture.readPixels(face, i, undefined, false);
227
+ if (faceData && faceData.byteLength === faceData.length) {
228
+ const faceDataFloat = new Float32Array(faceData.byteLength * 4);
229
+ for (let i = 0; i < faceData.byteLength; i++) {
230
+ faceDataFloat[i] = faceData[i] / 255;
231
+ // Gamma to linear
232
+ faceDataFloat[i] = Math.pow(faceDataFloat[i], 2.2);
233
+ }
234
+ faceData = faceDataFloat;
235
+ }
236
+ else if (faceData && texture.gammaSpace) {
237
+ const floatData = faceData;
238
+ for (let i = 0; i < floatData.length; i++) {
239
+ // Gamma to linear
240
+ floatData[i] = Math.pow(floatData[i], 2.2);
241
+ }
242
+ }
243
+ const engine = hostingScene.getEngine();
244
+ const tempTexture = engine.createRawTexture(faceData, size, size, 5, false, true, 1, null, textureType);
245
+ await RGBDTextureTools.EncodeTextureToRGBD(tempTexture, hostingScene, textureType);
246
+ const rgbdEncodedData = await engine._readTexturePixels(tempTexture, size, size);
247
+ const imageEncodedData = await DumpDataAsync(size, size, rgbdEncodedData, imageType, undefined, false, true, imageQuality);
248
+ tempTexture.dispose();
249
+ return imageEncodedData;
250
+ }
199
251
  /**
200
252
  * Creates a JSON representation of the spherical data.
201
253
  * @param texture defines the texture containing the polynomials
@@ -224,7 +276,7 @@ function _CreateEnvTextureIrradiance(texture) {
224
276
  * @param info parameters that determine what views will be created for accessing the underlying buffer
225
277
  * @returns the views described by info providing access to the underlying buffer
226
278
  */
227
- export function CreateImageDataArrayBufferViews(data, info) {
279
+ export function CreateRadianceImageDataArrayBufferViews(data, info) {
228
280
  info = normalizeEnvInfo(info);
229
281
  const specularInfo = info.specular;
230
282
  // Double checks the enclosed info
@@ -238,7 +290,28 @@ export function CreateImageDataArrayBufferViews(data, info) {
238
290
  imageData[i] = new Array(6);
239
291
  for (let face = 0; face < 6; face++) {
240
292
  const imageInfo = specularInfo.mipmaps[i * 6 + face];
241
- imageData[i][face] = new Uint8Array(data.buffer, data.byteOffset + specularInfo.specularDataPosition + imageInfo.position, imageInfo.length);
293
+ imageData[i][face] = new Uint8Array(data.buffer, data.byteOffset + info.binaryDataPosition + imageInfo.position, imageInfo.length);
294
+ }
295
+ }
296
+ return imageData;
297
+ }
298
+ /**
299
+ * Creates the ArrayBufferViews used for initializing environment texture image data.
300
+ * @param data the image data
301
+ * @param info parameters that determine what views will be created for accessing the underlying buffer
302
+ * @returns the views described by info providing access to the underlying buffer
303
+ */
304
+ export function CreateIrradianceImageDataArrayBufferViews(data, info) {
305
+ info = normalizeEnvInfo(info);
306
+ const imageData = new Array(6);
307
+ const irradianceTexture = info.irradiance?.irradianceTexture;
308
+ if (irradianceTexture) {
309
+ if (irradianceTexture.faces.length !== 6) {
310
+ throw new Error(`Incorrect irradiance texture faces number "${irradianceTexture.faces.length}"`);
311
+ }
312
+ for (let face = 0; face < 6; face++) {
313
+ const imageInfo = irradianceTexture.faces[face];
314
+ imageData[face] = new Uint8Array(data.buffer, data.byteOffset + info.binaryDataPosition + imageInfo.position, imageInfo.length);
242
315
  }
243
316
  }
244
317
  return imageData;
@@ -255,11 +328,18 @@ export function UploadEnvLevelsAsync(texture, data, info) {
255
328
  const specularInfo = info.specular;
256
329
  if (!specularInfo) {
257
330
  // Nothing else parsed so far
258
- return Promise.resolve();
331
+ return Promise.resolve([]);
259
332
  }
260
333
  texture._lodGenerationScale = specularInfo.lodGenerationScale;
261
- const imageData = CreateImageDataArrayBufferViews(data, info);
262
- return UploadLevelsAsync(texture, imageData, info.imageType);
334
+ const promises = [];
335
+ const radianceImageData = CreateRadianceImageDataArrayBufferViews(data, info);
336
+ promises.push(UploadRadianceLevelsAsync(texture, radianceImageData, info.imageType));
337
+ const irradianceTexture = info.irradiance?.irradianceTexture;
338
+ if (irradianceTexture) {
339
+ const irradianceImageData = CreateIrradianceImageDataArrayBufferViews(data, info);
340
+ promises.push(UploadIrradianceLevelsAsync(texture, irradianceImageData, irradianceTexture.size, info.imageType));
341
+ }
342
+ return Promise.all(promises);
263
343
  }
264
344
  function _OnImageReadyAsync(image, engine, expandTexture, rgbdPostProcess, url, face, i, generateNonLODTextures, lodTextures, cubeRtt, texture) {
265
345
  return new Promise((resolve, reject) => {
@@ -307,7 +387,54 @@ function _OnImageReadyAsync(image, engine, expandTexture, rgbdPostProcess, url,
307
387
  * @param imageType the mime type of the image data
308
388
  * @returns a promise
309
389
  */
310
- export async function UploadLevelsAsync(texture, imageData, imageType = DefaultEnvironmentTextureImageType) {
390
+ export async function UploadRadianceLevelsAsync(texture, imageData, imageType = DefaultEnvironmentTextureImageType) {
391
+ const engine = texture.getEngine();
392
+ texture.format = 5;
393
+ texture.type = 0;
394
+ texture.generateMipMaps = true;
395
+ texture._cachedAnisotropicFilteringLevel = null;
396
+ engine.updateTextureSamplingMode(3, texture);
397
+ await _UploadLevelsAsync(texture, imageData, true, imageType);
398
+ // Flag internal texture as ready in case they are in use.
399
+ texture.isReady = true;
400
+ }
401
+ /**
402
+ * Uploads the levels of image data to the GPU.
403
+ * @param mainTexture defines the internal texture to upload to
404
+ * @param imageData defines the array buffer views of image data [mipmap][face]
405
+ * @param size defines the size of the texture faces
406
+ * @param imageType the mime type of the image data
407
+ * @returns a promise
408
+ */
409
+ export async function UploadIrradianceLevelsAsync(mainTexture, imageData, size, imageType = DefaultEnvironmentTextureImageType) {
410
+ // Gets everything ready.
411
+ const engine = mainTexture.getEngine();
412
+ const texture = new InternalTexture(engine, 5 /* InternalTextureSource.RenderTarget */);
413
+ const baseTexture = new BaseTexture(engine, texture);
414
+ mainTexture._irradianceTexture = baseTexture;
415
+ texture.isCube = true;
416
+ texture.format = 5;
417
+ texture.type = 0;
418
+ texture.generateMipMaps = true;
419
+ texture._cachedAnisotropicFilteringLevel = null;
420
+ texture.generateMipMaps = true;
421
+ texture.width = size;
422
+ texture.height = size;
423
+ engine.updateTextureSamplingMode(3, texture);
424
+ await _UploadLevelsAsync(texture, [imageData], false, imageType);
425
+ engine.generateMipMapsForCubemap(texture);
426
+ // Flag internal texture as ready in case they are in use.
427
+ texture.isReady = true;
428
+ }
429
+ /**
430
+ * Uploads the levels of image data to the GPU.
431
+ * @param texture defines the internal texture to upload to
432
+ * @param imageData defines the array buffer views of image data [mipmap][face]
433
+ * @param canGenerateNonLODTextures defines whether or not to generate non lod textures
434
+ * @param imageType the mime type of the image data
435
+ * @returns a promise
436
+ */
437
+ async function _UploadLevelsAsync(texture, imageData, canGenerateNonLODTextures, imageType = DefaultEnvironmentTextureImageType) {
311
438
  if (!Tools.IsExponentOfTwo(texture.width)) {
312
439
  throw new Error("Texture size must be a power of two");
313
440
  }
@@ -320,18 +447,10 @@ export async function UploadLevelsAsync(texture, imageData, imageType = DefaultE
320
447
  let cubeRtt = null;
321
448
  let lodTextures = null;
322
449
  const caps = engine.getCaps();
323
- texture.format = 5;
324
- texture.type = 0;
325
- texture.generateMipMaps = true;
326
- texture._cachedAnisotropicFilteringLevel = null;
327
- engine.updateTextureSamplingMode(3, texture);
328
- // Add extra process if texture lod is not supported
329
450
  if (!caps.textureLOD) {
330
451
  expandTexture = false;
331
- generateNonLODTextures = true;
332
- lodTextures = {};
452
+ generateNonLODTextures = canGenerateNonLODTextures;
333
453
  }
334
- // in webgl 1 there are no ways to either render or copy lod level information for float textures.
335
454
  else if (!engine._features.supportRenderAndCopyToLodForFloatTextures) {
336
455
  expandTexture = false;
337
456
  }
@@ -374,6 +493,7 @@ export async function UploadLevelsAsync(texture, imageData, imageType = DefaultE
374
493
  // In case of missing support, applies the same patch than DDS files.
375
494
  if (generateNonLODTextures) {
376
495
  const mipSlices = 3;
496
+ lodTextures = {};
377
497
  const scale = texture._lodGenerationScale;
378
498
  const offset = texture._lodGenerationOffset;
379
499
  for (let i = 0; i < mipSlices; i++) {
@@ -384,6 +504,7 @@ export async function UploadLevelsAsync(texture, imageData, imageType = DefaultE
384
504
  const maxLODIndex = (mipmapsCount - 1) * scale + offset; // roughness = 1 (mipmaps start from 0)
385
505
  const lodIndex = minLODIndex + (maxLODIndex - minLODIndex) * roughness;
386
506
  const mipmapIndex = Math.round(Math.min(Math.max(lodIndex, 0), maxLODIndex));
507
+ //compute LOD from even spacing in smoothness (matching shader calculation)
387
508
  const glTextureFromLod = new InternalTexture(engine, 2 /* InternalTextureSource.Temp */);
388
509
  glTextureFromLod.isCube = true;
389
510
  glTextureFromLod.invertY = true;
@@ -468,30 +589,31 @@ export async function UploadLevelsAsync(texture, imageData, imageType = DefaultE
468
589
  }
469
590
  }
470
591
  }
471
- // Once all done, finishes the cleanup and return
472
- return Promise.all(promises).then(() => {
473
- // Release temp RTT.
474
- if (cubeRtt) {
475
- engine._releaseTexture(texture);
476
- cubeRtt._swapAndDie(texture);
592
+ await Promise.all(promises);
593
+ // Release temp RTT.
594
+ if (cubeRtt) {
595
+ const irradiance = texture._irradianceTexture;
596
+ texture._irradianceTexture = null;
597
+ engine._releaseTexture(texture);
598
+ cubeRtt._swapAndDie(texture);
599
+ texture._irradianceTexture = irradiance;
600
+ }
601
+ // Release temp Post Process.
602
+ if (rgbdPostProcess) {
603
+ rgbdPostProcess.dispose();
604
+ }
605
+ // Flag internal texture as ready in case they are in use.
606
+ if (generateNonLODTextures) {
607
+ if (texture._lodTextureHigh && texture._lodTextureHigh._texture) {
608
+ texture._lodTextureHigh._texture.isReady = true;
477
609
  }
478
- // Release temp Post Process.
479
- if (rgbdPostProcess) {
480
- rgbdPostProcess.dispose();
610
+ if (texture._lodTextureMid && texture._lodTextureMid._texture) {
611
+ texture._lodTextureMid._texture.isReady = true;
481
612
  }
482
- // Flag internal texture as ready in case they are in use.
483
- if (generateNonLODTextures) {
484
- if (texture._lodTextureHigh && texture._lodTextureHigh._texture) {
485
- texture._lodTextureHigh._texture.isReady = true;
486
- }
487
- if (texture._lodTextureMid && texture._lodTextureMid._texture) {
488
- texture._lodTextureMid._texture.isReady = true;
489
- }
490
- if (texture._lodTextureLow && texture._lodTextureLow._texture) {
491
- texture._lodTextureLow._texture.isReady = true;
492
- }
613
+ if (texture._lodTextureLow && texture._lodTextureLow._texture) {
614
+ texture._lodTextureLow._texture.isReady = true;
493
615
  }
494
- });
616
+ }
495
617
  }
496
618
  /**
497
619
  * Uploads spherical polynomials information to the texture.
@@ -523,7 +645,7 @@ export function _UpdateRGBDAsync(internalTexture, data, sphericalPolynomial, lod
523
645
  const proxy = internalTexture
524
646
  .getEngine()
525
647
  .createRawCubeTexture(null, internalTexture.width, internalTexture.format, internalTexture.type, internalTexture.generateMipMaps, internalTexture.invertY, internalTexture.samplingMode, internalTexture._compression);
526
- const proxyPromise = UploadLevelsAsync(proxy, data).then(() => internalTexture);
648
+ const proxyPromise = UploadRadianceLevelsAsync(proxy, data).then(() => internalTexture);
527
649
  internalTexture.onRebuildCallback = (_internalTexture) => {
528
650
  return {
529
651
  proxy: proxyPromise,
@@ -536,7 +658,7 @@ export function _UpdateRGBDAsync(internalTexture, data, sphericalPolynomial, lod
536
658
  internalTexture._lodGenerationScale = lodScale;
537
659
  internalTexture._lodGenerationOffset = lodOffset;
538
660
  internalTexture._sphericalPolynomial = sphericalPolynomial;
539
- return UploadLevelsAsync(internalTexture, data).then(() => {
661
+ return UploadRadianceLevelsAsync(internalTexture, data).then(() => {
540
662
  internalTexture.isReady = true;
541
663
  return internalTexture;
542
664
  });
@@ -568,7 +690,14 @@ export const EnvironmentTextureTools = {
568
690
  * @param info parameters that determine what views will be created for accessing the underlying buffer
569
691
  * @returns the views described by info providing access to the underlying buffer
570
692
  */
571
- CreateImageDataArrayBufferViews,
693
+ CreateRadianceImageDataArrayBufferViews,
694
+ /**
695
+ * Creates the ArrayBufferViews used for initializing environment texture image data.
696
+ * @param data the image data
697
+ * @param info parameters that determine what views will be created for accessing the underlying buffer
698
+ * @returns the views described by info providing access to the underlying buffer
699
+ */
700
+ CreateIrradianceImageDataArrayBufferViews,
572
701
  /**
573
702
  * Uploads the texture info contained in the env file to the GPU.
574
703
  * @param texture defines the internal texture to upload to
@@ -584,7 +713,15 @@ export const EnvironmentTextureTools = {
584
713
  * @param imageType the mime type of the image data
585
714
  * @returns a promise
586
715
  */
587
- UploadLevelsAsync,
716
+ UploadRadianceLevelsAsync,
717
+ /**
718
+ * Uploads the levels of image data to the GPU.
719
+ * @param texture defines the internal texture to upload to
720
+ * @param imageData defines the array buffer views of image data [mipmap][face]
721
+ * @param imageType the mime type of the image data
722
+ * @returns a promise
723
+ */
724
+ UploadIrradianceLevelsAsync,
588
725
  /**
589
726
  * Uploads spherical polynomials information to the texture.
590
727
  * @param texture defines the texture we are trying to upload the information to