@gnsx/three 0.186.9 → 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.
@@ -76,7 +76,8 @@ const refreshUniforms = [
76
76
  'steps',
77
77
  'thickness',
78
78
  'transmission',
79
- 'transmissionMap'
79
+ 'transmissionMap',
80
+ 'wireframe'
80
81
  ];
81
82
 
82
83
 
@@ -271,14 +272,12 @@ class NodeMaterialObserver {
271
272
 
272
273
  }
273
274
 
274
- if ( renderObject.material.transmission > 0 ) {
275
+ // global data
275
276
 
276
- const { width, height } = renderObject.context;
277
-
278
- data.bufferWidth = width;
279
- data.bufferHeight = height;
277
+ const { width, height } = renderObject.context;
280
278
 
281
- }
279
+ data.bufferWidth = width;
280
+ data.bufferHeight = height;
282
281
 
283
282
  const { environmentIntensity, environmentRotation } = renderObject.scene;
284
283
 
@@ -599,18 +598,14 @@ class NodeMaterialObserver {
599
598
 
600
599
  }
601
600
 
602
- if ( materialData.transmission > 0 ) {
601
+ const { width, height } = renderObject.context;
603
602
 
604
- const { width, height } = renderObject.context;
603
+ if ( renderObjectData.bufferWidth !== width || renderObjectData.bufferHeight !== height ) {
605
604
 
606
- if ( renderObjectData.bufferWidth !== width || renderObjectData.bufferHeight !== height ) {
607
-
608
- renderObjectData.bufferWidth = width;
609
- renderObjectData.bufferHeight = height;
610
-
611
- return false;
605
+ renderObjectData.bufferWidth = width;
606
+ renderObjectData.bufferHeight = height;
612
607
 
613
- }
608
+ return false;
614
609
 
615
610
  }
616
611
 
@@ -21853,7 +21848,7 @@ const LIGHTING_COMPLEXITY_SHADOW_WEIGHT = 0.5;
21853
21848
  *
21854
21849
  * @type {Array<Array<number>>}
21855
21850
  */
21856
- const SHADER_COMPLEXITY_COLORS = [
21851
+ const SHADER_COMPLEXITY_COLORS$1 = [
21857
21852
  [ 0.0, 1.0, 0.127 ],
21858
21853
  [ 0.0, 1.0, 0.0 ],
21859
21854
  [ 0.046, 0.52, 0.0 ],
@@ -21890,7 +21885,7 @@ const QUAD_COMPLEXITY_COLORS = [
21890
21885
  *
21891
21886
  * @type {Array<Array<number>>}
21892
21887
  */
21893
- const LIGHT_COMPLEXITY_COLORS = [
21888
+ const LIGHT_COMPLEXITY_COLORS$1 = [
21894
21889
  [ 0.0, 0.0, 0.0 ],
21895
21890
  [ 0.0, 0.0, 0.4 ],
21896
21891
  [ 0.0, 0.3, 1.0 ],
@@ -21941,7 +21936,7 @@ const colorizeLinear = ( cost, colors ) => {
21941
21936
  * @param {Node<float>} cost - Accumulated light weight.
21942
21937
  * @return {Node<vec3>} The ramp color.
21943
21938
  */
21944
- const colorizeLightComplexity = ( cost ) => colorizeLinear( cost, LIGHT_COMPLEXITY_COLORS );
21939
+ const colorizeLightComplexity = ( cost ) => colorizeLinear( cost, LIGHT_COMPLEXITY_COLORS$1 );
21945
21940
 
21946
21941
  /**
21947
21942
  * Unreal's stair sample for quad overdraw (`CS_STAIR`).
@@ -22036,33 +22031,33 @@ function debugViewSkipsToneMapping( view ) {
22036
22031
  const colorizeShaderComplexity = /*@__PURE__*/ Fn( ( [ complexity ] ) => {
22037
22032
 
22038
22033
  const c = complexity.clamp( 0, 0.999 );
22039
- 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 ) );
22040
22035
  const color = vec3( 0 ).toVar();
22041
22036
  const frac = fract( expanded );
22042
22037
 
22043
22038
  If( expanded.lessThan( 1 ), () => {
22044
22039
 
22045
- 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 ) );
22046
22041
 
22047
22042
  } ).ElseIf( expanded.lessThan( 2 ), () => {
22048
22043
 
22049
- 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 ) );
22050
22045
 
22051
22046
  } ).ElseIf( expanded.lessThan( 3 ), () => {
22052
22047
 
22053
- 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 ) );
22054
22049
 
22055
22050
  } ).ElseIf( expanded.lessThan( 4 ), () => {
22056
22051
 
22057
- 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 ) );
22058
22053
 
22059
22054
  } ).ElseIf( expanded.lessThan( 5 ), () => {
22060
22055
 
22061
- 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 ) );
22062
22057
 
