rayzee 7.28.2 → 7.29.0
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.
- package/dist/rayzee.es.js +935 -902
- package/dist/rayzee.umd.js +20 -20
- package/package.json +1 -1
- package/src/Processor/SceneProcessor.js +1 -1
- package/src/Processor/TextureCreator.js +102 -5
package/package.json
CHANGED
|
@@ -836,7 +836,7 @@ export class SceneProcessor {
|
|
|
836
836
|
// (the prior model-load path omitted this, leaving albedo un-decoded / too bright).
|
|
837
837
|
const buildBucket = ( list, srgb ) => list.length === 0
|
|
838
838
|
? Promise.resolve( null )
|
|
839
|
-
: this.textureCreator.createTexturesToDataTexture( list ).then( tex => {
|
|
839
|
+
: this.textureCreator.createTexturesToDataTexture( list, { srgbPool: srgb } ).then( tex => {
|
|
840
840
|
|
|
841
841
|
if ( tex && srgb ) tex.colorSpace = SRGBColorSpace;
|
|
842
842
|
return tex;
|
|
@@ -1,7 +1,38 @@
|
|
|
1
|
-
import { DataArrayTexture, RGBAFormat, LinearFilter, UnsignedByteType, SRGBColorSpace, RepeatWrapping } from "three";
|
|
1
|
+
import { DataArrayTexture, RGBAFormat, LinearFilter, UnsignedByteType, SRGBColorSpace, LinearSRGBColorSpace, RepeatWrapping } from "three";
|
|
2
2
|
import { TEXTURE_CONSTANTS, MEMORY_CONSTANTS, DEFAULT_TEXTURE_MATRIX, MATERIAL_DATA_LAYOUT, normalizeAttenuationDistance } from '../EngineDefaults.js';
|
|
3
3
|
import TexturesWorker from './Workers/TexturesWorker.js?worker&inline';
|
|
4
4
|
import { ISSUE_CODES } from '../EngineIssues.js';
|
|
5
|
+
import { linearToSRGB } from './ToneMapCPU.js';
|
|
6
|
+
import { createLogger } from '../utils/Logger.js';
|
|
7
|
+
|
|
8
|
+
const log = createLogger( 'textures' );
|
|
9
|
+
|
|
10
|
+
// A bucket array carries ONE colorSpace and the shader reads each slot from a fixed pool, so a
|
|
11
|
+
// texture disagreeing with its pool cannot be re-routed — its pixels are re-encoded instead.
|
|
12
|
+
const TRANSFER_LUT = { toSRGB: null, toLinear: null };
|
|
13
|
+
|
|
14
|
+
function transferLUT( direction ) {
|
|
15
|
+
|
|
16
|
+
if ( ! TRANSFER_LUT[ direction ] ) {
|
|
17
|
+
|
|
18
|
+
const lut = new Uint8Array( 256 );
|
|
19
|
+
for ( let i = 0; i < 256; i ++ ) {
|
|
20
|
+
|
|
21
|
+
const c = i / 255;
|
|
22
|
+
const v = direction === 'toSRGB'
|
|
23
|
+
? linearToSRGB( c )
|
|
24
|
+
: ( c <= 0.04045 ? c / 12.92 : Math.pow( ( c + 0.055 ) / 1.055, 2.4 ) );
|
|
25
|
+
lut[ i ] = Math.max( 0, Math.min( 255, Math.round( v * 255 ) ) );
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
TRANSFER_LUT[ direction ] = lut;
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return TRANSFER_LUT[ direction ];
|
|
34
|
+
|
|
35
|
+
}
|
|
5
36
|
|
|
6
37
|
// Canvas pooling for efficient reuse of canvas elements
|
|
7
38
|
class CanvasPool {
|
|
@@ -541,13 +572,76 @@ export class TextureCreator {
|
|
|
541
572
|
|
|
542
573
|
}
|
|
543
574
|
|
|
575
|
+
/**
|
|
576
|
+
* Re-encodes any layer whose declared colour space disagrees with its pool, so the GPU's
|
|
577
|
+
* decode — or absence of one — lands on the values the author stored. An untagged texture
|
|
578
|
+
* keeps its pool's convention, which is what every loader produces for glTF.
|
|
579
|
+
*
|
|
580
|
+
* @param {import('three').DataArrayTexture} arrayTex
|
|
581
|
+
* @param {Array<import('three').Texture>} textures - pre-normalization list, in layer order
|
|
582
|
+
* @param {boolean} srgbPool - true when the array is tagged SRGBColorSpace
|
|
583
|
+
*/
|
|
584
|
+
_harmonizeTransfer( arrayTex, textures, srgbPool ) {
|
|
585
|
+
|
|
586
|
+
const data = arrayTex?.image?.data;
|
|
587
|
+
if ( ! data ) return;
|
|
588
|
+
|
|
589
|
+
// Mirrors _normalizeTexturesForProcessing's skip rule, which is what sets layer order.
|
|
590
|
+
const kept = textures.filter( t => t?.image );
|
|
591
|
+
const want = srgbPool ? SRGBColorSpace : LinearSRGBColorSpace;
|
|
592
|
+
const layerSize = arrayTex.image.width * arrayTex.image.height * 4;
|
|
593
|
+
const converted = [];
|
|
594
|
+
|
|
595
|
+
for ( let layer = 0; layer < kept.length; layer ++ ) {
|
|
596
|
+
|
|
597
|
+
const declared = kept[ layer ].colorSpace;
|
|
598
|
+
// Only an explicit disagreement is acted on; NoColorSpace defers to the pool.
|
|
599
|
+
if ( declared !== SRGBColorSpace && declared !== LinearSRGBColorSpace ) continue;
|
|
600
|
+
if ( declared === want ) continue;
|
|
601
|
+
|
|
602
|
+
const lut = transferLUT( srgbPool ? 'toSRGB' : 'toLinear' );
|
|
603
|
+
const start = layer * layerSize;
|
|
604
|
+
const end = Math.min( start + layerSize, data.length );
|
|
605
|
+
// RGB only — alpha is never transfer-encoded.
|
|
606
|
+
for ( let i = start; i < end; i += 4 ) {
|
|
607
|
+
|
|
608
|
+
data[ i ] = lut[ data[ i ] ];
|
|
609
|
+
data[ i + 1 ] = lut[ data[ i + 1 ] ];
|
|
610
|
+
data[ i + 2 ] = lut[ data[ i + 2 ] ];
|
|
611
|
+
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
converted.push( kept[ layer ].name || kept[ layer ].source?.uuid?.slice( 0, 8 ) || `layer ${layer}` );
|
|
615
|
+
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if ( converted.length === 0 ) return;
|
|
619
|
+
|
|
620
|
+
arrayTex.needsUpdate = true;
|
|
621
|
+
const names = converted.slice( 0, 4 ).join( ', ' ) + ( converted.length > 4 ? `, +${converted.length - 4} more` : '' );
|
|
622
|
+
|
|
623
|
+
if ( srgbPool ) {
|
|
624
|
+
|
|
625
|
+
log.info( `re-encoded ${converted.length}/${kept.length} linear-declared colour map(s) to sRGB so the GPU decode round-trips: ${names}` );
|
|
626
|
+
|
|
627
|
+
} else {
|
|
628
|
+
|
|
629
|
+
// Storing a decoded value in an 8-bit linear array is the lossy direction, and a
|
|
630
|
+
// transfer-encoded data map is almost always an authoring slip rather than intent.
|
|
631
|
+
log.warn( `decoded ${converted.length}/${kept.length} sRGB-declared data map(s) to linear — precision is lost in the darks, check the asset: ${names}` );
|
|
632
|
+
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
}
|
|
636
|
+
|
|
544
637
|
// Unified texture processing with strategy selection
|
|
545
|
-
async createTexturesToDataTexture( textures ) {
|
|
638
|
+
async createTexturesToDataTexture( textures, { srgbPool = false } = {} ) {
|
|
546
639
|
|
|
547
640
|
if ( ! textures || textures.length === 0 ) return null;
|
|
548
641
|
|
|
549
|
-
//
|
|
550
|
-
|
|
642
|
+
// Pool is part of the identity: the same list packed for the colour pool and the data pool
|
|
643
|
+
// gets different pixels (see _harmonizeTransfer), so one key must not serve both.
|
|
644
|
+
const cacheKey = `${srgbPool ? 's' : 'l'}:${this.textureCache.generateHash( textures )}`;
|
|
551
645
|
const cached = this.textureCache.get( cacheKey );
|
|
552
646
|
if ( cached ) return cached;
|
|
553
647
|
|
|
@@ -579,6 +673,7 @@ export class TextureCreator {
|
|
|
579
673
|
// Cache successful result
|
|
580
674
|
if ( result ) {
|
|
581
675
|
|
|
676
|
+
this._harmonizeTransfer( result, textures, srgbPool );
|
|
582
677
|
this.textureCache.set( cacheKey, result );
|
|
583
678
|
|
|
584
679
|
}
|
|
@@ -592,7 +687,9 @@ export class TextureCreator {
|
|
|
592
687
|
'worker texture processing failed — retrying on the main thread',
|
|
593
688
|
{ cause: String( error?.message ?? error ) }
|
|
594
689
|
);
|
|
595
|
-
|
|
690
|
+
const fallback = await this.processOnMainThreadSync( normalized );
|
|
691
|
+
this._harmonizeTransfer( fallback, textures, srgbPool );
|
|
692
|
+
return fallback;
|
|
596
693
|
|
|
597
694
|
} finally {
|
|
598
695
|
|