rayzee 11.7.0 → 11.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +31 -6
  2. package/dist/{ArchiveImporter-DYcRXeKE.js → ArchiveImporter-BJxszU_k.js} +2353 -1356
  3. package/dist/{BidirectionalIntegrator-BvjRpbTP.js → BidirectionalIntegrator-DWg66vAd.js} +302 -301
  4. package/dist/{BufferLayout-xPRH9jYD.js → BufferLayout-Bzgccg1o.js} +1 -0
  5. package/dist/{ColorManagement-oeEk7oSu.js → ColorManagement-B_HjAiy2.js} +1 -1
  6. package/dist/{EngineIssues-CIZnXHsF.js → EngineIssues-CfJiBaNj.js} +19 -8
  7. package/dist/{NeuralRetouch-ClaMiKYg.js → NeuralRetouch-BD9E-Yht.js} +2 -2
  8. package/dist/{NeuralSuperRes-WNEXun9s.js → NeuralSuperRes-C8Mf3tJX.js} +2 -2
  9. package/dist/{PathTracer-B9wZPPVL.js → PathTracer-DeE9UJ1n.js} +3530 -3402
  10. package/dist/{PhysicalSky-DcjItqKi.js → PhysicalSky-cKy75-oR.js} +45 -45
  11. package/dist/{RayzeeRenderer-p-NeE6E2.js → RayzeeRenderer-CzLyBA3G.js} +378 -374
  12. package/dist/SceneMetadata-C17UfOEp.js +183 -0
  13. package/dist/{SolarLimb-bZE6y63S.js → SolarLimb-DRYy8tln.js} +184 -172
  14. package/dist/{ToneMapGPU-sAs8wdgI.js → ToneMapGPU-4GPwoG_c.js} +13 -6
  15. package/dist/addons/archives.es.js +1 -1
  16. package/dist/addons/bidirectional.es.js +1 -1
  17. package/dist/addons/color.es.js +1 -1
  18. package/dist/addons/physical-sky.es.js +1 -1
  19. package/dist/addons/storage.es.js +1 -1
  20. package/dist/{openStorage-Fe9BoS1_.js → openStorage-CdyVjmps.js} +1 -1
  21. package/dist/rayzee-core.es.js +7 -7
  22. package/dist/rayzee.es.js +821 -794
  23. package/dist/rayzee.umd.js +112 -103
  24. package/dist/{sceneCachePolicy-B9w3BnaP.js → sceneCachePolicy-ywbGKHD3.js} +9 -6
  25. package/package.json +1 -1
  26. package/src/EngineDefaults.js +1 -0
  27. package/src/EngineIssues.js +21 -6
  28. package/src/Passes/AIUpscaler.js +4 -0
  29. package/src/Passes/OIDNDenoiser.js +8 -6
  30. package/src/Passes/OIDNTemporalHistory.js +1 -1
  31. package/src/PathTracerApp.js +10 -1
  32. package/src/Processor/ArchiveImporter.js +106 -27
  33. package/src/Processor/AssetLoader.js +22 -0
  34. package/src/Processor/BufferLayout.js +3 -3
  35. package/src/Processor/GeometryExtractor.js +12 -4
  36. package/src/Processor/LightSerializer.js +6 -2
  37. package/src/Processor/MaterialPacking.js +2 -2
  38. package/src/Processor/PBRT/PBRTMaterials.js +49 -13
  39. package/src/Processor/PBRT/PBRTParser.js +144 -22
  40. package/src/Processor/PBRT/PBRTSceneBuilder.js +287 -67
  41. package/src/Processor/PBRT/PBRTTextureBake.js +191 -9
  42. package/src/Processor/PBRT/index.js +19 -4
  43. package/src/Processor/PlatformImageLoader.js +9 -0
  44. package/src/Processor/SceneProcessor.js +42 -26
  45. package/src/Processor/TextureCreator.js +44 -4
  46. package/src/Processor/ToneMapCPU.js +34 -5
  47. package/src/Processor/ToneMapGPU.js +15 -9
  48. package/src/Processor/USD/USDScene.js +981 -17
  49. package/src/RayzeeRenderer.js +85 -28
  50. package/src/Stages/AutoExposure.js +21 -8
  51. package/src/Stages/Compositor.js +39 -1
  52. package/src/Stages/DenoiserSettings.js +0 -4
  53. package/src/Stages/LocalExposure.js +44 -18
  54. package/src/Stages/NRD.js +53 -24
  55. package/src/Stages/NormalDepth.js +12 -6
  56. package/src/Stages/PathTracer.js +27 -1
  57. package/src/Stages/PathTracerStage.js +14 -2
  58. package/src/Storage/SceneGraphCodec.js +6 -3
  59. package/src/TSL/BVHTraversal.js +33 -20
  60. package/src/TSL/CameraRay.js +12 -1
  61. package/src/TSL/Common.js +25 -8
  62. package/src/TSL/DebugKernel.js +3 -2
  63. package/src/TSL/Displacement.js +66 -113
  64. package/src/TSL/FinalWriteKernel.js +4 -7
  65. package/src/TSL/GenerateKernel.js +6 -2
  66. package/src/TSL/LightSplatKernel.js +7 -4
  67. package/src/TSL/LightsCore.js +6 -2
  68. package/src/TSL/LightsDirect.js +33 -11
  69. package/src/TSL/LightsSampling.js +45 -12
  70. package/src/TSL/SceneResources.js +19 -0
  71. package/src/TSL/ShadeKernel.js +3 -4
  72. package/src/TSL/Struct.js +1 -0
  73. package/src/integrators/BidirectionalIntegrator.js +1 -0
  74. package/src/managers/CameraManager.js +9 -0
  75. package/src/managers/DenoisingManager.js +1 -0
  76. package/src/managers/InteractionManager.js +6 -0
  77. package/src/managers/MaterialDataManager.js +11 -0
  78. package/src/managers/UniformManager.js +3 -1
  79. package/dist/SceneMetadata-D2Sh-0e-.js +0 -110
