@gnsx/three 0.186.3 → 0.186.4

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.
@@ -21614,6 +21614,284 @@ class VertexColorNode extends AttributeNode {
21614
21614
  */
21615
21615
  const vertexColor = ( index = 0 ) => new VertexColorNode( index );
21616
21616
 
21617
+ // WITH_GENESYS
21618
+ // Shader and lighting complexity debug views. Costs are a material proxy, not ISA instruction counts.
21619
+ // !WITH_GENESYS
21620
+
21621
+
21622
+ /**
21623
+ * Shaded output. No complexity debug view.
21624
+ *
21625
+ * @type {string}
21626
+ */
21627
+ const DEBUG_VIEW_NONE = 'none';
21628
+
21629
+ /**
21630
+ * Material-cost heatmap. Overlapping draws accumulate, then a fullscreen pass applies the ramp.
21631
+ *
21632
+ * @type {string}
21633
+ */
21634
+ const DEBUG_VIEW_SHADER_COMPLEXITY = 'shaderComplexity';
21635
+
21636
+ /**
21637
+ * Direct-light count heatmap. The visible surface wins.
21638
+ *
21639
+ * @type {string}
21640
+ */
21641
+ const DEBUG_VIEW_LIGHTING_COMPLEXITY = 'lightingComplexity';
21642
+
21643
+ /**
21644
+ * Proxy budget that fills the shader-complexity ramp.
21645
+ * A plain lit material stays in the green stops. Transmission plus several
21646
+ * texture samples climbs toward red.
21647
+ *
21648
+ * @type {number}
21649
+ */
21650
+ const DEFAULT_SHADER_COMPLEXITY_BUDGET = 800;
21651
+
21652
+ /** @type {number} */
21653
+ const SHADER_COMPLEXITY_BASE_UNLIT = 20;
21654
+
21655
+ /** @type {number} */
21656
+ const SHADER_COMPLEXITY_BASE_LIT = 40;
21657
+
21658
+ /** @type {number} */
21659
+ const SHADER_COMPLEXITY_TEXTURE_COST = 16;
21660
+
21661
+ /** @type {number} */
21662
+ const SHADER_COMPLEXITY_FEATURE_COST = 30;
21663
+
21664
+ /** @type {number} */
21665
+ const SHADER_COMPLEXITY_TRANSMISSION_COST = 60;
21666
+
21667
+ /**
21668
+ * Attenuation above this counts as a light that shades the pixel.
21669
+ *
21670
+ * @type {number}
21671
+ */
21672
+ const LIGHTING_COMPLEXITY_EPSILON = 0.001;
21673
+
21674
+ /**
21675
+ * Added on top of 1 when a received light casts a shadow.
21676
+ * Applied even where the shadow map is black.
21677
+ *
21678
+ * @type {number}
21679
+ */
21680
+ const LIGHTING_COMPLEXITY_SHADOW_WEIGHT = 0.5;
21681
+
21682
+ /**
21683
+ * Unreal `ShaderComplexityColors` from `BaseEngine.ini`.
21684
+ *
21685
+ * @type {Array<Array<number>>}
21686
+ */
21687
+ const SHADER_COMPLEXITY_COLORS = [
21688
+ [ 0.0, 1.0, 0.127 ],
21689
+ [ 0.0, 1.0, 0.0 ],
21690
+ [ 0.046, 0.52, 0.0 ],
21691
+ [ 0.215, 0.215, 0.0 ],
21692
+ [ 0.52, 0.046, 0.0 ],
21693
+ [ 0.7, 0.0, 0.0 ],
21694
+ [ 1.0, 0.0, 0.0 ],
21695
+ [ 1.0, 0.0, 0.5 ],
21696
+ [ 1.0, 0.9, 0.9 ]
21697
+ ];
21698
+
21699
+ /**
21700
+ * Unreal `LightComplexityColors` from `BaseEngine.ini`.
21701
+ *
21702
+ * @type {Array<Array<number>>}
21703
+ */
21704
+ const LIGHT_COMPLEXITY_COLORS = [
21705
+ [ 0.0, 0.0, 0.0 ],
21706
+ [ 0.0, 0.0, 0.4 ],
21707
+ [ 0.0, 0.3, 1.0 ],
21708
+ [ 0.0, 0.7, 0.4 ],
21709
+ [ 0.0, 1.0, 0.0 ],
21710
+ [ 0.8, 0.8, 0.0 ],
21711
+ [ 1.0, 0.3, 0.0 ],
21712
+ [ 0.7, 0.0, 0.0 ],
21713
+ [ 0.5, 0.0, 0.5 ],
21714
+ [ 0.7, 0.3, 0.7 ],
21715
+ [ 1.0, 0.9, 0.9 ]
21716
+ ];
21717
+
21718
+ const rgb = ( colors, index ) => vec3( colors[ index ][ 0 ], colors[ index ][ 1 ], colors[ index ][ 2 ] );
21719
+
21720
+ /**
21721
+ * Linear sample of a fixed RGB ramp. `cost` is in steps, and one step is one light.
21722
+ *
21723
+ * @param {Node<float>} cost - Complexity in ramp steps.
21724
+ * @param {Array<Array<number>>} colors - Ramp stops.
21725
+ * @return {Node<vec3>} The ramp color.
21726
+ */
21727
+ const colorizeLinear = ( cost, colors ) => {
21728
+
21729
+ const steps = colors.length - 1;
21730
+ const expanded = cost.div( steps ).clamp( 0, 0.999 ).mul( steps );
21731
+ const index = expanded.floor();
21732
+ const frac = fract( expanded );
21733
+
21734
+ let color = rgb( colors, 0 );
21735
+
21736
+ for ( let i = 0; i < steps; i ++ ) {
21737
+
21738
+ const sample = mix( rgb( colors, i ), rgb( colors, i + 1 ), frac );
21739
+
21740
+ color = index.greaterThanEqual( float( i ) ).and( index.lessThan( float( i + 1 ) ) ).select( sample, color );
21741
+
21742
+ }
21743
+
21744
+ return color;
21745
+
21746
+ };
21747
+
21748
+ /**
21749
+ * Maps a direct-light cost through Unreal's light-complexity ramp.
21750
+ * Ten lights saturate at white.
21751
+ *
21752
+ * @param {Node<float>} cost - Accumulated light weight.
21753
+ * @return {Node<vec3>} The ramp color.
21754
+ */
21755
+ const colorizeLightComplexity = ( cost ) => colorizeLinear( cost, LIGHT_COMPLEXITY_COLORS );
21756
+
21757
+ /**
21758
+ * Unreal's nonlinear shader-complexity ramp (`ColorizeComplexity`).
21759
+ * The first third of the normalized cost spreads across the first seven stops.
21760
+ *
21761
+ * @param {Node<float>} complexity - Accumulated cost divided by the budget.
21762
+ * @return {Node<vec3>} The ramp color.
21763
+ */
21764
+ const colorizeShaderComplexity = /*@__PURE__*/ Fn( ( [ complexity ] ) => {
21765
+
21766
+ const c = complexity.clamp( 0, 0.999 );
21767
+ const expanded = min$1( float( SHADER_COMPLEXITY_COLORS.length - 2 ), c.mul( 18 ) );
21768
+ const color = vec3( 0 ).toVar();
21769
+ const frac = fract( expanded );
21770
+
21771
+ If( expanded.lessThan( 1 ), () => {
21772
+
21773
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 0 ), rgb( SHADER_COMPLEXITY_COLORS, 1 ), frac ) );
21774
+
21775
+ } ).ElseIf( expanded.lessThan( 2 ), () => {
21776
+
21777
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 1 ), rgb( SHADER_COMPLEXITY_COLORS, 2 ), frac ) );
21778
+
21779
+ } ).ElseIf( expanded.lessThan( 3 ), () => {
21780
+
21781
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 2 ), rgb( SHADER_COMPLEXITY_COLORS, 3 ), frac ) );
21782
+
21783
+ } ).ElseIf( expanded.lessThan( 4 ), () => {
21784
+
21785
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 3 ), rgb( SHADER_COMPLEXITY_COLORS, 4 ), frac ) );
21786
+
21787
+ } ).ElseIf( expanded.lessThan( 5 ), () => {
21788
+
21789
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 4 ), rgb( SHADER_COMPLEXITY_COLORS, 5 ), frac ) );
21790
+
21791
+ } ).ElseIf( expanded.lessThan( 6 ), () => {
21792
+
21793
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 5 ), rgb( SHADER_COMPLEXITY_COLORS, 6 ), frac ) );
21794
+
21795
+ } ).Else( () => {
21796
+
21797
+ // Nine stops, so the upper range uses the `count > 8` scale of 3.
21798
+ const upper = c.sub( 0.33333333 ).mul( 3.0 );
21799
+ const upperFrac = fract( upper );
21800
+
21801
+ If( upper.lessThanEqual( 1 ), () => {
21802
+
21803
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 6 ), rgb( SHADER_COMPLEXITY_COLORS, 7 ), upperFrac ) );
21804
+
21805
+ } ).Else( () => {
21806
+
21807
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 7 ), rgb( SHADER_COMPLEXITY_COLORS, 8 ), upperFrac ) );
21808
+
21809
+ } );
21810
+
21811
+ } );
21812
+
21813
+ return color;
21814
+
21815
+ } );
21816
+
21817
+ /**
21818
+ * Reads the builder's shader-complexity proxy at code-generation time,
21819
+ * after the real material graph has been generated and texture samples counted.
21820
+ *
21821
+ * @augments Node
21822
+ */
21823
+ class ShaderComplexityRatioNode extends Node {
21824
+
21825
+ static get type() {
21826
+
21827
+ return 'ShaderComplexityRatioNode';
21828
+
21829
+ }
21830
+
21831
+ constructor() {
21832
+
21833
+ super( 'float' );
21834
+
21835
+ }
21836
+
21837
+ generate( builder ) {
21838
+
21839
+ const budget = builder.renderer.debug.shaderComplexityBudget;
21840
+ const safeBudget = ( typeof budget === 'number' && budget > 0 ) ? budget : 1;
21841
+ const ratio = builder.getShaderComplexityCost() / safeBudget;
21842
+
21843
+ return ratio.toFixed( 6 );
21844
+
21845
+ }
21846
+
21847
+ }
21848
+
21849
+ /**
21850
+ * Static fragment proxy for a material. Texture samples are counted later, during generation.
21851
+ *
21852
+ * @param {NodeMaterial} material - The material being built.
21853
+ * @return {number} The base cost, excluding texture samples.
21854
+ */
21855
+ function getShaderComplexityBase( material ) {
21856
+
21857
+ let cost = material.lights === true ? SHADER_COMPLEXITY_BASE_LIT : SHADER_COMPLEXITY_BASE_UNLIT;
21858
+
21859
+ if ( material.useClearcoat === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
21860
+
21861
+ if ( material.useSheen === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
21862
+
21863
+ if ( material.useIridescence === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
21864
+
21865
+ if ( material.useAnisotropy === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
21866
+
21867
+ if ( material.useTransmission === true ) cost += SHADER_COMPLEXITY_TRANSMISSION_COST;
21868
+
21869
+ return cost;
21870
+
21871
+ }
21872
+
21873
+ /**
21874
+ * Replaces the shaded color with `vec4(cost / budget, 0, 0, 1)`.
21875
+ * The real graph is still generated so texture samples are counted first.
21876
+ *
21877
+ * @param {Node} resultNode - The material's shaded output.
21878
+ * @return {Node<vec4>} The complexity ratio in the red channel.
21879
+ */
21880
+ function shaderComplexityOutput( resultNode ) {
21881
+
21882
+ // Generate the shaded graph first so texture samples are counted, then
21883
+ // replace the color. A bypass of a value node emits a bare expression
21884
+ // statement, which WGSL rejects.
21885
+ const shaded = vec4( resultNode ).toVar();
21886
+
21887
+ const ratio = new ShaderComplexityRatioNode();
21888
+
21889
+ return vec4( ratio, 0, 0, 1 ).add( shaded.mul( 0 ) );
21890
+
21891
+ }
21892
+
21893
+ // !WITH_GENESYS
21894
+
21617
21895
  /**
21618
21896
  * Base class for all node materials.
21619
21897
  *
@@ -22185,6 +22463,17 @@ class NodeMaterial extends Material {
22185
22463
 
22186
22464
  }
22187
22465
 
22466
+ // WITH_GENESYS
22467
+ // The screen output pass colorizes the accumulated ratio. Wrapping that pass
22468
+ // would replace the heatmap with this material's own constant cost.
22469
+ if ( renderer.debug.view === DEBUG_VIEW_SHADER_COMPLEXITY && this.isShadowPassMaterial !== true && renderer.isOutputTarget !== true ) {
22470
+
22471
+ builder.shaderComplexityBase = getShaderComplexityBase( this );
22472
+ resultNode = shaderComplexityOutput( resultNode );
22473
+
22474
+ }
22475
+ // !WITH_GENESYS
22476
+
22188
22477
  builder.stack.outputNode = resultNode;
22189
22478
 
22190
22479
  builder.addFlow( 'fragment', builder.removeStack() );
@@ -22698,6 +22987,22 @@ class NodeMaterial extends Material {
22698
22987
 
22699
22988
  }
22700
22989
 
22990
+ // WITH_GENESYS
22991
+ if ( builder.renderer.debug.view === DEBUG_VIEW_LIGHTING_COMPLEXITY && this.isShadowPassMaterial !== true ) {
22992
+
22993
+ const hasLightLoop = lightsNode && ( materialLightings.length > 0 || lightsNode.getScope().hasLights );
22994
+
22995
+ if ( hasLightLoop !== true ) {
22996
+
22997
+ outgoingLightNode = vec3( 0 );
22998
+
22999
+ }
23000
+
23001
+ return outgoingLightNode;
23002
+
23003
+ }
23004
+ // !WITH_GENESYS
23005
+
22701
23006
  // EMISSIVE
22702
23007
 
22703
23008
  if ( ( emissiveNode && emissiveNode.isNode === true ) || ( material.emissive && material.emissive.isColor === true ) ) {
@@ -31460,6 +31765,11 @@ class RenderObject {
31460
31765
 
31461
31766
  cacheKey = hash$1( cacheKey, this.renderer.contextNode.id, this.renderer.contextNode.version );
31462
31767
 
31768
+ // WITH_GENESYS
31769
+ // hash() xors each argument as a number, so a view name has to be hashed first.
31770
+ cacheKey = hash$1( cacheKey, hashString( this.renderer.debug.view ), this.renderer.debug.shaderComplexityBudget );
31771
+ // !WITH_GENESYS
31772
+
31463
31773
  return cacheKey;
31464
31774
 
31465
31775
  }
@@ -46355,6 +46665,10 @@ const totalSpecular = property( 'vec3', 'totalSpecular' );
46355
46665
  */
46356
46666
  const outgoingLight = property( 'vec3', 'outgoingLight' );
46357
46667
 
46668
+ // WITH_GENESYS
46669
+ const lightingComplexity = property( 'float', 'lightingComplexity' );
46670
+ // !WITH_GENESYS
46671
+
46358
46672
  /**
46359
46673
  * Sorts an array of lights in ascending order by their IDs.
46360
46674
  *
@@ -46708,6 +47022,10 @@ class LightsNode extends Node {
46708
47022
  reflectedLight
46709
47023
  }, builder );
46710
47024
 
47025
+ // WITH_GENESYS
47026
+ this._addLightingComplexity( builder, lightNode, lightData );
47027
+ // !WITH_GENESYS
47028
+
46711
47029
  }
46712
47030
 
46713
47031
  /**
@@ -46727,8 +47045,40 @@ class LightsNode extends Node {
46727
47045
  reflectedLight
46728
47046
  }, builder );
46729
47047
 
47048
+ // WITH_GENESYS
47049
+ this._addLightingComplexity( builder, lightNode, lightData );
47050
+ // !WITH_GENESYS
47051
+
46730
47052
  }
46731
47053
 
47054
+ // WITH_GENESYS
47055
+ /**
47056
+ * Adds this direct light's weight to the lighting-complexity accumulator.
47057
+ * Point, spot, and rect-area lights count only where attenuation is in range.
47058
+ * Directional lights have no falloff, so they always count. Shadow-casting
47059
+ * lights add extra weight even where the shadow map is black.
47060
+ *
47061
+ * @private
47062
+ * @param {NodeBuilder} builder - The current node builder.
47063
+ * @param {LightingNode} lightNode - The light node being shaded.
47064
+ * @param {Object} lightData - Direct light terms, including an optional attenuation node.
47065
+ */
47066
+ _addLightingComplexity( builder, lightNode, lightData ) {
47067
+
47068
+ if ( builder.renderer.debug.view !== DEBUG_VIEW_LIGHTING_COMPLEXITY ) return;
47069
+
47070
+ if ( builder.material && builder.material.isShadowPassMaterial === true ) return;
47071
+
47072
+ const attenuation = lightData.attenuation !== undefined ? lightData.attenuation : float( 1 );
47073
+ const light = lightNode.light;
47074
+ const castsShadow = light !== undefined && light !== null && light.castShadow === true && builder.object.receiveShadow === true;
47075
+ const weight = castsShadow ? float( 1 + LIGHTING_COMPLEXITY_SHADOW_WEIGHT ) : float( 1 );
47076
+
47077
+ lightingComplexity.addAssign( attenuation.greaterThan( LIGHTING_COMPLEXITY_EPSILON ).select( weight, float( 0 ) ) );
47078
+
47079
+ }
47080
+ // !WITH_GENESYS
47081
+
46732
47082
  /**
46733
47083
  * Setups the internal lights by building all respective
46734
47084
  * light nodes.
@@ -46791,6 +47141,14 @@ class LightsNode extends Node {
46791
47141
 
46792
47142
  properties.nodes = stack.nodes;
46793
47143
 
47144
+ // WITH_GENESYS
47145
+ if ( builder.renderer.debug.view === DEBUG_VIEW_LIGHTING_COMPLEXITY && ( builder.material === null || builder.material.isShadowPassMaterial !== true ) ) {
47146
+
47147
+ lightingComplexity.assign( 0 );
47148
+
47149
+ }
47150
+ // !WITH_GENESYS
47151
+
46794
47152
  lightingModel.start( builder );
46795
47153
 
46796
47154
  const { backdrop, backdropAlpha } = context;
@@ -46815,10 +47173,28 @@ class LightsNode extends Node {
46815
47173
  totalDiffuseNode.assign( totalDiffuse );
46816
47174
  totalSpecularNode.assign( directSpecular.add( indirectSpecular ) );
46817
47175
 
46818
- outgoingLightNode.assign( totalDiffuseNode.add( totalSpecularNode ) );
47176
+ // WITH_GENESYS
47177
+ const lightingComplexityView = builder.renderer.debug.view === DEBUG_VIEW_LIGHTING_COMPLEXITY && ( builder.material === null || builder.material.isShadowPassMaterial !== true );
47178
+
47179
+ if ( lightingComplexityView !== true ) {
47180
+
47181
+ outgoingLightNode.assign( totalDiffuseNode.add( totalSpecularNode ) );
47182
+
47183
+ }
47184
+ // !WITH_GENESYS
47185
+ // outgoingLightNode.assign( totalDiffuseNode.add( totalSpecularNode ) );
46819
47186
 
46820
47187
  lightingModel.finish( builder );
46821
47188
 
47189
+ // WITH_GENESYS
47190
+ // After finish(), so clearcoat and sheen cannot draw over the heatmap.
47191
+ if ( lightingComplexityView === true ) {
47192
+
47193
+ outgoingLightNode.assign( colorizeLightComplexity( lightingComplexity ) );
47194
+
47195
+ }
47196
+ // !WITH_GENESYS
47197
+
46822
47198
  outgoingLightNode = outgoingLightNode.bypass( builder.removeStack() );
46823
47199
 
46824
47200
  } else {
@@ -48910,7 +49286,10 @@ const directPointLight = ( { color, lightVector, cutoffDistance, decayExponent }
48910
49286
 
48911
49287
  const lightColor = color.mul( attenuation );
48912
49288
 
48913
- return { lightDirection, lightColor };
49289
+ // WITH_GENESYS
49290
+ return { lightDirection, lightColor, attenuation };
49291
+ // !WITH_GENESYS
49292
+ // return { lightDirection, lightColor };
48914
49293
 
48915
49294
  };
48916
49295
 
@@ -52025,7 +52404,15 @@ class Background extends DataMap {
52025
52404
 
52026
52405
  }
52027
52406
 
52028
- renderList.unshift( backgroundMesh, backgroundMesh.geometry, backgroundMesh.material, 0, 0, null, null );
52407
+ // WITH_GENESYS
52408
+ // The background is depth-tested off, so additive shader complexity would add it to every pixel.
52409
+ if ( renderer.debug.view !== 'shaderComplexity' ) {
52410
+
52411
+ renderList.unshift( backgroundMesh, backgroundMesh.geometry, backgroundMesh.material, 0, 0, null, null );
52412
+
52413
+ }
52414
+ // !WITH_GENESYS
52415
+ // renderList.unshift( backgroundMesh, backgroundMesh.geometry, backgroundMesh.material, 0, 0, null, null );
52029
52416
 
52030
52417
  } else {
52031
52418
 
@@ -52049,6 +52436,14 @@ class Background extends DataMap {
52049
52436
 
52050
52437
  //
52051
52438
 
52439
+ // WITH_GENESYS
52440
+ if ( renderer.debug.view === 'shaderComplexity' ) {
52441
+
52442
+ _clearColor.set( 0, 0, 0, 1 );
52443
+
52444
+ }
52445
+ // !WITH_GENESYS
52446
+
52052
52447
  if ( renderer.autoClear === true || forceClear === true ) {
52053
52448
 
52054
52449
  const clearColorValue = renderContext.clearColorValue;
@@ -53689,6 +54084,31 @@ class NodeBuilder {
53689
54084
  */
53690
54085
  this.fragmentShader = null;
53691
54086
 
54087
+ // WITH_GENESYS
54088
+ /**
54089
+ * Static material-feature cost for the shader-complexity debug view.
54090
+ *
54091
+ * @type {number}
54092
+ * @default 0
54093
+ */
54094
+ this.shaderComplexityBase = 0;
54095
+
54096
+ /**
54097
+ * Fragment texture samples counted while generating the shader.
54098
+ *
54099
+ * @type {number}
54100
+ * @default 0
54101
+ */
54102
+ this.fragmentTextureSamples = 0;
54103
+
54104
+ /**
54105
+ * Sample sites already counted, so a node that is generated twice is not double-counted.
54106
+ *
54107
+ * @type {Set<string>}
54108
+ */
54109
+ this.shaderComplexitySampleKeys = new Set();
54110
+ // !WITH_GENESYS
54111
+
53692
54112
  /**
53693
54113
  * The generated compute shader.
53694
54114
  *
@@ -54717,6 +55137,46 @@ class NodeBuilder {
54717
55137
 
54718
55138
  }
54719
55139
 
55140
+ // WITH_GENESYS
55141
+ /**
55142
+ * Counts one fragment texture sample toward the shader-complexity proxy.
55143
+ * Depth and cube textures are shadow maps and environment maps, and rect-area
55144
+ * LTC tables belong to the light loop, so they are not material cost.
55145
+ *
55146
+ * @param {?Texture} texture - The texture being sampled.
55147
+ * @param {string} [shaderStage=this.shaderStage] - The stage the sample is emitted for.
55148
+ * @param {string} [key=''] - Stable id for this sample site. Repeated generation of the same site counts once.
55149
+ */
55150
+ recordFragmentTextureSample( texture, shaderStage = this.shaderStage, key = '' ) {
55151
+
55152
+ if ( shaderStage !== 'fragment' ) return;
55153
+
55154
+ if ( texture && ( texture.isDepthTexture === true || texture.isCubeTexture === true || texture.isRectAreaLTC === true ) ) return;
55155
+
55156
+ if ( key !== '' ) {
55157
+
55158
+ if ( this.shaderComplexitySampleKeys.has( key ) ) return;
55159
+
55160
+ this.shaderComplexitySampleKeys.add( key );
55161
+
55162
+ }
55163
+
55164
+ this.fragmentTextureSamples ++;
55165
+
55166
+ }
55167
+
55168
+ /**
55169
+ * Returns the shader-complexity proxy for the material currently being built.
55170
+ *
55171
+ * @return {number} Base feature cost plus weighted fragment texture samples.
55172
+ */
55173
+ getShaderComplexityCost() {
55174
+
55175
+ return this.shaderComplexityBase + this.fragmentTextureSamples * SHADER_COMPLEXITY_TEXTURE_COST;
55176
+
55177
+ }
55178
+ // !WITH_GENESYS
55179
+
54720
55180
  /**
54721
55181
  * Generates a texture LOD shader string for the given texture data.
54722
55182
  *
@@ -56540,6 +57000,12 @@ class NodeBuilder {
56540
57000
  */
56541
57001
  prebuild() {
56542
57002
 
57003
+ // WITH_GENESYS
57004
+ this.shaderComplexityBase = 0;
57005
+ this.fragmentTextureSamples = 0;
57006
+ this.shaderComplexitySampleKeys.clear();
57007
+ // !WITH_GENESYS
57008
+
56543
57009
  const { renderer, material } = this;
56544
57010
 
56545
57011
  // < renderer.contextNode >
@@ -57564,7 +58030,12 @@ class SpotLightNode extends AnalyticLightNode {
57564
58030
  decayExponent: decayExponentNode
57565
58031
  } );
57566
58032
 
57567
- let lightColor = colorNode.mul( spotAttenuation ).mul( lightAttenuation );
58033
+ // WITH_GENESYS
58034
+ const attenuation = spotAttenuation.mul( lightAttenuation );
58035
+
58036
+ let lightColor = colorNode.mul( attenuation );
58037
+ // !WITH_GENESYS
58038
+ // let lightColor = colorNode.mul( spotAttenuation ).mul( lightAttenuation );
57568
58039
 
57569
58040
  let projected, lightCoord;
57570
58041
 
@@ -57588,7 +58059,10 @@ class SpotLightNode extends AnalyticLightNode {
57588
58059
 
57589
58060
  }
57590
58061
 
57591
- return { lightColor, lightDirection };
58062
+ // WITH_GENESYS
58063
+ return { lightColor, lightDirection, attenuation };
58064
+ // !WITH_GENESYS
58065
+ // return { lightColor, lightDirection };
57592
58066
 
57593
58067
  }
57594
58068
 
@@ -57904,6 +58378,20 @@ class RectAreaLightNode extends AnalyticLightNode {
57904
58378
 
57905
58379
  setupDirectRectArea( builder ) {
57906
58380
 
58381
+ // WITH_GENESYS
58382
+ // LTC lookups are part of the light loop, not the material's shader cost.
58383
+ if ( _ltcLib !== null ) {
58384
+
58385
+ _ltcLib.LTC_FLOAT_1.isRectAreaLTC = true;
58386
+ _ltcLib.LTC_FLOAT_2.isRectAreaLTC = true;
58387
+
58388
+ if ( _ltcLib.LTC_HALF_1 ) _ltcLib.LTC_HALF_1.isRectAreaLTC = true;
58389
+
58390
+ if ( _ltcLib.LTC_HALF_2 ) _ltcLib.LTC_HALF_2.isRectAreaLTC = true;
58391
+
58392
+ }
58393
+ // !WITH_GENESYS
58394
+
57907
58395
  let ltc_1, ltc_2;
57908
58396
 
57909
58397
  if ( builder.isAvailable( 'float32Filterable' ) ) {
@@ -59200,7 +59688,10 @@ class NodeManager extends DataMap {
59200
59688
 
59201
59689
  const renderer = this.renderer;
59202
59690
 
59203
- return renderer.toneMapping + ',' + renderer.currentColorSpace + ',' + renderer.xr.isPresenting;
59691
+ // WITH_GENESYS
59692
+ return renderer.toneMapping + ',' + renderer.currentColorSpace + ',' + renderer.xr.isPresenting + ',' + renderer.debug.view;
59693
+ // !WITH_GENESYS
59694
+ // return renderer.toneMapping + ',' + renderer.currentColorSpace + ',' + renderer.xr.isPresenting;
59204
59695
 
59205
59696
  }
59206
59697
 
@@ -59215,27 +59706,35 @@ class NodeManager extends DataMap {
59215
59706
 
59216
59707
  const renderer = this.renderer;
59217
59708
 
59218
- let output;
59709
+ let sampled;
59219
59710
 
59220
59711
  if ( outputTarget.isArrayTexture ) {
59221
59712
 
59222
59713
  if ( this.backend.isWebGLBackend ) {
59223
59714
 
59224
- output = texture( outputTarget, screenUV ).depth( builtin( 'gl_ViewID_OVR' ) ).renderOutput( renderer.toneMapping, renderer.currentColorSpace );
59715
+ sampled = texture( outputTarget, screenUV ).depth( builtin( 'gl_ViewID_OVR' ) );
59225
59716
 
59226
59717
  } else {
59227
59718
 
59228
- output = texture( outputTarget, screenUV ).depth( _outputLayerIndex ).renderOutput( renderer.toneMapping, renderer.currentColorSpace );
59719
+ sampled = texture( outputTarget, screenUV ).depth( _outputLayerIndex );
59229
59720
 
59230
59721
  }
59231
59722
 
59232
59723
  } else {
59233
59724
 
59234
- output = texture( outputTarget, screenUV ).renderOutput( renderer.toneMapping, renderer.currentColorSpace );
59725
+ sampled = texture( outputTarget, screenUV );
59235
59726
 
59236
59727
  }
59237
59728
 
59238
- return output;
59729
+ // WITH_GENESYS
59730
+ if ( renderer.debug.view === DEBUG_VIEW_SHADER_COMPLEXITY ) {
59731
+
59732
+ return vec4( colorizeShaderComplexity( sampled.r ), 1 ).renderOutput( NoToneMapping, renderer.currentColorSpace );
59733
+
59734
+ }
59735
+ // !WITH_GENESYS
59736
+
59737
+ return sampled.renderOutput( renderer.toneMapping, renderer.currentColorSpace );
59239
59738
 
59240
59739
  }
59241
59740
 
@@ -63193,6 +63692,8 @@ class Renderer {
63193
63692
  * @property {?Function} onNodeBuilderCreated - A callback function that is executed after a node builder has been created and before it is built.
63194
63693
  * @property {?Function} onShaderError - A callback function that is executed when a shader error happens. Only supported with WebGL 2 right now.
63195
63694
  * @property {Function} getShaderAsync - Allows the get the raw shader code for the given scene, camera and 3D object.
63695
+ * @property {string} view - Debug view. `shaderComplexity` and `lightingComplexity` replace the shaded color with a heatmap.
63696
+ * @property {number} shaderComplexityBudget - Proxy budget that fills the shader-complexity ramp.
63196
63697
  */
63197
63698
 
63198
63699
  /**
@@ -63207,6 +63708,10 @@ class Renderer {
63207
63708
  },
63208
63709
  onNodeBuilderCreated: null,
63209
63710
  onShaderError: null,
63711
+ // WITH_GENESYS
63712
+ view: DEBUG_VIEW_NONE,
63713
+ shaderComplexityBudget: DEFAULT_SHADER_COMPLEXITY_BUDGET,
63714
+ // !WITH_GENESYS
63210
63715
  getShaderAsync: async ( scene, camera, object ) => {
63211
63716
 
63212
63717
  await this.compileAsync( object, camera, scene );
@@ -65141,7 +65646,13 @@ class Renderer {
65141
65646
  const useToneMapping = this.currentToneMapping !== NoToneMapping;
65142
65647
  const useColorSpace = this.currentColorSpace !== ColorManagement.workingColorSpace;
65143
65648
 
65144
- return useToneMapping || useColorSpace;
65649
+ // WITH_GENESYS
65650
+ // Shader complexity writes a scalar into an intermediate target, then the output pass colorizes it.
65651
+ const shaderComplexity = this.debug.view === DEBUG_VIEW_SHADER_COMPLEXITY;
65652
+
65653
+ return useToneMapping || useColorSpace || shaderComplexity;
65654
+ // !WITH_GENESYS
65655
+ // return useToneMapping || useColorSpace;
65145
65656
 
65146
65657
  }
65147
65658
 
@@ -65192,6 +65703,16 @@ class Renderer {
65192
65703
  */
65193
65704
  get currentToneMapping() {
65194
65705
 
65706
+ // WITH_GENESYS
65707
+ const view = this.debug.view;
65708
+
65709
+ if ( view === DEBUG_VIEW_SHADER_COMPLEXITY || view === DEBUG_VIEW_LIGHTING_COMPLEXITY ) {
65710
+
65711
+ return NoToneMapping;
65712
+
65713
+ }
65714
+ // !WITH_GENESYS
65715
+
65195
65716
  return this.isOutputTarget ? this.toneMapping : NoToneMapping;
65196
65717
 
65197
65718
  }
@@ -68508,6 +69029,10 @@ ${ flowData.code }
68508
69029
  */
68509
69030
  generateTexture( texture, textureProperty, uvSnippet, depthSnippet, offsetSnippet ) {
68510
69031
 
69032
+ // WITH_GENESYS
69033
+ this.recordFragmentTextureSample( texture, this.shaderStage, textureProperty + '|' + uvSnippet );
69034
+ // !WITH_GENESYS
69035
+
68511
69036
  if ( depthSnippet ) uvSnippet = `vec3( ${ uvSnippet }, ${ depthSnippet } )`;
68512
69037
 
68513
69038
  if ( texture.isDepthTexture ) {
@@ -68551,6 +69076,10 @@ ${ flowData.code }
68551
69076
  */
68552
69077
  generateTextureLevel( texture, textureProperty, uvSnippet, levelSnippet, depthSnippet, offsetSnippet ) {
68553
69078
 
69079
+ // WITH_GENESYS
69080
+ this.recordFragmentTextureSample( texture, this.shaderStage, textureProperty + '|' + uvSnippet + '|' + levelSnippet );
69081
+ // !WITH_GENESYS
69082
+
68554
69083
  if ( depthSnippet ) uvSnippet = `vec3( ${ uvSnippet }, ${ depthSnippet } )`;
68555
69084
 
68556
69085
  if ( offsetSnippet ) {
@@ -71903,9 +72432,25 @@ class WebGLState {
71903
72432
 
71904
72433
  this.setFlipSided( flipSided );
71905
72434
 
71906
- ( material.blending === NormalBlending && material.transparent === false )
71907
- ? this.setBlending( NoBlending )
71908
- : this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.blendColor, material.blendAlpha, material.premultipliedAlpha );
72435
+ // WITH_GENESYS
72436
+ // WebGL has no cached render pipeline. One+One is applied on each draw instead.
72437
+ if ( this.backend.renderer.debug.view === 'shaderComplexity' && material.isShadowPassMaterial !== true && this.backend.renderer.isOutputTarget !== true ) {
72438
+
72439
+ this.setBlending( AdditiveBlending, AddEquation, OneFactor, OneFactor, AddEquation, OneFactor, OneFactor, material.blendColor, material.blendAlpha, true );
72440
+
72441
+ } else if ( material.blending === NormalBlending && material.transparent === false ) {
72442
+
72443
+ this.setBlending( NoBlending );
72444
+
72445
+ } else {
72446
+
72447
+ this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.blendColor, material.blendAlpha, material.premultipliedAlpha );
72448
+
72449
+ }
72450
+ // !WITH_GENESYS
72451
+ // ( material.blending === NormalBlending && material.transparent === false )
72452
+ // ? this.setBlending( NoBlending )
72453
+ // : this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.blendColor, material.blendAlpha, material.premultipliedAlpha );
71909
72454
 
71910
72455
  this.setDepthFunc( material.depthFunc );
71911
72456
  this.setDepthTest( material.depthTest );
@@ -82774,6 +83319,10 @@ class WGSLNodeBuilder extends NodeBuilder {
82774
83319
  */
82775
83320
  _generateTextureSample( texture, textureProperty, uvSnippet, depthSnippet, offsetSnippet, shaderStage = this.shaderStage ) {
82776
83321
 
83322
+ // WITH_GENESYS
83323
+ if ( shaderStage === 'fragment' ) this.recordFragmentTextureSample( texture, shaderStage, textureProperty + '|' + uvSnippet );
83324
+ // !WITH_GENESYS
83325
+
82777
83326
  if ( shaderStage === 'fragment' ) {
82778
83327
 
82779
83328
  if ( depthSnippet ) {
@@ -83054,6 +83603,10 @@ class WGSLNodeBuilder extends NodeBuilder {
83054
83603
  */
83055
83604
  generateFilteredTexture( texture, textureProperty, uvSnippet, offsetSnippet, levelSnippet = '0u', depthSnippet ) {
83056
83605
 
83606
+ // WITH_GENESYS
83607
+ this.recordFragmentTextureSample( texture, this.shaderStage, textureProperty + '|' + uvSnippet + '|' + levelSnippet );
83608
+ // !WITH_GENESYS
83609
+
83057
83610
  const wrapFunction = this.generateWrapFunction( texture );
83058
83611
  const textureDimension = this.generateTextureDimension( texture, textureProperty, levelSnippet );
83059
83612
 
@@ -83091,6 +83644,10 @@ class WGSLNodeBuilder extends NodeBuilder {
83091
83644
  */
83092
83645
  generateTextureLod( texture, textureProperty, uvSnippet, depthSnippet, offsetSnippet, levelSnippet = '0u' ) {
83093
83646
 
83647
+ // WITH_GENESYS
83648
+ this.recordFragmentTextureSample( texture, this.shaderStage, textureProperty + '|' + uvSnippet + '|' + levelSnippet );
83649
+ // !WITH_GENESYS
83650
+
83094
83651
  // Cube textures cannot use textureLoad in WGSL, must use textureSampleLevel
83095
83652
  if ( texture.isCubeTexture === true ) {
83096
83653
 
@@ -83469,6 +84026,10 @@ class WGSLNodeBuilder extends NodeBuilder {
83469
84026
 
83470
84027
  if ( this.isUnfilterable( texture ) === false ) {
83471
84028
 
84029
+ // WITH_GENESYS
84030
+ this.recordFragmentTextureSample( texture, this.shaderStage, textureProperty + '|' + uvSnippet + '|' + levelSnippet );
84031
+ // !WITH_GENESYS
84032
+
83472
84033
  if ( depthSnippet ) {
83473
84034
 
83474
84035
  if ( offsetSnippet ) {
@@ -86576,6 +87137,26 @@ class WebGPUPipelineUtils {
86576
87137
 
86577
87138
  }
86578
87139
 
87140
+ // WITH_GENESYS
87141
+ // Accumulate shader cost with One+One. Opaque materials normally disable blending.
87142
+ if ( backend.renderer.debug.view === 'shaderComplexity' && material.isShadowPassMaterial !== true && backend.renderer.isOutputTarget !== true ) {
87143
+
87144
+ materialBlending = {
87145
+ color: {
87146
+ srcFactor: GPUBlendFactor.One,
87147
+ dstFactor: GPUBlendFactor.One,
87148
+ operation: GPUBlendOperation.Add
87149
+ },
87150
+ alpha: {
87151
+ srcFactor: GPUBlendFactor.One,
87152
+ dstFactor: GPUBlendFactor.One,
87153
+ operation: GPUBlendOperation.Add
87154
+ }
87155
+ };
87156
+
87157
+ }
87158
+ // !WITH_GENESYS
87159
+
86579
87160
  // stencil
86580
87161
 
86581
87162
  let stencilFront = {};
@@ -90378,6 +90959,9 @@ class WebGPUBackend extends Backend {
90378
90959
  const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderObject.context );
90379
90960
  const primitiveTopology = utils.getPrimitiveTopology( object, material );
90380
90961
  const frontFaceCW = ( object.isMesh && object.matrixWorld.determinantAffine() < 0 );
90962
+ // WITH_GENESYS
90963
+ const debugView = renderObject.renderer.debug.view;
90964
+ // !WITH_GENESYS
90381
90965
 
90382
90966
  let needsUpdate = false;
90383
90967
 
@@ -90395,7 +90979,10 @@ class WebGPUBackend extends Backend {
90395
90979
  data.colorFormat !== colorFormat || data.depthStencilFormat !== depthStencilFormat ||
90396
90980
  data.primitiveTopology !== primitiveTopology ||
90397
90981
  data.frontFaceCW !== frontFaceCW ||
90398
- data.clippingContextCacheKey !== renderObject.clippingContextCacheKey
90982
+ data.clippingContextCacheKey !== renderObject.clippingContextCacheKey ||
90983
+ // WITH_GENESYS
90984
+ data.debugView !== debugView
90985
+ // !WITH_GENESYS
90399
90986
  ) {
90400
90987
 
90401
90988
  data.material = material; data.materialVersion = material.version;
@@ -90416,6 +91003,9 @@ class WebGPUBackend extends Backend {
90416
91003
  data.primitiveTopology = primitiveTopology;
90417
91004
  data.frontFaceCW = frontFaceCW;
90418
91005
  data.clippingContextCacheKey = renderObject.clippingContextCacheKey;
91006
+ // WITH_GENESYS
91007
+ data.debugView = debugView;
91008
+ // !WITH_GENESYS
90419
91009
 
90420
91010
  needsUpdate = true;
90421
91011
 
@@ -90459,7 +91049,10 @@ class WebGPUBackend extends Backend {
90459
91049
  utils.getCurrentColorSpace( renderContext ), utils.getCurrentColorFormat( renderContext ), utils.getCurrentDepthStencilFormat( renderContext ),
90460
91050
  utils.getPrimitiveTopology( object, material ),
90461
91051
  renderObject.getGeometryCacheKey(),
90462
- renderObject.clippingContextCacheKey
91052
+ renderObject.clippingContextCacheKey,
91053
+ // WITH_GENESYS
91054
+ renderObject.renderer.debug.view
91055
+ // !WITH_GENESYS
90463
91056
  ].join();
90464
91057
 
90465
91058
  }