22063
22058
  } ).ElseIf( expanded.lessThan( 6 ), () => {
22064
22059
 
22065
- 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 ) );
22066
22061
 
22067
22062
  } ).Else( () => {
22068
22063
 
@@ -22072,11 +22067,11 @@ const colorizeShaderComplexity = /*@__PURE__*/ Fn( ( [ complexity ] ) => {
22072
22067
 
22073
22068
  If( upper.lessThanEqual( 1 ), () => {
22074
22069
 
22075
- 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 ) );
22076
22071
 
22077
22072
  } ).Else( () => {
22078
22073
 
22079
- 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 ) );
22080
22075
 
22081
22076
  } );
22082
22077
 
@@ -23133,6 +23128,165 @@ function applyShadowCasterDebug( builder, material ) {
23133
23128
 
23134
23129
  }
23135
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
+
23136
23290
  // WITH_GENESYS
23137
23291
  // Double sided. Gray is a single-sided surface or the front of a double-sided one.
23138
23292
  // Blue is a double-sided back face you can see. Red is a double-sided back face hidden behind the front.
@@ -23830,6 +23984,10 @@ class NodeMaterial extends Material {
23830
23984
 
23831
23985
  applyShadowCasterDebug( builder, this );
23832
23986
 
23987
+ } else if ( renderer.debug.view === DEBUG_VIEW_LOD_COLORATION && this.isShadowPassMaterial !== true ) {
23988
+
23989
+ applyLODColorationDebug( builder, this );
23990
+
23833
23991
  } else if ( renderer.debug.view === DEBUG_VIEW_DOUBLE_SIDE && this.isShadowPassMaterial !== true ) {
23834
23992
 
23835
23993
  applyDoubleSideDebug( builder, this );
@@ -24292,7 +24450,7 @@ class NodeMaterial extends Material {
24292
24450
  setupNormal( builder ) {
24293
24451
 
24294
24452
  // WITH_GENESYS
24295
- 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 ) {
24296
24454
 
24297
24455
  return lightingOnlyNormal();
24298
24456
 
@@ -24497,7 +24655,7 @@ class NodeMaterial extends Material {
24497
24655
  // WITH_GENESYS
24498
24656
  // Lighting only and draw call substitute a plain lit material, so emissive is dropped.
24499
24657
  // Detail lighting keeps it.
24500
- 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 ) {
24501
24659
 
24502
24660
  return outgoingLightNode;
24503
24661
 
@@ -34279,6 +34437,8 @@ class Geometries extends DataMap {
34279
34437
 
34280
34438
  this._geometryDisposeListeners.delete( geometry );
34281
34439
 
34440
+ this.delete( geometry );
34441
+
34282
34442
  };
34283
34443
 
34284
34444
  geometry.addEventListener( 'dispose', onDispose );
@@ -43351,7 +43511,7 @@ class VelocityNode extends TempNode {
43351
43511
  * @type {UniformNode<mat4>}
43352
43512
  * @default null
43353
43513
  */
43354
- this.previousCameraViewMatrix = uniform( new Matrix4() );
43514
+ this.previousCameraViewMatrix = uniform( new Matrix4() ).setGroup( renderGroup );
43355
43515
 
43356
43516
  }
43357
43517
 
@@ -56085,6 +56245,8 @@ class NodeBuilder {
56085
56245
  delete context.getAO;
56086
56246
  delete context.getGI;
56087
56247
  delete context.getShadow;
56248
+ delete context.nodeLoop;
56249
+ delete context.nodeBlock;
56088
56250
 
56089
56251
  return context;
56090
56252
 
@@ -64162,6 +64324,565 @@ class CanvasTarget extends EventDispatcher {
64162
64324
 
64163
64325
  }
64164
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
+
64165
64886
  const _scene = /*@__PURE__*/ new Scene();
64166
64887
  const _drawingBufferSize = /*@__PURE__*/ new Vector2();
64167
64888
  const _screen = /*@__PURE__*/ new Vector4();
@@ -64862,7 +65583,8 @@ class Renderer {
64862
65583
  * @property {?Function} onNodeBuilderCreated - A callback function that is executed after a node builder has been created and before it is built.
64863
65584
  * @property {?Function} onShaderError - A callback function that is executed when a shader error happens. Only supported with WebGL 2 right now.
64864
65585
  * @property {Function} getShaderAsync - Allows the get the raw shader code for the given scene, camera and 3D object.
64865
- * @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`.
64866
65588
  * @property {string} buffer - Channel drawn by `bufferVisualization`: `baseColor`, `worldNormal`, `roughness`, `metallic`, `ambientOcclusion`, or `emissive`.
64867
65589
  * @property {number} shaderComplexityBudget - Proxy budget that fills the shader-complexity ramp.
64868
65590
  * @property {number} quadOverdrawBudget - Overlapping fragments that fill the quad-overdraw ramp.
@@ -64887,6 +65609,7 @@ class Renderer {
64887
65609
  quadOverdrawBudget: DEFAULT_QUAD_OVERDRAW_BUDGET,
64888
65610
  doubleSideHidden: false,
64889
65611
  doubleSideBack: false,
65612
+ lodColorationColors: DEFAULT_LOD_COLORATION_COLORS.map( color => color.clone() ),
64890
65613
  // !WITH_GENESYS
64891
65614
  getShaderAsync: async ( scene, camera, object ) => {
64892
65615
 
@@ -64909,8 +65632,30 @@ class Renderer {
64909
65632
  }
64910
65633
  };
64911
65634
 
65635
+ this._debugViewLegend = typeof document !== 'undefined' ? new DebugViewLegend() : null;
65636
+ this.debug = wrapDebugConfigForLegend( this, this.debug );
65637
+
65638
+ }
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
+
64912
65655
  }
64913
65656
 
65657
+ // !WITH_GENESYS
65658
+
64914
65659
  /**
64915
65660
  * Initializes the renderer so it is ready for usage.
64916
65661
  *
@@ -64978,6 +65723,10 @@ class Renderer {
64978
65723
  this._animation.start();
64979
65724
  this._initialized = true;
64980
65725
 
65726
+ // WITH_GENESYS
65727
+ this._syncDebugViewLegend();
65728
+ // !WITH_GENESYS
65729
+
64981
65730
  resolve( this );
64982
65731
 
64983
65732
  } );
@@ -66927,6 +67676,15 @@ class Renderer {
66927
67676
 
66928
67677
  this.info.dispose();
66929
67678
 
67679
+ // WITH_GENESYS
67680
+ if ( this._debugViewLegend !== null ) {
67681
+
67682
+ this._debugViewLegend.dispose();
67683
+ this._debugViewLegend = null;
67684
+
67685
+ }
67686
+ // !WITH_GENESYS
67687
+
66930
67688
  this._inspector.dispose();
66931
67689
  this._animation.dispose();
66932
67690
  this._objects.dispose();