@gnsx/three 0.186.10 → 0.186.11

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.
@@ -21848,7 +21848,7 @@ const LIGHTING_COMPLEXITY_SHADOW_WEIGHT = 0.5;
21848
21848
  *
21849
21849
  * @type {Array<Array<number>>}
21850
21850
  */
21851
- const SHADER_COMPLEXITY_COLORS = [
21851
+ const SHADER_COMPLEXITY_COLORS$1 = [
21852
21852
  [ 0.0, 1.0, 0.127 ],
21853
21853
  [ 0.0, 1.0, 0.0 ],
21854
21854
  [ 0.046, 0.52, 0.0 ],
@@ -21885,7 +21885,7 @@ const QUAD_COMPLEXITY_COLORS = [
21885
21885
  *
21886
21886
  * @type {Array<Array<number>>}
21887
21887
  */
21888
- const LIGHT_COMPLEXITY_COLORS = [
21888
+ const LIGHT_COMPLEXITY_COLORS$1 = [
21889
21889
  [ 0.0, 0.0, 0.0 ],
21890
21890
  [ 0.0, 0.0, 0.4 ],
21891
21891
  [ 0.0, 0.3, 1.0 ],
@@ -21936,7 +21936,7 @@ const colorizeLinear = ( cost, colors ) => {
21936
21936
  * @param {Node<float>} cost - Accumulated light weight.
21937
21937
  * @return {Node<vec3>} The ramp color.
21938
21938
  */
21939
- const colorizeLightComplexity = ( cost ) => colorizeLinear( cost, LIGHT_COMPLEXITY_COLORS );
21939
+ const colorizeLightComplexity = ( cost ) => colorizeLinear( cost, LIGHT_COMPLEXITY_COLORS$1 );
21940
21940
 
21941
21941
  /**
21942
21942
  * Unreal's stair sample for quad overdraw (`CS_STAIR`).
@@ -22031,33 +22031,33 @@ function debugViewSkipsToneMapping( view ) {
22031
22031
  const colorizeShaderComplexity = /*@__PURE__*/ Fn( ( [ complexity ] ) => {
22032
22032
 
22033
22033
  const c = complexity.clamp( 0, 0.999 );
22034
- const expanded = min$1( float( SHADER_COMPLEXITY_COLORS.length - 2 ), c.mul( 18 ) );
22034
+ const expanded = min$1( float( SHADER_COMPLEXITY_COLORS$1.length - 2 ), c.mul( 18 ) );
22035
22035
  const color = vec3( 0 ).toVar();
22036
22036
  const frac = fract( expanded );
22037
22037
 
22038
22038
  If( expanded.lessThan( 1 ), () => {
22039
22039
 
22040
- color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 0 ), rgb( SHADER_COMPLEXITY_COLORS, 1 ), frac ) );
22040
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS$1, 0 ), rgb( SHADER_COMPLEXITY_COLORS$1, 1 ), frac ) );
22041
22041
 
22042
22042
  } ).ElseIf( expanded.lessThan( 2 ), () => {
22043
22043
 
22044
- color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 1 ), rgb( SHADER_COMPLEXITY_COLORS, 2 ), frac ) );
22044
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS$1, 1 ), rgb( SHADER_COMPLEXITY_COLORS$1, 2 ), frac ) );
22045
22045
 
22046
22046
  } ).ElseIf( expanded.lessThan( 3 ), () => {
22047
22047
 
22048
- color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 2 ), rgb( SHADER_COMPLEXITY_COLORS, 3 ), frac ) );
22048
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS$1, 2 ), rgb( SHADER_COMPLEXITY_COLORS$1, 3 ), frac ) );
22049
22049
 
22050
22050
  } ).ElseIf( expanded.lessThan( 4 ), () => {
22051
22051
 
22052
- color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 3 ), rgb( SHADER_COMPLEXITY_COLORS, 4 ), frac ) );
22052
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS$1, 3 ), rgb( SHADER_COMPLEXITY_COLORS$1, 4 ), frac ) );
22053
22053
 
22054
22054
  } ).ElseIf( expanded.lessThan( 5 ), () => {
22055
22055
 
22056
- color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 4 ), rgb( SHADER_COMPLEXITY_COLORS, 5 ), frac ) );
22056
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS$1, 4 ), rgb( SHADER_COMPLEXITY_COLORS$1, 5 ), frac ) );
22057
22057
 
22058
22058
  } ).ElseIf( expanded.lessThan( 6 ), () => {
22059
22059
 
22060
- color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 5 ), rgb( SHADER_COMPLEXITY_COLORS, 6 ), frac ) );
22060
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS$1, 5 ), rgb( SHADER_COMPLEXITY_COLORS$1, 6 ), frac ) );
22061
22061
 
22062
22062
  } ).Else( () => {
22063
22063
 
@@ -22067,11 +22067,11 @@ const colorizeShaderComplexity = /*@__PURE__*/ Fn( ( [ complexity ] ) => {
22067
22067
 
22068
22068
  If( upper.lessThanEqual( 1 ), () => {
22069
22069
 
22070
- color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 6 ), rgb( SHADER_COMPLEXITY_COLORS, 7 ), upperFrac ) );
22070
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS$1, 6 ), rgb( SHADER_COMPLEXITY_COLORS$1, 7 ), upperFrac ) );
22071
22071
 