@@ -86,7 +86,9 @@ import {
86
86
  offsetRayOrigin,
87
87
  SHADOW_END,
88
88
  REC709_LUMINANCE_COEFFICIENTS,
89
+ constructTBN,
89
90
  } from './Common.js';
91
+ import { usesLightRadius } from './SceneResources.js';
90
92
  import { sampleEnvironment, sampleEnvironmentExact } from './Environment.js';
91
93
  import { shadowTerminatorOrigin } from './ShadowTerminator.js';
92
94
  import { sunRadianceToward, sampleSunDisc } from './Sun.js';
@@ -233,8 +235,30 @@ export const sampleRectAreaLight = Fn( ( [ light, rayOrigin, ruv, lightSelection
233
235
 
234
236
  } );
235
237
 
236
- // Enhanced spot light sampling with radius support
237
- export const sampleSpotLightWithRadius = Fn( ( [ light, rayOrigin, lightSelectionPdf ] ) => {
238
+ /**
239
+ * A point or spot light with a radius is a disc of it facing the lit point, as Cycles' with Soft Falloff: uniform on the
240
+ * disc, so on its axis the light gives intensity / ( d² + r² ) where a point gives intensity / d². Moves `toLight` and
241
+ * `distSq` to the sampled point and returns the disc's tilt toward it. No BSDF ray hits these lights, so no MIS.
242
+ */
243
+ const aimAtLightDisc = ( light, toLight, distSq, ruv ) => {
244
+
245
+ const facing = float( 1.0 ).toVar();
246
+ If( light.radius.greaterThan( 0.0 ), () => {
247
+
248
+ const axis = toLight.div( sqrt( distSq ) ).toVar();
249
+ const r = light.radius.mul( sqrt( ruv.x ) );
250
+ const phi = ruv.y.mul( 2 * Math.PI );
251
+ toLight.addAssign( constructTBN( { N: axis } ).mul( vec3( r.mul( cos( phi ) ), r.mul( sin( phi ) ), 0.0 ) ) );
252
+ distSq.assign( dot( toLight, toLight ) );
253
+ facing.assign( max( dot( toLight, axis ).div( sqrt( distSq ) ), 0.0 ) );
254
+
255
+ } );
256
+ return facing;
257
+
258
+ };
259
+
260
+ // Spot light sampling; a radius makes it a disc (aimAtLightDisc), its cone still read from the centre.
261
+ export const sampleSpotLightWithRadius = Fn( ( [ light, rayOrigin, lightSelectionPdf, ruv ], builder ) => {
238
262
 
239
263
  const ls_valid = tslBool( false ).toVar();
240
264
  const ls_direction = vec3( 0.0, 1.0, 0.0 ).toVar();
@@ -250,11 +274,16 @@ export const sampleSpotLightWithRadius = Fn( ( [ light, rayOrigin, lightSelectio
250
274
  // Guard against zero distance
251
275
  If( lightDistSq.greaterThanEqual( 1e-20 ), () => {
252
276
 
277
+ // Compiled in only while some light has a radius (SceneResources lightRadius); otherwise the exact point code.
278
+ const sized = usesLightRadius( builder );
279
+ const centerDir = sized ? toLight.div( sqrt( lightDistSq ) ).toVar() : null;
280
+ const facing = sized ? aimAtLightDisc( light, toLight, lightDistSq, ruv ) : null;
253
281
  const lightDist = sqrt( lightDistSq ).toVar();
254
282
  const lightDir = toLight.div( lightDist ).toVar();
283
+ const coneDir = sized ? centerDir : lightDir;
255
284
 
256
285
  // Check cone attenuation
257
- const spotCosAngle = dot( lightDir.negate(), light.direction ).toVar();
286
+ const spotCosAngle = dot( coneDir.negate(), light.direction ).toVar();
258
287
  const coneCosAngle = cos( light.angle ).toVar();
259
288
 
260
289
  ls_direction.assign( lightDir );
@@ -268,10 +297,11 @@ export const sampleSpotLightWithRadius = Fn( ( [ light, rayOrigin, lightSelectio
268
297
  const distanceAttenuation = getDistanceAttenuation( { lightDistance: lightDist, cutoffDistance: light.distance, decayExponent: light.decay } );
269
298
 
270
299
  // Gobo projection mask + IES photometric profile — both 1.0 when not assigned.
271
- const goboMask = sampleSpotGoboMask( light, lightDir );
272
- const iesProfile = sampleIESProfile( light, lightDir );
300
+ const goboMask = sampleSpotGoboMask( light, coneDir );
301
+ const iesProfile = sampleIESProfile( light, coneDir );
273
302
 
274
- ls_emission.assign( light.color.mul( light.intensity ).mul( distanceAttenuation ).mul( coneAttenuation ).mul( goboMask ).mul( iesProfile ) );
303
+ const emission = light.color.mul( light.intensity ).mul( distanceAttenuation ).mul( coneAttenuation ).mul( goboMask ).mul( iesProfile );
304
+ ls_emission.assign( sized ? emission.mul( facing ) : emission );
275
305
 
276
306
  } );
277
307
 
@@ -290,7 +320,7 @@ export const sampleSpotLightWithRadius = Fn( ( [ light, rayOrigin, lightSelectio
290
320
  } );
291
321
 
292
322
  // Enhanced point light sampling with distance attenuation
293
- export const samplePointLightWithAttenuation = Fn( ( [ light, rayOrigin, lightSelectionPdf ] ) => {
323
+ export const samplePointLightWithAttenuation = Fn( ( [ light, rayOrigin, lightSelectionPdf, ruv ], builder ) => {
294
324
 
295
325
  const ls_valid = tslBool( false ).toVar();
296
326
  const ls_direction = vec3( 0.0, 1.0, 0.0 ).toVar();
@@ -306,6 +336,8 @@ export const samplePointLightWithAttenuation = Fn( ( [ light, rayOrigin, lightSe
306
336
  // Guard against zero distance
307
337
  If( lightDistSq.greaterThanEqual( 1e-20 ), () => {
308
338
 
339
+ const sized = usesLightRadius( builder );
340
+ const facing = sized ? aimAtLightDisc( light, toLight, lightDistSq, ruv ) : null;
309
341
  const lightDist = sqrt( lightDistSq ).toVar();
310
342
  const lightDir = toLight.div( lightDist );
311
343
 
@@ -315,7 +347,8 @@ export const samplePointLightWithAttenuation = Fn( ( [ light, rayOrigin, lightSe
315
347
  ls_lightType.assign( int( LIGHT_TYPE_POINT ) );
316
348
  ls_direction.assign( lightDir );
317
349
  ls_distance.assign( lightDist );
318
- ls_emission.assign( light.color.mul( light.intensity ).mul( distanceAttenuation ) );
350
+ const emission = light.color.mul( light.intensity ).mul( distanceAttenuation );
351
+ ls_emission.assign( sized ? emission.mul( facing ) : emission );
319
352
  ls_pdf.assign( lightSelectionPdf );
320
353
  ls_valid.assign( tslBool( true ) );
321
354
 
@@ -614,7 +647,7 @@ export const sampleLightWithImportance = Fn( ( [
614
647
 
615
648
  If( light.intensity.greaterThan( 0.0 ), () => {
616
649
 
617
- const ptSample = LightSample.wrap( samplePointLightWithAttenuation( light, rayOrigin, lightSelectionPdf ) );
650
+ const ptSample = LightSample.wrap( samplePointLightWithAttenuation( light, rayOrigin, lightSelectionPdf, vec2( randomSeed.y, getRandomSample1D( pixelCoord, int( 0 ), dimBase.add( int( 15 ) ), rngState, resolution, frame ) ) ) );
618
651
  r_valid.assign( ptSample.valid );
619
652
  r_direction.assign( ptSample.direction );
620
653
  r_emission.assign( ptSample.emission );
@@ -639,7 +672,7 @@ export const sampleLightWithImportance = Fn( ( [
639
672
 
640
673
  If( light.intensity.greaterThan( 0.0 ), () => {
641
674
 
642
- const spotSample = LightSample.wrap( sampleSpotLightWithRadius( light, rayOrigin, lightSelectionPdf ) );
675
+ const spotSample = LightSample.wrap( sampleSpotLightWithRadius( light, rayOrigin, lightSelectionPdf, vec2( randomSeed.y, getRandomSample1D( pixelCoord, int( 0 ), dimBase.add( int( 15 ) ), rngState, resolution, frame ) ) ) );
643
676
  r_valid.assign( spotSample.valid );
644
677
  r_direction.assign( spotSample.direction );
645
678
  r_emission.assign( spotSample.emission );
@@ -702,7 +735,7 @@ export const sampleLightWithImportance = Fn( ( [
702
735
  If( selectedType.equal( int( 2 ) ).and( selectedIdx.greaterThanEqual( int( 0 ) ) ), () => {
703
736
 
704
737
  const light = PointLight.wrap( getPointLight( pointLightsBuffer, selectedIdx ) );
705
- const ptSample = LightSample.wrap( samplePointLightWithAttenuation( light, rayOrigin, pdf ) );
738
+ const ptSample = LightSample.wrap( samplePointLightWithAttenuation( light, rayOrigin, pdf, vec2( randomSeed.y, getRandomSample1D( pixelCoord, int( 0 ), dimBase.add( int( 15 ) ), rngState, resolution, frame ) ) ) );
706
739
  r_valid.assign( ptSample.valid );
707
740
  r_direction.assign( ptSample.direction );
708
741
  r_emission.assign( ptSample.emission );
@@ -716,7 +749,7 @@ export const sampleLightWithImportance = Fn( ( [
716
749
  If( selectedType.equal( int( 3 ) ).and( selectedIdx.greaterThanEqual( int( 0 ) ) ), () => {
717
750
 
718
751
  const light = SpotLight.wrap( getSpotLight( spotLightsBuffer, selectedIdx ) );
719
- const spotSample = LightSample.wrap( sampleSpotLightWithRadius( light, rayOrigin, pdf ) );
752
+ const spotSample = LightSample.wrap( sampleSpotLightWithRadius( light, rayOrigin, pdf, vec2( randomSeed.y, getRandomSample1D( pixelCoord, int( 0 ), dimBase.add( int( 15 ) ), rngState, resolution, frame ) ) ) );
720
753
  r_valid.assign( spotSample.valid );
721
754
  r_direction.assign( spotSample.direction );
722
755
  r_emission.assign( spotSample.emission );
@@ -12,6 +12,8 @@
12
12
  * @property {?Node} iesProfiles - IES profile texture array, or null
13
13
  * @property {?Node} alphaShadows - int uniform, 1 = shadow rays test texture alpha; null = never
14
14
  * @property {?Object<string, boolean>} materialLayers - the MATERIAL_LAYERS some material uses; null = all of them
15
+ * @property {boolean} [lightRadius] - some point or spot light has a radius (path tracing only); false = all are points
16
+ * @property {boolean} [transmissionMaps] - some material has a transmission map; false = shadow rays never sample one
15
17
  */
16
18
 
17
19
  import { context } from 'three/tsl';
@@ -49,3 +51,20 @@ export function materialLayers( builder ) {
49
51
  return builder.context[ KEY ]?.materialLayers ?? ALL_MATERIAL_LAYERS;
50
52
 
51
53
  }
54
+
55
+ /**
56
+ * Whether the kernel being built samples light radii. Off unless a point or spot light has a radius, and off for the
57
+ * bidirectional integrator, whose light paths leave a light's centre. Outside a scene kernel (a test) it is on.
58
+ */
59
+ export function usesLightRadius( builder ) {
60
+
61
+ return builder.context[ KEY ]?.lightRadius ?? true;
62
+
63
+ }
64
+
65
+ /** Whether the kernel being built has shadow rays sample transmission maps. Outside a scene kernel (a test) it does. */
66
+ export function usesTransmissionMaps( builder ) {
67
+
68
+ return builder.context[ KEY ]?.transmissionMaps ?? true;
69
+
70
+ }
@@ -653,8 +653,7 @@ export function buildShadeKernel( params ) {
653
653
  // surface that escapes to env keeps alpha 1 (HAS_HIT_OPAQUE set), so glass-in-front-of-an-
654
654
  // object stays opaque while glass-in-front-of-sky exports see-through.
655
655
  // Ungated (was transparentBackground-only) so the alpha lane always carries coverage: it is the
656
- // only hit/miss signal valid at FinalWrite. The image is unchanged — FinalWrite forces alpha 1
657
- // when transparent-bg is off.
656
+ // only hit/miss signal valid at FinalWrite, which accumulates it as is.
658
657
  If( flags.bitAnd( uint( RAY_FLAG.HAS_HIT_OPAQUE ) ).equal( uint( 0 ) ), () => {
659
658
 
660
659
  currentRadiance.w.assign( 0.0 );
@@ -918,7 +917,7 @@ export function buildShadeKernel( params ) {
918
917
  const displacedNormal = N.toVar();
919
918
  If(
920
919
  material.displacementMapIndex.greaterThanEqual( int( 0 ) )
921
- .and( material.displacementScale.greaterThan( 0.0 ) ),
920
+ .and( material.displacementScale.notEqual( 0.0 ) ),
922
921
  () => {
923
922
 
924
923
  const dispRay = Ray( { origin, direction } );
@@ -1004,7 +1003,7 @@ export function buildShadeKernel( params ) {
1004
1003
  const albedo = matSamples.albedo.toVar();
1005
1004
  If(
1006
1005
  material.displacementMapIndex.greaterThanEqual( int( 0 ) )
1007
- .and( material.displacementScale.greaterThan( 0.0 ) ),
1006
+ .and( material.displacementScale.notEqual( 0.0 ) ),
1008
1007
  () => {
1009
1008
 
1010
1009
  N.assign( normalize( displacedNormal.add( matSamples.normal.sub( normalize( hitNormal ) ) ) ) );
package/src/TSL/Struct.js CHANGED
@@ -36,6 +36,7 @@ export const RayTracingMaterial = struct( {
36
36
  bumpMapIndex: 'int',
37
37
  bumpScale: 'float',
38
38
  displacementScale: 'float',
39
+ displacementBias: 'float',
39
40
  metalnessMapIndex: 'int',
40
41
  roughnessMapIndex: 'int',
41
42
  displacementMapIndex: 'int',
@@ -336,6 +336,7 @@ export class BidirectionalIntegrator {
336
336
  strategyView: bd.strategyView,
337
337
  cameraPosition: bd.cameraPosition,
338
338
  cameraForward: bd.cameraForward,
339
+ cameraClipStart: this.pt.cameraClipStart,
339
340
  cameraViewMatrix: this.pt.cameraViewMatrix,
340
341
  cameraProjectionMatrix: this.pt.cameraProjectionMatrix,
341
342
  pixelArea: bd.pixelArea,
@@ -93,6 +93,7 @@ export class CameraManager extends EventDispatcher {
93
93
 
94
94
  this.cameras = cameras;
95
95
  this._userCameraCounter = 0;
96
+ this.camera.userData.clipStart = 0;
96
97
  this.resetAutoFocus();
97
98
 
98
99
  }
@@ -120,6 +121,7 @@ export class CameraManager extends EventDispatcher {
120
121
  cam.userData.__rayzeeUserCamera = true;
121
122
  cam.userData.__rayzeeEffects = this._captureEffects();
122
123
  if ( this.controls ) cam.userData.__rayzeeOrbitTarget = this.controls.target.clone();
124
+ if ( src.userData.clipStart > 0 ) cam.userData.clipStart = src.userData.clipStart;
123
125
 
124
126
  this._userCameraCounter += 1;
125
127
  cam.name = name || `View Camera ${this._userCameraCounter}`;
@@ -310,6 +312,7 @@ export class CameraManager extends EventDispatcher {
310
312
  this.camera.fov = s.fov;
311
313
  this.camera.near = s.near;
312
314
  this.camera.far = s.far;
315
+ this.camera.userData.clipStart = 0;
313
316
  this.camera.updateProjectionMatrix();
314
317
  this.camera.updateMatrixWorld( true );
315
318
 
@@ -380,6 +383,8 @@ export class CameraManager extends EventDispatcher {
380
383
  if ( source.isPerspectiveCamera ) this.camera.fov = source.fov;
381
384
  this.camera.near = source.near;
382
385
  this.camera.far = source.far;
386
+ // Only an imported camera's clip start clips the render: see PathTracerStage._updateCameraUniforms.
387
+ this.camera.userData.clipStart = source.userData?.clipStart ?? 0;
383
388
  this.camera.updateProjectionMatrix();
384
389
  this.camera.updateMatrixWorld( true );
385
390
 
@@ -611,6 +616,7 @@ export class CameraManager extends EventDispatcher {
611
616
  fov: camera.fov,
612
617
  near: camera.near,
613
618
  far: camera.far,
619
+ clipStart: camera.userData.clipStart ?? 0,
614
620
  zoom: camera.zoom,
615
621
  orthographic: !! camera.orthographic,
616
622
  orthoHalfHeight: camera.orthoHalfHeight,
@@ -641,6 +647,7 @@ export class CameraManager extends EventDispatcher {
641
647
  aspect: cam.aspect ?? null,
642
648
  near: cam.near,
643
649
  far: cam.far,
650
+ clipStart: cam.userData.clipStart ?? 0,
644
651
  zoom: cam.zoom,
645
652
  position: cam.position.toArray(),
646
653
  quaternion: cam.quaternion.toArray(),
@@ -684,6 +691,7 @@ export class CameraManager extends EventDispatcher {
684
691
  cam.updateMatrixWorld( true );
685
692
  cam.userData.__rayzeeUserCamera = true;
686
693
  cam.userData.__rayzeeEffects = fromPortable( entry.effects );
694
+ if ( entry.clipStart > 0 ) cam.userData.clipStart = entry.clipStart;
687
695
  if ( entry.orbitTarget ) cam.userData.__rayzeeOrbitTarget = new Vector3().fromArray( entry.orbitTarget );
688
696
  indexMap.set( entry.index, this.cameras.length );
689
697
  this.cameras.push( cam );
@@ -730,6 +738,7 @@ export class CameraManager extends EventDispatcher {
730
738
  camera.fov = v.fov;
731
739
  camera.near = v.near;
732
740
  camera.far = v.far;
741
+ camera.userData.clipStart = v.clipStart ?? 0;
733
742
  this._setView( v.orthographic, v.orthoHalfHeight );
734
743
  camera.zoom = v.zoom ?? 1;
735
744
  camera.updateProjectionMatrix();
@@ -426,6 +426,7 @@ export class DenoisingManager extends EventDispatcher {
426
426
  getToneMapping: () => this._getToneMapping(),
427
427
  getSaturation: () => this._getSaturation(),
428
428
  getDisplayGain: () => this._getDisplayGain?.() ?? null,
429
+ getTransparentBackground: () => !! this._settings?.get( 'transparentBackground' ),
429
430
  } );
430
431
 
431
432
  this.upscaler.enabled = DENOISER_DEFAULTS.enableUpscaler || false;
@@ -704,6 +704,12 @@ export class InteractionManager extends EventDispatcher {
704
704
  */
705
705
  _intersectScene() {
706
706
 
707
+ // Nothing in front of an imported camera's clip plane is rendered, so nothing there is picked. An orthographic
708
+ // ray already starts on the near plane, which a switch to such a camera set to its clip start.
709
+ const clipStart = this.camera.userData?.clipStart ?? 0;
710
+ this.raycaster.near = clipStart > 0 && ! this.camera.isOrthographicCamera
711
+ ? clipStart / Math.max( this.raycaster.ray.direction.dot( this.camera.getWorldDirection( _viewAxis ) ), 1e-4 )
712
+ : 0;
707
713
  return this.isGeometryOnDisk?.() ? [] : this.raycaster.intersectObjects( this.scene.children, true );
708
714
 
709
715
  }
@@ -342,6 +342,16 @@ export class MaterialDataManager {
342
342
 
343
343
  }
344
344
 
345
+ /** Whether some material has a transmission map: only then do shadow rays sample one. */
346
+ hasTransmissionMaps() {
347
+
348
+ const data = this.materialStorageAttr?.array;
349
+ if ( ! data ) return false;
350
+ for ( let i = 0; i < this.materialCount; i ++ ) if ( data[ i * M.FLOATS_PER_MATERIAL + M.TRANSMISSION_MAP_INDEX ] >= 0 ) return true;
351
+ return false;
352
+
353
+ }
354
+
345
355
  /**
346
356
  * Where a material property's value came from, as a MATERIAL_VALUE_SOURCE. `default` means the
347
357
  * model never said and the engine filled it in.
@@ -503,6 +513,7 @@ export class MaterialDataManager {
503
513
  break;
504
514
  case 'bumpScale': data[ stride + M.BUMP_SCALE ] = value; break;
505
515
  case 'displacementScale': data[ stride + M.DISPLACEMENT_SCALE ] = value; break;
516
+ case 'displacementBias': data[ stride + M.DISPLACEMENT_BIAS ] = value; break;
506
517
  case 'subsurface': data[ stride + M.SUBSURFACE ] = value; break;
507
518
  case 'subsurfaceRadiusScale': data[ stride + M.SUBSURFACE_RADIUS_SCALE ] = value; break;
508
519
  case 'subsurfaceAnisotropy': data[ stride + M.SUBSURFACE_ANISOTROPY ] = value; break;
@@ -23,7 +23,7 @@ const SHADER_NAMES = {
23
23
  export const LIGHT_FLOATS = Object.freeze( {
24
24
  directional: 12, // 8 light fields + gobo { index, signed intensity, scale, pad }
25
25
  area: 16, // 13 base + normalize, spread, shape
26
- point: 9,
26
+ point: 10, // 9 light fields + radius
27
27
  spot: 20, // 14 light fields + gobo { index, signed intensity } + IES { index, intensity } + 2 reserved
28
28
  } );
29
29
 
@@ -301,6 +301,8 @@ export class UniformManager {
301
301
  u( 'cameraProjectionMatrixInverse', new Matrix4(), 'mat4' );
302
302
  u( 'cameraViewMatrix', new Matrix4(), 'mat4' );
303
303
  u( 'cameraProjectionMatrix', new Matrix4(), 'mat4' );
304
+ // An imported camera's near clipping plane: camera rays start this far in front of it (0 = at the camera).
305
+ u( 'cameraClipStart', 0, 'float' );
304
306
 
305
307
  // Projection: CAMERA_PROJECTION_IDS. Ranges are radians (UI carries degrees).
306
308
  const radRange = ( [ min, max ] ) => new Vector2( min, max ).multiplyScalar( MathUtils.DEG2RAD );
@@ -1,110 +0,0 @@
1
- //#region src/Processor/archiveFormats.js
2
- var e = Object.freeze({
3
- zip: {
4
- type: "archive",
5
- name: "ZIP Archive"
6
- },
7
- gz: {
8
- type: "archive",
9
- name: "Gzipped TAR Archive"
10
- },
11
- tgz: {
12
- type: "archive",
13
- name: "Gzipped TAR Archive"
14
- },
15
- tar: {
16
- type: "archive",
17
- name: "TAR Archive"
18
- }
19
- }), t = (e) => typeof Blob < "u" && e instanceof Blob, n = (e) => !!e && typeof e == "object" && !t(e) && e.files != null, r = (e) => e.startsWith(".") || e === "__MACOSX";
20
- function i({ name: e = null, files: n }, { isModel: i = () => !1 } = {}) {
21
- let a = /* @__PURE__ */ new Map();
22
- for (let e of n) {
23
- let n = t(e) ? e : e.file, i = t(e) ? e.webkitRelativePath || e.name : e.path, o = String(i ?? "").replace(/\\/g, "/").split("/").filter((e) => e && e !== ".").join("/");
24
- !o || !n || o.split("/").some(r) || a.set(o, {
25
- path: o,
26
- file: n
27
- });
28
- }
29
- let o = [...a.values()].sort((e, t) => e.path < t.path ? -1 : +(e.path > t.path)), s = o[0]?.path.split("/")[0], c = o.length > 0 && o.every((e) => e.path.startsWith(s + "/")) ? s : null;
30
- return {
31
- name: e ?? c ?? (o.find((e) => i(e.path)) ?? o[0])?.path.split("/").pop() ?? "Folder",
32
- files: o,
33
- flat: c === null && o.every((e) => !e.path.includes("/"))
34
- };
35
- }
36
- //#endregion
37
- //#region src/Processor/SceneMetadata.js
38
- var a = [
39
- "sourceFile",
40
- "url",
41
- "file"
42
- ];
43
- function o(e) {
44
- if (typeof e == "string") try {
45
- let t = JSON.parse(e);
46
- return t && typeof t == "object" ? t : null;
47
- } catch {
48
- return null;
49
- }
50
- return e && typeof e == "object" ? e : null;
51
- }
52
- function s(e) {
53
- let t = typeof e == "string" ? parseFloat(e) : e;
54
- return Number.isFinite(t) ? t : void 0;
55
- }
56
- function c(e) {
57
- let t = o(e);
58
- if (!t) return null;
59
- let n = (e) => e && (e.environment !== void 0 || e.render !== void 0), r = o(t.rayzee);
60
- return n(r) ? r : n(t) ? t : null;
61
- }
62
- function l(e) {
63
- let t = o(e);
64
- if (!t) return null;
65
- let n = a.map((e) => t[e]).find((e) => typeof e == "string" && e.trim());
66
- if (!n) return null;
67
- let r = s(t.rotation), i = s(t.intensity), c = { sourceFile: n.trim() };
68
- return r !== void 0 && (c.rotation = r), i !== void 0 && (c.intensity = Math.max(0, i)), c;
69
- }
70
- var u = [
71
- "maxBounces",
72
- "samples",
73
- "width",
74
- "height"
75
- ];
76
- function d(e) {
77
- let t = o(e);
78
- if (!t) return null;
79
- let n = {};
80
- for (let e of u) {
81
- let r = s(t[e]);
82
- r !== void 0 && r > 0 && (n[e] = Math.round(r));
83
- }
84
- return Object.keys(n).length ? n : null;
85
- }
86
- function f(e) {
87
- let t = c(e);
88
- if (!t) return null;
89
- let n = l(t.environment), r = d(t.render);
90
- return !n && !r ? null : {
91
- ...n ? { environment: n } : {},
92
- ...r ? { render: r } : {}
93
- };
94
- }
95
- function p(e) {
96
- if (!e) return null;
97
- let t = [
98
- e.userData,
99
- e.asset?.extras,
100
- e.scene?.userData
101
- ];
102
- Array.isArray(e.scenes) && t.push(...e.scenes.map((e) => e?.userData));
103
- for (let e of t) {
104
- let t = f(e);
105
- if (t) return t;
106
- }
107
- return null;
108
- }
109
- //#endregion
110
- export { i as a, n as i, f as n, e as r, p as t };