22072
22072
  } ).Else( () => {
22073
22073
 
22074
- color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 7 ), rgb( SHADER_COMPLEXITY_COLORS, 8 ), upperFrac ) );
22074
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS$1, 7 ), rgb( SHADER_COMPLEXITY_COLORS$1, 8 ), upperFrac ) );
22075
22075
 
22076
22076
  } );
22077
22077
 
@@ -23128,6 +23128,165 @@ function applyShadowCasterDebug( builder, material ) {
23128
23128
 
23129
23129
  }
23130
23130
 
23131
+ // WITH_GENESYS
23132
+ // LOD coloration. Each drawn LOD tier gets a fixed color from the palette, then lit.
23133
+ // !WITH_GENESYS
23134
+
23135
+
23136
+ /**
23137
+ * LOD coloration. Colors geometry by its active LOD index, then lights that color.
23138
+ * Matches Unreal `LODColorationColors` from `BaseEngine.ini`.
23139
+ *
23140
+ * @type {string}
23141
+ */
23142
+ const DEBUG_VIEW_LOD_COLORATION = 'lodColoration';
23143
+
23144
+ /**
23145
+ * Default palette when `renderer.debug.lodColorationColors` is not set.
23146
+ * Unreal: white, red, green, blue, yellow, fuchsia, cyan, purple.
23147
+ *
23148
+ * @type {Color[]}
23149
+ */
23150
+ const DEFAULT_LOD_COLORATION_COLORS = [
23151
+ new Color( 1, 1, 1 ),
23152
+ new Color( 1, 0, 0 ),
23153
+ new Color( 0, 1, 0 ),
23154
+ new Color( 0, 0, 1 ),
23155
+ new Color( 1, 1, 0 ),
23156
+ new Color( 1, 0, 1 ),
23157
+ new Color( 0, 1, 1 ),
23158
+ new Color( 0.5, 0, 0.5 ),
23159
+ ];
23160
+
23161
+ const INVALID_LOD_COLOR = new Color( 1, 0, 1 );
23162
+
23163
+ /**
23164
+ * Resolves the LOD index used for coloration on a drawn object.
23165
+ * `userData.lodColorationIndex` on the object or an ancestor wins (Genesys instanced LOD tiers).
23166
+ * Otherwise the nearest `THREE.LOD` parent is consulted: the level object that owns the draw,
23167
+ * or the parent's current level as a fallback.
23168
+ *
23169
+ * @param {?Object3D} object - The drawn object.
23170
+ * @return {number} LOD index, or `-1` when no palette entry should be used.
23171
+ */
23172
+ function resolveLODColorationIndex( object ) {
23173
+
23174
+ if ( object === null ) return -1;
23175
+
23176
+ let node = object;
23177
+
23178
+ while ( node !== null ) {
23179
+
23180
+ const tagged = node.userData.lodColorationIndex;
23181
+
23182
+ if ( typeof tagged === 'number' && Number.isFinite( tagged ) ) {
23183
+
23184
+ return tagged;
23185
+
23186
+ }
23187
+
23188
+ node = node.parent;
23189
+
23190
+ }
23191
+
23192
+ node = object;
23193
+
23194
+ while ( node !== null ) {
23195
+
23196
+ const parent = node.parent;
23197
+
23198
+ if ( parent !== null && parent.isLOD === true ) {
23199
+
23200
+ const levels = parent.levels;
23201
+
23202
+ for ( let i = 0; i < levels.length; i ++ ) {
23203
+
23204
+ const levelObject = levels[ i ].object;
23205
+
23206
+ if ( levelObject === object || levelObject.getObjectById( object.id ) !== undefined ) {
23207
+
23208
+ return i;
23209
+
23210
+ }
23211
+
23212
+ }
23213
+
23214
+ return parent.getCurrentLevel();
23215
+
23216
+ }
23217
+
23218
+ node = parent;
23219
+
23220
+ }
23221
+
23222
+ return 0;
23223
+
23224
+ }
23225
+
23226
+ /**
23227
+ * @param {Color} color - Destination color.
23228
+ * @param {?Object3D} object - The drawn object.
23229
+ * @param {Renderer~DebugConfig} debug - Renderer debug state.
23230
+ * @return {Color} `color`.
23231
+ */
23232
+ function writeLODColorationColor( color, object, debug ) {
23233
+
23234
+ const index = resolveLODColorationIndex( object );
23235
+ const palette = debug.lodColorationColors ?? DEFAULT_LOD_COLORATION_COLORS;
23236
+
23237
+ if ( index < 0 || index >= palette.length ) {
23238
+
23239
+ return color.copy( INVALID_LOD_COLOR );
23240
+
23241
+ }
23242
+
23243
+ return color.copy( palette[ index ] );
23244
+
23245
+ }
23246
+
23247
+ /**
23248
+ * Replaces albedo with the LOD palette color after the material has written its channels.
23249
+ *
23250
+ * @param {NodeBuilder} builder - The current node builder.
23251
+ * @param {NodeMaterial} material - The node material being set up.
23252
+ */
23253
+ function applyLODColorationDebug( builder, material ) {
23254
+
23255
+ const colorNode = uniform( new Color() ).onObjectUpdate( ( { object }, self ) => {
23256
+
23257
+ if ( object === null ) return;
23258
+
23259
+ return writeLODColorationColor( self.value, object, builder.renderer.debug );
23260
+
23261
+ } );
23262
+
23263
+ diffuseColor.rgb.assign( colorNode );
23264
+
23265
+ if ( material.lights !== true ) return;
23266
+
23267
+ metalness.assign( float( 0 ) );
23268
+ diffuseContribution.assign( colorNode );
23269
+ roughness.assign( float( 1 ) );
23270
+ specularColor.assign( vec3( 0 ) );
23271
+ specularColorBlended.assign( vec3( 0 ) );
23272
+ specularF90.assign( float( 0 ) );
23273
+
23274
+ builder.context.ambientOcclusion = null;
23275
+
23276
+ if ( material.useClearcoat === true ) clearcoat.assign( float( 0 ) );
23277
+
23278
+ if ( material.useSheen === true ) sheen.assign( vec3( 0 ) );
23279
+
23280
+ if ( material.useIridescence === true ) iridescence.assign( float( 0 ) );
23281
+
23282
+ if ( material.useAnisotropy === true ) anisotropy.assign( float( 0 ) );
23283
+
23284
+ if ( material.useTransmission === true ) transmission.assign( float( 0 ) );
23285
+
23286
+ if ( material.useRetroreflection === true ) retroreflectivity.assign( float( 0 ) );
23287
+
23288
+ }
23289
+
23131
23290
  // WITH_GENESYS
23132
23291
  // Double sided. Gray is a single-sided surface or the front of a double-sided one.
23133
23292
  // Blue is a double-sided back face you can see. Red is a double-sided back face hidden behind the front.
@@ -23825,6 +23984,10 @@ class NodeMaterial extends Material {
23825
23984
 
23826
23985
  applyShadowCasterDebug( builder, this );
23827
23986
 
23987
+ } else if ( renderer.debug.view === DEBUG_VIEW_LOD_COLORATION && this.isShadowPassMaterial !== true ) {
23988
+
23989
+ applyLODColorationDebug( builder, this );
23990
+
23828
23991
  } else if ( renderer.debug.view === DEBUG_VIEW_DOUBLE_SIDE && this.isShadowPassMaterial !== true ) {
23829
23992
 
23830
23993
  applyDoubleSideDebug( builder, this );
@@ -24287,7 +24450,7 @@ class NodeMaterial extends Material {
24287
24450
  setupNormal( builder ) {
24288
24451
 
24289
24452
  // WITH_GENESYS
24290
- if ( ( builder.renderer.debug.view === DEBUG_VIEW_LIGHTING_ONLY || builder.renderer.debug.view === DEBUG_VIEW_DRAW_CALL || builder.renderer.debug.view === DEBUG_VIEW_FRONT_BACK_FACE || builder.renderer.debug.view === DEBUG_VIEW_SHADOW_CASTER || builder.renderer.debug.view === DEBUG_VIEW_DOUBLE_SIDE ) && this.isShadowPassMaterial !== true && this.fragmentNode === null ) {
24453
+ if ( ( builder.renderer.debug.view === DEBUG_VIEW_LIGHTING_ONLY || builder.renderer.debug.view === DEBUG_VIEW_DRAW_CALL || builder.renderer.debug.view === DEBUG_VIEW_FRONT_BACK_FACE || builder.renderer.debug.view === DEBUG_VIEW_SHADOW_CASTER || builder.renderer.debug.view === DEBUG_VIEW_LOD_COLORATION || builder.renderer.debug.view === DEBUG_VIEW_DOUBLE_SIDE ) && this.isShadowPassMaterial !== true && this.fragmentNode === null ) {
24291
24454
 
24292
24455
  return lightingOnlyNormal();
24293
24456
 
@@ -24492,7 +24655,7 @@ class NodeMaterial extends Material {
24492
24655
  // WITH_GENESYS
24493
24656
  // Lighting only and draw call substitute a plain lit material, so emissive is dropped.
24494
24657
  // Detail lighting keeps it.
24495
- if ( ( builder.renderer.debug.view === DEBUG_VIEW_LIGHTING_ONLY || builder.renderer.debug.view === DEBUG_VIEW_DRAW_CALL || builder.renderer.debug.view === DEBUG_VIEW_FRONT_BACK_FACE || builder.renderer.debug.view === DEBUG_VIEW_SHADOW_CASTER || builder.renderer.debug.view === DEBUG_VIEW_DOUBLE_SIDE ) && this.isShadowPassMaterial !== true ) {
24658
+ if ( ( builder.renderer.debug.view === DEBUG_VIEW_LIGHTING_ONLY || builder.renderer.debug.view === DEBUG_VIEW_DRAW_CALL || builder.renderer.debug.view === DEBUG_VIEW_FRONT_BACK_FACE || builder.renderer.debug.view === DEBUG_VIEW_SHADOW_CASTER || builder.renderer.debug.view === DEBUG_VIEW_LOD_COLORATION || builder.renderer.debug.view === DEBUG_VIEW_DOUBLE_SIDE ) && this.isShadowPassMaterial !== true ) {
24496
24659
 
24497
24660
  return outgoingLightNode;
24498
24661
 
@@ -64161,6 +64324,565 @@ class CanvasTarget extends EventDispatcher {
64161
64324
 
64162
64325
  }
64163
64326
 
64327
+ // WITH_GENESYS
64328
+ // Viewport legend for renderer.debug views that use fixed ramps or symbolic colors.
64329
+ // !WITH_GENESYS
64330
+
64331
+ const SHADER_COMPLEXITY_COLORS = [
64332
+ [ 0.0, 1.0, 0.127 ],
64333
+ [ 0.0, 1.0, 0.0 ],
64334
+ [ 0.046, 0.52, 0.0 ],
64335
+ [ 0.215, 0.215, 0.0 ],
64336
+ [ 0.52, 0.046, 0.0 ],
64337
+ [ 0.7, 0.0, 0.0 ],
64338
+ [ 1.0, 0.0, 0.0 ],
64339
+ [ 1.0, 0.0, 0.5 ],
64340
+ [ 1.0, 0.9, 0.9 ]
64341
+ ];
64342
+
64343
+ const QUAD_OVERDRAW_COLORS = [
64344
+ [ 0.0, 0.0, 0.0 ],
64345
+ [ 0.0, 0.0, 0.4 ],
64346
+ [ 0.0, 0.3, 1.0 ],
64347
+ [ 0.0, 0.7, 0.4 ],
64348
+ [ 0.0, 1.0, 0.0 ],
64349
+ [ 0.8, 0.8, 0.0 ],
64350
+ [ 1.0, 0.3, 0.0 ],
64351
+ [ 0.7, 0.0, 0.0 ],
64352
+ [ 0.5, 0.0, 0.5 ],
64353
+ [ 0.7, 0.3, 0.7 ],
64354
+ [ 1.0, 0.9, 0.9 ]
64355
+ ];
64356
+
64357
+ const LIGHT_COMPLEXITY_COLORS = QUAD_OVERDRAW_COLORS;
64358
+
64359
+ const DEFAULT_LOD_LABELS = [
64360
+ 'LOD 0',
64361
+ 'LOD 1',
64362
+ 'LOD 2',
64363
+ 'LOD 3',
64364
+ 'LOD 4',
64365
+ 'LOD 5',
64366
+ 'LOD 6',
64367
+ 'LOD 7'
64368
+ ];
64369
+
64370
+ const DEFAULT_LOD_COLORS = [
64371
+ [ 1, 1, 1 ],
64372
+ [ 1, 0, 0 ],
64373
+ [ 0, 1, 0 ],
64374
+ [ 0, 0, 1 ],
64375
+ [ 1, 1, 0 ],
64376
+ [ 1, 0, 1 ],
64377
+ [ 0, 1, 1 ],
64378
+ [ 0.5, 0, 0.5 ]
64379
+ ];
64380
+
64381
+ const LEGEND_DEBUG_KEYS = new Set( [
64382
+ 'view',
64383
+ 'buffer',
64384
+ 'shaderComplexityBudget',
64385
+ 'quadOverdrawBudget'
64386
+ ] );
64387
+
64388
+ let _stylesInjected = false;
64389
+
64390
+ function injectStyles() {
64391
+
64392
+ if ( _stylesInjected === true || typeof document === 'undefined' ) return;
64393
+
64394
+ _stylesInjected = true;
64395
+
64396
+ const style = document.createElement( 'style' );
64397
+ style.textContent = `
64398
+ .three-debug-view-legend {
64399
+ position: absolute;
64400
+ left: 12px;
64401
+ bottom: 12px;
64402
+ z-index: 900;
64403
+ max-width: min( 420px, calc( 100% - 24px ) );
64404
+ padding: 10px 12px;
64405
+ border-radius: 8px;
64406
+ border: 1px solid rgba( 74, 74, 90, 0.55 );
64407
+ background: rgba( 30, 30, 36, 0.88 );
64408
+ color: #e0e0e0;
64409
+ font: 12px/1.35 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
64410
+ pointer-events: none;
64411
+ backdrop-filter: blur( 8px );
64412
+ box-shadow: 0 4px 15px rgba( 0, 0, 0, 0.35 );
64413
+ }
64414
+
64415
+ .three-debug-view-legend[ hidden ] {
64416
+ display: none !important;
64417
+ }
64418
+
64419
+ .three-debug-view-legend-title {
64420
+ font-size: 11px;
64421
+ font-weight: 600;
64422
+ letter-spacing: 0.02em;
64423
+ text-transform: uppercase;
64424
+ color: #9a9aab;
64425
+ margin-bottom: 6px;
64426
+ }
64427
+
64428
+ .three-debug-view-legend-caption {
64429
+ margin-top: 6px;
64430
+ color: #9a9aab;
64431
+ font-size: 11px;
64432
+ }
64433
+
64434
+ .three-debug-view-legend-ramp {
64435
+ height: 10px;
64436
+ border-radius: 4px;
64437
+ border: 1px solid rgba( 255, 255, 255, 0.12 );
64438
+ margin: 4px 0 6px;
64439
+ }
64440
+
64441
+ .three-debug-view-legend-items {
64442
+ display: flex;
64443
+ flex-wrap: wrap;
64444
+ gap: 8px 12px;
64445
+ }
64446
+
64447
+ .three-debug-view-legend-item {
64448
+ display: inline-flex;
64449
+ align-items: center;
64450
+ gap: 6px;
64451
+ white-space: nowrap;
64452
+ }
64453
+
64454
+ .three-debug-view-legend-swatch {
64455
+ width: 10px;
64456
+ height: 10px;
64457
+ border-radius: 2px;
64458
+ border: 1px solid rgba( 255, 255, 255, 0.2 );
64459
+ flex-shrink: 0;
64460
+ }
64461
+
64462
+ .three-debug-view-legend-textonly {
64463
+ color: #c8c8d4;
64464
+ }
64465
+ `;
64466
+
64467
+ document.head.appendChild( style );
64468
+
64469
+ }
64470
+
64471
+ function rgbToCss( rgb ) {
64472
+
64473
+ const r = Math.round( rgb[ 0 ] * 255 );
64474
+ const g = Math.round( rgb[ 1 ] * 255 );
64475
+ const b = Math.round( rgb[ 2 ] * 255 );
64476
+
64477
+ return `rgb( ${ r }, ${ g }, ${ b } )`;
64478
+
64479
+ }
64480
+
64481
+ function rampGradient( colors ) {
64482
+
64483
+ const stops = colors.map( ( c, i ) => `${ rgbToCss( c ) } ${ ( i / ( colors.length - 1 ) ) * 100 }%` );
64484
+
64485
+ return `linear-gradient( to right, ${ stops.join( ', ' ) } )`;
64486
+
64487
+ }
64488
+
64489
+ function legendRamp( labelLow, labelHigh, colors, caption ) {
64490
+
64491
+ return {
64492
+ title: null,
64493
+ ramp: rampGradient( colors ),
64494
+ rampLabels: [ labelLow, labelHigh ],
64495
+ items: null,
64496
+ caption
64497
+ };
64498
+
64499
+ }
64500
+
64501
+ function legendItems( title, items, caption ) {
64502
+
64503
+ return {
64504
+ title,
64505
+ ramp: null,
64506
+ rampLabels: null,
64507
+ items,
64508
+ caption
64509
+ };
64510
+
64511
+ }
64512
+
64513
+ /**
64514
+ * @param {Renderer~DebugConfig} debug
64515
+ * @return {?object}
64516
+ */
64517
+ function getLegendSpec( debug ) {
64518
+
64519
+ const view = debug.view;
64520
+ const buffer = debug.buffer;
64521
+
64522
+ if ( view === 'shaderComplexity' || view === 'shaderComplexityAndOverdraw' ) {
64523
+
64524
+ const budget = debug.shaderComplexityBudget ?? 800;
64525
+
64526
+ return legendRamp(
64527
+ 'Low cost',
64528
+ `Budget (${ budget })`,
64529
+ SHADER_COMPLEXITY_COLORS,
64530
+ view === 'shaderComplexityAndOverdraw'
64531
+ ? 'Shader cost multiplied by overlapping fragment count.'
64532
+ : 'Estimated material shading cost.'
64533
+ );
64534
+
64535
+ }
64536
+
64537
+ if ( view === 'lightingComplexity' ) {
64538
+
64539
+ return legendRamp(
64540
+ 'No lights',
64541
+ '~10 lights',
64542
+ LIGHT_COMPLEXITY_COLORS,
64543
+ 'Direct lights per pixel. Shadow casters count as 1.5.'
64544
+ );
64545
+
64546
+ }
64547
+
64548
+ if ( view === 'overdraw' ) {
64549
+
64550
+ const budget = debug.quadOverdrawBudget ?? 10;
64551
+
64552
+ return legendRamp(
64553
+ '1 fragment',
64554
+ `Budget (${ budget })`,
64555
+ QUAD_OVERDRAW_COLORS,
64556
+ 'Fragments that pass the depth test per pixel.'
64557
+ );
64558
+
64559
+ }
64560
+
64561
+ if ( view === 'lodColoration' ) {
64562
+
64563
+ const palette = debug.lodColorationColors;
64564
+ const items = [];
64565
+
64566
+ for ( let i = 0; i < DEFAULT_LOD_LABELS.length; i ++ ) {
64567
+
64568
+ const color = palette && palette[ i ] ? palette[ i ] : DEFAULT_LOD_COLORS[ i ];
64569
+ const rgb = color.isColor ? [ color.r, color.g, color.b ] : DEFAULT_LOD_COLORS[ i ];
64570
+
64571
+ items.push( { color: rgbToCss( rgb ), label: DEFAULT_LOD_LABELS[ i ] } );
64572
+
64573
+ }
64574
+
64575
+ return legendItems( 'LOD coloration', items, 'Palette matches Unreal LODColorationColors.' );
64576
+
64577
+ }
64578
+
64579
+ if ( view === 'doubleSide' ) {
64580
+
64581
+ return legendItems( 'Double side', [
64582
+ { color: rgbToCss( [ 0.5, 0.5, 0.5 ] ), label: 'Single-sided or front' },
64583
+ { color: rgbToCss( [ 0.25, 0.45, 0.85 ] ), label: 'Visible back face' },
64584
+ { color: rgbToCss( [ 0.85, 0.12, 0.1 ] ), label: 'Hidden back face' }
64585
+ ], null );
64586
+
64587
+ }
64588
+
64589
+ if ( view === 'drawCall' ) {
64590
+
64591
+ return {
64592
+ title: 'Draw call',
64593
+ ramp: null,
64594
+ rampLabels: null,
64595
+ items: null,
64596
+ caption: 'Each color is one submitted draw. Instanced meshes share one color.'
64597
+ };
64598
+
64599
+ }
64600
+
64601
+ if ( view === 'shadowCaster' ) {
64602
+
64603
+ return legendItems( 'Shadow caster', [
64604
+ { color: rgbToCss( [ 0, 1, 0 ] ), label: 'Casts shadows' },
64605
+ { color: rgbToCss( [ 0.5, 0.5, 0.5 ] ), label: 'Does not cast' }
64606
+ ], null );
64607
+
64608
+ }
64609
+
64610
+ if ( view === 'frontBackFace' ) {
64611
+
64612
+ return legendItems( 'Front / back face', [
64613
+ { color: rgbToCss( [ 0.72, 0.70, 0.66 ] ), label: 'Facing camera' },
64614
+ { color: rgbToCss( [ 0.25, 0.45, 0.85 ] ), label: 'Opposite winding' }
64615
+ ], null );
64616
+
64617
+ }
64618
+
64619
+ if ( view === 'bufferVisualization' ) {
64620
+
64621
+ if ( buffer === 'worldNormal' ) {
64622
+
64623
+ return legendItems( 'World normal', [
64624
+ { color: rgbToCss( [ 0.5, 0.5, 1 ] ), label: '+X' },
64625
+ { color: rgbToCss( [ 0.5, 1, 0.5 ] ), label: '+Y (up)' },
64626
+ { color: rgbToCss( [ 1, 0.5, 0.5 ] ), label: '+Z' }
64627
+ ], 'Encoded as normal × 0.5 + 0.5.' );
64628
+
64629
+ }
64630
+
64631
+ if ( buffer === 'roughness' ) {
64632
+
64633
+ return legendRamp( 'Smooth', 'Rough', [[ 0, 0, 0 ], [ 1, 1, 1 ]], null );
64634
+
64635
+ }
64636
+
64637
+ if ( buffer === 'metallic' ) {
64638
+
64639
+ return legendRamp( 'Dielectric', 'Metal', [[ 0, 0, 0 ], [ 1, 1, 1 ]], null );
64640
+
64641
+ }
64642
+
64643
+ if ( buffer === 'ambientOcclusion' ) {
64644
+
64645
+ return legendRamp( 'Occluded', 'None', [[ 0.2, 0.2, 0.2 ], [ 1, 1, 1 ]], 'White when no AO map is applied.' );
64646
+
64647
+ }
64648
+
64649
+ if ( buffer === 'emissive' ) {
64650
+
64651
+ return legendRamp( 'None', 'Bright', [[ 0, 0, 0 ], [ 1, 1, 1 ]], 'Emissive color only, no lighting.' );
64652
+
64653
+ }
64654
+
64655
+ }
64656
+
64657
+ return null;
64658
+
64659
+ }
64660
+
64661
+ function renderSpec( root, spec ) {
64662
+
64663
+ root.replaceChildren();
64664
+
64665
+ if ( spec.title ) {
64666
+
64667
+ const title = document.createElement( 'div' );
64668
+ title.className = 'three-debug-view-legend-title';
64669
+ title.textContent = spec.title;
64670
+ root.appendChild( title );
64671
+
64672
+ }
64673
+
64674
+ if ( spec.ramp ) {
64675
+
64676
+ const ramp = document.createElement( 'div' );
64677
+ ramp.className = 'three-debug-view-legend-ramp';
64678
+ ramp.style.background = spec.ramp;
64679
+ root.appendChild( ramp );
64680
+
64681
+ if ( spec.rampLabels ) {
64682
+
64683
+ const labels = document.createElement( 'div' );
64684
+ labels.className = 'three-debug-view-legend-items';
64685
+ labels.style.justifyContent = 'space-between';
64686
+ labels.style.width = '100%';
64687
+
64688
+ for ( const label of spec.rampLabels ) {
64689
+
64690
+ const item = document.createElement( 'span' );
64691
+ item.className = 'three-debug-view-legend-item';
64692
+ item.textContent = label;
64693
+ labels.appendChild( item );
64694
+
64695
+ }
64696
+
64697
+ root.appendChild( labels );
64698
+
64699
+ }
64700
+
64701
+ }
64702
+
64703
+ if ( spec.items ) {
64704
+
64705
+ const row = document.createElement( 'div' );
64706
+ row.className = 'three-debug-view-legend-items';
64707
+
64708
+ for ( const entry of spec.items ) {
64709
+
64710
+ const item = document.createElement( 'span' );
64711
+ item.className = 'three-debug-view-legend-item';
64712
+
64713
+ const swatch = document.createElement( 'span' );
64714
+ swatch.className = 'three-debug-view-legend-swatch';
64715
+ swatch.style.background = entry.color;
64716
+
64717
+ const text = document.createElement( 'span' );
64718
+ text.textContent = entry.label;
64719
+
64720
+ item.append( swatch, text );
64721
+ row.appendChild( item );
64722
+
64723
+ }
64724
+
64725
+ root.appendChild( row );
64726
+
64727
+ }
64728
+
64729
+ if ( spec.caption ) {
64730
+
64731
+ const caption = document.createElement( 'div' );
64732
+ caption.className = spec.items === null && spec.ramp === null
64733
+ ? 'three-debug-view-legend-textonly'
64734
+ : 'three-debug-view-legend-caption';
64735
+ caption.textContent = spec.caption;
64736
+ root.appendChild( caption );
64737
+
64738
+ }
64739
+
64740
+ }
64741
+
64742
+ /**
64743
+ * Wraps `renderer.debug` so legend-related assignments refresh the overlay immediately.
64744
+ *
64745
+ * @param {Renderer} renderer
64746
+ * @param {Renderer~DebugConfig} debug
64747
+ * @return {Renderer~DebugConfig}
64748
+ */
64749
+ function wrapDebugConfigForLegend( renderer, debug ) {
64750
+
64751
+ return new Proxy( debug, {
64752
+
64753
+ set( target, prop, value ) {
64754
+
64755
+ const changed = target[ prop ] !== value;
64756
+
64757
+ target[ prop ] = value;
64758
+
64759
+ if ( changed === true && LEGEND_DEBUG_KEYS.has( prop ) ) {
64760
+
64761
+ renderer._syncDebugViewLegend();
64762
+
64763
+ }
64764
+
64765
+ return true;
64766
+
64767
+ }
64768
+
64769
+ } );
64770
+
64771
+ }
64772
+
64773
+ /**
64774
+ * DOM overlay that explains the active `renderer.debug` view.
64775
+ */
64776
+ class DebugViewLegend {
64777
+
64778
+ constructor() {
64779
+
64780
+ if ( typeof document === 'undefined' ) {
64781
+
64782
+ this.domElement = null;
64783
+ this._parent = null;
64784
+ this._signature = '';
64785
+ return;
64786
+
64787
+ }
64788
+
64789
+ injectStyles();
64790
+
64791
+ this.domElement = document.createElement( 'div' );
64792
+ this.domElement.className = 'three-debug-view-legend';
64793
+ this.domElement.hidden = true;
64794
+
64795
+ this._parent = null;
64796
+ this._signature = '';
64797
+
64798
+ }
64799
+
64800
+ /**
64801
+ * @param {HTMLElement} parent - Usually the canvas container.
64802
+ */
64803
+ _mountParent( parent ) {
64804
+
64805
+ if ( this.domElement === null || parent === this._parent ) return;
64806
+
64807
+ if ( this._parent !== null ) {
64808
+
64809
+ this.domElement.remove();
64810
+
64811
+ }
64812
+
64813
+ this._parent = parent;
64814
+
64815
+ if ( getComputedStyle( parent ).position === 'static' ) {
64816
+
64817
+ parent.style.position = 'relative';
64818
+
64819
+ }
64820
+
64821
+ parent.appendChild( this.domElement );
64822
+
64823
+ }
64824
+
64825
+ /**
64826
+ * @param {Renderer} renderer
64827
+ */
64828
+ sync( renderer ) {
64829
+
64830
+ if ( this.domElement === null ) return;
64831
+
64832
+ if ( renderer === null || renderer.debug === undefined ) {
64833
+
64834
+ this.domElement.hidden = true;
64835
+ return;
64836
+
64837
+ }
64838
+
64839
+ const parent = renderer.domElement.parentElement;
64840
+
64841
+ if ( parent !== null ) {
64842
+
64843
+ this._mountParent( parent );
64844
+
64845
+ }
64846
+
64847
+ const debug = renderer.debug;
64848
+ const signature = `${ debug.view }:${ debug.buffer ?? '' }:${ debug.shaderComplexityBudget }:${ debug.quadOverdrawBudget }`;
64849
+
64850
+ const spec = getLegendSpec( debug );
64851
+
64852
+ if ( spec === null ) {
64853
+
64854
+ this.domElement.hidden = true;
64855
+ this._signature = '';
64856
+ return;
64857
+
64858
+ }
64859
+
64860
+ this.domElement.hidden = false;
64861
+
64862
+ if ( signature !== this._signature ) {
64863
+
64864
+ renderSpec( this.domElement, spec );
64865
+ this._signature = signature;
64866
+
64867
+ }
64868
+
64869
+ }
64870
+
64871
+ dispose() {
64872
+
64873
+ if ( this.domElement !== null ) {
64874
+
64875
+ this.domElement.remove();
64876
+
64877
+ }
64878
+
64879
+ this._parent = null;
64880
+ this._signature = '';
64881
+
64882
+ }
64883
+
64884
+ }
64885
+
64164
64886
  const _scene = /*@__PURE__*/ new Scene();
64165
64887
  const _drawingBufferSize = /*@__PURE__*/ new Vector2();
64166
64888
  const _screen = /*@__PURE__*/ new Vector4();
@@ -64861,7 +65583,8 @@ class Renderer {
64861
65583
  * @property {?Function} onNodeBuilderCreated - A callback function that is executed after a node builder has been created and before it is built.
64862
65584
  * @property {?Function} onShaderError - A callback function that is executed when a shader error happens. Only supported with WebGL 2 right now.
64863
65585
  * @property {Function} getShaderAsync - Allows the get the raw shader code for the given scene, camera and 3D object.
64864
- * @property {string} view - Debug view. `shaderComplexity`, `lightingComplexity`, `overdraw`, and `shaderComplexityAndOverdraw` replace the shaded color with a heatmap. `shaderComplexityAndOverdraw` multiplies the shader cost by the overdraw count. `bufferVisualization` shows one material channel. `lightingOnly` and `detailLighting` light a flat gray surface. `drawCall` lights each submitted draw with its own diffuse color. `frontBackFace` draws both windings and tints the side facing the camera. `shadowCaster` lights a mesh green when it casts shadows and gray when it does not. `doubleSide` lights a single-sided surface gray, a visible double-sided back face blue, and a hidden double-sided back face red. `wireframe` draws each mesh as lines along its triangle edges. Lighting stays the same as the shaded view. Shadow passes and fullscreen quads stay triangles.
65586
+ * @property {string} view - Debug view. `shaderComplexity`, `lightingComplexity`, `overdraw`, and `shaderComplexityAndOverdraw` replace the shaded color with a heatmap. `shaderComplexityAndOverdraw` multiplies the shader cost by the overdraw count. `bufferVisualization` shows one material channel. `lightingOnly` and `detailLighting` light a flat gray surface. `drawCall` lights each submitted draw with its own diffuse color. `lodColoration` lights geometry with the palette entry for its LOD index. `frontBackFace` draws both windings and tints the side facing the camera. `shadowCaster` lights a mesh green when it casts shadows and gray when it does not. `doubleSide` lights a single-sided surface gray, a visible double-sided back face blue, and a hidden double-sided back face red. `wireframe` draws each mesh as lines along its triangle edges. Lighting stays the same as the shaded view. Shadow passes and fullscreen quads stay triangles.
65587
+ * @property {Color[]} lodColorationColors - Palette for `lodColoration`. Defaults to Unreal `LODColorationColors`.
64865
65588
  * @property {string} buffer - Channel drawn by `bufferVisualization`: `baseColor`, `worldNormal`, `roughness`, `metallic`, `ambientOcclusion`, or `emissive`.
64866
65589
  * @property {number} shaderComplexityBudget - Proxy budget that fills the shader-complexity ramp.
64867
65590
  * @property {number} quadOverdrawBudget - Overlapping fragments that fill the quad-overdraw ramp.
@@ -64886,6 +65609,7 @@ class Renderer {
64886
65609
  quadOverdrawBudget: DEFAULT_QUAD_OVERDRAW_BUDGET,
64887
65610
  doubleSideHidden: false,
64888
65611
  doubleSideBack: false,
65612
+ lodColorationColors: DEFAULT_LOD_COLORATION_COLORS.map( color => color.clone() ),
64889
65613
  // !WITH_GENESYS
64890
65614
  getShaderAsync: async ( scene, camera, object ) => {
64891
65615
 
@@ -64908,8 +65632,30 @@ class Renderer {
64908
65632
  }
64909
65633
  };
64910
65634
 
65635
+ this._debugViewLegend = typeof document !== 'undefined' ? new DebugViewLegend() : null;
65636
+ this.debug = wrapDebugConfigForLegend( this, this.debug );
65637
+
64911
65638
  }
64912
65639
 
65640
+ // WITH_GENESYS
65641
+
65642
+ /**
65643
+ * Keeps the debug-view legend aligned with `renderer.debug`.
65644
+ *
65645
+ * @private
65646
+ */
65647
+ _syncDebugViewLegend() {
65648
+
65649
+ if ( this._debugViewLegend !== null ) {
65650
+
65651
+ this._debugViewLegend.sync( this );
65652
+
65653
+ }
65654
+
65655
+ }
65656
+
65657
+ // !WITH_GENESYS
65658
+
64913
65659
  /**
64914
65660
  * Initializes the renderer so it is ready for usage.
64915
65661
  *
@@ -64977,6 +65723,10 @@ class Renderer {
64977
65723
  this._animation.start();
64978
65724
  this._initialized = true;
64979
65725
 
65726
+ // WITH_GENESYS
65727
+ this._syncDebugViewLegend();
65728
+ // !WITH_GENESYS
65729
+
64980
65730
  resolve( this );
64981
65731
 
64982
65732
  } );
@@ -66926,6 +67676,15 @@ class Renderer {
66926
67676
 
66927
67677
  this.info.dispose();
66928
67678
 
67679
+ // WITH_GENESYS
67680
+ if ( this._debugViewLegend !== null ) {
67681
+
67682
+ this._debugViewLegend.dispose();
67683
+ this._debugViewLegend = null;
67684
+
67685
+ }
67686
+ // !WITH_GENESYS
67687
+
66929
67688
  this._inspector.dispose();
66930
67689
  this._animation.dispose();
66931
67690
  this._objects.dispose();